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:
DumpRecordCommand()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 
Run(const std::vector<std::string> & args)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 
ParseOptions(const std::vector<std::string> & args)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 
GetFeatureNameOrUnknown(int feature)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 
DumpFileHeader()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 
DumpAttrSection()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 
DumpDataSection()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 
DumpAuxData(const AuxRecord & aux,ETMDecoder & etm_decoder)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 
DumpFeatureSection()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 
RegisterDumpRecordCommand()314 void RegisterDumpRecordCommand() {
315   RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); });
316 }
317