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