OSDN Git Service

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