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 constexpr char kSuspendResumeMinimalTrigger[] =
54 "hist:keys=start:size=128:onmatch(power.suspend_resume)"
55 ".trace(suspend_resume_minimal, start) if action == 'syscore_resume'";
56 }
57
KernelLogWrite(const char * s)58 void KernelLogWrite(const char* s) {
59 PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
60 if (FtraceProcfs::g_kmesg_fd != -1)
61 base::ignore_result(base::WriteAll(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
62 }
63
WriteFileInternal(const std::string & path,const std::string & str,int flags)64 bool WriteFileInternal(const std::string& path,
65 const std::string& str,
66 int flags) {
67 base::ScopedFile fd = base::OpenFile(path, flags);
68 if (!fd)
69 return false;
70 ssize_t written = base::WriteAll(fd.get(), str.c_str(), str.length());
71 ssize_t length = static_cast<ssize_t>(str.length());
72 // This should either fail or write fully.
73 PERFETTO_CHECK(written == length || written == -1);
74 return written == length;
75 }
76
77 } // namespace
78
79 // static
80 int FtraceProcfs::g_kmesg_fd = -1; // Set by ProbesMain() in probes.cc .
81
82 const char* const FtraceProcfs::kTracingPaths[] = {
83 "/sys/kernel/tracing/",
84 "/sys/kernel/debug/tracing/",
85 nullptr,
86 };
87
88 // static
CreateGuessingMountPoint(const std::string & instance_path)89 std::unique_ptr<FtraceProcfs> FtraceProcfs::CreateGuessingMountPoint(
90 const std::string& instance_path) {
91 std::unique_ptr<FtraceProcfs> ftrace_procfs;
92 size_t index = 0;
93 while (!ftrace_procfs && kTracingPaths[index]) {
94 std::string path = kTracingPaths[index++];
95 if (!instance_path.empty())
96 path += instance_path;
97
98 ftrace_procfs = Create(path);
99 }
100 return ftrace_procfs;
101 }
102
103 // static
Create(const std::string & root)104 std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) {
105 if (!CheckRootPath(root))
106 return nullptr;
107 return std::unique_ptr<FtraceProcfs>(new FtraceProcfs(root));
108 }
109
FtraceProcfs(const std::string & root)110 FtraceProcfs::FtraceProcfs(const std::string& root) : root_(root) {}
111 FtraceProcfs::~FtraceProcfs() = default;
112
SetSyscallFilter(const std::set<size_t> & filter)113 bool FtraceProcfs::SetSyscallFilter(const std::set<size_t>& filter) {
114 std::vector<std::string> parts;
115 for (size_t id : filter) {
116 base::StackString<16> m("id == %zu", id);
117 parts.push_back(m.ToStdString());
118 }
119
120 std::string filter_str = "0";
121 if (!parts.empty()) {
122 filter_str = base::Join(parts, " || ");
123 }
124
125 for (const char* event : {"sys_enter", "sys_exit"}) {
126 std::string path = root_ + "events/raw_syscalls/" + event + "/filter";
127 if (!WriteToFile(path, filter_str)) {
128 PERFETTO_ELOG("Failed to write file: %s", path.c_str());
129 return false;
130 }
131 }
132 return true;
133 }
134
EnableEvent(const std::string & group,const std::string & name)135 bool FtraceProcfs::EnableEvent(const std::string& group,
136 const std::string& name) {
137 std::string path = root_ + "events/" + group + "/" + name + "/enable";
138
139 // Create any required triggers for the ftrace event being enabled.
140 // Some ftrace events (synthetic events) need to set up an event trigger
141 MaybeSetUpEventTriggers(group, name);
142
143 if (WriteToFile(path, "1"))
144 return true;
145 path = root_ + "set_event";
146 return AppendToFile(path, group + ":" + name);
147 }
148
DisableEvent(const std::string & group,const std::string & name)149 bool FtraceProcfs::DisableEvent(const std::string& group,
150 const std::string& name) {
151 std::string path = root_ + "events/" + group + "/" + name + "/enable";
152
153 bool ret = WriteToFile(path, "0");
154 if (!ret) {
155 path = root_ + "set_event";
156 ret = AppendToFile(path, "!" + group + ":" + name);
157 }
158
159 // Remove any associated event triggers after disabling the event
160 MaybeTearDownEventTriggers(group, name);
161
162 return ret;
163 }
164
IsEventAccessible(const std::string & group,const std::string & name)165 bool FtraceProcfs::IsEventAccessible(const std::string& group,
166 const std::string& name) {
167 std::string path = root_ + "events/" + group + "/" + name + "/enable";
168
169 return IsFileWriteable(path);
170 }
171
DisableAllEvents()172 bool FtraceProcfs::DisableAllEvents() {
173 std::string path = root_ + "events/enable";
174 return WriteToFile(path, "0");
175 }
176
ReadEventFormat(const std::string & group,const std::string & name) const177 std::string FtraceProcfs::ReadEventFormat(const std::string& group,
178 const std::string& name) const {
179 std::string path = root_ + "events/" + group + "/" + name + "/format";
180 return ReadFileIntoString(path);
181 }
182
GetCurrentTracer()183 std::string FtraceProcfs::GetCurrentTracer() {
184 std::string path = root_ + "current_tracer";
185 std::string current_tracer = ReadFileIntoString(path);
186 return base::StripSuffix(current_tracer, "\n");
187 }
188
SetCurrentTracer(const std::string & tracer)189 bool FtraceProcfs::SetCurrentTracer(const std::string& tracer) {
190 std::string path = root_ + "current_tracer";
191 return WriteToFile(path, tracer);
192 }
193
ResetCurrentTracer()194 bool FtraceProcfs::ResetCurrentTracer() {
195 return SetCurrentTracer("nop");
196 }
197
AppendFunctionFilters(const std::vector<std::string> & filters)198 bool FtraceProcfs::AppendFunctionFilters(
199 const std::vector<std::string>& filters) {
200 std::string path = root_ + "set_ftrace_filter";
201 std::string filter = base::Join(filters, "\n");
202
203 // The same file accepts special actions to perform when a corresponding
204 // kernel function is hit (regardless of active tracer). For example
205 // "__schedule_bug:traceoff" would disable tracing once __schedule_bug is
206 // called.
207 // We disallow these commands as most of them break the isolation of
208 // concurrent ftrace data sources (as the underlying ftrace instance is
209 // shared).
210 if (base::Contains(filter, ':')) {
211 PERFETTO_ELOG("Filter commands are disallowed.");
212 return false;
213 }
214 return AppendToFile(path, filter);
215 }
216
ClearFunctionFilters()217 bool FtraceProcfs::ClearFunctionFilters() {
218 std::string path = root_ + "set_ftrace_filter";
219 return ClearFile(path);
220 }
221
AppendFunctionGraphFilters(const std::vector<std::string> & filters)222 bool FtraceProcfs::AppendFunctionGraphFilters(
223 const std::vector<std::string>& filters) {
224 std::string path = root_ + "set_graph_function";
225 std::string filter = base::Join(filters, "\n");
226 return AppendToFile(path, filter);
227 }
228
ClearFunctionGraphFilters()229 bool FtraceProcfs::ClearFunctionGraphFilters() {
230 std::string path = root_ + "set_graph_function";
231 return ClearFile(path);
232 }
233
ReadEventTriggers(const std::string & group,const std::string & name) const234 std::vector<std::string> FtraceProcfs::ReadEventTriggers(
235 const std::string& group,
236 const std::string& name) const {
237 std::string path = root_ + "events/" + group + "/" + name + "/trigger";
238 std::string s = ReadFileIntoString(path);
239 std::vector<std::string> triggers;
240
241 for (base::StringSplitter ss(s, '\n'); ss.Next();) {
242 std::string trigger = ss.cur_token();
243 if (trigger.empty() || trigger[0] == '#')
244 continue;
245
246 base::StringSplitter ts(trigger, ' ');
247 PERFETTO_CHECK(ts.Next());
248 triggers.push_back(ts.cur_token());
249 }
250
251 return triggers;
252 }
253
CreateEventTrigger(const std::string & group,const std::string & name,const std::string & trigger)254 bool FtraceProcfs::CreateEventTrigger(const std::string& group,
255 const std::string& name,
256 const std::string& trigger) {
257 std::string path = root_ + "events/" + group + "/" + name + "/trigger";
258 return WriteToFile(path, trigger);
259 }
260
RemoveEventTrigger(const std::string & group,const std::string & name,const std::string & trigger)261 bool FtraceProcfs::RemoveEventTrigger(const std::string& group,
262 const std::string& name,
263 const std::string& trigger) {
264 std::string path = root_ + "events/" + group + "/" + name + "/trigger";
265 return WriteToFile(path, "!" + trigger);
266 }
267
RemoveAllEventTriggers(const std::string & group,const std::string & name)268 bool FtraceProcfs::RemoveAllEventTriggers(const std::string& group,
269 const std::string& name) {
270 std::vector<std::string> triggers = ReadEventTriggers(group, name);
271
272 // Remove the triggers in reverse order since a trigger can depend
273 // on another trigger created earlier.
274 for (auto it = triggers.rbegin(); it != triggers.rend(); ++it)
275 if (!RemoveEventTrigger(group, name, *it))
276 return false;
277 return true;
278 }
279
MaybeSetUpEventTriggers(const std::string & group,const std::string & name)280 bool FtraceProcfs::MaybeSetUpEventTriggers(const std::string& group,
281 const std::string& name) {
282 bool ret = true;
283
284 if (group == "synthetic") {
285 if (name == "rss_stat_throttled") {
286 ret = RemoveAllEventTriggers("kmem", "rss_stat") &&
287 CreateEventTrigger("kmem", "rss_stat", kRssStatThrottledTrigger);
288 } else if (name == "suspend_resume_minimal") {
289 ret = RemoveAllEventTriggers("power", "suspend_resume") &&
290 CreateEventTrigger("power", "suspend_resume", kSuspendResumeMinimalTrigger);
291 }
292 }
293
294 if (!ret) {
295 PERFETTO_PLOG("Failed to setup event triggers for %s:%s", group.c_str(),
296 name.c_str());
297 }
298
299 return ret;
300 }
301
MaybeTearDownEventTriggers(const std::string & group,const std::string & name)302 bool FtraceProcfs::MaybeTearDownEventTriggers(const std::string& group,
303 const std::string& name) {
304 bool ret = true;
305
306 if (group == "synthetic") {
307 if (name == "rss_stat_throttled") {
308 ret = RemoveAllEventTriggers("kmem", "rss_stat");
309 } else if (name == "suspend_resume_minimal") {
310 ret = RemoveEventTrigger("power", "suspend_resume", kSuspendResumeMinimalTrigger);
311 }
312 }
313
314 if (!ret) {
315 PERFETTO_PLOG("Failed to tear down event triggers for: %s:%s",
316 group.c_str(), name.c_str());
317 }
318
319 return ret;
320 }
321
SupportsRssStatThrottled()322 bool FtraceProcfs::SupportsRssStatThrottled() {
323 std::string group = "synthetic";
324 std::string name = "rss_stat_throttled";
325
326 // Check if the trigger already exists. Don't try recreating
327 // or removing the trigger if it is already in use.
328 auto triggers = ReadEventTriggers("kmem", "rss_stat");
329 for (const auto& trigger : triggers) {
330 // The kernel shows all the default values of a trigger
331 // when read from and trace event 'trigger' file.
332 //
333 // Trying to match the complete trigger string is prone
334 // to fail if, in the future, the kernel changes default
335 // fields or values for event triggers.
336 //
337 // Do a partial match on the generated event name
338 // (rss_stat_throttled) to detect if the trigger
339 // is already created.
340 if (trigger.find(name) != std::string::npos)
341 return true;
342 }
343
344 // Attempt to create rss_stat_throttled hist trigger */
345 bool ret = MaybeSetUpEventTriggers(group, name);
346
347 return ret && MaybeTearDownEventTriggers(group, name);
348 }
349
ReadPrintkFormats() const350 std::string FtraceProcfs::ReadPrintkFormats() const {
351 std::string path = root_ + "printk_formats";
352 return ReadFileIntoString(path);
353 }
354
ReadEnabledEvents()355 std::vector<std::string> FtraceProcfs::ReadEnabledEvents() {
356 std::string path = root_ + "set_event";
357 std::string s = ReadFileIntoString(path);
358 base::StringSplitter ss(s, '\n');
359 std::vector<std::string> events;
360 while (ss.Next()) {
361 std::string event = ss.cur_token();
362 if (event.empty())
363 continue;
364 events.push_back(base::StripChars(event, ":", '/'));
365 }
366 return events;
367 }
368
ReadPageHeaderFormat() const369 std::string FtraceProcfs::ReadPageHeaderFormat() const {
370 std::string path = root_ + "events/header_page";
371 return ReadFileIntoString(path);
372 }
373
OpenCpuStats(size_t cpu) const374 base::ScopedFile FtraceProcfs::OpenCpuStats(size_t cpu) const {
375 std::string path = root_ + "per_cpu/cpu" + std::to_string(cpu) + "/stats";
376 return base::OpenFile(path, O_RDONLY);
377 }
378
ReadCpuStats(size_t cpu) const379 std::string FtraceProcfs::ReadCpuStats(size_t cpu) const {
380 std::string path = root_ + "per_cpu/cpu" + std::to_string(cpu) + "/stats";
381 return ReadFileIntoString(path);
382 }
383
NumberOfCpus() const384 size_t FtraceProcfs::NumberOfCpus() const {
385 static size_t num_cpus = static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF));
386 return num_cpus;
387 }
388
ClearTrace()389 void FtraceProcfs::ClearTrace() {
390 std::string path = root_ + "trace";
391 PERFETTO_CHECK(ClearFile(path)); // Could not clear.
392
393 // Truncating the trace file leads to tracing_reset_online_cpus being called
394 // in the kernel.
395 //
396 // In case some of the CPUs were not online, their buffer needs to be
397 // cleared manually.
398 //
399 // We cannot use PERFETTO_CHECK as we might get a permission denied error
400 // on Android. The permissions to these files are configured in
401 // platform/framework/native/cmds/atrace/atrace.rc.
402 for (size_t cpu = 0; cpu < NumberOfCpus(); cpu++) {
403 ClearPerCpuTrace(cpu);
404 }
405 }
406
ClearPerCpuTrace(size_t cpu)407 void FtraceProcfs::ClearPerCpuTrace(size_t cpu) {
408 if (!ClearFile(root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace"))
409 PERFETTO_ELOG("Failed to clear buffer for CPU %zd", cpu);
410 }
411
WriteTraceMarker(const std::string & str)412 bool FtraceProcfs::WriteTraceMarker(const std::string& str) {
413 std::string path = root_ + "trace_marker";
414 return WriteToFile(path, str);
415 }
416
SetCpuBufferSizeInPages(size_t pages)417 bool FtraceProcfs::SetCpuBufferSizeInPages(size_t pages) {
418 if (pages * base::kPageSize > 1 * 1024 * 1024 * 1024) {
419 PERFETTO_ELOG("Tried to set the per CPU buffer size to more than 1gb.");
420 return false;
421 }
422 std::string path = root_ + "buffer_size_kb";
423 return WriteNumberToFile(path, pages * (base::kPageSize / 1024ul));
424 }
425
GetTracingOn()426 bool FtraceProcfs::GetTracingOn() {
427 std::string path = root_ + "tracing_on";
428 char tracing_on = ReadOneCharFromFile(path);
429 if (tracing_on == '\0')
430 PERFETTO_PLOG("Failed to read %s", path.c_str());
431 return tracing_on == '1';
432 }
433
SetTracingOn(bool on)434 bool FtraceProcfs::SetTracingOn(bool on) {
435 std::string path = root_ + "tracing_on";
436 if (!WriteToFile(path, on ? "1" : "0")) {
437 PERFETTO_PLOG("Failed to write %s", path.c_str());
438 return false;
439 }
440 if (on) {
441 KernelLogWrite("perfetto: enabled ftrace\n");
442 PERFETTO_LOG("enabled ftrace in %s", root_.c_str());
443 } else {
444 KernelLogWrite("perfetto: disabled ftrace\n");
445 PERFETTO_LOG("disabled ftrace in %s", root_.c_str());
446 }
447
448 return true;
449 }
450
IsTracingAvailable()451 bool FtraceProcfs::IsTracingAvailable() {
452 std::string current_tracer = GetCurrentTracer();
453
454 // Ftrace tracing is available if current_tracer == "nop".
455 // events/enable could be 0, 1, X or 0*. 0* means events would be
456 // dynamically enabled so we need to treat as event tracing is in use.
457 // However based on the discussion in asop/2328817, on Android events/enable
458 // is "X" after boot up. To avoid causing more problem, the decision is just
459 // look at current_tracer.
460 // As the discussion in asop/2328817, if GetCurrentTracer failed to
461 // read file and return "", we treat it as tracing is available.
462 return current_tracer == "nop" || current_tracer == "";
463 }
464
SetClock(const std::string & clock_name)465 bool FtraceProcfs::SetClock(const std::string& clock_name) {
466 std::string path = root_ + "trace_clock";
467 return WriteToFile(path, clock_name);
468 }
469
GetClock()470 std::string FtraceProcfs::GetClock() {
471 std::string path = root_ + "trace_clock";
472 std::string s = ReadFileIntoString(path);
473
474 size_t start = s.find('[');
475 if (start == std::string::npos)
476 return "";
477
478 size_t end = s.find(']', start);
479 if (end == std::string::npos)
480 return "";
481
482 return s.substr(start + 1, end - start - 1);
483 }
484
AvailableClocks()485 std::set<std::string> FtraceProcfs::AvailableClocks() {
486 std::string path = root_ + "trace_clock";
487 std::string s = ReadFileIntoString(path);
488 std::set<std::string> names;
489
490 size_t start = 0;
491 size_t end = 0;
492
493 for (;;) {
494 end = s.find(' ', start);
495 if (end == std::string::npos)
496 end = s.size();
497 while (end > start && s[end - 1] == '\n')
498 end--;
499 if (start == end)
500 break;
501
502 std::string name = s.substr(start, end - start);
503
504 if (name[0] == '[')
505 name = name.substr(1, name.size() - 2);
506
507 names.insert(name);
508
509 if (end == s.size())
510 break;
511
512 start = end + 1;
513 }
514
515 return names;
516 }
517
WriteNumberToFile(const std::string & path,size_t value)518 bool FtraceProcfs::WriteNumberToFile(const std::string& path, size_t value) {
519 // 2^65 requires 20 digits to write.
520 char buf[21];
521 snprintf(buf, sizeof(buf), "%zu", value);
522 return WriteToFile(path, std::string(buf));
523 }
524
WriteToFile(const std::string & path,const std::string & str)525 bool FtraceProcfs::WriteToFile(const std::string& path,
526 const std::string& str) {
527 return WriteFileInternal(path, str, O_WRONLY);
528 }
529
AppendToFile(const std::string & path,const std::string & str)530 bool FtraceProcfs::AppendToFile(const std::string& path,
531 const std::string& str) {
532 return WriteFileInternal(path, str, O_WRONLY | O_APPEND);
533 }
534
OpenPipeForCpu(size_t cpu)535 base::ScopedFile FtraceProcfs::OpenPipeForCpu(size_t cpu) {
536 std::string path =
537 root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace_pipe_raw";
538 return base::OpenFile(path, O_RDONLY | O_NONBLOCK);
539 }
540
ReadOneCharFromFile(const std::string & path)541 char FtraceProcfs::ReadOneCharFromFile(const std::string& path) {
542 base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
543 PERFETTO_CHECK(fd);
544 char result = '\0';
545 ssize_t bytes = PERFETTO_EINTR(read(fd.get(), &result, 1));
546 PERFETTO_CHECK(bytes == 1 || bytes == -1);
547 return result;
548 }
549
ClearFile(const std::string & path)550 bool FtraceProcfs::ClearFile(const std::string& path) {
551 base::ScopedFile fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
552 return !!fd;
553 }
554
IsFileWriteable(const std::string & path)555 bool FtraceProcfs::IsFileWriteable(const std::string& path) {
556 return access(path.c_str(), W_OK) == 0;
557 }
558
ReadFileIntoString(const std::string & path) const559 std::string FtraceProcfs::ReadFileIntoString(const std::string& path) const {
560 // You can't seek or stat the procfs files on Android.
561 // The vast majority (884/886) of format files are under 4k.
562 std::string str;
563 str.reserve(4096);
564 if (!base::ReadFile(path, &str))
565 return "";
566 return str;
567 }
568
GetEventNamesForGroup(const std::string & path) const569 const std::set<std::string> FtraceProcfs::GetEventNamesForGroup(
570 const std::string& path) const {
571 std::set<std::string> names;
572 std::string full_path = root_ + path;
573 base::ScopedDir dir(opendir(full_path.c_str()));
574 if (!dir) {
575 PERFETTO_DLOG("Unable to read events from %s", full_path.c_str());
576 return names;
577 }
578 struct dirent* ent;
579 while ((ent = readdir(*dir)) != nullptr) {
580 if (strncmp(ent->d_name, ".", 1) == 0 ||
581 strncmp(ent->d_name, "..", 2) == 0) {
582 continue;
583 }
584 // Check ent is a directory.
585 struct stat statbuf;
586 std::string dir_path = full_path + "/" + ent->d_name;
587 if (stat(dir_path.c_str(), &statbuf) == 0) {
588 if (S_ISDIR(statbuf.st_mode)) {
589 names.insert(ent->d_name);
590 }
591 }
592 }
593 return names;
594 }
595
ReadEventId(const std::string & group,const std::string & name) const596 uint32_t FtraceProcfs::ReadEventId(const std::string& group,
597 const std::string& name) const {
598 std::string path = root_ + "events/" + group + "/" + name + "/id";
599
600 std::string str;
601 if (!base::ReadFile(path, &str))
602 return 0;
603
604 if (str.size() && str[str.size() - 1] == '\n')
605 str.resize(str.size() - 1);
606
607 std::optional<uint32_t> id = base::StringToUInt32(str);
608 if (!id)
609 return 0;
610 return *id;
611 }
612
613 // static
CheckRootPath(const std::string & root)614 bool FtraceProcfs::CheckRootPath(const std::string& root) {
615 base::ScopedFile fd = base::OpenFile(root + "trace", O_RDONLY);
616 return static_cast<bool>(fd);
617 }
618
619 } // namespace perfetto
620