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