1 /*
2 * Copyright (C) 2022 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 <cinttypes>
17 #include <csignal>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <ctime>
22 #include <fcntl.h>
23 #include <fstream>
24 #include <getopt.h>
25 #include <map>
26 #include <regex>
27 #include <sstream>
28 #include <string>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <thread>
32 #include <unistd.h>
33 #include <vector>
34 #include <memory>
35 #include <iostream>
36 #include <zlib.h>
37
38 #include "hitrace_meter.h"
39 #include "common_utils.h"
40 #include "trace_collector.h"
41 #include "hitrace_osal.h"
42 #include "securec.h"
43
44 using namespace std;
45 using namespace OHOS::HiviewDFX::HitraceOsal;
46
47 namespace {
48
49 struct TraceArgs {
50 std::string tags;
51 std::string tagGroups;
52 std::string clockType;
53 int bufferSize = 0;
54 int fileSize = 0;
55 bool overwrite = true;
56 std::string output;
57
58 int duration = 0;
59 bool isCompress = false;
60 };
61
62 enum RunningState {
63 /* Initial value */
64 STATE_NULL = 0,
65
66 /* Record a short trace */
67 RECORDING_SHORT_TEXT = 1, // --text
68 RECORDING_SHORT_RAW = 2, // --raw
69
70 /* Record a long trace */
71 RECORDING_LONG_BEGIN = 10, // --trace_begin
72 RECORDING_LONG_DUMP = 11, // --trace_dump
73 RECORDING_LONG_FINISH = 12, // --trace_finish
74 RECORDING_LONG_FINISH_NODUMP = 13, // --trace_finish_nodump
75 RECORDING_LONG_BEGIN_RECORD = 14, // --trace_begin --record
76 RECORDING_LONG_FINISH_RECORD = 15, // --trace_finish --record
77
78 /* Manipulating trace services in snapshot mode */
79 SNAPSHOT_START = 20, // --start_bgsrv
80 SNAPSHOT_DUMP = 21, // --dump_bgsrv
81 SNAPSHOT_STOP = 22, // --stop_bgsrv
82
83 /* Help Info */
84 SHOW_HELP = 31, // -h, --help
85 SHOW_LIST_CATEGORY = 32, // -l, --list_categories
86 };
87
88 const std::map<RunningState, std::string> STATE_INFO = {
89 { STATE_NULL, "STATE_NULL" },
90 { RECORDING_SHORT_TEXT, "RECORDING_SHORT_TEXT" },
91 { RECORDING_SHORT_RAW, "RECORDING_SHORT_RAW" },
92 { RECORDING_LONG_BEGIN, "RECORDING_LONG_BEGIN" },
93 { RECORDING_LONG_DUMP, "RECORDING_LONG_DUMP" },
94 { RECORDING_LONG_FINISH_NODUMP, "RECORDING_LONG_FINISH_NODUMP" },
95 { RECORDING_LONG_BEGIN_RECORD, "RECORDING_LONG_BEGIN_RECORD" },
96 { RECORDING_LONG_FINISH_RECORD, "RECORDING_LONG_FINISH_RECORD" },
97 { SNAPSHOT_START, "SNAPSHOT_START" },
98 { SNAPSHOT_DUMP, "SNAPSHOT_DUMP" },
99 { SNAPSHOT_STOP, "SNAPSHOT_STOP" },
100 { SHOW_HELP, "SHOW_HELP" },
101 { SHOW_LIST_CATEGORY, "SHOW_LIST_CATEGORY" },
102 };
103
104 constexpr struct option LONG_OPTIONS[] = {
105 { "buffer_size", required_argument, nullptr, 0 },
106 { "trace_clock", required_argument, nullptr, 0 },
107 { "help", no_argument, nullptr, 0 },
108 { "output", required_argument, nullptr, 0 },
109 { "time", required_argument, nullptr, 0 },
110 { "text", no_argument, nullptr, 0 },
111 { "raw", no_argument, nullptr, 0 },
112 { "trace_begin", no_argument, nullptr, 0 },
113 { "trace_finish", no_argument, nullptr, 0 },
114 { "trace_finish_nodump", no_argument, nullptr, 0 },
115 { "record", no_argument, nullptr, 0 },
116 { "trace_dump", no_argument, nullptr, 0 },
117 { "list_categories", no_argument, nullptr, 0 },
118 { "overwrite", no_argument, nullptr, 0 },
119 { "start_bgsrv", no_argument, nullptr, 0 },
120 { "dump_bgsrv", no_argument, nullptr, 0 },
121 { "stop_bgsrv", no_argument, nullptr, 0 },
122 { "file_size", required_argument, nullptr, 0 },
123 { nullptr, 0, nullptr, 0 },
124 };
125 const unsigned int CHUNK_SIZE = 65536;
126 const int SHELL_UID = 2000;
127
128 constexpr const char *TRACE_TAG_PROPERTY = "debug.hitrace.tags.enableflags";
129
130 // various operating paths of ftrace
131 constexpr const char *TRACING_ON_PATH = "tracing_on";
132 constexpr const char *TRACE_PATH = "trace";
133 constexpr const char *TRACE_MARKER_PATH = "trace_marker";
134
135 // support customization of some parameters
136 const int KB_PER_MB = 1024;
137 const int MIN_BUFFER_SIZE = 256;
138 const int MAX_BUFFER_SIZE = 307200; // 300 MB
139 const int HM_MAX_BUFFER_SIZE = 1024 * KB_PER_MB; // 1024 MB
140 const int DEFAULT_BUFFER_SIZE = 18432; // 18 MB
141 constexpr unsigned int MAX_OUTPUT_LEN = 255;
142 const int PAGE_SIZE_KB = 4; // 4 KB
143 const int MIN_FILE_SIZE = 51200; // 50 MB
144 const int MAX_FILE_SIZE = 512000; // 500 MB
145
146 string g_traceRootPath;
147 string g_traceHmDir;
148
149 std::shared_ptr<OHOS::HiviewDFX::UCollectClient::TraceCollector> g_traceCollector;
150
151 TraceArgs g_traceArgs;
152 std::map<std::string, OHOS::HiviewDFX::Hitrace::TagCategory> g_allTags;
153 std::map<std::string, std::vector<std::string>> g_allTagGroups;
154 RunningState g_runningState = STATE_NULL;
155 }
156
ConsoleLog(const std::string & logInfo)157 static void ConsoleLog(const std::string& logInfo)
158 {
159 // get localtime
160 time_t currentTime;
161 time(¤tTime);
162 struct tm timeInfo = {};
163 const int bufferSize = 20;
164 char timeStr[bufferSize] = {0};
165 localtime_r(¤tTime, &timeInfo);
166 strftime(timeStr, bufferSize, "%Y/%m/%d %H:%M:%S", &timeInfo);
167 std::cout << timeStr << " " << logInfo << std::endl;
168 }
169
GetStateInfo(const RunningState state)170 static std::string GetStateInfo(const RunningState state)
171 {
172 if (STATE_INFO.find(state) == STATE_INFO.end()) {
173 ConsoleLog("error: running_state is invalid.");
174 return "";
175 }
176 return STATE_INFO.at(state);
177 }
178
IsTraceMounted()179 static bool IsTraceMounted()
180 {
181 const string debugfsPath = "/sys/kernel/debug/tracing/";
182 const string tracefsPath = "/sys/kernel/tracing/";
183
184 if (access((debugfsPath + TRACE_MARKER_PATH).c_str(), F_OK) != -1) {
185 g_traceRootPath = debugfsPath;
186 return true;
187 }
188 if (access((tracefsPath + TRACE_MARKER_PATH).c_str(), F_OK) != -1) {
189 g_traceRootPath = tracefsPath;
190 return true;
191 }
192 return false;
193 }
194
WriteStrToFile(const string & filename,const std::string & str)195 static bool WriteStrToFile(const string& filename, const std::string& str)
196 {
197 ofstream out;
198 std::string inSpecPath =
199 OHOS::HiviewDFX::Hitrace::CanonicalizeSpecPath((g_traceRootPath + g_traceHmDir + filename).c_str());
200 out.open(inSpecPath, ios::out);
201 if (out.fail()) {
202 ConsoleLog("error: open " + inSpecPath + " failed.");
203 return false;
204 }
205 out << str;
206 if (out.bad()) {
207 ConsoleLog("error: can not write " + inSpecPath);
208 out.close();
209 return false;
210 }
211 out.flush();
212 out.close();
213 return true;
214 }
215
SetFtraceEnabled(const string & path,bool enabled)216 static bool SetFtraceEnabled(const string& path, bool enabled)
217 {
218 return WriteStrToFile(path, enabled ? "1" : "0");
219 }
220
SetProperty(const string & property,const string & value)221 static bool SetProperty(const string& property, const string& value)
222 {
223 return SetPropertyInner(property, value);
224 }
225
SetTraceTagsEnabled(uint64_t tags)226 static bool SetTraceTagsEnabled(uint64_t tags)
227 {
228 string value = std::to_string(tags);
229 return SetProperty(TRACE_TAG_PROPERTY, value);
230 }
231
ShowListCategory()232 static void ShowListCategory()
233 {
234 printf(" %18s description:\n", "tagName:");
235 for (auto it = g_allTags.begin(); it != g_allTags.end(); ++it) {
236 printf(" %18s - %s\n", it->first.c_str(), it->second.description.c_str());
237 }
238 }
239
ShowHelp(const string & cmd)240 static void ShowHelp(const string& cmd)
241 {
242 printf("usage: %s [options] [categories...]\n", cmd.c_str());
243 printf("options include:\n"
244 " -b N Sets the size of the buffer (KB) for storing and reading traces. The default \n"
245 " buffer size is 2048 KB.\n"
246 " --buffer_size N Like \"-b N\".\n"
247 " -l Lists available hitrace categories.\n"
248 " --list_categories Like \"-l\".\n"
249 " -t N Sets the hitrace running duration in seconds (5s by default), which depends on \n"
250 " the time required for analysis.\n"
251 " --time N Like \"-t N\".\n"
252 " --trace_clock clock\n"
253 " Sets the type of the clock for adding a timestamp to a trace, which can be\n"
254 " boot (default), global, mono, uptime, or perf.\n"
255 " --trace_begin Starts capturing traces.\n"
256 " --trace_dump Dumps traces to a specified path (stdout by default).\n"
257 " --trace_finish Stops capturing traces and dumps traces to a specified path (stdout by default).\n"
258 " --trace_finish_nodump\n"
259 " Stops capturing traces and not dumps traces.\n"
260 " --record Enable or disable long-term trace collection tasks in conjunction with\n"
261 " \"--trace_begin\" and \"--trace_finish\".\n"
262 " --overwrite Sets the action to take when the buffer is full. If this option is used,\n"
263 " the latest traces are discarded; if this option is not used (default setting),\n"
264 " the earliest traces are discarded.\n"
265 " -o filename Specifies the name of the target file (stdout by default).\n"
266 " --output filename\n"
267 " Like \"-o filename\".\n"
268 " -z Compresses a captured trace.\n"
269 " --text Specify the output format of trace as text.\n"
270 " --raw Specify the output format of trace as raw trace, the default format is text.\n"
271 " --start_bgsrv Enable trace_service in snapshot mode.\n"
272 " --dump_bgsrv Trigger the dump trace task of the trace_service.\n"
273 " --stop_bgsrv Disable trace_service in snapshot mode.\n"
274 " --file_size Sets the size of the raw trace (KB). The default file size is 102400 KB.\n"
275 " Only effective in raw trace mode\n"
276 );
277 }
278
279 template <typename T>
StrToNum(const std::string & sString,T & tX)280 inline bool StrToNum(const std::string& sString, T &tX)
281 {
282 std::istringstream iStream(sString);
283 return (iStream >> tX) ? true : false;
284 }
285
SetRunningState(const RunningState & setValue)286 static bool SetRunningState(const RunningState& setValue)
287 {
288 if (g_runningState != STATE_NULL) {
289 ConsoleLog("error: the parameter is set incorrectly, " + GetStateInfo(g_runningState) +
290 " and " + GetStateInfo(setValue) + " cannot coexist.");
291 return false;
292 }
293 g_runningState = setValue;
294 return true;
295 }
296
CheckOutputFile(const char * path)297 static bool CheckOutputFile(const char* path)
298 {
299 struct stat buf;
300 size_t len = strnlen(path, MAX_OUTPUT_LEN);
301 if (len == MAX_OUTPUT_LEN || len < 1 || (stat(path, &buf) == 0 && (buf.st_mode & S_IFDIR))) {
302 ConsoleLog("error: output file is illegal");
303 return false;
304 }
305 g_traceArgs.output = path;
306 return true;
307 }
308
ParseLongOpt(const string & cmd,int optionIndex)309 static bool ParseLongOpt(const string& cmd, int optionIndex)
310 {
311 bool isTrue = true;
312 if (!strcmp(LONG_OPTIONS[optionIndex].name, "buffer_size")) {
313 int bufferSizeKB = 0;
314 int maxBufferSizeKB = MAX_BUFFER_SIZE;
315 if (g_traceHmDir != "") {
316 maxBufferSizeKB = HM_MAX_BUFFER_SIZE;
317 }
318 if (!StrToNum(optarg, bufferSizeKB)) {
319 ConsoleLog("error: buffer size is illegal input. eg: \"--buffer_size 1024\".");
320 isTrue = false;
321 } else if (bufferSizeKB < MIN_BUFFER_SIZE || bufferSizeKB > maxBufferSizeKB) {
322 ConsoleLog("error: buffer size must be from 256 KB to " + std::to_string(maxBufferSizeKB / KB_PER_MB) +
323 " MB. eg: \"--buffer_size 1024\".");
324 isTrue = false;
325 }
326 g_traceArgs.bufferSize = bufferSizeKB / PAGE_SIZE_KB * PAGE_SIZE_KB;
327 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_clock")) {
328 regex re("[a-zA-Z]{4,6}");
329 if (regex_match(optarg, re)) {
330 g_traceArgs.clockType = optarg;
331 } else {
332 ConsoleLog("error: \"--trace_clock\" is illegal input. eg: \"--trace_clock boot\".");
333 isTrue = false;
334 }
335 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "help")) {
336 isTrue = SetRunningState(SHOW_HELP);
337 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "time")) {
338 if (!StrToNum(optarg, g_traceArgs.duration)) {
339 ConsoleLog("error: the time is illegal input. eg: \"--time 5\".");
340 isTrue = false;
341 } else if (g_traceArgs.duration < 1) {
342 ConsoleLog("error: \"-t " + std::string(optarg) + "\" to be greater than zero. eg: \"--time 5\".");
343 isTrue = false;
344 }
345 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "list_categories")) {
346 isTrue = SetRunningState(SHOW_LIST_CATEGORY);
347 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "output")) {
348 isTrue = CheckOutputFile(optarg);
349 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "overwrite")) {
350 g_traceArgs.overwrite = false;
351 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_begin")) {
352 isTrue = SetRunningState(RECORDING_LONG_BEGIN);
353 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_finish")) {
354 isTrue = SetRunningState(RECORDING_LONG_FINISH);
355 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_finish_nodump")) {
356 isTrue = SetRunningState(RECORDING_LONG_FINISH_NODUMP);
357 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_dump")) {
358 isTrue = SetRunningState(RECORDING_LONG_DUMP);
359 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "record")) {
360 if (g_runningState == RECORDING_LONG_BEGIN) {
361 g_runningState = RECORDING_LONG_BEGIN_RECORD;
362 } else if (g_runningState == RECORDING_LONG_FINISH) {
363 g_runningState = RECORDING_LONG_FINISH_RECORD;
364 } else {
365 ConsoleLog("error: \"--record\" is set incorrectly. eg: \"--trace_begin --record\","
366 " \"--trace_finish --record\".");
367 isTrue = false;
368 }
369 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "start_bgsrv")) {
370 isTrue = SetRunningState(SNAPSHOT_START);
371 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "dump_bgsrv")) {
372 isTrue = SetRunningState(SNAPSHOT_DUMP);
373 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "stop_bgsrv")) {
374 isTrue = SetRunningState(SNAPSHOT_STOP);
375 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "text")) {
376 isTrue = SetRunningState(RECORDING_SHORT_TEXT);
377 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "raw")) {
378 isTrue = SetRunningState(RECORDING_SHORT_RAW);
379 } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "file_size")) {
380 int fileSizeKB = 0;
381 if (!StrToNum(optarg, fileSizeKB)) {
382 ConsoleLog("error: file size is illegal input. eg: \"--file_size 1024\".");
383 isTrue = false;
384 } else if (fileSizeKB < MIN_FILE_SIZE || fileSizeKB > MAX_FILE_SIZE) {
385 ConsoleLog("error: file size must be from 50 MB to 500 MB. eg: \"--file_size 102400\".");
386 isTrue = false;
387 }
388 g_traceArgs.fileSize = fileSizeKB;
389 }
390
391 return isTrue;
392 }
393
ParseOpt(int opt,char ** argv,int optIndex)394 static bool ParseOpt(int opt, char** argv, int optIndex)
395 {
396 bool isTrue = true;
397 switch (opt) {
398 case 'b': {
399 int bufferSizeKB = 0;
400 int maxBufferSizeKB = MAX_BUFFER_SIZE;
401 if (g_traceHmDir != "") {
402 maxBufferSizeKB = HM_MAX_BUFFER_SIZE;
403 }
404 if (!StrToNum(optarg, bufferSizeKB)) {
405 ConsoleLog("error: buffer size is illegal input. eg: \"--buffer_size 1024\".");
406 isTrue = false;
407 } else if (bufferSizeKB < MIN_BUFFER_SIZE || bufferSizeKB > maxBufferSizeKB) {
408 ConsoleLog("error: buffer size must be from 256 KB to " + std::to_string(maxBufferSizeKB / KB_PER_MB) +
409 " MB. eg: \"--buffer_size 1024\".");
410 isTrue = false;
411 }
412 g_traceArgs.bufferSize = bufferSizeKB / PAGE_SIZE_KB * PAGE_SIZE_KB;
413 break;
414 }
415 case 'h':
416 isTrue = SetRunningState(SHOW_HELP);
417 break;
418 case 'l':
419 isTrue = SetRunningState(SHOW_LIST_CATEGORY);
420 break;
421 case 't': {
422 if (!StrToNum(optarg, g_traceArgs.duration)) {
423 ConsoleLog("error: the time is illegal input. eg: \"--time 5\".");
424 isTrue = false;
425 } else if (g_traceArgs.duration < 1) {
426 ConsoleLog("error: \"-t " + std::string(optarg) + "\" to be greater than zero. eg: \"--time 5\".");
427 isTrue = false;
428 }
429 break;
430 }
431 case 'o': {
432 isTrue = CheckOutputFile(optarg);
433 break;
434 }
435 case 'z':
436 g_traceArgs.isCompress = true;
437 break;
438 case 0: // long options
439 isTrue = ParseLongOpt(argv[0], optIndex);
440 break;
441 case '?':
442 isTrue = false;
443 break;
444 default:
445 break;
446 }
447 return isTrue;
448 }
449
AddTagItems(int argc,char ** argv)450 static bool AddTagItems(int argc, char** argv)
451 {
452 for (int i = optind; i < argc; i++) {
453 std::string tag = std::string(argv[i]);
454 if (g_allTags.find(tag) == g_allTags.end()) {
455 std::string errorInfo = "error: " + tag + " is not support category on this device.";
456 ConsoleLog(errorInfo);
457 return false;
458 }
459
460 if (i == optind) {
461 g_traceArgs.tags = tag;
462 } else {
463 g_traceArgs.tags += ("," + tag);
464 }
465 }
466 return true;
467 }
468
HandleOpt(int argc,char ** argv)469 static bool HandleOpt(int argc, char** argv)
470 {
471 bool isTrue = true;
472 int opt = 0;
473 int optionIndex = 0;
474 string shortOption = "b:c:hlo:t:z";
475 int argcSize = argc;
476 while (isTrue && argcSize-- > 0) {
477 opt = getopt_long(argc, argv, shortOption.c_str(), LONG_OPTIONS, &optionIndex);
478 if (opt < 0 && (!AddTagItems(argc, argv))) {
479 isTrue = false;
480 break;
481 }
482 isTrue = ParseOpt(opt, argv, optionIndex);
483 }
484
485 return isTrue;
486 }
487
StopTrace()488 static void StopTrace()
489 {
490 const int napTime = 10000;
491 usleep(napTime);
492 SetTraceTagsEnabled(0);
493 SetFtraceEnabled(TRACING_ON_PATH, false);
494 }
495
DumpCompressedTrace(int traceFd,int outFd)496 static void DumpCompressedTrace(int traceFd, int outFd)
497 {
498 z_stream zs { nullptr };
499 int flush = Z_NO_FLUSH;
500 ssize_t bytesWritten;
501 ssize_t bytesRead;
502 if (memset_s(&zs, sizeof(zs), 0, sizeof(zs)) != 0) {
503 ConsoleLog("error: zip stream buffer init failed.");
504 return;
505 }
506 int ret = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
507 if (ret != Z_OK) {
508 ConsoleLog("error: initializing zlib failed ret " + std::to_string(ret));
509 return;
510 }
511 std::unique_ptr<uint8_t[]> in = std::make_unique<uint8_t[]>(CHUNK_SIZE);
512 std::unique_ptr<uint8_t[]> out = std::make_unique<uint8_t[]>(CHUNK_SIZE);
513 if (!in || !out) {
514 ConsoleLog("error: couldn't allocate buffers.");
515 return;
516 }
517 zs.next_out = reinterpret_cast<Bytef*>(out.get());
518 zs.avail_out = CHUNK_SIZE;
519
520 do {
521 if (zs.avail_in == 0 && flush == Z_NO_FLUSH) {
522 bytesRead = TEMP_FAILURE_RETRY(read(traceFd, in.get(), CHUNK_SIZE));
523 if (bytesRead == 0) {
524 flush = Z_FINISH;
525 } else if (bytesRead == -1) {
526 ConsoleLog("error: reading trace, errno " + std::to_string(errno));
527 break;
528 } else {
529 zs.next_in = reinterpret_cast<Bytef*>(in.get());
530 zs.avail_in = bytesRead;
531 }
532 }
533 if (zs.avail_out == 0) {
534 bytesWritten = TEMP_FAILURE_RETRY(write(outFd, out.get(), CHUNK_SIZE));
535 if (bytesWritten < static_cast<ssize_t>(CHUNK_SIZE)) {
536 ConsoleLog("error: writing deflated trace, errno " + std::to_string(errno));
537 break;
538 }
539 zs.next_out = reinterpret_cast<Bytef*>(out.get());
540 zs.avail_out = CHUNK_SIZE;
541 }
542 ret = deflate(&zs, flush);
543 if (flush == Z_FINISH && ret == Z_STREAM_END) {
544 size_t have = CHUNK_SIZE - zs.avail_out;
545 bytesWritten = TEMP_FAILURE_RETRY(write(outFd, out.get(), have));
546 if (static_cast<size_t>(bytesWritten) < have) {
547 ConsoleLog("error: writing deflated trace, errno " + std::to_string(errno));
548 }
549 break;
550 } else if (ret != Z_OK) {
551 if (ret == Z_ERRNO) {
552 ConsoleLog("error: deflate failed with errno " + std::to_string(errno));
553 } else {
554 ConsoleLog("error: deflate failed return " + std::to_string(ret));
555 }
556 break;
557 }
558 } while (ret == Z_OK);
559
560 ret = deflateEnd(&zs);
561 if (ret != Z_OK) {
562 ConsoleLog("error: cleaning up zlib return " + std::to_string(ret));
563 }
564 }
565
DumpTrace()566 static void DumpTrace()
567 {
568 std::string tracePath = g_traceRootPath + g_traceHmDir + TRACE_PATH;
569 string traceSpecPath = OHOS::HiviewDFX::Hitrace::CanonicalizeSpecPath(tracePath.c_str());
570 int traceFd = open(traceSpecPath.c_str(), O_RDONLY);
571 if (traceFd == -1) {
572 ConsoleLog("error: opening " + tracePath + ", errno: " + std::to_string(errno));
573 return;
574 }
575
576 int outFd = STDOUT_FILENO;
577 if (g_traceArgs.output.size() > 0) {
578 string outSpecPath = OHOS::HiviewDFX::Hitrace::CanonicalizeSpecPath(g_traceArgs.output.c_str());
579 outFd = open(outSpecPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
580 }
581
582 if (outFd == -1) {
583 ConsoleLog("error: opening " + g_traceArgs.output + ", errno: " + std::to_string(errno));
584 close(traceFd);
585 return;
586 }
587
588 ssize_t bytesWritten;
589 ssize_t bytesRead;
590 if (g_traceArgs.isCompress) {
591 DumpCompressedTrace(traceFd, outFd);
592 } else {
593 const int blockSize = 4096;
594 char buffer[blockSize];
595 do {
596 bytesRead = TEMP_FAILURE_RETRY(read(traceFd, buffer, blockSize));
597 if ((bytesRead == 0) || (bytesRead == -1)) {
598 break;
599 }
600 bytesWritten = TEMP_FAILURE_RETRY(write(outFd, buffer, bytesRead));
601 } while (bytesWritten > 0);
602 }
603
604 if (outFd != STDOUT_FILENO) {
605 ConsoleLog("trace read done, output: " + g_traceArgs.output);
606 close(outFd);
607 }
608 close(traceFd);
609 }
610
InitAllSupportTags()611 static bool InitAllSupportTags()
612 {
613 if (!OHOS::HiviewDFX::Hitrace::ParseTagInfo(g_allTags, g_allTagGroups)) {
614 ConsoleLog("error: hitrace_utils.json file is damaged.");
615 return false;
616 }
617 return true;
618 }
619
ReloadTraceArgs()620 static std::string ReloadTraceArgs()
621 {
622 if (g_traceArgs.tags.size() == 0) {
623 ConsoleLog("error: tag is empty, please add.");
624 return "";
625 }
626 std::string args = "tags:" + g_traceArgs.tags;
627
628 if (g_traceArgs.bufferSize > 0) {
629 args += (" bufferSize:" + std::to_string(g_traceArgs.bufferSize));
630 } else {
631 args += (" bufferSize:" + std::to_string(DEFAULT_BUFFER_SIZE));
632 }
633
634 if (g_traceArgs.clockType.size() > 0) {
635 args += (" clockType:" + g_traceArgs.clockType);
636 }
637
638 if (g_traceArgs.overwrite) {
639 args += " overwrite:";
640 args += "1";
641 } else {
642 args += " overwrite:";
643 args += "0";
644 }
645
646 if (g_traceArgs.fileSize > 0) {
647 if (g_runningState == RECORDING_SHORT_RAW || g_runningState == RECORDING_LONG_BEGIN_RECORD) {
648 args += (" fileSize:" + std::to_string(g_traceArgs.fileSize));
649 } else {
650 ConsoleLog("warnning: The current state does not support specifying the file size, file size: " +
651 std::to_string(g_traceArgs.fileSize) + " is invalid.");
652 }
653 }
654
655 if (g_runningState != RECORDING_SHORT_TEXT) {
656 ConsoleLog("args: " + args);
657 }
658 return args;
659 }
660
HandleRecordingShortRaw()661 static bool HandleRecordingShortRaw()
662 {
663 std::string args = ReloadTraceArgs();
664 if (g_traceArgs.output.size() > 0) {
665 ConsoleLog("warnning: The current state does not support specifying the output file path, " +
666 g_traceArgs.output + " is invalid.");
667 }
668 auto openRet = g_traceCollector->OpenRecording(args);
669 if (openRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
670 ConsoleLog("error: OpenRecording failed, errorCode(" + std::to_string(openRet.retCode) +")");
671 if (openRet.retCode == OHOS::HiviewDFX::UCollect::UcError::TRACE_IS_OCCUPIED ||
672 openRet.retCode == OHOS::HiviewDFX::UCollect::UcError::TRACE_CALL_ERROR) {
673 return false;
674 } else {
675 g_traceCollector->Recover();
676 return false;
677 }
678 }
679
680 auto recOnRet = g_traceCollector->RecordingOn();
681 if (recOnRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
682 ConsoleLog("error: RecordingOn failed, errorCode(" + std::to_string(recOnRet.retCode) +")");
683 g_traceCollector->Recover();
684 return false;
685 }
686 ConsoleLog("start capture, please wait " + std::to_string(g_traceArgs.duration) +"s ...");
687 sleep(g_traceArgs.duration);
688
689 auto recOffRet = g_traceCollector->RecordingOff();
690 if (recOffRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
691 ConsoleLog("error: RecordingOff failed, errorCode(" + std::to_string(recOffRet.retCode) +")");
692 g_traceCollector->Recover();
693 return false;
694 }
695 ConsoleLog("capture done, output files:");
696 for (std::string item : recOffRet.data) {
697 std::cout << " " << item << std::endl;
698 }
699 g_traceCollector->Recover();
700 return true;
701 }
702
HandleRecordingShortText()703 static bool HandleRecordingShortText()
704 {
705 std::string args = ReloadTraceArgs();
706 auto openRet = g_traceCollector->OpenRecording(args);
707 if (openRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
708 ConsoleLog("error: OpenRecording failed, errorCode(" + std::to_string(openRet.retCode) +")");
709 if (openRet.retCode == OHOS::HiviewDFX::UCollect::UcError::TRACE_IS_OCCUPIED ||
710 openRet.retCode == OHOS::HiviewDFX::UCollect::UcError::TRACE_CALL_ERROR) {
711 return false;
712 } else {
713 g_traceCollector->Recover();
714 return false;
715 }
716 }
717 ConsoleLog("start capture, please wait " + std::to_string(g_traceArgs.duration) +"s ...");
718 sleep(g_traceArgs.duration);
719
720 OHOS::HiviewDFX::Hitrace::MarkClockSync(g_traceRootPath);
721 StopTrace();
722
723 if (g_traceArgs.output.size() > 0) {
724 ConsoleLog("capture done, start to read trace.");
725 }
726 DumpTrace();
727 g_traceCollector->Recover();
728 return true;
729 }
730
HandleRecordingLongBegin()731 static bool HandleRecordingLongBegin()
732 {
733 std::string args = ReloadTraceArgs();
734 if (g_traceArgs.output.size() > 0) {
735 ConsoleLog("warnning: The current state does not support specifying the output file path, " +
736 g_traceArgs.output + " is invalid.");
737 }
738 auto openRet = g_traceCollector->OpenRecording(args);
739 if (openRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
740 ConsoleLog("error: OpenRecording failed, errorCode(" + std::to_string(openRet.retCode) +")");
741 if (openRet.retCode == OHOS::HiviewDFX::UCollect::UcError::TRACE_IS_OCCUPIED ||
742 openRet.retCode == OHOS::HiviewDFX::UCollect::UcError::TRACE_CALL_ERROR) {
743 return false;
744 } else {
745 g_traceCollector->Recover();
746 return false;
747 }
748 }
749 ConsoleLog("OpenRecording done.");
750 return true;
751 }
752
HandleRecordingLongDump()753 static bool HandleRecordingLongDump()
754 {
755 OHOS::HiviewDFX::Hitrace::MarkClockSync(g_traceRootPath);
756 ConsoleLog("start to read trace.");
757 DumpTrace();
758 return true;
759 }
760
HandleRecordingLongFinish()761 static bool HandleRecordingLongFinish()
762 {
763 OHOS::HiviewDFX::Hitrace::MarkClockSync(g_traceRootPath);
764 StopTrace();
765 ConsoleLog("start to read trace.");
766 DumpTrace();
767 g_traceCollector->Recover();
768 return true;
769 }
770
HandleRecordingLongFinishNodump()771 static bool HandleRecordingLongFinishNodump()
772 {
773 g_traceCollector->Recover();
774 ConsoleLog("end capture trace.");
775 return true;
776 }
777
HandleRecordingLongBeginRecord()778 static bool HandleRecordingLongBeginRecord()
779 {
780 std::string args = ReloadTraceArgs();
781 if (g_traceArgs.output.size() > 0) {
782 ConsoleLog("warnning: The current state does not support specifying the output file path, " +
783 g_traceArgs.output + " is invalid.");
784 }
785 auto openRet = g_traceCollector->OpenRecording(args);
786 if (openRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
787 ConsoleLog("error: OpenRecording failed, errorCode(" + std::to_string(openRet.retCode) +")");
788 g_traceCollector->Recover();
789 return false;
790 }
791
792 auto recOnRet = g_traceCollector->RecordingOn();
793 if (recOnRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
794 ConsoleLog("error: RecordingOn failed, errorCode(" + std::to_string(recOnRet.retCode) +")");
795 g_traceCollector->Recover();
796 return false;
797 }
798 ConsoleLog("trace capturing. ");
799 return true;
800 }
801
HandleRecordingLongFinishRecord()802 static bool HandleRecordingLongFinishRecord()
803 {
804 auto recOffRet = g_traceCollector->RecordingOff();
805 if (recOffRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
806 ConsoleLog("error: RecordingOff failed, errorCode(" + std::to_string(recOffRet.retCode) +")");
807 g_traceCollector->Recover();
808 return false;
809 }
810 ConsoleLog("capture done, output files:");
811 for (std::string item : recOffRet.data) {
812 std::cout << " " << item << std::endl;
813 }
814 g_traceCollector->Recover();
815 return true;
816 }
817
HandleOpenSnapshot()818 static bool HandleOpenSnapshot()
819 {
820 std::vector<std::string> tagGroups = { "scene_performance" };
821 auto openRet = g_traceCollector->OpenSnapshot(tagGroups);
822 if (openRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
823 ConsoleLog("error: OpenSnapshot failed, errorCode(" + std::to_string(openRet.retCode) +")");
824 if (openRet.retCode == OHOS::HiviewDFX::UCollect::UcError::TRACE_IS_OCCUPIED ||
825 openRet.retCode == OHOS::HiviewDFX::UCollect::UcError::TRACE_CALL_ERROR) {
826 return false;
827 } else {
828 g_traceCollector->Recover();
829 return false;
830 }
831 }
832 ConsoleLog("OpenSnapshot done.");
833 return true;
834 }
835
HandleDumpSnapshot()836 static bool HandleDumpSnapshot()
837 {
838 bool isSuccess = true;
839 auto dumpRet = g_traceCollector->DumpSnapshot(OHOS::HiviewDFX::UCollectClient::TraceCollector::Caller::DEVELOP);
840 if (dumpRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
841 ConsoleLog("error: DumpSnapshot failed, errorCode(" + std::to_string(dumpRet.retCode) +")");
842 isSuccess = false;
843 } else {
844 ConsoleLog("DumpSnapshot done, output:");
845 for (std::string item : dumpRet.data) {
846 std::cout << " " << item << std::endl;
847 }
848 }
849 return isSuccess;
850 }
851
HandlCloseSnapshot()852 static bool HandlCloseSnapshot()
853 {
854 bool isSuccess = true;
855 auto closeRet = g_traceCollector->Close();
856 if (closeRet.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
857 ConsoleLog("error: CloseSnapshot failed, errorCode(" + std::to_string(closeRet.retCode) +")");
858 isSuccess = false;
859 } else {
860 ConsoleLog("CloseSnapshot done.");
861 }
862 return isSuccess;
863 }
864
InterruptExit(int signo)865 static void InterruptExit(int signo)
866 {
867 /**
868 * trace reset.
869 */
870 g_traceCollector->Recover();
871 _exit(-1);
872 }
873
main(int argc,char ** argv)874 int main(int argc, char **argv)
875 {
876 bool isSuccess = true;
877 setgid(SHELL_UID);
878 g_traceCollector = OHOS::HiviewDFX::UCollectClient::TraceCollector::Create();
879 if (g_traceCollector == nullptr) {
880 ConsoleLog("error: traceCollector create failed, exit.");
881 return -1;
882 }
883 (void)signal(SIGKILL, InterruptExit);
884 (void)signal(SIGINT, InterruptExit);
885
886 if (!IsTraceMounted()) {
887 ConsoleLog("error: trace isn't mounted, exit.");
888 return -1;
889 }
890
891 if (access((g_traceRootPath + "hongmeng/").c_str(), F_OK) != -1) {
892 g_traceHmDir = "hongmeng/";
893 }
894
895 if (!InitAllSupportTags()) {
896 return -1;
897 }
898
899 if (!HandleOpt(argc, argv)) {
900 ConsoleLog("error: parsing args failed, exit.");
901 return -1;
902 }
903
904 if (g_runningState == STATE_NULL) {
905 g_runningState = RECORDING_SHORT_TEXT;
906 }
907
908 if (g_runningState != RECORDING_SHORT_TEXT && g_runningState != RECORDING_LONG_DUMP &&
909 g_runningState != RECORDING_LONG_FINISH) {
910 ConsoleLog(std::string(argv[0]) + " enter, running_state is " + GetStateInfo(g_runningState));
911 }
912
913 switch (g_runningState) {
914 case RECORDING_SHORT_RAW:
915 isSuccess = HandleRecordingShortRaw();
916 break;
917 case RECORDING_SHORT_TEXT:
918 isSuccess = HandleRecordingShortText();
919 break;
920 case RECORDING_LONG_BEGIN:
921 isSuccess = HandleRecordingLongBegin();
922 break;
923 case RECORDING_LONG_DUMP:
924 isSuccess = HandleRecordingLongDump();
925 break;
926 case RECORDING_LONG_FINISH:
927 isSuccess = HandleRecordingLongFinish();
928 break;
929 case RECORDING_LONG_FINISH_NODUMP:
930 isSuccess = HandleRecordingLongFinishNodump();
931 break;
932 case RECORDING_LONG_BEGIN_RECORD:
933 isSuccess = HandleRecordingLongBeginRecord();
934 break;
935 case RECORDING_LONG_FINISH_RECORD:
936 isSuccess = HandleRecordingLongFinishRecord();
937 break;
938 case SNAPSHOT_START:
939 isSuccess = HandleOpenSnapshot();
940 break;
941 case SNAPSHOT_DUMP:
942 isSuccess = HandleDumpSnapshot();
943 break;
944 case SNAPSHOT_STOP:
945 isSuccess = HandlCloseSnapshot();
946 break;
947 case SHOW_HELP:
948 ShowHelp(argv[0]);
949 break;
950 case SHOW_LIST_CATEGORY:
951 ShowListCategory();
952 break;
953 default:
954 ShowHelp(argv[0]);
955 isSuccess = false;
956 break;
957 }
958 return isSuccess ? 0 : -1;
959 }
960