1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <inttypes.h> 18 19 #include <map> 20 #include <string> 21 #include <vector> 22 23 #include <android-base/logging.h> 24 #include <android-base/stringprintf.h> 25 #include <android-base/strings.h> 26 27 #include "command.h" 28 #include "event_attr.h" 29 #include "event_type.h" 30 #include "perf_regs.h" 31 #include "record.h" 32 #include "record_file.h" 33 #include "utils.h" 34 35 using namespace PerfFileFormat; 36 37 class DumpRecordCommand : public Command { 38 public: 39 DumpRecordCommand() 40 : Command("dump", "dump perf record file", 41 "Usage: simpleperf dumprecord [options] [perf_record_file]\n" 42 " Dump different parts of a perf record file. Default file is perf.data.\n"), 43 record_filename_("perf.data"), record_file_arch_(GetBuildArch()) { 44 } 45 46 bool Run(const std::vector<std::string>& args); 47 48 private: 49 bool ParseOptions(const std::vector<std::string>& args); 50 void DumpFileHeader(); 51 void DumpAttrSection(); 52 bool DumpDataSection(); 53 bool DumpFeatureSection(); 54 55 std::string record_filename_; 56 std::unique_ptr<RecordFileReader> record_file_reader_; 57 ArchType record_file_arch_; 58 }; 59 60 bool DumpRecordCommand::Run(const std::vector<std::string>& args) { 61 if (!ParseOptions(args)) { 62 return false; 63 } 64 record_file_reader_ = RecordFileReader::CreateInstance(record_filename_); 65 if (record_file_reader_ == nullptr) { 66 return false; 67 } 68 std::string arch = record_file_reader_->ReadFeatureString(FEAT_ARCH); 69 if (!arch.empty()) { 70 record_file_arch_ = GetArchType(arch); 71 if (record_file_arch_ == ARCH_UNSUPPORTED) { 72 return false; 73 } 74 } 75 ScopedCurrentArch scoped_arch(record_file_arch_); 76 std::unique_ptr<ScopedEventTypes> scoped_event_types; 77 if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO)) { 78 std::unordered_map<std::string, std::string> meta_info; 79 if (!record_file_reader_->ReadMetaInfoFeature(&meta_info)) { 80 return false; 81 } 82 auto it = meta_info.find("event_type_info"); 83 if (it != meta_info.end()) { 84 scoped_event_types.reset(new ScopedEventTypes(it->second)); 85 } 86 } 87 DumpFileHeader(); 88 DumpAttrSection(); 89 if (!DumpDataSection()) { 90 return false; 91 } 92 return DumpFeatureSection(); 93 } 94 95 bool DumpRecordCommand::ParseOptions(const std::vector<std::string>& args) { 96 if (args.size() == 1) { 97 record_filename_ = args[0]; 98 } else if (args.size() > 1) { 99 ReportUnknownOption(args, 1); 100 return false; 101 } 102 return true; 103 } 104 105 static const std::string GetFeatureNameOrUnknown(int feature) { 106 std::string name = GetFeatureName(feature); 107 return name.empty() ? android::base::StringPrintf("unknown_feature(%d)", feature) : name; 108 } 109 110 void DumpRecordCommand::DumpFileHeader() { 111 const FileHeader& header = record_file_reader_->FileHeader(); 112 printf("magic: "); 113 for (size_t i = 0; i < 8; ++i) { 114 printf("%c", header.magic[i]); 115 } 116 printf("\n"); 117 printf("header_size: %" PRId64 "\n", header.header_size); 118 if (header.header_size != sizeof(header)) { 119 PLOG(WARNING) << "record file header size " << header.header_size 120 << "doesn't match expected header size " << sizeof(header); 121 } 122 printf("attr_size: %" PRId64 "\n", header.attr_size); 123 if (header.attr_size != sizeof(FileAttr)) { 124 PLOG(WARNING) << "record file attr size " << header.attr_size 125 << " doesn't match expected attr size " << sizeof(FileAttr); 126 } 127 printf("attrs[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.attrs.offset, 128 header.attrs.size); 129 printf("data[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.data.offset, 130 header.data.size); 131 printf("event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n", 132 header.event_types.offset, header.event_types.size); 133 134 std::vector<int> features; 135 for (size_t i = 0; i < FEAT_MAX_NUM; ++i) { 136 size_t j = i / 8; 137 size_t k = i % 8; 138 if ((header.features[j] & (1 << k)) != 0) { 139 features.push_back(i); 140 } 141 } 142 for (auto& feature : features) { 143 printf("feature: %s\n", GetFeatureNameOrUnknown(feature).c_str()); 144 } 145 } 146 147 void DumpRecordCommand::DumpAttrSection() { 148 std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection(); 149 for (size_t i = 0; i < attrs.size(); ++i) { 150 const auto& attr = attrs[i]; 151 printf("attr %zu:\n", i + 1); 152 DumpPerfEventAttr(*attr.attr, 1); 153 if (!attr.ids.empty()) { 154 printf(" ids:"); 155 for (const auto& id : attr.ids) { 156 printf(" %" PRId64, id); 157 } 158 printf("\n"); 159 } 160 } 161 } 162 163 bool DumpRecordCommand::DumpDataSection() { 164 ThreadTree thread_tree; 165 thread_tree.ShowIpForUnknownSymbol(); 166 record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree); 167 168 auto get_symbol_function = [&](uint32_t pid, uint32_t tid, uint64_t ip, std::string& dso_name, 169 std::string& symbol_name, uint64_t& vaddr_in_file) { 170 ThreadEntry* thread = thread_tree.FindThreadOrNew(pid, tid); 171 const MapEntry* map = thread_tree.FindMap(thread, ip); 172 Dso* dso; 173 const Symbol* symbol = thread_tree.FindSymbol(map, ip, &vaddr_in_file, &dso); 174 dso_name = dso->Path(); 175 symbol_name = symbol->DemangledName(); 176 }; 177 178 auto record_callback = [&](std::unique_ptr<Record> r) { 179 r->Dump(); 180 thread_tree.Update(*r); 181 if (r->type() == PERF_RECORD_SAMPLE) { 182 SampleRecord& sr = *static_cast<SampleRecord*>(r.get()); 183 if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) { 184 PrintIndented(1, "callchain:\n"); 185 for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) { 186 std::string dso_name; 187 std::string symbol_name; 188 uint64_t vaddr_in_file; 189 get_symbol_function(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i], 190 dso_name, symbol_name, vaddr_in_file); 191 PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", symbol_name.c_str(), dso_name.c_str(), 192 vaddr_in_file); 193 } 194 } 195 } else if (r->type() == SIMPLE_PERF_RECORD_CALLCHAIN) { 196 CallChainRecord& cr = *static_cast<CallChainRecord*>(r.get()); 197 PrintIndented(1, "callchain:\n"); 198 for (size_t i = 0; i < cr.ip_nr; ++i) { 199 std::string dso_name; 200 std::string symbol_name; 201 uint64_t vaddr_in_file; 202 get_symbol_function(cr.pid, cr.tid, cr.ips[i], dso_name, symbol_name, vaddr_in_file); 203 PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", symbol_name.c_str(), dso_name.c_str(), 204 vaddr_in_file); 205 } 206 } 207 return true; 208 }; 209 return record_file_reader_->ReadDataSection(record_callback, false); 210 } 211 212 bool DumpRecordCommand::DumpFeatureSection() { 213 std::map<int, SectionDesc> section_map = record_file_reader_->FeatureSectionDescriptors(); 214 for (const auto& pair : section_map) { 215 int feature = pair.first; 216 const auto& section = pair.second; 217 printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n", 218 GetFeatureNameOrUnknown(feature).c_str(), section.offset, section.size); 219 if (feature == FEAT_BUILD_ID) { 220 std::vector<BuildIdRecord> records = record_file_reader_->ReadBuildIdFeature(); 221 for (auto& r : records) { 222 r.Dump(1); 223 } 224 } else if (feature == FEAT_OSRELEASE) { 225 std::string s = record_file_reader_->ReadFeatureString(feature); 226 PrintIndented(1, "osrelease: %s\n", s.c_str()); 227 } else if (feature == FEAT_ARCH) { 228 std::string s = record_file_reader_->ReadFeatureString(feature); 229 PrintIndented(1, "arch: %s\n", s.c_str()); 230 } else if (feature == FEAT_CMDLINE) { 231 std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature(); 232 PrintIndented(1, "cmdline: %s\n", android::base::Join(cmdline, ' ').c_str()); 233 } else if (feature == FEAT_FILE) { 234 std::string file_path; 235 uint32_t file_type; 236 uint64_t min_vaddr; 237 std::vector<Symbol> symbols; 238 size_t read_pos = 0; 239 PrintIndented(1, "file:\n"); 240 while (record_file_reader_->ReadFileFeature(read_pos, &file_path, 241 &file_type, &min_vaddr, 242 &symbols)) { 243 PrintIndented(2, "file_path %s\n", file_path.c_str()); 244 PrintIndented(2, "file_type %s\n", DsoTypeToString(static_cast<DsoType>(file_type))); 245 PrintIndented(2, "min_vaddr 0x%" PRIx64 "\n", min_vaddr); 246 PrintIndented(2, "symbols:\n"); 247 for (const auto& symbol : symbols) { 248 PrintIndented(3, "%s [0x%" PRIx64 "-0x%" PRIx64 "]\n", symbol.DemangledName(), 249 symbol.addr, symbol.addr + symbol.len); 250 } 251 } 252 } else if (feature == FEAT_META_INFO) { 253 std::unordered_map<std::string, std::string> info_map; 254 if (!record_file_reader_->ReadMetaInfoFeature(&info_map)) { 255 return false; 256 } 257 PrintIndented(1, "meta_info:\n"); 258 for (auto& pair : info_map) { 259 PrintIndented(2, "%s = %s\n", pair.first.c_str(), pair.second.c_str()); 260 } 261 } 262 } 263 return true; 264 } 265 266 void RegisterDumpRecordCommand() { 267 RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); }); 268 } 269