diff options
author | Yabin Cui <yabinc@google.com> | 2017-02-23 15:54:11 -0800 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2017-02-23 15:55:21 -0800 |
commit | ada97db3d4336faac601663e8bb4b59835ae49a0 (patch) | |
tree | d7e036162f24d6991ffc158c04927bf197f7c7af | |
parent | d9d23181768df567d166b79a89cfc0408e086509 (diff) | |
download | extras-ada97db3d4336faac601663e8bb4b59835ae49a0.tar.gz |
simpleperf: generate one report for each event attr.
Bug: http://b/35475170
Test: run simpleperf_unit_test.
Test: run report.py.
Change-Id: Ie9329a64c701bce38f7b440c16cb47e99e83db45
-rw-r--r-- | simpleperf/cmd_report.cpp | 95 | ||||
-rw-r--r-- | simpleperf/cmd_report_test.cpp | 7 | ||||
-rw-r--r-- | simpleperf/record.cpp | 4 | ||||
-rw-r--r-- | simpleperf/record.h | 2 | ||||
-rw-r--r-- | simpleperf/record_file.h | 2 | ||||
-rw-r--r-- | simpleperf/record_file_reader.cpp | 4 | ||||
-rw-r--r-- | simpleperf/report_lib_interface.cpp | 2 | ||||
-rw-r--r-- | simpleperf/scripts/report.py | 70 |
8 files changed, 128 insertions, 58 deletions
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp index 3b727a5e..0aa7b8ad 100644 --- a/simpleperf/cmd_report.cpp +++ b/simpleperf/cmd_report.cpp @@ -243,6 +243,31 @@ class ReportCmdSampleTreeBuilder uint64_t total_period_; }; +struct SampleTreeBuilderOptions { + SampleComparator<SampleEntry> comparator; + ThreadTree* thread_tree; + std::unordered_set<std::string> comm_filter; + std::unordered_set<std::string> dso_filter; + std::unordered_set<std::string> symbol_filter; + std::unordered_set<int> pid_filter; + std::unordered_set<int> 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<ReportCmdSampleTreeBuilder> CreateSampleTreeBuilder() { + std::unique_ptr<ReportCmdSampleTreeBuilder> 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<SampleEntry>; using ReportCmdSampleTreeDisplayer = SampleTreeDisplayer<SampleEntry, SampleTree>; @@ -343,8 +368,11 @@ class ReportCommand : public Command { std::unique_ptr<RecordFileReader> record_file_reader_; std::vector<EventAttrWithName> event_attrs_; ThreadTree thread_tree_; - SampleTree sample_tree_; - std::unique_ptr<ReportCmdSampleTreeBuilder> sample_tree_builder_; + // Create a SampleTreeBuilder and SampleTree for each event_attr. + std::vector<SampleTree> sample_tree_; + SampleTreeBuilderOptions sample_tree_builder_options_; + std::vector<std::unique_ptr<ReportCmdSampleTreeBuilder>> sample_tree_builder_; + std::unique_ptr<ReportCmdSampleTreeSorter> sample_tree_sorter_; std::unique_ptr<ReportCmdSampleTreeDisplayer> sample_tree_displayer_; bool use_branch_address_; @@ -397,11 +425,6 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) { std::string vmlinux; bool print_sample_count = false; std::vector<std::string> sort_keys = {"comm", "pid", "tid", "dso", "symbol"}; - std::unordered_set<std::string> comm_filter; - std::unordered_set<std::string> dso_filter; - std::unordered_set<std::string> symbol_filter; - std::unordered_set<int> pid_filter; - std::unordered_set<int> tid_filter; for (size_t i = 0; i < args.size(); ++i) { if (args[i] == "-b") { @@ -410,7 +433,8 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) { accumulate_callchain_ = true; } else if (args[i] == "--comms" || args[i] == "--dsos") { std::unordered_set<std::string>& 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; } @@ -478,7 +502,8 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) { } else if (args[i] == "--pids" || args[i] == "--tids") { const std::string& option = args[i]; std::unordered_set<int>& 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; } @@ -502,7 +527,7 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) { return false; } std::vector<std::string> 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; @@ -607,10 +632,8 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) { } } - 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<SampleEntry> sort_comparator; sort_comparator.AddCompareFunction(CompareTotalPeriod); @@ -690,28 +713,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> 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> 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<const SampleRecord*>(record.get())); } else if (record->type() == PERF_RECORD_TRACING_DATA) { const auto& r = *static_cast<TracingDataRecord*>(record.get()); @@ -745,8 +776,18 @@ 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); + 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"; @@ -760,12 +801,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 diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp index 9a5c23bd..e7437d5a 100644 --- a/simpleperf/cmd_report_test.cpp +++ b/simpleperf/cmd_report_test.cpp @@ -324,8 +324,11 @@ TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) { TEST_F(ReportCommandTest, report_more_than_one_event_types) { Report(PERF_DATA_WITH_TWO_EVENT_TYPES); ASSERT_TRUE(success); - ASSERT_NE(content.find("cpu-cycles"), std::string::npos); - ASSERT_NE(content.find("cpu-clock"), std::string::npos); + size_t pos = 0; + ASSERT_NE(pos = content.find("cpu-cycles", pos), std::string::npos); + ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos); + ASSERT_NE(pos = content.find("cpu-clock", pos), std::string::npos); + ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos); } TEST_F(ReportCommandTest, report_kernel_symbol) { diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp index ba04daff..65e98ea6 100644 --- a/simpleperf/record.cpp +++ b/simpleperf/record.cpp @@ -187,6 +187,7 @@ void Record::Dump(size_t indent) const { uint64_t Record::Timestamp() const { return sample_id.time_data.time; } uint32_t Record::Cpu() const { return sample_id.cpu_data.cpu; } +uint64_t Record::Id() const { return sample_id.id_data.id; } void Record::UpdateBinary(const char* new_binary) { if (own_binary_) { @@ -380,6 +381,8 @@ SampleRecord::SampleRecord(const perf_event_attr& attr, const char* p) p += header_size(); sample_type = attr.sample_type; + // Set a default id value to report correctly even if ID is not recorded. + id_data.id = 0; if (sample_type & PERF_SAMPLE_IDENTIFIER) { MoveFromBinaryFormat(id_data, p); } @@ -670,6 +673,7 @@ void SampleRecord::DumpData(size_t indent) const { uint64_t SampleRecord::Timestamp() const { return time_data.time; } uint32_t SampleRecord::Cpu() const { return cpu_data.cpu; } +uint64_t SampleRecord::Id() const { return id_data.id; } BuildIdRecord::BuildIdRecord(const char* p) : Record(p) { const char* end = p + size(); diff --git a/simpleperf/record.h b/simpleperf/record.h index 4be46d9f..d6ee2ce5 100644 --- a/simpleperf/record.h +++ b/simpleperf/record.h @@ -247,6 +247,7 @@ struct Record { virtual uint64_t Timestamp() const; virtual uint32_t Cpu() const; + virtual uint64_t Id() const; protected: void UpdateBinary(const char* new_binary); @@ -388,6 +389,7 @@ struct SampleRecord : public Record { void ReplaceRegAndStackWithCallChain(const std::vector<uint64_t>& ips); uint64_t Timestamp() const override; uint32_t Cpu() const override; + uint64_t Id() const override; uint64_t GetValidStackSize() const { // If stack_user_data.dyn_size == 0, it may be because the kernel misses diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h index a2a5b5b1..51d4f437 100644 --- a/simpleperf/record_file.h +++ b/simpleperf/record_file.h @@ -134,7 +134,7 @@ class RecordFileReader { // Otherwise return false. bool ReadRecord(std::unique_ptr<Record>& record, bool sorted = true); - size_t GetAttrIndexOfRecord(const SampleRecord& record); + size_t GetAttrIndexOfRecord(const Record* record); std::vector<std::string> ReadCmdlineFeature(); std::vector<BuildIdRecord> ReadBuildIdFeature(); diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp index 06a2644d..0fdb6ddc 100644 --- a/simpleperf/record_file_reader.cpp +++ b/simpleperf/record_file_reader.cpp @@ -299,8 +299,8 @@ void RecordFileReader::ProcessEventIdRecord(const EventIdRecord& r) { } } -size_t RecordFileReader::GetAttrIndexOfRecord(const SampleRecord& record) { - auto it = event_id_to_attr_map_.find(record.id_data.id); +size_t RecordFileReader::GetAttrIndexOfRecord(const Record* record) { + auto it = event_id_to_attr_map_.find(record->Id()); if (it != event_id_to_attr_map_.end()) { return it->second; } diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp index abd09628..cc793074 100644 --- a/simpleperf/report_lib_interface.cpp +++ b/simpleperf/report_lib_interface.cpp @@ -231,7 +231,7 @@ Event* ReportLib::GetEventOfCurrentSample() { } } size_t attr_index = - record_file_reader_->GetAttrIndexOfRecord(*current_record_); + record_file_reader_->GetAttrIndexOfRecord(current_record_.get()); current_event_.name = event_attrs_[attr_index].name.c_str(); update_flag_ |= UPDATE_FLAG_OF_EVENT; } diff --git a/simpleperf/scripts/report.py b/simpleperf/scripts/report.py index b58ac6bc..6bba6771 100644 --- a/simpleperf/scripts/report.py +++ b/simpleperf/scripts/report.py @@ -82,20 +82,53 @@ class ReportItem(object): strs.append('%s' % self.call_tree) return '\n'.join(strs) +class EventReport(object): -def parse_report_items(lines): - report_items = [] + """Representing report for one event attr.""" + + def __init__(self, common_report_context): + self.context = common_report_context[:] + self.title_line = None + self.report_items = [] + + +def parse_event_reports(lines): + # Parse common report context + common_report_context = [] + line_id = 0 + while line_id < len(lines): + line = lines[line_id] + if not line or line.find('Event:') == 0: + break + common_report_context.append(line) + line_id += 1 + + event_reports = [] + in_report_context = True + cur_event_report = EventReport(common_report_context) cur_report_item = None call_tree_stack = {} vertical_columns = [] last_node = None - for line in lines: + for line in lines[line_id:]: if not line: + in_report_context = not in_report_context + if in_report_context: + cur_event_report = EventReport(common_report_context) + continue + + if in_report_context: + cur_event_report.context.append(line) + if line.find('Event:') == 0: + event_reports.append(cur_event_report) continue - if not line[0].isspace(): + + if cur_event_report.title_line is None: + cur_event_report.title_line = line + elif not line[0].isspace(): cur_report_item = ReportItem(line) - report_items.append(cur_report_item) + cur_event_report.report_items.append(cur_report_item) # Each report item can have different column depths. vertical_columns = [] else: @@ -135,7 +168,7 @@ def parse_report_items(lines): call_tree_stack[depth] = node last_node = node - return report_items + return event_reports class ReportWindow(object): @@ -224,22 +257,15 @@ def display_report_file(report_file): fh.close() lines = [x.rstrip() for x in lines] - - blank_line_index = -1 - for i in range(len(lines)): - if not lines[i]: - blank_line_index = i - break - assert blank_line_index != -1 - assert blank_line_index + 1 < len(lines) - - report_context = lines[:blank_line_index] - title_line = lines[blank_line_index + 1] - report_items = parse_report_items(lines[blank_line_index + 2:]) - - root = Tk() - ReportWindow(root, report_context, title_line, report_items) - root.mainloop() + event_reports = parse_event_reports(lines) + + if event_reports: + root = Tk() + for i in range(len(event_reports)): + report = event_reports[i] + parent = root if i == 0 else Toplevel(root) + ReportWindow(parent, report.context, report.title_line, report.report_items) + root.mainloop() def call_simpleperf_report(args, report_file): |