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