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