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