• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
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 <arpa/inet.h>
17 #include <cinttypes>
18 #include <cstdio>
19 #include <cstring>
20 #include <fstream>
21 #include <getopt.h>
22 #include <grpcpp/grpcpp.h>
23 #include <ifaddrs.h>
24 #include <netinet/in.h>
25 #include <ostream>
26 #include <sys/types.h>
27 #include <thread>
28 #include <unistd.h>
29 #include <vector>
30 
31 #include "common.h"
32 #include "command_line.h"
33 #include "google/protobuf/text_format.h"
34 #include "parameters.h"
35 #include "parse_plugin_config.h"
36 #include "profiler_service.grpc.pb.h"
37 #include "trace_plugin_config.pb.h"
38 
39 using google::protobuf::TextFormat;
40 
41 namespace {
42 constexpr int ADDR_BUFFER_SIZE = 128;
43 constexpr int MS_PER_S = 1000;
44 constexpr int KEEP_SESSION_TIMEOUT_MS = 5 * 1000;
45 constexpr int KEEP_SESSION_SLEEP_SECOND = 3;
46 constexpr int DEFAULT_SESSION_TIME_S = 10;
47 const std::string DEFAULT_OUTPUT_FILE = "/data/local/tmp/hiprofiler_data.htrace";
48 const std::string HIPROFILERD_NAME("hiprofilerd");
49 const std::string HIPROFILER_PLUGINS_NAME("hiprofiler_plugins");
50 const std::string NATIVE_DAEMON_NAME("native_daemon");
51 
52 uint32_t g_sampleDuration = 0;
53 int g_hiprofilerdPid = -1;
54 int g_hiprofilerPluginsPid = -1;
55 int g_nativeDaemonPid = -1;
56 
GetLoopbackAddress()57 std::string GetLoopbackAddress()
58 {
59     char addressBuffer[ADDR_BUFFER_SIZE] = "";
60     struct ifaddrs* ifAddrStruct = nullptr;
61     void* tmpAddrPtr = nullptr;
62 
63     getifaddrs(&ifAddrStruct);
64     while (ifAddrStruct != nullptr) {
65         if (ifAddrStruct->ifa_addr == nullptr) {
66             ifAddrStruct = ifAddrStruct->ifa_next;
67             continue;
68         }
69         if (ifAddrStruct->ifa_addr->sa_family == AF_INET) {
70             // is a valid IP4 Address
71             tmpAddrPtr = &((reinterpret_cast<struct sockaddr_in*>(ifAddrStruct->ifa_addr))->sin_addr);
72             inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
73             if (strcmp(addressBuffer, "127.0.0.1") == 0) {
74                 break;
75             }
76         } else if (ifAddrStruct->ifa_addr->sa_family == AF_INET6) { // check it is IP6
77             // is a valid IP6 Address
78             tmpAddrPtr = &((reinterpret_cast<struct sockaddr_in*>(ifAddrStruct->ifa_addr))->sin_addr);
79             inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
80         }
81         ifAddrStruct = ifAddrStruct->ifa_next;
82     }
83 
84     freeifaddrs(ifAddrStruct);
85     return addressBuffer;
86 }
87 
GetServicePort()88 uint16_t GetServicePort()
89 {
90     return COMMON::GetServicePort();
91 }
92 
MakeCreateRequest(const std::string & config,const std::string & keepSecond,const std::string & outputFile)93 std::unique_ptr<CreateSessionRequest> MakeCreateRequest(const std::string& config,
94                                                         const std::string& keepSecond,
95                                                         const std::string& outputFile)
96 {
97     auto request = std::make_unique<CreateSessionRequest>();
98     if (!request) {
99         return nullptr;
100     }
101 
102     std::string content = config;
103     if (content.empty()) {
104         printf("config file empty!");
105         return nullptr;
106     }
107 
108     if (!ParsePluginConfig::GetInstance().GetParser().ParseFromString(content, request.get())) {
109         printf("config [%s] parse FAILED!\n", content.c_str());
110         return nullptr;
111     }
112 
113     auto sessionConfig = request->mutable_session_config();
114     if (!sessionConfig) {
115         return nullptr;
116     }
117 
118     request->set_request_id(1);
119     if (!keepSecond.empty()) {
120         int ks = std::stoi(keepSecond);
121         if (ks > 0) {
122             sessionConfig->set_sample_duration(ks * MS_PER_S);
123         }
124     } else if (sessionConfig->sample_duration() <= 0) {
125         sessionConfig->set_sample_duration(DEFAULT_SESSION_TIME_S * MS_PER_S);
126     }
127     if (!outputFile.empty()) {
128         sessionConfig->set_result_file(outputFile);
129     } else if (sessionConfig->result_file() == "") {
130         sessionConfig->set_result_file(DEFAULT_OUTPUT_FILE);
131     }
132     printf("keepSecond: %us, outputFileName: %s\n", sessionConfig->sample_duration() / MS_PER_S,
133         sessionConfig->result_file().c_str());
134 
135     g_sampleDuration = sessionConfig->sample_duration();
136     for (int i = 0; i < request->plugin_configs().size(); i++) {
137         auto pluginConfig = request->mutable_plugin_configs(i);
138         if (!ParsePluginConfig::GetInstance().SetSerializePluginsConfig(pluginConfig->name(), *pluginConfig)) {
139             printf("set %s plugin config failed\n", pluginConfig->name().c_str());
140             return nullptr;
141         }
142         if (sessionConfig->split_file() && pluginConfig->name() != "ftrace-plugin") {
143             printf("only ftrace-plugin support split_file, please check session_config.\n");
144             return nullptr;
145         }
146     }
147 
148     content.clear();
149     if (!TextFormat::PrintToString(*request.get(), &content)) {
150         printf("config message format FAILED!\n");
151         return nullptr;
152     }
153 
154     return request;
155 }
156 
GetProfilerServiceStub()157 std::unique_ptr<IProfilerService::Stub> GetProfilerServiceStub()
158 {
159     std::string serviceUri = GetLoopbackAddress() + ":" + std::to_string(GetServicePort());
160     auto grpcChannel = grpc::CreateChannel(serviceUri, grpc::InsecureChannelCredentials());
161     if (grpcChannel == nullptr) {
162         printf("Create gRPC channel failed!\n");
163         return nullptr;
164     }
165     return IProfilerService::NewStub(grpcChannel);
166 }
167 
GetCapabilities(std::string & content,bool isCheck)168 bool GetCapabilities(std::string& content, bool isCheck)
169 {
170     auto profilerStub = GetProfilerServiceStub();
171     if (profilerStub == nullptr) {
172         printf("Get profiler service stub failed!\n");
173         return false;
174     }
175 
176     GetCapabilitiesRequest capRequest;
177     GetCapabilitiesResponse capResponse;
178     capRequest.set_request_id(0);
179     grpc::ClientContext capContext;
180     grpc::Status status = profilerStub->GetCapabilities(&capContext, capRequest, &capResponse);
181     if (!status.ok()) {
182         printf("Service not started\n");
183         return false;
184     }
185 
186     if (!TextFormat::PrintToString(capResponse, &content)) {
187         printf("capabilities message format FAILED!\n");
188         return false;
189     }
190 
191     if (!isCheck) {
192         printf("support plugin list:\n%s\n", content.c_str());
193     }
194     return true;
195 }
196 
CreateSession(std::unique_ptr<IProfilerService::Stub> & profilerStub,const std::string & config,const std::string & keepSecond,const std::string & outputFile)197 uint32_t CreateSession(std::unique_ptr<IProfilerService::Stub>& profilerStub, const std::string& config,
198     const std::string& keepSecond, const std::string& outputFile)
199 {
200     auto request = MakeCreateRequest(config, keepSecond, outputFile);
201     if (!request) {
202         printf("MakeCreateRequest failed!\n");
203         return 0;
204     }
205 
206     CreateSessionResponse createResponse;
207     grpc::ClientContext createSessionContext;
208     grpc::Status status = profilerStub->CreateSession(&createSessionContext, *request, &createResponse);
209     if (!status.ok()) {
210         printf("CreateSession FAIL\n");
211         return 0;
212     }
213 
214     return createResponse.session_id();
215 }
216 
CheckStartSession(std::unique_ptr<IProfilerService::Stub> & profilerStub,uint32_t & sessionId)217 bool CheckStartSession(std::unique_ptr<IProfilerService::Stub>& profilerStub, uint32_t& sessionId)
218 {
219     StartSessionRequest startRequest;
220     StartSessionResponse startResponse;
221     startRequest.set_request_id(0);
222     startRequest.set_session_id(sessionId);
223     grpc::ClientContext startContext;
224     grpc::Status status = profilerStub->StartSession(&startContext, startRequest, &startResponse);
225     if (!status.ok()) {
226         printf("StartSession FAIL\n");
227         return false;
228     }
229 
230     return true;
231 }
232 
CheckStopSession(std::unique_ptr<IProfilerService::Stub> & profilerStub,uint32_t & sessionId)233 bool CheckStopSession(std::unique_ptr<IProfilerService::Stub>& profilerStub, uint32_t& sessionId)
234 {
235     StopSessionRequest stopRequest;
236     StopSessionResponse stopResponse;
237     grpc::ClientContext stopContext;
238     stopRequest.set_session_id(sessionId);
239     grpc::Status status = profilerStub->StopSession(&stopContext, stopRequest, &stopResponse);
240     if (!status.ok()) {
241         return false;
242     }
243 
244     return true;
245 }
246 
CheckDestroySession(std::unique_ptr<IProfilerService::Stub> & profilerStub,uint32_t & sessionId)247 bool CheckDestroySession(std::unique_ptr<IProfilerService::Stub>& profilerStub, uint32_t& sessionId)
248 {
249     DestroySessionRequest destroyRequest;
250     DestroySessionResponse destroyResponse;
251     grpc::ClientContext destroyContext;
252     destroyRequest.set_session_id(sessionId);
253     grpc::Status status = profilerStub->DestroySession(&destroyContext, destroyRequest, &destroyResponse);
254     if (!status.ok()) {
255         return false;
256     }
257 
258     return true;
259 }
260 
DoCapture(const std::string & config,const std::string & keepSecond,const std::string & outputFile)261 bool DoCapture(const std::string& config, const std::string& keepSecond, const std::string& outputFile)
262 {
263     auto profilerStub = GetProfilerServiceStub();
264     if (profilerStub == nullptr) {
265         printf("Get profiler service stub failed!\n");
266         return 0;
267     }
268 
269     uint32_t sessionId = CreateSession(profilerStub, config, keepSecond, outputFile);
270     if (sessionId == 0) {
271         printf("Create session returns Id 0\n");
272         return false;
273     }
274 
275     // 开启心跳线程确保会话正常,睡眠3s下发一次5s超时心跳
276     bool sendHeart = true;
277     std::thread keepSessionThread([&]() {
278         while (sendHeart) {
279             KeepSessionRequest keepRequest;
280             keepRequest.set_request_id(0);
281             keepRequest.set_session_id(sessionId);
282             keepRequest.set_keep_alive_time(KEEP_SESSION_TIMEOUT_MS);
283             grpc::ClientContext keepContext;
284             KeepSessionResponse keepResponse;
285             profilerStub->KeepSession(&keepContext, keepRequest, &keepResponse);
286             std::this_thread::sleep_for(std::chrono::seconds(KEEP_SESSION_SLEEP_SECOND));
287         }
288     });
289 
290     if (!CheckStartSession(profilerStub, sessionId)) {
291         return false;
292     }
293 
294     printf("tracing %u ms....\n", g_sampleDuration);
295     std::this_thread::sleep_for(std::chrono::milliseconds(g_sampleDuration));
296 
297     if (!CheckStopSession(profilerStub, sessionId)) {
298         sendHeart = false;
299         keepSessionThread.join();
300         return false;
301     }
302     printf("StopSession done!\n");
303 
304     if (!CheckDestroySession(profilerStub, sessionId)) {
305         sendHeart = false;
306         keepSessionThread.join();
307         return false;
308     }
309     printf("DestroySession done!\n");
310 
311     sendHeart = false;
312     keepSessionThread.join();
313     return true;
314 }
315 
316 struct DataContext {
317     bool isGetGrpcAddr = false;
318     std::string traceKeepSecond;
319     std::string configFile;
320     std::string outputFile;
321     bool isHelp = false;
322     bool isShowPluginList = false;
323     bool isStartProcess = false;
324     bool isKillProcess = false;
325 };
326 
ParseCmdline(CommandLine & cmdLine,DataContext & data)327 void ParseCmdline(CommandLine& cmdLine, DataContext& data)
328 {
329     cmdLine.AddParamSwitch("--getport", "-q", data.isGetGrpcAddr, "get grpc address");
330     cmdLine.AddParamText("--time", "-t", data.traceKeepSecond, "trace time");
331     cmdLine.AddParamText("--out", "-o", data.outputFile, "output file name");
332     cmdLine.AddParamSwitch("--help", "-h", data.isHelp, "make some help");
333     cmdLine.AddParamSwitch("--list", "-l", data.isShowPluginList, "plugin list");
334     cmdLine.AddParamSwitch("--start", "-s", data.isStartProcess, "start dependent process");
335     cmdLine.AddParamSwitch("--kill", "-k", data.isKillProcess, "kill dependent process");
336     cmdLine.AddParamText("--config", "-c", data.configFile, "start trace by config file");
337 }
338 
CheckGrpcMsgSend()339 int CheckGrpcMsgSend()
340 {
341     auto profilerStub = GetProfilerServiceStub();
342     if (profilerStub == nullptr) {
343         printf("Get profiler service stub failed!\n");
344         return -1;
345     }
346 
347     GetCapabilitiesRequest request;
348     GetCapabilitiesResponse response;
349     request.set_request_id(0);
350 
351     grpc::ClientContext context;
352     grpc::Status status = profilerStub->GetCapabilities(&context, request, &response);
353     if (!status.ok()) {
354         printf("Service not started\n");
355         return -1;
356     }
357 
358     printf("OK\n");
359     printf("ip:%s\n", GetLoopbackAddress().c_str());
360     printf("port:%u\n", GetServicePort());
361     return 0;
362 }
363 
StartDependentProcess()364 bool StartDependentProcess()
365 {
366     constexpr int waitProcMills = 300;
367     if (getuid() == 0) {
368         if (!COMMON::IsProcessExist(HIPROFILERD_NAME, g_hiprofilerdPid)) {
369             // need start hiprofilerd
370             std::vector<char*> argvVec;
371             argvVec.push_back(const_cast<char*>(HIPROFILERD_NAME.c_str()));
372             g_hiprofilerdPid = COMMON::StartProcess(HIPROFILERD_NAME, argvVec);
373             // Wait for the hiprofilerd to start
374             std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
375         }
376 
377         if (!COMMON::IsProcessExist(HIPROFILER_PLUGINS_NAME, g_hiprofilerPluginsPid)) {
378             // need start hiprofiler_plugins
379             std::vector<char*> argvVec;
380             argvVec.push_back(const_cast<char*>(HIPROFILER_PLUGINS_NAME.c_str()));
381             g_hiprofilerPluginsPid = COMMON::StartProcess(HIPROFILER_PLUGINS_NAME, argvVec);
382             // Wait for the hiprofiler_plugins add preset plugin
383             std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
384         }
385 
386         if (!COMMON::IsProcessExist(NATIVE_DAEMON_NAME, g_nativeDaemonPid)) {
387             // need start native_daemon
388             std::vector<char*> argvVec;
389             argvVec.push_back(const_cast<char*>(NATIVE_DAEMON_NAME.c_str()));
390             g_nativeDaemonPid = COMMON::StartProcess(NATIVE_DAEMON_NAME, argvVec);
391             // Wait for the native_daemon to start
392             std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
393         }
394     } else {
395         OHOS::system::SetParameter("hiviewdfx.hiprofiler.profilerd.start", "0");
396         OHOS::system::SetParameter("hiviewdfx.hiprofiler.plugins.start", "0");
397         OHOS::system::SetParameter("hiviewdfx.hiprofiler.native_memoryd.start", "0");
398 
399         OHOS::system::SetParameter("hiviewdfx.hiprofiler.profilerd.start", "1");
400         std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
401         OHOS::system::SetParameter("hiviewdfx.hiprofiler.plugins.start", "1");
402         std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
403         OHOS::system::SetParameter("hiviewdfx.hiprofiler.native_memoryd.start", "1");
404         std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
405     }
406 
407     std::string content = "";
408     GetCapabilities(content, true);
409     if (content == "") {
410         printf("Please confirm whether the plugin exists\n");
411         return false;
412     }
413 
414     return true;
415 }
416 
KillDependentProcess()417 void KillDependentProcess()
418 {
419     if (getuid() == 0) {
420         // if pid is equal to -1, need to get pid first.
421         if (g_nativeDaemonPid == -1) {
422             COMMON::IsProcessExist(NATIVE_DAEMON_NAME, g_nativeDaemonPid);
423         }
424         if (g_hiprofilerPluginsPid == -1) {
425             COMMON::IsProcessExist(HIPROFILER_PLUGINS_NAME, g_hiprofilerPluginsPid);
426         }
427         if (g_hiprofilerdPid == -1) {
428             COMMON::IsProcessExist(HIPROFILERD_NAME, g_hiprofilerdPid);
429         }
430 
431         COMMON::KillProcess(g_nativeDaemonPid);
432         COMMON::KillProcess(g_hiprofilerPluginsPid);
433         COMMON::KillProcess(g_hiprofilerdPid);
434     } else {
435         OHOS::system::SetParameter("hiviewdfx.hiprofiler.profilerd.start", "0");
436         OHOS::system::SetParameter("hiviewdfx.hiprofiler.plugins.start", "0");
437         OHOS::system::SetParameter("hiviewdfx.hiprofiler.native_memoryd.start", "0");
438     }
439 }
440 
ParseConfig(const std::string & configFile,std::string & config)441 bool ParseConfig(const std::string& configFile, std::string& config)
442 {
443     std::string configFileWithPath = configFile;
444     if (configFile.find('/') == std::string::npos) {
445         std::string path("/data/local/tmp/");
446         configFileWithPath = path + configFile; // add default path
447     }
448 
449     printf("Read config from %s\n", configFileWithPath.c_str());
450     std::vector<std::string> validPaths = { "/data/local/tmp/" };
451     if (!COMMON::ReadFile(configFileWithPath, validPaths, config)) {
452         printf("Read %s fail, please place it under \'/data/local/tmp/\'.\n", configFile.c_str());
453         return false;
454     }
455     config = ParsePluginConfig::GetInstance().GetPluginsConfig(config);
456     if (config.empty()) {
457         printf("Error config file: %s\n", configFileWithPath.c_str()); // no config in command or config file
458         return false;
459     }
460     return true;
461 }
462 } // namespace
463 
main(int argc,char * argv[])464 int main(int argc, char* argv[])
465 {
466     std::string config = "";
467     while (true) {
468         struct option long_options[] = {
469             {"getport", no_argument, nullptr, 'q'},
470             {"time", required_argument, nullptr, 't'},
471             {"out", required_argument, nullptr, 'o'},
472             {"help", no_argument, nullptr, 'h'},
473             {"list", no_argument, nullptr, 'l'},
474             {"start", no_argument,  nullptr, 's'},
475             {"kill", no_argument,  nullptr, 'k'},
476             {"config", required_argument, nullptr, 'c'},
477             {nullptr, 0, nullptr, 0}
478         };
479         int option = getopt_long(argc, argv, "c:t:o:qhlsk", long_options, nullptr);
480         if (option == -1) {
481             break;  // CONFIG.
482         }
483 
484         if (option == 'c' && strcmp(optarg, "-") == 0) {
485             std::string content;
486             std::istreambuf_iterator<char> begin(std::cin);
487             std::istreambuf_iterator<char> end = {};
488             content.assign(begin, end);
489             config = ParsePluginConfig::GetInstance().GetPluginsConfig(content);
490             if (config.empty()) {
491                 printf("Please check the configuration!\n");
492                 return -1;
493             }
494         }
495     }
496 
497     DataContext data;
498     CommandLine& cmdLine = CommandLine::GetInstance();
499     ParseCmdline(cmdLine, data);
500 
501     std::vector<std::string> argvVector;
502     for (int i = 0; i < argc; i++) {
503         if (((i + 1) < argc) && (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) &&
504             (strcmp(argv[i + 1], "-") == 0)) {
505             i++;
506         } else {
507             argvVector.push_back(argv[i]);
508         }
509     }
510     if (argc < 1 || cmdLine.AnalyzeParam(argvVector) < 0 || data.isHelp) {
511         cmdLine.PrintHelp();
512         exit(0);
513     }
514 
515     if (config.empty() && !data.configFile.empty()) {
516         if (!ParseConfig(data.configFile, config)) {
517             return -1;
518         }
519     }
520 
521     if (data.isStartProcess) {
522         if (!StartDependentProcess()) {
523             if (data.isKillProcess) {
524                 KillDependentProcess();
525             }
526             return 0;
527         }
528     }
529 
530     if (data.isGetGrpcAddr) { // handle get port
531         int ret = CheckGrpcMsgSend();
532         if (data.isKillProcess) {
533             KillDependentProcess();
534         }
535         return ret;
536     }
537 
538     if (data.isShowPluginList) { // handle show plugin list
539         std::string content = "";
540         GetCapabilities(content, false);
541         if (data.isKillProcess) {
542             KillDependentProcess();
543         }
544         return 0;
545     }
546 
547     if (config.empty()) { // normal case
548         if (data.isKillProcess) {
549             KillDependentProcess();
550             return 1;
551         }
552         if (!data.isStartProcess) {
553             printf("config file argument must sepcified!\n");
554         }
555         return 1;
556     }
557     // do capture work
558     if (DoCapture(config, data.traceKeepSecond, data.outputFile)) {
559         printf("DONE\n");
560     }
561 
562     if (data.isKillProcess) {
563         KillDependentProcess();
564     }
565     return 0;
566 }
567