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