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 46namespace { 47 48static std::set<std::string> branch_sort_keys = { 49 "dso_from", "dso_to", "symbol_from", "symbol_to", 50}; 51struct 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 61struct 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 98struct SampleTree { 99 std::vector<SampleEntry*> samples; 100 uint64_t total_samples; 101 uint64_t total_period; 102 uint64_t total_error_callchains; 103}; 104 105BUILD_COMPARE_VALUE_FUNCTION(CompareVaddrInFile, vaddr_in_file); 106BUILD_DISPLAY_HEX64_FUNCTION(DisplayVaddrInFile, vaddr_in_file); 107 108class 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 256struct 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 281using ReportCmdSampleTreeSorter = SampleTreeSorter<SampleEntry>; 282using ReportCmdSampleTreeDisplayer = 283 SampleTreeDisplayer<SampleEntry, SampleTree>; 284 285using ReportCmdCallgraphDisplayer = 286 CallgraphDisplayer<SampleEntry, CallChainNode<SampleEntry>>; 287 288class 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 298struct EventAttrWithName { 299 perf_event_attr attr; 300 std::string name; 301}; 302 303class 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 408bool 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 439bool 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 679bool 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 704bool 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 748bool 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 774bool 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 789bool 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 800bool 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 837void 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 846void RegisterReportCommand() { 847 RegisterCommand("report", 848 [] { return std::unique_ptr<Command>(new ReportCommand()); }); 849} 850