1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <chrono>
17 #include <cerrno>
18 #include <csignal>
19 #include <cstdlib>
20 #include <cstdio>
21 #include <cstring>
22 #include <iostream>
23
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <linux/perf_event.h>
27 #include <linux/bpf.h>
28 #include <sys/resource.h>
29
30 #include "bpf_controller.h"
31 #include "commandline_flags.h"
32 #include "hhlog.h"
33 #include "command_helper.h"
34 #include "ipc_unix_socket.h"
35
36 HIEBPF_DEFINE_string(help, "none", "specify what help message to print");
37 HIEBPF_DEFINE_string(output_file, "/data/local/tmp/hiebpf.data", "the file used to save hiebpf data");
38 HIEBPF_DEFINE_bool(exclude_tracer, true, "the flag indicates whether to trace the tracer itself or not");
39 HIEBPF_DEFINE_uint32(dump_events, 0, "maximum number of events to print instead of save into file");
40 HIEBPF_DEFINE_bool(unwind_stack, true, "whether enable unwinding or not");
41 HIEBPF_DEFINE_uint32(max_stack_depth, MAX_STACK_DEPTH, "max unwinding stack depth");
42 HIEBPF_DEFINE_uint32(duration, 0, "the tracer will run for <duration> seconds");
43 HIEBPF_DEFINE_array(events, string, "events to trace");
44 HIEBPF_DEFINE_array(pids, uint32_t, "pids of target process to trace");
45 HIEBPF_DEFINE_string(bpf_log_level, "NONE", "lowest level of BPF programs logs to print");
46 HIEBPF_DEFINE_string(bpf_log_file, "/data/local/tmp/bpf.log", "the file used to save BPF program logs");
47 HIEBPF_DEFINE_string(libbpf_log_level, "NONE", "lowest level of libbpf internal error messages to print");
48 HIEBPF_DEFINE_string(libbpf_log_file, "/data/local/tmp/libbpf.log",
49 "the file used to save libbpf internal error messages");
50 HIEBPF_DEFINE_string(hhlog_level, "NONE", "lowest level of HHLog to print");
51 HIEBPF_DEFINE_string(hhlog_file, "/data/local/tmp/hhlog.txt", "the file used to save HHLog");
52 HIEBPF_DEFINE_bool(start, false, "setup and start hiebpf server to trace");
53 HIEBPF_DEFINE_bool(stop, false, "stop hiebpf server to trace");
54
55 static BPFConfig GenBPFConfigFromFlags(const std::string &cmd);
56 static int GetHHLogLevelFromFlag();
57 static inline std::vector<pid_t> GetTargetPidsFromFlag();
58 static std::unique_ptr<BPFController> bpfController;
59 static OHOS::Developtools::Hiebpf::IpcUnixSocketServer g_ipcServer;
60 static bool g_interrupted = false;
61
62 const uint32_t IPC_RECV_TIMEOUT = 1000;
63
64 enum HiebpfIpcCommand:uint32_t {
65 CMD_START = 0,
66 CMD_STOP,
67 CMD_HEART_BEAT,
68 RET_OK = 0x10000,
69 };
70
HandleIpcMessage(const void * data,size_t size)71 static void HandleIpcMessage(const void *data, size_t size)
72 {
73 if (size != sizeof(HiebpfIpcCommand)) {
74 HHLOGE(true, "unknown command");
75 return;
76 }
77
78 HiebpfIpcCommand cmd = *(const_cast<HiebpfIpcCommand*>(reinterpret_cast<const HiebpfIpcCommand*>(data)));
79 if (cmd == HiebpfIpcCommand::CMD_STOP) {
80 g_interrupted = true;
81 if (bpfController != nullptr) {
82 bpfController->Stop();
83 }
84 cmd = HiebpfIpcCommand::RET_OK;
85 if (!g_ipcServer.SendMessage(&cmd, sizeof(HiebpfIpcCommand))) {
86 HHLOGE(true, "send reply(0x%x) failed", cmd);
87 }
88 } else if (cmd == HiebpfIpcCommand::CMD_HEART_BEAT) {
89 cmd = HiebpfIpcCommand::RET_OK;
90 if (!g_ipcServer.SendMessage(&cmd, sizeof(HiebpfIpcCommand))) {
91 HHLOGE(true, "send reply(0x%x) failed\n", cmd);
92 }
93 } else {
94 HHLOGW(true, "the command(0x%x) don't support\n", cmd);
95 }
96 }
97
SendIpcCommand(OHOS::Developtools::Hiebpf::IpcUnixSocketClient & client,HiebpfIpcCommand cmd,uint32_t timeout)98 static bool SendIpcCommand(OHOS::Developtools::Hiebpf::IpcUnixSocketClient &client,
99 HiebpfIpcCommand cmd, uint32_t timeout)
100 {
101 CHECK_TRUE(client.SendMessage(&cmd, sizeof(HiebpfIpcCommand)), false, "send request(0x%x) failed\n", cmd);
102
103 size_t size = OHOS::Developtools::Hiebpf::UNIX_SOCKET_BUFFER_SIZE;
104 char buf[OHOS::Developtools::Hiebpf::UNIX_SOCKET_BUFFER_SIZE] = {0};
105 CHECK_TRUE(client.RecvMessage(buf, size, timeout), false, "recv reply failed\n");
106 if (size == 0) { // timeout
107 HHLOGE(true, "recv reply timeout(%dms)\n", timeout);
108 return false;
109 } else if (size != sizeof(HiebpfIpcCommand)) {
110 HHLOGE(true, "recv reply size error(%zu)\n", size);
111 return false;
112 }
113 HiebpfIpcCommand reply = *(reinterpret_cast<HiebpfIpcCommand*>(buf));
114 CHECK_TRUE(reply == HiebpfIpcCommand::RET_OK, false, "recv unknown reply: 0x%x\n", reply);
115 return true;
116 }
117
StopHiebpfServer()118 static void StopHiebpfServer()
119 {
120 OHOS::Developtools::Hiebpf::IpcUnixSocketClient ipcClient;
121 if (!ipcClient.Connect()) {
122 printf("ipc client connect failed\n");
123 return;
124 }
125
126 if (SendIpcCommand(ipcClient, HiebpfIpcCommand::CMD_STOP, IPC_RECV_TIMEOUT)) {
127 printf("send STOP command to hiebpf server sucess");
128 fflush(stdout);
129 while (SendIpcCommand(ipcClient, HiebpfIpcCommand::CMD_HEART_BEAT, IPC_RECV_TIMEOUT)) {
130 sleep(1);
131 printf(".");
132 fflush(stdout);
133 }
134 printf("\n");
135 } else {
136 printf("send STOP command to hiebpf server failed\n");
137 }
138
139 ipcClient.Disconnect();
140 }
141
CheckHiebpfServer()142 static bool CheckHiebpfServer()
143 {
144 sleep(1); // wait hiebpf IPC server started
145 OHOS::Developtools::Hiebpf::IpcUnixSocketClient ipcClient;
146 if (!ipcClient.Connect()) {
147 printf("ipc client connect failed\n");
148 return false;
149 }
150
151 bool ret = false;
152 if (SendIpcCommand(ipcClient, HiebpfIpcCommand::CMD_HEART_BEAT, IPC_RECV_TIMEOUT)) {
153 ret = true;
154 }
155
156 ipcClient.Disconnect();
157 return ret;
158 }
159
main(int argc,char * argv[])160 int main(int argc, char* argv[])
161 {
162 std::string cmd;
163 for (int32_t i = 0; i < argc; ++i) {
164 cmd += std::string(argv[i]) + " ";
165 }
166 // parse all defined command line arguments
167 if (CommandLineFlags::ParseFlags(&argc, &argv, true) != 0) {
168 std::cout << "[ERROR] " << __FILE__ << ":"
169 << __FUNCTION__ << ":"
170 << __LINE__ << ": "
171 << "failed to parse command line flags" << std::endl;
172 return -1;
173 }
174
175 // check if user issued help, none-zero return value means user does issue
176 // help, hence we exit right after printing the required help message.
177 if (CommandHelper::GetInstance().Start(&argc, &argv, FLAGS_help) != 0) {
178 // Note this is the workflow when user asking for command help, it's not
179 // an error. Besides, help workflow never fails.
180 return 0;
181 }
182
183 // start HHLogger
184 if (HHLogger::GetInstance().Start(GetHHLogLevelFromFlag(), FLAGS_hhlog_file) != 0) {
185 std::cout << "[ERROR] " << __FILE__ << ":"
186 << __FUNCTION__ << ":"
187 << __LINE__ << ": "
188 << "failed to start HHLogger" << std::endl;
189 return -1;
190 }
191
192 if (FLAGS_stop) {
193 StopHiebpfServer();
194 return 0;
195 }
196
197 int32_t globalFd = -1;
198 // fork() must be precede BPFController::MakeUnique(),
199 // otherwise ring_buffer__poll() will crash. why?
200 if (FLAGS_start) {
201 pid_t pid = fork();
202 if (pid == -1) {
203 printf("fork failed. %d:%s\n", errno, strerror(errno));
204 return -1;
205 } else if (pid == 0) { // child process
206 int32_t nullFd = open("/dev/null", O_WRONLY);
207 globalFd = nullFd;
208 (void)dup2(nullFd, STDOUT_FILENO); // redirect stdout to /dev/null
209 g_ipcServer.SetHandleCallback(HandleIpcMessage);
210 if (!g_ipcServer.Start()) {
211 close(nullFd);
212 return 0;
213 }
214 } else { // parent process
215 if (CheckHiebpfServer()) {
216 printf("hiebpf server has started.\n");
217 } else {
218 printf("hiebpf server start failed\n");
219 }
220 return 0;
221 }
222 }
223 HHLogger::GetInstance().LogSaver();
224 // start BPFController
225 bpfController = BPFController::MakeUnique(GenBPFConfigFromFlags(cmd));
226 if (bpfController == nullptr) {
227 std::cout << "[ERROR] " << __FILE__ << ":"
228 << __FUNCTION__ << ":"
229 << __LINE__ << ": "
230 << "failed to init BPFController" << std::endl;
231 return -1;
232 }
233
234 if (g_interrupted) {
235 if (globalFd != -1) {
236 close(globalFd);
237 }
238 return 0;
239 }
240
241 if (bpfController->Start() != 0) {
242 std::cout << "[ERROR] " << __FILE__ << ":"
243 << __FUNCTION__ << ":"
244 << __LINE__ << ": "
245 << "failed to start BPFEvent loop" << std::endl;
246 return -1;
247 }
248 bpfController = nullptr;
249
250 if (FLAGS_start) {
251 g_ipcServer.Stop();
252 if (globalFd != -1) {
253 close(globalFd);
254 }
255 }
256
257 return 0;
258 }
259
GetTargetPidsFromFlag()260 static inline std::vector<pid_t> GetTargetPidsFromFlag()
261 {
262 std::vector<pid_t> pids {};
263 for (size_t k = 0; k < FLAGS_pids.size(); ++ k) {
264 pids.push_back(static_cast<pid_t>(FLAGS_pids[k]));
265 }
266 return pids;
267 }
268
GetTargetEventsFromFlag()269 static inline std::set<HiebpfEventGroup> GetTargetEventsFromFlag()
270 {
271 const static std::unordered_map<std::string, HiebpfEventGroup> eventGroupMap {
272 {"fs", FS_GROUP_ALL},
273 {"fs:open", FS_GROUP_OPEN},
274 {"fs:read", FS_GROUP_READ},
275 {"fs:write", FS_GROUP_WRITE},
276 {"fs:close", FS_GROUP_CLOSE},
277 {"ptrace", MEM_GROUP_ALL},
278 {"bio", BIO_GROUP_ALL},
279 };
280
281 std::set<HiebpfEventGroup> result {};
282 for (size_t k = 0; k < FLAGS_events.size(); ++k) {
283 const auto& it = eventGroupMap.find(FLAGS_events[k]);
284 if (it == eventGroupMap.end()) {
285 // cmdline input error, how notify cmdline?
286 } else {
287 result.insert(it->second);
288 }
289 }
290 return result;
291 }
292
GetBPFLogLevelFromFlag()293 static inline __u32 GetBPFLogLevelFromFlag()
294 {
295 if (FLAGS_bpf_log_level.compare("DEBUG") == 0 or
296 FLAGS_bpf_log_level.compare("debug") == 0) {
297 return BPF_LOG_DEBUG;
298 }
299 if (FLAGS_bpf_log_level.compare("INFO") == 0 or
300 FLAGS_bpf_log_level.compare("info") == 0) {
301 return BPF_LOG_INFO;
302 }
303 if (FLAGS_bpf_log_level.compare("WARN") == 0 or
304 FLAGS_bpf_log_level.compare("warn") == 0) {
305 return BPF_LOG_WARN;
306 }
307 if (FLAGS_bpf_log_level.compare("ERROR") == 0 or
308 FLAGS_bpf_log_level.compare("error") == 0) {
309 return BPF_LOG_ERROR;
310 }
311 if (FLAGS_bpf_log_level.compare("FATAL") == 0 or
312 FLAGS_bpf_log_level.compare("fatal") == 0) {
313 return BPF_LOG_FATAL;
314 }
315 return BPF_LOG_NONE;
316 }
317
GetLIBBPFLogLevelFromFlag()318 static inline int GetLIBBPFLogLevelFromFlag()
319 {
320 if (FLAGS_libbpf_log_level.compare("DEBUG") == 0 or
321 FLAGS_libbpf_log_level.compare("debug") == 0) {
322 return LIBBPF_DEBUG;
323 }
324 if (FLAGS_libbpf_log_level.compare("INFO") == 0 or
325 FLAGS_libbpf_log_level.compare("info") == 0) {
326 return LIBBPF_INFO;
327 }
328 if (FLAGS_libbpf_log_level.compare("WARN") == 0 or
329 FLAGS_libbpf_log_level.compare("warn") == 0) {
330 return LIBBPF_WARN;
331 }
332 if (FLAGS_libbpf_log_level.compare("ERROR") == 0 or
333 FLAGS_libbpf_log_level.compare("error") == 0) {
334 return LIBBPF_ERROR;
335 }
336 if (FLAGS_libbpf_log_level.compare("FATAL") == 0 or
337 FLAGS_libbpf_log_level.compare("fatal") == 0) {
338 return LIBBPF_FATAL;
339 }
340 return LIBBPF_NONE;
341 }
342
GetHHLogLevelFromFlag()343 static int GetHHLogLevelFromFlag()
344 {
345 if (FLAGS_hhlog_level.compare("DEBUG") == 0 or
346 FLAGS_hhlog_level.compare("debug") == 0) {
347 return HHLOG_DEBUG;
348 }
349 if (FLAGS_hhlog_level.compare("INFO") == 0 or
350 FLAGS_hhlog_level.compare("info") == 0) {
351 return HHLOG_INFO;
352 }
353 if (FLAGS_hhlog_level.compare("WARN") == 0 or
354 FLAGS_hhlog_level.compare("warn") == 0) {
355 return HHLOG_WARN;
356 }
357 if (FLAGS_hhlog_level.compare("ERROR") == 0 or
358 FLAGS_hhlog_level.compare("error") == 0) {
359 return HHLOG_ERROR;
360 }
361 if (FLAGS_hhlog_level.compare("FATAL") == 0 or
362 FLAGS_hhlog_level.compare("fatal") == 0) {
363 return HHLOG_FATAL;
364 }
365 if (FLAGS_hhlog_level.compare("NONE") == 0 or
366 FLAGS_hhlog_level.compare("none") == 0) {
367 return HHLOG_NONE;
368 }
369 return HHLOG_NONE;
370 }
371
GenBPFConfigFromFlags(const std::string & cmd)372 static inline BPFConfig GenBPFConfigFromFlags(const std::string &cmd)
373 {
374 BPFConfig config {};
375 config.cmd_ = cmd;
376 config.excludeTracer_ = FLAGS_exclude_tracer;
377 config.unwindStack_ = FLAGS_max_stack_depth == 0 ? false : FLAGS_unwind_stack;
378 config.dumpEvents_ = FLAGS_dump_events;
379 config.traceDuration_ = FLAGS_duration;
380 config.maxStackDepth_ = FLAGS_max_stack_depth == 0 ? 1 : FLAGS_max_stack_depth;
381 config.targetPids_ = GetTargetPidsFromFlag();
382 config.selectEventGroups_ = GetTargetEventsFromFlag();
383 config.BPFLogLevel_ = GetBPFLogLevelFromFlag();
384 config.LIBBPFLogLevel_ = GetLIBBPFLogLevelFromFlag();
385 config.BPFLogFile_ = FLAGS_bpf_log_file;
386 config.LIBBPFLogFile_ = FLAGS_libbpf_log_file;
387 config.outputFile_ = FLAGS_output_file;
388 return config;
389 }