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
33const char TRACING_INFO_MAGIC[10] = {23,  8,   68,  't', 'r',
34                                     'a', 'c', 'i', 'n', 'g'};
35
36template <class T>
37void 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
42static void AppendData(std::vector<char>& data, const char* s) {
43  data.insert(data.end(), s, s + strlen(s) + 1);
44}
45
46template <>
47void 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
51template <>
52void MoveFromBinaryFormat(std::string& data, const char*& p) {
53  data.clear();
54  while (*p != '\0') {
55    data.push_back(*p++);
56  }
57  p++;
58}
59
60static 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
72static 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
81struct TraceType {
82  std::string system;
83  std::string name;
84};
85
86class 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;
98  const std::string& GetKallsymsFile() const { return kallsyms_file; }
99  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
118TracingFile::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
126bool 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
143void 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
154bool 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
170bool 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
179std::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
207void 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
247void 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
276enum 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;
284static 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
323std::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
358Tracing::Tracing(const std::vector<char>& data) {
359  tracing_file_ = new TracingFile;
360  tracing_file_->LoadFromBinary(data);
361}
362
363Tracing::~Tracing() { delete tracing_file_; }
364
365void Tracing::Dump(size_t indent) { tracing_file_->Dump(indent); }
366
367TracingFormat 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
380std::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
393const std::string& Tracing::GetKallsyms() const {
394  return tracing_file_->GetKallsymsFile();
395}
396
397uint32_t Tracing::GetPageSize() const { return tracing_file_->GetPageSize(); }
398
399bool 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