/* * 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 <android-base/logging.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include "command.h" #include "event_attr.h" #include "perf_regs.h" #include "record.h" #include "record_file.h" #include "utils.h" using namespace PerfFileFormat; 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"), record_filename_("perf.data"), record_file_arch_(GetBuildArch()) { } 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_; ArchType record_file_arch_; }; bool DumpRecordCommand::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; } std::string arch = record_file_reader_->ReadFeatureString(FEAT_ARCH); if (!arch.empty()) { record_file_arch_ = GetArchType(arch); if (record_file_arch_ == ARCH_UNSUPPORTED) { return false; } } ScopedCurrentArch scoped_arch(record_file_arch_); DumpFileHeader(); DumpAttrSection(); DumpDataSection(); DumpFeatureSection(); return true; } bool DumpRecordCommand::ParseOptions(const std::vector<std::string>& args) { if (args.size() == 1) { record_filename_ = args[0]; } else if (args.size() > 1) { ReportUnknownOption(args, 1); return false; } return true; } static const std::string GetFeatureName(int feature); void DumpRecordCommand::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 " << header.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 " << header.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); std::vector<int> features; 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, "branch_stack"}, {FEAT_PMU_MAPPINGS, "pmu_mappings"}, {FEAT_GROUP_DESC, "group_desc"}, {FEAT_FILE, "file"}, }; 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 DumpRecordCommand::DumpAttrSection() { std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection(); for (size_t i = 0; i < attrs.size(); ++i) { const auto& attr = attrs[i]; printf("attr %zu:\n", i + 1); DumpPerfEventAttr(*attr.attr, 1); if (!attr.ids.empty()) { printf(" ids:"); for (const auto& id : attr.ids) { printf(" %" PRId64, id); } printf("\n"); } } } void DumpRecordCommand::DumpDataSection() { record_file_reader_->ReadDataSection([](std::unique_ptr<Record> record) { record->Dump(); return true; }, false); } void DumpRecordCommand::DumpFeatureSection() { std::map<int, SectionDesc> section_map = record_file_reader_->FeatureSectionDescriptors(); for (const auto& pair : section_map) { int feature = pair.first; const auto& section = pair.second; printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n", GetFeatureName(feature).c_str(), section.offset, section.size); if (feature == FEAT_BUILD_ID) { std::vector<BuildIdRecord> records = record_file_reader_->ReadBuildIdFeature(); for (auto& r : records) { r.Dump(1); } } else if (feature == FEAT_OSRELEASE) { std::string s = record_file_reader_->ReadFeatureString(feature); PrintIndented(1, "osrelease: %s\n", s.c_str()); } else if (feature == FEAT_ARCH) { std::string s = record_file_reader_->ReadFeatureString(feature); PrintIndented(1, "arch: %s\n", s.c_str()); } else if (feature == FEAT_CMDLINE) { std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature(); PrintIndented(1, "cmdline: %s\n", android::base::Join(cmdline, ' ').c_str()); } else if (feature == FEAT_FILE) { std::string file_path; uint32_t file_type; uint64_t min_vaddr; std::vector<Symbol> symbols; size_t read_pos = 0; PrintIndented(1, "file:\n"); while (record_file_reader_->ReadFileFeature(read_pos, &file_path, &file_type, &min_vaddr, &symbols)) { PrintIndented(2, "file_path %s\n", file_path.c_str()); PrintIndented(2, "file_type %s\n", DsoTypeToString(static_cast<DsoType>(file_type))); PrintIndented(2, "min_vaddr 0x%" PRIx64 "\n", min_vaddr); PrintIndented(2, "symbols:\n"); for (const auto& symbol : symbols) { PrintIndented(3, "%s [0x%" PRIx64 "-0x%" PRIx64 "]\n", symbol.DemangledName(), symbol.addr, symbol.addr + symbol.len); } } } } } void RegisterDumpRecordCommand() { RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); }); }