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