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