From d713f959ec1fe07ed993e2c9f4166b52aa98a58c Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 23 Jun 2015 18:50:36 -0700 Subject: [PATCH] Simpleperf: support cmdline feature in perf.data. Bug: 19483574 Change-Id: I92f16d6616f274f31ea54e305fe1de10049baf02 --- simpleperf/cmd_dumprecord.cpp | 22 ++++---- simpleperf/cmd_record.cpp | 17 ++++-- simpleperf/cmd_report.cpp | 12 +++++ simpleperf/environment.cpp | 12 +++++ simpleperf/environment.h | 2 + simpleperf/record.cpp | 6 --- simpleperf/record_file.cpp | 116 +++++++++++++++++++++++++++------------- simpleperf/record_file.h | 10 +++- simpleperf/record_file_test.cpp | 9 ++-- simpleperf/utils.h | 6 +++ 10 files changed, 149 insertions(+), 63 deletions(-) diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp index e5e239b5..28175d95 100644 --- a/simpleperf/cmd_dumprecord.cpp +++ b/simpleperf/cmd_dumprecord.cpp @@ -22,11 +22,13 @@ #include #include +#include #include "command.h" #include "event_attr.h" #include "record.h" #include "record_file.h" +#include "utils.h" using namespace PerfFileFormat; @@ -50,8 +52,6 @@ class DumpRecordCommand : public Command { std::string record_filename_; std::unique_ptr record_file_reader_; - - std::vector features_; }; bool DumpRecordCommand::Run(const std::vector& args) { @@ -105,15 +105,15 @@ void DumpRecordCommand::DumpFileHeader() { printf("event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n", header->event_types.offset, header->event_types.size); - features_.clear(); + std::vector features; for (size_t i = 0; i < FEAT_MAX_NUM; ++i) { size_t j = i / 8; size_t k = i % 8; if ((header->features[j] & (1 << k)) != 0) { - features_.push_back(i); + features.push_back(i); } } - for (auto& feature : features_) { + for (auto& feature : features) { printf("feature: %s\n", GetFeatureName(feature).c_str()); } } @@ -172,11 +172,10 @@ void DumpRecordCommand::DumpDataSection() { } void DumpRecordCommand::DumpFeatureSection() { - std::vector sections = record_file_reader_->FeatureSectionDescriptors(); - CHECK_EQ(sections.size(), features_.size()); - for (size_t i = 0; i < features_.size(); ++i) { - int feature = features_[i]; - SectionDesc& section = sections[i]; + std::map section_map = record_file_reader_->FeatureSectionDescriptors(); + for (auto& pair : section_map) { + int feature = pair.first; + SectionDesc& section = pair.second; printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n", GetFeatureName(feature).c_str(), section.offset, section.size); if (feature == FEAT_BUILD_ID) { @@ -190,6 +189,9 @@ void DumpRecordCommand::DumpFeatureSection() { record.Dump(1); p += header->size; } + } else if (feature == FEAT_CMDLINE) { + std::vector cmdline = record_file_reader_->ReadCmdlineFeature(); + PrintIndented(1, "cmdline: %s\n", android::base::Join(cmdline, ' ').c_str()); } } } diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 1b692c93..59cc3dc5 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -113,7 +113,7 @@ class RecordCommand : public Command { bool WriteData(const char* data, size_t size); bool DumpKernelAndModuleMmaps(); bool DumpThreadCommAndMmaps(); - bool DumpAdditionalFeatures(); + bool DumpAdditionalFeatures(const std::vector& args); bool DumpBuildIdFeature(); bool use_sample_freq_; // Use sample_freq_ when true, otherwise using sample_period_. @@ -224,7 +224,7 @@ bool RecordCommand::Run(const std::vector& args) { } // 6. Dump additional features, and close record file. - if (!DumpAdditionalFeatures()) { + if (!DumpAdditionalFeatures(args)) { return false; } if (!record_file_writer_->Close()) { @@ -439,14 +439,23 @@ bool RecordCommand::DumpThreadCommAndMmaps() { return true; } -bool RecordCommand::DumpAdditionalFeatures() { - size_t feature_count = (branch_sampling_ != 0 ? 2 : 1); +bool RecordCommand::DumpAdditionalFeatures(const std::vector& args) { + size_t feature_count = (branch_sampling_ != 0 ? 3 : 2); if (!record_file_writer_->WriteFeatureHeader(feature_count)) { return false; } if (!DumpBuildIdFeature()) { return false; } + std::string exec_path = "simpleperf"; + GetExecPath(&exec_path); + std::vector cmdline; + cmdline.push_back(exec_path); + cmdline.push_back("record"); + cmdline.insert(cmdline.end(), args.begin(), args.end()); + if (!record_file_writer_->WriteCmdlineFeature(cmdline)) { + return false; + } if (branch_sampling_ != 0 && !record_file_writer_->WriteBranchStackFeature()) { return false; } diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp index d7635ccb..59acc2c2 100644 --- a/simpleperf/cmd_report.cpp +++ b/simpleperf/cmd_report.cpp @@ -253,6 +253,7 @@ class ReportCommand : public Command { bool ParseOptions(const std::vector& args); bool ReadEventAttrFromRecordFile(); void ReadSampleTreeFromRecordFile(); + void ReadFeaturesFromRecordFile(); int CompareSampleEntry(const SampleEntry& sample1, const SampleEntry& sample2); void PrintReport(); void PrintReportContext(); @@ -267,6 +268,7 @@ class ReportCommand : public Command { std::vector report_items_; std::unique_ptr sample_tree_; bool use_branch_address_; + std::string record_cmdline_; }; bool ReportCommand::Run(const std::vector& args) { @@ -410,6 +412,13 @@ void ReportCommand::ReadSampleTreeFromRecordFile() { } } +void ReportCommand::ReadFeaturesFromRecordFile() { + std::vector cmdline = record_file_reader_->ReadCmdlineFeature(); + if (!cmdline.empty()) { + record_cmdline_ = android::base::Join(cmdline, ' '); + } +} + int ReportCommand::CompareSampleEntry(const SampleEntry& sample1, const SampleEntry& sample2) { for (auto& item : report_items_) { if (item->compare_function != nullptr) { @@ -440,6 +449,9 @@ void ReportCommand::PrintReportContext() { event_type_name = android::base::StringPrintf("(type %u, config %llu)", event_attr_.type, event_attr_.config); } + if (!record_cmdline_.empty()) { + printf("Cmdline: %s\n", record_cmdline_.c_str()); + } printf("Samples: %" PRIu64 " of event '%s'\n", sample_tree_->TotalSamples(), event_type_name.c_str()); printf("Event count: %" PRIu64 "\n\n", sample_tree_->TotalPeriod()); diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index f4e98e69..e139b03b 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -401,3 +401,15 @@ bool GetValidThreadsFromThreadString(const std::string& tid_str, std::set } return true; } + +bool GetExecPath(std::string* exec_path) { + char path[PATH_MAX]; + ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path)); + if (path_len <= 0 || path_len >= static_cast(sizeof(path))) { + PLOG(ERROR) << "readlink failed"; + return false; + } + path[path_len] = '\0'; + *exec_path = path; + return true; +} diff --git a/simpleperf/environment.h b/simpleperf/environment.h index 6d844ff6..cfa324c3 100644 --- a/simpleperf/environment.h +++ b/simpleperf/environment.h @@ -71,6 +71,8 @@ bool GetModuleBuildId(const std::string& module_name, BuildId* build_id); bool GetValidThreadsFromProcessString(const std::string& pid_str, std::set* tid_set); bool GetValidThreadsFromThreadString(const std::string& tid_str, std::set* tid_set); +bool GetExecPath(std::string* exec_path); + // Expose the following functions for unit tests. std::vector GetOnlineCpusFromString(const std::string& s); diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp index b0b4b252..f6b25604 100644 --- a/simpleperf/record.cpp +++ b/simpleperf/record.cpp @@ -43,12 +43,6 @@ static std::string RecordTypeToString(int record_type) { } template -void MoveFromBinaryFormat(T& data, const char*& p) { - data = *reinterpret_cast(p); - p += sizeof(T); -} - -template void MoveFromBinaryFormat(T* data_p, size_t n, const char*& p) { size_t size = n * sizeof(T); memcpy(data_p, p, size); diff --git a/simpleperf/record_file.cpp b/simpleperf/record_file.cpp index 2a7c6a14..2f6bf109 100644 --- a/simpleperf/record_file.cpp +++ b/simpleperf/record_file.cpp @@ -245,10 +245,8 @@ bool RecordFileWriter::WriteFeatureHeader(size_t feature_count) { } bool RecordFileWriter::WriteBuildIdFeature(const std::vector& build_id_records) { - CHECK_LT(current_feature_index_, feature_count_); - // Always write features at the end of the file. uint64_t start_offset; - if (!SeekFileEnd(&start_offset)) { + if (!WriteFeatureBegin(&start_offset)) { return false; } for (auto& record : build_id_records) { @@ -257,48 +255,67 @@ bool RecordFileWriter::WriteBuildIdFeature(const std::vector& bui return false; } } - uint64_t end_offset; - if (!SeekFileEnd(&end_offset)) { + return WriteFeatureEnd(FEAT_BUILD_ID, start_offset); +} + +bool RecordFileWriter::WriteCmdlineFeature(const std::vector& cmdline) { + uint64_t start_offset; + if (!WriteFeatureBegin(&start_offset)) { return false; } - - if (!ModifyFeatureSectionDescriptor(current_feature_index_, start_offset, - end_offset - start_offset)) { + uint32_t arg_count = cmdline.size(); + if (!Write(&arg_count, sizeof(arg_count))) { return false; } - ++current_feature_index_; - features_.push_back(FEAT_BUILD_ID); - return true; + for (auto& arg : cmdline) { + uint32_t len = static_cast(ALIGN(arg.size() + 1, 64)); + if (!Write(&len, sizeof(len))) { + return false; + } + std::vector array(len, '\0'); + std::copy(arg.begin(), arg.end(), array.begin()); + if (!Write(array.data(), array.size())) { + return false; + } + } + return WriteFeatureEnd(FEAT_CMDLINE, start_offset); } bool RecordFileWriter::WriteBranchStackFeature() { - CHECK_LT(current_feature_index_, feature_count_); uint64_t start_offset; - if (!SeekFileEnd(&start_offset)) { + if (!WriteFeatureBegin(&start_offset)) { return false; } - if (!ModifyFeatureSectionDescriptor(current_feature_index_, start_offset, 0)) { + return WriteFeatureEnd(FEAT_BRANCH_STACK, start_offset); +} + +bool RecordFileWriter::WriteFeatureBegin(uint64_t* start_offset) { + CHECK_LT(current_feature_index_, feature_count_); + if (!SeekFileEnd(start_offset)) { return false; } - ++current_feature_index_; - features_.push_back(FEAT_BRANCH_STACK); return true; } -bool RecordFileWriter::ModifyFeatureSectionDescriptor(size_t feature_index, uint64_t offset, - uint64_t size) { +bool RecordFileWriter::WriteFeatureEnd(int feature, uint64_t start_offset) { + uint64_t end_offset; + if (!SeekFileEnd(&end_offset)) { + return false; + } SectionDesc desc; - desc.offset = offset; - desc.size = size; + desc.offset = start_offset; + desc.size = end_offset - start_offset; uint64_t feature_offset = data_section_offset_ + data_section_size_; - if (fseek(record_fp_, feature_offset + feature_index * sizeof(SectionDesc), SEEK_SET) == -1) { + if (fseek(record_fp_, feature_offset + current_feature_index_ * sizeof(SectionDesc), SEEK_SET) == + -1) { PLOG(ERROR) << "fseek() failed"; return false; } - if (fwrite(&desc, sizeof(SectionDesc), 1, record_fp_) != 1) { - PLOG(ERROR) << "fwrite() failed"; + if (!Write(&desc, sizeof(SectionDesc))) { return false; } + ++current_feature_index_; + features_.push_back(feature); return true; } @@ -467,21 +484,46 @@ std::vector> RecordFileReader::DataSection() { return result; } -std::vector RecordFileReader::FeatureSectionDescriptors() { - std::vector result; - const struct FileHeader* header = FileHeader(); - size_t feature_count = 0; - for (size_t i = 0; i < sizeof(header->features); ++i) { - for (size_t j = 0; j < 8; ++j) { - if (header->features[i] & (1 << j)) { - ++feature_count; +const std::map& RecordFileReader::FeatureSectionDescriptors() { + if (feature_sections_.empty()) { + std::vector features; + const struct FileHeader* header = FileHeader(); + for (size_t i = 0; i < sizeof(header->features); ++i) { + for (size_t j = 0; j < 8; ++j) { + if (header->features[i] & (1 << j)) { + features.push_back(i * 8 + j); + } } } + uint64_t feature_section_offset = header->data.offset + header->data.size; + const SectionDesc* p = reinterpret_cast(mmap_addr_ + feature_section_offset); + for (auto& feature : features) { + feature_sections_.insert(std::make_pair(feature, *p)); + ++p; + } } - uint64_t feature_section_offset = header->data.offset + header->data.size; - const SectionDesc* p = reinterpret_cast(mmap_addr_ + feature_section_offset); - for (size_t i = 0; i < feature_count; ++i) { - result.push_back(*p++); - } - return result; + return feature_sections_; +} + +std::vector RecordFileReader::ReadCmdlineFeature() { + const std::map& section_map = FeatureSectionDescriptors(); + auto it = section_map.find(FEAT_CMDLINE); + if (it == section_map.end()) { + return std::vector(); + } + SectionDesc section = it->second; + const char* p = DataAtOffset(section.offset); + const char* end = DataAtOffset(section.offset + section.size); + std::vector cmdline; + uint32_t arg_count; + MoveFromBinaryFormat(arg_count, p); + CHECK_LE(p, end); + for (size_t i = 0; i < arg_count; ++i) { + uint32_t len; + MoveFromBinaryFormat(len, p); + CHECK_LE(p + len, end); + cmdline.push_back(p); + p += len; + } + return cmdline; } diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h index e217b3c2..d8b44135 100644 --- a/simpleperf/record_file.h +++ b/simpleperf/record_file.h @@ -18,6 +18,7 @@ #define SIMPLE_PERF_RECORD_FILE_H_ #include +#include #include #include #include @@ -52,6 +53,7 @@ class RecordFileWriter { bool WriteFeatureHeader(size_t feature_count); bool WriteBuildIdFeature(const std::vector& build_id_records); + bool WriteCmdlineFeature(const std::vector& cmdline); bool WriteBranchStackFeature(); // Normally, Close() should be called after writing. But if something @@ -69,7 +71,8 @@ class RecordFileWriter { bool WriteFileHeader(); bool Write(const void* buf, size_t len); bool SeekFileEnd(uint64_t* file_end); - bool ModifyFeatureSectionDescriptor(size_t feature_index, uint64_t offset, uint64_t size); + bool WriteFeatureBegin(uint64_t* start_offset); + bool WriteFeatureEnd(int feature, uint64_t start_offset); const std::string filename_; FILE* record_fp_; @@ -98,10 +101,11 @@ class RecordFileReader { std::vector AttrSection(); std::vector IdsForAttr(const PerfFileFormat::FileAttr* attr); std::vector> DataSection(); - std::vector FeatureSectionDescriptors(); + const std::map& FeatureSectionDescriptors(); const char* DataAtOffset(uint64_t offset) { return mmap_addr_ + offset; } + std::vector ReadCmdlineFeature(); bool Close(); private: @@ -114,6 +118,8 @@ class RecordFileReader { const char* mmap_addr_; size_t mmap_len_; + std::map feature_sections_; + DISALLOW_COPY_AND_ASSIGN(RecordFileReader); }; diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp index c4bb255d..6e6bc134 100644 --- a/simpleperf/record_file_test.cpp +++ b/simpleperf/record_file_test.cpp @@ -86,12 +86,13 @@ TEST_F(RecordFileTest, smoke) { // Read and check feature section. ASSERT_TRUE(file_header->features[FEAT_BUILD_ID / 8] & (1 << (FEAT_BUILD_ID % 8))); - std::vector sections = reader->FeatureSectionDescriptors(); + std::map sections = reader->FeatureSectionDescriptors(); ASSERT_EQ(1u, sections.size()); - const perf_event_header* header = - reinterpret_cast(reader->DataAtOffset(sections[0].offset)); + ASSERT_TRUE(sections.find(FEAT_BUILD_ID) != sections.end()); + const perf_event_header* header = reinterpret_cast( + reader->DataAtOffset(sections[FEAT_BUILD_ID].offset)); ASSERT_TRUE(header != nullptr); - ASSERT_EQ(sections[0].size, header->size); + ASSERT_EQ(sections[FEAT_BUILD_ID].size, header->size); CheckRecordEqual(build_id_record, BuildIdRecord(header)); ASSERT_TRUE(reader->Close()); diff --git a/simpleperf/utils.h b/simpleperf/utils.h index b0171978..1108f725 100644 --- a/simpleperf/utils.h +++ b/simpleperf/utils.h @@ -72,6 +72,12 @@ class SignalHandlerRegister { std::vector> saved_signal_handlers_; }; +template +void MoveFromBinaryFormat(T& data, const char*& p) { + data = *reinterpret_cast(p); + p += sizeof(T); +} + void PrintIndented(size_t indent, const char* fmt, ...); bool IsPowerOfTwo(uint64_t value); -- 2.11.0