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