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   if (header.attr_size != sizeof(FileAttr)) {
288     LOG(WARNING) << "record file attr size " << header.attr_size
289                  << " doesn't match expected attr size " << sizeof(FileAttr);
290   }
291   printf("attrs[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.attrs.offset,
292          header.attrs.size);
293   printf("data[file section]: offset %" PRId64 ", size %" PRId64 "\n", header.data.offset,
294          header.data.size);
295   printf("event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n",
296          header.event_types.offset, header.event_types.size);
297 
298   std::vector<int> features;
299   for (size_t i = 0; i < FEAT_MAX_NUM; ++i) {
300     size_t j = i / 8;
301     size_t k = i % 8;
302     if ((header.features[j] & (1 << k)) != 0) {
303       features.push_back(i);
304     }
305   }
306   for (auto& feature : features) {
307     printf("feature: %s\n", GetFeatureNameOrUnknown(feature).c_str());
308   }
309 }
310 
DumpAttrSection()311 void DumpRecordCommand::DumpAttrSection() {
312   std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
313   for (size_t i = 0; i < attrs.size(); ++i) {
314     const auto& attr = attrs[i];
315     printf("attr %zu:\n", i + 1);
316     DumpPerfEventAttr(*attr.attr, 1);
317     if (!attr.ids.empty()) {
318       printf("  ids:");
319       for (const auto& id : attr.ids) {
320         printf(" %" PRId64, id);
321       }
322       printf("\n");
323     }
324   }
325 }
326 
DumpDataSection()327 bool DumpRecordCommand::DumpDataSection() {
328   thread_tree_.ShowIpForUnknownSymbol();
329   record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
330 
331   auto record_callback = [&](std::unique_ptr<Record> r) { return ProcessRecord(r.get()); };
332   return record_file_reader_->ReadDataSection(record_callback);
333 }
334 
ProcessRecord(Record * r)335 bool DumpRecordCommand::ProcessRecord(Record* r) {
336   r->Dump();
337   thread_tree_.Update(*r);
338 
339   bool res = true;
340   switch (r->type()) {
341     case PERF_RECORD_SAMPLE:
342       ProcessSampleRecord(*static_cast<SampleRecord*>(r));
343       break;
344     case SIMPLE_PERF_RECORD_CALLCHAIN:
345       ProcessCallChainRecord(*static_cast<CallChainRecord*>(r));
346       break;
347     case PERF_RECORD_AUXTRACE_INFO: {
348       etm_decoder_ = ETMDecoder::Create(*static_cast<AuxTraceInfoRecord*>(r), thread_tree_);
349       if (etm_decoder_) {
350         etm_decoder_->EnableDump(etm_dump_option_);
351       } else {
352         res = false;
353       }
354       break;
355     }
356     case PERF_RECORD_AUX: {
357       res = DumpAuxData(*static_cast<AuxRecord*>(r));
358       break;
359     }
360     case PERF_RECORD_TRACING_DATA:
361     case SIMPLE_PERF_RECORD_TRACING_DATA: {
362       ProcessTracingData(*static_cast<TracingDataRecord*>(r));
363       break;
364     }
365   }
366   return res;
367 }
368 
ProcessSampleRecord(const SampleRecord & sr)369 void DumpRecordCommand::ProcessSampleRecord(const SampleRecord& sr) {
370   bool in_kernel = sr.InKernel();
371   if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) {
372     PrintIndented(1, "callchain:\n");
373     for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) {
374       if (sr.callchain_data.ips[i] >= PERF_CONTEXT_MAX) {
375         if (sr.callchain_data.ips[i] == PERF_CONTEXT_USER) {
376           in_kernel = false;
377         }
378         continue;
379       }
380       SymbolInfo s =
381           GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i], in_kernel);
382       PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", s.symbol->DemangledName(), s.dso->Path().c_str(),
383                     s.vaddr_in_file);
384     }
385   }
386   // Dump tracepoint fields.
387   if (!events_.empty()) {
388     size_t attr_index = record_file_reader_->GetAttrIndexOfRecord(&sr);
389     auto& event = events_[attr_index];
390     if (event.tp_data_size > 0 && sr.raw_data.size >= event.tp_data_size) {
391       PrintIndented(1, "tracepoint fields:\n");
392       for (size_t i = 0; i < event.tp_fields.size(); i++) {
393         auto& field = event.tp_fields[i];
394         std::string s = event.extract_field_functions[i](field, sr.raw_data);
395         PrintIndented(2, "%s: %s\n", field.name.c_str(), s.c_str());
396       }
397     }
398   }
399 }
400 
ProcessCallChainRecord(const CallChainRecord & cr)401 void DumpRecordCommand::ProcessCallChainRecord(const CallChainRecord& cr) {
402   PrintIndented(1, "callchain:\n");
403   for (size_t i = 0; i < cr.ip_nr; ++i) {
404     SymbolInfo s = GetSymbolInfo(cr.pid, cr.tid, cr.ips[i], false);
405     PrintIndented(2, "%s (%s[+%" PRIx64 "])\n", s.symbol->DemangledName(), s.dso->Path().c_str(),
406                   s.vaddr_in_file);
407   }
408 }
409 
GetSymbolInfo(uint32_t pid,uint32_t tid,uint64_t ip,bool in_kernel)410 SymbolInfo DumpRecordCommand::GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip,
411                                             bool in_kernel) {
412   ThreadEntry* thread = thread_tree_.FindThreadOrNew(pid, tid);
413   const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
414   SymbolInfo info;
415   info.symbol = thread_tree_.FindSymbol(map, ip, &info.vaddr_in_file, &info.dso);
416   return info;
417 }
418 
DumpAuxData(const AuxRecord & aux)419 bool DumpRecordCommand::DumpAuxData(const AuxRecord& aux) {
420   size_t size = aux.data->aux_size;
421   if (size > 0) {
422     std::unique_ptr<uint8_t[]> data(new uint8_t[size]);
423     if (!record_file_reader_->ReadAuxData(aux.Cpu(), aux.data->aux_offset, data.get(), size)) {
424       return false;
425     }
426     return etm_decoder_->ProcessData(data.get(), size);
427   }
428   return true;
429 }
430 
ProcessTracingData(const TracingDataRecord & r)431 void DumpRecordCommand::ProcessTracingData(const TracingDataRecord& r) {
432   Tracing tracing(std::vector<char>(r.data, r.data + r.data_size));
433   std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
434   events_.resize(attrs.size());
435   for (size_t i = 0; i < attrs.size(); i++) {
436     auto& attr = attrs[i].attr;
437     auto& event = events_[i];
438 
439     if (attr->type != PERF_TYPE_TRACEPOINT) {
440       continue;
441     }
442     TracingFormat format = tracing.GetTracingFormatHavingId(attr->config);
443     event.tp_fields = format.fields;
444     // Decide dump function for each field.
445     for (size_t j = 0; j < event.tp_fields.size(); j++) {
446       auto& field = event.tp_fields[j];
447       event.extract_field_functions.push_back(GetExtractFieldFunction(field));
448       event.tp_data_size += field.elem_count * field.elem_size;
449     }
450   }
451 }
452 
DumpFeatureSection()453 bool DumpRecordCommand::DumpFeatureSection() {
454   std::map<int, SectionDesc> section_map = record_file_reader_->FeatureSectionDescriptors();
455   for (const auto& pair : section_map) {
456     int feature = pair.first;
457     const auto& section = pair.second;
458     printf("feature section for %s: offset %" PRId64 ", size %" PRId64 "\n",
459            GetFeatureNameOrUnknown(feature).c_str(), section.offset, section.size);
460     if (feature == FEAT_BUILD_ID) {
461       std::vector<BuildIdRecord> records = record_file_reader_->ReadBuildIdFeature();
462       for (auto& r : records) {
463         r.Dump(1);
464       }
465     } else if (feature == FEAT_OSRELEASE) {
466       std::string s = record_file_reader_->ReadFeatureString(feature);
467       PrintIndented(1, "osrelease: %s\n", s.c_str());
468     } else if (feature == FEAT_ARCH) {
469       std::string s = record_file_reader_->ReadFeatureString(feature);
470       PrintIndented(1, "arch: %s\n", s.c_str());
471     } else if (feature == FEAT_CMDLINE) {
472       std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
473       PrintIndented(1, "cmdline: %s\n", android::base::Join(cmdline, ' ').c_str());
474     } else if (feature == FEAT_FILE) {
475       FileFeature file;
476       size_t read_pos = 0;
477       PrintIndented(1, "file:\n");
478       while (record_file_reader_->ReadFileFeature(read_pos, &file)) {
479         PrintIndented(2, "file_path %s\n", file.path.c_str());
480         PrintIndented(2, "file_type %s\n", DsoTypeToString(file.type));
481         PrintIndented(2, "min_vaddr 0x%" PRIx64 "\n", file.min_vaddr);
482         PrintIndented(2, "file_offset_of_min_vaddr 0x%" PRIx64 "\n", file.file_offset_of_min_vaddr);
483         PrintIndented(2, "symbols:\n");
484         for (const auto& symbol : file.symbols) {
485           PrintIndented(3, "%s [0x%" PRIx64 "-0x%" PRIx64 "]\n", symbol.DemangledName(),
486                         symbol.addr, symbol.addr + symbol.len);
487         }
488         if (file.type == DSO_DEX_FILE) {
489           PrintIndented(2, "dex_file_offsets:\n");
490           for (uint64_t offset : file.dex_file_offsets) {
491             PrintIndented(3, "0x%" PRIx64 "\n", offset);
492           }
493         }
494       }
495     } else if (feature == FEAT_META_INFO) {
496       PrintIndented(1, "meta_info:\n");
497       for (auto& pair : record_file_reader_->GetMetaInfoFeature()) {
498         PrintIndented(2, "%s = %s\n", pair.first.c_str(), pair.second.c_str());
499       }
500     } else if (feature == FEAT_AUXTRACE) {
501       PrintIndented(1, "file_offsets_of_auxtrace_records:\n");
502       for (auto offset : record_file_reader_->ReadAuxTraceFeature()) {
503         PrintIndented(2, "%" PRIu64 "\n", offset);
504       }
505     } else if (feature == FEAT_DEBUG_UNWIND) {
506       PrintIndented(1, "debug_unwind:\n");
507       if (auto opt_debug_unwind = record_file_reader_->ReadDebugUnwindFeature(); opt_debug_unwind) {
508         for (const DebugUnwindFile& file : opt_debug_unwind.value()) {
509           PrintIndented(2, "path: %s\n", file.path.c_str());
510           PrintIndented(2, "size: %" PRIu64 "\n", file.size);
511         }
512       }
513     }
514   }
515   return true;
516 }
517 
518 }  // namespace
519 
RegisterDumpRecordCommand()520 void RegisterDumpRecordCommand() {
521   RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); });
522 }
523 
524 }  // namespace simpleperf
525