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