summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2016-06-01 17:29:06 -0700
committerYabin Cui <yabinc@google.com>2016-06-03 15:24:03 -0700
commit4f41df6f68efc3b9b534014a8cfb6ee8ac4d5c91 (patch)
tree9cd04ed0e963ba2ce911087cf414df6cce35daf6
parent471ec606874a7224e304482f5fa9cc7d207d26b8 (diff)
downloadextras-4f41df6f68efc3b9b534014a8cfb6ee8ac4d5c91.tar.gz
simpleperf: dump tracing data when needed.
When monitoring tracepoint events, dump tracing data to perf.data can enable reporting on a different machine. Bug: 27403614 Change-Id: Ie1af624717a245cacbeb44b4c1bcd499fc9ad8db
-rw-r--r--simpleperf/Android.mk1
-rw-r--r--simpleperf/cmd_dumprecord.cpp16
-rw-r--r--simpleperf/cmd_record.cpp78
-rw-r--r--simpleperf/cmd_report.cpp62
-rw-r--r--simpleperf/cmd_stat.cpp5
-rw-r--r--simpleperf/environment.cpp4
-rw-r--r--simpleperf/event_attr.cpp2
-rw-r--r--simpleperf/event_selection_set.cpp22
-rw-r--r--simpleperf/event_selection_set.h4
-rw-r--r--simpleperf/record.cpp39
-rw-r--r--simpleperf/record.h16
-rw-r--r--simpleperf/record_file.h18
-rw-r--r--simpleperf/record_file_reader.cpp1
-rw-r--r--simpleperf/record_file_test.cpp22
-rw-r--r--simpleperf/tracing.cpp427
-rw-r--r--simpleperf/tracing.h79
-rw-r--r--simpleperf/utils.cpp23
-rw-r--r--simpleperf/utils.h4
18 files changed, 752 insertions, 71 deletions
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index b64af01b..2e4c512b 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -88,6 +88,7 @@ libsimpleperf_src_files := \
record.cpp \
record_file_reader.cpp \
thread_tree.cpp \
+ tracing.cpp \
utils.cpp \
libsimpleperf_src_files_linux := \
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index f19cfd3a..3e89b827 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -157,20 +157,14 @@ static const std::string GetFeatureName(int feature) {
}
void DumpRecordCommand::DumpAttrSection() {
- const std::vector<FileAttr>& attrs = record_file_reader_->AttrSection();
+ std::vector<AttrWithId> attrs = record_file_reader_->AttrSection();
for (size_t i = 0; i < attrs.size(); ++i) {
const auto& attr = attrs[i];
- printf("file_attr %zu:\n", i + 1);
- DumpPerfEventAttr(attr.attr, 1);
- printf(" ids[file_section]: offset %" PRId64 ", size %" PRId64 "\n", attr.ids.offset,
- attr.ids.size);
- std::vector<uint64_t> ids;
- if (!record_file_reader_->ReadIdsForAttr(attr, &ids)) {
- return;
- }
- if (!ids.empty()) {
+ printf("attr %zu:\n", i + 1);
+ DumpPerfEventAttr(*attr.attr, 1);
+ if (!attr.ids.empty()) {
printf(" ids:");
- for (const auto& id : ids) {
+ for (const auto& id : attr.ids) {
printf(" %" PRId64, id);
}
printf("\n");
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 3fa4cbdb..a989d37b 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -39,6 +39,7 @@
#include "record_file.h"
#include "scoped_signal_handler.h"
#include "thread_tree.h"
+#include "tracing.h"
#include "utils.h"
#include "workload.h"
@@ -56,6 +57,9 @@ static std::unordered_map<std::string, uint64_t> branch_sampling_type_map = {
static volatile bool signaled;
static void signal_handler(int) { signaled = true; }
+constexpr uint64_t DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT = 4000;
+constexpr uint64_t DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT = 1;
+
class RecordCommand : public Command {
public:
RecordCommand()
@@ -66,7 +70,9 @@ class RecordCommand : public Command {
" Gather sampling information when running [command].\n"
"-a System-wide collection.\n"
"-b Enable take branch stack sampling. Same as '-j any'\n"
-"-c count Set event sample period.\n"
+"-c count Set event sample period. It means recording one sample when\n"
+" [count] events happen. Can't be used with -f/-F option.\n"
+" For tracepoint events, the default option is -c 1.\n"
"--call-graph fp | dwarf[,<dump_stack_size>]\n"
" Enable call graph recording. Use frame pointer or dwarf debug\n"
" frame as the method to parse call graph in stack.\n"
@@ -84,7 +90,9 @@ class RecordCommand : public Command {
" Possible modifiers are:\n"
" u - monitor user space events only\n"
" k - monitor kernel space events only\n"
-"-f freq Set event sample frequency.\n"
+"-f freq Set event sample frequency. It means recording at most [freq]\n"
+" samples every second. For non-tracepoint events, the default\n"
+" option is -f 4000.\n"
"-F freq Same as '-f freq'.\n"
"-g Same as '--call-graph dwarf'.\n"
"-j branch_filter1,branch_filter2,...\n"
@@ -117,8 +125,10 @@ class RecordCommand : public Command {
"-t tid1,tid2,... Record events on existing threads. Mutually exclusive with -a.\n"
// clang-format on
),
- use_sample_freq_(true),
- sample_freq_(4000),
+ use_sample_freq_(false),
+ sample_freq_(0),
+ use_sample_period_(false),
+ sample_period_(0),
system_wide_collection_(false),
branch_sampling_(0),
fp_callchain_sampling_(false),
@@ -148,6 +158,7 @@ class RecordCommand : public Command {
std::unique_ptr<RecordFileWriter> CreateRecordFile(
const std::string& filename);
bool DumpKernelSymbol();
+ bool DumpTracingData();
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,
@@ -162,9 +173,9 @@ class RecordCommand : public Command {
void CollectHitFileInfo(Record* record);
std::pair<std::string, uint64_t> TestForEmbeddedElf(Dso* dso, uint64_t pgoff);
- // Use sample_freq_ when true, otherwise using sample_period_.
bool use_sample_freq_;
- uint64_t sample_freq_; // Sample 'sample_freq_' times per second.
+ uint64_t sample_freq_; // Sample 'sample_freq_' times per second.
+ bool use_sample_period_;
uint64_t sample_period_; // Sample once when 'sample_period_' events occur.
bool system_wide_collection_;
@@ -313,7 +324,7 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
LOG(ERROR) << "Invalid sample period: '" << args[i] << "'";
return false;
}
- use_sample_freq_ = false;
+ use_sample_period_ = true;
} else if (args[i] == "--call-graph") {
if (!NextArgumentOrError(args, &i)) {
return false;
@@ -434,6 +445,11 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
}
}
+ if (use_sample_freq_ && use_sample_period_) {
+ LOG(ERROR) << "-f option can't be used with -c option.";
+ return false;
+ }
+
if (!dwarf_callchain_sampling_) {
if (!unwind_dwarf_callchain_) {
LOG(ERROR)
@@ -482,6 +498,11 @@ bool RecordCommand::AddMeasuredEventType(const std::string& event_type_name) {
if (event_type_modifier == nullptr) {
return false;
}
+ for (const auto& type : measured_event_types_) {
+ if (type.name == event_type_modifier->name) {
+ return true;
+ }
+ }
measured_event_types_.push_back(*event_type_modifier);
return true;
}
@@ -492,10 +513,20 @@ bool RecordCommand::SetEventSelection() {
return false;
}
}
- if (use_sample_freq_) {
- event_selection_set_.SetSampleFreq(sample_freq_);
- } else {
- event_selection_set_.SetSamplePeriod(sample_period_);
+ for (auto& event_type : measured_event_types_) {
+ if (use_sample_freq_) {
+ event_selection_set_.SetSampleFreq(event_type, sample_freq_);
+ } else if (use_sample_period_) {
+ event_selection_set_.SetSamplePeriod(event_type, sample_period_);
+ } else {
+ if (event_type.event_type.type == PERF_TYPE_TRACEPOINT) {
+ event_selection_set_.SetSamplePeriod(
+ event_type, DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT);
+ } else {
+ event_selection_set_.SetSampleFreq(
+ event_type, DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT);
+ }
+ }
}
event_selection_set_.SampleIdAll();
if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
@@ -527,6 +558,9 @@ bool RecordCommand::CreateAndInitRecordFile() {
if (!DumpKernelSymbol()) {
return false;
}
+ if (!DumpTracingData()) {
+ return false;
+ }
if (!DumpKernelAndModuleMmaps(attr, event_id)) {
return false;
}
@@ -591,6 +625,28 @@ bool RecordCommand::DumpKernelSymbol() {
return true;
}
+bool RecordCommand::DumpTracingData() {
+ bool has_tracepoint = false;
+ for (const auto& type : measured_event_types_) {
+ if (type.event_type.type == PERF_TYPE_TRACEPOINT) {
+ has_tracepoint = true;
+ break;
+ }
+ }
+ if (!has_tracepoint) {
+ return true; // No need to dump tracing data.
+ }
+ std::vector<char> tracing_data;
+ if (!GetTracingData(measured_event_types_, &tracing_data)) {
+ return false;
+ }
+ TracingDataRecord record = TracingDataRecord::Create(std::move(tracing_data));
+ if (!ProcessRecord(&record)) {
+ return false;
+ }
+ return true;
+}
+
bool RecordCommand::DumpKernelAndModuleMmaps(const perf_event_attr* attr,
uint64_t event_id) {
KernelMmap kernel_mmap;
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index 863057dd..90e07781 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -39,6 +39,7 @@
#include "record_file.h"
#include "sample_tree.h"
#include "thread_tree.h"
+#include "tracing.h"
#include "utils.h"
namespace {
@@ -224,6 +225,12 @@ using ReportCmdSampleTreeSorter = SampleTreeSorter<SampleEntry>;
using ReportCmdSampleTreeDisplayer =
SampleTreeDisplayer<SampleEntry, SampleTree>;
+struct EventAttrWithName {
+ perf_event_attr attr;
+ std::string name;
+ std::vector<uint64_t> event_ids;
+};
+
class ReportCommand : public Command {
public:
ReportCommand()
@@ -273,13 +280,14 @@ class ReportCommand : public Command {
bool ReadFeaturesFromRecordFile();
bool ReadSampleTreeFromRecordFile();
bool ProcessRecord(std::unique_ptr<Record> record);
+ bool ProcessTracingData(const std::vector<char>& data);
bool PrintReport();
void PrintReportContext(FILE* fp);
std::string record_filename_;
ArchType record_file_arch_;
std::unique_ptr<RecordFileReader> record_file_reader_;
- std::vector<perf_event_attr> event_attrs_;
+ std::vector<EventAttrWithName> event_attrs_;
ThreadTree thread_tree_;
SampleTree sample_tree_;
std::unique_ptr<ReportCmdSampleTreeBuilder> sample_tree_builder_;
@@ -499,15 +507,22 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
}
bool ReportCommand::ReadEventAttrFromRecordFile() {
- const std::vector<PerfFileFormat::FileAttr>& file_attrs =
- record_file_reader_->AttrSection();
- for (const auto& attr : file_attrs) {
- event_attrs_.push_back(attr.attr);
+ std::vector<AttrWithId> attrs = record_file_reader_->AttrSection();
+ for (const auto& attr_with_id : attrs) {
+ EventAttrWithName attr;
+ attr.attr = *attr_with_id.attr;
+ attr.event_ids = attr_with_id.ids;
+ const EventType* event_type =
+ FindEventTypeByConfig(attr.attr.type, attr.attr.config);
+ if (event_type != nullptr) {
+ attr.name = event_type->name;
+ }
+ event_attrs_.push_back(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) {
+ if ((attr.attr.sample_type & PERF_SAMPLE_BRANCH_STACK) == 0) {
has_branch_stack = false;
break;
}
@@ -558,6 +573,16 @@ bool ReportCommand::ReadFeaturesFromRecordFile() {
}
}
}
+ if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_TRACING_DATA)) {
+ std::vector<char> tracing_data;
+ if (!record_file_reader_->ReadFeatureSection(
+ PerfFileFormat::FEAT_TRACING_DATA, &tracing_data)) {
+ return false;
+ }
+ if (!ProcessTracingData(tracing_data)) {
+ return false;
+ }
+ }
return true;
}
@@ -586,6 +611,22 @@ bool ReportCommand::ProcessRecord(std::unique_ptr<Record> record) {
if (record->type() == PERF_RECORD_SAMPLE) {
sample_tree_builder_->ProcessSampleRecord(
*static_cast<const SampleRecord*>(record.get()));
+ } else if (record->type() == PERF_RECORD_TRACING_DATA) {
+ const auto& r = *static_cast<TracingDataRecord*>(record.get());
+ if (!ProcessTracingData(r.data)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ReportCommand::ProcessTracingData(const std::vector<char>& data) {
+ Tracing tracing(data);
+ for (auto& attr : event_attrs_) {
+ if (attr.attr.type == PERF_TYPE_TRACEPOINT) {
+ uint64_t trace_event_id = attr.attr.config;
+ attr.name = tracing.GetTracingEventNameHavingId(trace_event_id);
+ }
}
return true;
}
@@ -618,13 +659,8 @@ void ReportCommand::PrintReportContext(FILE* report_fp) {
}
fprintf(report_fp, "Arch: %s\n", GetArchString(record_file_arch_).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, "Event: %s (type %u, config %llu)\n", attr.name.c_str(),
+ attr.attr.type, attr.attr.config);
}
fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree_.total_samples);
fprintf(report_fp, "Event count: %" PRIu64 "\n\n", sample_tree_.total_period);
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index dca7ed05..15e74658 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -240,6 +240,11 @@ bool StatCommand::AddMeasuredEventType(const std::string& event_type_name) {
if (event_type_modifier == nullptr) {
return false;
}
+ for (const auto& type : measured_event_types_) {
+ if (type.name == event_type_modifier->name) {
+ return true;
+ }
+ }
measured_event_types_.push_back(*event_type_modifier);
return true;
}
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index 798d08dc..7829110d 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -424,6 +424,10 @@ static const char* GetLimitLevelDescription(int limit_level) {
}
bool CheckPerfEventLimit() {
+ // root is not limited by /proc/sys/kernel/perf_event_paranoid.
+ if (IsRoot()) {
+ return true;
+ }
int limit_level;
if (!ReadPerfEventParanoid(&limit_level)) {
return false;
diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp
index 92226335..12ab1ada 100644
--- a/simpleperf/event_attr.cpp
+++ b/simpleperf/event_attr.cpp
@@ -91,8 +91,6 @@ perf_event_attr CreateDefaultPerfEventAttr(const EventType& event_type) {
PERF_SAMPLE_CPU | PERF_SAMPLE_ID;
if (attr.type == PERF_TYPE_TRACEPOINT) {
- attr.sample_freq = 0;
- attr.sample_period = 1;
// Tracepoint information are stored in raw data in sample records.
attr.sample_type |= PERF_SAMPLE_RAW;
}
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 49ccd0d8..92e559c5 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -112,20 +112,18 @@ void EventSelectionSet::SampleIdAll() {
}
}
-void EventSelectionSet::SetSampleFreq(uint64_t sample_freq) {
- for (auto& selection : selections_) {
- perf_event_attr& attr = selection.event_attr;
- attr.freq = 1;
- attr.sample_freq = sample_freq;
- }
+void EventSelectionSet::SetSampleFreq(const EventTypeAndModifier& event_type_modifier, uint64_t sample_freq) {
+ EventSelection* sel = FindSelectionByType(event_type_modifier);
+ CHECK(sel != nullptr);
+ sel->event_attr.freq = 1;
+ sel->event_attr.sample_freq = sample_freq;
}
-void EventSelectionSet::SetSamplePeriod(uint64_t sample_period) {
- for (auto& selection : selections_) {
- perf_event_attr& attr = selection.event_attr;
- attr.freq = 0;
- attr.sample_period = sample_period;
- }
+void EventSelectionSet::SetSamplePeriod(const EventTypeAndModifier& event_type_modifier, uint64_t sample_period) {
+ EventSelection* sel = FindSelectionByType(event_type_modifier);
+ CHECK(sel != nullptr);
+ sel->event_attr.freq = 0;
+ sel->event_attr.sample_period = sample_period;
}
bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) {
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index d2e2511a..3cfdb46b 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -64,8 +64,8 @@ class EventSelectionSet {
void SetEnableOnExec(bool enable);
bool GetEnableOnExec();
void SampleIdAll();
- void SetSampleFreq(uint64_t sample_freq);
- void SetSamplePeriod(uint64_t sample_period);
+ void SetSampleFreq(const EventTypeAndModifier& event_type_modifier, uint64_t sample_freq);
+ void SetSamplePeriod(const EventTypeAndModifier& event_type_modifier, uint64_t sample_period);
bool SetBranchSampling(uint64_t branch_sample_type);
void EnableFpCallChainSampling();
bool EnableDwarfCallChainSampling(uint32_t dump_stack_size);
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index a9cf67ce..0f1a8f44 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -26,6 +26,7 @@
#include "dso.h"
#include "environment.h"
#include "perf_regs.h"
+#include "tracing.h"
#include "utils.h"
static std::string RecordTypeToString(int record_type) {
@@ -41,6 +42,7 @@ static std::string RecordTypeToString(int record_type) {
{PERF_RECORD_SAMPLE, "sample"},
{PERF_RECORD_BUILD_ID, "build_id"},
{PERF_RECORD_MMAP2, "mmap2"},
+ {PERF_RECORD_TRACING_DATA, "tracing_data"},
{SIMPLE_PERF_RECORD_KERNEL_SYMBOL, "kernel_symbol"},
{SIMPLE_PERF_RECORD_DSO, "dso"},
{SIMPLE_PERF_RECORD_SYMBOL, "symbol"},
@@ -790,6 +792,41 @@ SymbolRecord SymbolRecord::Create(uint64_t addr, uint64_t len,
return record;
}
+TracingDataRecord::TracingDataRecord(const perf_event_header* pheader) : Record(pheader) {
+ const char* p = reinterpret_cast<const char*>(pheader + 1);
+ const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+ uint32_t size;
+ MoveFromBinaryFormat(size, p);
+ data.resize(size);
+ memcpy(data.data(), p, size);
+ p += ALIGN(size, 64);
+ CHECK_EQ(p, end);
+}
+
+std::vector<char> TracingDataRecord::BinaryFormat() const {
+ std::vector<char> buf(size());
+ char* p = buf.data();
+ MoveToBinaryFormat(header, p);
+ uint32_t size = static_cast<uint32_t>(data.size());
+ MoveToBinaryFormat(size, p);
+ memcpy(p, data.data(), size);
+ return buf;
+}
+
+void TracingDataRecord::DumpData(size_t indent) const {
+ Tracing tracing(data);
+ tracing.Dump(indent);
+}
+
+TracingDataRecord TracingDataRecord::Create(std::vector<char> tracing_data) {
+ TracingDataRecord record;
+ record.SetTypeAndMisc(PERF_RECORD_TRACING_DATA, 0);
+ record.data = std::move(tracing_data);
+ record.SetSize(record.header_size() + sizeof(uint32_t) +
+ ALIGN(record.data.size(), 64));
+ return record;
+}
+
UnknownRecord::UnknownRecord(const perf_event_header* pheader)
: Record(pheader) {
const char* p = reinterpret_cast<const char*>(pheader + 1);
@@ -822,6 +859,8 @@ std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
return std::unique_ptr<Record>(new ForkRecord(attr, pheader));
case PERF_RECORD_SAMPLE:
return std::unique_ptr<Record>(new SampleRecord(attr, pheader));
+ case PERF_RECORD_TRACING_DATA:
+ return std::unique_ptr<Record>(new TracingDataRecord(pheader));
case SIMPLE_PERF_RECORD_KERNEL_SYMBOL:
return std::unique_ptr<Record>(new KernelSymbolRecord(pheader));
case SIMPLE_PERF_RECORD_DSO:
diff --git a/simpleperf/record.h b/simpleperf/record.h
index 64789a68..f145e80f 100644
--- a/simpleperf/record.h
+++ b/simpleperf/record.h
@@ -380,14 +380,26 @@ struct SymbolRecord : public Record {
static SymbolRecord Create(uint64_t addr, uint64_t len,
const std::string& name, uint64_t dso_id);
+ protected:
+ void DumpData(size_t indent) const override;
+};
+
+struct TracingDataRecord : public Record {
+ std::vector<char> data;
+
+ TracingDataRecord() {
+ }
+
+ TracingDataRecord(const perf_event_header* pheader);
+ std::vector<char> BinaryFormat() const override;
+ static TracingDataRecord Create(std::vector<char> tracing_data);
protected:
void DumpData(size_t indent) const override;
};
// UnknownRecord is used for unknown record types, it makes sure all unknown
-// records
-// are not changed when modifying perf.data.
+// records are not changed when modifying perf.data.
struct UnknownRecord : public Record {
std::vector<char> data;
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index 3005dee6..a0af30e4 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -100,15 +100,22 @@ class RecordFileReader {
return header_;
}
- const std::vector<PerfFileFormat::FileAttr>& AttrSection() const {
- return file_attrs_;
+ std::vector<AttrWithId> AttrSection() const {
+ std::vector<AttrWithId> result(file_attrs_.size());
+ for (size_t i = 0; i < file_attrs_.size(); ++i) {
+ result[i].attr = &file_attrs_[i].attr;
+ result[i].ids = event_ids_for_file_attrs_[i];
+ }
+ return result;
}
const std::map<int, PerfFileFormat::SectionDesc>& FeatureSectionDescriptors() const {
return feature_section_descriptors_;
}
-
- bool ReadIdsForAttr(const PerfFileFormat::FileAttr& attr, std::vector<uint64_t>* ids);
+ bool HasFeature(int feature) const {
+ return feature_section_descriptors_.find(feature) != feature_section_descriptors_.end();
+ }
+ bool ReadFeatureSection(int feature, std::vector<char>* data);
// If sorted is true, sort records before passing them to callback function.
bool ReadDataSection(std::function<bool(std::unique_ptr<Record>)> callback, bool sorted = true);
std::vector<std::string> ReadCmdlineFeature();
@@ -123,8 +130,8 @@ class RecordFileReader {
RecordFileReader(const std::string& filename, FILE* fp);
bool ReadHeader();
bool ReadAttrSection();
+ bool ReadIdsForAttr(const PerfFileFormat::FileAttr& attr, std::vector<uint64_t>* ids);
bool ReadFeatureSectionDescriptors();
- bool ReadFeatureSection(int feature, std::vector<char>* data);
std::unique_ptr<Record> ReadRecord();
const std::string filename_;
@@ -132,6 +139,7 @@ class RecordFileReader {
PerfFileFormat::FileHeader header_;
std::vector<PerfFileFormat::FileAttr> file_attrs_;
+ std::vector<std::vector<uint64_t>> event_ids_for_file_attrs_;
std::unordered_map<uint64_t, perf_event_attr*> event_id_to_attr_map_;
std::map<int, PerfFileFormat::SectionDesc> feature_section_descriptors_;
diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp
index 9b97936f..b124d281 100644
--- a/simpleperf/record_file_reader.cpp
+++ b/simpleperf/record_file_reader.cpp
@@ -118,6 +118,7 @@ bool RecordFileReader::ReadAttrSection() {
if (!ReadIdsForAttr(file_attrs_[i], &ids)) {
return false;
}
+ event_ids_for_file_attrs_.push_back(ids);
for (auto id : ids) {
event_id_to_attr_map_[id] = &file_attrs_[i].attr;
}
diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp
index 4cc9e50e..f9707ef8 100644
--- a/simpleperf/record_file_test.cpp
+++ b/simpleperf/record_file_test.cpp
@@ -79,12 +79,10 @@ TEST_F(RecordFileTest, smoke) {
// Read from a record file.
std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
ASSERT_TRUE(reader != nullptr);
- const std::vector<FileAttr>& file_attrs = reader->AttrSection();
- ASSERT_EQ(1u, file_attrs.size());
- ASSERT_EQ(0, memcmp(&file_attrs[0].attr, attr_ids_[0].attr, sizeof(perf_event_attr)));
- std::vector<uint64_t> ids;
- ASSERT_TRUE(reader->ReadIdsForAttr(file_attrs[0], &ids));
- ASSERT_EQ(ids, attr_ids_[0].ids);
+ std::vector<AttrWithId> attrs = reader->AttrSection();
+ ASSERT_EQ(1u, attrs.size());
+ ASSERT_EQ(0, memcmp(attrs[0].attr, attr_ids_[0].attr, sizeof(perf_event_attr)));
+ ASSERT_EQ(attrs[0].ids, attr_ids_[0].ids);
// Read and check data section.
std::vector<std::unique_ptr<Record>> records = reader->DataSection();
@@ -152,12 +150,10 @@ TEST_F(RecordFileTest, record_more_than_one_attr) {
// Read from a record file.
std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
ASSERT_TRUE(reader != nullptr);
- const std::vector<FileAttr>& file_attrs = reader->AttrSection();
- ASSERT_EQ(3u, file_attrs.size());
- for (size_t i = 0; i < file_attrs.size(); ++i) {
- ASSERT_EQ(0, memcmp(&file_attrs[i].attr, attr_ids_[i].attr, sizeof(perf_event_attr)));
- std::vector<uint64_t> ids;
- ASSERT_TRUE(reader->ReadIdsForAttr(file_attrs[i], &ids));
- ASSERT_EQ(ids, attr_ids_[i].ids);
+ std::vector<AttrWithId> attrs = reader->AttrSection();
+ ASSERT_EQ(3u, attrs.size());
+ for (size_t i = 0; i < attrs.size(); ++i) {
+ ASSERT_EQ(0, memcmp(attrs[i].attr, attr_ids_[i].attr, sizeof(perf_event_attr)));
+ ASSERT_EQ(attrs[i].ids, attr_ids_[i].ids);
}
}
diff --git a/simpleperf/tracing.cpp b/simpleperf/tracing.cpp
new file mode 100644
index 00000000..1757e05d
--- /dev/null
+++ b/simpleperf/tracing.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "tracing.h"
+
+#include <string.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "perf_event.h"
+#include "utils.h"
+
+const char TRACING_INFO_MAGIC[10] = {23, 8, 68, 't', 'r',
+ 'a', 'c', 'i', 'n', 'g'};
+
+template <class T>
+void AppendData(std::vector<char>& data, const T& s) {
+ const char* p = reinterpret_cast<const char*>(&s);
+ data.insert(data.end(), p, p + sizeof(T));
+}
+
+static void AppendData(std::vector<char>& data, const char* s) {
+ data.insert(data.end(), s, s + strlen(s) + 1);
+}
+
+template <>
+void AppendData(std::vector<char>& data, const std::string& s) {
+ data.insert(data.end(), s.c_str(), s.c_str() + s.size() + 1);
+}
+
+template <>
+void MoveFromBinaryFormat(std::string& data, const char*& p) {
+ data.clear();
+ while (*p != '\0') {
+ data.push_back(*p++);
+ }
+ p++;
+}
+
+static void AppendFile(std::vector<char>& data, const std::string& file,
+ uint32_t file_size_bytes = 8) {
+ if (file_size_bytes == 8) {
+ uint64_t file_size = file.size();
+ AppendData(data, file_size);
+ } else if (file_size_bytes == 4) {
+ uint32_t file_size = file.size();
+ AppendData(data, file_size);
+ }
+ data.insert(data.end(), file.begin(), file.end());
+}
+
+static void DetachFile(const char*& p, std::string& file,
+ uint32_t file_size_bytes = 8) {
+ uint64_t file_size = ConvertBytesToValue(p, file_size_bytes);
+ p += file_size_bytes;
+ file.clear();
+ file.insert(file.end(), p, p + file_size);
+ p += file_size;
+}
+
+struct TraceType {
+ std::string system;
+ std::string name;
+};
+
+class TracingFile {
+ public:
+ TracingFile();
+ bool RecordHeaderFiles();
+ void RecordFtraceFiles(const std::vector<TraceType>& trace_types);
+ bool RecordEventFiles(const std::vector<TraceType>& trace_types);
+ bool RecordKallsymsFile();
+ bool RecordPrintkFormatsFile();
+ std::vector<char> BinaryFormat() const;
+ void LoadFromBinary(const std::vector<char>& data);
+ void Dump(size_t indent) const;
+ std::vector<TracingFormat> LoadTracingFormatsFromEventFiles() const;
+ const std::string& GetKallsymsFile() const { return kallsyms_file; }
+ uint32_t GetPageSize() const { return page_size; }
+
+ private:
+ char magic[10];
+ std::string version;
+ char endian;
+ uint8_t size_of_long;
+ uint32_t page_size;
+ std::string header_page_file;
+ std::string header_event_file;
+
+ std::vector<std::string> ftrace_format_files;
+ // pair of system, format_file_data.
+ std::vector<std::pair<std::string, std::string>> event_format_files;
+
+ std::string kallsyms_file;
+ std::string printk_formats_file;
+};
+
+TracingFile::TracingFile() {
+ memcpy(magic, TRACING_INFO_MAGIC, sizeof(TRACING_INFO_MAGIC));
+ version = "0.5";
+ endian = 0;
+ size_of_long = static_cast<int>(sizeof(long));
+ page_size = static_cast<uint32_t>(::GetPageSize());
+}
+
+bool TracingFile::RecordHeaderFiles() {
+ if (!android::base::ReadFileToString(
+ "/sys/kernel/debug/tracing/events/header_page", &header_page_file)) {
+ PLOG(ERROR)
+ << "failed to read /sys/kernel/debug/tracing/events/header_page";
+ return false;
+ }
+ if (!android::base::ReadFileToString(
+ "/sys/kernel/debug/tracing/events/header_event",
+ &header_event_file)) {
+ PLOG(ERROR)
+ << "failed to read /sys/kernel/debug/tracing/events/header_event";
+ return false;
+ }
+ return true;
+}
+
+void TracingFile::RecordFtraceFiles(const std::vector<TraceType>& trace_types) {
+ for (const auto& type : trace_types) {
+ std::string format_path = android::base::StringPrintf(
+ "/sys/kernel/debug/tracing/events/ftrace/%s/format", type.name.c_str());
+ std::string format_data;
+ if (android::base::ReadFileToString(format_path, &format_data)) {
+ ftrace_format_files.push_back(std::move(format_data));
+ }
+ }
+}
+
+bool TracingFile::RecordEventFiles(const std::vector<TraceType>& trace_types) {
+ for (const auto& type : trace_types) {
+ std::string format_path = android::base::StringPrintf(
+ "/sys/kernel/debug/tracing/events/%s/%s/format", type.system.c_str(),
+ type.name.c_str());
+ std::string format_data;
+ if (!android::base::ReadFileToString(format_path, &format_data)) {
+ PLOG(ERROR) << "failed to read " << format_path;
+ return false;
+ }
+ event_format_files.push_back(
+ std::make_pair(type.system, std::move(format_data)));
+ }
+ return true;
+}
+
+bool TracingFile::RecordPrintkFormatsFile() {
+ if (!android::base::ReadFileToString(
+ "/sys/kernel/debug/tracing/printk_formats", &printk_formats_file)) {
+ PLOG(ERROR) << "failed to read /sys/kernel/debug/tracing/printk_formats";
+ return false;
+ }
+ return true;
+}
+
+std::vector<char> TracingFile::BinaryFormat() const {
+ std::vector<char> ret;
+ ret.insert(ret.end(), magic, magic + sizeof(magic));
+ AppendData(ret, version);
+ ret.push_back(endian);
+ AppendData(ret, size_of_long);
+ AppendData(ret, page_size);
+ AppendData(ret, "header_page");
+ AppendFile(ret, header_page_file);
+ AppendData(ret, "header_event");
+ AppendFile(ret, header_event_file);
+ int count = static_cast<int>(ftrace_format_files.size());
+ AppendData(ret, count);
+ for (const auto& format : ftrace_format_files) {
+ AppendFile(ret, format);
+ }
+ count = static_cast<int>(event_format_files.size());
+ AppendData(ret, count);
+ for (const auto& pair : event_format_files) {
+ AppendData(ret, pair.first);
+ AppendData(ret, 1);
+ AppendFile(ret, pair.second);
+ }
+ AppendFile(ret, kallsyms_file, 4);
+ AppendFile(ret, printk_formats_file, 4);
+ return ret;
+}
+
+void TracingFile::LoadFromBinary(const std::vector<char>& data) {
+ const char* p = data.data();
+ const char* end = data.data() + data.size();
+ CHECK(memcmp(p, magic, sizeof(magic)) == 0);
+ p += sizeof(magic);
+ MoveFromBinaryFormat(version, p);
+ MoveFromBinaryFormat(endian, p);
+ MoveFromBinaryFormat(size_of_long, p);
+ MoveFromBinaryFormat(page_size, p);
+ std::string filename;
+ MoveFromBinaryFormat(filename, p);
+ CHECK_EQ(filename, "header_page");
+ DetachFile(p, header_page_file);
+ MoveFromBinaryFormat(filename, p);
+ CHECK_EQ(filename, "header_event");
+ DetachFile(p, header_event_file);
+ uint32_t count;
+ MoveFromBinaryFormat(count, p);
+ ftrace_format_files.resize(count);
+ for (uint32_t i = 0; i < count; ++i) {
+ DetachFile(p, ftrace_format_files[i]);
+ }
+ MoveFromBinaryFormat(count, p);
+ event_format_files.clear();
+ for (uint32_t i = 0; i < count; ++i) {
+ std::string system;
+ MoveFromBinaryFormat(system, p);
+ uint32_t count_in_system;
+ MoveFromBinaryFormat(count_in_system, p);
+ for (uint32_t i = 0; i < count_in_system; ++i) {
+ std::string format;
+ DetachFile(p, format);
+ event_format_files.push_back(std::make_pair(system, std::move(format)));
+ }
+ }
+ DetachFile(p, kallsyms_file, 4);
+ DetachFile(p, printk_formats_file, 4);
+ CHECK_EQ(p, end);
+}
+
+void TracingFile::Dump(size_t indent) const {
+ PrintIndented(indent, "tracing data:\n");
+ PrintIndented(indent + 1, "magic: ");
+ for (size_t i = 0; i < 3u; ++i) {
+ printf("0x%x ", magic[i]);
+ }
+ for (size_t i = 3; i < sizeof(magic); ++i) {
+ printf("%c", magic[i]);
+ }
+ printf("\n");
+ PrintIndented(indent + 1, "version: %s\n", version.c_str());
+ PrintIndented(indent + 1, "endian: %d\n", endian);
+ PrintIndented(indent + 1, "header_page:\n%s\n\n", header_page_file.c_str());
+ PrintIndented(indent + 1, "header_event:\n%s\n\n", header_event_file.c_str());
+ for (size_t i = 0; i < ftrace_format_files.size(); ++i) {
+ PrintIndented(indent + 1, "ftrace format file %zu/%zu:\n%s\n\n", i + 1,
+ ftrace_format_files.size(), ftrace_format_files[i].c_str());
+ }
+ for (size_t i = 0; i < event_format_files.size(); ++i) {
+ PrintIndented(indent + 1, "event format file %zu/%zu %s:\n%s\n\n", i + 1,
+ event_format_files.size(),
+ event_format_files[i].first.c_str(),
+ event_format_files[i].second.c_str());
+ }
+ PrintIndented(indent + 1, "kallsyms:\n%s\n\n", kallsyms_file.c_str());
+ PrintIndented(indent + 1, "printk_formats:\n%s\n\n",
+ printk_formats_file.c_str());
+}
+
+enum class FormatParsingState {
+ READ_NAME,
+ READ_ID,
+ READ_FIELDS,
+ READ_PRINTFMT,
+};
+
+// Parse lines like: field:char comm[16]; offset:8; size:16; signed:1;
+static TracingField ParseTracingField(const std::string& s) {
+ TracingField field;
+ size_t start = 0;
+ std::string name;
+ std::string value;
+ for (size_t i = 0; i < s.size(); ++i) {
+ if (!isspace(s[i]) && (i == 0 || isspace(s[i - 1]))) {
+ start = i;
+ } else if (s[i] == ':') {
+ name = s.substr(start, i - start);
+ start = i + 1;
+ } else if (s[i] == ';') {
+ value = s.substr(start, i - start);
+ if (name == "field") {
+ size_t pos = value.find_first_of('[');
+ if (pos == std::string::npos) {
+ field.name = value;
+ field.elem_count = 1;
+ } else {
+ field.name = value.substr(0, pos);
+ field.elem_count =
+ static_cast<size_t>(strtoull(&value[pos + 1], nullptr, 10));
+ }
+ } else if (name == "offset") {
+ field.offset =
+ static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
+ } else if (name == "size") {
+ size_t size = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
+ CHECK_EQ(size % field.elem_count, 0u);
+ field.elem_size = size / field.elem_count;
+ } else if (name == "signed") {
+ int is_signed = static_cast<int>(strtoull(value.c_str(), nullptr, 10));
+ field.is_signed = (is_signed == 1);
+ }
+ }
+ }
+ return field;
+}
+
+std::vector<TracingFormat> TracingFile::LoadTracingFormatsFromEventFiles()
+ const {
+ std::vector<TracingFormat> formats;
+ for (const auto& pair : event_format_files) {
+ TracingFormat format;
+ format.system_name = pair.first;
+ std::vector<std::string> strs = android::base::Split(pair.second, "\n");
+ FormatParsingState state = FormatParsingState::READ_NAME;
+ for (const auto& s : strs) {
+ if (state == FormatParsingState::READ_NAME) {
+ size_t pos = s.find_first_of("name:");
+ if (pos != std::string::npos) {
+ format.name = android::base::Trim(s.substr(pos + strlen("name:")));
+ state = FormatParsingState::READ_ID;
+ }
+ } else if (state == FormatParsingState::READ_ID) {
+ size_t pos = s.find_first_of("ID:");
+ if (pos != std::string::npos) {
+ format.id =
+ strtoull(s.substr(pos + strlen("ID:")).c_str(), nullptr, 10);
+ state = FormatParsingState::READ_FIELDS;
+ }
+ } else if (state == FormatParsingState::READ_FIELDS) {
+ size_t pos = s.find_first_of("field:");
+ if (pos != std::string::npos) {
+ TracingField field = ParseTracingField(s);
+ format.fields.push_back(field);
+ }
+ }
+ }
+ formats.push_back(format);
+ }
+ return formats;
+}
+
+Tracing::Tracing(const std::vector<char>& data) {
+ tracing_file_ = new TracingFile;
+ tracing_file_->LoadFromBinary(data);
+}
+
+Tracing::~Tracing() { delete tracing_file_; }
+
+void Tracing::Dump(size_t indent) { tracing_file_->Dump(indent); }
+
+TracingFormat Tracing::GetTracingFormatHavingId(uint64_t trace_event_id) {
+ if (tracing_formats_.empty()) {
+ tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
+ }
+ for (const auto& format : tracing_formats_) {
+ if (format.id == trace_event_id) {
+ return format;
+ }
+ }
+ LOG(FATAL) << "no tracing format for id " << trace_event_id;
+ return TracingFormat();
+}
+
+std::string Tracing::GetTracingEventNameHavingId(uint64_t trace_event_id) {
+ if (tracing_formats_.empty()) {
+ tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
+ }
+ for (const auto& format : tracing_formats_) {
+ if (format.id == trace_event_id) {
+ return android::base::StringPrintf("%s:%s", format.system_name.c_str(),
+ format.name.c_str());
+ }
+ }
+ return "";
+}
+
+const std::string& Tracing::GetKallsyms() const {
+ return tracing_file_->GetKallsymsFile();
+}
+
+uint32_t Tracing::GetPageSize() const { return tracing_file_->GetPageSize(); }
+
+bool GetTracingData(const std::vector<EventTypeAndModifier>& event_types,
+ std::vector<char>* data) {
+ data->clear();
+ std::vector<TraceType> trace_types;
+ for (const auto& type : event_types) {
+ if (type.event_type.type == PERF_TYPE_TRACEPOINT) {
+ size_t pos = type.event_type.name.find(':');
+ TraceType trace_type;
+ trace_type.system = type.event_type.name.substr(0, pos);
+ trace_type.name = type.event_type.name.substr(pos + 1);
+ trace_types.push_back(trace_type);
+ }
+ }
+ TracingFile tracing_file;
+ if (!tracing_file.RecordHeaderFiles()) {
+ return false;
+ }
+ tracing_file.RecordFtraceFiles(trace_types);
+ if (!tracing_file.RecordEventFiles(trace_types)) {
+ return false;
+ }
+ // Don't record /proc/kallsyms here, as it will be contained in
+ // KernelSymbolRecord.
+ if (!tracing_file.RecordPrintkFormatsFile()) {
+ return false;
+ }
+ *data = tracing_file.BinaryFormat();
+ return true;
+}
diff --git a/simpleperf/tracing.h b/simpleperf/tracing.h
new file mode 100644
index 00000000..bd3c1bfe
--- /dev/null
+++ b/simpleperf/tracing.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef SIMPLE_PERF_TRACING_H_
+#define SIMPLE_PERF_TRACING_H_
+
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "event_type.h"
+
+struct TracingField {
+ std::string name;
+ size_t offset;
+ size_t elem_size;
+ size_t elem_count;
+ bool is_signed;
+};
+
+struct TracingFormat {
+ std::string system_name;
+ std::string name;
+ uint64_t id;
+ std::vector<TracingField> fields;
+
+ void GetField(const std::string& name, uint32_t& offset, uint32_t& size) {
+ const TracingField& field = GetField(name);
+ offset = field.offset;
+ size = field.elem_size;
+ }
+
+ private:
+ const TracingField& GetField(const std::string& name) {
+ for (const auto& field : fields) {
+ if (field.name == name) {
+ return field;
+ }
+ }
+ LOG(FATAL) << "Couldn't find field " << name << "in TracingFormat of "
+ << this->name;
+ return fields[0];
+ }
+};
+
+class TracingFile;
+
+class Tracing {
+ public:
+ Tracing(const std::vector<char>& data);
+ ~Tracing();
+ void Dump(size_t indent);
+ TracingFormat GetTracingFormatHavingId(uint64_t trace_event_id);
+ std::string GetTracingEventNameHavingId(uint64_t trace_event_id);
+ const std::string& GetKallsyms() const;
+ uint32_t GetPageSize() const;
+
+ private:
+ TracingFile* tracing_file_;
+ std::vector<TracingFormat> tracing_formats_;
+};
+
+bool GetTracingData(const std::vector<EventTypeAndModifier>& event_types,
+ std::vector<char>* data);
+
+#endif // SIMPLE_PERF_TRACING_H_
diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp
index 5fb940e1..2df15e6b 100644
--- a/simpleperf/utils.cpp
+++ b/simpleperf/utils.cpp
@@ -310,3 +310,26 @@ bool ProcessKernelSymbols(std::string& symbol_data,
}
return false;
}
+
+size_t GetPageSize() {
+#if defined(__linux__)
+ return sysconf(_SC_PAGE_SIZE);
+#else
+ return 4096;
+#endif
+}
+
+uint64_t ConvertBytesToValue(const char* bytes, uint32_t size) {
+ switch (size) {
+ case 1:
+ return *reinterpret_cast<const uint8_t*>(bytes);
+ case 2:
+ return *reinterpret_cast<const uint16_t*>(bytes);
+ case 4:
+ return *reinterpret_cast<const uint32_t*>(bytes);
+ case 8:
+ return *reinterpret_cast<const uint64_t*>(bytes);
+ }
+ LOG(FATAL) << "unexpected size " << size << " in ConvertBytesToValue";
+ return 0;
+}
diff --git a/simpleperf/utils.h b/simpleperf/utils.h
index a797a321..ef7d3860 100644
--- a/simpleperf/utils.h
+++ b/simpleperf/utils.h
@@ -136,4 +136,8 @@ struct KernelSymbol {
bool ProcessKernelSymbols(std::string& symbol_data,
std::function<bool(const KernelSymbol&)> callback);
+size_t GetPageSize();
+
+uint64_t ConvertBytesToValue(const char* bytes, uint32_t size);
+
#endif // SIMPLE_PERF_UTILS_H_