You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
8.0 KiB
241 lines
8.0 KiB
/* |
|
* 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(); |
|
bool 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(); |
|
if (!DumpFeatureSection()) { |
|
return false; |
|
} |
|
|
|
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); |
|
} |
|
|
|
bool 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); |
|
} |
|
} |
|
} else if (feature == FEAT_META_INFO) { |
|
std::unordered_map<std::string, std::string> info_map; |
|
if (!record_file_reader_->ReadMetaInfoFeature(&info_map)) { |
|
return false; |
|
} |
|
PrintIndented(1, "meta_info:\n"); |
|
for (auto& pair : info_map) { |
|
PrintIndented(2, "%s = %s\n", pair.first.c_str(), pair.second.c_str()); |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
void RegisterDumpRecordCommand() { |
|
RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); }); |
|
}
|
|
|