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