Home | History | Annotate | Download | only in simpleperf
      1 /*
      2  * Copyright (C) 2015 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 <inttypes.h>
     18 
     19 #include <map>
     20 #include <string>
     21 #include <vector>
     22 
     23 #include <android-base/logging.h>
     24 #include <android-base/stringprintf.h>
     25 #include <android-base/strings.h>
     26 
     27 #include "command.h"
     28 #include "event_attr.h"
     29 #include "event_type.h"
     30 #include "perf_regs.h"
     31 #include "record.h"
     32 #include "record_file.h"
     33 #include "utils.h"
     34 
     35 using namespace PerfFileFormat;
     36 
     37 class DumpRecordCommand : public Command {
     38  public:
     39   DumpRecordCommand()
     40       : Command("dump", "dump perf record file",
     41                 "Usage: simpleperf dumprecord [options] [perf_record_file]\n"
     42                 "    Dump different parts of a perf record file. Default file is perf.data.\n"),
     43         record_filename_("perf.data"), record_file_arch_(GetBuildArch()) {
     44   }
     45 
     46   bool Run(const std::vector<std::string>& args);
     47 
     48  private:
     49   bool ParseOptions(const std::vector<std::string>& args);
     50   void DumpFileHeader();
     51   void DumpAttrSection();
     52   bool DumpDataSection();
     53   bool DumpFeatureSection();
     54 
     55   std::string record_filename_;
     56   std::unique_ptr<RecordFileReader> record_file_reader_;
     57   ArchType record_file_arch_;
     58 };
     59 
     60 bool DumpRecordCommand::Run(const std::vector<std::string>& args) {
     61   if (!ParseOptions(args)) {
     62     return false;
     63   }
     64   record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
     65   if (record_file_reader_ == nullptr) {
     66     return false;
     67   }
     68   std::string arch = record_file_reader_->ReadFeatureString(FEAT_ARCH);
     69   if (!arch.empty()) {
     70     record_file_arch_ = GetArchType(arch);
     71     if (record_file_arch_ == ARCH_UNSUPPORTED) {
     72       return false;
     73     }
     74   }
     75   ScopedCurrentArch scoped_arch(record_file_arch_);
     76   std::unique_ptr<ScopedEventTypes> scoped_event_types;
     77   if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO)) {
     78     std::unordered_map<std::string, std::string> meta_info;
     79     if (!record_file_reader_->ReadMetaInfoFeature(&meta_info)) {
     80       return false;
     81     }
     82     auto it = meta_info.find("event_type_info");
     83     if (it != meta_info.end()) {
     84       scoped_event_types.reset(new ScopedEventTypes(it->second));
     85     }
     86   }
     87   DumpFileHeader();
     88   DumpAttrSection();
     89   if (!DumpDataSection()) {
     90     return false;
     91   }
     92   return DumpFeatureSection();
     93 }
     94 
     95 bool DumpRecordCommand::ParseOptions(const std::vector<std::string>& args) {
     96   if (args.size() == 1) {
     97     record_filename_ = args[0];
     98   } else if (args.size() > 1) {
     99     ReportUnknownOption(args, 1);
    100     return false;
    101   }
    102   return true;
    103 }
    104 
    105 static const std::string GetFeatureNameOrUnknown(int feature) {
    106   std::string name = GetFeatureName(feature);
    107   return name.empty() ? android::base::StringPrintf("unknown_feature(%d)", feature) : name;
    108 }
    109 
    110 void DumpRecordCommand::DumpFileHeader() {
    111   const FileHeader& header = record_file_reader_->FileHeader();
    112   printf("magic: ");
    113   for (size_t i = 0; i < 8; ++i) {
    114     printf("%c", header.magic[i]);
    115   }
    116   printf("\n");
    117   printf("header_size: %" PRId64 "\n", header.header_size);
    118   if (header.header_size != sizeof(header)) {
    119     PLOG(WARNING) << "record file header size " << header.header_size
    120                   << "doesn't match expected header size " << sizeof(header);
    121   }
    122   printf("attr_size: %" PRId64 "\n", header.attr_size);
    123   if (header.attr_size != sizeof(FileAttr)) {
    124     PLOG(WARNING) << "record file attr size " << header.attr_size
    125                   << " doesn't match expected attr size " << sizeof(FileAttr);
    126   }
    127   printf("attrs[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.attrs.offset,
    128          header.attrs.size);
    129   printf("data[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.data.offset,
    130          header.data.size);
    131   printf("event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n",
    132          header.event_types.offset, header.event_types.size);
    133 
    134   std::vector<int> features;
    135   for (size_t i = 0; i < FEAT_MAX_NUM; ++i) {
    136     size_t j = i / 8;
    137     size_t k = i % 8;
    138     if ((header.features[j] & (1 << k)) != 0) {
    139       features.push_back(i);
    140     }
    141   }
    142   for (auto& feature : features) {
    143     printf("feature: %s\n", GetFeatureNameOrUnknown(feature).c_str());
    144   }
    145 }
    146 
    147 void DumpRecordCommand::DumpAttrSection() {
    148   std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
    149   for (size_t i = 0; i < attrs.size(); ++i) {
    150     const auto& attr = attrs[i];
    151     printf("attr %zu:\n", i + 1);
    152     DumpPerfEventAttr(*attr.attr, 1);
    153     if (!attr.ids.empty()) {
    154       printf("  ids:");
    155       for (const auto& id : attr.ids) {
    156         printf(" %" PRId64, id);
    157       }
    158       printf("\n");
    159     }
    160   }
    161 }
    162 
    163 bool DumpRecordCommand::DumpDataSection() {
    164   ThreadTree thread_tree;
    165   thread_tree.ShowIpForUnknownSymbol();
    166   record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree);
    167 
    168   auto get_symbol_function = [&](uint32_t pid, uint32_t tid, uint64_t ip, std::string& dso_name,
    169                                  std::string& symbol_name, uint64_t& vaddr_in_file) {
    170     ThreadEntry* thread = thread_tree.FindThreadOrNew(pid, tid);
    171     const MapEntry* map = thread_tree.FindMap(thread, ip);
    172     Dso* dso;
    173     const Symbol* symbol = thread_tree.FindSymbol(map, ip, &vaddr_in_file, &dso);
    174     dso_name = dso->Path();
    175     symbol_name = symbol->DemangledName();
    176   };
    177 
    178   auto record_callback = [&](std::unique_ptr<Record> r) {
    179     r->Dump();
    180     thread_tree.Update(*r);
    181     if (r->type() == PERF_RECORD_SAMPLE) {
    182       SampleRecord& sr = *static_cast<SampleRecord*>(r.get());
    183       if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) {
    184         PrintIndented(1, "callchain:\n");
    185         for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) {
    186           std::string dso_name;
    187           std::string symbol_name;
    188           uint64_t vaddr_in_file;
    189           get_symbol_function(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i],
    190                               dso_name, symbol_name, vaddr_in_file);
    191           PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", symbol_name.c_str(), dso_name.c_str(),
    192                         vaddr_in_file);
    193         }
    194       }
    195     } else if (r->type() == SIMPLE_PERF_RECORD_CALLCHAIN) {
    196       CallChainRecord& cr = *static_cast<CallChainRecord*>(r.get());
    197       PrintIndented(1, "callchain:\n");
    198       for (size_t i = 0; i < cr.ip_nr; ++i) {
    199         std::string dso_name;
    200         std::string symbol_name;
    201         uint64_t vaddr_in_file;
    202         get_symbol_function(cr.pid, cr.tid, cr.ips[i], dso_name, symbol_name, vaddr_in_file);
    203         PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", symbol_name.c_str(), dso_name.c_str(),
    204                       vaddr_in_file);
    205       }
    206     }
    207     return true;
    208   };
    209   return record_file_reader_->ReadDataSection(record_callback, false);
    210 }
    211 
    212 bool DumpRecordCommand::DumpFeatureSection() {
    213   std::map<int, SectionDesc> section_map = record_file_reader_->FeatureSectionDescriptors();
    214   for (const auto& pair : section_map) {
    215     int feature = pair.first;
    216     const auto& section = pair.second;
    217     printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n",
    218            GetFeatureNameOrUnknown(feature).c_str(), section.offset, section.size);
    219     if (feature == FEAT_BUILD_ID) {
    220       std::vector<BuildIdRecord> records = record_file_reader_->ReadBuildIdFeature();
    221       for (auto& r : records) {
    222         r.Dump(1);
    223       }
    224     } else if (feature == FEAT_OSRELEASE) {
    225       std::string s = record_file_reader_->ReadFeatureString(feature);
    226       PrintIndented(1, "osrelease: %s\n", s.c_str());
    227     } else if (feature == FEAT_ARCH) {
    228       std::string s = record_file_reader_->ReadFeatureString(feature);
    229       PrintIndented(1, "arch: %s\n", s.c_str());
    230     } else if (feature == FEAT_CMDLINE) {
    231       std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
    232       PrintIndented(1, "cmdline: %s\n", android::base::Join(cmdline, ' ').c_str());
    233     } else if (feature == FEAT_FILE) {
    234       std::string file_path;
    235       uint32_t file_type;
    236       uint64_t min_vaddr;
    237       std::vector<Symbol> symbols;
    238       size_t read_pos = 0;
    239       PrintIndented(1, "file:\n");
    240       while (record_file_reader_->ReadFileFeature(read_pos, &file_path,
    241                                                   &file_type, &min_vaddr,
    242                                                   &symbols)) {
    243         PrintIndented(2, "file_path %s\n", file_path.c_str());
    244         PrintIndented(2, "file_type %s\n", DsoTypeToString(static_cast<DsoType>(file_type)));
    245         PrintIndented(2, "min_vaddr 0x%" PRIx64 "\n", min_vaddr);
    246         PrintIndented(2, "symbols:\n");
    247         for (const auto& symbol : symbols) {
    248           PrintIndented(3, "%s [0x%" PRIx64 "-0x%" PRIx64 "]\n", symbol.DemangledName(),
    249                         symbol.addr, symbol.addr + symbol.len);
    250         }
    251       }
    252     } else if (feature == FEAT_META_INFO) {
    253       std::unordered_map<std::string, std::string> info_map;
    254       if (!record_file_reader_->ReadMetaInfoFeature(&info_map)) {
    255         return false;
    256       }
    257       PrintIndented(1, "meta_info:\n");
    258       for (auto& pair : info_map) {
    259         PrintIndented(2, "%s = %s\n", pair.first.c_str(), pair.second.c_str());
    260       }
    261     }
    262   }
    263   return true;
    264 }
    265 
    266 void RegisterDumpRecordCommand() {
    267   RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); });
    268 }
    269