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 "dso.h" 29 #include "ETMDecoder.h" 30 #include "event_attr.h" 31 #include "event_type.h" 32 #include "perf_regs.h" 33 #include "record.h" 34 #include "record_file.h" 35 #include "utils.h" 36 37 using namespace PerfFileFormat; 38 using namespace simpleperf; 39 40 class DumpRecordCommand : public Command { 41 public: 42 DumpRecordCommand() 43 : Command("dump", "dump perf record file", 44 // clang-format off 45 "Usage: simpleperf dumprecord [options] [perf_record_file]\n" 46 " Dump different parts of a perf record file. Default file is perf.data.\n" 47 "--dump-etm type1,type2,... Dump etm data. A type is one of raw, packet and element.\n" 48 "--symdir <dir> Look for binaries in a directory recursively.\n" 49 // clang-format on 50 ) {} 51 52 bool Run(const std::vector<std::string>& args); 53 54 private: 55 bool ParseOptions(const std::vector<std::string>& args); 56 void DumpFileHeader(); 57 void DumpAttrSection(); 58 bool DumpDataSection(); 59 bool DumpAuxData(const AuxRecord& aux, ETMDecoder& etm_decoder); 60 bool DumpFeatureSection(); 61 62 std::string record_filename_ = "perf.data"; 63 std::unique_ptr<RecordFileReader> record_file_reader_; 64 ETMDumpOption etm_dump_option_; 65 }; 66 67 bool DumpRecordCommand::Run(const std::vector<std::string>& args) { 68 if (!ParseOptions(args)) { 69 return false; 70 } 71 record_file_reader_ = RecordFileReader::CreateInstance(record_filename_); 72 if (record_file_reader_ == nullptr) { 73 return false; 74 } 75 DumpFileHeader(); 76 DumpAttrSection(); 77 if (!DumpDataSection()) { 78 return false; 79 } 80 return DumpFeatureSection(); 81 } 82 83 bool DumpRecordCommand::ParseOptions(const std::vector<std::string>& args) { 84 size_t i; 85 for (i = 0; i < args.size() && !args[i].empty() && args[i][0] == '-'; ++i) { 86 if (args[i] == "--dump-etm") { 87 if (!NextArgumentOrError(args, &i) || !ParseEtmDumpOption(args[i], &etm_dump_option_)) { 88 return false; 89 } 90 } else if (args[i] == "--symdir") { 91 if (!NextArgumentOrError(args, &i)) { 92 return false; 93 } 94 if (!Dso::AddSymbolDir(args[i])) { 95 return false; 96 } 97 } else { 98 ReportUnknownOption(args, i); 99 return false; 100 } 101 } 102 if (i + 1 < args.size()) { 103 LOG(ERROR) << "too many record files"; 104 return false; 105 } 106 if (i + 1 == args.size()) { 107 record_filename_ = args[i]; 108 } 109 return true; 110 } 111 112 static const std::string GetFeatureNameOrUnknown(int feature) { 113 std::string name = GetFeatureName(feature); 114 return name.empty() ? android::base::StringPrintf("unknown_feature(%d)", feature) : name; 115 } 116 117 void DumpRecordCommand::DumpFileHeader() { 118 const FileHeader& header = record_file_reader_->FileHeader(); 119 printf("magic: "); 120 for (size_t i = 0; i < 8; ++i) { 121 printf("%c", header.magic[i]); 122 } 123 printf("\n"); 124 printf("header_size: %" PRId64 "\n", header.header_size); 125 if (header.header_size != sizeof(header)) { 126 PLOG(WARNING) << "record file header size " << header.header_size 127 << "doesn't match expected header size " << sizeof(header); 128 } 129 printf("attr_size: %" PRId64 "\n", header.attr_size); 130 if (header.attr_size != sizeof(FileAttr)) { 131 PLOG(WARNING) << "record file attr size " << header.attr_size 132 << " doesn't match expected attr size " << sizeof(FileAttr); 133 } 134 printf("attrs[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.attrs.offset, 135 header.attrs.size); 136 printf("data[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.data.offset, 137 header.data.size); 138 printf("event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n", 139 header.event_types.offset, header.event_types.size); 140 141 std::vector<int> features; 142 for (size_t i = 0; i < FEAT_MAX_NUM; ++i) { 143 size_t j = i / 8; 144 size_t k = i % 8; 145 if ((header.features[j] & (1 << k)) != 0) { 146 features.push_back(i); 147 } 148 } 149 for (auto& feature : features) { 150 printf("feature: %s\n", GetFeatureNameOrUnknown(feature).c_str()); 151 } 152 } 153 154 void DumpRecordCommand::DumpAttrSection() { 155 std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection(); 156 for (size_t i = 0; i < attrs.size(); ++i) { 157 const auto& attr = attrs[i]; 158 printf("attr %zu:\n", i + 1); 159 DumpPerfEventAttr(*attr.attr, 1); 160 if (!attr.ids.empty()) { 161 printf(" ids:"); 162 for (const auto& id : attr.ids) { 163 printf(" %" PRId64, id); 164 } 165 printf("\n"); 166 } 167 } 168 } 169 170 bool DumpRecordCommand::DumpDataSection() { 171 std::unique_ptr<ETMDecoder> etm_decoder; 172 ThreadTree thread_tree; 173 thread_tree.ShowIpForUnknownSymbol(); 174 record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree); 175 176 auto get_symbol_function = [&](uint32_t pid, uint32_t tid, uint64_t ip, std::string& dso_name, 177 std::string& symbol_name, uint64_t& vaddr_in_file, 178 bool in_kernel) { 179 ThreadEntry* thread = thread_tree.FindThreadOrNew(pid, tid); 180 const MapEntry* map = thread_tree.FindMap(thread, ip, in_kernel); 181 Dso* dso; 182 const Symbol* symbol = thread_tree.FindSymbol(map, ip, &vaddr_in_file, &dso); 183 dso_name = dso->Path(); 184 symbol_name = symbol->DemangledName(); 185 }; 186 187 auto record_callback = [&](std::unique_ptr<Record> r) { 188 r->Dump(); 189 thread_tree.Update(*r); 190 if (r->type() == PERF_RECORD_SAMPLE) { 191 SampleRecord& sr = *static_cast<SampleRecord*>(r.get()); 192 bool in_kernel = sr.InKernel(); 193 if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) { 194 PrintIndented(1, "callchain:\n"); 195 for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) { 196 if (sr.callchain_data.ips[i] >= PERF_CONTEXT_MAX) { 197 if (sr.callchain_data.ips[i] == PERF_CONTEXT_USER) { 198 in_kernel = false; 199 } 200 continue; 201 } 202 std::string dso_name; 203 std::string symbol_name; 204 uint64_t vaddr_in_file; 205 get_symbol_function(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i], 206 dso_name, symbol_name, vaddr_in_file, in_kernel); 207 PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", symbol_name.c_str(), dso_name.c_str(), 208 vaddr_in_file); 209 } 210 } 211 } else if (r->type() == SIMPLE_PERF_RECORD_CALLCHAIN) { 212 CallChainRecord& cr = *static_cast<CallChainRecord*>(r.get()); 213 PrintIndented(1, "callchain:\n"); 214 for (size_t i = 0; i < cr.ip_nr; ++i) { 215 std::string dso_name; 216 std::string symbol_name; 217 uint64_t vaddr_in_file; 218 get_symbol_function(cr.pid, cr.tid, cr.ips[i], dso_name, symbol_name, vaddr_in_file, 219 false); 220 PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", symbol_name.c_str(), dso_name.c_str(), 221 vaddr_in_file); 222 } 223 } else if (r->type() == PERF_RECORD_AUXTRACE_INFO) { 224 etm_decoder = ETMDecoder::Create(*static_cast<AuxTraceInfoRecord*>(r.get()), thread_tree); 225 if (!etm_decoder) { 226 return false; 227 } 228 etm_decoder->EnableDump(etm_dump_option_); 229 } else if (r->type() == PERF_RECORD_AUX) { 230 CHECK(etm_decoder); 231 return DumpAuxData(*static_cast<AuxRecord*>(r.get()), *etm_decoder); 232 } 233 return true; 234 }; 235 return record_file_reader_->ReadDataSection(record_callback); 236 } 237 238 bool DumpRecordCommand::DumpAuxData(const AuxRecord& aux, ETMDecoder& etm_decoder) { 239 size_t size = aux.data->aux_size; 240 if (size > 0) { 241 std::unique_ptr<uint8_t[]> data(new uint8_t[size]); 242 if (!record_file_reader_->ReadAuxData(aux.Cpu(), aux.data->aux_offset, data.get(), size)) { 243 return false; 244 } 245 return etm_decoder.ProcessData(data.get(), size); 246 } 247 return true; 248 } 249 250 bool DumpRecordCommand::DumpFeatureSection() { 251 std::map<int, SectionDesc> section_map = record_file_reader_->FeatureSectionDescriptors(); 252 for (const auto& pair : section_map) { 253 int feature = pair.first; 254 const auto& section = pair.second; 255 printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n", 256 GetFeatureNameOrUnknown(feature).c_str(), section.offset, section.size); 257 if (feature == FEAT_BUILD_ID) { 258 std::vector<BuildIdRecord> records = record_file_reader_->ReadBuildIdFeature(); 259 for (auto& r : records) { 260 r.Dump(1); 261 } 262 } else if (feature == FEAT_OSRELEASE) { 263 std::string s = record_file_reader_->ReadFeatureString(feature); 264 PrintIndented(1, "osrelease: %s\n", s.c_str()); 265 } else if (feature == FEAT_ARCH) { 266 std::string s = record_file_reader_->ReadFeatureString(feature); 267 PrintIndented(1, "arch: %s\n", s.c_str()); 268 } else if (feature == FEAT_CMDLINE) { 269 std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature(); 270 PrintIndented(1, "cmdline: %s\n", android::base::Join(cmdline, ' ').c_str()); 271 } else if (feature == FEAT_FILE) { 272 std::string file_path; 273 uint32_t file_type; 274 uint64_t min_vaddr; 275 uint64_t file_offset_of_min_vaddr; 276 std::vector<Symbol> symbols; 277 std::vector<uint64_t> dex_file_offsets; 278 size_t read_pos = 0; 279 PrintIndented(1, "file:\n"); 280 while (record_file_reader_->ReadFileFeature(read_pos, &file_path, &file_type, 281 &min_vaddr, &file_offset_of_min_vaddr, 282 &symbols, &dex_file_offsets)) { 283 PrintIndented(2, "file_path %s\n", file_path.c_str()); 284 PrintIndented(2, "file_type %s\n", DsoTypeToString(static_cast<DsoType>(file_type))); 285 PrintIndented(2, "min_vaddr 0x%" PRIx64 "\n", min_vaddr); 286 PrintIndented(2, "file_offset_of_min_vaddr 0x%" PRIx64 "\n", file_offset_of_min_vaddr); 287 PrintIndented(2, "symbols:\n"); 288 for (const auto& symbol : symbols) { 289 PrintIndented(3, "%s [0x%" PRIx64 "-0x%" PRIx64 "]\n", symbol.DemangledName(), 290 symbol.addr, symbol.addr + symbol.len); 291 } 292 if (file_type == static_cast<uint32_t>(DSO_DEX_FILE)) { 293 PrintIndented(2, "dex_file_offsets:\n"); 294 for (uint64_t offset : dex_file_offsets) { 295 PrintIndented(3, "0x%" PRIx64 "\n", offset); 296 } 297 } 298 } 299 } else if (feature == FEAT_META_INFO) { 300 PrintIndented(1, "meta_info:\n"); 301 for (auto& pair : record_file_reader_->GetMetaInfoFeature()) { 302 PrintIndented(2, "%s = %s\n", pair.first.c_str(), pair.second.c_str()); 303 } 304 } else if (feature == FEAT_AUXTRACE) { 305 PrintIndented(1, "file_offsets_of_auxtrace_records:\n"); 306 for (auto offset : record_file_reader_->ReadAuxTraceFeature()) { 307 PrintIndented(2, "%" PRIu64 "\n", offset); 308 } 309 } 310 } 311 return true; 312 } 313 314 void RegisterDumpRecordCommand() { 315 RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); }); 316 } 317