1 /*
2 * Copyright (C) 2016 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 "tracing.h"
18
19 #include <string.h>
20
21 #include <map>
22 #include <string>
23 #include <vector>
24
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27 #include <android-base/parseint.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30
31 #include "perf_event.h"
32 #include "utils.h"
33
34 const char TRACING_INFO_MAGIC[10] = {23, 8, 68, 't', 'r',
35 'a', 'c', 'i', 'n', 'g'};
36
37 template <class T>
AppendData(std::vector<char> & data,const T & s)38 void AppendData(std::vector<char>& data, const T& s) {
39 const char* p = reinterpret_cast<const char*>(&s);
40 data.insert(data.end(), p, p + sizeof(T));
41 }
42
AppendData(std::vector<char> & data,const char * s)43 static void AppendData(std::vector<char>& data, const char* s) {
44 data.insert(data.end(), s, s + strlen(s) + 1);
45 }
46
47 template <>
AppendData(std::vector<char> & data,const std::string & s)48 void AppendData(std::vector<char>& data, const std::string& s) {
49 data.insert(data.end(), s.c_str(), s.c_str() + s.size() + 1);
50 }
51
52 template <>
MoveFromBinaryFormat(std::string & data,const char * & p)53 void MoveFromBinaryFormat(std::string& data, const char*& p) {
54 data.clear();
55 while (*p != '\0') {
56 data.push_back(*p++);
57 }
58 p++;
59 }
60
AppendFile(std::vector<char> & data,const std::string & file,uint32_t file_size_bytes=8)61 static void AppendFile(std::vector<char>& data, const std::string& file,
62 uint32_t file_size_bytes = 8) {
63 if (file_size_bytes == 8) {
64 uint64_t file_size = file.size();
65 AppendData(data, file_size);
66 } else if (file_size_bytes == 4) {
67 uint32_t file_size = file.size();
68 AppendData(data, file_size);
69 }
70 data.insert(data.end(), file.begin(), file.end());
71 }
72
DetachFile(const char * & p,std::string & file,uint32_t file_size_bytes=8)73 static void DetachFile(const char*& p, std::string& file,
74 uint32_t file_size_bytes = 8) {
75 uint64_t file_size = ConvertBytesToValue(p, file_size_bytes);
76 p += file_size_bytes;
77 file.clear();
78 file.insert(file.end(), p, p + file_size);
79 p += file_size;
80 }
81
82 struct TraceType {
83 std::string system;
84 std::string name;
85 };
86
87 class TracingFile {
88 public:
89 TracingFile();
90 bool RecordHeaderFiles();
91 void RecordFtraceFiles(const std::vector<TraceType>& trace_types);
92 bool RecordEventFiles(const std::vector<TraceType>& trace_types);
93 bool RecordKallsymsFile();
94 bool RecordPrintkFormatsFile();
95 std::vector<char> BinaryFormat() const;
96 void LoadFromBinary(const std::vector<char>& data);
97 void Dump(size_t indent) const;
98 std::vector<TracingFormat> LoadTracingFormatsFromEventFiles() const;
GetKallsymsFile() const99 const std::string& GetKallsymsFile() const { return kallsyms_file; }
GetPageSize() const100 uint32_t GetPageSize() const { return page_size; }
101
102 private:
103 char magic[10];
104 std::string version;
105 char endian;
106 uint8_t size_of_long;
107 uint32_t page_size;
108 std::string header_page_file;
109 std::string header_event_file;
110
111 std::vector<std::string> ftrace_format_files;
112 // pair of system, format_file_data.
113 std::vector<std::pair<std::string, std::string>> event_format_files;
114
115 std::string kallsyms_file;
116 std::string printk_formats_file;
117 };
118
TracingFile()119 TracingFile::TracingFile() {
120 memcpy(magic, TRACING_INFO_MAGIC, sizeof(TRACING_INFO_MAGIC));
121 version = "0.5";
122 endian = 0;
123 size_of_long = static_cast<int>(sizeof(long)); // NOLINT(google-runtime-int)
124 page_size = static_cast<uint32_t>(::GetPageSize());
125 }
126
RecordHeaderFiles()127 bool TracingFile::RecordHeaderFiles() {
128 if (!android::base::ReadFileToString(
129 "/sys/kernel/debug/tracing/events/header_page", &header_page_file)) {
130 PLOG(ERROR)
131 << "failed to read /sys/kernel/debug/tracing/events/header_page";
132 return false;
133 }
134 if (!android::base::ReadFileToString(
135 "/sys/kernel/debug/tracing/events/header_event",
136 &header_event_file)) {
137 PLOG(ERROR)
138 << "failed to read /sys/kernel/debug/tracing/events/header_event";
139 return false;
140 }
141 return true;
142 }
143
RecordFtraceFiles(const std::vector<TraceType> & trace_types)144 void TracingFile::RecordFtraceFiles(const std::vector<TraceType>& trace_types) {
145 for (const auto& type : trace_types) {
146 std::string format_path = android::base::StringPrintf(
147 "/sys/kernel/debug/tracing/events/ftrace/%s/format", type.name.c_str());
148 std::string format_data;
149 if (android::base::ReadFileToString(format_path, &format_data)) {
150 ftrace_format_files.push_back(std::move(format_data));
151 }
152 }
153 }
154
RecordEventFiles(const std::vector<TraceType> & trace_types)155 bool TracingFile::RecordEventFiles(const std::vector<TraceType>& trace_types) {
156 for (const auto& type : trace_types) {
157 std::string format_path = android::base::StringPrintf(
158 "/sys/kernel/debug/tracing/events/%s/%s/format", type.system.c_str(),
159 type.name.c_str());
160 std::string format_data;
161 if (!android::base::ReadFileToString(format_path, &format_data)) {
162 PLOG(ERROR) << "failed to read " << format_path;
163 return false;
164 }
165 event_format_files.push_back(
166 std::make_pair(type.system, std::move(format_data)));
167 }
168 return true;
169 }
170
RecordPrintkFormatsFile()171 bool TracingFile::RecordPrintkFormatsFile() {
172 if (!android::base::ReadFileToString(
173 "/sys/kernel/debug/tracing/printk_formats", &printk_formats_file)) {
174 PLOG(ERROR) << "failed to read /sys/kernel/debug/tracing/printk_formats";
175 return false;
176 }
177 return true;
178 }
179
BinaryFormat() const180 std::vector<char> TracingFile::BinaryFormat() const {
181 std::vector<char> ret;
182 ret.insert(ret.end(), magic, magic + sizeof(magic));
183 AppendData(ret, version);
184 ret.push_back(endian);
185 AppendData(ret, size_of_long);
186 AppendData(ret, page_size);
187 AppendData(ret, "header_page");
188 AppendFile(ret, header_page_file);
189 AppendData(ret, "header_event");
190 AppendFile(ret, header_event_file);
191 int count = static_cast<int>(ftrace_format_files.size());
192 AppendData(ret, count);
193 for (const auto& format : ftrace_format_files) {
194 AppendFile(ret, format);
195 }
196 count = static_cast<int>(event_format_files.size());
197 AppendData(ret, count);
198 for (const auto& pair : event_format_files) {
199 AppendData(ret, pair.first);
200 AppendData(ret, 1);
201 AppendFile(ret, pair.second);
202 }
203 AppendFile(ret, kallsyms_file, 4);
204 AppendFile(ret, printk_formats_file, 4);
205 return ret;
206 }
207
LoadFromBinary(const std::vector<char> & data)208 void TracingFile::LoadFromBinary(const std::vector<char>& data) {
209 const char* p = data.data();
210 const char* end = data.data() + data.size();
211 CHECK(memcmp(p, magic, sizeof(magic)) == 0);
212 p += sizeof(magic);
213 MoveFromBinaryFormat(version, p);
214 MoveFromBinaryFormat(endian, p);
215 MoveFromBinaryFormat(size_of_long, p);
216 MoveFromBinaryFormat(page_size, p);
217 std::string filename;
218 MoveFromBinaryFormat(filename, p);
219 CHECK_EQ(filename, "header_page");
220 DetachFile(p, header_page_file);
221 MoveFromBinaryFormat(filename, p);
222 CHECK_EQ(filename, "header_event");
223 DetachFile(p, header_event_file);
224 uint32_t count;
225 MoveFromBinaryFormat(count, p);
226 ftrace_format_files.resize(count);
227 for (uint32_t i = 0; i < count; ++i) {
228 DetachFile(p, ftrace_format_files[i]);
229 }
230 MoveFromBinaryFormat(count, p);
231 event_format_files.clear();
232 for (uint32_t i = 0; i < count; ++i) {
233 std::string system;
234 MoveFromBinaryFormat(system, p);
235 uint32_t count_in_system;
236 MoveFromBinaryFormat(count_in_system, p);
237 for (uint32_t i = 0; i < count_in_system; ++i) {
238 std::string format;
239 DetachFile(p, format);
240 event_format_files.push_back(std::make_pair(system, std::move(format)));
241 }
242 }
243 DetachFile(p, kallsyms_file, 4);
244 DetachFile(p, printk_formats_file, 4);
245 CHECK_EQ(p, end);
246 }
247
Dump(size_t indent) const248 void TracingFile::Dump(size_t indent) const {
249 PrintIndented(indent, "tracing data:\n");
250 PrintIndented(indent + 1, "magic: ");
251 for (size_t i = 0; i < 3u; ++i) {
252 printf("0x%x ", magic[i]);
253 }
254 for (size_t i = 3; i < sizeof(magic); ++i) {
255 printf("%c", magic[i]);
256 }
257 printf("\n");
258 PrintIndented(indent + 1, "version: %s\n", version.c_str());
259 PrintIndented(indent + 1, "endian: %d\n", endian);
260 PrintIndented(indent + 1, "header_page:\n%s\n\n", header_page_file.c_str());
261 PrintIndented(indent + 1, "header_event:\n%s\n\n", header_event_file.c_str());
262 for (size_t i = 0; i < ftrace_format_files.size(); ++i) {
263 PrintIndented(indent + 1, "ftrace format file %zu/%zu:\n%s\n\n", i + 1,
264 ftrace_format_files.size(), ftrace_format_files[i].c_str());
265 }
266 for (size_t i = 0; i < event_format_files.size(); ++i) {
267 PrintIndented(indent + 1, "event format file %zu/%zu %s:\n%s\n\n", i + 1,
268 event_format_files.size(),
269 event_format_files[i].first.c_str(),
270 event_format_files[i].second.c_str());
271 }
272 PrintIndented(indent + 1, "kallsyms:\n%s\n\n", kallsyms_file.c_str());
273 PrintIndented(indent + 1, "printk_formats:\n%s\n\n",
274 printk_formats_file.c_str());
275 }
276
277 enum class FormatParsingState {
278 READ_NAME,
279 READ_ID,
280 READ_FIELDS,
281 READ_PRINTFMT,
282 };
283
284 // Parse lines like: field:char comm[16]; offset:8; size:16; signed:1;
ParseTracingField(const std::string & s)285 static TracingField ParseTracingField(const std::string& s) {
286 TracingField field;
287 size_t start = 0;
288 std::string name;
289 std::string value;
290 for (size_t i = 0; i < s.size(); ++i) {
291 if (!isspace(s[i]) && (i == 0 || isspace(s[i - 1]))) {
292 start = i;
293 } else if (s[i] == ':') {
294 name = s.substr(start, i - start);
295 start = i + 1;
296 } else if (s[i] == ';') {
297 value = s.substr(start, i - start);
298 if (name == "field") {
299 // Parse value with brackets like "comm[16]", or just a field name.
300 size_t left_bracket_pos = value.find('[');
301 if (left_bracket_pos == std::string::npos) {
302 field.name = value;
303 field.elem_count = 1;
304 } else {
305 field.name = value.substr(0, left_bracket_pos);
306 field.elem_count = 1;
307 size_t right_bracket_pos = value.find(']', left_bracket_pos);
308 if (right_bracket_pos != std::string::npos) {
309 size_t len = right_bracket_pos - left_bracket_pos - 1;
310 size_t elem_count;
311 // Array size may not be a number, like field:u32 rates[IEEE80211_NUM_BANDS].
312 if (android::base::ParseUint(value.substr(left_bracket_pos + 1, len), &elem_count)) {
313 field.elem_count = elem_count;
314 }
315 }
316 }
317 } else if (name == "offset") {
318 field.offset =
319 static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
320 } else if (name == "size") {
321 size_t size = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
322 CHECK_EQ(size % field.elem_count, 0u);
323 field.elem_size = size / field.elem_count;
324 } else if (name == "signed") {
325 int is_signed = static_cast<int>(strtoull(value.c_str(), nullptr, 10));
326 field.is_signed = (is_signed == 1);
327 }
328 }
329 }
330 return field;
331 }
332
LoadTracingFormatsFromEventFiles() const333 std::vector<TracingFormat> TracingFile::LoadTracingFormatsFromEventFiles()
334 const {
335 std::vector<TracingFormat> formats;
336 for (const auto& pair : event_format_files) {
337 TracingFormat format;
338 format.system_name = pair.first;
339 std::vector<std::string> strs = android::base::Split(pair.second, "\n");
340 FormatParsingState state = FormatParsingState::READ_NAME;
341 for (const auto& s : strs) {
342 if (state == FormatParsingState::READ_NAME) {
343 size_t pos = s.find("name:");
344 if (pos != std::string::npos) {
345 format.name = android::base::Trim(s.substr(pos + strlen("name:")));
346 state = FormatParsingState::READ_ID;
347 }
348 } else if (state == FormatParsingState::READ_ID) {
349 size_t pos = s.find("ID:");
350 if (pos != std::string::npos) {
351 format.id =
352 strtoull(s.substr(pos + strlen("ID:")).c_str(), nullptr, 10);
353 state = FormatParsingState::READ_FIELDS;
354 }
355 } else if (state == FormatParsingState::READ_FIELDS) {
356 size_t pos = s.find("field:");
357 if (pos != std::string::npos) {
358 TracingField field = ParseTracingField(s);
359 format.fields.push_back(field);
360 }
361 }
362 }
363 formats.push_back(format);
364 }
365 return formats;
366 }
367
Tracing(const std::vector<char> & data)368 Tracing::Tracing(const std::vector<char>& data) {
369 tracing_file_ = new TracingFile;
370 tracing_file_->LoadFromBinary(data);
371 }
372
~Tracing()373 Tracing::~Tracing() { delete tracing_file_; }
374
Dump(size_t indent)375 void Tracing::Dump(size_t indent) { tracing_file_->Dump(indent); }
376
GetTracingFormatHavingId(uint64_t trace_event_id)377 TracingFormat Tracing::GetTracingFormatHavingId(uint64_t trace_event_id) {
378 if (tracing_formats_.empty()) {
379 tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
380 }
381 for (const auto& format : tracing_formats_) {
382 if (format.id == trace_event_id) {
383 return format;
384 }
385 }
386 LOG(FATAL) << "no tracing format for id " << trace_event_id;
387 return TracingFormat();
388 }
389
GetTracingEventNameHavingId(uint64_t trace_event_id)390 std::string Tracing::GetTracingEventNameHavingId(uint64_t trace_event_id) {
391 if (tracing_formats_.empty()) {
392 tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
393 }
394 for (const auto& format : tracing_formats_) {
395 if (format.id == trace_event_id) {
396 return android::base::StringPrintf("%s:%s", format.system_name.c_str(),
397 format.name.c_str());
398 }
399 }
400 return "";
401 }
402
GetKallsyms() const403 const std::string& Tracing::GetKallsyms() const {
404 return tracing_file_->GetKallsymsFile();
405 }
406
GetPageSize() const407 uint32_t Tracing::GetPageSize() const { return tracing_file_->GetPageSize(); }
408
GetTracingData(const std::vector<const EventType * > & event_types,std::vector<char> * data)409 bool GetTracingData(const std::vector<const EventType*>& event_types,
410 std::vector<char>* data) {
411 data->clear();
412 std::vector<TraceType> trace_types;
413 for (const auto& type : event_types) {
414 CHECK_EQ(static_cast<uint32_t>(PERF_TYPE_TRACEPOINT), type->type);
415 size_t pos = type->name.find(':');
416 TraceType trace_type;
417 trace_type.system = type->name.substr(0, pos);
418 trace_type.name = type->name.substr(pos + 1);
419 trace_types.push_back(trace_type);
420 }
421 TracingFile tracing_file;
422 if (!tracing_file.RecordHeaderFiles()) {
423 return false;
424 }
425 tracing_file.RecordFtraceFiles(trace_types);
426 if (!tracing_file.RecordEventFiles(trace_types)) {
427 return false;
428 }
429 // Don't record /proc/kallsyms here, as it will be contained in
430 // KernelSymbolRecord.
431 if (!tracing_file.RecordPrintkFormatsFile()) {
432 return false;
433 }
434 *data = tracing_file.BinaryFormat();
435 return true;
436 }
437