• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&currentTime);
162     struct tm timeInfo = {};
163     const int bufferSize = 20;
164     char timeStr[bufferSize] = {0};
165     localtime_r(&currentTime, &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