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