diff options
author | Yabin Cui <yabinc@google.com> | 2021-03-01 10:07:55 -0800 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2021-03-01 18:10:36 -0800 |
commit | fbbe9e5cf0407c1cec719e6d47c4d6f433be2cb5 (patch) | |
tree | 0a82fd4a047fb2c5f4652ae2f4816c2fac240960 /simpleperf | |
parent | f4089ed75c62d0a092c9b5d5b22f8cafebf302b5 (diff) | |
download | extras-fbbe9e5cf0407c1cec719e6d47c4d6f433be2cb5.tar.gz |
simpleperf: keep binaries for debugging failed unwinding cases.
We need binaries for debugging failed unwinding cases. So
change --keep-failed-unwinding-stack-data to
--keep-failed-unwinding-debug-info, which stores both stack data and
used binaries for failed unwinding cases.
To store binaries in perf.data:
1. Add debug_unwind feature section to keep binary path and size info.
2. Add debug_unwind_file feature section to keep binary data.
Bug: 181075274
Test: run simpleperf_unit_test
Change-Id: Id8759051ba520f1be9c2ef6030143f8b4bafc7cb
Diffstat (limited to 'simpleperf')
-rw-r--r-- | simpleperf/Android.bp | 1 | ||||
-rw-r--r-- | simpleperf/cmd_debug_unwind.cpp | 3 | ||||
-rw-r--r-- | simpleperf/cmd_dumprecord.cpp | 8 | ||||
-rw-r--r-- | simpleperf/cmd_merge.cpp | 2 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 135 | ||||
-rw-r--r-- | simpleperf/cmd_record_impl.h | 2 | ||||
-rw-r--r-- | simpleperf/cmd_record_test.cpp | 2 | ||||
-rw-r--r-- | simpleperf/record_file.h | 13 | ||||
-rw-r--r-- | simpleperf/record_file.proto | 31 | ||||
-rw-r--r-- | simpleperf/record_file_format.h | 11 | ||||
-rw-r--r-- | simpleperf/record_file_reader.cpp | 38 | ||||
-rw-r--r-- | simpleperf/record_file_test.cpp | 30 | ||||
-rw-r--r-- | simpleperf/record_file_writer.cpp | 29 |
13 files changed, 234 insertions, 71 deletions
diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp index 52e62a76..edb5323a 100644 --- a/simpleperf/Android.bp +++ b/simpleperf/Android.bp @@ -223,6 +223,7 @@ cc_defaults { "read_elf.cpp", "read_symbol_map.cpp", "record.cpp", + "record_file.proto", "record_file_reader.cpp", "record_file_writer.cpp", "report_sample.proto", diff --git a/simpleperf/cmd_debug_unwind.cpp b/simpleperf/cmd_debug_unwind.cpp index f5fecc46..53fd38b8 100644 --- a/simpleperf/cmd_debug_unwind.cpp +++ b/simpleperf/cmd_debug_unwind.cpp @@ -350,7 +350,8 @@ bool DebugUnwindCommand::WriteFeatureSections() { // Copy all feature sections except FEAT_FILE and FEAT_META_INFO, which require special handling. while (it != features.end() && it->first < PerfFileFormat::FEAT_FILE) { std::vector<char> data; - if (!reader_->ReadFeatureSection(it->first, &data) || !writer_->WriteFeature(it->first, data)) { + if (!reader_->ReadFeatureSection(it->first, &data) || + !writer_->WriteFeature(it->first, data.data(), data.size())) { return false; } ++it; diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp index 8e8bcd21..9d8cba90 100644 --- a/simpleperf/cmd_dumprecord.cpp +++ b/simpleperf/cmd_dumprecord.cpp @@ -502,6 +502,14 @@ bool DumpRecordCommand::DumpFeatureSection() { for (auto offset : record_file_reader_->ReadAuxTraceFeature()) { PrintIndented(2, "%" PRIu64 "\n", offset); } + } else if (feature == FEAT_DEBUG_UNWIND) { + PrintIndented(1, "debug_unwind:\n"); + if (auto opt_debug_unwind = record_file_reader_->ReadDebugUnwindFeature(); opt_debug_unwind) { + for (const DebugUnwindFile& file : opt_debug_unwind.value()) { + PrintIndented(2, "path: %s\n", file.path.c_str()); + PrintIndented(2, "size: %" PRIu64 "\n", file.size); + } + } } } return true; diff --git a/simpleperf/cmd_merge.cpp b/simpleperf/cmd_merge.cpp index 9d413083..d1ac6dca 100644 --- a/simpleperf/cmd_merge.cpp +++ b/simpleperf/cmd_merge.cpp @@ -333,7 +333,7 @@ class MergeCommand : public Command { feature == PerfFileFormat::FEAT_META_INFO || feature == PerfFileFormat::FEAT_CMDLINE) { std::vector<char> data; if (!readers_[0]->ReadFeatureSection(feature, &data) || - !writer_->WriteFeature(feature, data)) { + !writer_->WriteFeature(feature, data.data(), data.size())) { return false; } } else if (feature == PerfFileFormat::FEAT_BUILD_ID) { diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 6aa2a549..274ad0f3 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -37,6 +37,12 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#include <llvm/Support/MemoryBuffer.h> +#pragma clang diagnostic pop + #if defined(__ANDROID__) #include <android-base/properties.h> #endif @@ -256,8 +262,8 @@ class RecordCommand : public Command { " the stack data in samples. When the available space reaches critical level,\n" " it drops all samples. This option makes simpleperf not cut samples when the\n" " available space reaches low level.\n" -"--keep-failed-unwinding-result Keep reasons for failed-to-unwind samples for debugging\n" -"--keep-failed-unwinding-stack Keep stack data for failed-to-unwind samples for debugging\n" +"--keep-failed-unwinding-result Keep reasons for failed unwinding cases\n" +"--keep-failed-unwinding-debug-info Keep debug info for failed unwinding cases\n" "\n" "Sample filter options:\n" "--exclude-perf Exclude samples for simpleperf process.\n" @@ -364,7 +370,8 @@ RECORD_FILTER_OPTION_HELP_MSG bool DumpBuildIdFeature(); bool DumpFileFeature(); bool DumpMetaInfoFeature(bool kernel_symbols_available); - void CollectHitFileInfo(const SampleRecord& r); + bool DumpDebugUnwindFeature(const std::unordered_set<Dso*>& dso_set); + void CollectHitFileInfo(const SampleRecord& r, std::unordered_set<Dso*>* dso_set); std::unique_ptr<SampleSpeed> sample_speed_; bool system_wide_collection_; @@ -375,7 +382,7 @@ RECORD_FILTER_OPTION_HELP_MSG bool unwind_dwarf_callchain_; bool post_unwind_; bool keep_failed_unwinding_result_ = false; - bool keep_failed_unwinding_stack_ = false; + bool keep_failed_unwinding_debug_info_ = false; std::unique_ptr<OfflineUnwinder> offline_unwinder_; bool child_inherit_; double duration_in_sec_; @@ -868,7 +875,10 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args, } } keep_failed_unwinding_result_ = options.PullBoolValue("--keep-failed-unwinding-result"); - keep_failed_unwinding_stack_ = options.PullBoolValue("--keep-failed-unwinding-stack"); + keep_failed_unwinding_debug_info_ = options.PullBoolValue("--keep-failed-unwinding-debug-info"); + if (keep_failed_unwinding_debug_info_) { + keep_failed_unwinding_result_ = true; + } for (const OptionValue& value : options.PullValues("--kprobe")) { std::vector<std::string> cmds = android::base::Split(*value.str_value, ","); @@ -1575,7 +1585,7 @@ bool RecordCommand::KeepFailedUnwindingResult(const SampleRecord& r) { if (result.error_code != unwindstack::ERROR_NONE) { PerfSampleRegsUserType regs_user_data = {}; PerfSampleStackUserType stack_user_data = {}; - if (keep_failed_unwinding_stack_) { + if (keep_failed_unwinding_debug_info_) { regs_user_data = r.regs_user_data; stack_user_data = r.stack_user_data; } @@ -1696,9 +1706,7 @@ bool RecordCommand::JoinCallChains() { return reader->ReadDataSection(record_callback); } -namespace { - -void LoadSymbolMapFile(int pid, const std::string& package, ThreadTree* thread_tree) { +static void LoadSymbolMapFile(int pid, const std::string& package, ThreadTree* thread_tree) { // On Linux, symbol map files usually go to /tmp/perf-<pid>.map // On Android, there is no directory where any process can create files. // For now, use /data/local/tmp/perf-<pid>.map, which works for standalone programs, @@ -1713,8 +1721,6 @@ void LoadSymbolMapFile(int pid, const std::string& package, ThreadTree* thread_t } } -} // namespace - bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args) { // Read data section of perf.data to collect hit file information. thread_tree_.ClearThreadAndMap(); @@ -1726,6 +1732,9 @@ bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args) } std::unordered_set<int> loaded_symbol_maps; std::vector<uint64_t> auxtrace_offset; + std::unordered_set<Dso*> debug_unwinding_files; + bool failed_unwinding_sample = false; + auto callback = [&](const Record* r) { thread_tree_.Update(*r); if (r->type() == PERF_RECORD_SAMPLE) { @@ -1734,12 +1743,20 @@ bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args) if (loaded_symbol_maps.insert(sample->tid_data.pid).second) { LoadSymbolMapFile(sample->tid_data.pid, app_package_name_, &thread_tree_); } - CollectHitFileInfo(*sample); + if (failed_unwinding_sample) { + failed_unwinding_sample = false; + CollectHitFileInfo(*sample, &debug_unwinding_files); + } else { + CollectHitFileInfo(*sample, nullptr); + } } else if (r->type() == PERF_RECORD_AUXTRACE) { auto auxtrace = static_cast<const AuxTraceRecord*>(r); auxtrace_offset.emplace_back(auxtrace->location.file_offset - auxtrace->size()); + } else if (r->type() == SIMPLE_PERF_RECORD_UNWINDING_RESULT) { + failed_unwinding_sample = true; } }; + if (!record_file_writer_->ReadDataSection(callback)) { return false; } @@ -1751,6 +1768,9 @@ bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args) if (!auxtrace_offset.empty()) { feature_count++; } + if (keep_failed_unwinding_debug_info_) { + feature_count += 2; + } if (!record_file_writer_->BeginWriteFeatures(feature_count)) { return false; } @@ -1790,6 +1810,9 @@ bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args) if (!auxtrace_offset.empty() && !record_file_writer_->WriteAuxTraceFeature(auxtrace_offset)) { return false; } + if (keep_failed_unwinding_debug_info_ && !DumpDebugUnwindFeature(debug_unwinding_files)) { + return false; + } if (!record_file_writer_->EndWriteFeatures()) { return false; @@ -1875,55 +1898,47 @@ bool RecordCommand::DumpMetaInfoFeature(bool kernel_symbols_available) { return record_file_writer_->WriteMetaInfoFeature(info_map); } -void RecordCommand::CollectHitFileInfo(const SampleRecord& r) { +bool RecordCommand::DumpDebugUnwindFeature(const std::unordered_set<Dso*>& dso_set) { + DebugUnwindFeature debug_unwind_feature; + debug_unwind_feature.reserve(dso_set.size()); + for (const Dso* dso : dso_set) { + if (dso->type() != DSO_ELF_FILE) { + continue; + } + const std::string& filename = dso->GetDebugFilePath(); + std::unique_ptr<ElfFile> elf = ElfFile::Open(filename); + if (elf) { + llvm::MemoryBuffer* buffer = elf->GetMemoryBuffer(); + debug_unwind_feature.resize(debug_unwind_feature.size() + 1); + auto& debug_unwind_file = debug_unwind_feature.back(); + debug_unwind_file.path = filename; + debug_unwind_file.size = buffer->getBufferSize(); + if (!record_file_writer_->WriteFeature(PerfFileFormat::FEAT_DEBUG_UNWIND_FILE, + buffer->getBufferStart(), buffer->getBufferSize())) { + return false; + } + } + } + return record_file_writer_->WriteDebugUnwindFeature(debug_unwind_feature); +} + +void RecordCommand::CollectHitFileInfo(const SampleRecord& r, std::unordered_set<Dso*>* dso_set) { const ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid); - const MapEntry* map = thread_tree_.FindMap(thread, r.ip_data.ip, r.InKernel()); - Dso* dso = map->dso; - const Symbol* symbol; - if (dump_symbols_) { - symbol = thread_tree_.FindSymbol(map, r.ip_data.ip, nullptr, &dso); - if (!symbol->HasDumpId()) { - dso->CreateSymbolDumpId(symbol); - } - } - if (!dso->HasDumpId() && dso->type() != DSO_UNKNOWN_FILE) { - dso->CreateDumpId(); - } - if (r.sample_type & PERF_SAMPLE_CALLCHAIN) { - bool in_kernel = r.InKernel(); - bool first_ip = true; - for (uint64_t i = 0; i < r.callchain_data.ip_nr; ++i) { - uint64_t ip = r.callchain_data.ips[i]; - if (ip >= PERF_CONTEXT_MAX) { - switch (ip) { - case PERF_CONTEXT_KERNEL: - in_kernel = true; - break; - case PERF_CONTEXT_USER: - in_kernel = false; - break; - default: - LOG(DEBUG) << "Unexpected perf_context in callchain: " << std::hex << ip; - } - } else { - if (first_ip) { - first_ip = false; - // Remove duplication with sample ip. - if (ip == r.ip_data.ip) { - continue; - } - } - map = thread_tree_.FindMap(thread, ip, in_kernel); - dso = map->dso; - if (dump_symbols_) { - symbol = thread_tree_.FindSymbol(map, ip, nullptr, &dso); - if (!symbol->HasDumpId()) { - dso->CreateSymbolDumpId(symbol); - } - } - if (!dso->HasDumpId() && dso->type() != DSO_UNKNOWN_FILE) { - dso->CreateDumpId(); - } + size_t kernel_ip_count; + std::vector<uint64_t> ips = r.GetCallChain(&kernel_ip_count); + for (size_t i = 0; i < ips.size(); i++) { + const MapEntry* map = thread_tree_.FindMap(thread, ips[i], i < kernel_ip_count); + Dso* dso = map->dso; + if (dump_symbols_) { + const Symbol* symbol = thread_tree_.FindSymbol(map, ips[i], nullptr, &dso); + if (!symbol->HasDumpId()) { + dso->CreateSymbolDumpId(symbol); + } + } + if (!dso->HasDumpId() && dso->type() != DSO_UNKNOWN_FILE) { + dso->CreateDumpId(); + if (dso_set != nullptr) { + dso_set->insert(dso); } } } diff --git a/simpleperf/cmd_record_impl.h b/simpleperf/cmd_record_impl.h index fa43b1ca..6753ab92 100644 --- a/simpleperf/cmd_record_impl.h +++ b/simpleperf/cmd_record_impl.h @@ -55,7 +55,7 @@ inline const OptionFormatMap& GetRecordCmdOptionFormats() { {"-j", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}}, {"--keep-failed-unwinding-result", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}}, - {"--keep-failed-unwinding-stack", + {"--keep-failed-unwinding-debug-info", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}}, {"--kprobe", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::NOT_ALLOWED}}, {"-m", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}}, diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp index 3cf3f59f..b0e4e553 100644 --- a/simpleperf/cmd_record_test.cpp +++ b/simpleperf/cmd_record_test.cpp @@ -1111,5 +1111,5 @@ TEST(record_cmd, keep_failed_unwinding_result_option) { CreateProcesses(1, &workloads); std::string pid = std::to_string(workloads[0]->GetPid()); ASSERT_TRUE(RunRecordCmd( - {"-p", pid, "-g", "--keep-failed-unwinding-result", "--keep-failed-unwinding-stack"})); + {"-p", pid, "-g", "--keep-failed-unwinding-result", "--keep-failed-unwinding-debug-info"})); } diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h index 485ba9f0..cc29c028 100644 --- a/simpleperf/record_file.h +++ b/simpleperf/record_file.h @@ -22,6 +22,7 @@ #include <functional> #include <map> #include <memory> +#include <optional> #include <string> #include <unordered_map> #include <vector> @@ -52,6 +53,13 @@ struct FileFeature { DISALLOW_COPY_AND_ASSIGN(FileFeature); }; +struct DebugUnwindFile { + std::string path; + uint64_t size; +}; + +using DebugUnwindFeature = std::vector<DebugUnwindFile>; + // RecordFileWriter writes to a perf record file, like perf.data. // User should call RecordFileWriter::Close() to finish writing the file, otherwise the file will // be removed in RecordFileWriter::~RecordFileWriter(). @@ -77,7 +85,8 @@ class RecordFileWriter { bool WriteFileFeatures(const std::vector<Dso*>& dsos); bool WriteFileFeature(const FileFeature& file); bool WriteMetaInfoFeature(const std::unordered_map<std::string, std::string>& info_map); - bool WriteFeature(int feature, const std::vector<char>& data); + bool WriteDebugUnwindFeature(const DebugUnwindFeature& debug_unwind); + bool WriteFeature(int feature, const char* data, size_t size); bool EndWriteFeatures(); bool Close(); @@ -138,6 +147,7 @@ class RecordFileReader { return feature_section_descriptors_.find(feature) != feature_section_descriptors_.end(); } bool ReadFeatureSection(int feature, std::vector<char>* data); + bool ReadFeatureSection(int feature, std::string* data); // There are two ways to read records in data section: one is by calling // ReadDataSection(), and [callback] is called for each Record. the other @@ -167,6 +177,7 @@ class RecordFileReader { bool ReadFileFeature(size_t& read_pos, FileFeature* file); const std::unordered_map<std::string, std::string>& GetMetaInfoFeature() { return meta_info_; } + std::optional<DebugUnwindFeature> ReadDebugUnwindFeature(); void LoadBuildIdAndFileFeatures(ThreadTree& thread_tree); diff --git a/simpleperf/record_file.proto b/simpleperf/record_file.proto new file mode 100644 index 00000000..752c1f6b --- /dev/null +++ b/simpleperf/record_file.proto @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// message types used in perf.data. + +syntax = "proto3"; + +package simpleperf.proto; + + +message DebugUnwindFeature { + message File { + string path = 1; + uint64 size = 2; + } + + repeated File file = 1; +} diff --git a/simpleperf/record_file_format.h b/simpleperf/record_file_format.h index 0419cddf..07a489c8 100644 --- a/simpleperf/record_file_format.h +++ b/simpleperf/record_file_format.h @@ -66,6 +66,15 @@ meta_info feature section: keys in meta_info feature section include: simpleperf_version, +debug_unwind feature section: + message DebugUnwindSection from record_file.proto + +debug_unwind_file feature section: + data for file 1 + data for file 2 + ... + + The file list is stored in debug_unwind feature section. */ namespace simpleperf { @@ -97,6 +106,8 @@ enum { FEAT_SIMPLEPERF_START = 128, FEAT_FILE = FEAT_SIMPLEPERF_START, FEAT_META_INFO, + FEAT_DEBUG_UNWIND, + FEAT_DEBUG_UNWIND_FILE, FEAT_MAX_NUM = 256, }; diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp index eab95521..bba96830 100644 --- a/simpleperf/record_file_reader.cpp +++ b/simpleperf/record_file_reader.cpp @@ -25,6 +25,7 @@ #include "event_attr.h" #include "record.h" +#include "system/extras/simpleperf/record_file.pb.h" #include "utils.h" namespace simpleperf { @@ -54,6 +55,8 @@ static const std::map<int, std::string> feature_name_map = { {FEAT_AUXTRACE, "auxtrace"}, {FEAT_FILE, "file"}, {FEAT_META_INFO, "meta_info"}, + {FEAT_DEBUG_UNWIND, "debug_unwind"}, + {FEAT_DEBUG_UNWIND_FILE, "debug_unwind_file"}, }; std::string GetFeatureName(int feature_id) { @@ -386,6 +389,23 @@ bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) return true; } +bool RecordFileReader::ReadFeatureSection(int feature, std::string* data) { + const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors(); + auto it = section_map.find(feature); + if (it == section_map.end()) { + return false; + } + SectionDesc section = it->second; + data->resize(section.size); + if (section.size == 0) { + return true; + } + if (!ReadAtOffset(section.offset, data->data(), data->size())) { + return false; + } + return true; +} + std::vector<std::string> RecordFileReader::ReadCmdlineFeature() { std::vector<char> buf; if (!ReadFeatureSection(FEAT_CMDLINE, &buf)) { @@ -550,6 +570,24 @@ bool RecordFileReader::ReadMetaInfoFeature() { return true; } +std::optional<DebugUnwindFeature> RecordFileReader::ReadDebugUnwindFeature() { + if (feature_section_descriptors_.count(FEAT_DEBUG_UNWIND)) { + std::string s; + if (!ReadFeatureSection(FEAT_DEBUG_UNWIND, &s)) { + return std::nullopt; + } + proto::DebugUnwindFeature proto_debug_unwind; + proto_debug_unwind.ParseFromString(s); + DebugUnwindFeature debug_unwind(proto_debug_unwind.file_size()); + for (size_t i = 0; i < proto_debug_unwind.file_size(); i++) { + debug_unwind[i].path = proto_debug_unwind.file(i).path(); + debug_unwind[i].size = proto_debug_unwind.file(i).size(); + } + return debug_unwind; + } + return std::nullopt; +} + void RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) { std::vector<BuildIdRecord> records = ReadBuildIdFeature(); std::vector<std::pair<std::string, BuildId>> build_ids; diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp index 67ae0c52..20bacaa9 100644 --- a/simpleperf/record_file_test.cpp +++ b/simpleperf/record_file_test.cpp @@ -150,3 +150,33 @@ TEST_F(RecordFileTest, write_meta_info_feature_section) { ASSERT_TRUE(reader != nullptr); ASSERT_EQ(reader->GetMetaInfoFeature(), info_map); } + +TEST_F(RecordFileTest, write_debug_unwind_feature_section) { + // Write to a record file. + std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(tmpfile_.path); + ASSERT_TRUE(writer != nullptr); + AddEventType("cpu-cycles"); + ASSERT_TRUE(writer->WriteAttrSection(attr_ids_)); + + // Write debug_unwind feature section. + ASSERT_TRUE(writer->BeginWriteFeatures(1)); + DebugUnwindFeature debug_unwind(2); + debug_unwind[0].path = "file1"; + debug_unwind[0].size = 1000; + debug_unwind[1].path = "file2"; + debug_unwind[1].size = 2000; + ASSERT_TRUE(writer->WriteDebugUnwindFeature(debug_unwind)); + ASSERT_TRUE(writer->EndWriteFeatures()); + ASSERT_TRUE(writer->Close()); + + // Read from a record file. + std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path); + ASSERT_TRUE(reader != nullptr); + std::optional<DebugUnwindFeature> opt_debug_unwind = reader->ReadDebugUnwindFeature(); + ASSERT_TRUE(opt_debug_unwind.has_value()); + ASSERT_EQ(opt_debug_unwind.value().size(), debug_unwind.size()); + for (size_t i = 0; i < debug_unwind.size(); i++) { + ASSERT_EQ(opt_debug_unwind.value()[i].path, debug_unwind[i].path); + ASSERT_EQ(opt_debug_unwind.value()[i].size, debug_unwind[i].size); + } +} diff --git a/simpleperf/record_file_writer.cpp b/simpleperf/record_file_writer.cpp index f45885ce..ded69b8d 100644 --- a/simpleperf/record_file_writer.cpp +++ b/simpleperf/record_file_writer.cpp @@ -33,6 +33,7 @@ #include "event_attr.h" #include "perf_event.h" #include "record.h" +#include "system/extras/simpleperf/record_file.pb.h" #include "utils.h" namespace simpleperf { @@ -317,8 +318,8 @@ bool RecordFileWriter::WriteAuxTraceFeature(const std::vector<uint64_t>& auxtrac data.push_back(offset); data.push_back(AuxTraceRecord::Size()); } - return WriteFeatureBegin(FEAT_AUXTRACE) && Write(data.data(), data.size() * sizeof(uint64_t)) && - WriteFeatureEnd(FEAT_AUXTRACE); + return WriteFeature(FEAT_AUXTRACE, reinterpret_cast<char*>(data.data()), + data.size() * sizeof(uint64_t)); } bool RecordFileWriter::WriteFileFeatures(const std::vector<Dso*>& dsos) { @@ -390,7 +391,7 @@ bool RecordFileWriter::WriteFileFeature(const FileFeature& file) { } CHECK_EQ(buf.size(), static_cast<size_t>(p - buf.data())); - return WriteFeature(FEAT_FILE, buf); + return WriteFeature(FEAT_FILE, buf.data(), buf.size()); } bool RecordFileWriter::WriteMetaInfoFeature( @@ -406,11 +407,27 @@ bool RecordFileWriter::WriteMetaInfoFeature( MoveToBinaryFormat(pair.first.c_str(), pair.first.size() + 1, p); MoveToBinaryFormat(pair.second.c_str(), pair.second.size() + 1, p); } - return WriteFeature(FEAT_META_INFO, buf); + return WriteFeature(FEAT_META_INFO, buf.data(), buf.size()); } -bool RecordFileWriter::WriteFeature(int feature, const std::vector<char>& data) { - return WriteFeatureBegin(feature) && Write(data.data(), data.size()) && WriteFeatureEnd(feature); +bool RecordFileWriter::WriteDebugUnwindFeature(const DebugUnwindFeature& debug_unwind) { + GOOGLE_PROTOBUF_VERIFY_VERSION; + proto::DebugUnwindFeature proto_debug_unwind; + for (auto& file : debug_unwind) { + auto proto_file = proto_debug_unwind.add_file(); + proto_file->set_path(file.path); + proto_file->set_size(file.size); + } + std::string s; + if (!proto_debug_unwind.SerializeToString(&s)) { + LOG(ERROR) << "SerializeToString() failed"; + return false; + } + return WriteFeature(FEAT_DEBUG_UNWIND, s.data(), s.size()); +} + +bool RecordFileWriter::WriteFeature(int feature, const char* data, size_t size) { + return WriteFeatureBegin(feature) && Write(data, size) && WriteFeatureEnd(feature); } bool RecordFileWriter::WriteFeatureBegin(int feature) { |