diff options
author | Yabin Cui <yabinc@google.com> | 2016-06-01 17:29:06 -0700 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2016-06-03 15:24:03 -0700 |
commit | 4f41df6f68efc3b9b534014a8cfb6ee8ac4d5c91 (patch) | |
tree | 9cd04ed0e963ba2ce911087cf414df6cce35daf6 | |
parent | 471ec606874a7224e304482f5fa9cc7d207d26b8 (diff) | |
download | extras-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.mk | 1 | ||||
-rw-r--r-- | simpleperf/cmd_dumprecord.cpp | 16 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 78 | ||||
-rw-r--r-- | simpleperf/cmd_report.cpp | 62 | ||||
-rw-r--r-- | simpleperf/cmd_stat.cpp | 5 | ||||
-rw-r--r-- | simpleperf/environment.cpp | 4 | ||||
-rw-r--r-- | simpleperf/event_attr.cpp | 2 | ||||
-rw-r--r-- | simpleperf/event_selection_set.cpp | 22 | ||||
-rw-r--r-- | simpleperf/event_selection_set.h | 4 | ||||
-rw-r--r-- | simpleperf/record.cpp | 39 | ||||
-rw-r--r-- | simpleperf/record.h | 16 | ||||
-rw-r--r-- | simpleperf/record_file.h | 18 | ||||
-rw-r--r-- | simpleperf/record_file_reader.cpp | 1 | ||||
-rw-r--r-- | simpleperf/record_file_test.cpp | 22 | ||||
-rw-r--r-- | simpleperf/tracing.cpp | 427 | ||||
-rw-r--r-- | simpleperf/tracing.h | 79 | ||||
-rw-r--r-- | simpleperf/utils.cpp | 23 | ||||
-rw-r--r-- | simpleperf/utils.h | 4 |
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_ |