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