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(¤tTime);
190 struct tm timeInfo = {};
191 const int bufferSize = 20;
192 char timeStr[bufferSize] = {0};
193 localtime_r(¤tTime, &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