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
48 namespace {
49 constexpr char kRssStatThrottledTrigger[] =
50 "hist:keys=mm_id,member:bucket=size/0x80000"
51 ":onchange($bucket).rss_stat_throttled(mm_id,curr,member,size)";
52 }
53
KernelLogWrite(const char * s)54 void KernelLogWrite(const char* s) {
55 PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
56 if (FtraceProcfs::g_kmesg_fd != -1)
57 base::ignore_result(base::WriteAll(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
58 }
59
WriteFileInternal(const std::string & path,const std::string & str,int flags)60 bool WriteFileInternal(const std::string& path,
61 const std::string& str,
62 int flags) {
63 base::ScopedFile fd = base::OpenFile(path, flags);
64 if (!fd)
65 return false;
66 ssize_t written = base::WriteAll(fd.get(), str.c_str(), str.length());
67 ssize_t length = static_cast<ssize_t>(str.length());
68 // This should either fail or write fully.
69 PERFETTO_CHECK(written == length || written == -1);
70 return written == length;
71 }
72
73 } // namespace
74
75 // static
76 int FtraceProcfs::g_kmesg_fd = -1; // Set by ProbesMain() in probes.cc .
77
78 const char* const FtraceProcfs::kTracingPaths[] = {
79 "/sys/kernel/tracing/",
80 "/sys/kernel/debug/tracing/",
81 nullptr,
82 };
83
84 // static
CreateGuessingMountPoint(const std::string & instance_path)85 std::unique_ptr<FtraceProcfs> FtraceProcfs::CreateGuessingMountPoint(
86 const std::string& instance_path) {
87 std::unique_ptr<FtraceProcfs> ftrace_procfs;
88 size_t index = 0;
89 while (!ftrace_procfs && kTracingPaths[index]) {
90 std::string path = kTracingPaths[index++];
91 if (!instance_path.empty())
92 path += instance_path;
93
94 ftrace_procfs = Create(path);
95 }
96 return ftrace_procfs;
97 }
98
99 // static
Create(const std::string & root)100 std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) {
101 if (!CheckRootPath(root)) {
102 return nullptr;
103 }
104 return std::unique_ptr<FtraceProcfs>(new FtraceProcfs(root));
105 }
106
FtraceProcfs(const std::string & root)107 FtraceProcfs::FtraceProcfs(const std::string& root) : root_(root) {}
108 FtraceProcfs::~FtraceProcfs() = default;
109
EnableEvent(const std::string & group,const std::string & name)110 bool FtraceProcfs::EnableEvent(const std::string& group,
111 const std::string& name) {
112 std::string path = root_ + "events/" + group + "/" + name + "/enable";
113
114 // Create any required triggers for the ftrace event being enabled.
115 // Some ftrace events (synthetic events) need to set up an event trigger
116 MaybeSetUpEventTriggers(group, name);
117
118 if (WriteToFile(path, "1"))
119 return true;
120 path = root_ + "set_event";
121 return AppendToFile(path, group + ":" + name);
122 }
123
DisableEvent(const std::string & group,const std::string & name)124 bool FtraceProcfs::DisableEvent(const std::string& group,
125 const std::string& name) {
126 std::string path = root_ + "events/" + group + "/" + name + "/enable";
127
128 bool ret = WriteToFile(path, "0");
129 if (!ret) {
130 path = root_ + "set_event";
131 ret = AppendToFile(path, "!" + group + ":" + name);
132 }
133
134 // Remove any associated event triggers after disabling the event
135 MaybeTearDownEventTriggers(group, name);
136
137 return ret;
138 }
139
DisableAllEvents()140 bool FtraceProcfs::DisableAllEvents() {
141 std::string path = root_ + "events/enable";
142 return WriteToFile(path, "0");
143 }
144
ReadEventFormat(const std::string & group,const std::string & name) const145 std::string FtraceProcfs::ReadEventFormat(const std::string& group,
146 const std::string& name) const {
147 std::string path = root_ + "events/" + group + "/" + name + "/format";
148 return ReadFileIntoString(path);
149 }
150
ReadEventTriggers(const std::string & group,const std::string & name) const151 std::vector<std::string> FtraceProcfs::ReadEventTriggers(
152 const std::string& group,
153 const std::string& name) const {
154 std::string path = root_ + "events/" + group + "/" + name + "/trigger";
155 std::string s = ReadFileIntoString(path);
156 std::vector<std::string> triggers;
157
158 for (base::StringSplitter ss(s, '\n'); ss.Next();) {
159 std::string trigger = ss.cur_token();
160 if (trigger.empty() || trigger[0] == '#')
161 continue;
162
163 base::StringSplitter ts(trigger, ' ');
164 PERFETTO_CHECK(ts.Next());
165 triggers.push_back(ts.cur_token());
166 }
167
168 return triggers;
169 }
170
CreateEventTrigger(const std::string & group,const std::string & name,const std::string & trigger)171 bool FtraceProcfs::CreateEventTrigger(const std::string& group,
172 const std::string& name,
173 const std::string& trigger) {
174 std::string path = root_ + "events/" + group + "/" + name + "/trigger";
175 return WriteToFile(path, trigger);
176 }
177
RemoveEventTrigger(const std::string & group,const std::string & name,const std::string & trigger)178 bool FtraceProcfs::RemoveEventTrigger(const std::string& group,
179 const std::string& name,
180 const std::string& trigger) {
181 std::string path = root_ + "events/" + group + "/" + name + "/trigger";
182 return WriteToFile(path, "!" + trigger);
183 }
184
RemoveAllEventTriggers(const std::string & group,const std::string & name)185 bool FtraceProcfs::RemoveAllEventTriggers(const std::string& group,
186 const std::string& name) {
187 std::vector<std::string> triggers = ReadEventTriggers(group, name);
188
189 // Remove the triggers in reverse order since a trigger can depend
190 // on another trigger created earlier.
191 for (auto it = triggers.rbegin(); it != triggers.rend(); ++it)
192 if (!RemoveEventTrigger(group, name, *it))
193 return false;
194 return true;
195 }
196
MaybeSetUpEventTriggers(const std::string & group,const std::string & name)197 bool FtraceProcfs::MaybeSetUpEventTriggers(const std::string& group,
198 const std::string& name) {
199 bool ret = true;
200
201 if (group == "synthetic" && name == "rss_stat_throttled") {
202 ret = RemoveAllEventTriggers("kmem", "rss_stat") &&
203 CreateEventTrigger("kmem", "rss_stat", kRssStatThrottledTrigger);
204 }
205
206 if (!ret) {
207 PERFETTO_PLOG("Failed to setup event triggers for %s:%s", group.c_str(),
208 name.c_str());
209 }
210
211 return ret;
212 }
213
MaybeTearDownEventTriggers(const std::string & group,const std::string & name)214 bool FtraceProcfs::MaybeTearDownEventTriggers(const std::string& group,
215 const std::string& name) {
216 bool ret = true;
217
218 if (group == "synthetic" && name == "rss_stat_throttled")
219 ret = RemoveAllEventTriggers("kmem", "rss_stat");
220
221 if (!ret) {
222 PERFETTO_PLOG("Failed to tear down event triggers for: %s:%s",
223 group.c_str(), name.c_str());
224 }
225
226 return ret;
227 }
228
SupportsRssStatThrottled()229 bool FtraceProcfs::SupportsRssStatThrottled() {
230 std::string group = "synthetic";
231 std::string name = "rss_stat_throttled";
232
233 // Check if the trigger already exists. Don't try recreating
234 // or removing the trigger if it is already in use.
235 auto triggers = ReadEventTriggers("kmem", "rss_stat");
236 for (const auto& trigger : triggers) {
237 // The kernel shows all the default values of a trigger
238 // when read from and trace event 'trigger' file.
239 //
240 // Trying to match the complete trigger string is prone
241 // to fail if, in the future, the kernel changes default
242 // fields or values for event triggers.
243 //
244 // Do a partial match on the generated event name
245 // (rss_stat_throttled) to detect if the trigger
246 // is already created.
247 if (trigger.find(name) != std::string::npos)
248 return true;
249 }
250
251 // Attempt to create rss_stat_throttled hist trigger */
252 bool ret = MaybeSetUpEventTriggers(group, name);
253
254 return ret && MaybeTearDownEventTriggers(group, name);
255 }
256
ReadPrintkFormats() const257 std::string FtraceProcfs::ReadPrintkFormats() const {
258 std::string path = root_ + "printk_formats";
259 return ReadFileIntoString(path);
260 }
261
ReadEnabledEvents()262 std::vector<std::string> FtraceProcfs::ReadEnabledEvents() {
263 std::string path = root_ + "set_event";
264 std::string s = ReadFileIntoString(path);
265 base::StringSplitter ss(s, '\n');
266 std::vector<std::string> events;
267 while (ss.Next()) {
268 std::string event = ss.cur_token();
269 if (event.empty())
270 continue;
271 events.push_back(base::StripChars(event, ":", '/'));
272 }
273 return events;
274 }
275
ReadPageHeaderFormat() const276 std::string FtraceProcfs::ReadPageHeaderFormat() const {
277 std::string path = root_ + "events/header_page";
278 return ReadFileIntoString(path);
279 }
280
OpenCpuStats(size_t cpu) const281 base::ScopedFile FtraceProcfs::OpenCpuStats(size_t cpu) const {
282 std::string path = root_ + "per_cpu/cpu" + std::to_string(cpu) + "/stats";
283 return base::OpenFile(path, O_RDONLY);
284 }
285
ReadCpuStats(size_t cpu) const286 std::string FtraceProcfs::ReadCpuStats(size_t cpu) const {
287 std::string path = root_ + "per_cpu/cpu" + std::to_string(cpu) + "/stats";
288 return ReadFileIntoString(path);
289 }
290
NumberOfCpus() const291 size_t FtraceProcfs::NumberOfCpus() const {
292 static size_t num_cpus = static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF));
293 return num_cpus;
294 }
295
ClearTrace()296 void FtraceProcfs::ClearTrace() {
297 std::string path = root_ + "trace";
298 PERFETTO_CHECK(ClearFile(path)); // Could not clear.
299
300 // Truncating the trace file leads to tracing_reset_online_cpus being called
301 // in the kernel.
302 //
303 // In case some of the CPUs were not online, their buffer needs to be
304 // cleared manually.
305 //
306 // We cannot use PERFETTO_CHECK as we might get a permission denied error
307 // on Android. The permissions to these files are configured in
308 // platform/framework/native/cmds/atrace/atrace.rc.
309 for (size_t cpu = 0; cpu < NumberOfCpus(); cpu++) {
310 ClearPerCpuTrace(cpu);
311 }
312 }
313
ClearPerCpuTrace(size_t cpu)314 void FtraceProcfs::ClearPerCpuTrace(size_t cpu) {
315 if (!ClearFile(root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace"))
316 PERFETTO_ELOG("Failed to clear buffer for CPU %zd", cpu);
317 }
318
WriteTraceMarker(const std::string & str)319 bool FtraceProcfs::WriteTraceMarker(const std::string& str) {
320 std::string path = root_ + "trace_marker";
321 return WriteToFile(path, str);
322 }
323
SetCpuBufferSizeInPages(size_t pages)324 bool FtraceProcfs::SetCpuBufferSizeInPages(size_t pages) {
325 if (pages * base::kPageSize > 1 * 1024 * 1024 * 1024) {
326 PERFETTO_ELOG("Tried to set the per CPU buffer size to more than 1gb.");
327 return false;
328 }
329 std::string path = root_ + "buffer_size_kb";
330 return WriteNumberToFile(path, pages * (base::kPageSize / 1024ul));
331 }
332
EnableTracing()333 bool FtraceProcfs::EnableTracing() {
334 KernelLogWrite("perfetto: enabled ftrace\n");
335 PERFETTO_LOG("enabled ftrace in %s", root_.c_str());
336 std::string path = root_ + "tracing_on";
337 return WriteToFile(path, "1");
338 }
339
DisableTracing()340 bool FtraceProcfs::DisableTracing() {
341 KernelLogWrite("perfetto: disabled ftrace\n");
342 PERFETTO_LOG("disabled ftrace in %s", root_.c_str());
343 std::string path = root_ + "tracing_on";
344 return WriteToFile(path, "0");
345 }
346
SetTracingOn(bool enable)347 bool FtraceProcfs::SetTracingOn(bool enable) {
348 return enable ? EnableTracing() : DisableTracing();
349 }
350
IsTracingEnabled()351 bool FtraceProcfs::IsTracingEnabled() {
352 std::string path = root_ + "tracing_on";
353 char tracing_on = ReadOneCharFromFile(path);
354 if (tracing_on == '\0')
355 PERFETTO_PLOG("Failed to read %s", path.c_str());
356 return tracing_on == '1';
357 }
358
SetClock(const std::string & clock_name)359 bool FtraceProcfs::SetClock(const std::string& clock_name) {
360 std::string path = root_ + "trace_clock";
361 return WriteToFile(path, clock_name);
362 }
363
GetClock()364 std::string FtraceProcfs::GetClock() {
365 std::string path = root_ + "trace_clock";
366 std::string s = ReadFileIntoString(path);
367
368 size_t start = s.find('[');
369 if (start == std::string::npos)
370 return "";
371
372 size_t end = s.find(']', start);
373 if (end == std::string::npos)
374 return "";
375
376 return s.substr(start + 1, end - start - 1);
377 }
378
AvailableClocks()379 std::set<std::string> FtraceProcfs::AvailableClocks() {
380 std::string path = root_ + "trace_clock";
381 std::string s = ReadFileIntoString(path);
382 std::set<std::string> names;
383
384 size_t start = 0;
385 size_t end = 0;
386
387 for (;;) {
388 end = s.find(' ', start);
389 if (end == std::string::npos)
390 end = s.size();
391 while (end > start && s[end - 1] == '\n')
392 end--;
393 if (start == end)
394 break;
395
396 std::string name = s.substr(start, end - start);
397
398 if (name[0] == '[')
399 name = name.substr(1, name.size() - 2);
400
401 names.insert(name);
402
403 if (end == s.size())
404 break;
405
406 start = end + 1;
407 }
408
409 return names;
410 }
411
WriteNumberToFile(const std::string & path,size_t value)412 bool FtraceProcfs::WriteNumberToFile(const std::string& path, size_t value) {
413 // 2^65 requires 20 digits to write.
414 char buf[21];
415 snprintf(buf, sizeof(buf), "%zu", value);
416 return WriteToFile(path, std::string(buf));
417 }
418
WriteToFile(const std::string & path,const std::string & str)419 bool FtraceProcfs::WriteToFile(const std::string& path,
420 const std::string& str) {
421 return WriteFileInternal(path, str, O_WRONLY);
422 }
423
AppendToFile(const std::string & path,const std::string & str)424 bool FtraceProcfs::AppendToFile(const std::string& path,
425 const std::string& str) {
426 return WriteFileInternal(path, str, O_WRONLY | O_APPEND);
427 }
428
OpenPipeForCpu(size_t cpu)429 base::ScopedFile FtraceProcfs::OpenPipeForCpu(size_t cpu) {
430 std::string path =
431 root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace_pipe_raw";
432 return base::OpenFile(path, O_RDONLY | O_NONBLOCK);
433 }
434
ReadOneCharFromFile(const std::string & path)435 char FtraceProcfs::ReadOneCharFromFile(const std::string& path) {
436 base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
437 PERFETTO_CHECK(fd);
438 char result = '\0';
439 ssize_t bytes = PERFETTO_EINTR(read(fd.get(), &result, 1));
440 PERFETTO_CHECK(bytes == 1 || bytes == -1);
441 return result;
442 }
443
ClearFile(const std::string & path)444 bool FtraceProcfs::ClearFile(const std::string& path) {
445 base::ScopedFile fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
446 return !!fd;
447 }
448
ReadFileIntoString(const std::string & path) const449 std::string FtraceProcfs::ReadFileIntoString(const std::string& path) const {
450 // You can't seek or stat the procfs files on Android.
451 // The vast majority (884/886) of format files are under 4k.
452 std::string str;
453 str.reserve(4096);
454 if (!base::ReadFile(path, &str))
455 return "";
456 return str;
457 }
458
GetEventNamesForGroup(const std::string & path) const459 const std::set<std::string> FtraceProcfs::GetEventNamesForGroup(
460 const std::string& path) const {
461 std::set<std::string> names;
462 std::string full_path = root_ + path;
463 base::ScopedDir dir(opendir(full_path.c_str()));
464 if (!dir) {
465 PERFETTO_DLOG("Unable to read events from %s", full_path.c_str());
466 return names;
467 }
468 struct dirent* ent;
469 while ((ent = readdir(*dir)) != nullptr) {
470 if (strncmp(ent->d_name, ".", 1) == 0 ||
471 strncmp(ent->d_name, "..", 2) == 0) {
472 continue;
473 }
474 // Check ent is a directory.
475 struct stat statbuf;
476 std::string dir_path = full_path + "/" + ent->d_name;
477 if (stat(dir_path.c_str(), &statbuf) == 0) {
478 if (S_ISDIR(statbuf.st_mode)) {
479 names.insert(ent->d_name);
480 }
481 }
482 }
483 return names;
484 }
485
ReadEventId(const std::string & group,const std::string & name) const486 uint32_t FtraceProcfs::ReadEventId(const std::string& group,
487 const std::string& name) const {
488 std::string path = root_ + "events/" + group + "/" + name + "/id";
489
490 std::string str;
491 if (!base::ReadFile(path, &str))
492 return 0;
493
494 if (str.size() && str[str.size() - 1] == '\n')
495 str.resize(str.size() - 1);
496
497 base::Optional<uint32_t> id = base::StringToUInt32(str);
498 if (!id)
499 return 0;
500 return *id;
501 }
502
503 // static
CheckRootPath(const std::string & root)504 bool FtraceProcfs::CheckRootPath(const std::string& root) {
505 base::ScopedFile fd = base::OpenFile(root + "trace", O_RDONLY);
506 return static_cast<bool>(fd);
507 }
508
509 } // namespace perfetto
510