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 #include <algorithm>
     19 #include <functional>
     20 #include <map>
     21 #include <set>
     22 #include <string>
     23 #include <unordered_map>
     24 #include <unordered_set>
     25 #include <vector>
     26 
     27 #include <android-base/file.h>
     28 #include <android-base/logging.h>
     29 #include <android-base/parsedouble.h>
     30 #include <android-base/parseint.h>
     31 #include <android-base/stringprintf.h>
     32 #include <android-base/strings.h>
     33 
     34 #include "command.h"
     35 #include "event_attr.h"
     36 #include "event_type.h"
     37 #include "perf_regs.h"
     38 #include "record.h"
     39 #include "record_file.h"
     40 #include "sample_tree.h"
     41 #include "thread_tree.h"
     42 #include "tracing.h"
     43 #include "utils.h"
     44 
     45 namespace {
     46 
     47 static std::set<std::string> branch_sort_keys = {
     48     "dso_from", "dso_to", "symbol_from", "symbol_to",
     49 };
     50 struct BranchFromEntry {
     51   const MapEntry* map;
     52   const Symbol* symbol;
     53   uint64_t vaddr_in_file;
     54   uint64_t flags;
     55 
     56   BranchFromEntry()
     57       : map(nullptr), symbol(nullptr), vaddr_in_file(0), flags(0) {}
     58 };
     59 
     60 struct SampleEntry {
     61   uint64_t time;
     62   uint64_t period;
     63   // accumuated when appearing in other sample's callchain
     64   uint64_t accumulated_period;
     65   uint64_t sample_count;
     66   const ThreadEntry* thread;
     67   const char* thread_comm;
     68   const MapEntry* map;
     69   const Symbol* symbol;
     70   uint64_t vaddr_in_file;
     71   BranchFromEntry branch_from;
     72   // a callchain tree representing all callchains in the sample
     73   CallChainRoot<SampleEntry> callchain;
     74 
     75   SampleEntry(uint64_t time, uint64_t period, uint64_t accumulated_period,
     76               uint64_t sample_count, const ThreadEntry* thread,
     77               const MapEntry* map, const Symbol* symbol, uint64_t vaddr_in_file)
     78       : time(time),
     79         period(period),
     80         accumulated_period(accumulated_period),
     81         sample_count(sample_count),
     82         thread(thread),
     83         thread_comm(thread->comm),
     84         map(map),
     85         symbol(symbol),
     86         vaddr_in_file(vaddr_in_file) {}
     87 
     88   // The data member 'callchain' can only move, not copy.
     89   SampleEntry(SampleEntry&&) = default;
     90   SampleEntry(SampleEntry&) = delete;
     91 
     92   uint64_t GetPeriod() const {
     93     return period;
     94   }
     95 };
     96 
     97 struct SampleTree {
     98   std::vector<SampleEntry*> samples;
     99   uint64_t total_samples;
    100   uint64_t total_period;
    101   uint64_t total_error_callchains;
    102 };
    103 
    104 BUILD_COMPARE_VALUE_FUNCTION(CompareVaddrInFile, vaddr_in_file);
    105 BUILD_DISPLAY_HEX64_FUNCTION(DisplayVaddrInFile, vaddr_in_file);
    106 
    107 class ReportCmdSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, uint64_t> {
    108  public:
    109   ReportCmdSampleTreeBuilder(const SampleComparator<SampleEntry>& sample_comparator,
    110                              ThreadTree* thread_tree)
    111       : SampleTreeBuilder(sample_comparator),
    112         thread_tree_(thread_tree),
    113         total_samples_(0),
    114         total_period_(0),
    115         total_error_callchains_(0) {}
    116 
    117   void SetFilters(const std::unordered_set<int>& pid_filter,
    118                   const std::unordered_set<int>& tid_filter,
    119                   const std::unordered_set<std::string>& comm_filter,
    120                   const std::unordered_set<std::string>& dso_filter,
    121                   const std::unordered_set<std::string>& symbol_filter) {
    122     pid_filter_ = pid_filter;
    123     tid_filter_ = tid_filter;
    124     comm_filter_ = comm_filter;
    125     dso_filter_ = dso_filter;
    126     symbol_filter_ = symbol_filter;
    127   }
    128 
    129   SampleTree GetSampleTree() {
    130     AddCallChainDuplicateInfo();
    131     SampleTree sample_tree;
    132     sample_tree.samples = GetSamples();
    133     sample_tree.total_samples = total_samples_;
    134     sample_tree.total_period = total_period_;
    135     sample_tree.total_error_callchains = total_error_callchains_;
    136     return sample_tree;
    137   }
    138 
    139   virtual void ReportCmdProcessSampleRecord(std::shared_ptr<SampleRecord>& r) {
    140     return ProcessSampleRecord(*r);
    141   }
    142 
    143   virtual void ReportCmdProcessSampleRecord(const SampleRecord& r) {
    144     return ProcessSampleRecord(r);
    145   }
    146 
    147  protected:
    148   virtual uint64_t GetPeriod(const SampleRecord& r) = 0;
    149 
    150   SampleEntry* CreateSample(const SampleRecord& r, bool in_kernel,
    151                             uint64_t* acc_info) override {
    152     const ThreadEntry* thread =
    153         thread_tree_->FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
    154     const MapEntry* map =
    155         thread_tree_->FindMap(thread, r.ip_data.ip, in_kernel);
    156     uint64_t vaddr_in_file;
    157     const Symbol* symbol =
    158         thread_tree_->FindSymbol(map, r.ip_data.ip, &vaddr_in_file);
    159     uint64_t period = GetPeriod(r);
    160     *acc_info = period;
    161     return InsertSample(std::unique_ptr<SampleEntry>(
    162         new SampleEntry(r.time_data.time, period, 0, 1, thread, map, symbol, vaddr_in_file)));
    163   }
    164 
    165   SampleEntry* CreateBranchSample(const SampleRecord& r,
    166                                   const BranchStackItemType& item) override {
    167     const ThreadEntry* thread =
    168         thread_tree_->FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
    169     const MapEntry* from_map = thread_tree_->FindMap(thread, item.from);
    170     uint64_t from_vaddr_in_file;
    171     const Symbol* from_symbol =
    172         thread_tree_->FindSymbol(from_map, item.from, &from_vaddr_in_file);
    173     const MapEntry* to_map = thread_tree_->FindMap(thread, item.to);
    174     uint64_t to_vaddr_in_file;
    175     const Symbol* to_symbol =
    176         thread_tree_->FindSymbol(to_map, item.to, &to_vaddr_in_file);
    177     std::unique_ptr<SampleEntry> sample(
    178         new SampleEntry(r.time_data.time, r.period_data.period, 0, 1, thread,
    179                         to_map, to_symbol, to_vaddr_in_file));
    180     sample->branch_from.map = from_map;
    181     sample->branch_from.symbol = from_symbol;
    182     sample->branch_from.vaddr_in_file = from_vaddr_in_file;
    183     sample->branch_from.flags = item.flags;
    184     return InsertSample(std::move(sample));
    185   }
    186 
    187   SampleEntry* CreateCallChainSample(const SampleEntry* sample, uint64_t ip,
    188                                      bool in_kernel,
    189                                      const std::vector<SampleEntry*>& callchain,
    190                                      const uint64_t& acc_info) override {
    191     const ThreadEntry* thread = sample->thread;
    192     const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel);
    193     if (thread_tree_->IsUnknownDso(map->dso)) {
    194       // The unwinders can give wrong ip addresses, which can't map to a valid dso. Skip them.
    195       total_error_callchains_++;
    196       return nullptr;
    197     }
    198     uint64_t vaddr_in_file;
    199     const Symbol* symbol = thread_tree_->FindSymbol(map, ip, &vaddr_in_file);
    200     std::unique_ptr<SampleEntry> callchain_sample(new SampleEntry(
    201         sample->time, 0, acc_info, 0, thread, map, symbol, vaddr_in_file));
    202     callchain_sample->thread_comm = sample->thread_comm;
    203     return InsertCallChainSample(std::move(callchain_sample), callchain);
    204   }
    205 
    206   const ThreadEntry* GetThreadOfSample(SampleEntry* sample) override {
    207     return sample->thread;
    208   }
    209 
    210   uint64_t GetPeriodForCallChain(const uint64_t& acc_info) override {
    211     return acc_info;
    212   }
    213 
    214   bool FilterSample(const SampleEntry* sample) override {
    215     if (!pid_filter_.empty() &&
    216         pid_filter_.find(sample->thread->pid) == pid_filter_.end()) {
    217       return false;
    218     }
    219     if (!tid_filter_.empty() &&
    220         tid_filter_.find(sample->thread->tid) == tid_filter_.end()) {
    221       return false;
    222     }
    223     if (!comm_filter_.empty() &&
    224         comm_filter_.find(sample->thread_comm) == comm_filter_.end()) {
    225       return false;
    226     }
    227     if (!dso_filter_.empty() &&
    228         dso_filter_.find(sample->map->dso->Path()) == dso_filter_.end()) {
    229       return false;
    230     }
    231     if (!symbol_filter_.empty() &&
    232         symbol_filter_.find(sample->symbol->DemangledName()) ==
    233             symbol_filter_.end()) {
    234       return false;
    235     }
    236     return true;
    237   }
    238 
    239   void UpdateSummary(const SampleEntry* sample) override {
    240     total_samples_ += sample->sample_count;
    241     total_period_ += sample->period;
    242   }
    243 
    244   void MergeSample(SampleEntry* sample1, SampleEntry* sample2) override {
    245     sample1->period += sample2->period;
    246     sample1->accumulated_period += sample2->accumulated_period;
    247     sample1->sample_count += sample2->sample_count;
    248   }
    249 
    250  private:
    251   ThreadTree* thread_tree_;
    252 
    253   std::unordered_set<int> pid_filter_;
    254   std::unordered_set<int> tid_filter_;
    255   std::unordered_set<std::string> comm_filter_;
    256   std::unordered_set<std::string> dso_filter_;
    257   std::unordered_set<std::string> symbol_filter_;
    258 
    259   uint64_t total_samples_;
    260   uint64_t total_period_;
    261   uint64_t total_error_callchains_;
    262 };
    263 
    264 // Build sample tree based on event count in each sample.
    265 class EventCountSampleTreeBuilder : public ReportCmdSampleTreeBuilder {
    266  public:
    267   EventCountSampleTreeBuilder(const SampleComparator<SampleEntry>& sample_comparator,
    268                               ThreadTree* thread_tree)
    269       : ReportCmdSampleTreeBuilder(sample_comparator, thread_tree) { }
    270 
    271  protected:
    272   uint64_t GetPeriod(const SampleRecord& r) override {
    273     return r.period_data.period;
    274   }
    275 };
    276 
    277 // Build sample tree based on the time difference between current sample and next sample.
    278 class TimestampSampleTreeBuilder : public ReportCmdSampleTreeBuilder {
    279  public:
    280   TimestampSampleTreeBuilder(const SampleComparator<SampleEntry>& sample_comparator,
    281                              ThreadTree* thread_tree)
    282       : ReportCmdSampleTreeBuilder(sample_comparator, thread_tree) { }
    283 
    284   void ReportCmdProcessSampleRecord(std::shared_ptr<SampleRecord>& r) override {
    285     pid_t tid = static_cast<pid_t>(r->tid_data.tid);
    286     auto it = next_sample_cache_.find(tid);
    287     if (it == next_sample_cache_.end()) {
    288       next_sample_cache_[tid] = r;
    289     } else {
    290       std::shared_ptr<SampleRecord> cur = it->second;
    291       it->second = r;
    292       ProcessSampleRecord(*cur);
    293     }
    294   }
    295 
    296  protected:
    297   uint64_t GetPeriod(const SampleRecord& r) override {
    298     auto it = next_sample_cache_.find(r.tid_data.tid);
    299     CHECK(it != next_sample_cache_.end());
    300     // Normally the samples are sorted by time, but check here for safety.
    301     if (it->second->time_data.time > r.time_data.time) {
    302       return it->second->time_data.time - r.time_data.time;
    303     }
    304     return 1u;
    305   }
    306 
    307  private:
    308   std::unordered_map<pid_t, std::shared_ptr<SampleRecord>> next_sample_cache_;
    309 };
    310 
    311 struct SampleTreeBuilderOptions {
    312   SampleComparator<SampleEntry> comparator;
    313   ThreadTree* thread_tree;
    314   std::unordered_set<std::string> comm_filter;
    315   std::unordered_set<std::string> dso_filter;
    316   std::unordered_set<std::string> symbol_filter;
    317   std::unordered_set<int> pid_filter;
    318   std::unordered_set<int> tid_filter;
    319   bool use_branch_address;
    320   bool accumulate_callchain;
    321   bool build_callchain;
    322   bool use_caller_as_callchain_root;
    323   bool trace_offcpu;
    324 
    325   std::unique_ptr<ReportCmdSampleTreeBuilder> CreateSampleTreeBuilder() {
    326     std::unique_ptr<ReportCmdSampleTreeBuilder> builder;
    327     if (trace_offcpu) {
    328       builder.reset(new TimestampSampleTreeBuilder(comparator, thread_tree));
    329     } else {
    330       builder.reset(new EventCountSampleTreeBuilder(comparator, thread_tree));
    331     }
    332     builder->SetFilters(pid_filter, tid_filter, comm_filter, dso_filter, symbol_filter);
    333     builder->SetBranchSampleOption(use_branch_address);
    334     builder->SetCallChainSampleOptions(accumulate_callchain, build_callchain,
    335                                        use_caller_as_callchain_root);
    336     return builder;
    337   }
    338 };
    339 
    340 using ReportCmdSampleTreeSorter = SampleTreeSorter<SampleEntry>;
    341 using ReportCmdSampleTreeDisplayer =
    342     SampleTreeDisplayer<SampleEntry, SampleTree>;
    343 
    344 using ReportCmdCallgraphDisplayer =
    345     CallgraphDisplayer<SampleEntry, CallChainNode<SampleEntry>>;
    346 
    347 class ReportCmdCallgraphDisplayerWithVaddrInFile
    348     : public ReportCmdCallgraphDisplayer {
    349  protected:
    350   std::string PrintSampleName(const SampleEntry* sample) override {
    351     return android::base::StringPrintf("%s [+0x%" PRIx64 "]",
    352                                        sample->symbol->DemangledName(),
    353                                        sample->vaddr_in_file);
    354   }
    355 };
    356 
    357 struct EventAttrWithName {
    358   perf_event_attr attr;
    359   std::string name;
    360 };
    361 
    362 class ReportCommand : public Command {
    363  public:
    364   ReportCommand()
    365       : Command(
    366             "report", "report sampling information in perf.data",
    367             // clang-format off
    368 "Usage: simpleperf report [options]\n"
    369 "The default options are: -i perf.data --sort comm,pid,tid,dso,symbol.\n"
    370 "-b    Use the branch-to addresses in sampled take branches instead of the\n"
    371 "      instruction addresses. Only valid for perf.data recorded with -b/-j\n"
    372 "      option.\n"
    373 "--children    Print the overhead accumulated by appearing in the callchain.\n"
    374 "--comms comm1,comm2,...   Report only for selected comms.\n"
    375 "--dsos dso1,dso2,...      Report only for selected dsos.\n"
    376 "--full-callgraph  Print full call graph. Used with -g option. By default,\n"
    377 "                  brief call graph is printed.\n"
    378 "-g [callee|caller]    Print call graph. If callee mode is used, the graph\n"
    379 "                      shows how functions are called from others. Otherwise,\n"
    380 "                      the graph shows how functions call others.\n"
    381 "                      Default is caller mode.\n"
    382 "-i <file>  Specify path of record file, default is perf.data.\n"
    383 "--kallsyms <file>     Set the file to read kernel symbols.\n"
    384 "--max-stack <frames>  Set max stack frames shown when printing call graph.\n"
    385 "-n         Print the sample count for each item.\n"
    386 "--no-demangle         Don't demangle symbol names.\n"
    387 "--no-show-ip          Don't show vaddr in file for unknown symbols.\n"
    388 "-o report_file_name   Set report file name, default is stdout.\n"
    389 "--percent-limit <percent>  Set min percentage shown when printing call graph.\n"
    390 "--pids pid1,pid2,...  Report only for selected pids.\n"
    391 "--raw-period          Report period count instead of period percentage.\n"
    392 "--sort key1,key2,...  Select keys used to sort and print the report. The\n"
    393 "                      appearance order of keys decides the order of keys used\n"
    394 "                      to sort and print the report.\n"
    395 "                      Possible keys include:\n"
    396 "                        pid             -- process id\n"
    397 "                        tid             -- thread id\n"
    398 "                        comm            -- thread name (can be changed during\n"
    399 "                                           the lifetime of a thread)\n"
    400 "                        dso             -- shared library\n"
    401 "                        symbol          -- function name in the shared library\n"
    402 "                        vaddr_in_file   -- virtual address in the shared\n"
    403 "                                           library\n"
    404 "                      Keys can only be used with -b option:\n"
    405 "                        dso_from        -- shared library branched from\n"
    406 "                        dso_to          -- shared library branched to\n"
    407 "                        symbol_from     -- name of function branched from\n"
    408 "                        symbol_to       -- name of function branched to\n"
    409 "                      The default sort keys are:\n"
    410 "                        comm,pid,tid,dso,symbol\n"
    411 "--symbols symbol1;symbol2;...    Report only for selected symbols.\n"
    412 "--symfs <dir>         Look for files with symbols relative to this directory.\n"
    413 "--tids tid1,tid2,...  Report only for selected tids.\n"
    414 "--vmlinux <file>      Parse kernel symbols from <file>.\n"
    415             // clang-format on
    416             ),
    417         record_filename_("perf.data"),
    418         record_file_arch_(GetBuildArch()),
    419         use_branch_address_(false),
    420         system_wide_collection_(false),
    421         accumulate_callchain_(false),
    422         print_callgraph_(false),
    423         callgraph_show_callee_(false),
    424         callgraph_max_stack_(UINT32_MAX),
    425         callgraph_percent_limit_(0),
    426         raw_period_(false),
    427         brief_callgraph_(true),
    428         trace_offcpu_(false),
    429         sched_switch_attr_id_(0u) {}
    430 
    431   bool Run(const std::vector<std::string>& args);
    432 
    433  private:
    434   bool ParseOptions(const std::vector<std::string>& args);
    435   bool ReadMetaInfoFromRecordFile();
    436   bool ReadEventAttrFromRecordFile();
    437   bool ReadFeaturesFromRecordFile();
    438   bool ReadSampleTreeFromRecordFile();
    439   bool ProcessRecord(std::unique_ptr<Record> record);
    440   void ProcessSampleRecordInTraceOffCpuMode(std::unique_ptr<Record> record, size_t attr_id);
    441   bool ProcessTracingData(const std::vector<char>& data);
    442   bool PrintReport();
    443   void PrintReportContext(FILE* fp);
    444 
    445   std::string record_filename_;
    446   ArchType record_file_arch_;
    447   std::unique_ptr<RecordFileReader> record_file_reader_;
    448   std::vector<EventAttrWithName> event_attrs_;
    449   ThreadTree thread_tree_;
    450   // Create a SampleTreeBuilder and SampleTree for each event_attr.
    451   std::vector<SampleTree> sample_tree_;
    452   SampleTreeBuilderOptions sample_tree_builder_options_;
    453   std::vector<std::unique_ptr<ReportCmdSampleTreeBuilder>> sample_tree_builder_;
    454 
    455   std::unique_ptr<ReportCmdSampleTreeSorter> sample_tree_sorter_;
    456   std::unique_ptr<ReportCmdSampleTreeDisplayer> sample_tree_displayer_;
    457   bool use_branch_address_;
    458   std::string record_cmdline_;
    459   bool system_wide_collection_;
    460   bool accumulate_callchain_;
    461   bool print_callgraph_;
    462   bool callgraph_show_callee_;
    463   uint32_t callgraph_max_stack_;
    464   double callgraph_percent_limit_;
    465   bool raw_period_;
    466   bool brief_callgraph_;
    467   bool trace_offcpu_;
    468   size_t sched_switch_attr_id_;
    469 
    470   std::string report_filename_;
    471   std::unordered_map<std::string, std::string> meta_info_;
    472   std::unique_ptr<ScopedEventTypes> scoped_event_types_;
    473 };
    474 
    475 bool ReportCommand::Run(const std::vector<std::string>& args) {
    476   // 1. Parse options.
    477   if (!ParseOptions(args)) {
    478     return false;
    479   }
    480 
    481   // 2. Read record file and build SampleTree.
    482   record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
    483   if (record_file_reader_ == nullptr) {
    484     return false;
    485   }
    486   if (!ReadMetaInfoFromRecordFile()) {
    487     return false;
    488   }
    489   if (!ReadEventAttrFromRecordFile()) {
    490     return false;
    491   }
    492   // Read features first to prepare build ids used when building SampleTree.
    493   if (!ReadFeaturesFromRecordFile()) {
    494     return false;
    495   }
    496   ScopedCurrentArch scoped_arch(record_file_arch_);
    497   if (!ReadSampleTreeFromRecordFile()) {
    498     return false;
    499   }
    500 
    501   // 3. Show collected information.
    502   if (!PrintReport()) {
    503     return false;
    504   }
    505 
    506   return true;
    507 }
    508 
    509 bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
    510   bool demangle = true;
    511   bool show_ip_for_unknown_symbol = true;
    512   std::string symfs_dir;
    513   std::string vmlinux;
    514   bool print_sample_count = false;
    515   std::vector<std::string> sort_keys = {"comm", "pid", "tid", "dso", "symbol"};
    516 
    517   for (size_t i = 0; i < args.size(); ++i) {
    518     if (args[i] == "-b") {
    519       use_branch_address_ = true;
    520     } else if (args[i] == "--children") {
    521       accumulate_callchain_ = true;
    522     } else if (args[i] == "--comms" || args[i] == "--dsos") {
    523       std::unordered_set<std::string>& filter =
    524           (args[i] == "--comms" ? sample_tree_builder_options_.comm_filter
    525                                 : sample_tree_builder_options_.dso_filter);
    526       if (!NextArgumentOrError(args, &i)) {
    527         return false;
    528       }
    529       std::vector<std::string> strs = android::base::Split(args[i], ",");
    530       filter.insert(strs.begin(), strs.end());
    531     } else if (args[i] == "--full-callgraph") {
    532       brief_callgraph_ = false;
    533     } else if (args[i] == "-g") {
    534       print_callgraph_ = true;
    535       accumulate_callchain_ = true;
    536       if (i + 1 < args.size() && args[i + 1][0] != '-') {
    537         ++i;
    538         if (args[i] == "callee") {
    539           callgraph_show_callee_ = true;
    540         } else if (args[i] == "caller") {
    541           callgraph_show_callee_ = false;
    542         } else {
    543           LOG(ERROR) << "Unknown argument with -g option: " << args[i];
    544           return false;
    545         }
    546       }
    547     } else if (args[i] == "-i") {
    548       if (!NextArgumentOrError(args, &i)) {
    549         return false;
    550       }
    551       record_filename_ = args[i];
    552 
    553     } else if (args[i] == "--kallsyms") {
    554       if (!NextArgumentOrError(args, &i)) {
    555         return false;
    556       }
    557       std::string kallsyms;
    558       if (!android::base::ReadFileToString(args[i], &kallsyms)) {
    559         LOG(ERROR) << "Can't read kernel symbols from " << args[i];
    560         return false;
    561       }
    562       Dso::SetKallsyms(kallsyms);
    563     } else if (args[i] == "--max-stack") {
    564       if (!NextArgumentOrError(args, &i)) {
    565         return false;
    566       }
    567       if (!android::base::ParseUint(args[i].c_str(), &callgraph_max_stack_)) {
    568         LOG(ERROR) << "invalid arg for --max-stack: " << args[i];
    569         return false;
    570       }
    571     } else if (args[i] == "-n") {
    572       print_sample_count = true;
    573 
    574     } else if (args[i] == "--no-demangle") {
    575       demangle = false;
    576     } else if (args[i] == "--no-show-ip") {
    577       show_ip_for_unknown_symbol = false;
    578     } else if (args[i] == "-o") {
    579       if (!NextArgumentOrError(args, &i)) {
    580         return false;
    581       }
    582       report_filename_ = args[i];
    583     } else if (args[i] == "--percent-limit") {
    584       if (!NextArgumentOrError(args, &i)) {
    585         return false;
    586       }
    587       if (!android::base::ParseDouble(args[i].c_str(),
    588                                       &callgraph_percent_limit_, 0.0)) {
    589         LOG(ERROR) << "invalid arg for --percent-limit: " << args[i];
    590       }
    591     } else if (args[i] == "--pids" || args[i] == "--tids") {
    592       const std::string& option = args[i];
    593       std::unordered_set<int>& filter =
    594           (option == "--pids" ? sample_tree_builder_options_.pid_filter
    595                               : sample_tree_builder_options_.tid_filter);
    596       if (!NextArgumentOrError(args, &i)) {
    597         return false;
    598       }
    599       std::vector<std::string> strs = android::base::Split(args[i], ",");
    600       for (const auto& s : strs) {
    601         int id;
    602         if (!android::base::ParseInt(s.c_str(), &id, 0)) {
    603           LOG(ERROR) << "invalid id in " << option << " option: " << s;
    604           return false;
    605         }
    606         filter.insert(id);
    607       }
    608     } else if (args[i] == "--raw-period") {
    609       raw_period_ = true;
    610     } else if (args[i] == "--sort") {
    611       if (!NextArgumentOrError(args, &i)) {
    612         return false;
    613       }
    614       sort_keys = android::base::Split(args[i], ",");
    615     } else if (args[i] == "--symbols") {
    616       if (!NextArgumentOrError(args, &i)) {
    617         return false;
    618       }
    619       std::vector<std::string> strs = android::base::Split(args[i], ";");
    620       sample_tree_builder_options_.symbol_filter.insert(strs.begin(), strs.end());
    621     } else if (args[i] == "--symfs") {
    622       if (!NextArgumentOrError(args, &i)) {
    623         return false;
    624       }
    625       symfs_dir = args[i];
    626 
    627     } else if (args[i] == "--vmlinux") {
    628       if (!NextArgumentOrError(args, &i)) {
    629         return false;
    630       }
    631       vmlinux = args[i];
    632     } else {
    633       ReportUnknownOption(args, i);
    634       return false;
    635     }
    636   }
    637 
    638   Dso::SetDemangle(demangle);
    639   if (!Dso::SetSymFsDir(symfs_dir)) {
    640     return false;
    641   }
    642   if (!vmlinux.empty()) {
    643     Dso::SetVmlinux(vmlinux);
    644   }
    645 
    646   if (show_ip_for_unknown_symbol) {
    647     thread_tree_.ShowIpForUnknownSymbol();
    648   }
    649 
    650   SampleDisplayer<SampleEntry, SampleTree> displayer;
    651   SampleComparator<SampleEntry> comparator;
    652 
    653   if (accumulate_callchain_) {
    654     if (raw_period_) {
    655       displayer.AddDisplayFunction("Children", DisplayAccumulatedPeriod);
    656       displayer.AddDisplayFunction("Self", DisplaySelfPeriod);
    657     } else {
    658       displayer.AddDisplayFunction("Children", DisplayAccumulatedOverhead);
    659       displayer.AddDisplayFunction("Self", DisplaySelfOverhead);
    660     }
    661   } else {
    662     if (raw_period_) {
    663       displayer.AddDisplayFunction("Overhead", DisplaySelfPeriod);
    664     } else {
    665       displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead);
    666     }
    667   }
    668   if (print_sample_count) {
    669     displayer.AddDisplayFunction("Sample", DisplaySampleCount);
    670   }
    671 
    672   for (auto& key : sort_keys) {
    673     if (!use_branch_address_ &&
    674         branch_sort_keys.find(key) != branch_sort_keys.end()) {
    675       LOG(ERROR) << "sort key '" << key << "' can only be used with -b option.";
    676       return false;
    677     }
    678     if (key == "pid") {
    679       comparator.AddCompareFunction(ComparePid);
    680       displayer.AddDisplayFunction("Pid", DisplayPid);
    681     } else if (key == "tid") {
    682       comparator.AddCompareFunction(CompareTid);
    683       displayer.AddDisplayFunction("Tid", DisplayTid);
    684     } else if (key == "comm") {
    685       comparator.AddCompareFunction(CompareComm);
    686       displayer.AddDisplayFunction("Command", DisplayComm);
    687     } else if (key == "dso") {
    688       comparator.AddCompareFunction(CompareDso);
    689       displayer.AddDisplayFunction("Shared Object", DisplayDso);
    690     } else if (key == "symbol") {
    691       comparator.AddCompareFunction(CompareSymbol);
    692       displayer.AddDisplayFunction("Symbol", DisplaySymbol);
    693     } else if (key == "vaddr_in_file") {
    694       comparator.AddCompareFunction(CompareVaddrInFile);
    695       displayer.AddDisplayFunction("VaddrInFile", DisplayVaddrInFile);
    696     } else if (key == "dso_from") {
    697       comparator.AddCompareFunction(CompareDsoFrom);
    698       displayer.AddDisplayFunction("Source Shared Object", DisplayDsoFrom);
    699     } else if (key == "dso_to") {
    700       comparator.AddCompareFunction(CompareDso);
    701       displayer.AddDisplayFunction("Target Shared Object", DisplayDso);
    702     } else if (key == "symbol_from") {
    703       comparator.AddCompareFunction(CompareSymbolFrom);
    704       displayer.AddDisplayFunction("Source Symbol", DisplaySymbolFrom);
    705     } else if (key == "symbol_to") {
    706       comparator.AddCompareFunction(CompareSymbol);
    707       displayer.AddDisplayFunction("Target Symbol", DisplaySymbol);
    708     } else {
    709       LOG(ERROR) << "Unknown sort key: " << key;
    710       return false;
    711     }
    712   }
    713   if (print_callgraph_) {
    714     bool has_symbol_key = false;
    715     bool has_vaddr_in_file_key = false;
    716     for (const auto& key : sort_keys) {
    717       if (key == "symbol") {
    718         has_symbol_key = true;
    719       } else if (key == "vaddr_in_file") {
    720         has_vaddr_in_file_key = true;
    721       }
    722     }
    723     if (has_symbol_key) {
    724       if (has_vaddr_in_file_key) {
    725         displayer.AddExclusiveDisplayFunction(
    726             ReportCmdCallgraphDisplayerWithVaddrInFile());
    727       } else {
    728         displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer(
    729             callgraph_max_stack_, callgraph_percent_limit_, brief_callgraph_));
    730       }
    731     }
    732   }
    733 
    734   sample_tree_builder_options_.comparator = comparator;
    735   sample_tree_builder_options_.thread_tree = &thread_tree_;
    736 
    737   SampleComparator<SampleEntry> sort_comparator;
    738   sort_comparator.AddCompareFunction(CompareTotalPeriod);
    739   if (print_callgraph_) {
    740     sort_comparator.AddCompareFunction(CompareCallGraphDuplicated);
    741   }
    742   sort_comparator.AddCompareFunction(ComparePeriod);
    743   sort_comparator.AddComparator(comparator);
    744   sample_tree_sorter_.reset(new ReportCmdSampleTreeSorter(sort_comparator));
    745   sample_tree_displayer_.reset(new ReportCmdSampleTreeDisplayer(displayer));
    746   return true;
    747 }
    748 
    749 bool ReportCommand::ReadMetaInfoFromRecordFile() {
    750   if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO)) {
    751     if (!record_file_reader_->ReadMetaInfoFeature(&meta_info_)) {
    752       return false;
    753     }
    754     auto it = meta_info_.find("system_wide_collection");
    755     if (it != meta_info_.end()) {
    756       system_wide_collection_ = it->second == "true";
    757     }
    758     it = meta_info_.find("trace_offcpu");
    759     if (it != meta_info_.end()) {
    760       trace_offcpu_ = it->second == "true";
    761     }
    762     it = meta_info_.find("event_type_info");
    763     if (it != meta_info_.end()) {
    764       scoped_event_types_.reset(new ScopedEventTypes(it->second));
    765     }
    766   }
    767   return true;
    768 }
    769 
    770 bool ReportCommand::ReadEventAttrFromRecordFile() {
    771   std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
    772   for (const auto& attr_with_id : attrs) {
    773     EventAttrWithName attr;
    774     attr.attr = *attr_with_id.attr;
    775     attr.name = GetEventNameByAttr(attr.attr);
    776     event_attrs_.push_back(attr);
    777   }
    778   if (use_branch_address_) {
    779     bool has_branch_stack = true;
    780     for (const auto& attr : event_attrs_) {
    781       if ((attr.attr.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) {
    782         has_branch_stack = false;
    783         break;
    784       }
    785     }
    786     if (!has_branch_stack) {
    787       LOG(ERROR) << record_filename_
    788                  << " is not recorded with branch stack sampling option.";
    789       return false;
    790     }
    791   }
    792   if (trace_offcpu_) {
    793     size_t i;
    794     for (i = 0; i < event_attrs_.size(); ++i) {
    795       if (event_attrs_[i].name == "sched:sched_switch") {
    796         break;
    797       }
    798     }
    799     CHECK_NE(i, event_attrs_.size());
    800     sched_switch_attr_id_ = i;
    801   }
    802   return true;
    803 }
    804 
    805 bool ReportCommand::ReadFeaturesFromRecordFile() {
    806   record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
    807 
    808   std::string arch =
    809       record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
    810   if (!arch.empty()) {
    811     record_file_arch_ = GetArchType(arch);
    812     if (record_file_arch_ == ARCH_UNSUPPORTED) {
    813       return false;
    814     }
    815   }
    816 
    817   std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
    818   if (!cmdline.empty()) {
    819     record_cmdline_ = android::base::Join(cmdline, ' ');
    820     if (meta_info_.find("system_wide_collection") == meta_info_.end()) {
    821       // TODO: the code to detect system wide collection option is fragile, remove
    822       // it once we can do cross unwinding.
    823       for (size_t i = 0; i < cmdline.size(); i++) {
    824         std::string& s = cmdline[i];
    825         if (s == "-a") {
    826           system_wide_collection_ = true;
    827           break;
    828         } else if (s == "--call-graph" || s == "--cpu" || s == "-e" ||
    829                    s == "-f" || s == "-F" || s == "-j" || s == "-m" ||
    830                    s == "-o" || s == "-p" || s == "-t") {
    831           i++;
    832         } else if (!s.empty() && s[0] != '-') {
    833           break;
    834         }
    835       }
    836     }
    837   }
    838   if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_TRACING_DATA)) {
    839     std::vector<char> tracing_data;
    840     if (!record_file_reader_->ReadFeatureSection(
    841             PerfFileFormat::FEAT_TRACING_DATA, &tracing_data)) {
    842       return false;
    843     }
    844     if (!ProcessTracingData(tracing_data)) {
    845       return false;
    846     }
    847   }
    848   return true;
    849 }
    850 
    851 bool ReportCommand::ReadSampleTreeFromRecordFile() {
    852   sample_tree_builder_options_.use_branch_address = use_branch_address_;
    853   sample_tree_builder_options_.accumulate_callchain = accumulate_callchain_;
    854   sample_tree_builder_options_.build_callchain = print_callgraph_;
    855   sample_tree_builder_options_.use_caller_as_callchain_root = !callgraph_show_callee_;
    856   sample_tree_builder_options_.trace_offcpu = trace_offcpu_;
    857 
    858   for (size_t i = 0; i < event_attrs_.size(); ++i) {
    859     sample_tree_builder_.push_back(sample_tree_builder_options_.CreateSampleTreeBuilder());
    860   }
    861 
    862   if (!record_file_reader_->ReadDataSection(
    863           [this](std::unique_ptr<Record> record) {
    864             return ProcessRecord(std::move(record));
    865           })) {
    866     return false;
    867   }
    868   for (size_t i = 0; i < sample_tree_builder_.size(); ++i) {
    869     sample_tree_.push_back(sample_tree_builder_[i]->GetSampleTree());
    870     sample_tree_sorter_->Sort(sample_tree_.back().samples, print_callgraph_);
    871   }
    872   return true;
    873 }
    874 
    875 bool ReportCommand::ProcessRecord(std::unique_ptr<Record> record) {
    876   thread_tree_.Update(*record);
    877   if (record->type() == PERF_RECORD_SAMPLE) {
    878     size_t attr_id = record_file_reader_->GetAttrIndexOfRecord(record.get());
    879     if (!trace_offcpu_) {
    880       sample_tree_builder_[attr_id]->ReportCmdProcessSampleRecord(
    881           *static_cast<SampleRecord*>(record.get()));
    882     } else {
    883       ProcessSampleRecordInTraceOffCpuMode(std::move(record), attr_id);
    884     }
    885   } else if (record->type() == PERF_RECORD_TRACING_DATA) {
    886     const auto& r = *static_cast<TracingDataRecord*>(record.get());
    887     if (!ProcessTracingData(std::vector<char>(r.data, r.data + r.data_size))) {
    888       return false;
    889     }
    890   }
    891   return true;
    892 }
    893 
    894 
    895 void ReportCommand::ProcessSampleRecordInTraceOffCpuMode(std::unique_ptr<Record> record,
    896                                                          size_t attr_id) {
    897   std::shared_ptr<SampleRecord> r(static_cast<SampleRecord*>(record.release()));
    898   if (attr_id == sched_switch_attr_id_) {
    899     // If this sample belongs to sched_switch event, we should broadcast the offcpu info
    900     // to other event types.
    901     for (size_t i = 0; i < event_attrs_.size(); ++i) {
    902       if (i == sched_switch_attr_id_) {
    903         continue;
    904       }
    905       sample_tree_builder_[i]->ReportCmdProcessSampleRecord(r);
    906     }
    907   } else {
    908     sample_tree_builder_[attr_id]->ReportCmdProcessSampleRecord(r);
    909   }
    910 }
    911 
    912 bool ReportCommand::ProcessTracingData(const std::vector<char>& data) {
    913   Tracing tracing(data);
    914   for (auto& attr : event_attrs_) {
    915     if (attr.attr.type == PERF_TYPE_TRACEPOINT) {
    916       uint64_t trace_event_id = attr.attr.config;
    917       attr.name = tracing.GetTracingEventNameHavingId(trace_event_id);
    918     }
    919   }
    920   return true;
    921 }
    922 
    923 bool ReportCommand::PrintReport() {
    924   std::unique_ptr<FILE, decltype(&fclose)> file_handler(nullptr, fclose);
    925   FILE* report_fp = stdout;
    926   if (!report_filename_.empty()) {
    927     report_fp = fopen(report_filename_.c_str(), "w");
    928     if (report_fp == nullptr) {
    929       PLOG(ERROR) << "failed to open file " << report_filename_;
    930       return false;
    931     }
    932     file_handler.reset(report_fp);
    933   }
    934   PrintReportContext(report_fp);
    935   for (size_t i = 0; i < event_attrs_.size(); ++i) {
    936     if (trace_offcpu_ && i == sched_switch_attr_id_) {
    937       continue;
    938     }
    939     if (i != 0) {
    940       fprintf(report_fp, "\n");
    941     }
    942     EventAttrWithName& attr = event_attrs_[i];
    943     SampleTree& sample_tree = sample_tree_[i];
    944     fprintf(report_fp, "Event: %s (type %u, config %llu)\n", attr.name.c_str(),
    945             attr.attr.type, attr.attr.config);
    946     fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree.total_samples);
    947     if (sample_tree.total_error_callchains != 0) {
    948       fprintf(report_fp, "Error Callchains: %" PRIu64 ", %f%%\n",
    949               sample_tree.total_error_callchains,
    950               sample_tree.total_error_callchains * 100.0 / sample_tree.total_samples);
    951     }
    952     const char* period_prefix = trace_offcpu_ ? "Time in ns" : "Event count";
    953     fprintf(report_fp, "%s: %" PRIu64 "\n\n", period_prefix, sample_tree.total_period);
    954     sample_tree_displayer_->DisplaySamples(report_fp, sample_tree.samples, &sample_tree);
    955   }
    956   fflush(report_fp);
    957   if (ferror(report_fp) != 0) {
    958     PLOG(ERROR) << "print report failed";
    959     return false;
    960   }
    961   return true;
    962 }
    963 
    964 void ReportCommand::PrintReportContext(FILE* report_fp) {
    965   if (!record_cmdline_.empty()) {
    966     fprintf(report_fp, "Cmdline: %s\n", record_cmdline_.c_str());
    967   }
    968   fprintf(report_fp, "Arch: %s\n", GetArchString(record_file_arch_).c_str());
    969 }
    970 
    971 }  // namespace
    972 
    973 void RegisterReportCommand() {
    974   RegisterCommand("report",
    975                   [] { return std::unique_ptr<Command>(new ReportCommand()); });
    976 }
    977