summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2021-03-01 10:07:55 -0800
committerYabin Cui <yabinc@google.com>2021-03-01 18:10:36 -0800
commitfbbe9e5cf0407c1cec719e6d47c4d6f433be2cb5 (patch)
tree0a82fd4a047fb2c5f4652ae2f4816c2fac240960 /simpleperf
parentf4089ed75c62d0a092c9b5d5b22f8cafebf302b5 (diff)
downloadextras-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.bp1
-rw-r--r--simpleperf/cmd_debug_unwind.cpp3
-rw-r--r--simpleperf/cmd_dumprecord.cpp8
-rw-r--r--simpleperf/cmd_merge.cpp2
-rw-r--r--simpleperf/cmd_record.cpp135
-rw-r--r--simpleperf/cmd_record_impl.h2
-rw-r--r--simpleperf/cmd_record_test.cpp2
-rw-r--r--simpleperf/record_file.h13
-rw-r--r--simpleperf/record_file.proto31
-rw-r--r--simpleperf/record_file_format.h11
-rw-r--r--simpleperf/record_file_reader.cpp38
-rw-r--r--simpleperf/record_file_test.cpp30
-rw-r--r--simpleperf/record_file_writer.cpp29
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) {