OSDN Git Service

Merge "simpleperf: fix unknown binary in samples."
[android-x86/system-extras.git] / simpleperf / cmd_report.cpp
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
46 namespace {
47
48 static std::set<std::string> branch_sort_keys = {
49     "dso_from", "dso_to", "symbol_from", "symbol_to",
50 };
51 struct 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
61 struct 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
98 struct SampleTree {
99   std::vector<SampleEntry*> samples;
100   uint64_t total_samples;
101   uint64_t total_period;
102   uint64_t total_error_callchains;
103 };
104
105 BUILD_COMPARE_VALUE_FUNCTION(CompareVaddrInFile, vaddr_in_file);
106 BUILD_DISPLAY_HEX64_FUNCTION(DisplayVaddrInFile, vaddr_in_file);
107
108 class 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
256 struct 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
281 using ReportCmdSampleTreeSorter = SampleTreeSorter<SampleEntry>;
282 using ReportCmdSampleTreeDisplayer =
283     SampleTreeDisplayer<SampleEntry, SampleTree>;
284
285 using ReportCmdCallgraphDisplayer =
286     CallgraphDisplayer<SampleEntry, CallChainNode<SampleEntry>>;
287
288 class 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
298 struct EventAttrWithName {
299   perf_event_attr attr;
300   std::string name;
301 };
302
303 class 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 "-b    Use the branch-to addresses in sampled take branches instead of the\n"
311 "      instruction addresses. Only valid for perf.data recorded with -b/-j\n"
312 "      option.\n"
313 "--brief-callgraph     Print brief call graph.\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 "-g [callee|caller]    Print call graph. If callee mode is used, the graph\n"
318 "                      shows how functions are called from others. Otherwise,\n"
319 "                      the graph shows how functions call others.\n"
320 "                      Default is caller mode.\n"
321 "-i <file>  Specify path of record file, default is perf.data.\n"
322 "--kallsyms <file>     Set the file to read kernel symbols.\n"
323 "--max-stack <frames>  Set max stack frames shown when printing call graph.\n"
324 "-n         Print the sample count for each item.\n"
325 "--no-demangle         Don't demangle symbol names.\n"
326 "--no-show-ip          Don't show vaddr in file for unknown symbols.\n"
327 "-o report_file_name   Set report file name, default is stdout.\n"
328 "--percent-limit <percent>  Set min percentage shown when printing call graph.\n"
329 "--pids pid1,pid2,...  Report only for selected pids.\n"
330 "--raw-period          Report period count instead of period percentage.\n"
331 "--sort key1,key2,...  Select keys used to sort and print the report. The\n"
332 "                      appearance order of keys decides the order of keys used\n"
333 "                      to sort and print the report.\n"
334 "                      Possible keys include:\n"
335 "                        pid             -- process id\n"
336 "                        tid             -- thread id\n"
337 "                        comm            -- thread name (can be changed during\n"
338 "                                           the lifetime of a thread)\n"
339 "                        dso             -- shared library\n"
340 "                        symbol          -- function name in the shared library\n"
341 "                        vaddr_in_file   -- virtual address in the shared\n"
342 "                                           library\n"
343 "                      Keys can only be used with -b option:\n"
344 "                        dso_from        -- shared library branched from\n"
345 "                        dso_to          -- shared library branched to\n"
346 "                        symbol_from     -- name of function branched from\n"
347 "                        symbol_to       -- name of function branched to\n"
348 "                      The default sort keys are:\n"
349 "                        comm,pid,tid,dso,symbol\n"
350 "--symbols symbol1;symbol2;...    Report only for selected symbols.\n"
351 "--symfs <dir>         Look for files with symbols relative to this directory.\n"
352 "--tids tid1,tid2,...  Report only for selected tids.\n"
353 "--vmlinux <file>      Parse kernel symbols from <file>.\n"
354             // clang-format on
355             ),
356         record_filename_("perf.data"),
357         record_file_arch_(GetBuildArch()),
358         use_branch_address_(false),
359         system_wide_collection_(false),
360         accumulate_callchain_(false),
361         print_callgraph_(false),
362         callgraph_show_callee_(false),
363         callgraph_max_stack_(UINT32_MAX),
364         callgraph_percent_limit_(0),
365         raw_period_(false),
366         brief_callgraph_(false) {}
367
368   bool Run(const std::vector<std::string>& args);
369
370  private:
371   bool ParseOptions(const std::vector<std::string>& args);
372   bool ReadEventAttrFromRecordFile();
373   bool ReadFeaturesFromRecordFile();
374   bool ReadSampleTreeFromRecordFile();
375   bool ProcessRecord(std::unique_ptr<Record> record);
376   bool ProcessTracingData(const std::vector<char>& data);
377   bool PrintReport();
378   void PrintReportContext(FILE* fp);
379
380   std::string record_filename_;
381   ArchType record_file_arch_;
382   std::unique_ptr<RecordFileReader> record_file_reader_;
383   std::vector<EventAttrWithName> event_attrs_;
384   ThreadTree thread_tree_;
385   // Create a SampleTreeBuilder and SampleTree for each event_attr.
386   std::vector<SampleTree> sample_tree_;
387   SampleTreeBuilderOptions sample_tree_builder_options_;
388   std::vector<std::unique_ptr<ReportCmdSampleTreeBuilder>> sample_tree_builder_;
389
390   std::unique_ptr<ReportCmdSampleTreeSorter> sample_tree_sorter_;
391   std::unique_ptr<ReportCmdSampleTreeDisplayer> sample_tree_displayer_;
392   bool use_branch_address_;
393   std::string record_cmdline_;
394   bool system_wide_collection_;
395   bool accumulate_callchain_;
396   bool print_callgraph_;
397   bool callgraph_show_callee_;
398   uint32_t callgraph_max_stack_;
399   double callgraph_percent_limit_;
400   bool raw_period_;
401   bool brief_callgraph_;
402
403   std::string report_filename_;
404 };
405
406 bool ReportCommand::Run(const std::vector<std::string>& args) {
407   // 1. Parse options.
408   if (!ParseOptions(args)) {
409     return false;
410   }
411
412   // 2. Read record file and build SampleTree.
413   record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
414   if (record_file_reader_ == nullptr) {
415     return false;
416   }
417   if (!ReadEventAttrFromRecordFile()) {
418     return false;
419   }
420   // Read features first to prepare build ids used when building SampleTree.
421   if (!ReadFeaturesFromRecordFile()) {
422     return false;
423   }
424   ScopedCurrentArch scoped_arch(record_file_arch_);
425   if (!ReadSampleTreeFromRecordFile()) {
426     return false;
427   }
428
429   // 3. Show collected information.
430   if (!PrintReport()) {
431     return false;
432   }
433
434   return true;
435 }
436
437 bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
438   bool demangle = true;
439   bool show_ip_for_unknown_symbol = true;
440   std::string symfs_dir;
441   std::string vmlinux;
442   bool print_sample_count = false;
443   std::vector<std::string> sort_keys = {"comm", "pid", "tid", "dso", "symbol"};
444
445   for (size_t i = 0; i < args.size(); ++i) {
446     if (args[i] == "-b") {
447       use_branch_address_ = true;
448     } else if (args[i] == "--brief-callgraph") {
449       brief_callgraph_ = 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
462     } else if (args[i] == "-g") {
463       print_callgraph_ = true;
464       accumulate_callchain_ = true;
465       if (i + 1 < args.size() && args[i + 1][0] != '-') {
466         ++i;
467         if (args[i] == "callee") {
468           callgraph_show_callee_ = true;
469         } else if (args[i] == "caller") {
470           callgraph_show_callee_ = false;
471         } else {
472           LOG(ERROR) << "Unknown argument with -g option: " << args[i];
473           return false;
474         }
475       }
476     } else if (args[i] == "-i") {
477       if (!NextArgumentOrError(args, &i)) {
478         return false;
479       }
480       record_filename_ = args[i];
481
482     } else if (args[i] == "--kallsyms") {
483       if (!NextArgumentOrError(args, &i)) {
484         return false;
485       }
486       std::string kallsyms;
487       if (!android::base::ReadFileToString(args[i], &kallsyms)) {
488         LOG(ERROR) << "Can't read kernel symbols from " << args[i];
489         return false;
490       }
491       Dso::SetKallsyms(kallsyms);
492     } else if (args[i] == "--max-stack") {
493       if (!NextArgumentOrError(args, &i)) {
494         return false;
495       }
496       if (!android::base::ParseUint(args[i].c_str(), &callgraph_max_stack_)) {
497         LOG(ERROR) << "invalid arg for --max-stack: " << args[i];
498         return false;
499       }
500     } else if (args[i] == "-n") {
501       print_sample_count = true;
502
503     } else if (args[i] == "--no-demangle") {
504       demangle = false;
505     } else if (args[i] == "--no-show-ip") {
506       show_ip_for_unknown_symbol = false;
507     } else if (args[i] == "-o") {
508       if (!NextArgumentOrError(args, &i)) {
509         return false;
510       }
511       report_filename_ = args[i];
512     } else if (args[i] == "--percent-limit") {
513       if (!NextArgumentOrError(args, &i)) {
514         return false;
515       }
516       if (!android::base::ParseDouble(args[i].c_str(),
517                                       &callgraph_percent_limit_, 0.0)) {
518         LOG(ERROR) << "invalid arg for --percent-limit: " << args[i];
519       }
520     } else if (args[i] == "--pids" || args[i] == "--tids") {
521       const std::string& option = args[i];
522       std::unordered_set<int>& filter =
523           (option == "--pids" ? sample_tree_builder_options_.pid_filter
524                               : sample_tree_builder_options_.tid_filter);
525       if (!NextArgumentOrError(args, &i)) {
526         return false;
527       }
528       std::vector<std::string> strs = android::base::Split(args[i], ",");
529       for (const auto& s : strs) {
530         int id;
531         if (!android::base::ParseInt(s.c_str(), &id, 0)) {
532           LOG(ERROR) << "invalid id in " << option << " option: " << s;
533           return false;
534         }
535         filter.insert(id);
536       }
537     } else if (args[i] == "--raw-period") {
538       raw_period_ = true;
539     } else if (args[i] == "--sort") {
540       if (!NextArgumentOrError(args, &i)) {
541         return false;
542       }
543       sort_keys = android::base::Split(args[i], ",");
544     } else if (args[i] == "--symbols") {
545       if (!NextArgumentOrError(args, &i)) {
546         return false;
547       }
548       std::vector<std::string> strs = android::base::Split(args[i], ";");
549       sample_tree_builder_options_.symbol_filter.insert(strs.begin(), strs.end());
550     } else if (args[i] == "--symfs") {
551       if (!NextArgumentOrError(args, &i)) {
552         return false;
553       }
554       symfs_dir = args[i];
555
556     } else if (args[i] == "--vmlinux") {
557       if (!NextArgumentOrError(args, &i)) {
558         return false;
559       }
560       vmlinux = args[i];
561     } else {
562       ReportUnknownOption(args, i);
563       return false;
564     }
565   }
566
567   Dso::SetDemangle(demangle);
568   if (!Dso::SetSymFsDir(symfs_dir)) {
569     return false;
570   }
571   if (!vmlinux.empty()) {
572     Dso::SetVmlinux(vmlinux);
573   }
574
575   if (show_ip_for_unknown_symbol) {
576     thread_tree_.ShowIpForUnknownSymbol();
577   }
578
579   SampleDisplayer<SampleEntry, SampleTree> displayer;
580   SampleComparator<SampleEntry> comparator;
581
582   if (accumulate_callchain_) {
583     if (raw_period_) {
584       displayer.AddDisplayFunction("Children", DisplayAccumulatedPeriod);
585       displayer.AddDisplayFunction("Self", DisplaySelfPeriod);
586     } else {
587       displayer.AddDisplayFunction("Children", DisplayAccumulatedOverhead);
588       displayer.AddDisplayFunction("Self", DisplaySelfOverhead);
589     }
590   } else {
591     if (raw_period_) {
592       displayer.AddDisplayFunction("Overhead", DisplaySelfPeriod);
593     } else {
594       displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead);
595     }
596   }
597   if (print_sample_count) {
598     displayer.AddDisplayFunction("Sample", DisplaySampleCount);
599   }
600
601   for (auto& key : sort_keys) {
602     if (!use_branch_address_ &&
603         branch_sort_keys.find(key) != branch_sort_keys.end()) {
604       LOG(ERROR) << "sort key '" << key << "' can only be used with -b option.";
605       return false;
606     }
607     if (key == "pid") {
608       comparator.AddCompareFunction(ComparePid);
609       displayer.AddDisplayFunction("Pid", DisplayPid);
610     } else if (key == "tid") {
611       comparator.AddCompareFunction(CompareTid);
612       displayer.AddDisplayFunction("Tid", DisplayTid);
613     } else if (key == "comm") {
614       comparator.AddCompareFunction(CompareComm);
615       displayer.AddDisplayFunction("Command", DisplayComm);
616     } else if (key == "dso") {
617       comparator.AddCompareFunction(CompareDso);
618       displayer.AddDisplayFunction("Shared Object", DisplayDso);
619     } else if (key == "symbol") {
620       comparator.AddCompareFunction(CompareSymbol);
621       displayer.AddDisplayFunction("Symbol", DisplaySymbol);
622     } else if (key == "vaddr_in_file") {
623       comparator.AddCompareFunction(CompareVaddrInFile);
624       displayer.AddDisplayFunction("VaddrInFile", DisplayVaddrInFile);
625     } else if (key == "dso_from") {
626       comparator.AddCompareFunction(CompareDsoFrom);
627       displayer.AddDisplayFunction("Source Shared Object", DisplayDsoFrom);
628     } else if (key == "dso_to") {
629       comparator.AddCompareFunction(CompareDso);
630       displayer.AddDisplayFunction("Target Shared Object", DisplayDso);
631     } else if (key == "symbol_from") {
632       comparator.AddCompareFunction(CompareSymbolFrom);
633       displayer.AddDisplayFunction("Source Symbol", DisplaySymbolFrom);
634     } else if (key == "symbol_to") {
635       comparator.AddCompareFunction(CompareSymbol);
636       displayer.AddDisplayFunction("Target Symbol", DisplaySymbol);
637     } else {
638       LOG(ERROR) << "Unknown sort key: " << key;
639       return false;
640     }
641   }
642   if (print_callgraph_) {
643     bool has_symbol_key = false;
644     bool has_vaddr_in_file_key = false;
645     for (const auto& key : sort_keys) {
646       if (key == "symbol") {
647         has_symbol_key = true;
648       } else if (key == "vaddr_in_file") {
649         has_vaddr_in_file_key = true;
650       }
651     }
652     if (has_symbol_key) {
653       if (has_vaddr_in_file_key) {
654         displayer.AddExclusiveDisplayFunction(
655             ReportCmdCallgraphDisplayerWithVaddrInFile());
656       } else {
657         displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer(
658             callgraph_max_stack_, callgraph_percent_limit_, brief_callgraph_));
659       }
660     }
661   }
662
663   sample_tree_builder_options_.comparator = comparator;
664   sample_tree_builder_options_.thread_tree = &thread_tree_;
665
666   SampleComparator<SampleEntry> sort_comparator;
667   sort_comparator.AddCompareFunction(CompareTotalPeriod);
668   if (print_callgraph_) {
669     sort_comparator.AddCompareFunction(CompareCallGraphDuplicated);
670   }
671   sort_comparator.AddCompareFunction(ComparePeriod);
672   sort_comparator.AddComparator(comparator);
673   sample_tree_sorter_.reset(new ReportCmdSampleTreeSorter(sort_comparator));
674   sample_tree_displayer_.reset(new ReportCmdSampleTreeDisplayer(displayer));
675   return true;
676 }
677
678 bool ReportCommand::ReadEventAttrFromRecordFile() {
679   std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
680   for (const auto& attr_with_id : attrs) {
681     EventAttrWithName attr;
682     attr.attr = *attr_with_id.attr;
683     attr.name = GetEventNameByAttr(attr.attr);
684     event_attrs_.push_back(attr);
685   }
686   if (use_branch_address_) {
687     bool has_branch_stack = true;
688     for (const auto& attr : event_attrs_) {
689       if ((attr.attr.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) {
690         has_branch_stack = false;
691         break;
692       }
693     }
694     if (!has_branch_stack) {
695       LOG(ERROR) << record_filename_
696                  << " is not recorded with branch stack sampling option.";
697       return false;
698     }
699   }
700   return true;
701 }
702
703 bool ReportCommand::ReadFeaturesFromRecordFile() {
704   record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
705
706   std::string arch =
707       record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
708   if (!arch.empty()) {
709     record_file_arch_ = GetArchType(arch);
710     if (record_file_arch_ == ARCH_UNSUPPORTED) {
711       return false;
712     }
713   }
714
715   std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
716   if (!cmdline.empty()) {
717     record_cmdline_ = android::base::Join(cmdline, ' ');
718     // TODO: the code to detect system wide collection option is fragile, remove
719     // it once we can do cross unwinding.
720     for (size_t i = 0; i < cmdline.size(); i++) {
721       std::string& s = cmdline[i];
722       if (s == "-a") {
723         system_wide_collection_ = true;
724         break;
725       } else if (s == "--call-graph" || s == "--cpu" || s == "-e" ||
726                  s == "-f" || s == "-F" || s == "-j" || s == "-m" ||
727                  s == "-o" || s == "-p" || s == "-t") {
728         i++;
729       } else if (!s.empty() && s[0] != '-') {
730         break;
731       }
732     }
733   }
734   if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_TRACING_DATA)) {
735     std::vector<char> tracing_data;
736     if (!record_file_reader_->ReadFeatureSection(
737             PerfFileFormat::FEAT_TRACING_DATA, &tracing_data)) {
738       return false;
739     }
740     if (!ProcessTracingData(tracing_data)) {
741       return false;
742     }
743   }
744   return true;
745 }
746
747 bool ReportCommand::ReadSampleTreeFromRecordFile() {
748   sample_tree_builder_options_.use_branch_address = use_branch_address_;
749   // Normally do strict arch check when unwinding stack. But allow unwinding
750   // 32-bit processes on 64-bit devices for system wide profiling.
751   sample_tree_builder_options_.strict_unwind_arch_check = !system_wide_collection_;
752   sample_tree_builder_options_.accumulate_callchain = accumulate_callchain_;
753   sample_tree_builder_options_.build_callchain = print_callgraph_;
754   sample_tree_builder_options_.use_caller_as_callchain_root = !callgraph_show_callee_;
755
756   for (size_t i = 0; i < event_attrs_.size(); ++i) {
757     sample_tree_builder_.push_back(sample_tree_builder_options_.CreateSampleTreeBuilder());
758   }
759
760   if (!record_file_reader_->ReadDataSection(
761           [this](std::unique_ptr<Record> record) {
762             return ProcessRecord(std::move(record));
763           })) {
764     return false;
765   }
766   for (size_t i = 0; i < sample_tree_builder_.size(); ++i) {
767     sample_tree_.push_back(sample_tree_builder_[i]->GetSampleTree());
768     sample_tree_sorter_->Sort(sample_tree_.back().samples, print_callgraph_);
769   }
770   return true;
771 }
772
773 bool ReportCommand::ProcessRecord(std::unique_ptr<Record> record) {
774   thread_tree_.Update(*record);
775   if (record->type() == PERF_RECORD_SAMPLE) {
776     size_t attr_id = record_file_reader_->GetAttrIndexOfRecord(record.get());
777     sample_tree_builder_[attr_id]->ProcessSampleRecord(
778         *static_cast<const SampleRecord*>(record.get()));
779   } else if (record->type() == PERF_RECORD_TRACING_DATA) {
780     const auto& r = *static_cast<TracingDataRecord*>(record.get());
781     if (!ProcessTracingData(std::vector<char>(r.data, r.data + r.data_size))) {
782       return false;
783     }
784   }
785   return true;
786 }
787
788 bool ReportCommand::ProcessTracingData(const std::vector<char>& data) {
789   Tracing tracing(data);
790   for (auto& attr : event_attrs_) {
791     if (attr.attr.type == PERF_TYPE_TRACEPOINT) {
792       uint64_t trace_event_id = attr.attr.config;
793       attr.name = tracing.GetTracingEventNameHavingId(trace_event_id);
794     }
795   }
796   return true;
797 }
798
799 bool ReportCommand::PrintReport() {
800   std::unique_ptr<FILE, decltype(&fclose)> file_handler(nullptr, fclose);
801   FILE* report_fp = stdout;
802   if (!report_filename_.empty()) {
803     report_fp = fopen(report_filename_.c_str(), "w");
804     if (report_fp == nullptr) {
805       PLOG(ERROR) << "failed to open file " << report_filename_;
806       return false;
807     }
808     file_handler.reset(report_fp);
809   }
810   PrintReportContext(report_fp);
811   for (size_t i = 0; i < event_attrs_.size(); ++i) {
812     if (i != 0) {
813       fprintf(report_fp, "\n");
814     }
815     EventAttrWithName& attr = event_attrs_[i];
816     SampleTree& sample_tree = sample_tree_[i];
817     fprintf(report_fp, "Event: %s (type %u, config %llu)\n", attr.name.c_str(),
818             attr.attr.type, attr.attr.config);
819     fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree.total_samples);
820     if (sample_tree.total_error_callchains != 0) {
821       fprintf(report_fp, "Error Callchains: %" PRIu64 ", %f%%\n",
822               sample_tree.total_error_callchains,
823               sample_tree.total_error_callchains * 100.0 / sample_tree.total_samples);
824     }
825     fprintf(report_fp, "Event count: %" PRIu64 "\n\n", sample_tree.total_period);
826     sample_tree_displayer_->DisplaySamples(report_fp, sample_tree.samples, &sample_tree);
827   }
828   fflush(report_fp);
829   if (ferror(report_fp) != 0) {
830     PLOG(ERROR) << "print report failed";
831     return false;
832   }
833   return true;
834 }
835
836 void ReportCommand::PrintReportContext(FILE* report_fp) {
837   if (!record_cmdline_.empty()) {
838     fprintf(report_fp, "Cmdline: %s\n", record_cmdline_.c_str());
839   }
840   fprintf(report_fp, "Arch: %s\n", GetArchString(record_file_arch_).c_str());
841 }
842
843 }  // namespace
844
845 void RegisterReportCommand() {
846   RegisterCommand("report",
847                   [] { return std::unique_ptr<Command>(new ReportCommand()); });
848 }