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 #include <stdint.h>
19
20 #include <map>
21 #include <string>
22 #include <type_traits>
23 #include <vector>
24
25 #include <android-base/logging.h>
26 #include <android-base/stringprintf.h>
27 #include <android-base/strings.h>
28
29 #include "BranchListFile.h"
30 #include "ETMDecoder.h"
31 #include "command.h"
32 #include "dso.h"
33 #include "event_attr.h"
34 #include "event_type.h"
35 #include "perf_regs.h"
36 #include "record.h"
37 #include "record_file.h"
38 #include "tracing.h"
39 #include "utils.h"
40
41 namespace simpleperf {
42 namespace {
43
44 using namespace PerfFileFormat;
45
46 struct SymbolInfo {
47 Dso* dso;
48 const Symbol* symbol;
49 uint64_t vaddr_in_file;
50 };
51
52 using ExtractFieldFn = std::function<std::string(const TracingField&, const PerfSampleRawType&)>;
53
54 struct EventInfo {
55 size_t tp_data_size = 0;
56 std::vector<TracingField> tp_fields;
57 std::vector<ExtractFieldFn> extract_field_functions;
58 };
59
ExtractStringField(const TracingField & field,const PerfSampleRawType & data)60 std::string ExtractStringField(const TracingField& field, const PerfSampleRawType& data) {
61 std::string s;
62 // data points to a char [field.elem_count] array. It is not guaranteed to be ended
63 // with '\0'. So need to copy from data like strncpy.
64 size_t max_len = std::min(data.size - field.offset, field.elem_count);
65 const char* p = data.data + field.offset;
66 for (size_t i = 0; i < max_len && *p != '\0'; i++) {
67 s.push_back(*p++);
68 }
69 return s;
70 }
71
ExtractDynamicStringField(const TracingField & field,const PerfSampleRawType & data)72 std::string ExtractDynamicStringField(const TracingField& field, const PerfSampleRawType& data) {
73 std::string s;
74 const char* p = data.data + field.offset;
75 if (field.elem_size != 4 || field.offset + field.elem_size > data.size) {
76 return s;
77 }
78 uint32_t location;
79 MoveFromBinaryFormat(location, p);
80 // Parse location: (max_len << 16) | off.
81 uint32_t offset = location & 0xffff;
82 uint32_t max_len = location >> 16;
83 if (offset + max_len <= data.size) {
84 p = data.data + offset;
85 for (size_t i = 0; i < max_len && *p != '\0'; i++) {
86 s.push_back(*p++);
87 }
88 }
89 return s;
90 }
91
92 template <typename T, typename UT = typename std::make_unsigned<T>::type>
ExtractIntFieldFromPointer(const TracingField & field,const char * p)93 std::string ExtractIntFieldFromPointer(const TracingField& field, const char* p) {
94 static_assert(std::is_signed<T>::value);
95 T value;
96 MoveFromBinaryFormat(value, p);
97
98 if (field.is_signed) {
99 return android::base::StringPrintf("%" PRId64, static_cast<int64_t>(value));
100 }
101 return android::base::StringPrintf("0x%" PRIx64, static_cast<uint64_t>(static_cast<UT>(value)));
102 }
103
104 template <typename T>
ExtractIntField(const TracingField & field,const PerfSampleRawType & data)105 std::string ExtractIntField(const TracingField& field, const PerfSampleRawType& data) {
106 if (field.offset + sizeof(T) > data.size) {
107 return "";
108 }
109 return ExtractIntFieldFromPointer<T>(field, data.data + field.offset);
110 }
111
112 template <typename T>
ExtractIntArrayField(const TracingField & field,const PerfSampleRawType & data)113 std::string ExtractIntArrayField(const TracingField& field, const PerfSampleRawType& data) {
114 if (field.offset + field.elem_size * field.elem_count > data.size) {
115 return "";
116 }
117 std::string s;
118 const char* p = data.data + field.offset;
119 for (size_t i = 0; i < field.elem_count; i++) {
120 if (i != 0) {
121 s.push_back(' ');
122 }
123 ExtractIntFieldFromPointer<T>(field, p);
124 p += field.elem_size;
125 }
126 return s;
127 }
128
ExtractUnknownField(const TracingField & field,const PerfSampleRawType & data)129 std::string ExtractUnknownField(const TracingField& field, const PerfSampleRawType& data) {
130 size_t total = field.elem_size * field.elem_count;
131 if (field.offset + total > data.size) {
132 return "";
133 }
134 uint32_t value;
135 std::string s;
136 const char* p = data.data + field.offset;
137 for (size_t i = 0; i + sizeof(value) <= total; i += sizeof(value)) {
138 if (i != 0) {
139 s.push_back(' ');
140 }
141 MoveFromBinaryFormat(value, p);
142 s += android::base::StringPrintf("0x%08x", value);
143 }
144 return s;
145 }
146
GetExtractFieldFunction(const TracingField & field)147 ExtractFieldFn GetExtractFieldFunction(const TracingField& field) {
148 if (field.is_dynamic) {
149 return ExtractDynamicStringField;
150 }
151 if (field.elem_count > 1 && field.elem_size == 1) {
152 // Probably the field is a string.
153 // Don't use field.is_signed, which has different values on x86 and arm.
154 return ExtractStringField;
155 }
156 if (field.elem_count == 1) {
157 switch (field.elem_size) {
158 case 1:
159 return ExtractIntField<int8_t>;
160 case 2:
161 return ExtractIntField<int16_t>;
162 case 4:
163 return ExtractIntField<int32_t>;
164 case 8:
165 return ExtractIntField<int64_t>;
166 }
167 } else {
168 switch (field.elem_size) {
169 case 1:
170 return ExtractIntArrayField<int8_t>;
171 case 2:
172 return ExtractIntArrayField<int16_t>;
173 case 4:
174 return ExtractIntArrayField<int32_t>;
175 case 8:
176 return ExtractIntArrayField<int64_t>;
177 }
178 }
179 return ExtractUnknownField;
180 }
181
182 class ETMThreadTreeForDumpCmd : public ETMThreadTree {
183 public:
ETMThreadTreeForDumpCmd(ThreadTree & thread_tree)184 ETMThreadTreeForDumpCmd(ThreadTree& thread_tree) : thread_tree_(thread_tree) {}
185
DisableThreadExitRecords()186 void DisableThreadExitRecords() override { thread_tree_.DisableThreadExitRecords(); }
FindThread(int tid)187 const ThreadEntry* FindThread(int tid) override { return thread_tree_.FindThread(tid); }
GetKernelMaps()188 const MapSet& GetKernelMaps() override { return thread_tree_.GetKernelMaps(); }
189
190 private:
191 ThreadTree& thread_tree_;
192 };
193
194 class DumpRecordCommand : public Command {
195 public:
DumpRecordCommand()196 DumpRecordCommand()
197 : Command("dump", "dump perf record file",
198 // clang-format off
199 "Usage: simpleperf dumprecord [options] [perf_record_file]\n"
200 " Dump different parts of a perf record file. Default file is perf.data.\n"
201 "--dump-etm type1,type2,... Dump etm data. A type is one of raw, packet and element.\n"
202 "--dump-feature feature1,feature2,... Only dump selected feature sections.\n"
203 "-i <record_file> Record file to dump. Default is perf.data.\n"
204 "--symdir <dir> Look for binaries in a directory recursively.\n"
205 // clang-format on
206 ) {}
207
208 bool Run(const std::vector<std::string>& args);
209
210 private:
211 bool ParseOptions(const std::vector<std::string>& args);
212 void DumpFileHeader();
213 void DumpAttrSection();
214 bool DumpDataSection();
215 bool ProcessRecord(Record* r);
216 void ProcessSampleRecord(const SampleRecord& r);
217 void ProcessCallChainRecord(const CallChainRecord& r);
218 SymbolInfo GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip,
219 std::optional<bool> in_kernel = std::nullopt);
220 bool ProcessTracingData(const TracingDataRecord& r);
221 bool DumpAuxData(const AuxRecord& aux);
222 bool DumpFeatureSection();
223
224 // options
225 std::string record_filename_ = "perf.data";
226 ETMDumpOption etm_dump_option_;
227 std::vector<std::string> dump_features_;
228
229 std::unique_ptr<RecordFileReader> record_file_reader_;
230 std::unique_ptr<ETMDecoder> etm_decoder_;
231 std::unique_ptr<ETMThreadTree> etm_thread_tree_;
232 ThreadTree thread_tree_;
233
234 std::vector<EventInfo> events_;
235 };
236
Run(const std::vector<std::string> & args)237 bool DumpRecordCommand::Run(const std::vector<std::string>& args) {
238 if (!ParseOptions(args)) {
239 return false;
240 }
241 record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
242 if (record_file_reader_ == nullptr) {
243 return false;
244 }
245
246 if (!dump_features_.empty()) {
247 return DumpFeatureSection();
248 }
249
250 DumpFileHeader();
251 DumpAttrSection();
252 if (!DumpDataSection()) {
253 return false;
254 }
255 return DumpFeatureSection();
256 }
257
ParseOptions(const std::vector<std::string> & args)258 bool DumpRecordCommand::ParseOptions(const std::vector<std::string>& args) {
259 const OptionFormatMap option_formats = {
260 {"--dump-etm", {OptionValueType::STRING, OptionType::SINGLE}},
261 {"--dump-feature", {OptionValueType::STRING, OptionType::MULTIPLE}},
262 {"-i", {OptionValueType::STRING, OptionType::SINGLE}},
263 {"--symdir", {OptionValueType::STRING, OptionType::MULTIPLE}},
264 };
265 OptionValueMap options;
266 std::vector<std::pair<OptionName, OptionValue>> ordered_options;
267 std::vector<std::string> non_option_args;
268 if (!PreprocessOptions(args, option_formats, &options, &ordered_options, &non_option_args)) {
269 return false;
270 }
271 if (auto value = options.PullValue("--dump-etm"); value) {
272 if (!ParseEtmDumpOption(value->str_value, &etm_dump_option_)) {
273 return false;
274 }
275 }
276 dump_features_ = options.PullStringValues("--dump-feature");
277 options.PullStringValue("-i", &record_filename_);
278 for (const OptionValue& value : options.PullValues("--symdir")) {
279 if (!Dso::AddSymbolDir(value.str_value)) {
280 return false;
281 }
282 }
283 CHECK(options.values.empty());
284 if (non_option_args.size() > 1) {
285 LOG(ERROR) << "too many record files";
286 return false;
287 }
288 if (non_option_args.size() == 1) {
289 record_filename_ = non_option_args[0];
290 }
291 return true;
292 }
293
GetFeatureNameOrUnknown(int feature)294 static const std::string GetFeatureNameOrUnknown(int feature) {
295 std::string name = GetFeatureName(feature);
296 return name.empty() ? android::base::StringPrintf("unknown_feature(%d)", feature) : name;
297 }
298
DumpFileHeader()299 void DumpRecordCommand::DumpFileHeader() {
300 const FileHeader& header = record_file_reader_->FileHeader();
301 printf("magic: ");
302 for (size_t i = 0; i < 8; ++i) {
303 printf("%c", header.magic[i]);
304 }
305 printf("\n");
306 printf("header_size: %" PRId64 "\n", header.header_size);
307 if (header.header_size != sizeof(header)) {
308 PLOG(WARNING) << "record file header size " << header.header_size
309 << "doesn't match expected header size " << sizeof(header);
310 }
311 printf("attr_size: %" PRId64 "\n", header.attr_size);
312 printf("attrs[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.attrs.offset,
313 header.attrs.size);
314 printf("data[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.data.offset,
315 header.data.size);
316 printf("event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n",
317 header.event_types.offset, header.event_types.size);
318
319 std::vector<int> features;
320 for (size_t i = 0; i < FEAT_MAX_NUM; ++i) {
321 size_t j = i / 8;
322 size_t k = i % 8;
323 if ((header.features[j] & (1 << k)) != 0) {
324 features.push_back(i);
325 }
326 }
327 for (auto& feature : features) {
328 printf("feature: %s\n", GetFeatureNameOrUnknown(feature).c_str());
329 }
330 }
331
DumpAttrSection()332 void DumpRecordCommand::DumpAttrSection() {
333 const EventAttrIds& attrs = record_file_reader_->AttrSection();
334 for (size_t i = 0; i < attrs.size(); ++i) {
335 const auto& attr = attrs[i];
336 printf("attr %zu:\n", i + 1);
337 DumpPerfEventAttr(attr.attr, 1);
338 if (!attr.ids.empty()) {
339 printf(" ids:");
340 for (const auto& id : attr.ids) {
341 printf(" %" PRId64, id);
342 }
343 printf("\n");
344 }
345 }
346 }
347
DumpDataSection()348 bool DumpRecordCommand::DumpDataSection() {
349 thread_tree_.ShowIpForUnknownSymbol();
350 if (!record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_)) {
351 return false;
352 }
353
354 auto record_callback = [&](std::unique_ptr<Record> r) { return ProcessRecord(r.get()); };
355 return record_file_reader_->ReadDataSection(record_callback);
356 }
357
ProcessRecord(Record * r)358 bool DumpRecordCommand::ProcessRecord(Record* r) {
359 r->Dump();
360 thread_tree_.Update(*r);
361
362 bool res = true;
363 switch (r->type()) {
364 case PERF_RECORD_SAMPLE:
365 ProcessSampleRecord(*static_cast<SampleRecord*>(r));
366 break;
367 case SIMPLE_PERF_RECORD_CALLCHAIN:
368 ProcessCallChainRecord(*static_cast<CallChainRecord*>(r));
369 break;
370 case PERF_RECORD_AUXTRACE_INFO: {
371 etm_thread_tree_.reset(new ETMThreadTreeForDumpCmd(thread_tree_));
372 etm_decoder_ = ETMDecoder::Create(*static_cast<AuxTraceInfoRecord*>(r), *etm_thread_tree_);
373 if (etm_decoder_) {
374 etm_decoder_->EnableDump(etm_dump_option_);
375 } else {
376 res = false;
377 }
378 break;
379 }
380 case PERF_RECORD_AUX: {
381 res = DumpAuxData(*static_cast<AuxRecord*>(r));
382 break;
383 }
384 case PERF_RECORD_TRACING_DATA:
385 case SIMPLE_PERF_RECORD_TRACING_DATA: {
386 res = ProcessTracingData(*static_cast<TracingDataRecord*>(r));
387 break;
388 }
389 }
390 return res;
391 }
392
ProcessSampleRecord(const SampleRecord & sr)393 void DumpRecordCommand::ProcessSampleRecord(const SampleRecord& sr) {
394 bool in_kernel = sr.InKernel();
395 if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) {
396 PrintIndented(1, "callchain:\n");
397 for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) {
398 if (sr.callchain_data.ips[i] >= PERF_CONTEXT_MAX) {
399 if (sr.callchain_data.ips[i] == PERF_CONTEXT_USER) {
400 in_kernel = false;
401 }
402 continue;
403 }
404 SymbolInfo s =
405 GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i], in_kernel);
406 PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", s.symbol->DemangledName(), s.dso->Path().c_str(),
407 s.vaddr_in_file);
408 }
409 }
410 if (sr.sample_type & PERF_SAMPLE_BRANCH_STACK) {
411 PrintIndented(1, "branch_stack:\n");
412 for (size_t i = 0; i < sr.branch_stack_data.stack_nr; ++i) {
413 uint64_t from_ip = sr.branch_stack_data.stack[i].from;
414 uint64_t to_ip = sr.branch_stack_data.stack[i].to;
415 SymbolInfo from_symbol = GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, from_ip);
416 SymbolInfo to_symbol = GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, to_ip);
417 PrintIndented(2, "%s (%s[+%" PRIx64 "]) -> %s (%s[+%" PRIx64 "])\n",
418 from_symbol.symbol->DemangledName(), from_symbol.dso->Path().c_str(),
419 from_symbol.vaddr_in_file, to_symbol.symbol->DemangledName(),
420 to_symbol.dso->Path().c_str(), to_symbol.vaddr_in_file);
421 }
422 }
423 // Dump tracepoint fields.
424 if (!events_.empty()) {
425 size_t attr_index = record_file_reader_->GetAttrIndexOfRecord(&sr);
426 auto& event = events_[attr_index];
427 if (event.tp_data_size > 0 && sr.raw_data.size >= event.tp_data_size) {
428 PrintIndented(1, "tracepoint fields:\n");
429 for (size_t i = 0; i < event.tp_fields.size(); i++) {
430 auto& field = event.tp_fields[i];
431 std::string s = event.extract_field_functions[i](field, sr.raw_data);
432 PrintIndented(2, "%s: %s\n", field.name.c_str(), s.c_str());
433 }
434 }
435 }
436 }
437
ProcessCallChainRecord(const CallChainRecord & cr)438 void DumpRecordCommand::ProcessCallChainRecord(const CallChainRecord& cr) {
439 PrintIndented(1, "callchain:\n");
440 for (size_t i = 0; i < cr.ip_nr; ++i) {
441 SymbolInfo s = GetSymbolInfo(cr.pid, cr.tid, cr.ips[i], false);
442 PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", s.symbol->DemangledName(), s.dso->Path().c_str(),
443 s.vaddr_in_file);
444 }
445 }
446
GetSymbolInfo(uint32_t pid,uint32_t tid,uint64_t ip,std::optional<bool> in_kernel)447 SymbolInfo DumpRecordCommand::GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip,
448 std::optional<bool> in_kernel) {
449 ThreadEntry* thread = thread_tree_.FindThreadOrNew(pid, tid);
450 const MapEntry* map;
451 if (in_kernel.has_value()) {
452 map = thread_tree_.FindMap(thread, ip, in_kernel.value());
453 } else {
454 map = thread_tree_.FindMap(thread, ip);
455 }
456 SymbolInfo info;
457 info.symbol = thread_tree_.FindSymbol(map, ip, &info.vaddr_in_file, &info.dso);
458 return info;
459 }
460
DumpAuxData(const AuxRecord & aux)461 bool DumpRecordCommand::DumpAuxData(const AuxRecord& aux) {
462 if (aux.data->aux_size > SIZE_MAX) {
463 LOG(ERROR) << "invalid aux size";
464 return false;
465 }
466 size_t size = aux.data->aux_size;
467 if (size > 0) {
468 std::vector<uint8_t> data;
469 bool error = false;
470 if (!record_file_reader_->ReadAuxData(aux.Cpu(), aux.data->aux_offset, size, data, error)) {
471 return !error;
472 }
473 if (!etm_decoder_) {
474 LOG(ERROR) << "ETMDecoder isn't created";
475 return false;
476 }
477 return etm_decoder_->ProcessData(data.data(), size, !aux.Unformatted(), aux.Cpu());
478 }
479 return true;
480 }
481
ProcessTracingData(const TracingDataRecord & r)482 bool DumpRecordCommand::ProcessTracingData(const TracingDataRecord& r) {
483 auto tracing = Tracing::Create(std::vector<char>(r.data, r.data + r.data_size));
484 if (!tracing) {
485 return false;
486 }
487 const EventAttrIds& attrs = record_file_reader_->AttrSection();
488 events_.resize(attrs.size());
489 for (size_t i = 0; i < attrs.size(); i++) {
490 auto& attr = attrs[i].attr;
491 auto& event = events_[i];
492
493 if (attr.type != PERF_TYPE_TRACEPOINT) {
494 continue;
495 }
496 std::optional<TracingFormat> format = tracing->GetTracingFormatHavingId(attr.config);
497 if (!format.has_value()) {
498 LOG(ERROR) << "failed to get tracing format";
499 return false;
500 }
501 event.tp_fields = format.value().fields;
502 // Decide dump function for each field.
503 for (size_t j = 0; j < event.tp_fields.size(); j++) {
504 auto& field = event.tp_fields[j];
505 event.extract_field_functions.push_back(GetExtractFieldFunction(field));
506 event.tp_data_size += field.elem_count * field.elem_size;
507 }
508 }
509 return true;
510 }
511
DumpFeatureSection()512 bool DumpRecordCommand::DumpFeatureSection() {
513 std::map<int, SectionDesc> section_map = record_file_reader_->FeatureSectionDescriptors();
514 for (const auto& pair : section_map) {
515 int feature = pair.first;
516 const auto& section = pair.second;
517 std::string feature_name = GetFeatureNameOrUnknown(feature);
518 if (!dump_features_.empty() &&
519 std::count(dump_features_.begin(), dump_features_.end(), feature_name) == 0) {
520 continue;
521 }
522 printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n", feature_name.c_str(),
523 section.offset, section.size);
524 if (feature == FEAT_BUILD_ID) {
525 std::vector<BuildIdRecord> records = record_file_reader_->ReadBuildIdFeature();
526 for (auto& r : records) {
527 r.Dump(1);
528 }
529 } else if (feature == FEAT_OSRELEASE) {
530 std::string s = record_file_reader_->ReadFeatureString(feature);
531 PrintIndented(1, "osrelease: %s\n", s.c_str());
532 } else if (feature == FEAT_ARCH) {
533 std::string s = record_file_reader_->ReadFeatureString(feature);
534 PrintIndented(1, "arch: %s\n", s.c_str());
535 } else if (feature == FEAT_CMDLINE) {
536 std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
537 PrintIndented(1, "cmdline: %s\n", android::base::Join(cmdline, ' ').c_str());
538 } else if (feature == FEAT_FILE || feature == FEAT_FILE2) {
539 FileFeature file;
540 uint64_t read_pos = 0;
541 bool error = false;
542 PrintIndented(1, "file:\n");
543 while (record_file_reader_->ReadFileFeature(read_pos, file, error)) {
544 PrintIndented(2, "file_path %s\n", file.path.c_str());
545 PrintIndented(2, "file_type %s\n", DsoTypeToString(file.type));
546 PrintIndented(2, "min_vaddr 0x%" PRIx64 "\n", file.min_vaddr);
547 PrintIndented(2, "file_offset_of_min_vaddr 0x%" PRIx64 "\n", file.file_offset_of_min_vaddr);
548 PrintIndented(2, "symbols:\n");
549 for (const auto& symbol : file.symbols) {
550 PrintIndented(3, "%s [0x%" PRIx64 "-0x%" PRIx64 "]\n", symbol.DemangledName(),
551 symbol.addr, symbol.addr + symbol.len);
552 }
553 if (file.type == DSO_DEX_FILE) {
554 PrintIndented(2, "dex_file_offsets:\n");
555 for (uint64_t offset : file.dex_file_offsets) {
556 PrintIndented(3, "0x%" PRIx64 "\n", offset);
557 }
558 }
559 }
560 if (error) {
561 return false;
562 }
563 } else if (feature == FEAT_META_INFO) {
564 PrintIndented(1, "meta_info:\n");
565 for (auto& pair : record_file_reader_->GetMetaInfoFeature()) {
566 PrintIndented(2, "%s = %s\n", pair.first.c_str(), pair.second.c_str());
567 }
568 } else if (feature == FEAT_AUXTRACE) {
569 PrintIndented(1, "file_offsets_of_auxtrace_records:\n");
570 for (auto offset : record_file_reader_->ReadAuxTraceFeature()) {
571 PrintIndented(2, "%" PRIu64 "\n", offset);
572 }
573 } else if (feature == FEAT_DEBUG_UNWIND) {
574 PrintIndented(1, "debug_unwind:\n");
575 if (auto opt_debug_unwind = record_file_reader_->ReadDebugUnwindFeature(); opt_debug_unwind) {
576 for (const DebugUnwindFile& file : opt_debug_unwind.value()) {
577 PrintIndented(2, "path: %s\n", file.path.c_str());
578 PrintIndented(2, "size: %" PRIu64 "\n", file.size);
579 }
580 }
581 } else if (feature == FEAT_ETM_BRANCH_LIST) {
582 std::string data;
583 if (!record_file_reader_->ReadFeatureSection(FEAT_ETM_BRANCH_LIST, &data)) {
584 return false;
585 }
586 ETMBinaryMap binary_map;
587 if (!StringToETMBinaryMap(data, binary_map)) {
588 return false;
589 }
590 PrintIndented(1, "etm_branch_list:\n");
591 for (const auto& [key, binary] : binary_map) {
592 PrintIndented(2, "path: %s\n", key.path.c_str());
593 PrintIndented(2, "build_id: %s\n", key.build_id.ToString().c_str());
594 PrintIndented(2, "binary_type: %s\n", DsoTypeToString(binary.dso_type));
595 if (binary.dso_type == DSO_KERNEL) {
596 PrintIndented(2, "kernel_start_addr: 0x%" PRIx64 "\n", key.kernel_start_addr);
597 }
598 for (const auto& [addr, branches] : binary.GetOrderedBranchMap()) {
599 PrintIndented(3, "addr: 0x%" PRIx64 "\n", addr);
600 for (const auto& [branch, count] : branches) {
601 std::string s = "0b";
602 for (auto it = branch.rbegin(); it != branch.rend(); ++it) {
603 s.push_back(*it ? '1' : '0');
604 }
605 PrintIndented(3, "branch: %s\n", s.c_str());
606 PrintIndented(3, "count: %" PRIu64 "\n", count);
607 }
608 }
609 }
610 } else if (feature == FEAT_INIT_MAP) {
611 PrintIndented(1, "init_map:\n");
612 auto callback = [&](std::unique_ptr<Record> r) { return ProcessRecord(r.get()); };
613 if (!record_file_reader_->ReadInitMapFeature(callback)) {
614 return false;
615 }
616 }
617 }
618 return true;
619 }
620
621 } // namespace
622
RegisterDumpRecordCommand()623 void RegisterDumpRecordCommand() {
624 RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); });
625 }
626
627 } // namespace simpleperf
628