1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <signal.h>
18
19 #include "perfetto/ext/base/file_utils.h"
20 #include "perfetto/ext/base/string_splitter.h"
21 #include "perfetto/ext/base/string_utils.h"
22 #include "perfetto/ext/base/string_writer.h"
23 #include "perfetto/ext/base/unix_task_runner.h"
24 #include "perfetto/ext/base/utils.h"
25
26 namespace perfetto {
27 namespace {
28
29 // This dumps the ftrace stats into trace marker every 500ms. This is useful for
30 // debugging overruns over time.
31
32 base::UnixTaskRunner* g_task_runner = nullptr;
33
ExtractInt(const char * s)34 uint32_t ExtractInt(const char* s) {
35 for (; *s != '\0'; s++) {
36 if (*s == ':') {
37 return static_cast<uint32_t>(atoi(s + 1));
38 }
39 }
40 return 0;
41 }
42
NumberOfCpus()43 size_t NumberOfCpus() {
44 static size_t num_cpus = static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF));
45 return num_cpus;
46 }
47
ReadFileIntoString(const std::string & path)48 std::string ReadFileIntoString(const std::string& path) {
49 // You can't seek or stat the procfs files on Android.
50 // The vast majority (884/886) of format files are under 4k.
51 std::string str;
52 str.reserve(4096);
53 if (!base::ReadFile(path, &str))
54 return "";
55 return str;
56 }
57
ReadCpuStats(size_t cpu)58 std::string ReadCpuStats(size_t cpu) {
59 std::string path =
60 "/sys/kernel/debug/tracing/per_cpu/cpu" + std::to_string(cpu) + "/stats";
61 return ReadFileIntoString(path);
62 }
63
DumpAllCpuStats()64 void DumpAllCpuStats() {
65 base::ScopedFile file(
66 base::OpenFile("/sys/kernel/debug/tracing/trace_marker", O_RDWR));
67 if (!file) {
68 PERFETTO_ELOG("Unable to open trace marker file");
69 g_task_runner->PostDelayedTask(&DumpAllCpuStats, 500);
70 return;
71 }
72
73 for (uint32_t cpu = 0; cpu < NumberOfCpus(); cpu++) {
74 std::string text = ReadCpuStats(cpu);
75 if (text.empty())
76 continue;
77
78 char buffer[1024];
79 base::StringSplitter splitter(std::move(text), '\n');
80 while (splitter.Next()) {
81 base::StringWriter writer(buffer, base::ArraySize(buffer));
82 writer.AppendLiteral("C|");
83 writer.AppendInt(getpid());
84 writer.AppendLiteral("|");
85
86 if (base::StartsWith(splitter.cur_token(), "overrun")) {
87 writer.AppendLiteral("overrun_");
88 } else if (base::StartsWith(splitter.cur_token(), "commit overrun")) {
89 writer.AppendLiteral("commit_overrun_");
90 } else {
91 continue;
92 }
93 writer.AppendInt(cpu);
94 writer.AppendLiteral("|");
95 writer.AppendInt(ExtractInt(splitter.cur_token()));
96 writer.AppendLiteral("\n");
97
98 auto output = writer.GetStringView();
99 PERFETTO_CHECK(write(*file, output.data(), output.size()) ==
100 static_cast<ssize_t>(output.size()));
101 }
102 }
103 g_task_runner->PostDelayedTask(&DumpAllCpuStats, 500);
104 }
105
SetupCtrlCHandler()106 void SetupCtrlCHandler() {
107 // Setup signal handler.
108 struct sigaction sa {};
109
110 // Glibc headers for sa_sigaction trigger this.
111 #pragma GCC diagnostic push
112 #if defined(__clang__)
113 #pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
114 #endif
115 sa.sa_handler = [](int) { g_task_runner->Quit(); };
116 sa.sa_flags = static_cast<decltype(sa.sa_flags)>(SA_RESETHAND | SA_RESTART);
117 #pragma GCC diagnostic pop
118 sigaction(SIGINT, &sa, nullptr);
119 sigaction(SIGTERM, &sa, nullptr);
120 }
121
DumpFtraceStatsMain()122 int DumpFtraceStatsMain() {
123 base::UnixTaskRunner task_runner;
124 g_task_runner = &task_runner;
125 SetupCtrlCHandler();
126 task_runner.PostTask(&DumpAllCpuStats);
127 task_runner.Run();
128 return 0;
129 }
130
131 } // namespace
132 } // namespace perfetto
133
main(int argc,char ** argv)134 int main(int argc, char** argv) {
135 perfetto::base::ignore_result(argc, argv);
136 return perfetto::DumpFtraceStatsMain();
137 }
138