123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- #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 "event_type.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();
- bool 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_);
- std::unique_ptr<ScopedEventTypes> scoped_event_types;
- if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO)) {
- std::unordered_map<std::string, std::string> meta_info;
- if (!record_file_reader_->ReadMetaInfoFeature(&meta_info)) {
- return false;
- }
- auto it = meta_info.find("event_type_info");
- if (it != meta_info.end()) {
- scoped_event_types.reset(new ScopedEventTypes(it->second));
- }
- }
- DumpFileHeader();
- DumpAttrSection();
- if (!DumpDataSection()) {
- return false;
- }
- return DumpFeatureSection();
- }
- 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 GetFeatureNameOrUnknown(int feature) {
- std::string name = GetFeatureName(feature);
- return name.empty() ? android::base::StringPrintf("unknown_feature(%d)", feature) : name;
- }
- 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", GetFeatureNameOrUnknown(feature).c_str());
- }
- }
- 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");
- }
- }
- }
- bool DumpRecordCommand::DumpDataSection() {
- ThreadTree thread_tree;
- thread_tree.ShowIpForUnknownSymbol();
- record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree);
- auto get_symbol_function = [&](uint32_t pid, uint32_t tid, uint64_t ip, std::string& dso_name,
- std::string& symbol_name, uint64_t& vaddr_in_file,
- bool in_kernel) {
- ThreadEntry* thread = thread_tree.FindThreadOrNew(pid, tid);
- const MapEntry* map = thread_tree.FindMap(thread, ip, in_kernel);
- Dso* dso;
- const Symbol* symbol = thread_tree.FindSymbol(map, ip, &vaddr_in_file, &dso);
- dso_name = dso->Path();
- symbol_name = symbol->DemangledName();
- };
- auto record_callback = [&](std::unique_ptr<Record> r) {
- r->Dump();
- thread_tree.Update(*r);
- if (r->type() == PERF_RECORD_SAMPLE) {
- SampleRecord& sr = *static_cast<SampleRecord*>(r.get());
- bool in_kernel = sr.InKernel();
- if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) {
- PrintIndented(1, "callchain:\n");
- for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) {
- if (sr.callchain_data.ips[i] >= PERF_CONTEXT_MAX) {
- if (sr.callchain_data.ips[i] == PERF_CONTEXT_USER) {
- in_kernel = false;
- }
- continue;
- }
- std::string dso_name;
- std::string symbol_name;
- uint64_t vaddr_in_file;
- get_symbol_function(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i],
- dso_name, symbol_name, vaddr_in_file, in_kernel);
- PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", symbol_name.c_str(), dso_name.c_str(),
- vaddr_in_file);
- }
- }
- } else if (r->type() == SIMPLE_PERF_RECORD_CALLCHAIN) {
- CallChainRecord& cr = *static_cast<CallChainRecord*>(r.get());
- PrintIndented(1, "callchain:\n");
- for (size_t i = 0; i < cr.ip_nr; ++i) {
- std::string dso_name;
- std::string symbol_name;
- uint64_t vaddr_in_file;
- get_symbol_function(cr.pid, cr.tid, cr.ips[i], dso_name, symbol_name, vaddr_in_file,
- false);
- PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", symbol_name.c_str(), dso_name.c_str(),
- vaddr_in_file);
- }
- }
- return true;
- };
- return record_file_reader_->ReadDataSection(record_callback);
- }
- 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",
- GetFeatureNameOrUnknown(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;
- uint64_t file_offset_of_min_vaddr;
- std::vector<Symbol> symbols;
- std::vector<uint64_t> dex_file_offsets;
- size_t read_pos = 0;
- PrintIndented(1, "file:\n");
- while (record_file_reader_->ReadFileFeature(read_pos, &file_path, &file_type,
- &min_vaddr, &file_offset_of_min_vaddr,
- &symbols, &dex_file_offsets)) {
- 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, "file_offset_of_min_vaddr 0x%" PRIx64 "\n", file_offset_of_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);
- }
- if (file_type == static_cast<uint32_t>(DSO_DEX_FILE)) {
- PrintIndented(2, "dex_file_offsets:\n");
- for (uint64_t offset : dex_file_offsets) {
- PrintIndented(3, "0x%" PRIx64 "\n", offset);
- }
- }
- }
- } 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); });
- }
|