Home | History | Annotate | Download | only in simpleperf
      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>
     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 
     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 <>
     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 <>
     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 
     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 
     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;
     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 
    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 
    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 
    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 
    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 
    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 
    179 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 
    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 
    247 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;
    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 
    323 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 
    358 Tracing::Tracing(const std::vector<char>& data) {
    359   tracing_file_ = new TracingFile;
    360   tracing_file_->LoadFromBinary(data);
    361 }
    362 
    363 Tracing::~Tracing() { delete tracing_file_; }
    364 
    365 void Tracing::Dump(size_t indent) { tracing_file_->Dump(indent); }
    366 
    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 
    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 
    393 const std::string& Tracing::GetKallsyms() const {
    394   return tracing_file_->GetKallsymsFile();
    395 }
    396 
    397 uint32_t Tracing::GetPageSize() const { return tracing_file_->GetPageSize(); }
    398 
    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