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