• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #define HILOG_TAG "Dump"
16 
17 #include "subcommand_dump.h"
18 
19 #include <cerrno>
20 #include <cinttypes>
21 #include <cstring>
22 #include <iostream>
23 #include <memory>
24 
25 #include "debug_logger.h"
26 #include "option.h"
27 #include "perf_event_record.h"
28 #include "perf_events.h"
29 #include "symbols_file.h"
30 #include "utilities.h"
31 #include "virtual_runtime.h"
32 
33 namespace OHOS {
34 namespace Developtools {
35 namespace HiPerf {
36 #define LEVEL1 (indent + 1)
37 #define LEVEL2 (indent + 2)
38 #define LEVEL3 (indent + 3)
39 
CheckInputFile()40 bool SubCommandDump::CheckInputFile()
41 {
42     if (!dumpFileName_.empty()) {
43         if (elfFileName_.empty() && protobufDumpFileName_.empty()) {
44             return true;
45         }
46     } else if (!elfFileName_.empty()) {
47         if (protobufDumpFileName_.empty()) {
48             return true;
49         }
50     } else if (!protobufDumpFileName_.empty()) {
51         return true;
52     } else { // all is empty
53         dumpFileName_ = DEFAULT_DUMP_FILENAME;
54         return true;
55     }
56 
57     printf("options conflict, please check usage\n");
58     return false;
59 }
60 
ParseOption(std::vector<std::string> & args)61 bool SubCommandDump::ParseOption(std::vector<std::string> &args)
62 {
63     HLOGV("enter");
64 
65     if (!Option::GetOptionValue(args, "--head", dumpHeader_)) {
66         HLOGD("get option --head failed");
67         return false;
68     }
69     if (!Option::GetOptionValue(args, "-f", dumpFeatures_)) {
70         HLOGD("get option -f failed");
71         return false;
72     }
73     if (!Option::GetOptionValue(args, "-d", dumpData_)) {
74         HLOGD("get option -d failed");
75         return false;
76     }
77     if (!Option::GetOptionValue(args, "--sympath", dumpSymbolsPaths_)) {
78         HLOGD("get option --sympath failed");
79         return false;
80     }
81     if (!Option::GetOptionValue(args, "--elf", elfFileName_)) {
82         HLOGD("get option --elf failed");
83         return false;
84     }
85 #if HAVE_PROTOBUF
86     if (!Option::GetOptionValue(args, "--proto", protobufDumpFileName_)) {
87         HLOGD("get option --proto failed");
88         return false;
89     }
90 #endif
91     if (!Option::GetOptionValue(args, "--export", exportSampleIndex_)) {
92         HLOGD("get option --export failed");
93         return false;
94     }
95 
96     if (dumpHeader_ || dumpFeatures_ || dumpData_) {
97         dumpAll_ = false;
98     }
99 
100     if (args.size() > 1) {
101         printf("unknown option %s\n", args[0].c_str());
102         return false;
103     }
104     if (args.size() == 1) {
105         dumpFileName_ = args[0];
106         args.clear();
107     }
108 
109     return CheckInputFile();
110 }
111 
~SubCommandDump()112 SubCommandDump::~SubCommandDump()
113 {
114     SymbolsFile::onRecording_ = true; // back to default for UT
115 }
116 
OnSubCommand(std::vector<std::string> & args)117 bool SubCommandDump::OnSubCommand(std::vector<std::string> &args)
118 {
119     HLOGV("enter");
120 
121     if (!elfFileName_.empty()) {
122         return DumpElfFile();
123     }
124 
125 #if HAVE_PROTOBUF
126     if (!protobufDumpFileName_.empty()) {
127         return DumpProtoFile();
128     }
129 #endif
130 
131     if (access(dumpFileName_.c_str(), F_OK) != 0) {
132         printf("Can not access data file %s\n", dumpFileName_.c_str());
133         return false;
134     }
135     // only one file should created
136     HLOG_ASSERT_MESSAGE(reader_ == nullptr, " reader_ %p\n", reader_.get());
137     reader_ = PerfFileReader::Instance(dumpFileName_);
138     if (reader_ == nullptr) {
139         HLOGE("HiperfFileReader::Instance(%s) return null", dumpFileName_.c_str());
140         return false;
141     }
142 
143     // any way tell symbols this is not on device
144     SymbolsFile::onRecording_ = false;
145     // we need unwind it (for function name match) even not give us path
146     vr_.SetDisableUnwind(false);
147 
148     if (!dumpSymbolsPaths_.empty()) {
149         // user give us path , we enable unwind
150         if (!vr_.SetSymbolsPaths(dumpSymbolsPaths_)) {
151             printf("Failed to set symbol path(%s)\n", VectorToString(dumpSymbolsPaths_).c_str());
152             return false;
153         }
154     }
155 
156     if (dumpHeader_ || dumpAll_) {
157         DumpPrintFileHeader(indent_);
158         DumpAttrPortion(indent_);
159     }
160 
161     if (dumpAll_ || dumpData_) {
162         DumpDataPortion(indent_);
163     }
164 
165     if (dumpFeatures_ || dumpAll_) {
166         DumpFeaturePortion(indent_);
167     }
168 
169     return true;
170 }
171 
DumpElfFile()172 bool SubCommandDump::DumpElfFile()
173 {
174     printf("dump elf: '%s'\n", elfFileName_.c_str());
175     auto elf = SymbolsFile::CreateSymbolsFile(elfFileName_);
176     if (!elf->LoadSymbols("")) {
177         printf("load elf failed.\n");
178         return false;
179     } else {
180         printf("load elf succeed.\n");
181     }
182     return true;
183 }
184 #if HAVE_PROTOBUF
DumpProtoFile()185 bool SubCommandDump::DumpProtoFile()
186 {
187     printf("dump protobuf file: '%s'\n", protobufDumpFileName_.c_str());
188     protobufInputFileReader_ = std::make_unique<ReportProtobufFileReader>();
189     if (!protobufInputFileReader_->Dump(protobufDumpFileName_)) {
190         printf("load proto failed.\n");
191         return false;
192     }
193     return true;
194 }
195 #endif
196 
PrintHeaderInfo(const int & indent)197 void SubCommandDump::PrintHeaderInfo(const int &indent)
198 {
199     const perf_file_header &header = reader_->GetHeader();
200     // magic
201     PrintIndent(indent, "magic: ");
202     for (size_t i = 0; i < sizeof(header.magic); ++i) {
203         PrintIndent(indent, "%c", header.magic[i]);
204     }
205     PrintIndent(indent, "\n");
206     PrintIndent(indent, "header_size: %" PRId64 "\n", header.size);
207     if (header.size != sizeof(header)) {
208         HLOGW("record file header size doesn't match");
209     }
210     PrintIndent(indent, "attr_size: %" PRId64 "\n", header.attrSize);
211     if (header.attrSize != sizeof(perf_file_attr)) {
212         HLOGW("attr size doesn't match");
213     }
214     // attr
215     PrintIndent(indent, "attrs[file section]: offset %" PRId64 ", size %" PRId64 "\n",
216                 header.attrs.offset, header.attrs.size);
217     // data
218     PrintIndent(indent, "data[file section]: offset %" PRId64 ", size %" PRId64 "\n",
219                 header.data.offset, header.data.size);
220     PrintIndent(indent, "event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n",
221                 header.eventTypes.offset, header.eventTypes.size);
222     // feature
223     PrintIndent(indent,
224                 "adds_features[]: 0x%" PRIX64 " 0x%" PRIX64 " 0x%" PRIX64 " 0x%" PRIX64 "\n",
225                 *(reinterpret_cast<const uint64_t *>(&header.features[0])),
226                 *(reinterpret_cast<const uint64_t *>(&header.features[8])),
227                 *(reinterpret_cast<const uint64_t *>(&header.features[16])),
228                 *(reinterpret_cast<const uint64_t *>(&header.features[24])));
229 }
230 
DumpPrintFileHeader(int indent)231 void SubCommandDump::DumpPrintFileHeader(int indent)
232 {
233     // print header
234     PrintHeaderInfo(indent);
235 
236     // print feature
237     auto features = reader_->GetFeatures();
238     for (auto feature : features) {
239         PrintIndent(indent, "feature: %s\n", PerfFileSection::GetFeatureName(feature).c_str());
240     }
241 
242     // read here , because we need found symbols
243     reader_->ReadFeatureSection();
244 
245     SetDeviceArch(GetArchTypeFromUname(reader_->GetFeatureString(FEATURE::ARCH)));
246 
247     // found symbols in file
248     for (auto &featureSection : reader_->GetFeatureSections()) {
249         if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_SYMBOL) {
250             const PerfFileSectionSymbolsFiles *sectionSymbolsFiles =
251                 static_cast<const PerfFileSectionSymbolsFiles *>(featureSection.get());
252             vr_.UpdateFromPerfData(sectionSymbolsFiles->symbolFileStructs_);
253         }
254     }
255 }
256 
257 static std::map<int, std::string> g_sampleTypeNames = {
258     {PERF_SAMPLE_IP, "ip"},
259     {PERF_SAMPLE_TID, "tid"},
260     {PERF_SAMPLE_TIME, "time"},
261     {PERF_SAMPLE_ADDR, "addr"},
262     {PERF_SAMPLE_READ, "read"},
263     {PERF_SAMPLE_CALLCHAIN, "callchain"},
264     {PERF_SAMPLE_ID, "id"},
265     {PERF_SAMPLE_CPU, "cpu"},
266     {PERF_SAMPLE_PERIOD, "period"},
267     {PERF_SAMPLE_STREAM_ID, "stream_id"},
268     {PERF_SAMPLE_RAW, "raw"},
269     {PERF_SAMPLE_BRANCH_STACK, "stack"},
270     {PERF_SAMPLE_REGS_USER, "regs_user"},
271     {PERF_SAMPLE_STACK_USER, "stack_user"},
272     {PERF_SAMPLE_WEIGHT, "weight"},
273     {PERF_SAMPLE_DATA_SRC, "data_src"},
274     {PERF_SAMPLE_IDENTIFIER, "identifier"},
275     {PERF_SAMPLE_TRANSACTION, "transaction"},
276     {PERF_SAMPLE_REGS_INTR, "reg_intr"},
277 };
278 
DumpSampleType(uint64_t sampleType,int indent)279 void SubCommandDump::DumpSampleType(uint64_t sampleType, int indent)
280 {
281     std::string names;
282     for (auto &pair : g_sampleTypeNames) {
283         if (sampleType & pair.first) {
284             if (!names.empty()) {
285                 names.append(",");
286             }
287             names.append(pair.second);
288         }
289     }
290     PrintIndent(LEVEL1, "sample_type names: %s\n", names.c_str());
291 }
292 
DumpPrintEventAttr(const perf_event_attr & attr,int indent)293 void SubCommandDump::DumpPrintEventAttr(const perf_event_attr &attr, int indent)
294 {
295     PrintIndent(indent, "event_attr: \n");
296 
297     PrintIndent(LEVEL1, "type %u, size %u, config %llu\n", attr.type, attr.size, attr.config);
298 
299     if (attr.freq != 0) {
300         PrintIndent(LEVEL1, "sample_freq %llu\n", attr.sample_freq);
301     } else {
302         PrintIndent(LEVEL1, "sample_period %llu\n", attr.sample_period);
303     }
304 
305     PrintIndent(LEVEL1, "sample_type (0x%llx) \n", attr.sample_type);
306     DumpSampleType(attr.sample_type, indent);
307 
308     PrintIndent(LEVEL1, "read_format (0x%llx) \n", attr.read_format);
309 
310     PrintIndent(LEVEL1, "disabled %u, inherit %u, pinned %u, exclusive %u\n", attr.disabled,
311                 attr.inherit, attr.pinned, attr.exclusive);
312 
313     PrintIndent(LEVEL1, "exclude_user %u, exclude_kernel %u, exclude_hv %u, exclude_idle %u\n",
314                 attr.exclude_user, attr.exclude_kernel, attr.exclude_hv, attr.exclude_idle);
315 
316     PrintIndent(LEVEL1, "mmap %u, mmap2 %u, comm %u, comm_exec %u, freq %u\n", attr.mmap,
317                 attr.mmap2, attr.comm, attr.comm_exec, attr.freq);
318 
319     PrintIndent(LEVEL1, "inherit_stat %u, enable_on_exec %u, task %u, use_clockid %u\n",
320                 attr.inherit_stat, attr.enable_on_exec, attr.task, attr.use_clockid);
321 
322     PrintIndent(LEVEL1, "watermark %u, precise_ip %u, mmap_data %u, clockid %u\n", attr.watermark,
323                 attr.precise_ip, attr.mmap_data, attr.clockid);
324 
325     PrintIndent(LEVEL1, "sample_id_all %u, exclude_host %u, exclude_guest %u\n", attr.sample_id_all,
326                 attr.exclude_host, attr.exclude_guest);
327     PrintIndent(LEVEL1, "branch_sample_type 0x%llx\n", attr.branch_sample_type);
328     PrintIndent(LEVEL1, "exclude_callchain_kernel %u, exclude_callchain_user %u\n",
329                 attr.exclude_callchain_kernel, attr.exclude_callchain_user);
330     PrintIndent(LEVEL1, "sample_regs_user 0x%llx\n", attr.sample_regs_user);
331     PrintIndent(LEVEL1, "sample_stack_user 0x%x\n", attr.sample_stack_user);
332 }
333 
DumpAttrPortion(int indent)334 void SubCommandDump::DumpAttrPortion(int indent)
335 {
336     attrIds_ = reader_->GetAttrSection();
337     for (size_t i = 0; i < attrIds_.size(); ++i) {
338         const AttrWithId &attr = attrIds_[i];
339         PrintIndent(indent, "attr %zu:\n", i + 1);
340         DumpPrintEventAttr(attr.attr, indent_ + 1);
341         if (!attr.ids.empty()) {
342             PrintIndent(indent, "  ids:");
343             for (const auto &id : attr.ids) {
344                 PrintIndent(indent, " %" PRId64, id);
345             }
346             PrintIndent(indent, "\n");
347         }
348     }
349 }
350 
ExprotUserStack(const PerfRecordSample & recordSample)351 void SubCommandDump::ExprotUserStack(const PerfRecordSample &recordSample)
352 {
353     if (recordSample.data_.reg_nr > 0 and recordSample.data_.dyn_size > 0) {
354         // <pid>_<tid>_user_regs_<time>
355         std::string userRegs =
356             StringPrintf("hiperf_%d_%d_user_regs_%zu.dump", recordSample.data_.pid,
357                          recordSample.data_.tid, exportSampleIndex_);
358         std::string resolvedPath = CanonicalizeSpecPath(userRegs.c_str());
359         std::unique_ptr<FILE, decltype(&fclose)> fpUserRegs(fopen(resolvedPath.c_str(), "wb"), fclose);
360         fwrite(recordSample.data_.user_regs, sizeof(u64), recordSample.data_.reg_nr,
361                fpUserRegs.get());
362 
363         std::string userData =
364             StringPrintf("hiperf_%d_%d_user_data_%zu.dump", recordSample.data_.pid,
365                          recordSample.data_.tid, exportSampleIndex_);
366         std::string resolvePath = CanonicalizeSpecPath(userData.c_str());
367         std::unique_ptr<FILE, decltype(&fclose)> fpUserData(fopen(resolvePath.c_str(), "wb"), fclose);
368         fwrite(recordSample.data_.stack_data, sizeof(u8), recordSample.data_.dyn_size,
369                fpUserData.get());
370     }
371 }
372 
ExprotUserData(std::unique_ptr<PerfEventRecord> & record)373 void SubCommandDump::ExprotUserData(std::unique_ptr<PerfEventRecord> &record)
374 {
375     if (record->GetType() == PERF_RECORD_SAMPLE) {
376         if (currectSampleIndex_++ != exportSampleIndex_) {
377             return;
378         }
379         PerfRecordSample *recordSample = static_cast<PerfRecordSample *>(record.get());
380         ExprotUserStack(*recordSample);
381 
382         std::string userData =
383             StringPrintf("hiperf_%d_%d_sample_record_%zu_%" PRIu64 ".dump", recordSample->data_.pid,
384                          recordSample->data_.tid, exportSampleIndex_, recordSample->data_.time);
385         std::string resolvedPath = CanonicalizeSpecPath(userData.c_str());
386         std::unique_ptr<FILE, decltype(&fclose)> fpUserData(fopen(resolvedPath.c_str(), "wb"), fclose);
387         std::vector<u8> buf(RECORD_SIZE_LIMIT);
388         if (!recordSample->GetBinary(buf)) {
389             HLOGE("export user sample data failed");
390             return;
391         }
392         fwrite(buf.data(), sizeof(u8), recordSample->GetSize(), fpUserData.get());
393 
394         HLOGD("export user data index %d time %llu", exportSampleIndex_, recordSample->data_.time);
395     }
396 }
DumpCallChain(int indent,std::unique_ptr<PerfRecordSample> & sample)397 void SubCommandDump::DumpCallChain(int indent, std::unique_ptr<PerfRecordSample> &sample)
398 {
399     PrintIndent(indent, "\n callchain: %zu\n", sample->callFrames_.size());
400     if (sample->callFrames_.size() > 0) {
401         indent += LEVEL1;
402         for (auto frameIt = sample->callFrames_.begin(); frameIt != sample->callFrames_.end();
403              frameIt++) {
404             PrintIndent(indent, "%02zd:%s\n", std::distance(frameIt, sample->callFrames_.end()),
405                         frameIt->ToSymbolString().c_str());
406         }
407     }
408 }
409 
DumpDataPortion(int indent)410 void SubCommandDump::DumpDataPortion(int indent)
411 {
412     int recordCount = 0;
413     auto record_callback = [&](std::unique_ptr<PerfEventRecord> record) {
414         if (record == nullptr) {
415             // return false in callback can stop the read process
416             return false;
417         }
418 
419         // tell process tree what happend for rebuild symbols
420         vr_.UpdateFromRecord(*record);
421 
422         recordCount++;
423         record->Dump(indent);
424 
425         if (exportSampleIndex_ > 0) {
426             ExprotUserData(record);
427         }
428 
429         if (record->GetType() == PERF_RECORD_SAMPLE) {
430             std::unique_ptr<PerfRecordSample> sample(
431                 static_cast<PerfRecordSample *>(record.release()));
432             DumpCallChain(indent, sample);
433         }
434 
435         return true;
436     };
437 
438     reader_->ReadDataSection(record_callback);
439 
440     PrintIndent(indent, "\n ======= there are %d records ======== \n", recordCount);
441 }
442 
PrintSymbolFile(const int & indent,const SymbolFileStruct & symbolFileStruct)443 void SubCommandDump::PrintSymbolFile(const int &indent, const SymbolFileStruct &symbolFileStruct)
444 {
445     PrintIndent(LEVEL2, "filePath:%s\n", symbolFileStruct.filePath_.c_str());
446     PrintIndent(LEVEL2, "symbolType:%d\n", symbolFileStruct.symbolType_);
447     PrintIndent(LEVEL2, "minExecAddr:0x%" PRIx64 "\n", symbolFileStruct.textExecVaddr_);
448     PrintIndent(LEVEL2, "minExecAddrFileOffset:0x%08" PRIx64 "\n",
449                 symbolFileStruct.textExecVaddrFileOffset_);
450     if (!symbolFileStruct.buildId_.empty()) {
451         PrintIndent(LEVEL2, "buildId:'%s'\n", symbolFileStruct.buildId_.c_str());
452     }
453     PrintIndent(LEVEL2, "symbol number: %zu\n", symbolFileStruct.symbolStructs_.size());
454     int symbolid = 0;
455     for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
456         PrintIndent(LEVEL3, "%05d [0x%016" PRIx64 "@0x%08x]  %s\n", symbolid++, symbolStruct.vaddr_,
457                     symbolStruct.len_, symbolStruct.symbolName_.c_str());
458     }
459 }
460 
PrintFeatureEventdesc(int indent,const PerfFileSectionEventDesc & sectionEventdesc)461 void SubCommandDump::PrintFeatureEventdesc(int indent,
462                                            const PerfFileSectionEventDesc &sectionEventdesc)
463 {
464     PrintIndent(LEVEL2, "Event descriptions: %zu\n", sectionEventdesc.eventDesces_.size());
465     for (size_t i = 0; i < sectionEventdesc.eventDesces_.size(); i++) {
466         const AttrWithId &desc = sectionEventdesc.eventDesces_[i];
467         PrintIndent(LEVEL2, "event name[%zu]: %s ids: %s\n", i, desc.name.c_str(),
468                     VectorToString(desc.ids).c_str());
469 
470         // attr is duplicated the attrs section
471     }
472     PrintIndent(LEVEL2, "\n");
473 }
474 
DumpFeaturePortion(int indent)475 void SubCommandDump::DumpFeaturePortion(int indent)
476 {
477     PrintIndent(indent, "\n ==== features ====\n");
478     auto features = reader_->GetFeatures();
479     for (auto feature : features) {
480         PrintIndent(LEVEL1, "feature %d:%s\n", feature,
481                     PerfFileSection::GetFeatureName(feature).c_str());
482     }
483 
484     const auto &featureSections = reader_->GetFeatureSections();
485     HLOGV("featureSections: %zu ", featureSections.size());
486 
487     PrintIndent(indent, "\n ==== feature sections ====\n");
488 
489     for (auto &featureSection : featureSections) {
490         PrintIndent(LEVEL1, "feature %d:%s content: \n", featureSection.get()->featureId_,
491                     PerfFileSection::GetFeatureName(featureSection.get()->featureId_).c_str());
492         if (reader_->IsFeatrureStringSection(featureSection.get()->featureId_)) {
493             const PerfFileSectionString *sectionString =
494                 static_cast<const PerfFileSectionString *>(featureSection.get());
495             PrintIndent(LEVEL2, "%s\n", sectionString->toString().c_str());
496             continue;
497         } else if (featureSection.get()->featureId_ == FEATURE::EVENT_DESC) {
498             PrintFeatureEventdesc(
499                 indent, *static_cast<const PerfFileSectionEventDesc *>(featureSection.get()));
500             continue;
501         }
502 
503         const PerfFileSectionSymbolsFiles *sectionSymbolsFiles =
504             static_cast<const PerfFileSectionSymbolsFiles *>(featureSection.get());
505         if (sectionSymbolsFiles != nullptr) {
506             PrintIndent(LEVEL2, "SymbolFiles:%zu\n",
507                         sectionSymbolsFiles->symbolFileStructs_.size());
508 
509             int fileid = 0;
510             for (auto &symbolFileStruct : sectionSymbolsFiles->symbolFileStructs_) {
511                 PrintIndent(LEVEL2, "\n");
512                 PrintIndent(LEVEL2, "fileid:%d\n", fileid++);
513                 // symbol file info
514                 PrintSymbolFile(indent, symbolFileStruct);
515             }
516             continue;
517         }
518 
519         PrintIndent(LEVEL2, "not support dump this feature.\n");
520     }
521 }
522 
RegisterSubCommandDump()523 bool SubCommandDump::RegisterSubCommandDump()
524 {
525     HLOGV("enter");
526     return SubCommand::RegisterSubCommand("dump", std::make_unique<SubCommandDump>());
527 }
528 } // namespace HiPerf
529 } // namespace Developtools
530 } // namespace OHOS