• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }