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