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