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