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