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 "event_attr.h" 36 #include "event_type.h" 37 #include "perf_regs.h" 38 #include "record.h" 39 #include "record_file.h" 40 #include "sample_tree.h" 41 #include "thread_tree.h" 42 #include "tracing.h" 43 #include "utils.h" 44 45 namespace { 46 47 static std::set<std::string> branch_sort_keys = { 48 "dso_from", "dso_to", "symbol_from", "symbol_to", 49 }; 50 struct BranchFromEntry { 51 const MapEntry* map; 52 const Symbol* symbol; 53 uint64_t vaddr_in_file; 54 uint64_t flags; 55 56 BranchFromEntry() 57 : map(nullptr), symbol(nullptr), vaddr_in_file(0), flags(0) {} 58 }; 59 60 struct SampleEntry { 61 uint64_t time; 62 uint64_t period; 63 // accumuated when appearing in other sample's callchain 64 uint64_t accumulated_period; 65 uint64_t sample_count; 66 const ThreadEntry* thread; 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 thread(thread), 83 thread_comm(thread->comm), 84 map(map), 85 symbol(symbol), 86 vaddr_in_file(vaddr_in_file) {} 87 88 // The data member 'callchain' can only move, not copy. 89 SampleEntry(SampleEntry&&) = default; 90 SampleEntry(SampleEntry&) = delete; 91 92 uint64_t GetPeriod() const { 93 return period; 94 } 95 }; 96 97 struct SampleTree { 98 std::vector<SampleEntry*> samples; 99 uint64_t total_samples; 100 uint64_t total_period; 101 uint64_t total_error_callchains; 102 }; 103 104 BUILD_COMPARE_VALUE_FUNCTION(CompareVaddrInFile, vaddr_in_file); 105 BUILD_DISPLAY_HEX64_FUNCTION(DisplayVaddrInFile, vaddr_in_file); 106 107 class ReportCmdSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, uint64_t> { 108 public: 109 ReportCmdSampleTreeBuilder(const SampleComparator<SampleEntry>& sample_comparator, 110 ThreadTree* thread_tree) 111 : SampleTreeBuilder(sample_comparator), 112 thread_tree_(thread_tree), 113 total_samples_(0), 114 total_period_(0), 115 total_error_callchains_(0) {} 116 117 void SetFilters(const std::unordered_set<int>& pid_filter, 118 const std::unordered_set<int>& tid_filter, 119 const std::unordered_set<std::string>& comm_filter, 120 const std::unordered_set<std::string>& dso_filter, 121 const std::unordered_set<std::string>& symbol_filter) { 122 pid_filter_ = pid_filter; 123 tid_filter_ = tid_filter; 124 comm_filter_ = comm_filter; 125 dso_filter_ = dso_filter; 126 symbol_filter_ = symbol_filter; 127 } 128 129 SampleTree GetSampleTree() { 130 AddCallChainDuplicateInfo(); 131 SampleTree sample_tree; 132 sample_tree.samples = GetSamples(); 133 sample_tree.total_samples = total_samples_; 134 sample_tree.total_period = total_period_; 135 sample_tree.total_error_callchains = total_error_callchains_; 136 return sample_tree; 137 } 138 139 virtual void ReportCmdProcessSampleRecord(std::shared_ptr<SampleRecord>& r) { 140 return ProcessSampleRecord(*r); 141 } 142 143 virtual void ReportCmdProcessSampleRecord(const SampleRecord& r) { 144 return ProcessSampleRecord(r); 145 } 146 147 protected: 148 virtual uint64_t GetPeriod(const SampleRecord& r) = 0; 149 150 SampleEntry* CreateSample(const SampleRecord& r, bool in_kernel, 151 uint64_t* acc_info) override { 152 const ThreadEntry* thread = 153 thread_tree_->FindThreadOrNew(r.tid_data.pid, r.tid_data.tid); 154 const MapEntry* map = 155 thread_tree_->FindMap(thread, r.ip_data.ip, in_kernel); 156 uint64_t vaddr_in_file; 157 const Symbol* symbol = 158 thread_tree_->FindSymbol(map, r.ip_data.ip, &vaddr_in_file); 159 uint64_t period = GetPeriod(r); 160 *acc_info = period; 161 return InsertSample(std::unique_ptr<SampleEntry>( 162 new SampleEntry(r.time_data.time, period, 0, 1, thread, map, symbol, vaddr_in_file))); 163 } 164 165 SampleEntry* CreateBranchSample(const SampleRecord& r, 166 const BranchStackItemType& item) override { 167 const ThreadEntry* thread = 168 thread_tree_->FindThreadOrNew(r.tid_data.pid, r.tid_data.tid); 169 const MapEntry* from_map = thread_tree_->FindMap(thread, item.from); 170 uint64_t from_vaddr_in_file; 171 const Symbol* from_symbol = 172 thread_tree_->FindSymbol(from_map, item.from, &from_vaddr_in_file); 173 const MapEntry* to_map = thread_tree_->FindMap(thread, item.to); 174 uint64_t to_vaddr_in_file; 175 const Symbol* to_symbol = 176 thread_tree_->FindSymbol(to_map, item.to, &to_vaddr_in_file); 177 std::unique_ptr<SampleEntry> sample( 178 new SampleEntry(r.time_data.time, r.period_data.period, 0, 1, thread, 179 to_map, to_symbol, to_vaddr_in_file)); 180 sample->branch_from.map = from_map; 181 sample->branch_from.symbol = from_symbol; 182 sample->branch_from.vaddr_in_file = from_vaddr_in_file; 183 sample->branch_from.flags = item.flags; 184 return InsertSample(std::move(sample)); 185 } 186 187 SampleEntry* CreateCallChainSample(const SampleEntry* sample, uint64_t ip, 188 bool in_kernel, 189 const std::vector<SampleEntry*>& callchain, 190 const uint64_t& acc_info) override { 191 const ThreadEntry* thread = sample->thread; 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 sample->thread; 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->thread->pid) == pid_filter_.end()) { 217 return false; 218 } 219 if (!tid_filter_.empty() && 220 tid_filter_.find(sample->thread->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 bool 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 std::unordered_map<std::string, std::string> meta_info_; 472 std::unique_ptr<ScopedEventTypes> scoped_event_types_; 473 }; 474 475 bool ReportCommand::Run(const std::vector<std::string>& args) { 476 // 1. Parse options. 477 if (!ParseOptions(args)) { 478 return false; 479 } 480 481 // 2. Read record file and build SampleTree. 482 record_file_reader_ = RecordFileReader::CreateInstance(record_filename_); 483 if (record_file_reader_ == nullptr) { 484 return false; 485 } 486 if (!ReadMetaInfoFromRecordFile()) { 487 return false; 488 } 489 if (!ReadEventAttrFromRecordFile()) { 490 return false; 491 } 492 // Read features first to prepare build ids used when building SampleTree. 493 if (!ReadFeaturesFromRecordFile()) { 494 return false; 495 } 496 ScopedCurrentArch scoped_arch(record_file_arch_); 497 if (!ReadSampleTreeFromRecordFile()) { 498 return false; 499 } 500 501 // 3. Show collected information. 502 if (!PrintReport()) { 503 return false; 504 } 505 506 return true; 507 } 508 509 bool ReportCommand::ParseOptions(const std::vector<std::string>& args) { 510 bool demangle = true; 511 bool show_ip_for_unknown_symbol = true; 512 std::string symfs_dir; 513 std::string vmlinux; 514 bool print_sample_count = false; 515 std::vector<std::string> sort_keys = {"comm", "pid", "tid", "dso", "symbol"}; 516 517 for (size_t i = 0; i < args.size(); ++i) { 518 if (args[i] == "-b") { 519 use_branch_address_ = true; 520 } else if (args[i] == "--children") { 521 accumulate_callchain_ = true; 522 } else if (args[i] == "--comms" || args[i] == "--dsos") { 523 std::unordered_set<std::string>& filter = 524 (args[i] == "--comms" ? sample_tree_builder_options_.comm_filter 525 : sample_tree_builder_options_.dso_filter); 526 if (!NextArgumentOrError(args, &i)) { 527 return false; 528 } 529 std::vector<std::string> strs = android::base::Split(args[i], ","); 530 filter.insert(strs.begin(), strs.end()); 531 } else if (args[i] == "--full-callgraph") { 532 brief_callgraph_ = false; 533 } else if (args[i] == "-g") { 534 print_callgraph_ = true; 535 accumulate_callchain_ = true; 536 if (i + 1 < args.size() && args[i + 1][0] != '-') { 537 ++i; 538 if (args[i] == "callee") { 539 callgraph_show_callee_ = true; 540 } else if (args[i] == "caller") { 541 callgraph_show_callee_ = false; 542 } else { 543 LOG(ERROR) << "Unknown argument with -g option: " << args[i]; 544 return false; 545 } 546 } 547 } else if (args[i] == "-i") { 548 if (!NextArgumentOrError(args, &i)) { 549 return false; 550 } 551 record_filename_ = args[i]; 552 553 } else if (args[i] == "--kallsyms") { 554 if (!NextArgumentOrError(args, &i)) { 555 return false; 556 } 557 std::string kallsyms; 558 if (!android::base::ReadFileToString(args[i], &kallsyms)) { 559 LOG(ERROR) << "Can't read kernel symbols from " << args[i]; 560 return false; 561 } 562 Dso::SetKallsyms(kallsyms); 563 } else if (args[i] == "--max-stack") { 564 if (!NextArgumentOrError(args, &i)) { 565 return false; 566 } 567 if (!android::base::ParseUint(args[i].c_str(), &callgraph_max_stack_)) { 568 LOG(ERROR) << "invalid arg for --max-stack: " << args[i]; 569 return false; 570 } 571 } else if (args[i] == "-n") { 572 print_sample_count = true; 573 574 } else if (args[i] == "--no-demangle") { 575 demangle = false; 576 } else if (args[i] == "--no-show-ip") { 577 show_ip_for_unknown_symbol = false; 578 } else if (args[i] == "-o") { 579 if (!NextArgumentOrError(args, &i)) { 580 return false; 581 } 582 report_filename_ = args[i]; 583 } else if (args[i] == "--percent-limit") { 584 if (!NextArgumentOrError(args, &i)) { 585 return false; 586 } 587 if (!android::base::ParseDouble(args[i].c_str(), 588 &callgraph_percent_limit_, 0.0)) { 589 LOG(ERROR) << "invalid arg for --percent-limit: " << args[i]; 590 } 591 } else if (args[i] == "--pids" || args[i] == "--tids") { 592 const std::string& option = args[i]; 593 std::unordered_set<int>& filter = 594 (option == "--pids" ? sample_tree_builder_options_.pid_filter 595 : sample_tree_builder_options_.tid_filter); 596 if (!NextArgumentOrError(args, &i)) { 597 return false; 598 } 599 std::vector<std::string> strs = android::base::Split(args[i], ","); 600 for (const auto& s : strs) { 601 int id; 602 if (!android::base::ParseInt(s.c_str(), &id, 0)) { 603 LOG(ERROR) << "invalid id in " << option << " option: " << s; 604 return false; 605 } 606 filter.insert(id); 607 } 608 } else if (args[i] == "--raw-period") { 609 raw_period_ = true; 610 } else if (args[i] == "--sort") { 611 if (!NextArgumentOrError(args, &i)) { 612 return false; 613 } 614 sort_keys = android::base::Split(args[i], ","); 615 } else if (args[i] == "--symbols") { 616 if (!NextArgumentOrError(args, &i)) { 617 return false; 618 } 619 std::vector<std::string> strs = android::base::Split(args[i], ";"); 620 sample_tree_builder_options_.symbol_filter.insert(strs.begin(), strs.end()); 621 } else if (args[i] == "--symfs") { 622 if (!NextArgumentOrError(args, &i)) { 623 return false; 624 } 625 symfs_dir = args[i]; 626 627 } else if (args[i] == "--vmlinux") { 628 if (!NextArgumentOrError(args, &i)) { 629 return false; 630 } 631 vmlinux = args[i]; 632 } else { 633 ReportUnknownOption(args, i); 634 return false; 635 } 636 } 637 638 Dso::SetDemangle(demangle); 639 if (!Dso::SetSymFsDir(symfs_dir)) { 640 return false; 641 } 642 if (!vmlinux.empty()) { 643 Dso::SetVmlinux(vmlinux); 644 } 645 646 if (show_ip_for_unknown_symbol) { 647 thread_tree_.ShowIpForUnknownSymbol(); 648 } 649 650 SampleDisplayer<SampleEntry, SampleTree> displayer; 651 SampleComparator<SampleEntry> comparator; 652 653 if (accumulate_callchain_) { 654 if (raw_period_) { 655 displayer.AddDisplayFunction("Children", DisplayAccumulatedPeriod); 656 displayer.AddDisplayFunction("Self", DisplaySelfPeriod); 657 } else { 658 displayer.AddDisplayFunction("Children", DisplayAccumulatedOverhead); 659 displayer.AddDisplayFunction("Self", DisplaySelfOverhead); 660 } 661 } else { 662 if (raw_period_) { 663 displayer.AddDisplayFunction("Overhead", DisplaySelfPeriod); 664 } else { 665 displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead); 666 } 667 } 668 if (print_sample_count) { 669 displayer.AddDisplayFunction("Sample", DisplaySampleCount); 670 } 671 672 for (auto& key : sort_keys) { 673 if (!use_branch_address_ && 674 branch_sort_keys.find(key) != branch_sort_keys.end()) { 675 LOG(ERROR) << "sort key '" << key << "' can only be used with -b option."; 676 return false; 677 } 678 if (key == "pid") { 679 comparator.AddCompareFunction(ComparePid); 680 displayer.AddDisplayFunction("Pid", DisplayPid); 681 } else if (key == "tid") { 682 comparator.AddCompareFunction(CompareTid); 683 displayer.AddDisplayFunction("Tid", DisplayTid); 684 } else if (key == "comm") { 685 comparator.AddCompareFunction(CompareComm); 686 displayer.AddDisplayFunction("Command", DisplayComm); 687 } else if (key == "dso") { 688 comparator.AddCompareFunction(CompareDso); 689 displayer.AddDisplayFunction("Shared Object", DisplayDso); 690 } else if (key == "symbol") { 691 comparator.AddCompareFunction(CompareSymbol); 692 displayer.AddDisplayFunction("Symbol", DisplaySymbol); 693 } else if (key == "vaddr_in_file") { 694 comparator.AddCompareFunction(CompareVaddrInFile); 695 displayer.AddDisplayFunction("VaddrInFile", DisplayVaddrInFile); 696 } else if (key == "dso_from") { 697 comparator.AddCompareFunction(CompareDsoFrom); 698 displayer.AddDisplayFunction("Source Shared Object", DisplayDsoFrom); 699 } else if (key == "dso_to") { 700 comparator.AddCompareFunction(CompareDso); 701 displayer.AddDisplayFunction("Target Shared Object", DisplayDso); 702 } else if (key == "symbol_from") { 703 comparator.AddCompareFunction(CompareSymbolFrom); 704 displayer.AddDisplayFunction("Source Symbol", DisplaySymbolFrom); 705 } else if (key == "symbol_to") { 706 comparator.AddCompareFunction(CompareSymbol); 707 displayer.AddDisplayFunction("Target Symbol", DisplaySymbol); 708 } else { 709 LOG(ERROR) << "Unknown sort key: " << key; 710 return false; 711 } 712 } 713 if (print_callgraph_) { 714 bool has_symbol_key = false; 715 bool has_vaddr_in_file_key = false; 716 for (const auto& key : sort_keys) { 717 if (key == "symbol") { 718 has_symbol_key = true; 719 } else if (key == "vaddr_in_file") { 720 has_vaddr_in_file_key = true; 721 } 722 } 723 if (has_symbol_key) { 724 if (has_vaddr_in_file_key) { 725 displayer.AddExclusiveDisplayFunction( 726 ReportCmdCallgraphDisplayerWithVaddrInFile()); 727 } else { 728 displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer( 729 callgraph_max_stack_, callgraph_percent_limit_, brief_callgraph_)); 730 } 731 } 732 } 733 734 sample_tree_builder_options_.comparator = comparator; 735 sample_tree_builder_options_.thread_tree = &thread_tree_; 736 737 SampleComparator<SampleEntry> sort_comparator; 738 sort_comparator.AddCompareFunction(CompareTotalPeriod); 739 if (print_callgraph_) { 740 sort_comparator.AddCompareFunction(CompareCallGraphDuplicated); 741 } 742 sort_comparator.AddCompareFunction(ComparePeriod); 743 sort_comparator.AddComparator(comparator); 744 sample_tree_sorter_.reset(new ReportCmdSampleTreeSorter(sort_comparator)); 745 sample_tree_displayer_.reset(new ReportCmdSampleTreeDisplayer(displayer)); 746 return true; 747 } 748 749 bool ReportCommand::ReadMetaInfoFromRecordFile() { 750 if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO)) { 751 if (!record_file_reader_->ReadMetaInfoFeature(&meta_info_)) { 752 return false; 753 } 754 auto it = meta_info_.find("system_wide_collection"); 755 if (it != meta_info_.end()) { 756 system_wide_collection_ = it->second == "true"; 757 } 758 it = meta_info_.find("trace_offcpu"); 759 if (it != meta_info_.end()) { 760 trace_offcpu_ = it->second == "true"; 761 } 762 it = meta_info_.find("event_type_info"); 763 if (it != meta_info_.end()) { 764 scoped_event_types_.reset(new ScopedEventTypes(it->second)); 765 } 766 } 767 return true; 768 } 769 770 bool ReportCommand::ReadEventAttrFromRecordFile() { 771 std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection(); 772 for (const auto& attr_with_id : attrs) { 773 EventAttrWithName attr; 774 attr.attr = *attr_with_id.attr; 775 attr.name = GetEventNameByAttr(attr.attr); 776 event_attrs_.push_back(attr); 777 } 778 if (use_branch_address_) { 779 bool has_branch_stack = true; 780 for (const auto& attr : event_attrs_) { 781 if ((attr.attr.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) { 782 has_branch_stack = false; 783 break; 784 } 785 } 786 if (!has_branch_stack) { 787 LOG(ERROR) << record_filename_ 788 << " is not recorded with branch stack sampling option."; 789 return false; 790 } 791 } 792 if (trace_offcpu_) { 793 size_t i; 794 for (i = 0; i < event_attrs_.size(); ++i) { 795 if (event_attrs_[i].name == "sched:sched_switch") { 796 break; 797 } 798 } 799 CHECK_NE(i, event_attrs_.size()); 800 sched_switch_attr_id_ = i; 801 } 802 return true; 803 } 804 805 bool ReportCommand::ReadFeaturesFromRecordFile() { 806 record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_); 807 808 std::string arch = 809 record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH); 810 if (!arch.empty()) { 811 record_file_arch_ = GetArchType(arch); 812 if (record_file_arch_ == ARCH_UNSUPPORTED) { 813 return false; 814 } 815 } 816 817 std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature(); 818 if (!cmdline.empty()) { 819 record_cmdline_ = android::base::Join(cmdline, ' '); 820 if (meta_info_.find("system_wide_collection") == meta_info_.end()) { 821 // TODO: the code to detect system wide collection option is fragile, remove 822 // it once we can do cross unwinding. 823 for (size_t i = 0; i < cmdline.size(); i++) { 824 std::string& s = cmdline[i]; 825 if (s == "-a") { 826 system_wide_collection_ = true; 827 break; 828 } else if (s == "--call-graph" || s == "--cpu" || s == "-e" || 829 s == "-f" || s == "-F" || s == "-j" || s == "-m" || 830 s == "-o" || s == "-p" || s == "-t") { 831 i++; 832 } else if (!s.empty() && s[0] != '-') { 833 break; 834 } 835 } 836 } 837 } 838 if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_TRACING_DATA)) { 839 std::vector<char> tracing_data; 840 if (!record_file_reader_->ReadFeatureSection( 841 PerfFileFormat::FEAT_TRACING_DATA, &tracing_data)) { 842 return false; 843 } 844 if (!ProcessTracingData(tracing_data)) { 845 return false; 846 } 847 } 848 return true; 849 } 850 851 bool ReportCommand::ReadSampleTreeFromRecordFile() { 852 sample_tree_builder_options_.use_branch_address = use_branch_address_; 853 sample_tree_builder_options_.accumulate_callchain = accumulate_callchain_; 854 sample_tree_builder_options_.build_callchain = print_callgraph_; 855 sample_tree_builder_options_.use_caller_as_callchain_root = !callgraph_show_callee_; 856 sample_tree_builder_options_.trace_offcpu = trace_offcpu_; 857 858 for (size_t i = 0; i < event_attrs_.size(); ++i) { 859 sample_tree_builder_.push_back(sample_tree_builder_options_.CreateSampleTreeBuilder()); 860 } 861 862 if (!record_file_reader_->ReadDataSection( 863 [this](std::unique_ptr<Record> record) { 864 return ProcessRecord(std::move(record)); 865 })) { 866 return false; 867 } 868 for (size_t i = 0; i < sample_tree_builder_.size(); ++i) { 869 sample_tree_.push_back(sample_tree_builder_[i]->GetSampleTree()); 870 sample_tree_sorter_->Sort(sample_tree_.back().samples, print_callgraph_); 871 } 872 return true; 873 } 874 875 bool ReportCommand::ProcessRecord(std::unique_ptr<Record> record) { 876 thread_tree_.Update(*record); 877 if (record->type() == PERF_RECORD_SAMPLE) { 878 size_t attr_id = record_file_reader_->GetAttrIndexOfRecord(record.get()); 879 if (!trace_offcpu_) { 880 sample_tree_builder_[attr_id]->ReportCmdProcessSampleRecord( 881 *static_cast<SampleRecord*>(record.get())); 882 } else { 883 ProcessSampleRecordInTraceOffCpuMode(std::move(record), attr_id); 884 } 885 } else if (record->type() == PERF_RECORD_TRACING_DATA) { 886 const auto& r = *static_cast<TracingDataRecord*>(record.get()); 887 if (!ProcessTracingData(std::vector<char>(r.data, r.data + r.data_size))) { 888 return false; 889 } 890 } 891 return true; 892 } 893 894 895 void ReportCommand::ProcessSampleRecordInTraceOffCpuMode(std::unique_ptr<Record> record, 896 size_t attr_id) { 897 std::shared_ptr<SampleRecord> r(static_cast<SampleRecord*>(record.release())); 898 if (attr_id == sched_switch_attr_id_) { 899 // If this sample belongs to sched_switch event, we should broadcast the offcpu info 900 // to other event types. 901 for (size_t i = 0; i < event_attrs_.size(); ++i) { 902 if (i == sched_switch_attr_id_) { 903 continue; 904 } 905 sample_tree_builder_[i]->ReportCmdProcessSampleRecord(r); 906 } 907 } else { 908 sample_tree_builder_[attr_id]->ReportCmdProcessSampleRecord(r); 909 } 910 } 911 912 bool ReportCommand::ProcessTracingData(const std::vector<char>& data) { 913 Tracing tracing(data); 914 for (auto& attr : event_attrs_) { 915 if (attr.attr.type == PERF_TYPE_TRACEPOINT) { 916 uint64_t trace_event_id = attr.attr.config; 917 attr.name = tracing.GetTracingEventNameHavingId(trace_event_id); 918 } 919 } 920 return true; 921 } 922 923 bool ReportCommand::PrintReport() { 924 std::unique_ptr<FILE, decltype(&fclose)> file_handler(nullptr, fclose); 925 FILE* report_fp = stdout; 926 if (!report_filename_.empty()) { 927 report_fp = fopen(report_filename_.c_str(), "w"); 928 if (report_fp == nullptr) { 929 PLOG(ERROR) << "failed to open file " << report_filename_; 930 return false; 931 } 932 file_handler.reset(report_fp); 933 } 934 PrintReportContext(report_fp); 935 for (size_t i = 0; i < event_attrs_.size(); ++i) { 936 if (trace_offcpu_ && i == sched_switch_attr_id_) { 937 continue; 938 } 939 if (i != 0) { 940 fprintf(report_fp, "\n"); 941 } 942 EventAttrWithName& attr = event_attrs_[i]; 943 SampleTree& sample_tree = sample_tree_[i]; 944 fprintf(report_fp, "Event: %s (type %u, config %llu)\n", attr.name.c_str(), 945 attr.attr.type, attr.attr.config); 946 fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree.total_samples); 947 if (sample_tree.total_error_callchains != 0) { 948 fprintf(report_fp, "Error Callchains: %" PRIu64 ", %f%%\n", 949 sample_tree.total_error_callchains, 950 sample_tree.total_error_callchains * 100.0 / sample_tree.total_samples); 951 } 952 const char* period_prefix = trace_offcpu_ ? "Time in ns" : "Event count"; 953 fprintf(report_fp, "%s: %" PRIu64 "\n\n", period_prefix, sample_tree.total_period); 954 sample_tree_displayer_->DisplaySamples(report_fp, sample_tree.samples, &sample_tree); 955 } 956 fflush(report_fp); 957 if (ferror(report_fp) != 0) { 958 PLOG(ERROR) << "print report failed"; 959 return false; 960 } 961 return true; 962 } 963 964 void ReportCommand::PrintReportContext(FILE* report_fp) { 965 if (!record_cmdline_.empty()) { 966 fprintf(report_fp, "Cmdline: %s\n", record_cmdline_.c_str()); 967 } 968 fprintf(report_fp, "Arch: %s\n", GetArchString(record_file_arch_).c_str()); 969 } 970 971 } // namespace 972 973 void RegisterReportCommand() { 974 RegisterCommand("report", 975 [] { return std::unique_ptr<Command>(new ReportCommand()); }); 976 } 977