diff options
author | Yabin Cui <yabinc@google.com> | 2016-04-06 01:15:22 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2016-04-06 01:15:22 +0000 |
commit | 0d47fe0430a0d6edc517486478f50b1fb2987106 (patch) | |
tree | f135bfaf77d12693399c6617cbcf61470944ccde | |
parent | 05e61bec49233712091be9d40ee46962170a118d (diff) | |
parent | 2d6efe4b167da4e6b77f168b1820239ee65599e2 (diff) | |
download | extras-0d47fe0430a0d6edc517486478f50b1fb2987106.tar.gz |
Merge "simpleperf: support reporting more than one event type."android-n-preview-2
-rw-r--r-- | simpleperf/cmd_record.cpp | 73 | ||||
-rw-r--r-- | simpleperf/cmd_report.cpp | 46 | ||||
-rw-r--r-- | simpleperf/cmd_report_test.cpp | 7 | ||||
-rw-r--r-- | simpleperf/event_attr.cpp | 87 | ||||
-rw-r--r-- | simpleperf/event_attr.h | 6 | ||||
-rw-r--r-- | simpleperf/event_fd.cpp | 2 | ||||
-rw-r--r-- | simpleperf/event_fd.h | 2 | ||||
-rw-r--r-- | simpleperf/event_selection_set.cpp | 74 | ||||
-rw-r--r-- | simpleperf/event_selection_set.h | 14 | ||||
-rw-r--r-- | simpleperf/get_test_data.h | 3 | ||||
-rw-r--r-- | simpleperf/record.cpp | 58 | ||||
-rw-r--r-- | simpleperf/record.h | 16 | ||||
-rw-r--r-- | simpleperf/record_file.h | 6 | ||||
-rw-r--r-- | simpleperf/record_file_reader.cpp | 66 | ||||
-rw-r--r-- | simpleperf/record_file_test.cpp | 6 | ||||
-rw-r--r-- | simpleperf/record_test.cpp | 27 | ||||
-rw-r--r-- | simpleperf/testdata/perf_with_two_event_types.data | bin | 0 -> 23168 bytes |
17 files changed, 330 insertions, 163 deletions
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 1cea1734..85a28d46 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -135,17 +135,15 @@ class RecordCommand : public Command { bool Run(const std::vector<std::string>& args); - static bool ReadMmapDataCallback(const char* data, size_t size); - private: bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args); bool AddMeasuredEventType(const std::string& event_type_name); bool SetEventSelection(); bool CreateAndInitRecordFile(); std::unique_ptr<RecordFileWriter> CreateRecordFile(const std::string& filename); - bool DumpKernelAndModuleMmaps(); - bool DumpThreadCommAndMmaps(bool all_threads, const std::vector<pid_t>& selected_threads); - bool CollectRecordsFromKernel(const char* data, size_t size); + bool DumpKernelAndModuleMmaps(const perf_event_attr* attr, uint64_t event_id); + bool DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t event_id, + bool all_threads, const std::vector<pid_t>& selected_threads); bool ProcessRecord(Record* record); void UpdateRecordForEmbeddedElfPath(Record* record); void UnwindRecord(Record* record); @@ -175,7 +173,6 @@ class RecordCommand : public Command { // mmap pages used by each perf event file, should be a power of 2. size_t perf_mmap_pages_; - std::unique_ptr<RecordCache> record_cache_; ThreadTree thread_tree_; std::string record_filename_; std::unique_ptr<RecordFileWriter> record_file_writer_; @@ -236,7 +233,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) { return false; } std::vector<pollfd> pollfds; - event_selection_set_.PreparePollForEventFiles(&pollfds); + event_selection_set_.PrepareToPollForEventFiles(&pollfds); // 4. Create perf.data. if (!CreateAndInitRecordFile()) { @@ -247,12 +244,10 @@ bool RecordCommand::Run(const std::vector<std::string>& args) { if (workload != nullptr && !workload->Start()) { return false; } - record_cache_.reset( - new RecordCache(*event_selection_set_.FindEventAttrByType(measured_event_types_[0]))); - auto callback = std::bind(&RecordCommand::CollectRecordsFromKernel, this, std::placeholders::_1, - std::placeholders::_2); + auto callback = std::bind(&RecordCommand::ProcessRecord, this, std::placeholders::_1); + event_selection_set_.PrepareToReadMmapEventData(callback); while (true) { - if (!event_selection_set_.ReadMmapEventData(callback)) { + if (!event_selection_set_.ReadMmapEventData()) { return false; } if (signaled) { @@ -260,12 +255,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) { } poll(&pollfds[0], pollfds.size(), -1); } - std::vector<std::unique_ptr<Record>> records = record_cache_->PopAll(); - for (auto& r : records) { - if (!ProcessRecord(r.get())) { - return false; - } - } + event_selection_set_.FinishReadMmapEventData(); // 6. Dump additional features, and close record file. if (!DumpAdditionalFeatures(args)) { @@ -491,10 +481,15 @@ bool RecordCommand::CreateAndInitRecordFile() { if (record_file_writer_ == nullptr) { return false; } - if (!DumpKernelAndModuleMmaps()) { + // Use first perf_event_attr and first event id to dump mmap and comm records. + const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]); + const std::vector<std::unique_ptr<EventFd>>* fds = + event_selection_set_.FindEventFdsByType(measured_event_types_[0]); + uint64_t event_id = (*fds)[0]->Id(); + if (!DumpKernelAndModuleMmaps(attr, event_id)) { return false; } - if (!DumpThreadCommAndMmaps(system_wide_collection_, monitored_threads_)) { + if (!DumpThreadCommAndMmaps(attr, event_id, system_wide_collection_, monitored_threads_)) { return false; } return true; @@ -525,21 +520,19 @@ std::unique_ptr<RecordFileWriter> RecordCommand::CreateRecordFile(const std::str return writer; } -bool RecordCommand::DumpKernelAndModuleMmaps() { +bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr* attr, uint64_t event_id) { KernelMmap kernel_mmap; std::vector<KernelMmap> module_mmaps; GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps); - const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]); - CHECK(attr != nullptr); MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, kernel_mmap.start_addr, - kernel_mmap.len, 0, kernel_mmap.filepath); + kernel_mmap.len, 0, kernel_mmap.filepath, event_id); if (!ProcessRecord(&mmap_record)) { return false; } for (auto& module_mmap : module_mmaps) { MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, module_mmap.start_addr, - module_mmap.len, 0, module_mmap.filepath); + module_mmap.len, 0, module_mmap.filepath, event_id); if (!ProcessRecord(&mmap_record)) { return false; } @@ -547,7 +540,8 @@ bool RecordCommand::DumpKernelAndModuleMmaps() { return true; } -bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads, +bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr* attr, uint64_t event_id, + bool all_threads, const std::vector<pid_t>& selected_threads) { std::vector<ThreadComm> thread_comms; if (!GetThreadComms(&thread_comms)) { @@ -565,9 +559,6 @@ bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads, } } - const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]); - CHECK(attr != nullptr); - // Dump processes. for (auto& thread : thread_comms) { if (thread.pid != thread.tid) { @@ -576,7 +567,7 @@ bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads, if (!all_threads && dump_processes.find(thread.pid) == dump_processes.end()) { continue; } - CommRecord record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm); + CommRecord record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm, event_id); if (!ProcessRecord(&record)) { return false; } @@ -591,7 +582,7 @@ bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads, } MmapRecord record = CreateMmapRecord(*attr, false, thread.pid, thread.tid, thread_mmap.start_addr, - thread_mmap.len, thread_mmap.pgoff, thread_mmap.name); + thread_mmap.len, thread_mmap.pgoff, thread_mmap.name, event_id); if (!ProcessRecord(&record)) { return false; } @@ -606,11 +597,13 @@ bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads, if (!all_threads && dump_threads.find(thread.tid) == dump_threads.end()) { continue; } - ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid, thread.pid, thread.pid); + ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid, thread.pid, + thread.pid, event_id); if (!ProcessRecord(&fork_record)) { return false; } - CommRecord comm_record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm); + CommRecord comm_record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm, + event_id); if (!ProcessRecord(&comm_record)) { return false; } @@ -618,20 +611,6 @@ bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads, return true; } -bool RecordCommand::CollectRecordsFromKernel(const char* data, size_t size) { - record_cache_->Push(data, size); - while (true) { - std::unique_ptr<Record> r = record_cache_->Pop(); - if (r == nullptr) { - break; - } - if (!ProcessRecord(r.get())) { - return false; - } - } - return true; -} - bool RecordCommand::ProcessRecord(Record* record) { UpdateRecordForEmbeddedElfPath(record); BuildThreadTree(*record, &thread_tree_); diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp index b9c3d6f7..db7f8a35 100644 --- a/simpleperf/cmd_report.cpp +++ b/simpleperf/cmd_report.cpp @@ -306,7 +306,7 @@ class ReportCommand : public Command { std::string record_filename_; ArchType record_file_arch_; std::unique_ptr<RecordFileReader> record_file_reader_; - perf_event_attr event_attr_; + std::vector<perf_event_attr> event_attrs_; std::vector<std::unique_ptr<Displayable>> displayable_items_; std::vector<Comparable*> comparable_items_; ThreadTree thread_tree_; @@ -515,15 +515,22 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) { } bool ReportCommand::ReadEventAttrFromRecordFile() { - const std::vector<PerfFileFormat::FileAttr>& attrs = record_file_reader_->AttrSection(); - if (attrs.size() != 1) { - LOG(ERROR) << "record file contains " << attrs.size() << " attrs"; - return false; - } - event_attr_ = attrs[0].attr; - if (use_branch_address_ && (event_attr_.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) { - LOG(ERROR) << record_filename_ << " is not recorded with branch stack sampling option."; - return false; + const std::vector<PerfFileFormat::FileAttr>& file_attrs = record_file_reader_->AttrSection(); + for (const auto& attr : file_attrs) { + event_attrs_.push_back(attr.attr); + } + if (use_branch_address_) { + bool has_branch_stack = true; + for (const auto& attr : event_attrs_) { + if ((attr.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) { + has_branch_stack = false; + break; + } + } + if (!has_branch_stack) { + LOG(ERROR) << record_filename_ << " is not recorded with branch stack sampling option."; + return false; + } } return true; } @@ -706,19 +713,18 @@ bool ReportCommand::PrintReport() { } void ReportCommand::PrintReportContext() { - const EventType* event_type = FindEventTypeByConfig(event_attr_.type, event_attr_.config); - std::string event_type_name; - if (event_type != nullptr) { - event_type_name = event_type->name; - } else { - event_type_name = - android::base::StringPrintf("(type %u, config %llu)", event_attr_.type, event_attr_.config); - } if (!record_cmdline_.empty()) { fprintf(report_fp_, "Cmdline: %s\n", record_cmdline_.c_str()); } - fprintf(report_fp_, "Samples: %" PRIu64 " of event '%s'\n", sample_tree_->TotalSamples(), - event_type_name.c_str()); + for (const auto& attr : event_attrs_) { + const EventType* event_type = FindEventTypeByConfig(attr.type, attr.config); + std::string name; + if (event_type != nullptr) { + name = event_type->name; + } + fprintf(report_fp_, "Event: %s (type %u, config %llu)\n", name.c_str(), attr.type, attr.config); + } + fprintf(report_fp_, "Samples: %" PRIu64 "\n", sample_tree_->TotalSamples()); fprintf(report_fp_, "Event count: %" PRIu64 "\n\n", sample_tree_->TotalPeriod()); } diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp index a4bd6f39..4c2b4978 100644 --- a/simpleperf/cmd_report_test.cpp +++ b/simpleperf/cmd_report_test.cpp @@ -267,6 +267,13 @@ TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) { ASSERT_NE(content.find("Func2"), std::string::npos); } +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); +} + #if defined(__linux__) static std::unique_ptr<Command> RecordCmd() { diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp index c9449b14..92226335 100644 --- a/simpleperf/event_attr.cpp +++ b/simpleperf/event_attr.cpp @@ -87,8 +87,8 @@ perf_event_attr CreateDefaultPerfEventAttr(const EventType& event_type) { // PerfCounter in event_fd.h. attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; - attr.sample_type |= - PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_PERIOD | PERF_SAMPLE_CPU; + attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_PERIOD | + PERF_SAMPLE_CPU | PERF_SAMPLE_ID; if (attr.type == PERF_TYPE_TRACEPOINT) { attr.sample_freq = 0; @@ -145,3 +145,86 @@ void DumpPerfEventAttr(const perf_event_attr& attr, size_t indent) { PrintIndented(indent + 1, "sample_regs_user 0x%" PRIx64 "\n", attr.sample_regs_user); PrintIndented(indent + 1, "sample_stack_user 0x%" PRIx64 "\n", attr.sample_stack_user); } + +bool GetCommonEventIdPositionsForAttrs(std::vector<perf_event_attr>& attrs, + size_t* event_id_pos_in_sample_records, + size_t* event_id_reverse_pos_in_non_sample_records) { + // When there are more than one perf_event_attrs, we need to read event id + // in each record to decide current record should use which attr. So + // we need to determine the event id position in a record here. + std::vector<uint64_t> sample_types; + for (const auto& attr : attrs) { + sample_types.push_back(attr.sample_type); + } + // First determine event_id_pos_in_sample_records. + // If PERF_SAMPLE_IDENTIFIER is enabled, it is just after perf_event_header. + // If PERF_SAMPLE_ID is enabled, then PERF_SAMPLE_IDENTIFIER | IP | TID | TIME | ADDR + // should also be the same. + bool identifier_enabled = true; + bool id_enabled = true; + uint64_t flags_before_id_mask = PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP | PERF_SAMPLE_TID | + PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR; + uint64_t flags_before_id = sample_types[0] & flags_before_id_mask; + bool flags_before_id_are_the_same = true; + for (auto type : sample_types) { + identifier_enabled &= (type & PERF_SAMPLE_IDENTIFIER) != 0; + id_enabled &= (type & PERF_SAMPLE_ID) != 0; + flags_before_id_are_the_same &= (type & flags_before_id_mask) == flags_before_id; + } + if (identifier_enabled) { + *event_id_pos_in_sample_records = sizeof(perf_event_header); + } else if (id_enabled && flags_before_id_are_the_same) { + uint64_t pos = sizeof(perf_event_header); + while (flags_before_id != 0) { + // Each flags takes 8 bytes in sample records. + flags_before_id &= flags_before_id - 1; + pos += 8; + } + *event_id_pos_in_sample_records = pos; + } else { + LOG(ERROR) << "perf_event_attrs don't have a common event id position in sample records"; + return false; + } + + // Secondly determine event_id_reverse_pos_in_non_sample_record. + // If sample_id_all is not enabled, there is no event id in non sample records. + // If PERF_SAMPLE_IDENTIFIER is enabled, it is at the last 8 bytes of the record. + // If PERF_SAMPLE_ID is enabled, then PERF_SAMPLE_IDENTIFIER | CPU | STREAM_ID should + // also be the same. + bool sample_id_all_enabled = true; + for (const auto& attr : attrs) { + if (attr.sample_id_all == 0) { + sample_id_all_enabled = false; + } + } + if (!sample_id_all_enabled) { + LOG(ERROR) << "there are perf_event_attrs not enabling sample_id_all, so can't determine " + << "perf_event_attr for non sample records"; + return false; + } + uint64_t flags_after_id_mask = PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_CPU | PERF_SAMPLE_STREAM_ID; + uint64_t flags_after_id = sample_types[0] & flags_after_id_mask; + bool flags_after_id_are_the_same = true; + for (auto type : sample_types) { + flags_after_id_are_the_same &= (type & flags_after_id_mask) == flags_after_id; + } + if (identifier_enabled) { + *event_id_reverse_pos_in_non_sample_records = 8; + } else if (id_enabled && flags_after_id_are_the_same) { + uint64_t pos = 8; + while (flags_after_id != 0) { + // Each flag takes 8 bytes in sample_id of non sample records. + flags_after_id &= flags_after_id - 1; + pos += 8; + } + *event_id_reverse_pos_in_non_sample_records = pos; + } else { + LOG(ERROR) << "perf_event_attrs don't have a common event id reverse position in non sample records"; + return false; + } + return true; +} + +bool IsTimestampSupported(const perf_event_attr& attr) { + return attr.sample_id_all && (attr.sample_type & PERF_SAMPLE_TIME); +} diff --git a/simpleperf/event_attr.h b/simpleperf/event_attr.h index 79d3df45..df97c2fe 100644 --- a/simpleperf/event_attr.h +++ b/simpleperf/event_attr.h @@ -19,11 +19,17 @@ #include <stddef.h> +#include <vector> + #include "perf_event.h" struct EventType; perf_event_attr CreateDefaultPerfEventAttr(const EventType& event_type); void DumpPerfEventAttr(const perf_event_attr& attr, size_t indent = 0); +bool GetCommonEventIdPositionsForAttrs(std::vector<perf_event_attr>& attrs, + size_t* event_id_pos_in_sample_records, + size_t* event_id_reverse_pos_in_non_sample_records); +bool IsTimestampSupported(const perf_event_attr& attr); #endif // SIMPLE_PERF_EVENT_ATTR_H_ diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp index 808639bd..83c5e26d 100644 --- a/simpleperf/event_fd.cpp +++ b/simpleperf/event_fd.cpp @@ -177,7 +177,7 @@ void EventFd::DiscardMmapData(size_t discard_size) { mmap_metadata_page_->data_tail += discard_size; } -void EventFd::PreparePollForMmapData(pollfd* poll_fd) { +void EventFd::PrepareToPollForMmapData(pollfd* poll_fd) { memset(poll_fd, 0, sizeof(pollfd)); poll_fd->fd = perf_event_fd_; poll_fd->events = POLLIN; diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h index c1a7d753..878ae718 100644 --- a/simpleperf/event_fd.h +++ b/simpleperf/event_fd.h @@ -65,7 +65,7 @@ class EventFd { size_t GetAvailableMmapData(char** pdata); // Prepare pollfd for poll() to wait on available mmap_data. - void PreparePollForMmapData(pollfd* poll_fd); + void PrepareToPollForMmapData(pollfd* poll_fd); private: EventFd(int perf_event_fd, const std::string& event_name, pid_t tid, int cpu) diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp index fad8b1e1..f8b759cc 100644 --- a/simpleperf/event_selection_set.cpp +++ b/simpleperf/event_selection_set.cpp @@ -250,11 +250,11 @@ bool EventSelectionSet::ReadCounters(std::vector<CountersInfo>* counters) { return true; } -void EventSelectionSet::PreparePollForEventFiles(std::vector<pollfd>* pollfds) { +void EventSelectionSet::PrepareToPollForEventFiles(std::vector<pollfd>* pollfds) { for (auto& selection : selections_) { for (auto& event_fd : selection.event_fds) { pollfd poll_fd; - event_fd->PreparePollForMmapData(&poll_fd); + event_fd->PrepareToPollForMmapData(&poll_fd); pollfds->push_back(poll_fd); } } @@ -271,41 +271,73 @@ bool EventSelectionSet::MmapEventFiles(size_t mmap_pages) { return true; } -static bool ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd, - std::function<bool(const char*, size_t)> callback, - bool* have_data) { - *have_data = false; - while (true) { - char* data; - size_t size = event_fd->GetAvailableMmapData(&data); - if (size == 0) { +void EventSelectionSet::PrepareToReadMmapEventData(std::function<bool (Record*)> callback) { + record_callback_ = callback; + bool has_timestamp = true; + for (const auto& selection : selections_) { + if (!IsTimestampSupported(selection.event_attr)) { + has_timestamp = false; break; } - if (!callback(data, size)) { - return false; + } + record_cache_.reset(new RecordCache(has_timestamp)); + + for (const auto& selection : selections_) { + for (const auto& event_fd : selection.event_fds) { + int event_id = event_fd->Id(); + event_id_to_attr_map_[event_id] = &selection.event_attr; } - *have_data = true; } - return true; } -bool EventSelectionSet::ReadMmapEventData(std::function<bool(const char*, size_t)> callback) { +bool EventSelectionSet::ReadMmapEventData() { for (auto& selection : selections_) { for (auto& event_fd : selection.event_fds) { - while (true) { - bool have_data; - if (!ReadMmapEventDataForFd(event_fd, callback, &have_data)) { + bool has_data = true; + while (has_data) { + if (!ReadMmapEventDataForFd(event_fd, selection.event_attr, &has_data)) { return false; } - if (!have_data) { - break; - } } } } return true; } +bool EventSelectionSet::ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd, + const perf_event_attr& attr, + bool* has_data) { + *has_data = false; + while (true) { + char* data; + size_t size = event_fd->GetAvailableMmapData(&data); + if (size == 0) { + break; + } + std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr, data, size); + record_cache_->Push(std::move(records)); + std::unique_ptr<Record> r = record_cache_->Pop(); + while (r != nullptr) { + if (!record_callback_(r.get())) { + return false; + } + r = record_cache_->Pop(); + } + *has_data = true; + } + return true; +} + +bool EventSelectionSet::FinishReadMmapEventData() { + std::vector<std::unique_ptr<Record>> records = record_cache_->PopAll(); + for (auto& r : records) { + if (!record_callback_(r.get())) { + return false; + } + } + return true; +} + EventSelectionSet::EventSelection* EventSelectionSet::FindSelectionByType( const EventTypeAndModifier& event_type_modifier) { for (auto& selection : selections_) { diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h index 746abfa7..d2e2511a 100644 --- a/simpleperf/event_selection_set.h +++ b/simpleperf/event_selection_set.h @@ -19,6 +19,7 @@ #include <functional> #include <map> +#include <unordered_map> #include <vector> #include <android-base/macros.h> @@ -26,6 +27,7 @@ #include "event_fd.h" #include "event_type.h" #include "perf_event.h" +#include "record.h" struct CountersInfo { const EventTypeAndModifier* event_type; @@ -72,9 +74,11 @@ class EventSelectionSet { bool OpenEventFilesForCpus(const std::vector<int>& cpus); bool OpenEventFilesForThreadsOnCpus(const std::vector<pid_t>& threads, std::vector<int> cpus); bool ReadCounters(std::vector<CountersInfo>* counters); - void PreparePollForEventFiles(std::vector<pollfd>* pollfds); + void PrepareToPollForEventFiles(std::vector<pollfd>* pollfds); bool MmapEventFiles(size_t mmap_pages); - bool ReadMmapEventData(std::function<bool(const char*, size_t)> callback); + void PrepareToReadMmapEventData(std::function<bool (Record*)> callback); + bool ReadMmapEventData(); + bool FinishReadMmapEventData(); const perf_event_attr* FindEventAttrByType(const EventTypeAndModifier& event_type_modifier); const std::vector<std::unique_ptr<EventFd>>* FindEventFdsByType( @@ -83,6 +87,8 @@ class EventSelectionSet { private: void UnionSampleType(); bool OpenEventFiles(const std::vector<pid_t>& threads, const std::vector<int>& cpus); + bool ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd, const perf_event_attr& attr, + bool* has_data); struct EventSelection { EventTypeAndModifier event_type_modifier; @@ -93,6 +99,10 @@ class EventSelectionSet { std::vector<EventSelection> selections_; + std::function<bool (Record*)> record_callback_; + std::unique_ptr<RecordCache> record_cache_; + std::unordered_map<uint64_t, const perf_event_attr*> event_id_to_attr_map_; + DISALLOW_COPY_AND_ASSIGN(EventSelectionSet); }; diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h index 57a6e646..4c21d39e 100644 --- a/simpleperf/get_test_data.h +++ b/simpleperf/get_test_data.h @@ -65,4 +65,7 @@ constexpr size_t NATIVELIB_SIZE_IN_APK = 0x1678; static BuildId native_lib_build_id("8ed5755a7fdc07586ca228b8ee21621bce2c7a97"); +// perf_with_two_event_types.data is generated by sampling using -e cpu-cycles,cpu-clock option. +static const std::string PERF_DATA_WITH_TWO_EVENT_TYPES = "perf_with_two_event_types.data"; + #endif // SIMPLE_PERF_GET_TEST_DATA_H_ diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp index d97ba811..02162753 100644 --- a/simpleperf/record.cpp +++ b/simpleperf/record.cpp @@ -69,9 +69,10 @@ SampleId::SampleId() { } // Return sample_id size in binary format. -size_t SampleId::CreateContent(const perf_event_attr& attr) { +size_t SampleId::CreateContent(const perf_event_attr& attr, uint64_t event_id) { sample_id_all = attr.sample_id_all; sample_type = attr.sample_type; + id_data.id = event_id; // Other data are not necessary. TODO: Set missing SampleId data. return Size(); } @@ -132,7 +133,7 @@ void SampleId::Dump(size_t indent) const { PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time); } if (sample_type & PERF_SAMPLE_ID) { - PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", id_data.id); + PrintIndented(indent, "sample_id: id %" PRId64 "\n", id_data.id); } if (sample_type & PERF_SAMPLE_STREAM_ID) { PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", stream_id_data.stream_id); @@ -576,8 +577,8 @@ std::vector<char> UnknownRecord::BinaryFormat() const { void UnknownRecord::DumpData(size_t) const { } -static std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr, - const perf_event_header* pheader) { +std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr, + const perf_event_header* pheader) { switch (pheader->type) { case PERF_RECORD_MMAP: return std::unique_ptr<Record>(new MmapRecord(attr, pheader)); @@ -611,25 +612,9 @@ std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr return result; } -std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp) { - std::vector<char> buf(sizeof(perf_event_header)); - perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]); - if (fread(header, sizeof(perf_event_header), 1, fp) != 1) { - PLOG(ERROR) << "Failed to read record file"; - return nullptr; - } - buf.resize(header->size); - header = reinterpret_cast<perf_event_header*>(&buf[0]); - if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, fp) != 1) { - PLOG(ERROR) << "Failed to read record file"; - return nullptr; - } - return ReadRecordFromBuffer(attr, header); -} - MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid, uint64_t addr, uint64_t len, uint64_t pgoff, - const std::string& filename) { + const std::string& filename, uint64_t event_id) { MmapRecord record; record.header.type = PERF_RECORD_MMAP; record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER); @@ -639,28 +624,28 @@ MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_ record.data.len = len; record.data.pgoff = pgoff; record.filename = filename; - size_t sample_id_size = record.sample_id.CreateContent(attr); + size_t sample_id_size = record.sample_id.CreateContent(attr, event_id); record.header.size = sizeof(record.header) + sizeof(record.data) + ALIGN(record.filename.size() + 1, 8) + sample_id_size; return record; } CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, - const std::string& comm) { + const std::string& comm, uint64_t event_id) { CommRecord record; record.header.type = PERF_RECORD_COMM; record.header.misc = 0; record.data.pid = pid; record.data.tid = tid; record.comm = comm; - size_t sample_id_size = record.sample_id.CreateContent(attr); + size_t sample_id_size = record.sample_id.CreateContent(attr, event_id); record.header.size = sizeof(record.header) + sizeof(record.data) + ALIGN(record.comm.size() + 1, 8) + sample_id_size; return record; } ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid, - uint32_t ptid) { + uint32_t ptid, uint64_t event_id) { ForkRecord record; record.header.type = PERF_RECORD_FORK; record.header.misc = 0; @@ -669,7 +654,7 @@ ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t record.data.tid = tid; record.data.ptid = ptid; record.data.time = 0; - size_t sample_id_size = record.sample_id.CreateContent(attr); + size_t sample_id_size = record.sample_id.CreateContent(attr, event_id); record.header.size = sizeof(record.header) + sizeof(record.data) + sample_id_size; return record; } @@ -710,10 +695,8 @@ bool RecordCache::RecordComparator::operator()(const RecordWithSeq& r1, return r2.IsHappensBefore(r1); } -RecordCache::RecordCache(const perf_event_attr& attr, size_t min_cache_size, - uint64_t min_time_diff_in_ns) - : attr_(attr), - has_timestamp_(attr.sample_id_all && (attr.sample_type & PERF_SAMPLE_TIME)), +RecordCache::RecordCache(bool has_timestamp, size_t min_cache_size, uint64_t min_time_diff_in_ns) + : has_timestamp_(has_timestamp), min_cache_size_(min_cache_size), min_time_diff_in_ns_(min_time_diff_in_ns), last_time_(0), @@ -725,22 +708,19 @@ RecordCache::~RecordCache() { PopAll(); } -void RecordCache::Push(const char* data, size_t size) { - std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr_, data, size); +void RecordCache::Push(std::unique_ptr<Record> record) { if (has_timestamp_) { - for (const auto& r : records) { - last_time_ = std::max(last_time_, r->Timestamp()); - } + last_time_ = std::max(last_time_, record->Timestamp()); } + queue_.push(CreateRecordWithSeq(record.release())); +} + +void RecordCache::Push(std::vector<std::unique_ptr<Record>> records) { for (auto& r : records) { queue_.push(CreateRecordWithSeq(r.release())); } } -void RecordCache::Push(std::unique_ptr<Record> record) { - queue_.push(CreateRecordWithSeq(record.release())); -} - std::unique_ptr<Record> RecordCache::Pop() { if (queue_.size() < min_cache_size_) { return nullptr; diff --git a/simpleperf/record.h b/simpleperf/record.h index a94a9179..8b17bc10 100644 --- a/simpleperf/record.h +++ b/simpleperf/record.h @@ -117,7 +117,7 @@ struct SampleId { SampleId(); // Create the content of sample_id. It depends on the attr we use. - size_t CreateContent(const perf_event_attr& attr); + size_t CreateContent(const perf_event_attr& attr, uint64_t event_id); // Parse sample_id from binary format in the buffer pointed by p. void ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end); @@ -322,11 +322,11 @@ struct UnknownRecord : public Record { // we are not likely to receive a record for time (t - min_time_diff) or earlier. class RecordCache { public: - RecordCache(const perf_event_attr& attr, size_t min_cache_size = 1000u, + RecordCache(bool has_timestamp, size_t min_cache_size = 1000u, uint64_t min_time_diff_in_ns = 1000000u); ~RecordCache(); - void Push(const char* data, size_t size); void Push(std::unique_ptr<Record> record); + void Push(std::vector<std::unique_ptr<Record>> records); std::unique_ptr<Record> Pop(); std::vector<std::unique_ptr<Record>> PopAll(); @@ -344,7 +344,6 @@ class RecordCache { RecordWithSeq CreateRecordWithSeq(Record *r); - const perf_event_attr attr_; bool has_timestamp_; size_t min_cache_size_; uint64_t min_time_diff_in_ns_; @@ -354,16 +353,17 @@ class RecordCache { RecordComparator> queue_; }; +std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr, + const perf_event_header* pheader); std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr& attr, const char* buf, size_t buf_size); -std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp); MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid, uint64_t addr, uint64_t len, uint64_t pgoff, - const std::string& filename); + const std::string& filename, uint64_t event_id); CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, - const std::string& comm); + const std::string& comm, uint64_t event_id); ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid, - uint32_t ptid); + uint32_t ptid, uint64_t event_id); BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id, const std::string& filename); diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h index c0f53b16..3005dee6 100644 --- a/simpleperf/record_file.h +++ b/simpleperf/record_file.h @@ -23,6 +23,7 @@ #include <map> #include <memory> #include <string> +#include <unordered_map> #include <vector> #include <android-base/macros.h> @@ -124,14 +125,19 @@ class RecordFileReader { bool ReadAttrSection(); bool ReadFeatureSectionDescriptors(); bool ReadFeatureSection(int feature, std::vector<char>* data); + std::unique_ptr<Record> ReadRecord(); const std::string filename_; FILE* record_fp_; PerfFileFormat::FileHeader header_; std::vector<PerfFileFormat::FileAttr> file_attrs_; + std::unordered_map<uint64_t, perf_event_attr*> event_id_to_attr_map_; std::map<int, PerfFileFormat::SectionDesc> feature_section_descriptors_; + size_t event_id_pos_in_sample_records_; + size_t event_id_reverse_pos_in_non_sample_records_; + DISALLOW_COPY_AND_ASSIGN(RecordFileReader); }; diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp index f126a6b7..1165494c 100644 --- a/simpleperf/record_file_reader.cpp +++ b/simpleperf/record_file_reader.cpp @@ -23,7 +23,7 @@ #include <android-base/logging.h> -#include "perf_event.h" +#include "event_attr.h" #include "record.h" #include "utils.h" @@ -45,7 +45,8 @@ std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::st } RecordFileReader::RecordFileReader(const std::string& filename, FILE* fp) - : filename_(filename), record_fp_(fp) { + : filename_(filename), record_fp_(fp), event_id_pos_in_sample_records_(0), + event_id_reverse_pos_in_non_sample_records_(0) { } RecordFileReader::~RecordFileReader() { @@ -102,6 +103,25 @@ bool RecordFileReader::ReadAttrSection() { memcpy(&attr.ids, &buf[perf_event_attr_size], section_desc_size); file_attrs_.push_back(attr); } + if (file_attrs_.size() > 1) { + std::vector<perf_event_attr> attrs; + for (const auto& file_attr : file_attrs_) { + attrs.push_back(file_attr.attr); + } + if (!GetCommonEventIdPositionsForAttrs(attrs, &event_id_pos_in_sample_records_, + &event_id_reverse_pos_in_non_sample_records_)) { + return false; + } + } + for (size_t i = 0; i < file_attrs_.size(); ++i) { + std::vector<uint64_t> ids; + if (!ReadIdsForAttr(file_attrs_[i], &ids)) { + return false; + } + for (auto id : ids) { + event_id_to_attr_map_[id] = &file_attrs_[i].attr; + } + } return true; } @@ -150,9 +170,16 @@ bool RecordFileReader::ReadDataSection(std::function<bool(std::unique_ptr<Record PLOG(ERROR) << "failed to fseek()"; return false; } - RecordCache cache(file_attrs_[0].attr); + bool has_timestamp = true; + for (const auto& attr : file_attrs_) { + if (!IsTimestampSupported(attr.attr)) { + has_timestamp = false; + break; + } + } + RecordCache cache(has_timestamp); for (size_t nbytes_read = 0; nbytes_read < header_.data.size;) { - std::unique_ptr<Record> record = ReadRecordFromFile(file_attrs_[0].attr, record_fp_); + std::unique_ptr<Record> record = ReadRecord(); if (record == nullptr) { return false; } @@ -180,6 +207,37 @@ bool RecordFileReader::ReadDataSection(std::function<bool(std::unique_ptr<Record return true; } +std::unique_ptr<Record> RecordFileReader::ReadRecord() { + std::vector<char> buf(sizeof(perf_event_header)); + if (fread(buf.data(), sizeof(perf_event_header), 1, record_fp_) != 1) { + PLOG(ERROR) << "failed to read file " << filename_; + return nullptr; + } + perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]); + if (buf.size() < header->size) { + buf.resize(header->size); + header = reinterpret_cast<perf_event_header*>(&buf[0]); + } + if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, record_fp_) != 1) { + PLOG(ERROR) << "failed to read file " << filename_; + return nullptr; + } + const perf_event_attr* attr = &file_attrs_[0].attr; + if (file_attrs_.size() > 1) { + uint64_t event_id; + if (header->type == PERF_RECORD_SAMPLE) { + event_id = *reinterpret_cast<uint64_t*>(&buf[event_id_pos_in_sample_records_]); + } else { + event_id = *reinterpret_cast<uint64_t*>( + &buf[header->size - event_id_reverse_pos_in_non_sample_records_]); + } + auto it = event_id_to_attr_map_.find(event_id); + CHECK(it != event_id_to_attr_map_.end()); + attr = it->second; + } + return ReadRecordFromBuffer(*attr, header); +} + bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) { const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors(); auto it = section_map.find(feature); diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp index 4648a649..96d1e53f 100644 --- a/simpleperf/record_file_test.cpp +++ b/simpleperf/record_file_test.cpp @@ -38,6 +38,7 @@ class RecordFileTest : public ::testing::Test { std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_str); ASSERT_TRUE(event_type_modifier != nullptr); perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type); + attr.sample_id_all = 1; attrs_.push_back(std::unique_ptr<perf_event_attr>(new perf_event_attr(attr))); AttrWithId attr_id; attr_id.attr = attrs_.back().get(); @@ -61,7 +62,7 @@ TEST_F(RecordFileTest, smoke) { // Write data section. MmapRecord mmap_record = CreateMmapRecord(*(attr_ids_[0].attr), true, 1, 1, 0x1000, 0x2000, - 0x3000, "mmap_record_example"); + 0x3000, "mmap_record_example", attr_ids_[0].ids[0]); ASSERT_TRUE(writer->WriteData(mmap_record.BinaryFormat())); // Write feature section. @@ -111,7 +112,8 @@ TEST_F(RecordFileTest, records_sorted_by_time) { // Write data section. MmapRecord r1 = - CreateMmapRecord(*(attr_ids_[0].attr), true, 1, 1, 0x100, 0x2000, 0x3000, "mmap_record1"); + CreateMmapRecord(*(attr_ids_[0].attr), true, 1, 1, 0x100, 0x2000, 0x3000, "mmap_record1", + attr_ids_[0].ids[0]); MmapRecord r2 = r1; MmapRecord r3 = r1; r1.sample_id.time_data.time = 2; diff --git a/simpleperf/record_test.cpp b/simpleperf/record_test.cpp index 76eebe98..32d73336 100644 --- a/simpleperf/record_test.cpp +++ b/simpleperf/record_test.cpp @@ -46,20 +46,20 @@ void RecordTest::CheckRecordMatchBinary(const RecordType& record) { TEST_F(RecordTest, MmapRecordMatchBinary) { MmapRecord record = - CreateMmapRecord(event_attr, true, 1, 2, 0x1000, 0x2000, 0x3000, "MmapRecord"); + CreateMmapRecord(event_attr, true, 1, 2, 0x1000, 0x2000, 0x3000, "MmapRecord", 0); CheckRecordMatchBinary(record); } TEST_F(RecordTest, CommRecordMatchBinary) { - CommRecord record = CreateCommRecord(event_attr, 1, 2, "CommRecord"); + CommRecord record = CreateCommRecord(event_attr, 1, 2, "CommRecord", 0); CheckRecordMatchBinary(record); } TEST_F(RecordTest, RecordCache_smoke) { event_attr.sample_id_all = 1; event_attr.sample_type |= PERF_SAMPLE_TIME; - RecordCache cache(event_attr, 2, 2); - MmapRecord r1 = CreateMmapRecord(event_attr, true, 1, 1, 0x100, 0x200, 0x300, "mmap_record1"); + RecordCache cache(true, 2, 2); + MmapRecord r1 = CreateMmapRecord(event_attr, true, 1, 1, 0x100, 0x200, 0x300, "mmap_record1", 0); MmapRecord r2 = r1; MmapRecord r3 = r1; MmapRecord r4 = r1; @@ -67,25 +67,21 @@ TEST_F(RecordTest, RecordCache_smoke) { r2.sample_id.time_data.time = 1; r3.sample_id.time_data.time = 4; r4.sample_id.time_data.time = 6; - std::vector<char> buf1 = r1.BinaryFormat(); - std::vector<char> buf2 = r2.BinaryFormat(); - std::vector<char> buf3 = r3.BinaryFormat(); - std::vector<char> buf4 = r4.BinaryFormat(); // Push r1. - cache.Push(buf1.data(), buf1.size()); + cache.Push(std::unique_ptr<Record>(new MmapRecord(r1))); ASSERT_EQ(nullptr, cache.Pop()); // Push r2. - cache.Push(buf2.data(), buf2.size()); + cache.Push(std::unique_ptr<Record>(new MmapRecord(r2))); // Pop r2. std::unique_ptr<Record> popped_r = cache.Pop(); ASSERT_TRUE(popped_r != nullptr); CheckRecordEqual(r2, *popped_r); ASSERT_EQ(nullptr, cache.Pop()); // Push r3. - cache.Push(buf3.data(), buf3.size()); + cache.Push(std::unique_ptr<Record>(new MmapRecord(r3))); ASSERT_EQ(nullptr, cache.Pop()); // Push r4. - cache.Push(buf4.data(), buf4.size()); + cache.Push(std::unique_ptr<Record>(new MmapRecord(r4))); // Pop r1. popped_r = cache.Pop(); ASSERT_TRUE(popped_r != nullptr); @@ -104,13 +100,12 @@ TEST_F(RecordTest, RecordCache_smoke) { TEST_F(RecordTest, RecordCache_FIFO) { event_attr.sample_id_all = 1; event_attr.sample_type |= PERF_SAMPLE_TIME; - RecordCache cache(event_attr, 2, 2); + RecordCache cache(true, 2, 2); std::vector<MmapRecord> records; for (size_t i = 0; i < 10; ++i) { - MmapRecord r = CreateMmapRecord(event_attr, true, 1, i, 0x100, 0x200, 0x300, "mmap_record1"); + MmapRecord r = CreateMmapRecord(event_attr, true, 1, i, 0x100, 0x200, 0x300, "mmap_record1", 0); records.push_back(r); - std::vector<char> buf = r.BinaryFormat(); - cache.Push(buf.data(), buf.size()); + cache.Push(std::unique_ptr<Record>(new MmapRecord(r))); } std::vector<std::unique_ptr<Record>> out_records = cache.PopAll(); ASSERT_EQ(records.size(), out_records.size()); diff --git a/simpleperf/testdata/perf_with_two_event_types.data b/simpleperf/testdata/perf_with_two_event_types.data Binary files differnew file mode 100644 index 00000000..ba9a6063 --- /dev/null +++ b/simpleperf/testdata/perf_with_two_event_types.data |