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