• 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 std::string HIPROFILERD_NAME("hiprofilerd");
49 std::string HIPROFILER_PLUGINS_NAME("hiprofiler_plugins");
50 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     }
143 
144     content.clear();
145     if (!TextFormat::PrintToString(*request.get(), &content)) {
146         printf("config message format FAILED!\n");
147         return nullptr;
148     }
149 
150     return request;
151 }
152 
GetProfilerServiceStub()153 std::unique_ptr<IProfilerService::Stub> GetProfilerServiceStub()
154 {
155     std::string serviceUri = GetLoopbackAddress() + ":" + std::to_string(GetServicePort());
156     auto grpcChannel = grpc::CreateChannel(serviceUri, grpc::InsecureChannelCredentials());
157     if (grpcChannel == nullptr) {
158         printf("Create gRPC channel failed!\n");
159         return nullptr;
160     }
161     return IProfilerService::NewStub(grpcChannel);
162 }
163 
GetCapabilities(std::string & content,bool isCheck)164 bool GetCapabilities(std::string& content, bool isCheck)
165 {
166     auto profilerStub = GetProfilerServiceStub();
167     if (profilerStub == nullptr) {
168         printf("Get profiler service stub failed!\n");
169         return false;
170     }
171 
172     GetCapabilitiesRequest capRequest;
173     GetCapabilitiesResponse capResponse;
174     capRequest.set_request_id(0);
175     grpc::ClientContext capContext;
176     grpc::Status status = profilerStub->GetCapabilities(&capContext, capRequest, &capResponse);
177     if (!status.ok()) {
178         printf("Service not started\n");
179         return false;
180     }
181 
182     if (!TextFormat::PrintToString(capResponse, &content)) {
183         printf("capabilities message format FAILED!\n");
184         return false;
185     }
186 
187     if (!isCheck) {
188         printf("support plugin list:\n%s\n", content.c_str());
189     }
190     return true;
191 }
192 
CreateSession(std::unique_ptr<IProfilerService::Stub> & profilerStub,const std::string & configFile,const std::string & keepSecond,const std::string & outputFile)193 uint32_t CreateSession(std::unique_ptr<IProfilerService::Stub>& profilerStub, const std::string& configFile,
194     const std::string& keepSecond, const std::string& outputFile)
195 {
196     auto request = MakeCreateRequest(configFile, keepSecond, outputFile);
197     if (!request) {
198         printf("MakeCreateRequest failed!\n");
199         return 0;
200     }
201 
202     CreateSessionResponse createResponse;
203     grpc::ClientContext createSessionContext;
204     grpc::Status status = profilerStub->CreateSession(&createSessionContext, *request, &createResponse);
205     if (!status.ok()) {
206         printf("CreateSession FAIL\n");
207         return 0;
208     }
209 
210     return createResponse.session_id();
211 }
212 
CheckStartSession(std::unique_ptr<IProfilerService::Stub> & profilerStub,uint32_t & sessionId)213 bool CheckStartSession(std::unique_ptr<IProfilerService::Stub>& profilerStub, uint32_t& sessionId)
214 {
215     StartSessionRequest startRequest;
216     StartSessionResponse startResponse;
217     startRequest.set_request_id(0);
218     startRequest.set_session_id(sessionId);
219     grpc::ClientContext startContext;
220     grpc::Status status = profilerStub->StartSession(&startContext, startRequest, &startResponse);
221     if (!status.ok()) {
222         printf("StartSession FAIL\n");
223         return false;
224     }
225 
226     return true;
227 }
228 
CheckStopSession(std::unique_ptr<IProfilerService::Stub> & profilerStub,uint32_t & sessionId)229 bool CheckStopSession(std::unique_ptr<IProfilerService::Stub>& profilerStub, uint32_t& sessionId)
230 {
231     StopSessionRequest stopRequest;
232     StopSessionResponse stopResponse;
233     grpc::ClientContext stopContext;
234     stopRequest.set_session_id(sessionId);
235     grpc::Status status = profilerStub->StopSession(&stopContext, stopRequest, &stopResponse);
236     if (!status.ok()) {
237         return false;
238     }
239 
240     return true;
241 }
242 
CheckDestroySession(std::unique_ptr<IProfilerService::Stub> & profilerStub,uint32_t & sessionId)243 bool CheckDestroySession(std::unique_ptr<IProfilerService::Stub>& profilerStub, uint32_t& sessionId)
244 {
245     DestroySessionRequest destroyRequest;
246     DestroySessionResponse destroyResponse;
247     grpc::ClientContext destroyContext;
248     destroyRequest.set_session_id(sessionId);
249     grpc::Status status = profilerStub->DestroySession(&destroyContext, destroyRequest, &destroyResponse);
250     if (!status.ok()) {
251         return false;
252     }
253 
254     return true;
255 }
256 
DoCapture(const std::string & configFile,const std::string & keepSecond,const std::string & outputFile)257 bool DoCapture(const std::string& configFile, const std::string& keepSecond, const std::string& outputFile)
258 {
259     auto profilerStub = GetProfilerServiceStub();
260     if (profilerStub == nullptr) {
261         printf("Get profiler service stub failed!\n");
262         return 0;
263     }
264 
265     uint32_t sessionId = CreateSession(profilerStub, configFile, keepSecond, outputFile);
266     if (sessionId == 0) {
267         printf("Create session returns Id 0\n");
268         return false;
269     }
270 
271     // 开启心跳线程确保会话正常,睡眠3s下发一次5s超时心跳
272     bool sendHeart = true;
273     std::thread keepSessionThread([&]() {
274         while (sendHeart) {
275             KeepSessionRequest keepRequest;
276             keepRequest.set_request_id(0);
277             keepRequest.set_session_id(sessionId);
278             keepRequest.set_keep_alive_time(KEEP_SESSION_TIMEOUT_MS);
279             grpc::ClientContext keepContext;
280             KeepSessionResponse keepResponse;
281             profilerStub->KeepSession(&keepContext, keepRequest, &keepResponse);
282             std::this_thread::sleep_for(std::chrono::seconds(KEEP_SESSION_SLEEP_SECOND));
283         }
284     });
285 
286     if (!CheckStartSession(profilerStub, sessionId)) {
287         return false;
288     }
289 
290     printf("tracing %u ms....\n", g_sampleDuration);
291     std::this_thread::sleep_for(std::chrono::milliseconds(g_sampleDuration));
292 
293     if (!CheckStopSession(profilerStub, sessionId)) {
294         sendHeart = false;
295         keepSessionThread.join();
296         return false;
297     }
298     printf("StopSession done!\n");
299 
300     if (!CheckDestroySession(profilerStub, sessionId)) {
301         sendHeart = false;
302         keepSessionThread.join();
303         return false;
304     }
305     printf("DestroySession done!\n");
306 
307     sendHeart = false;
308     keepSessionThread.join();
309     return true;
310 }
311 
312 struct DataContext {
313     bool isGetGrpcAddr = false;
314     std::string traceKeepSecond;
315     std::string configFile;
316     std::string outputFile;
317     bool isHelp = false;
318     bool isShowPluginList = false;
319     bool isStartProcess = false;
320     bool isKillProcess = false;
321 };
322 
ParseCmdline(CommandLine & cmdLine,DataContext & data)323 void ParseCmdline(CommandLine& cmdLine, DataContext& data)
324 {
325     cmdLine.AddParamSwitch("--getport", "-q", data.isGetGrpcAddr, "get grpc address");
326     cmdLine.AddParamText("--time", "-t", data.traceKeepSecond, "trace time");
327     cmdLine.AddParamText("--out", "-o", data.outputFile, "output file name");
328     cmdLine.AddParamSwitch("--help", "-h", data.isHelp, "make some help");
329     cmdLine.AddParamSwitch("--list", "-l", data.isShowPluginList, "plugin list");
330     cmdLine.AddParamSwitch("--start", "-s", data.isStartProcess, "start dependent process");
331     cmdLine.AddParamSwitch("--kill", "-k", data.isKillProcess, "kill dependent process");
332     cmdLine.AddParamText("--config", "-c", data.configFile, "start trace by config file");
333 }
334 
CheckGrpcMsgSend()335 int CheckGrpcMsgSend()
336 {
337     auto profilerStub = GetProfilerServiceStub();
338     if (profilerStub == nullptr) {
339         printf("Get profiler service stub failed!\n");
340         return -1;
341     }
342 
343     GetCapabilitiesRequest request;
344     GetCapabilitiesResponse response;
345     request.set_request_id(0);
346 
347     grpc::ClientContext context;
348     grpc::Status status = profilerStub->GetCapabilities(&context, request, &response);
349     if (!status.ok()) {
350         printf("Service not started\n");
351         return -1;
352     }
353 
354     printf("OK\n");
355     printf("ip:%s\n", GetLoopbackAddress().c_str());
356     printf("port:%u\n", GetServicePort());
357     return 0;
358 }
359 
StartDependentProcess()360 bool StartDependentProcess()
361 {
362     constexpr int waitProcMills = 300;
363     if (getuid() == 0) {
364         if (!COMMON::IsProcessExist(HIPROFILERD_NAME, g_hiprofilerdPid)) {
365             // need start hiprofilerd
366             std::vector<char*> argvVec;
367             argvVec.push_back(const_cast<char*>(HIPROFILERD_NAME.c_str()));
368             g_hiprofilerdPid = COMMON::StartProcess(HIPROFILERD_NAME, argvVec);
369             // Wait for the hiprofilerd to start
370             std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
371         }
372 
373         if (!COMMON::IsProcessExist(HIPROFILER_PLUGINS_NAME, g_hiprofilerPluginsPid)) {
374             // need start hiprofiler_plugins
375             std::vector<char*> argvVec;
376             argvVec.push_back(const_cast<char*>(HIPROFILER_PLUGINS_NAME.c_str()));
377             g_hiprofilerPluginsPid = COMMON::StartProcess(HIPROFILER_PLUGINS_NAME, argvVec);
378             // Wait for the hiprofiler_plugins add preset plugin
379             std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
380         }
381 
382         if (!COMMON::IsProcessExist(NATIVE_DAEMON_NAME, g_nativeDaemonPid)) {
383             // need start native_daemon
384             std::vector<char*> argvVec;
385             argvVec.push_back(const_cast<char*>(NATIVE_DAEMON_NAME.c_str()));
386             g_nativeDaemonPid = COMMON::StartProcess(NATIVE_DAEMON_NAME, argvVec);
387             // Wait for the native_daemon to start
388             std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
389         }
390     } else {
391         OHOS::system::SetParameter("hiviewdfx.hiprofiler.profilerd.start", "0");
392         OHOS::system::SetParameter("hiviewdfx.hiprofiler.plugins.start", "0");
393         OHOS::system::SetParameter("hiviewdfx.hiprofiler.native_memoryd.start", "0");
394 
395         OHOS::system::SetParameter("hiviewdfx.hiprofiler.profilerd.start", "1");
396         std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
397         OHOS::system::SetParameter("hiviewdfx.hiprofiler.plugins.start", "1");
398         std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
399         OHOS::system::SetParameter("hiviewdfx.hiprofiler.native_memoryd.start", "1");
400         std::this_thread::sleep_for(std::chrono::milliseconds(waitProcMills));
401     }
402 
403     std::string content = "";
404     GetCapabilities(content, true);
405     if (content == "") {
406         printf("Please confirm whether the plugin exists\n");
407         return false;
408     }
409 
410     return true;
411 }
412 
KillDependentProcess()413 void KillDependentProcess()
414 {
415     if (getuid() == 0) {
416         // if pid is equal to -1, need to get pid first.
417         if (g_nativeDaemonPid == -1) {
418             COMMON::IsProcessExist(NATIVE_DAEMON_NAME, g_nativeDaemonPid);
419         }
420         if (g_hiprofilerPluginsPid == -1) {
421             COMMON::IsProcessExist(HIPROFILER_PLUGINS_NAME, g_hiprofilerPluginsPid);
422         }
423         if (g_hiprofilerdPid == -1) {
424             COMMON::IsProcessExist(HIPROFILERD_NAME, g_hiprofilerdPid);
425         }
426 
427         COMMON::KillProcess(g_nativeDaemonPid);
428         COMMON::KillProcess(g_hiprofilerPluginsPid);
429         COMMON::KillProcess(g_hiprofilerdPid);
430     } else {
431         OHOS::system::SetParameter("hiviewdfx.hiprofiler.profilerd.start", "0");
432         OHOS::system::SetParameter("hiviewdfx.hiprofiler.plugins.start", "0");
433         OHOS::system::SetParameter("hiviewdfx.hiprofiler.native_memoryd.start", "0");
434     }
435 }
436 
ParseConfig(const std::string & configFile,std::string & config)437 bool ParseConfig(const std::string& configFile, std::string& config)
438 {
439     std::string configFileWithPath = configFile;
440     if (configFile.find('/') == std::string::npos) {
441         std::string path("/data/local/tmp/");
442         configFileWithPath = path + configFile; // add default path
443     }
444 
445     printf("Read config from %s\n", configFileWithPath.c_str());
446     std::vector<std::string> validPaths = { "/data/local/tmp/" };
447     if (!COMMON::ReadFile(configFileWithPath, validPaths, config)) {
448         printf("Read %s fail, please place it under \'/data/local/tmp/\'.\n", configFile.c_str());
449         return false;
450     }
451     config = ParsePluginConfig::GetInstance().GetPluginsConfig(config);
452     if (config.empty()) {
453         printf("Error config file: %s\n", configFileWithPath.c_str()); // no config in command or config file
454         return false;
455     }
456     return true;
457 }
458 } // namespace
459 
main(int argc,char * argv[])460 int main(int argc, char* argv[])
461 {
462     std::string config = "";
463     while (true) {
464         struct option long_options[] = {
465             {"getport", no_argument, nullptr, 'q'},
466             {"time", required_argument, nullptr, 't'},
467             {"out", required_argument, nullptr, 'o'},
468             {"help", no_argument, nullptr, 'h'},
469             {"list", no_argument, nullptr, 'l'},
470             {"start", no_argument,  nullptr, 's'},
471             {"kill", no_argument,  nullptr, 'k'},
472             {"config", required_argument, nullptr, 'c'},
473             {nullptr, 0, nullptr, 0}
474         };
475         int option = getopt_long(argc, argv, "c:t:o:qhlsk", long_options, nullptr);
476         if (option == -1) {
477             break;  // CONFIG.
478         }
479 
480         if (option == 'c' && strcmp(optarg, "-") == 0) {
481             std::string content;
482             std::istreambuf_iterator<char> begin(std::cin);
483             std::istreambuf_iterator<char> end = {};
484             content.assign(begin, end);
485             config = ParsePluginConfig::GetInstance().GetPluginsConfig(content);
486             if (config.empty()) {
487                 printf("Please check the configuration!\n");
488                 return -1;
489             }
490         }
491     }
492 
493     DataContext data;
494     CommandLine& cmdLine = CommandLine::GetInstance();
495     ParseCmdline(cmdLine, data);
496 
497     std::vector<std::string> argvVector;
498     for (int i = 0; i < argc; i++) {
499         if (((i + 1) < argc) && (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) &&
500             (strcmp(argv[i + 1], "-") == 0)) {
501             i++;
502         } else {
503             argvVector.push_back(argv[i]);
504         }
505     }
506     if (argc < 1 || cmdLine.AnalyzeParam(argvVector) < 0 || data.isHelp) {
507         cmdLine.PrintHelp();
508         exit(0);
509     }
510 
511     if (config.empty() && !data.configFile.empty()) {
512         if (!ParseConfig(data.configFile, config)) {
513             return -1;
514         }
515     }
516 
517     if (data.isStartProcess) {
518         if (!StartDependentProcess()) {
519             if (data.isKillProcess) {
520                 KillDependentProcess();
521             }
522             return 0;
523         }
524     }
525 
526     if (data.isGetGrpcAddr) { // handle get port
527         int ret = CheckGrpcMsgSend();
528         if (data.isKillProcess) {
529             KillDependentProcess();
530         }
531         return ret;
532     }
533 
534     if (data.isShowPluginList) { // handle show plugin list
535         std::string content = "";
536         GetCapabilities(content, false);
537         if (data.isKillProcess) {
538             KillDependentProcess();
539         }
540         return 0;
541     }
542 
543     if (config.empty()) { // normal case
544         if (data.isKillProcess) {
545             KillDependentProcess();
546             return 1;
547         }
548         if (!data.isStartProcess) {
549             printf("config file argument must sepcified!\n");
550         }
551         return 1;
552     }
553     // do capture work
554     if (DoCapture(config, data.traceKeepSecond, data.outputFile)) {
555         printf("DONE\n");
556     }
557 
558     if (data.isKillProcess) {
559         KillDependentProcess();
560     }
561     return 0;
562 }
563