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