• 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/file_utils.h"
29 #include "perfetto/base/logging.h"
30 #include "perfetto/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 
KernelLogWrite(const char * s)46 void KernelLogWrite(const char* s) {
47   PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
48   if (FtraceProcfs::g_kmesg_fd != -1)
49     base::ignore_result(base::WriteAll(FtraceProcfs::g_kmesg_fd, s, strlen(s)));
50 }
51 
WriteFileInternal(const std::string & path,const std::string & str,int flags)52 bool WriteFileInternal(const std::string& path,
53                        const std::string& str,
54                        int flags) {
55   base::ScopedFile fd = base::OpenFile(path, flags);
56   if (!fd)
57     return false;
58   ssize_t written = base::WriteAll(fd.get(), str.c_str(), str.length());
59   ssize_t length = static_cast<ssize_t>(str.length());
60   // This should either fail or write fully.
61   PERFETTO_CHECK(written == length || written == -1);
62   return written == length;
63 }
64 
65 }  // namespace
66 
67 // static
68 int FtraceProcfs::g_kmesg_fd = -1;  // Set by ProbesMain() in probes.cc .
69 
70 // static
Create(const std::string & root)71 std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) {
72   if (!CheckRootPath(root)) {
73     return nullptr;
74   }
75   return std::unique_ptr<FtraceProcfs>(new FtraceProcfs(root));
76 }
77 
FtraceProcfs(const std::string & root)78 FtraceProcfs::FtraceProcfs(const std::string& root) : root_(root) {}
79 FtraceProcfs::~FtraceProcfs() = default;
80 
EnableEvent(const std::string & group,const std::string & name)81 bool FtraceProcfs::EnableEvent(const std::string& group,
82                                const std::string& name) {
83   std::string path = root_ + "events/" + group + "/" + name + "/enable";
84   if (WriteToFile(path, "1"))
85     return true;
86   path = root_ + "set_event";
87   return AppendToFile(path, group + ":" + name);
88 }
89 
DisableEvent(const std::string & group,const std::string & name)90 bool FtraceProcfs::DisableEvent(const std::string& group,
91                                 const std::string& name) {
92   std::string path = root_ + "events/" + group + "/" + name + "/enable";
93   if (WriteToFile(path, "0"))
94     return true;
95   path = root_ + "set_event";
96   return AppendToFile(path, "!" + group + ":" + name);
97 }
98 
DisableAllEvents()99 bool FtraceProcfs::DisableAllEvents() {
100   std::string path = root_ + "events/enable";
101   return WriteToFile(path, "0");
102 }
103 
ReadEventFormat(const std::string & group,const std::string & name) const104 std::string FtraceProcfs::ReadEventFormat(const std::string& group,
105                                           const std::string& name) const {
106   std::string path = root_ + "events/" + group + "/" + name + "/format";
107   return ReadFileIntoString(path);
108 }
109 
ReadPageHeaderFormat() const110 std::string FtraceProcfs::ReadPageHeaderFormat() const {
111   std::string path = root_ + "events/header_page";
112   return ReadFileIntoString(path);
113 }
114 
ReadCpuStats(size_t cpu) const115 std::string FtraceProcfs::ReadCpuStats(size_t cpu) const {
116   std::string path = root_ + "per_cpu/cpu" + std::to_string(cpu) + "/stats";
117   return ReadFileIntoString(path);
118 }
119 
NumberOfCpus() const120 size_t FtraceProcfs::NumberOfCpus() const {
121   static size_t num_cpus = static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF));
122   return num_cpus;
123 }
124 
ClearTrace()125 void FtraceProcfs::ClearTrace() {
126   std::string path = root_ + "trace";
127   PERFETTO_CHECK(ClearFile(path));  // Could not clear.
128 
129   // Truncating the trace file leads to tracing_reset_online_cpus being called
130   // in the kernel.
131   //
132   // In case some of the CPUs were not online, their buffer needs to be
133   // cleared manually.
134   //
135   // We cannot use PERFETTO_CHECK as we might get a permission denied error
136   // on Android. The permissions to these files are configured in
137   // platform/framework/native/cmds/atrace/atrace.rc.
138   for (size_t cpu = 0; cpu < NumberOfCpus(); cpu++) {
139     if (!ClearFile(root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace"))
140       PERFETTO_ELOG("Failed to clear buffer for CPU %zd", cpu);
141   }
142 }
143 
WriteTraceMarker(const std::string & str)144 bool FtraceProcfs::WriteTraceMarker(const std::string& str) {
145   std::string path = root_ + "trace_marker";
146   return WriteToFile(path, str);
147 }
148 
SetCpuBufferSizeInPages(size_t pages)149 bool FtraceProcfs::SetCpuBufferSizeInPages(size_t pages) {
150   if (pages * base::kPageSize > 1 * 1024 * 1024 * 1024) {
151     PERFETTO_ELOG("Tried to set the per CPU buffer size to more than 1gb.");
152     return false;
153   }
154   std::string path = root_ + "buffer_size_kb";
155   return WriteNumberToFile(path, pages * (base::kPageSize / 1024ul));
156 }
157 
EnableTracing()158 bool FtraceProcfs::EnableTracing() {
159   KernelLogWrite("perfetto: enabled ftrace\n");
160   std::string path = root_ + "tracing_on";
161   return WriteToFile(path, "1");
162 }
163 
DisableTracing()164 bool FtraceProcfs::DisableTracing() {
165   KernelLogWrite("perfetto: disabled ftrace\n");
166   std::string path = root_ + "tracing_on";
167   return WriteToFile(path, "0");
168 }
169 
SetTracingOn(bool enable)170 bool FtraceProcfs::SetTracingOn(bool enable) {
171   return enable ? EnableTracing() : DisableTracing();
172 }
173 
IsTracingEnabled()174 bool FtraceProcfs::IsTracingEnabled() {
175   std::string path = root_ + "tracing_on";
176   return ReadOneCharFromFile(path) == '1';
177 }
178 
SetClock(const std::string & clock_name)179 bool FtraceProcfs::SetClock(const std::string& clock_name) {
180   std::string path = root_ + "trace_clock";
181   return WriteToFile(path, clock_name);
182 }
183 
GetClock()184 std::string FtraceProcfs::GetClock() {
185   std::string path = root_ + "trace_clock";
186   std::string s = ReadFileIntoString(path);
187 
188   size_t start = s.find('[');
189   if (start == std::string::npos)
190     return "";
191 
192   size_t end = s.find(']', start);
193   if (end == std::string::npos)
194     return "";
195 
196   return s.substr(start + 1, end - start - 1);
197 }
198 
AvailableClocks()199 std::set<std::string> FtraceProcfs::AvailableClocks() {
200   std::string path = root_ + "trace_clock";
201   std::string s = ReadFileIntoString(path);
202   std::set<std::string> names;
203 
204   size_t start = 0;
205   size_t end = 0;
206 
207   for (;;) {
208     end = s.find(' ', start);
209     if (end == std::string::npos)
210       end = s.size();
211     while (end > start && s[end - 1] == '\n')
212       end--;
213     if (start == end)
214       break;
215 
216     std::string name = s.substr(start, end - start);
217 
218     if (name[0] == '[')
219       name = name.substr(1, name.size() - 2);
220 
221     names.insert(name);
222 
223     if (end == s.size())
224       break;
225 
226     start = end + 1;
227   }
228 
229   return names;
230 }
231 
WriteNumberToFile(const std::string & path,size_t value)232 bool FtraceProcfs::WriteNumberToFile(const std::string& path, size_t value) {
233   // 2^65 requires 20 digits to write.
234   char buf[21];
235   int res = snprintf(buf, 21, "%zu", value);
236   if (res < 0 || res >= 21)
237     return false;
238   return WriteToFile(path, std::string(buf));
239 }
240 
WriteToFile(const std::string & path,const std::string & str)241 bool FtraceProcfs::WriteToFile(const std::string& path,
242                                const std::string& str) {
243   return WriteFileInternal(path, str, O_WRONLY);
244 }
245 
AppendToFile(const std::string & path,const std::string & str)246 bool FtraceProcfs::AppendToFile(const std::string& path,
247                                 const std::string& str) {
248   return WriteFileInternal(path, str, O_WRONLY | O_APPEND);
249 }
250 
OpenPipeForCpu(size_t cpu)251 base::ScopedFile FtraceProcfs::OpenPipeForCpu(size_t cpu) {
252   std::string path =
253       root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace_pipe_raw";
254   return base::OpenFile(path, O_RDONLY | O_NONBLOCK);
255 }
256 
ReadOneCharFromFile(const std::string & path)257 char FtraceProcfs::ReadOneCharFromFile(const std::string& path) {
258   base::ScopedFile fd = base::OpenFile(path, O_RDONLY);
259   PERFETTO_CHECK(fd);
260   char result = '\0';
261   ssize_t bytes = PERFETTO_EINTR(read(fd.get(), &result, 1));
262   PERFETTO_CHECK(bytes == 1 || bytes == -1);
263   return result;
264 }
265 
ClearFile(const std::string & path)266 bool FtraceProcfs::ClearFile(const std::string& path) {
267   base::ScopedFile fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
268   return !!fd;
269 }
270 
ReadFileIntoString(const std::string & path) const271 std::string FtraceProcfs::ReadFileIntoString(const std::string& path) const {
272   // You can't seek or stat the procfs files on Android.
273   // The vast majority (884/886) of format files are under 4k.
274   std::string str;
275   str.reserve(4096);
276   if (!base::ReadFile(path, &str))
277     return "";
278   return str;
279 }
280 
GetEventNamesForGroup(const std::string & path) const281 const std::set<std::string> FtraceProcfs::GetEventNamesForGroup(
282     const std::string& path) const {
283   std::set<std::string> names;
284   std::string full_path = root_ + path;
285   base::ScopedDir dir(opendir(full_path.c_str()));
286   if (!dir) {
287     PERFETTO_DLOG("Unable to read events from %s", full_path.c_str());
288     return names;
289   }
290   struct dirent* ent;
291   while ((ent = readdir(*dir)) != nullptr) {
292     if (strncmp(ent->d_name, ".", 1) == 0 ||
293         strncmp(ent->d_name, "..", 2) == 0) {
294       continue;
295     }
296     // Check ent is a directory.
297     struct stat statbuf;
298     std::string dir_path = full_path + "/" + ent->d_name;
299     if (stat(dir_path.c_str(), &statbuf) == 0) {
300       if (S_ISDIR(statbuf.st_mode)) {
301         names.insert(ent->d_name);
302       }
303     }
304   }
305   return names;
306 }
307 
308 // static
CheckRootPath(const std::string & root)309 bool FtraceProcfs::CheckRootPath(const std::string& root) {
310   base::ScopedFile fd = base::OpenFile(root + "trace", O_RDONLY);
311   return static_cast<bool>(fd);
312 }
313 
314 }  // namespace perfetto
315