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