1 /*
2 * Copyright (C) 2017 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 "src/traced/probes/ftrace/ftrace_procfs.h"
18
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <fstream>
25 #include <sstream>
26 #include <string>
27
28 #include "perfetto/base/logging.h"
29 #include "perfetto/ext/base/file_utils.h"
30 #include "perfetto/ext/base/string_splitter.h"
31 #include "perfetto/ext/base/string_utils.h"
32 #include "perfetto/ext/base/utils.h"
33
34 namespace perfetto {
35
36 // Reading /trace produces human readable trace output.
37 // Writing to this file clears all trace buffers for all CPUS.
38
39 // Writing to /trace_marker file injects an event into the trace buffer.
40
41 // Reading /tracing_on returns 1/0 if tracing is enabled/disabled.
42 // Writing 1/0 to this file enables/disables tracing.
43 // Disabling tracing with this file prevents further writes but
44 // does not clear the buffer.
45
46 namespace {
47
KernelLogWrite(const char * s)48 void KernelLogWrite(const char* s) {
49 PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
50 if (FtraceProcfs::g_kmesg_fd != -1)
51 base::ignore_result(base::WriteAll(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
52 }
53
WriteFileInternal(const std::string & path,const std::string & str,int flags)54 bool WriteFileInternal(const std::string& path,
55 const std::string& str,
56 int flags) {
57 base::ScopedFile fd = base::OpenFile(path, flags);
58 if (!fd)
59 return false;
60 ssize_t written = base::WriteAll(fd.get(), str.c_str(), str.length());
61 ssize_t length = static_cast<ssize_t>(str.length());
62 // This should either fail or write fully.
63 PERFETTO_CHECK(written == length || written == -1);
64 return written == length;
65 }
66
67 } // namespace
68
69 // static
70 int FtraceProcfs::g_kmesg_fd = -1; // Set by ProbesMain() in probes.cc .
71
72 // static
Create(const std::string & root)73 std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) {
74 if (!CheckRootPath(root)) {
75 return nullptr;
76 }
77 return std::unique_ptr<FtraceProcfs>(new FtraceProcfs(root));
78 }
79
FtraceProcfs(const std::string & root)80 FtraceProcfs::FtraceProcfs(const std::string& root) : root_(root) {}
81 FtraceProcfs::~FtraceProcfs() = default;
82
EnableEvent(const std::string & group,const std::string & name)83 bool FtraceProcfs::EnableEvent(const std::string& group,
84 const std::string& name) {
85 std::string path = root_ + "events/" + group + "/" + name + "/enable";
86 if (WriteToFile(path, "1"))
87 return true;
88 path = root_ + "set_event";
89 return AppendToFile(path, group + ":" + name);
90 }
91
DisableEvent(const std::string & group,const std::string & name)92 bool FtraceProcfs::DisableEvent(const std::string& group,
93 const std::string& name) {
94 std::string path = root_ + "events/" + group + "/" + name + "/enable";
95 if (WriteToFile(path, "0"))
96 return true;
97 path = root_ + "set_event";
98 return AppendToFile(path, "!" + group + ":" + name);
99 }
100
DisableAllEvents()101 bool FtraceProcfs::DisableAllEvents() {
102 std::string path = root_ + "events/enable";
103 return WriteToFile(path, "0");
104 }
105
ReadEventFormat(const std::string & group,const std::string & name) const106 std::string FtraceProcfs::ReadEventFormat(const std::string& group,
107 const std::string& name) const {
108 std::string path = root_ + "events/" + group + "/" + name + "/format";
109 return ReadFileIntoString(path);
110 }
111
ReadEnabledEvents()112 std::vector<std::string> FtraceProcfs::ReadEnabledEvents() {
113 std::string path = root_ + "set_event";
114 std::string s = ReadFileIntoString(path);
115 base::StringSplitter ss(s, '\n');
116 std::vector<std::string> events;
117 while (ss.Next()) {
118 std::string event = ss.cur_token();
119 if (event.size() == 0)
120 continue;
121 events.push_back(base::StripChars(event, ":", '/'));
122 }
123 return events;
124 }
125
ReadPageHeaderFormat() const126 std::string FtraceProcfs::ReadPageHeaderFormat() const {
127 std::string path = root_ + "events/header_page";
128 return ReadFileIntoString(path);
129 }
130
ReadCpuStats(size_t cpu) const131 std::string FtraceProcfs::ReadCpuStats(size_t cpu) const {
132 std::string path = root_ + "per_cpu/cpu" + std::to_string(cpu) + "/stats";
133 return ReadFileIntoString(path);
134 }
135
NumberOfCpus() const136 size_t FtraceProcfs::NumberOfCpus() const {
137 static size_t num_cpus = static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF));
138 return num_cpus;
139 }
140
ClearTrace()141 void FtraceProcfs::ClearTrace() {
142 std::string path = root_ + "trace";
143 PERFETTO_CHECK(ClearFile(path)); // Could not clear.
144
145 // Truncating the trace file leads to tracing_reset_online_cpus being called
146 // in the kernel.
147 //
148 // In case some of the CPUs were not online, their buffer needs to be
149 // cleared manually.
150 //
151 // We cannot use PERFETTO_CHECK as we might get a permission denied error
152 // on Android. The permissions to these files are configured in
153 // platform/framework/native/cmds/atrace/atrace.rc.
154 for (size_t cpu = 0; cpu < NumberOfCpus(); cpu++) {
155 if (!ClearFile(root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace"))
156 PERFETTO_ELOG("Failed to clear buffer for CPU %zd", cpu);
157 }
158 }
159
WriteTraceMarker(const std::string & str)160 bool FtraceProcfs::WriteTraceMarker(const std::string& str) {
161 std::string path = root_ + "trace_marker";
162 return WriteToFile(path, str);
163 }
164
SetCpuBufferSizeInPages(size_t pages)165 bool FtraceProcfs::SetCpuBufferSizeInPages(size_t pages) {
166 if (pages * base::kPageSize > 1 * 1024 * 1024 * 1024) {
167 PERFETTO_ELOG("Tried to set the per CPU buffer size to more than 1gb.");
168 return false;
169 }
170 std::string path = root_ + "buffer_size_kb";
171 return WriteNumberToFile(path, pages * (base::kPageSize / 1024ul));
172 }
173
EnableTracing()174 bool FtraceProcfs::EnableTracing() {
175 KernelLogWrite("perfetto: enabled ftrace\n");
176 PERFETTO_LOG("enabled ftrace");
177 std::string path = root_ + "tracing_on";
178 return WriteToFile(path, "1");
179 }
180
DisableTracing()181 bool FtraceProcfs::DisableTracing() {
182 KernelLogWrite("perfetto: disabled ftrace\n");
183 PERFETTO_LOG("disabled ftrace");
184 std::string path = root_ + "tracing_on";
185 return WriteToFile(path, "0");
186 }
187
SetTracingOn(bool enable)188 bool FtraceProcfs::SetTracingOn(bool enable) {
189 return enable ? EnableTracing() : DisableTracing();
190 }
191
IsTracingEnabled()192 bool FtraceProcfs::IsTracingEnabled() {
193 std::string path = root_ + "tracing_on";
194 char tracing_on = ReadOneCharFromFile(path);
195 if (tracing_on == '\0')
196 PERFETTO_PLOG("Failed to read %s", path.c_str());
197 return tracing_on == '1';
198 }
199
SetClock(const std::string & clock_name)200 bool FtraceProcfs::SetClock(const std::string& clock_name) {
201 std::string path = root_ + "trace_clock";
202 return WriteToFile(path, clock_name);
203 }
204
GetClock()205 std::string FtraceProcfs::GetClock() {
206 std::string path = root_ + "trace_clock";
207 std::string s = ReadFileIntoString(path);
208
209 size_t start = s.find('[');
210 if (start == std::string::npos)
211 return "";
212
213 size_t end = s.find(']', start);
214 if (end == std::string::npos)
215 return "";
216
217 return s.substr(start + 1, end - start - 1);
218 }
219
AvailableClocks()220 std::set<std::string> FtraceProcfs::AvailableClocks() {
221 std::string path = root_ + "trace_clock";
222 std::string s = ReadFileIntoString(path);
223 std::set<std::string> names;
224
225 size_t start = 0;
226 size_t end = 0;
227
228 for (;;) {
229 end = s.find(' ', start);
230 if (end == std::string::npos)
231 end = s.size();
232 while (end > start && s[end - 1] == '\n')
233 end--;
234 if (start == end)
235 break;
236
237 std::string name = s.substr(start, end - start);
238
239 if (name[0] == '[')
240 name = name.substr(1, name.size() - 2);
241
242 names.insert(name);
243
244 if (end == s.size())
245 break;
246
247 start = end + 1;
248 }
249
250 return names;
251 }
252
WriteNumberToFile(const std::string & path,size_t value)253 bool FtraceProcfs::WriteNumberToFile(const std::string& path, size_t value) {
254 // 2^65 requires 20 digits to write.
255 char buf[21];
256 int res = snprintf(buf, 21, "%zu", value);
257 if (res < 0 || res >= 21)
258 return false;
259 return WriteToFile(path, std::string(buf));
260 }
261
WriteToFile(const std::string & path,const std::string & str)262 bool FtraceProcfs::WriteToFile(const std::string& path,
263 const std::string& str) {
264 return WriteFileInternal(path, str, O_WRONLY);
265 }
266
AppendToFile(const std::string & path,const std::string & str)267 bool FtraceProcfs::AppendToFile(const std::string& path,
268 const std::string& str) {
269 return WriteFileInternal(path, str, O_WRONLY | O_APPEND);
270 }
271
OpenPipeForCpu(size_t cpu)272 base::ScopedFile FtraceProcfs::OpenPipeForCpu(size_t cpu) {
273 std::string path =
274 root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace_pipe_raw";
275 return base::OpenFile(path, O_RDONLY | O_NONBLOCK);
276 }
277
ReadOneCharFromFile(const std::string & path)278 char FtraceProcfs::ReadOneCharFromFile(const std::string& path) {
279 base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
280 PERFETTO_CHECK(fd);
281 char result = '\0';
282 ssize_t bytes = PERFETTO_EINTR(read(fd.get(), &result, 1));
283 PERFETTO_CHECK(bytes == 1 || bytes == -1);
284 return result;
285 }
286
ClearFile(const std::string & path)287 bool FtraceProcfs::ClearFile(const std::string& path) {
288 base::ScopedFile fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
289 return !!fd;
290 }
291
ReadFileIntoString(const std::string & path) const292 std::string FtraceProcfs::ReadFileIntoString(const std::string& path) const {
293 // You can't seek or stat the procfs files on Android.
294 // The vast majority (884/886) of format files are under 4k.
295 std::string str;
296 str.reserve(4096);
297 if (!base::ReadFile(path, &str))
298 return "";
299 return str;
300 }
301
GetEventNamesForGroup(const std::string & path) const302 const std::set<std::string> FtraceProcfs::GetEventNamesForGroup(
303 const std::string& path) const {
304 std::set<std::string> names;
305 std::string full_path = root_ + path;
306 base::ScopedDir dir(opendir(full_path.c_str()));
307 if (!dir) {
308 PERFETTO_DLOG("Unable to read events from %s", full_path.c_str());
309 return names;
310 }
311 struct dirent* ent;
312 while ((ent = readdir(*dir)) != nullptr) {
313 if (strncmp(ent->d_name, ".", 1) == 0 ||
314 strncmp(ent->d_name, "..", 2) == 0) {
315 continue;
316 }
317 // Check ent is a directory.
318 struct stat statbuf;
319 std::string dir_path = full_path + "/" + ent->d_name;
320 if (stat(dir_path.c_str(), &statbuf) == 0) {
321 if (S_ISDIR(statbuf.st_mode)) {
322 names.insert(ent->d_name);
323 }
324 }
325 }
326 return names;
327 }
328
329 // static
CheckRootPath(const std::string & root)330 bool FtraceProcfs::CheckRootPath(const std::string& root) {
331 base::ScopedFile fd = base::OpenFile(root + "trace", O_RDONLY);
332 return static_cast<bool>(fd);
333 }
334
335 } // namespace perfetto
336