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