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