| /* |
| * Copyright (C) 2015 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 <inttypes.h> |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include <base/logging.h> |
| #include <base/stringprintf.h> |
| |
| #include "command.h" |
| #include "event_attr.h" |
| #include "record.h" |
| #include "record_file.h" |
| |
| using namespace PerfFileFormat; |
| |
| class DumpRecordCommandImpl { |
| public: |
| DumpRecordCommandImpl() : record_filename_("perf.data") { |
| } |
| |
| bool Run(const std::vector<std::string>& args); |
| |
| private: |
| bool ParseOptions(const std::vector<std::string>& args); |
| void DumpFileHeader(); |
| void DumpAttrSection(); |
| void DumpDataSection(); |
| void DumpFeatureSection(); |
| |
| std::string record_filename_; |
| std::unique_ptr<RecordFileReader> record_file_reader_; |
| |
| std::vector<int> features_; |
| }; |
| |
| bool DumpRecordCommandImpl::Run(const std::vector<std::string>& args) { |
| if (!ParseOptions(args)) { |
| return false; |
| } |
| record_file_reader_ = RecordFileReader::CreateInstance(record_filename_); |
| if (record_file_reader_ == nullptr) { |
| return false; |
| } |
| DumpFileHeader(); |
| DumpAttrSection(); |
| DumpDataSection(); |
| DumpFeatureSection(); |
| |
| return true; |
| } |
| |
| bool DumpRecordCommandImpl::ParseOptions(const std::vector<std::string>& args) { |
| if (args.size() == 2) { |
| record_filename_ = args[1]; |
| } |
| return true; |
| } |
| |
| static const std::string GetFeatureName(int feature); |
| |
| void DumpRecordCommandImpl::DumpFileHeader() { |
| const FileHeader* header = record_file_reader_->FileHeader(); |
| printf("magic: "); |
| for (size_t i = 0; i < 8; ++i) { |
| printf("%c", header->magic[i]); |
| } |
| printf("\n"); |
| printf("header_size: %" PRId64 "\n", header->header_size); |
| if (header->header_size != sizeof(*header)) { |
| PLOG(WARNING) << "record file header size doesn't match expected header size " |
| << sizeof(*header); |
| } |
| printf("attr_size: %" PRId64 "\n", header->attr_size); |
| if (header->attr_size != sizeof(FileAttr)) { |
| PLOG(WARNING) << "record file attr size doesn't match expected attr size " << sizeof(FileAttr); |
| } |
| printf("attrs[file section]: offset %" PRId64 ", size %" PRId64 "\n", header->attrs.offset, |
| header->attrs.size); |
| printf("data[file section]: offset %" PRId64 ", size %" PRId64 "\n", header->data.offset, |
| header->data.size); |
| printf("event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n", |
| header->event_types.offset, header->event_types.size); |
| |
| features_.clear(); |
| for (size_t i = 0; i < FEAT_MAX_NUM; ++i) { |
| size_t j = i / 8; |
| size_t k = i % 8; |
| if ((header->features[j] & (1 << k)) != 0) { |
| features_.push_back(i); |
| } |
| } |
| for (auto& feature : features_) { |
| printf("feature: %s\n", GetFeatureName(feature).c_str()); |
| } |
| } |
| |
| static const std::string GetFeatureName(int feature) { |
| static std::map<int, std::string> feature_name_map = { |
| {FEAT_TRACING_DATA, "tracing_data"}, |
| {FEAT_BUILD_ID, "build_id"}, |
| {FEAT_HOSTNAME, "hostname"}, |
| {FEAT_OSRELEASE, "osrelease"}, |
| {FEAT_VERSION, "version"}, |
| {FEAT_ARCH, "arch"}, |
| {FEAT_NRCPUS, "nrcpus"}, |
| {FEAT_CPUDESC, "cpudesc"}, |
| {FEAT_CPUID, "cpuid"}, |
| {FEAT_TOTAL_MEM, "total_mem"}, |
| {FEAT_CMDLINE, "cmdline"}, |
| {FEAT_EVENT_DESC, "event_desc"}, |
| {FEAT_CPU_TOPOLOGY, "cpu_topology"}, |
| {FEAT_NUMA_TOPOLOGY, "numa_topology"}, |
| {FEAT_BRANCH_STACK, "branck_stack"}, |
| {FEAT_PMU_MAPPINGS, "pmu_mappings"}, |
| {FEAT_GROUP_DESC, "group_desc"}, |
| }; |
| auto it = feature_name_map.find(feature); |
| if (it != feature_name_map.end()) { |
| return it->second; |
| } |
| return android::base::StringPrintf("unknown_feature(%d)", feature); |
| } |
| |
| void DumpRecordCommandImpl::DumpAttrSection() { |
| std::vector<const FileAttr*> attrs = record_file_reader_->AttrSection(); |
| for (size_t i = 0; i < attrs.size(); ++i) { |
| 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 = record_file_reader_->IdsForAttr(attr); |
| if (ids.size() > 0) { |
| printf(" ids:"); |
| for (auto& id : ids) { |
| printf(" %" PRId64, id); |
| } |
| printf("\n"); |
| } |
| } |
| } |
| |
| void DumpRecordCommandImpl::DumpDataSection() { |
| std::vector<std::unique_ptr<const Record>> records = record_file_reader_->DataSection(); |
| for (auto& record : records) { |
| record->Dump(); |
| } |
| } |
| |
| void DumpRecordCommandImpl::DumpFeatureSection() { |
| std::vector<SectionDesc> sections = record_file_reader_->FeatureSectionDescriptors(); |
| CHECK_EQ(sections.size(), features_.size()); |
| for (size_t i = 0; i < features_.size(); ++i) { |
| int feature = features_[i]; |
| SectionDesc& section = sections[i]; |
| printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n", |
| GetFeatureName(feature).c_str(), section.offset, section.size); |
| if (feature == FEAT_BUILD_ID) { |
| const char* p = record_file_reader_->DataAtOffset(section.offset); |
| const char* end = p + section.size; |
| while (p < end) { |
| const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p); |
| CHECK_LE(p + header->size, end); |
| CHECK_EQ(PERF_RECORD_BUILD_ID, header->type); |
| BuildIdRecord record(header); |
| record.Dump(1); |
| p += header->size; |
| } |
| } |
| } |
| } |
| |
| class DumpRecordCommand : public Command { |
| public: |
| DumpRecordCommand() |
| : Command("dump", "dump perf record file", |
| "Usage: simpleperf dumprecord [options] [perf_record_file]\n" |
| " Dump different parts of a perf record file. Default file is perf.data.\n") { |
| } |
| |
| bool Run(const std::vector<std::string>& args) override { |
| DumpRecordCommandImpl impl; |
| return impl.Run(args); |
| } |
| }; |
| |
| DumpRecordCommand dumprecord_cmd; |