X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=simpleperf%2Fcmd_report.cpp;h=29cf743a7564fb82dee7b0f1bdc7e5dcc24af2e1;hb=0a0d208795ac89a1c78bb921ea82a5d6d7919c88;hp=fe93dbc3d0f75ceffbc625697e041214aeef9ddd;hpb=81f3edcdf139a0a1af9fe3699bfdda8fe8bc5426;p=android-x86%2Fsystem-extras.git diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp index fe93dbc3..29cf743a 100644 --- a/simpleperf/cmd_report.cpp +++ b/simpleperf/cmd_report.cpp @@ -24,14 +24,15 @@ #include #include +#include #include +#include #include #include #include #include "command.h" #include "dwarf_unwind.h" -#include "environment.h" #include "event_attr.h" #include "event_type.h" #include "perf_regs.h" @@ -88,12 +89,17 @@ struct SampleEntry { // The data member 'callchain' can only move, not copy. SampleEntry(SampleEntry&&) = default; SampleEntry(SampleEntry&) = delete; + + uint64_t GetPeriod() const { + return period; + } }; struct SampleTree { std::vector samples; uint64_t total_samples; uint64_t total_period; + uint64_t total_error_callchains; }; BUILD_COMPARE_VALUE_FUNCTION(CompareVaddrInFile, vaddr_in_file); @@ -107,7 +113,8 @@ class ReportCmdSampleTreeBuilder : SampleTreeBuilder(sample_comparator), thread_tree_(thread_tree), total_samples_(0), - total_period_(0) {} + total_period_(0), + total_error_callchains_(0) {} void SetFilters(const std::unordered_set& pid_filter, const std::unordered_set& tid_filter, @@ -121,11 +128,13 @@ class ReportCmdSampleTreeBuilder symbol_filter_ = symbol_filter; } - SampleTree GetSampleTree() const { + SampleTree GetSampleTree() { + AddCallChainDuplicateInfo(); SampleTree sample_tree; sample_tree.samples = GetSamples(); sample_tree.total_samples = total_samples_; sample_tree.total_period = total_period_; + sample_tree.total_error_callchains = total_error_callchains_; return sample_tree; } @@ -173,10 +182,16 @@ class ReportCmdSampleTreeBuilder const uint64_t& acc_info) override { const ThreadEntry* thread = sample->thread; const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel); + if (thread_tree_->IsUnknownDso(map->dso)) { + // The unwinders can give wrong ip addresses, which can't map to a valid dso. Skip them. + total_error_callchains_++; + return nullptr; + } uint64_t vaddr_in_file; const Symbol* symbol = thread_tree_->FindSymbol(map, ip, &vaddr_in_file); std::unique_ptr callchain_sample(new SampleEntry( sample->time, 0, acc_info, 0, thread, map, symbol, vaddr_in_file)); + callchain_sample->thread_comm = sample->thread_comm; return InsertCallChainSample(std::move(callchain_sample), callchain); } @@ -235,6 +250,32 @@ class ReportCmdSampleTreeBuilder uint64_t total_samples_; uint64_t total_period_; + uint64_t total_error_callchains_; +}; + +struct SampleTreeBuilderOptions { + SampleComparator comparator; + ThreadTree* thread_tree; + std::unordered_set comm_filter; + std::unordered_set dso_filter; + std::unordered_set symbol_filter; + std::unordered_set pid_filter; + std::unordered_set tid_filter; + bool use_branch_address; + bool accumulate_callchain; + bool build_callchain; + bool use_caller_as_callchain_root; + bool strict_unwind_arch_check; + + std::unique_ptr CreateSampleTreeBuilder() { + std::unique_ptr builder( + new ReportCmdSampleTreeBuilder(comparator, thread_tree)); + builder->SetFilters(pid_filter, tid_filter, comm_filter, dso_filter, symbol_filter); + builder->SetBranchSampleOption(use_branch_address); + builder->SetCallChainSampleOptions(accumulate_callchain, build_callchain, + use_caller_as_callchain_root, strict_unwind_arch_check); + return builder; + } }; using ReportCmdSampleTreeSorter = SampleTreeSorter; @@ -266,22 +307,29 @@ class ReportCommand : public Command { "report", "report sampling information in perf.data", // clang-format off "Usage: simpleperf report [options]\n" +"The default options are: -i perf.data --sort comm,pid,tid,dso,symbol.\n" "-b Use the branch-to addresses in sampled take branches instead of the\n" " instruction addresses. Only valid for perf.data recorded with -b/-j\n" " option.\n" "--children Print the overhead accumulated by appearing in the callchain.\n" "--comms comm1,comm2,... Report only for selected comms.\n" "--dsos dso1,dso2,... Report only for selected dsos.\n" +"--full-callgraph Print full call graph. Used with -g option. By default,\n" +" brief call graph is printed.\n" "-g [callee|caller] Print call graph. If callee mode is used, the graph\n" " shows how functions are called from others. Otherwise,\n" " the graph shows how functions call others.\n" " Default is caller mode.\n" "-i Specify path of record file, default is perf.data.\n" +"--kallsyms Set the file to read kernel symbols.\n" +"--max-stack Set max stack frames shown when printing call graph.\n" "-n Print the sample count for each item.\n" "--no-demangle Don't demangle symbol names.\n" "--no-show-ip Don't show vaddr in file for unknown symbols.\n" "-o report_file_name Set report file name, default is stdout.\n" +"--percent-limit Set min percentage shown when printing call graph.\n" "--pids pid1,pid2,... Report only for selected pids.\n" +"--raw-period Report period count instead of period percentage.\n" "--sort key1,key2,... Select keys used to sort and print the report. The\n" " appearance order of keys decides the order of keys used\n" " to sort and print the report.\n" @@ -313,7 +361,11 @@ class ReportCommand : public Command { system_wide_collection_(false), accumulate_callchain_(false), print_callgraph_(false), - callgraph_show_callee_(false) {} + callgraph_show_callee_(false), + callgraph_max_stack_(UINT32_MAX), + callgraph_percent_limit_(0), + raw_period_(false), + brief_callgraph_(true) {} bool Run(const std::vector& args); @@ -332,8 +384,11 @@ class ReportCommand : public Command { std::unique_ptr record_file_reader_; std::vector event_attrs_; ThreadTree thread_tree_; - SampleTree sample_tree_; - std::unique_ptr sample_tree_builder_; + // Create a SampleTreeBuilder and SampleTree for each event_attr. + std::vector sample_tree_; + SampleTreeBuilderOptions sample_tree_builder_options_; + std::vector> sample_tree_builder_; + std::unique_ptr sample_tree_sorter_; std::unique_ptr sample_tree_displayer_; bool use_branch_address_; @@ -342,6 +397,10 @@ class ReportCommand : public Command { bool accumulate_callchain_; bool print_callgraph_; bool callgraph_show_callee_; + uint32_t callgraph_max_stack_; + double callgraph_percent_limit_; + bool raw_period_; + bool brief_callgraph_; std::string report_filename_; }; @@ -384,11 +443,6 @@ bool ReportCommand::ParseOptions(const std::vector& args) { std::string vmlinux; bool print_sample_count = false; std::vector sort_keys = {"comm", "pid", "tid", "dso", "symbol"}; - std::unordered_set comm_filter; - std::unordered_set dso_filter; - std::unordered_set symbol_filter; - std::unordered_set pid_filter; - std::unordered_set tid_filter; for (size_t i = 0; i < args.size(); ++i) { if (args[i] == "-b") { @@ -397,13 +451,15 @@ bool ReportCommand::ParseOptions(const std::vector& args) { accumulate_callchain_ = true; } else if (args[i] == "--comms" || args[i] == "--dsos") { std::unordered_set& filter = - (args[i] == "--comms" ? comm_filter : dso_filter); + (args[i] == "--comms" ? sample_tree_builder_options_.comm_filter + : sample_tree_builder_options_.dso_filter); if (!NextArgumentOrError(args, &i)) { return false; } std::vector strs = android::base::Split(args[i], ","); filter.insert(strs.begin(), strs.end()); - + } else if (args[i] == "--full-callgraph") { + brief_callgraph_ = false; } else if (args[i] == "-g") { print_callgraph_ = true; accumulate_callchain_ = true; @@ -424,6 +480,24 @@ bool ReportCommand::ParseOptions(const std::vector& args) { } record_filename_ = args[i]; + } else if (args[i] == "--kallsyms") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + std::string kallsyms; + if (!android::base::ReadFileToString(args[i], &kallsyms)) { + LOG(ERROR) << "Can't read kernel symbols from " << args[i]; + return false; + } + Dso::SetKallsyms(kallsyms); + } else if (args[i] == "--max-stack") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + if (!android::base::ParseUint(args[i].c_str(), &callgraph_max_stack_)) { + LOG(ERROR) << "invalid arg for --max-stack: " << args[i]; + return false; + } } else if (args[i] == "-n") { print_sample_count = true; @@ -436,11 +510,19 @@ bool ReportCommand::ParseOptions(const std::vector& args) { return false; } report_filename_ = args[i]; - + } else if (args[i] == "--percent-limit") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + if (!android::base::ParseDouble(args[i].c_str(), + &callgraph_percent_limit_, 0.0)) { + LOG(ERROR) << "invalid arg for --percent-limit: " << args[i]; + } } else if (args[i] == "--pids" || args[i] == "--tids") { const std::string& option = args[i]; std::unordered_set& filter = - (option == "--pids" ? pid_filter : tid_filter); + (option == "--pids" ? sample_tree_builder_options_.pid_filter + : sample_tree_builder_options_.tid_filter); if (!NextArgumentOrError(args, &i)) { return false; } @@ -453,7 +535,8 @@ bool ReportCommand::ParseOptions(const std::vector& args) { } filter.insert(id); } - + } else if (args[i] == "--raw-period") { + raw_period_ = true; } else if (args[i] == "--sort") { if (!NextArgumentOrError(args, &i)) { return false; @@ -464,7 +547,7 @@ bool ReportCommand::ParseOptions(const std::vector& args) { return false; } std::vector strs = android::base::Split(args[i], ";"); - symbol_filter.insert(strs.begin(), strs.end()); + sample_tree_builder_options_.symbol_filter.insert(strs.begin(), strs.end()); } else if (args[i] == "--symfs") { if (!NextArgumentOrError(args, &i)) { return false; @@ -498,10 +581,19 @@ bool ReportCommand::ParseOptions(const std::vector& args) { SampleComparator comparator; if (accumulate_callchain_) { - displayer.AddDisplayFunction("Children", DisplayAccumulatedOverhead); - displayer.AddDisplayFunction("Self", DisplaySelfOverhead); + if (raw_period_) { + displayer.AddDisplayFunction("Children", DisplayAccumulatedPeriod); + displayer.AddDisplayFunction("Self", DisplaySelfPeriod); + } else { + displayer.AddDisplayFunction("Children", DisplayAccumulatedOverhead); + displayer.AddDisplayFunction("Self", DisplaySelfOverhead); + } } else { - displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead); + if (raw_period_) { + displayer.AddDisplayFunction("Overhead", DisplaySelfPeriod); + } else { + displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead); + } } if (print_sample_count) { displayer.AddDisplayFunction("Sample", DisplaySampleCount); @@ -563,18 +655,21 @@ bool ReportCommand::ParseOptions(const std::vector& args) { displayer.AddExclusiveDisplayFunction( ReportCmdCallgraphDisplayerWithVaddrInFile()); } else { - displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer()); + displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer( + callgraph_max_stack_, callgraph_percent_limit_, brief_callgraph_)); } } } - sample_tree_builder_.reset( - new ReportCmdSampleTreeBuilder(comparator, &thread_tree_)); - sample_tree_builder_->SetFilters(pid_filter, tid_filter, comm_filter, - dso_filter, symbol_filter); + sample_tree_builder_options_.comparator = comparator; + sample_tree_builder_options_.thread_tree = &thread_tree_; SampleComparator sort_comparator; sort_comparator.AddCompareFunction(CompareTotalPeriod); + if (print_callgraph_) { + sort_comparator.AddCompareFunction(CompareCallGraphDuplicated); + } + sort_comparator.AddCompareFunction(ComparePeriod); sort_comparator.AddComparator(comparator); sample_tree_sorter_.reset(new ReportCmdSampleTreeSorter(sort_comparator)); sample_tree_displayer_.reset(new ReportCmdSampleTreeDisplayer(displayer)); @@ -582,7 +677,7 @@ bool ReportCommand::ParseOptions(const std::vector& args) { } bool ReportCommand::ReadEventAttrFromRecordFile() { - std::vector attrs = record_file_reader_->AttrSection(); + std::vector attrs = record_file_reader_->AttrSection(); for (const auto& attr_with_id : attrs) { EventAttrWithName attr; attr.attr = *attr_with_id.attr; @@ -607,13 +702,7 @@ bool ReportCommand::ReadEventAttrFromRecordFile() { } bool ReportCommand::ReadFeaturesFromRecordFile() { - std::vector records = - record_file_reader_->ReadBuildIdFeature(); - std::vector> build_ids; - for (auto& r : records) { - build_ids.push_back(std::make_pair(r.filename, r.build_id)); - } - Dso::SetBuildIds(build_ids); + record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_); std::string arch = record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH); @@ -657,28 +746,36 @@ bool ReportCommand::ReadFeaturesFromRecordFile() { } bool ReportCommand::ReadSampleTreeFromRecordFile() { - sample_tree_builder_->SetBranchSampleOption(use_branch_address_); + sample_tree_builder_options_.use_branch_address = use_branch_address_; // Normally do strict arch check when unwinding stack. But allow unwinding // 32-bit processes on 64-bit devices for system wide profiling. - bool strict_unwind_arch_check = !system_wide_collection_; - sample_tree_builder_->SetCallChainSampleOptions( - accumulate_callchain_, print_callgraph_, !callgraph_show_callee_, - strict_unwind_arch_check); + sample_tree_builder_options_.strict_unwind_arch_check = !system_wide_collection_; + sample_tree_builder_options_.accumulate_callchain = accumulate_callchain_; + sample_tree_builder_options_.build_callchain = print_callgraph_; + sample_tree_builder_options_.use_caller_as_callchain_root = !callgraph_show_callee_; + + for (size_t i = 0; i < event_attrs_.size(); ++i) { + sample_tree_builder_.push_back(sample_tree_builder_options_.CreateSampleTreeBuilder()); + } + if (!record_file_reader_->ReadDataSection( [this](std::unique_ptr record) { return ProcessRecord(std::move(record)); })) { return false; } - sample_tree_ = sample_tree_builder_->GetSampleTree(); - sample_tree_sorter_->Sort(sample_tree_.samples, print_callgraph_); + for (size_t i = 0; i < sample_tree_builder_.size(); ++i) { + sample_tree_.push_back(sample_tree_builder_[i]->GetSampleTree()); + sample_tree_sorter_->Sort(sample_tree_.back().samples, print_callgraph_); + } return true; } bool ReportCommand::ProcessRecord(std::unique_ptr record) { thread_tree_.Update(*record); if (record->type() == PERF_RECORD_SAMPLE) { - sample_tree_builder_->ProcessSampleRecord( + size_t attr_id = record_file_reader_->GetAttrIndexOfRecord(record.get()); + sample_tree_builder_[attr_id]->ProcessSampleRecord( *static_cast(record.get())); } else if (record->type() == PERF_RECORD_TRACING_DATA) { const auto& r = *static_cast(record.get()); @@ -712,8 +809,23 @@ bool ReportCommand::PrintReport() { file_handler.reset(report_fp); } PrintReportContext(report_fp); - sample_tree_displayer_->DisplaySamples(report_fp, sample_tree_.samples, - &sample_tree_); + for (size_t i = 0; i < event_attrs_.size(); ++i) { + if (i != 0) { + fprintf(report_fp, "\n"); + } + EventAttrWithName& attr = event_attrs_[i]; + SampleTree& sample_tree = sample_tree_[i]; + fprintf(report_fp, "Event: %s (type %u, config %llu)\n", attr.name.c_str(), + attr.attr.type, attr.attr.config); + fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree.total_samples); + if (sample_tree.total_error_callchains != 0) { + fprintf(report_fp, "Error Callchains: %" PRIu64 ", %f%%\n", + sample_tree.total_error_callchains, + sample_tree.total_error_callchains * 100.0 / sample_tree.total_samples); + } + fprintf(report_fp, "Event count: %" PRIu64 "\n\n", sample_tree.total_period); + sample_tree_displayer_->DisplaySamples(report_fp, sample_tree.samples, &sample_tree); + } fflush(report_fp); if (ferror(report_fp) != 0) { PLOG(ERROR) << "print report failed"; @@ -727,12 +839,6 @@ void ReportCommand::PrintReportContext(FILE* report_fp) { fprintf(report_fp, "Cmdline: %s\n", record_cmdline_.c_str()); } fprintf(report_fp, "Arch: %s\n", GetArchString(record_file_arch_).c_str()); - for (const auto& attr : event_attrs_) { - fprintf(report_fp, "Event: %s (type %u, config %llu)\n", attr.name.c_str(), - attr.attr.type, attr.attr.config); - } - fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree_.total_samples); - fprintf(report_fp, "Event count: %" PRIu64 "\n\n", sample_tree_.total_period); } } // namespace