summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2017-02-23 15:54:11 -0800
committerYabin Cui <yabinc@google.com>2017-02-23 15:55:21 -0800
commitada97db3d4336faac601663e8bb4b59835ae49a0 (patch)
treed7e036162f24d6991ffc158c04927bf197f7c7af
parentd9d23181768df567d166b79a89cfc0408e086509 (diff)
downloadextras-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.cpp95
-rw-r--r--simpleperf/cmd_report_test.cpp7
-rw-r--r--simpleperf/record.cpp4
-rw-r--r--simpleperf/record.h2
-rw-r--r--simpleperf/record_file.h2
-rw-r--r--simpleperf/record_file_reader.cpp4
-rw-r--r--simpleperf/report_lib_interface.cpp2
-rw-r--r--simpleperf/scripts/report.py70
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):