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