• 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 <zlib.h>
35 
36 #include "hitrace_meter.h"
37 #include "hitrace_osal.h"
38 #include "securec.h"
39 
40 using namespace std;
41 using namespace OHOS::HiviewDFX::HitraceOsal;
42 
43 namespace {
44 constexpr struct option LONG_OPTIONS[] = {
45     { "buffer_size",       required_argument, nullptr, 0 },
46     { "trace_clock",       required_argument, nullptr, 0 },
47     { "help",              no_argument,       nullptr, 0 },
48     { "output",            required_argument, nullptr, 0 },
49     { "time",              required_argument, nullptr, 0 },
50     { "trace_begin",       no_argument,       nullptr, 0 },
51     { "trace_finish",      no_argument,       nullptr, 0 },
52     { "trace_finish_nodump",      no_argument,       nullptr, 0 },
53     { "trace_dump",        no_argument,       nullptr, 0 },
54     { "list_categories",   no_argument,       nullptr, 0 },
55     { "overwrite",         no_argument,       nullptr, 0 },
56     { nullptr,             0,                 nullptr, 0 },
57 };
58 const unsigned int CHUNK_SIZE = 65536;
59 const int BLOCK_SIZE = 4096;
60 const int SHELL_UID = 2000;
61 const int WAIT_MILLISECONDS = 10;
62 const int SAVED_CMDLINES_SIZE = 1024;
63 
64 constexpr const char *TRACE_TAG_PROPERTY = "debug.hitrace.tags.enableflags";
65 constexpr const char *TRACE_TAG_STATE = "debug.hitrace.enable.state";
66 
67 // various operating paths of ftrace
68 constexpr const char *TRACING_ON_PATH = "tracing_on";
69 constexpr const char *TRACE_PATH = "trace";
70 constexpr const char *TRACE_MARKER_PATH = "trace_marker";
71 
72 // support customization of some parameters
73 const int MIN_BUFFER_SIZE = 256;
74 const int MAX_BUFFER_SIZE = 307200; // 300 MB
75 constexpr unsigned int MAX_OUTPUT_LEN = 255;
76 const int PAGE_SIZE_KB = 4; // 4 KB
77 int g_traceDuration = 5;
78 int g_bufferSizeKB = 2048;
79 string g_clock = "boot";
80 bool g_overwrite = true;
81 string g_outputFile;
82 bool g_compress = false;
83 
84 string g_traceRootPath;
85 
86 const unsigned int START_NONE = 0;
87 const unsigned int START_NORMAL = 1;
88 const unsigned int START_ASYNC = 2;
89 unsigned int g_traceStart = START_NORMAL;
90 bool g_traceStop = true;
91 bool g_traceDump = true;
92 
93 map<string, TagCategory> g_tagMap;
94 vector<uint64_t> g_userEnabledTags;
95 vector<string> g_kernelEnabledPaths;
96 }
97 
IsTraceMounted()98 static bool IsTraceMounted()
99 {
100     const string debugfsPath = "/sys/kernel/debug/tracing/";
101     const string tracefsPath = "/sys/kernel/tracing/";
102 
103     if (access((debugfsPath + TRACE_MARKER_PATH).c_str(), F_OK) != -1) {
104         g_traceRootPath = debugfsPath;
105         return true;
106     }
107     if (access((tracefsPath + TRACE_MARKER_PATH).c_str(), F_OK) != -1) {
108         g_traceRootPath = tracefsPath;
109         return true;
110     }
111 
112     (void)fprintf(stderr, "Error: Did not find trace folder\n");
113     return false;
114 }
115 
IsFileExit(const string & filename)116 static bool IsFileExit(const string& filename)
117 {
118     return access((g_traceRootPath + filename).c_str(), F_OK) != -1;
119 }
120 
IsWritableFile(const string & filename)121 static bool IsWritableFile(const string& filename)
122 {
123     return access((g_traceRootPath + filename).c_str(), W_OK) != -1;
124 }
125 
WriteStrToFile(const string & filename,const std::string & str)126 static bool WriteStrToFile(const string& filename, const std::string& str)
127 {
128     ofstream out;
129     out.open(g_traceRootPath + filename, ios::out);
130     if (out.fail()) {
131         fprintf(stderr, "Error: Did not open %s\n", filename.c_str());
132         return false;
133     }
134     out << str;
135     if (out.bad()) {
136         fprintf(stderr, "Error: Did not write %s\n", filename.c_str());
137         out.close();
138         return false;
139     }
140     out.flush();
141     out.close();
142     return true;
143 }
144 
SetFtraceEnabled(const string & path,bool enabled)145 static bool SetFtraceEnabled(const string& path, bool enabled)
146 {
147     return WriteStrToFile(path, enabled ? "1" : "0");
148 }
149 
IsTagSupported(const string & name)150 static bool IsTagSupported(const string& name)
151 {
152     auto it = g_tagMap.find(name);
153     if (it == g_tagMap.end()) {
154         return false;
155     }
156 
157     TagCategory tagCategory = it->second;
158     if (tagCategory.type != KERNEL) {
159         g_userEnabledTags.push_back(tagCategory.tag);
160         return true;
161     }
162 
163     bool findPath = false;
164     for (int i = 0; i < MAX_SYS_FILES; i++) {
165         string path = tagCategory.SysFiles[i].path;
166         if (path.size() == 0) {
167             continue;
168         }
169         if (IsWritableFile(path)) {
170             g_kernelEnabledPaths.push_back(std::move(path));
171             findPath = true;
172         } else if (IsFileExit(path)) {
173             fprintf(stderr, "Warning: category \"%s\" requires root "
174                 "privileges.\n", name.c_str());
175         }
176     }
177     return findPath;
178 }
179 
CanonicalizeSpecPath(const char * src)180 static string CanonicalizeSpecPath(const char* src)
181 {
182     if (src == nullptr || strlen(src) >= PATH_MAX) {
183         fprintf(stderr, "Error: CanonicalizeSpecPath failed\n");
184         return "";
185     }
186     char resolvedPath[PATH_MAX] = { 0 };
187 #if defined(_WIN32)
188     if (!_fullpath(resolvedPath, src, PATH_MAX)) {
189         fprintf(stderr, "Error: _fullpath %s failed\n", src);
190         return "";
191     }
192 #else
193     if (access(src, F_OK) == 0) {
194         if (realpath(src, resolvedPath) == nullptr) {
195             fprintf(stderr, "Error: realpath %s failed\n", src);
196             return "";
197         }
198     } else {
199         string fileName(src);
200         if (fileName.find("..") == string::npos) {
201             if (sprintf_s(resolvedPath, PATH_MAX, "%s", src) == -1) {
202                 fprintf(stderr, "Error: sprintf_s %s failed\n", src);
203                 return "";
204             }
205         } else {
206             fprintf(stderr, "Error: find .. %s failed\n", src);
207             return "";
208         }
209     }
210 #endif
211     string res(resolvedPath);
212     return res;
213 }
214 
ReadFile(const string & filename)215 static string ReadFile(const string& filename)
216 {
217     string resolvedPath = CanonicalizeSpecPath((g_traceRootPath + filename).c_str());
218     ifstream fin(resolvedPath.c_str());
219     if (!fin.is_open()) {
220         fprintf(stderr, "open file: %s failed!\n", (g_traceRootPath + filename).c_str());
221         return "";
222     }
223 
224     string str((istreambuf_iterator<char>(fin)), istreambuf_iterator<char>());
225     fin.close();
226     return str;
227 }
228 
SetBufferSize(int bufferSize)229 static bool SetBufferSize(int bufferSize)
230 {
231     const char *currentTracerPath = "current_tracer";
232     if (!WriteStrToFile(currentTracerPath, "nop")) {
233         fprintf(stderr, "Error: write \"nop\" to %s\n", currentTracerPath);
234     }
235     const char *bufferSizePath = "buffer_size_kb";
236     return WriteStrToFile(bufferSizePath, std::to_string(bufferSize));
237 }
238 
SetClock(const string & timeclock)239 static bool SetClock(const string& timeclock)
240 {
241     const char *traceClockPath = "trace_clock";
242     string allClocks = ReadFile(traceClockPath);
243     size_t begin = allClocks.find("[");
244     size_t end = allClocks.find("]");
245     string newClock;
246     if (begin != string::npos && end != string::npos &&
247         timeclock.compare(0, timeclock.size(), allClocks, begin + 1, end - begin - 1) == 0) {
248         return true;
249     } else if (allClocks.find(timeclock) != string::npos) {
250         newClock = timeclock;
251     } else if (allClocks.find("boot") != string::npos) {
252         // boot: This is the boot clock (CLOCK_BOOTTIME) and is based on the fast monotonic clock,
253         // but also accounts for time in suspend.
254         newClock = "boot";
255     } else if (allClocks.find("mono") != string::npos) {
256         // mono: uses the fast monotonic clock (CLOCK_MONOTONIC)
257         // which is monotonic and is subject to NTP rate adjustments.
258         newClock = "mono";
259     } else if (allClocks.find("global") != string::npos) {
260         // global: is in sync with all CPUs but may be a bit slower than the local clock.
261         newClock = "global";
262     } else {
263         fprintf(stderr, "You can set trace clock in %s\n", allClocks.c_str());
264         return false;
265     }
266     if (newClock.size() != 0) {
267         return WriteStrToFile(traceClockPath, newClock);
268     }
269     return true;
270 }
271 
SetOverWriteEnable(bool enabled)272 static bool SetOverWriteEnable(bool enabled)
273 {
274     const char *overWritePath = "options/overwrite";
275     return SetFtraceEnabled(overWritePath, enabled);
276 }
277 
SetTgidEnable(bool enabled)278 static bool SetTgidEnable(bool enabled)
279 {
280     const char *recordTgidPath = "options/record-tgid";
281     return SetFtraceEnabled(recordTgidPath, enabled);
282 }
283 
SetCmdLinesSize(int cmdLinesSize)284 static bool SetCmdLinesSize(int cmdLinesSize)
285 {
286     const char *savedCmdLineSizePath = "saved_cmdlines_size";
287     return WriteStrToFile(savedCmdLineSizePath, std::to_string(cmdLinesSize));
288 }
289 
DisableAllFtraceEvents()290 static bool DisableAllFtraceEvents()
291 {
292     bool isTrue = true;
293     for (auto it = g_tagMap.begin(); it != g_tagMap.end(); ++it) {
294         TagCategory tag = it->second;
295         if (tag.type != KERNEL) {
296             continue;
297         }
298         for (int i = 0; i < MAX_SYS_FILES; i++) {
299             const string path = tag.SysFiles[i].path;
300             if ((path.size() > 0) && IsWritableFile(path)) {
301                 isTrue = isTrue && SetFtraceEnabled(path, false);
302             }
303         }
304     }
305     return isTrue;
306 }
307 
SetProperty(const string & property,const string & value)308 static bool SetProperty(const string& property, const string& value)
309 {
310     return SetPropertyInner(property, value);
311 }
312 
SetTraceTagsEnabled(uint64_t tags)313 static bool SetTraceTagsEnabled(uint64_t tags)
314 {
315     string value = std::to_string(tags);
316     return SetProperty(TRACE_TAG_PROPERTY, value);
317 }
318 
RefreshServices()319 static bool RefreshServices()
320 {
321     bool res = false;
322 
323     res = RefreshBinderServices();
324     if (!res) {
325         return res;
326     }
327     res = RefreshHalServices();
328     return res;
329 }
330 
SetUserSpaceSettings()331 static bool SetUserSpaceSettings()
332 {
333     uint64_t enabledTags = 0;
334     for (auto tag: g_userEnabledTags) {
335         enabledTags |= tag;
336     }
337     return SetTraceTagsEnabled(enabledTags) && RefreshServices();
338 }
339 
ClearUserSpaceSettings()340 static bool ClearUserSpaceSettings()
341 {
342     return SetTraceTagsEnabled(0) && RefreshServices();
343 }
344 
SetKernelSpaceSettings()345 static bool SetKernelSpaceSettings()
346 {
347     if (!(SetBufferSize(g_bufferSizeKB) && SetClock(g_clock) && SetOverWriteEnable(g_overwrite) &&
348         SetTgidEnable(true) && SetCmdLinesSize(SAVED_CMDLINES_SIZE))) {
349         fprintf(stderr, "Set trace kernel settings failed\n");
350         return false;
351     }
352     if (DisableAllFtraceEvents() == false) {
353         fprintf(stderr, "Pre-clear kernel tracers failed\n");
354         return false;
355     }
356     for (const auto& path : g_kernelEnabledPaths) {
357         SetFtraceEnabled(path, true);
358     }
359     return true;
360 }
361 
ClearKernelSpaceSettings()362 static bool ClearKernelSpaceSettings()
363 {
364     return DisableAllFtraceEvents() && SetOverWriteEnable(true) && SetBufferSize(1) && SetClock("boot");
365 }
366 
ShowListCategory()367 static void ShowListCategory()
368 {
369     printf("  %18s   description:\n", "tagName:");
370     for (auto it = g_tagMap.begin(); it != g_tagMap.end(); ++it) {
371         string key = it->first;
372         TagCategory tag = it->second;
373         if (IsTagSupported(key)) {
374             printf("  %18s - %s\n", tag.name.c_str(), tag.description.c_str());
375         }
376     }
377 }
378 
ShowHelp(const string & cmd)379 static void ShowHelp(const string& cmd)
380 {
381     printf("usage: %s [options] [categories...]\n", cmd.c_str());
382     printf("options include:\n"
383            "  -b N               Sets the size of the buffer (KB) for storing and reading traces. The default \n"
384            "                     buffer size is 2048 KB.\n"
385            "  --buffer_size N    Like \"-b N\".\n"
386            "  -l                 Lists available hitrace categories.\n"
387            "  --list_categories  Like \"-l\".\n"
388            "  -t N               Sets the hitrace running duration in seconds (5s by default), which depends on \n"
389            "                     the time required for analysis.\n"
390            "  --time N           Like \"-t N\".\n"
391            "  --trace_clock clock\n"
392            "                     Sets the type of the clock for adding a timestamp to a trace, which can be\n"
393            "                     boot (default), global, mono, uptime, or perf.\n"
394            "  --trace_begin      Starts capturing traces.\n"
395            "  --trace_dump       Dumps traces to a specified path (stdout by default).\n"
396            "  --trace_finish     Stops capturing traces and dumps traces to a specified path (stdout by default).\n"
397            "  --trace_finish_nodump\n"
398            "                     Stops capturing traces and not dumps traces.\n"
399            "  --overwrite        Sets the action to take when the buffer is full. If this option is used,\n"
400            "                     the latest traces are discarded; if this option is not used (default setting),\n"
401            "                     the earliest traces are discarded.\n"
402            "  -o filename        Specifies the name of the target file (stdout by default).\n"
403            "  --output filename\n"
404            "                     Like \"-o filename\".\n"
405            "  -z                 Compresses a captured trace.\n"
406     );
407 }
408 
409 template <typename T>
StrToNum(const std::string & sString,T & tX)410 inline bool StrToNum(const std::string& sString, T &tX)
411 {
412     std::istringstream iStream(sString);
413     return (iStream >> tX) ? true : false;
414 }
415 
ParseLongOpt(const string & cmd,int optionIndex)416 static bool ParseLongOpt(const string& cmd, int optionIndex)
417 {
418     bool isTrue = true;
419     if (!strcmp(LONG_OPTIONS[optionIndex].name, "buffer_size")) {
420         if (!StrToNum(optarg, g_bufferSizeKB)) {
421             fprintf(stderr, "Error: buffer size is illegal input. eg: \"--buffer_size 1024\"\n");
422             isTrue = false;
423         } else if (g_bufferSizeKB < MIN_BUFFER_SIZE || g_bufferSizeKB > MAX_BUFFER_SIZE) {
424             fprintf(stderr, "Error: buffer size must be from 256 KB to 300 MB. eg: \"--buffer_size 1024\"\n");
425             isTrue = false;
426         }
427         g_bufferSizeKB = g_bufferSizeKB / PAGE_SIZE_KB * PAGE_SIZE_KB;
428     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_clock")) {
429         regex re("[a-zA-Z]{4,6}");
430         if (regex_match(optarg, re)) {
431             g_clock = optarg;
432         } else {
433             fprintf(stderr, "Error: \"--trace_clock\" is illegal input. eg: \"--trace_clock boot\"\n");
434             isTrue = false;
435         }
436     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "help")) {
437         ShowHelp(cmd);
438         isTrue = false;
439     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "time")) {
440         if (!StrToNum(optarg, g_traceDuration)) {
441             fprintf(stderr, "Error: the time is illegal input. eg: \"--time 5\"\n");
442             isTrue = false;
443         } else if (g_traceDuration < 1) {
444             fprintf(stderr, "Error: \"-t %s\" to be greater than zero. eg: \"--time 5\"\n", optarg);
445             isTrue = false;
446         }
447     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "list_categories")) {
448         ShowListCategory();
449         isTrue = false;
450     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "output")) {
451         struct stat buf;
452         size_t len = strnlen(optarg, MAX_OUTPUT_LEN);
453         if (len == MAX_OUTPUT_LEN || len < 1 || (stat(optarg, &buf) == 0 && (buf.st_mode & S_IFDIR))) {
454             fprintf(stderr, "Error: output file is illegal\n");
455             isTrue = false;
456         } else {
457             g_outputFile = optarg;
458         }
459     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "overwrite")) {
460         g_overwrite = false;
461     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_begin")) {
462         g_traceStart = START_ASYNC;
463         g_traceStop = false;
464         g_traceDump = false;
465     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_finish")) {
466         g_traceStart = START_NONE;
467         g_traceStop = true;
468         g_traceDump = true;
469     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_finish_nodump")) {
470         g_traceStart = START_NONE;
471         g_traceStop = true;
472         g_traceDump = false;
473     } else if (!strcmp(LONG_OPTIONS[optionIndex].name, "trace_dump")) {
474         g_traceStart = START_NONE;
475         g_traceStop = false;
476         g_traceDump = true;
477     }
478     return isTrue;
479 }
480 
ParseOpt(int opt,char ** argv,int optIndex)481 static bool ParseOpt(int opt, char** argv, int optIndex)
482 {
483     bool isTrue = true;
484     switch (opt) {
485         case 'b': {
486             if (!StrToNum(optarg, g_bufferSizeKB)) {
487                 fprintf(stderr, "Error: buffer size is illegal input. eg: \"--buffer_size 1024\"\n");
488                 isTrue = false;
489             } else if (g_bufferSizeKB < MIN_BUFFER_SIZE || g_bufferSizeKB > MAX_BUFFER_SIZE) {
490                 fprintf(stderr, "Error: buffer size must be from 256 KB to 300 MB. eg: \"--buffer_size 1024\"\n");
491                 isTrue = false;
492             }
493             g_bufferSizeKB = g_bufferSizeKB / PAGE_SIZE_KB * PAGE_SIZE_KB;
494             break;
495         }
496         case 'h':
497             ShowHelp(argv[0]);
498             isTrue = false;
499             break;
500         case 'l':
501             ShowListCategory();
502             isTrue = false;
503             break;
504         case 't': {
505             if (!StrToNum(optarg, g_traceDuration)) {
506                 fprintf(stderr, "Error: the time is illegal input. eg: \"--time 5\"\n");
507                 isTrue = false;
508             } else if (g_traceDuration < 1) {
509                 fprintf(stderr, "Error: \"-t %s\" to be greater than zero. eg: \"--time 5\"\n", optarg);
510                 isTrue = false;
511             }
512             break;
513         }
514         case 'o': {
515             struct stat buf;
516             size_t len = strnlen(optarg, MAX_OUTPUT_LEN);
517             if (len == MAX_OUTPUT_LEN || len < 1 || (stat(optarg, &buf) == 0 && (buf.st_mode & S_IFDIR))) {
518                 fprintf(stderr, "Error: output file is illegal\n");
519                 isTrue = false;
520             } else {
521                 g_outputFile = optarg;
522             }
523             break;
524         }
525         case 'z':
526             g_compress = true;
527             break;
528         case 0: // long options
529             isTrue = ParseLongOpt(argv[0], optIndex);
530             break;
531         default:
532             ShowHelp(argv[0]);
533             isTrue = false;
534             break;
535     }
536     return isTrue;
537 }
538 
IsInvalidOpt(int argc,char ** argv)539 static void IsInvalidOpt(int argc, char** argv)
540 {
541     for (int i = optind; i < argc; i++) {
542         if (!IsTagSupported(argv[i])) {
543             fprintf(stderr, "Error: \"%s\" is not support category on this device\n", argv[i]);
544             exit(-1);
545         }
546     }
547 }
548 
HandleOpt(int argc,char ** argv)549 static bool HandleOpt(int argc, char** argv)
550 {
551     bool isTrue = true;
552     int opt = 0;
553     int optionIndex = 0;
554     string shortOption = "b:c:hlo:t:z";
555     int argcSize = argc;
556     while (isTrue && argcSize-- > 0) {
557         opt = getopt_long(argc, argv, shortOption.c_str(), LONG_OPTIONS, &optionIndex);
558         if (opt < 0) {
559             IsInvalidOpt(argc, argv);
560             break;
561         }
562         isTrue = ParseOpt(opt, argv, optionIndex);
563     }
564     return isTrue;
565 }
566 
TruncateFile(const string & path)567 static bool TruncateFile(const string& path)
568 {
569     int fd = creat((g_traceRootPath + path).c_str(), 0);
570     if (fd == -1) {
571         fprintf(stderr, "Error: clear %s, errno: %d\n", (g_traceRootPath + path).c_str(), errno);
572         return false;
573     }
574     close(fd);
575     return true;
576 }
577 
ClearTrace()578 static bool ClearTrace()
579 {
580     return TruncateFile(TRACE_PATH);
581 }
582 
StartTrace()583 static bool StartTrace()
584 {
585     SetPropertyInner(TRACE_TAG_STATE, "1");
586     if (!SetFtraceEnabled(TRACING_ON_PATH, true)) {
587         return false;
588     }
589     ClearTrace();
590     printf("capturing trace...\n");
591     fflush(stdout);
592     return true;
593 }
594 
WaitForTraceDone(void)595 static void WaitForTraceDone(void)
596 {
597     struct timespec ts = {0, 0};
598     ts.tv_sec = g_traceDuration;
599     ts.tv_nsec = 0;
600     while ((nanosleep(&ts, &ts) == -1) && (errno == EINTR)) {}
601 }
602 
StopTrace()603 static bool StopTrace()
604 {
605     return SetFtraceEnabled(TRACING_ON_PATH, false);
606 }
607 
DumpCompressedTrace(int traceFd,int outFd)608 static void DumpCompressedTrace(int traceFd, int outFd)
609 {
610     z_stream zs { nullptr };
611     int flush = Z_NO_FLUSH;
612     ssize_t bytesWritten;
613     ssize_t bytesRead;
614     if (memset_s(&zs, sizeof(zs), 0, sizeof(zs)) != 0) {
615         fprintf(stderr, "Error: zip stream buffer init failed\n");
616         return;
617     }
618     int ret = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
619     if (ret != Z_OK) {
620         fprintf(stderr, "Error: initializing zlib: %d\n", ret);
621         return;
622     }
623     std::unique_ptr<uint8_t[]>  in = std::make_unique<uint8_t[]>(CHUNK_SIZE);
624     std::unique_ptr<uint8_t[]>  out = std::make_unique<uint8_t[]>(CHUNK_SIZE);
625     if (!in || !out) {
626         fprintf(stderr, "Error: couldn't allocate buffers\n");
627         return;
628     }
629     zs.next_out = reinterpret_cast<Bytef*>(out.get());
630     zs.avail_out = CHUNK_SIZE;
631 
632     do {
633         if (zs.avail_in == 0 && flush == Z_NO_FLUSH) {
634             bytesRead = TEMP_FAILURE_RETRY(read(traceFd, in.get(), CHUNK_SIZE));
635             if (bytesRead == 0) {
636                 flush = Z_FINISH;
637             } else if (bytesRead == -1) {
638                 fprintf(stderr, "Error: reading trace, errno: %d\n", errno);
639                 break;
640             } else {
641                 zs.next_in = reinterpret_cast<Bytef*>(in.get());
642                 zs.avail_in = bytesRead;
643             }
644         }
645         if (zs.avail_out == 0) {
646             bytesWritten = TEMP_FAILURE_RETRY(write(outFd, out.get(), CHUNK_SIZE));
647             if (bytesWritten < CHUNK_SIZE) {
648                 fprintf(stderr, "Error: writing deflated trace, errno: %d\n", errno);
649                 break;
650             }
651             zs.next_out = reinterpret_cast<Bytef*>(out.get());
652             zs.avail_out = CHUNK_SIZE;
653         }
654         ret = deflate(&zs, flush);
655         if (flush == Z_FINISH && ret == Z_STREAM_END) {
656             size_t have = CHUNK_SIZE - zs.avail_out;
657             bytesWritten = TEMP_FAILURE_RETRY(write(outFd, out.get(), have));
658             if (static_cast<size_t>(bytesWritten) < have) {
659                 fprintf(stderr, "Error: writing deflated trace, errno: %d\n", errno);
660             }
661             break;
662         } else if (ret != Z_OK) {
663             if (ret == Z_ERRNO) {
664                 fprintf(stderr, "Error: deflate failed with errno %d\n", errno);
665             } else {
666                 fprintf(stderr, "Error: deflate failed return %d\n", ret);
667             }
668             break;
669         }
670     } while (ret == Z_OK);
671 
672     ret = deflateEnd(&zs);
673     if (ret != Z_OK) {
674         fprintf(stderr, "error cleaning up zlib: %d\n", ret);
675     }
676 }
677 
DumpTrace(int outFd,const string & path)678 static void DumpTrace(int outFd, const string& path)
679 {
680     string resolvedPath = CanonicalizeSpecPath((g_traceRootPath + path).c_str());
681     int traceFd = open(resolvedPath.c_str(), O_RDWR);
682     if (traceFd == -1) {
683         fprintf(stderr, "error opening %s, errno: %d\n", path.c_str(), errno);
684         return;
685     }
686     ssize_t bytesWritten;
687     ssize_t bytesRead;
688     if (g_compress) {
689         DumpCompressedTrace(traceFd, outFd);
690     } else {
691         char buffer[BLOCK_SIZE];
692         do {
693             bytesRead = TEMP_FAILURE_RETRY(read(traceFd, buffer, BLOCK_SIZE));
694             if ((bytesRead == 0) || (bytesRead == -1)) {
695                 break;
696             }
697             bytesWritten = TEMP_FAILURE_RETRY(write(outFd, buffer, bytesRead));
698         } while (bytesWritten > 0);
699     }
700     close(traceFd);
701 }
702 
MarkOthersClockSync()703 static bool MarkOthersClockSync()
704 {
705     constexpr unsigned int bufferSize = 128; // buffer size
706     char buffer[bufferSize] = { 0 };
707     string resolvedPath = CanonicalizeSpecPath((g_traceRootPath + TRACE_MARKER_PATH).c_str());
708     int fd = open(resolvedPath.c_str(), O_WRONLY);
709     if (fd == -1) {
710         fprintf(stderr, "Error: opening %s, errno: %d\n", TRACE_MARKER_PATH, errno);
711         return false;
712     }
713 
714     struct timespec mts = {0, 0};
715     struct timespec rts = {0, 0};
716     if (clock_gettime(CLOCK_REALTIME, &rts) == -1) {
717         fprintf(stderr, "Error: get realtime, errno: %d\n", errno);
718         close(fd);
719         return false;
720     } else if (clock_gettime(CLOCK_MONOTONIC, &mts) == -1) {
721         fprintf(stderr, "Error: get parent_ts, errno: %d\n", errno);
722         close(fd);
723         return false;
724     }
725     constexpr unsigned int nanoSeconds = 1000000000; // seconds converted to nanoseconds
726     constexpr unsigned int nanoToMill = 1000000; // millisecond converted to nanoseconds
727     constexpr float nanoToSecond = 1000000000.0f; // consistent with the ftrace timestamp format
728     int len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1,
729         "trace_event_clock_sync: realtime_ts=%" PRId64 "\n",
730         static_cast<int64_t>((rts.tv_sec * nanoSeconds + rts.tv_nsec) / nanoToMill));
731     if (len < 0) {
732         fprintf(stderr, "Error: entering data into buffer, errno: %d\n", errno);
733         close(fd);
734         return false;
735     }
736     if (write(fd, buffer, len) < 0) {
737         fprintf(stderr, "Warning: writing clock sync marker, errno: %d\n", errno);
738         fprintf(stderr, "the buffer is not enough, please increase the buffer\n");
739     }
740     len = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "trace_event_clock_sync: parent_ts=%f\n",
741         static_cast<float>(((static_cast<float>(mts.tv_sec)) * nanoSeconds + mts.tv_nsec) / nanoToSecond));
742     if (len < 0) {
743         fprintf(stderr, "Error: entering data into buffer, errno: %d\n", errno);
744         close(fd);
745         return false;
746     }
747     if (write(fd, buffer, len) < 0) {
748         fprintf(stderr, "Warning: writing clock sync marker, errno: %d\n", errno);
749         fprintf(stderr, "the buffer is not enough, please increase the buffer\n");
750     }
751     close(fd);
752     return true;
753 }
754 
InitDiskSupportTags()755 static void InitDiskSupportTags()
756 {
757     g_tagMap["disk"] = { "disk", "Disk I/O", 0, KERNEL, {
758         { "events/f2fs/f2fs_sync_file_enter/enable" },
759         { "events/f2fs/f2fs_sync_file_exit/enable" },
760         { "events/f2fs/f2fs_write_begin/enable" },
761         { "events/f2fs/f2fs_write_end/enable" },
762         { "events/ext4/ext4_da_write_begin/enable" },
763         { "events/ext4/ext4_da_write_end/enable" },
764         { "events/ext4/ext4_sync_file_enter/enable" },
765         { "events/ext4/ext4_sync_file_exit/enable" },
766         { "events/block/block_rq_issue/enable" },
767         { "events/block/block_rq_complete/enable" },
768     }};
769     g_tagMap["mmc"] = { "mmc", "eMMC commands", 0, KERNEL, {
770         { "events/mmc/enable" },
771     }};
772     g_tagMap["ufs"] = { "ufs", "UFS commands", 0, KERNEL, {
773         { "events/ufs/enable" },
774     }};
775 }
776 
InitHardwareSupportTags()777 static void InitHardwareSupportTags()
778 {
779     g_tagMap["irq"] = { "irq", "IRQ Events", 0, KERNEL, {
780         { "events/irq/enable" },
781         { "events/ipi/enable" },
782     }};
783     g_tagMap["irqoff"] = { "irqoff", "IRQ-disabled code section tracing", 0, KERNEL, {
784         { "events/preemptirq/irq_enable/enable" },
785         { "events/preemptirq/irq_disable/enable" },
786     }};
787     InitDiskSupportTags();
788     g_tagMap["i2c"] = { "i2c", "I2C Events", 0, KERNEL, {
789         { "events/i2c/enable" },
790         { "events/i2c/i2c_read/enable" },
791         { "events/i2c/i2c_write/enable" },
792         { "events/i2c/i2c_result/enable" },
793         { "events/i2c/i2c_reply/enable" },
794         { "events/i2c/smbus_read/enable" },
795         { "events/i2c/smbus_write/enable" },
796         { "events/i2c/smbus_result/enable" },
797         { "events/i2c/smbus_reply/enable" },
798     }};
799     g_tagMap["regulators"] = { "regulators", "Voltage and Current Regulators", 0, KERNEL, {
800         { "events/regulator/enable" },
801     }};
802     g_tagMap["membus"] = { "membus", "Memory Bus Utilization", 0, KERNEL, {
803         { "events/memory_bus/enable" },
804     }};
805 }
806 
InitCpuSupportTags()807 static void InitCpuSupportTags()
808 {
809     g_tagMap["freq"] = { "freq", "CPU Frequency", 0, KERNEL, {
810         { "events/power/cpu_frequency/enable" },
811         { "events/power/clock_set_rate/enable" },
812         { "events/power/clock_disable/enable" },
813         { "events/power/clock_enable/enable" },
814         { "events/clk/clk_set_rate/enable" },
815         { "events/clk/clk_disable/enable" },
816         { "events/clk/clk_enable/enable" },
817         { "events/power/cpu_frequency_limits/enable" },
818     }};
819     g_tagMap["idle"] = { "idle", "CPU Idle", 0, KERNEL, {
820         { "events/power/cpu_idle/enable" },
821     }};
822     g_tagMap["load"] = { "load", "CPU Load", 0, KERNEL, {
823         { "events/cpufreq_interactive/enable" },
824     }};
825 }
826 
InitKernelSupportTags()827 static void InitKernelSupportTags()
828 {
829     g_tagMap["sched"] = { "sched", "CPU Scheduling", 0, KERNEL, {
830         { "events/sched/sched_switch/enable" },
831         { "events/sched/sched_wakeup/enable" },
832         { "events/sched/sched_wakeup_new/enable" },
833         { "events/sched/sched_waking/enable" },
834         { "events/sched/sched_blocked_reason/enable" },
835         { "events/sched/sched_pi_setprio/enable" },
836         { "events/sched/sched_process_exit/enable" },
837         { "events/cgroup/enable" },
838         { "events/oom/oom_score_adj_update/enable" },
839         { "events/task/task_rename/enable" },
840         { "events/task/task_newtask/enable" },
841     }};
842     g_tagMap["preemptoff"] = { "preemptoff", "Preempt-disabled code section tracing", 0, KERNEL, {
843         { "events/preemptirq/preempt_enable/enable" },
844         { "events/preemptirq/preempt_disable/enable" },
845     }};
846 
847     g_tagMap["binder"] = { "binder", "Binder kernel Info", 0, KERNEL, {
848         { "events/binder/binder_transaction/enable" },
849         { "events/binder/binder_transaction_received/enable" },
850         { "events/binder/binder_transaction_alloc_buf/enable" },
851         { "events/binder/binder_set_priority/enable" },
852         { "events/binder/binder_lock/enable" },
853         { "events/binder/binder_locked/enable" },
854         { "events/binder/binder_unlock/enable" },
855     }};
856 
857     g_tagMap["sync"] = { "sync", "Synchronization", 0, KERNEL, {
858         // linux kernel > 4.9
859         { "events/dma_fence/enable" },
860     }};
861     g_tagMap["workq"] = { "workq", "Kernel Workqueues", 0, KERNEL, {
862         { "events/workqueue/enable" },
863     }};
864     g_tagMap["memreclaim"] = { "memreclaim", "Kernel Memory Reclaim", 0, KERNEL, {
865         { "events/vmscan/mm_vmscan_direct_reclaim_begin/enable" },
866         { "events/vmscan/mm_vmscan_direct_reclaim_end/enable" },
867         { "events/vmscan/mm_vmscan_kswapd_wake/enable" },
868         { "events/vmscan/mm_vmscan_kswapd_sleep/enable" },
869         { "events/lowmemorykiller/enable" },
870     }};
871     g_tagMap["pagecache"] = { "pagecache", "Page cache", 0, KERNEL, {
872         { "events/filemap/enable" },
873     }};
874     g_tagMap["memory"] = { "memory", "Memory", 0, KERNEL, {
875         { "events/kmem/rss_stat/enable" },
876         { "events/kmem/ion_heap_grow/enable" },
877         { "events/kmem/ion_heap_shrink/enable" },
878     }};
879     InitCpuSupportTags();
880     InitHardwareSupportTags();
881 }
882 
InitOtherUserTags()883 static void InitOtherUserTags()
884 {
885     g_tagMap["account"] = { "account", "Account Manager", HITRACE_TAG_ACCOUNT_MANAGER, USER, {}};
886     g_tagMap["dhfwk"] = { "dhfwk", "Distributed Hardware FWK", HITRACE_TAG_DISTRIBUTED_HARDWARE_FWK, USER, {}};
887     g_tagMap["dscreen"] = { "dscreen", "Distributed Screen", HITRACE_TAG_DISTRIBUTED_SCREEN, USER, {}};
888     g_tagMap["daudio"] = { "daudio", "Distributed Audio", HITRACE_TAG_DISTRIBUTED_AUDIO, USER, {}};
889     g_tagMap["dinput"] = { "dinput", "Distributed Input", HITRACE_TAG_DISTRIBUTED_INPUT, USER, {}};
890     g_tagMap["devicemanager"] = { "devicemanager", "Device Manager", HITRACE_TAG_DEVICE_MANAGER, USER, {}};
891     g_tagMap["deviceprofile"] = { "deviceprofile", "Device Profile", HITRACE_TAG_DEVICE_PROFILE, USER, {}};
892     g_tagMap["dsched"] = { "dsched", "Distributed Schedule", HITRACE_TAG_DISTRIBUTED_SCHEDULE, USER, {}};
893     g_tagMap["huks"] = { "huks", "Universal KeyStore", HITRACE_TAG_HUKS, USER, {}};
894     g_tagMap["dlpcre"] = { "dlpcre", "Dlp Credential Service", HITRACE_TAG_DLP_CREDENTIAL, USER, {}};
895     g_tagMap["samgr"] = { "samgr", "samgr", HITRACE_TAG_SAMGR, USER, {}};
896     g_tagMap["app"] = { "app", "APP Module", HITRACE_TAG_APP, USER, {}};
897     g_tagMap["dcamera"] = { "dcamera", "Distributed Camera", HITRACE_TAG_DISTRIBUTED_CAMERA, USER, {}};
898     g_tagMap["zbinder"] = { "zbinder", "OpenHarmony binder communication", 0, KERNEL, {
899         { "events/zbinder/enable" },
900     }};
901     g_tagMap["gresource"] = { "gresource", "Global Resource Manager", HITRACE_TAG_GLOBAL_RESMGR, USER, {}};
902     g_tagMap["power"] = { "power", "Power Manager", HITRACE_TAG_POWER, USER, {}};
903     g_tagMap["bluetooth"] = { "bluetooth", "communicatio bluetooth", HITRACE_TAG_BLUETOOTH, USER, {}};
904     g_tagMap["filemanagement"] = { "filemanagement", "filemanagement", HITRACE_TAG_FILEMANAGEMENT, USER, {}};
905     g_tagMap["dslm"] = {"dslm", "device security level", HITRACE_TAG_DLSM, USER, {}};
906     g_tagMap["useriam"] = {"useriam", "useriam", HITRACE_TAG_USERIAM, USER, {}};
907     g_tagMap["nweb"] = {"nweb", "NWEB Module", HITRACE_TAG_NWEB, USER, {}};
908     g_tagMap["net"] = {"net", "net", HITRACE_TAG_NET, USER, {}};
909     g_tagMap["accesscontrol"] = {"accesscontrol", "Access Control Module", HITRACE_TAG_ACCESS_CONTROL, USER, {}};
910     g_tagMap["interconn"] = {"interconn", "Interconnection subsystem", HITRACE_TAG_INTERCONNECTION, USER, {}};
911     g_tagMap["usb"] = {"usb", "usb subsystem", HITRACE_TAG_USB, USER, {}};
912     g_tagMap["hdf"] = {"hdf", "hdf subsystem", HITRACE_TAG_HDF, USER, {}};
913     g_tagMap["commonlibrary"] = {"commonlibrary", "commonlibrary subsystem", HITRACE_TAG_COMMONLIBRARY, USER, {}};
914     g_tagMap["hdcd"] = {"hdcd", "hdcd", HITRACE_TAG_HDCD, USER, {}};
915     g_tagMap["deviceauth"] = {"deviceauth", "Device Auth", HITRACE_TAG_DEV_AUTH, USER, {}};
916     g_tagMap["cloud"] = {"cloud", "Cloud subsystem", HITRACE_TAG_CLOUD, USER, {}};
917     g_tagMap["ffrt"] = {"ffrt", "Ffrt Tasks", HITRACE_TAG_FFRT, USER, {}};
918     g_tagMap["musl"] = {"musl", "Musl Module", HITRACE_TAG_MUSL, USER, {}};
919 }
920 
InitAllSupportTags()921 static void InitAllSupportTags()
922 {
923     // OHOS
924     g_tagMap["ohos"] = { "ohos", "OpenHarmony", HITRACE_TAG_OHOS, USER, {}};
925     g_tagMap["ability"] = { "ability", "Ability Manager", HITRACE_TAG_ABILITY_MANAGER, USER, {}};
926     g_tagMap["zcamera"] = { "zcamera", "OpenHarmony Camera Module", HITRACE_TAG_ZCAMERA, USER, {}};
927     g_tagMap["zmedia"] = { "zmedia", "OpenHarmony Media Module", HITRACE_TAG_ZMEDIA, USER, {}};
928     g_tagMap["zimage"] = { "zimage", "OpenHarmony Image Module", HITRACE_TAG_ZIMAGE, USER, {}};
929     g_tagMap["zaudio"] = { "zaudio", "OpenHarmony Audio Module", HITRACE_TAG_ZAUDIO, USER, {}};
930     g_tagMap["distributeddatamgr"] = { "distributeddatamgr", "Distributed Data Manager",
931         HITRACE_TAG_DISTRIBUTEDDATA, USER, {}};
932     g_tagMap["mdfs"] = { "mdfs", "Mobile Distributed File System", HITRACE_TAG_MDFS, USER, {}};
933     g_tagMap["graphic"] = { "graphic", "Graphic Module", HITRACE_TAG_GRAPHIC_AGP, USER, {}};
934     g_tagMap["ace"] = { "ace", "ACE development framework", HITRACE_TAG_ACE, USER, {}};
935     g_tagMap["notification"] = { "notification", "Notification Module", HITRACE_TAG_NOTIFICATION, USER, {}};
936     g_tagMap["misc"] = { "misc", "Misc Module", HITRACE_TAG_MISC, USER, {}};
937     g_tagMap["multimodalinput"] = { "multimodalinput", "Multimodal Input Module",
938         HITRACE_TAG_MULTIMODALINPUT, USER, {}};
939     g_tagMap["sensors"] = { "sensors", "Sensors Module", HITRACE_TAG_SENSORS, USER, {}};
940     g_tagMap["msdp"] = { "msdp", "Multimodal Sensor Data Platform", HITRACE_TAG_MSDP, USER, {}};
941     g_tagMap["dsoftbus"] = { "dsoftbus", "Distributed Softbus", HITRACE_TAG_DSOFTBUS, USER, {}};
942     g_tagMap["rpc"] = { "rpc", "RPC and IPC", HITRACE_TAG_RPC, USER, {}};
943     g_tagMap["ark"] = { "ark", "ARK Module", HITRACE_TAG_ARK, USER, {}};
944     g_tagMap["window"] = { "window", "Window Manager", HITRACE_TAG_WINDOW_MANAGER, USER, {}};
945     g_tagMap["accessibility"] = { "accessibility", "Accessibility Manager",
946         HITRACE_TAG_ACCESSIBILITY_MANAGER, USER, {}};
947     InitOtherUserTags();
948 
949     // Kernel os
950     InitKernelSupportTags();
951 }
952 
InterruptExit(int signo)953 static void InterruptExit(int signo)
954 {
955     _exit(-1);
956 }
957 
main(int argc,char ** argv)958 int main(int argc, char **argv)
959 {
960     setgid(SHELL_UID);
961     (void)signal(SIGKILL, InterruptExit);
962     (void)signal(SIGINT, InterruptExit);
963 
964     if (!IsTraceMounted()) {
965         exit(-1);
966     }
967 
968     InitAllSupportTags();
969 
970     if (!HandleOpt(argc, argv)) {
971         exit(-1);
972     }
973 
974     if (g_traceStart != START_NONE) {
975         if (!SetKernelSpaceSettings()) {
976             ClearKernelSpaceSettings();
977             exit(-1);
978         }
979     }
980 
981     bool isTrue = true;
982     if (g_traceStart != START_NONE) {
983         isTrue = isTrue && StartTrace();
984         if (!SetUserSpaceSettings()) {
985             ClearKernelSpaceSettings();
986             ClearUserSpaceSettings();
987             exit(-1);
988         }
989         if (g_traceStart == START_ASYNC) {
990             return isTrue ? 0 : -1;
991         }
992         WaitForTraceDone();
993     }
994 
995     // following is dump and stop handling
996     isTrue = isTrue && MarkOthersClockSync();
997 
998     if (g_traceStop) {
999         // clear user tags first and sleep a little to let apps already be notified.
1000         ClearUserSpaceSettings();
1001         std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_MILLISECONDS));
1002         isTrue = isTrue && StopTrace();
1003     }
1004 
1005     if (isTrue && g_traceDump) {
1006         int outFd = STDOUT_FILENO;
1007         if (g_outputFile.size() > 0) {
1008             printf("write trace to %s\n", g_outputFile.c_str());
1009             string resolvedPath = CanonicalizeSpecPath(g_outputFile.c_str());
1010             outFd = open(resolvedPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1011         }
1012         if (outFd == -1) {
1013             fprintf(stderr, "Failed to open file '%s', err=%d", g_outputFile.c_str(), errno);
1014             isTrue = false;
1015         } else {
1016             dprintf(outFd, "TRACE:\n");
1017             DumpTrace(outFd, TRACE_PATH);
1018             if (outFd != STDOUT_FILENO) {
1019                 close(outFd);
1020             }
1021         }
1022         ClearTrace();
1023     }
1024 
1025     if (g_traceStop) {
1026         // clear kernel setting including clock type after dump(MUST) and tracing_on is off.
1027         ClearKernelSpaceSettings();
1028     }
1029     return isTrue ? 0 : -1;
1030 }
1031