• 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 "hiperf_hilog.h"
27 #include "option.h"
28 #include "perf_event_record.h"
29 #include "perf_events.h"
30 #include "register.h"
31 #include "spe_decoder.h"
32 #include "symbols_file.h"
33 #include "utilities.h"
34 #include "virtual_runtime.h"
35 
36 namespace OHOS {
37 namespace Developtools {
38 namespace HiPerf {
39 using namespace OHOS::HiviewDFX;
40 
41 static const std::string DEFAULT_DUMP_FILENAME = "perf.data";
42 
CheckInputFile()43 bool SubCommandDump::CheckInputFile()
44 {
45     if (!dumpFileName_.empty()) {
46         if (elfFileName_.empty() && protobufDumpFileName_.empty()) {
47             return true;
48         }
49     } else if (!elfFileName_.empty()) {
50         if (protobufDumpFileName_.empty()) {
51             return true;
52         }
53     } else if (!protobufDumpFileName_.empty()) {
54         return true;
55     } else { // all is empty
56         dumpFileName_ = DEFAULT_DUMP_FILENAME;
57         return true;
58     }
59 
60     printf("options conflict, please check usage\n");
61     return false;
62 }
63 
CheckOutputFile()64 bool SubCommandDump::CheckOutputFile()
65 {
66     if (!IsValidOutPath(outputFilename_)) {
67         printf("Invalid output file path, permission denied\n");
68         return false;
69     }
70     return true;
71 }
72 
CheckOptions()73 bool SubCommandDump::CheckOptions()
74 {
75     if (!CheckInputFile()) {
76         return false;
77     }
78     if (!CheckOutputFile()) {
79         return false;
80     }
81     return true;
82 }
ParseOption(std::vector<std::string> & args)83 bool SubCommandDump::ParseOption(std::vector<std::string> &args)
84 {
85     if (!Option::GetOptionValue(args, "--head", dumpHeader_)) {
86         HLOGD("get option --head failed");
87         return false;
88     }
89     if (!Option::GetOptionValue(args, "-f", dumpFeatures_)) {
90         HLOGD("get option -f failed");
91         return false;
92     }
93     if (!Option::GetOptionValue(args, "-d", dumpData_)) {
94         HLOGD("get option -d failed");
95         return false;
96     }
97     if (!Option::GetOptionValue(args, "--sympath", dumpSymbolsPaths_)) {
98         HLOGD("get option --sympath failed");
99         return false;
100     }
101     if (!Option::GetOptionValue(args, "--elf", elfFileName_)) {
102         HLOGD("get option --elf failed");
103         return false;
104     }
105     if (!Option::GetOptionValue(args, "-i", dumpFileName_)) {
106         return false;
107     }
108 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
109     if (!Option::GetOptionValue(args, "--proto", protobufDumpFileName_)) {
110         HLOGD("get option --proto failed");
111         return false;
112     }
113 #endif
114     if (!Option::GetOptionValue(args, "-o", outputFilename_)) {
115         return false;
116     }
117     if (!Option::GetOptionValue(args, "--export", exportSampleIndex_)) {
118         HLOGD("get option --export failed");
119         return false;
120     }
121 
122     if (dumpHeader_ || dumpFeatures_ || dumpData_) {
123         dumpAll_ = false;
124     }
125     if (!args.empty()) {
126         printf("'%s' option usage error, please check usage.\n", VectorToString(args).c_str());
127         return false;
128     }
129 
130     return CheckOptions();
131 }
132 
PrepareDumpOutput()133 bool SubCommandDump::PrepareDumpOutput()
134 {
135     if (outputFilename_.empty()) {
136         return true;
137     }
138     std::string resolvedPath = CanonicalizeSpecPath(outputFilename_.c_str());
139     g_outputDump = fopen(resolvedPath.c_str(), "w");
140     if (g_outputDump == nullptr) {
141         printf("unable open file to '%s' because '%d'\n", outputFilename_.c_str(), errno);
142         return false;
143     }
144     printf("dump result will save at '%s'\n", outputFilename_.c_str());
145     return true;
146 }
147 
~SubCommandDump()148 SubCommandDump::~SubCommandDump()
149 {
150     if (g_outputDump != nullptr && g_outputDump != stdout) {
151         fclose(g_outputDump);
152     }
153     SymbolsFile::onRecording_ = true; // back to default for UT
154 }
155 
OnSubCommand(std::vector<std::string> & args)156 HiperfError SubCommandDump::OnSubCommand(std::vector<std::string>& args)
157 {
158     RETURN_IF(!PrepareDumpOutput(), HiperfError::PREPARE_DUMP_OUTPUT_FAIL);
159 
160     if (!elfFileName_.empty()) {
161         return DumpElfFile() ? HiperfError::NO_ERR : HiperfError::DUMP_ELF_FILE_ERROR;
162     }
163 
164 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
165     if (!protobufDumpFileName_.empty()) {
166         return DumpProtoFile() ? HiperfError::NO_ERR : HiperfError::DUMP_PROTO_FILE_ERROR;
167     }
168 #endif
169 
170     if (access(dumpFileName_.c_str(), F_OK) != 0) {
171         printf("Can not access data file %s\n", dumpFileName_.c_str());
172         return HiperfError::ACCESS_DATA_FILE_FAIL;
173     }
174     // only one file should created
175     HLOG_ASSERT_MESSAGE(reader_ == nullptr, " perf file reader for %s\n", dumpFileName_.c_str());
176     reader_ = PerfFileReader::Instance(dumpFileName_);
177     if (reader_ == nullptr) {
178         HLOGE("HiperfFileReader::Instance(%s) return null", dumpFileName_.c_str());
179         return HiperfError::OPEN_DATA_FILE_FAIL;
180     }
181 
182     // any way tell symbols this is not on device
183     SymbolsFile::onRecording_ = false;
184     // we need unwind it (for function name match) even not give us path
185     vr_.SetDisableUnwind(false);
186 
187     if (!dumpSymbolsPaths_.empty()) {
188         // user give us path , we enable unwind
189         if (!vr_.SetSymbolsPaths(dumpSymbolsPaths_)) {
190             printf("Failed to set symbol path(%s)\n", VectorToString(dumpSymbolsPaths_).c_str());
191             return HiperfError::SET_SYMBOLS_PATH_FAIL;
192         }
193     }
194 
195     if (dumpHeader_ || dumpAll_) {
196         DumpPrintFileHeader(indent_);
197         DumpAttrPortion(indent_);
198     }
199 
200     if (dumpAll_ || dumpData_) {
201         // before load data section
202         SetHM();
203         DumpDataPortion(indent_);
204         DumpSpeReport();
205     }
206 
207     if (dumpFeatures_ || dumpAll_) {
208         DumpFeaturePortion(indent_);
209     }
210     AgeHiperflogFiles();
211     return HiperfError::NO_ERR;
212 }
213 
DumpElfFile()214 bool SubCommandDump::DumpElfFile()
215 {
216     printf("dump elf: '%s'\n", elfFileName_.c_str());
217     auto elf = SymbolsFile::CreateSymbolsFile(elfFileName_);
218     if (!elf->LoadSymbols(nullptr, "")) {
219         printf("load elf failed.\n");
220         return false;
221     } else {
222         printf("load elf succeed.\n");
223     }
224     return true;
225 }
226 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
DumpProtoFile()227 bool SubCommandDump::DumpProtoFile()
228 {
229     printf("dump protobuf file: '%s'\n", protobufDumpFileName_.c_str());
230     protobufInputFileReader_ = std::make_unique<ReportProtobufFileReader>();
231     if (!protobufInputFileReader_->Dump(protobufDumpFileName_)) {
232         printf("load proto failed.\n");
233         return false;
234     }
235     return true;
236 }
237 #endif
238 
PrintHeaderInfo(const int & indent)239 void SubCommandDump::PrintHeaderInfo(const int &indent)
240 {
241     const perf_file_header &header = reader_->GetHeader();
242     // magic
243     PRINT_INDENT(indent, "magic: ");
244     for (size_t i = 0; i < sizeof(header.magic); ++i) {
245         PRINT_INDENT(indent, "%c", header.magic[i]);
246     }
247     PRINT_INDENT(indent, "\n");
248     PRINT_INDENT(indent, "header_size: %" PRId64 "\n", header.size);
249     if (header.size != sizeof(header)) {
250         HLOGW("record file header size doesn't match");
251     }
252     PRINT_INDENT(indent, "attr_size: %" PRId64 "\n", header.attrSize);
253     if (header.attrSize != sizeof(perf_file_attr)) {
254         HLOGW("attr size doesn't match");
255     }
256     // attr
257     PRINT_INDENT(indent, "attrs[file section]: offset %" PRId64 ", size %" PRId64 "\n",
258                  header.attrs.offset, header.attrs.size);
259     // data
260     PRINT_INDENT(indent, "data[file section]: offset %" PRId64 ", size %" PRId64 "\n",
261                  header.data.offset, header.data.size);
262     PRINT_INDENT(indent, "event_types[file section]: offset %" PRId64 ", size %" PRId64 "\n",
263                  header.eventTypes.offset, header.eventTypes.size);
264     // feature
265     PRINT_INDENT(indent,
266                  "adds_features[]: 0x%" PRIX64 " 0x%" PRIX64 " 0x%" PRIX64 " 0x%" PRIX64 "\n",
267                  *(reinterpret_cast<const uint64_t *>(&header.features[0])),
268                  *(reinterpret_cast<const uint64_t *>(&header.features[8])),
269                  *(reinterpret_cast<const uint64_t *>(&header.features[16])),
270                  *(reinterpret_cast<const uint64_t *>(&header.features[24])));
271 }
272 
DumpPrintFileHeader(const int indent)273 void SubCommandDump::DumpPrintFileHeader(const int indent)
274 {
275     // print header
276     PrintHeaderInfo(indent);
277 
278     // print feature
279     auto features = reader_->GetFeatures();
280     for (auto feature : features) {
281         PRINT_INDENT(indent, "feature: %s\n", PerfFileSection::GetFeatureName(feature).c_str());
282     }
283 
284     // read here , because we need found symbols
285     reader_->ReadFeatureSection();
286 
287     SetDeviceArch(GetArchTypeFromUname(reader_->GetFeatureString(FEATURE::ARCH)));
288 
289     // found symbols in file
290     for (auto &featureSection : reader_->GetFeatureSections()) {
291         if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_SYMBOL) {
292             const PerfFileSectionSymbolsFiles *sectionSymbolsFiles =
293                 static_cast<const PerfFileSectionSymbolsFiles *>(featureSection.get());
294             vr_.UpdateFromPerfData(sectionSymbolsFiles->symbolFileStructs_);
295         } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_UNISTACK_TABLE) {
296             const PerfFileSectionUniStackTable *sectionUniStackTable  =
297                 static_cast<const PerfFileSectionUniStackTable *>(featureSection.get());
298             vr_.ImportUniqueStackNodes(sectionUniStackTable->uniStackTableInfos_);
299             vr_.SetDedupStack();
300         }
301     }
302 }
303 
304 static std::map<int, std::string> g_sampleTypeNames = {
305     {PERF_SAMPLE_IP, "ip"},
306     {PERF_SAMPLE_TID, "tid"},
307     {PERF_SAMPLE_TIME, "time"},
308     {PERF_SAMPLE_ADDR, "addr"},
309     {PERF_SAMPLE_READ, "read"},
310     {PERF_SAMPLE_CALLCHAIN, "callchain"},
311     {PERF_SAMPLE_ID, "id"},
312     {PERF_SAMPLE_CPU, "cpu"},
313     {PERF_SAMPLE_PERIOD, "period"},
314     {PERF_SAMPLE_STREAM_ID, "stream_id"},
315     {PERF_SAMPLE_RAW, "raw"},
316     {PERF_SAMPLE_BRANCH_STACK, "stack"},
317     {PERF_SAMPLE_REGS_USER, "regs_user"},
318     {PERF_SAMPLE_STACK_USER, "stack_user"},
319     {PERF_SAMPLE_WEIGHT, "weight"},
320     {PERF_SAMPLE_DATA_SRC, "data_src"},
321     {PERF_SAMPLE_IDENTIFIER, "identifier"},
322     {PERF_SAMPLE_TRANSACTION, "transaction"},
323     {PERF_SAMPLE_REGS_INTR, "reg_intr"},
324     {PERF_SAMPLE_SERVER_PID, "server_pid"},
325 };
326 
DumpSampleType(const uint64_t sampleType,const int indent)327 void SubCommandDump::DumpSampleType(const uint64_t sampleType, const int indent)
328 {
329     std::string names;
330     for (auto &pair : g_sampleTypeNames) {
331         if (sampleType & pair.first) {
332             if (!names.empty()) {
333                 names.append(",");
334             }
335             names.append(pair.second);
336         }
337     }
338     PRINT_INDENT(indent + 1, "sample_type names: %s\n", names.c_str());
339 }
340 
DumpPrintEventAttr(const perf_event_attr & attr,const int indent)341 void SubCommandDump::DumpPrintEventAttr(const perf_event_attr &attr, const int indent)
342 {
343     PRINT_INDENT(indent, "event_attr: \n");
344 
345     PRINT_INDENT(indent + 1, "type %u, size %u, config %llu\n", attr.type, attr.size, attr.config);
346 
347     if (attr.freq != 0) {
348         PRINT_INDENT(indent + 1, "sample_freq %llu\n", attr.sample_freq);
349     } else {
350         PRINT_INDENT(indent + 1, "sample_period %llu\n", attr.sample_period);
351     }
352 
353     PRINT_INDENT(indent + 1, "sample_type (0x%llx) \n", attr.sample_type);
354     DumpSampleType(attr.sample_type, indent);
355 
356     PRINT_INDENT(indent + 1, "read_format (0x%llx) \n", attr.read_format);
357 
358     PRINT_INDENT(indent + 1, "disabled %u, inherit %u, pinned %u, exclusive %u\n", attr.disabled,
359                  attr.inherit, attr.pinned, attr.exclusive);
360 
361     PRINT_INDENT(indent + 1, "exclude_user %u, exclude_kernel %u, exclude_hv %u, exclude_idle %u\n",
362                  attr.exclude_user, attr.exclude_kernel, attr.exclude_hv, attr.exclude_idle);
363 
364     PRINT_INDENT(indent + 1, "mmap %u, mmap2 %u, comm %u, comm_exec %u, freq %u\n", attr.mmap,
365                  attr.mmap2, attr.comm, attr.comm_exec, attr.freq);
366 
367     PRINT_INDENT(indent + 1, "inherit_stat %u, enable_on_exec %u, task %u, use_clockid %u\n",
368                  attr.inherit_stat, attr.enable_on_exec, attr.task, attr.use_clockid);
369 
370     PRINT_INDENT(indent + 1, "watermark %u, precise_ip %u, mmap_data %u, clockid %d\n", attr.watermark,
371                  attr.precise_ip, attr.mmap_data, attr.clockid);
372 
373     PRINT_INDENT(indent + 1, "sample_id_all %u, exclude_host %u, exclude_guest %u\n", attr.sample_id_all,
374                  attr.exclude_host, attr.exclude_guest);
375     PRINT_INDENT(indent + 1, "branch_sample_type 0x%llx\n", attr.branch_sample_type);
376     PRINT_INDENT(indent + 1, "exclude_callchain_kernel %u, exclude_callchain_user %u\n",
377                  attr.exclude_callchain_kernel, attr.exclude_callchain_user);
378     PRINT_INDENT(indent + 1, "sample_regs_user 0x%llx\n", attr.sample_regs_user);
379     PRINT_INDENT(indent + 1, "sample_stack_user 0x%x\n", attr.sample_stack_user);
380 }
381 
DumpAttrPortion(const int indent)382 void SubCommandDump::DumpAttrPortion(const int indent)
383 {
384     attrIds_ = reader_->GetAttrSection();
385     for (size_t i = 0; i < attrIds_.size(); ++i) {
386         const AttrWithId &attr = attrIds_[i];
387         PRINT_INDENT(indent, "attr %zu:\n", i + 1);
388         DumpPrintEventAttr(attr.attr, indent_ + 1);
389         if (!attr.ids.empty()) {
390             PRINT_INDENT(indent, "  ids:");
391             for (const auto &id : attr.ids) {
392                 PRINT_INDENT(indent, " %" PRId64, id);
393             }
394             PRINT_INDENT(indent, "\n");
395         }
396     }
397 }
398 
ExportUserStack(const PerfRecordSample & recordSample)399 void SubCommandDump::ExportUserStack(const PerfRecordSample &recordSample)
400 {
401     if (recordSample.data_.reg_nr > 0 && recordSample.data_.dyn_size > 0) {
402         // <pid>_<tid>_user_regs_<time>
403         std::string userRegs =
404             StringPrintf("hiperf_%d_%d_user_regs_%zu.dump", recordSample.data_.pid,
405                          recordSample.data_.tid, exportSampleIndex_);
406         std::string resolvedPath = CanonicalizeSpecPath(userRegs.c_str());
407         FILE *userRegsFp = fopen(resolvedPath.c_str(), "wb");
408         CHECK_TRUE(userRegsFp != nullptr, NO_RETVAL, 1, "open userRegs failed");
409         std::unique_ptr<FILE, decltype(&fclose)> fpUserRegs(userRegsFp, fclose);
410         fwrite(recordSample.data_.user_regs, sizeof(u64), recordSample.data_.reg_nr,
411                fpUserRegs.get());
412 
413         std::string userData =
414             StringPrintf("hiperf_%d_%d_user_data_%zu.dump", recordSample.data_.pid,
415                          recordSample.data_.tid, exportSampleIndex_);
416         std::string resolvePath = CanonicalizeSpecPath(userData.c_str());
417         FILE *UserDataFp = fopen(resolvePath.c_str(), "wb");
418         CHECK_TRUE(UserDataFp != nullptr, NO_RETVAL, 1, "open UserData failed");
419         std::unique_ptr<FILE, decltype(&fclose)> fpUserData(UserDataFp, fclose);
420         fwrite(recordSample.data_.stack_data, sizeof(u8), recordSample.data_.dyn_size,
421                fpUserData.get());
422     }
423 }
424 
ExportUserData(PerfEventRecord & record)425 void SubCommandDump::ExportUserData(PerfEventRecord& record)
426 {
427     if (record.GetType() == PERF_RECORD_SAMPLE) {
428         if (currectSampleIndex_++ != exportSampleIndex_) {
429             return;
430         }
431         PerfRecordSample* recordSample = static_cast<PerfRecordSample*>(&record);
432         ExportUserStack(*recordSample);
433 
434         std::string userData =
435             StringPrintf("hiperf_%d_%d_sample_record_%zu_%" PRIu64 ".dump", recordSample->data_.pid,
436                          recordSample->data_.tid, exportSampleIndex_, recordSample->data_.time);
437         std::string resolvedPath = CanonicalizeSpecPath(userData.c_str());
438         std::unique_ptr<FILE, decltype(&fclose)> fpUserData(fopen(resolvedPath.c_str(), "wb"), fclose);
439         static std::vector<u8> buf(RECORD_SIZE_LIMIT);
440         CHECK_TRUE(recordSample->GetBinary(buf), NO_RETVAL, 1, "export user sample data failed");
441         fwrite(buf.data(), sizeof(u8), recordSample->GetSize(), fpUserData.get());
442 
443         HLOGD("export user data index %d time %llu", exportSampleIndex_, recordSample->data_.time);
444     }
445 }
446 
DumpCallChain(int indent,const PerfRecordSample & sample)447 void SubCommandDump::DumpCallChain(int indent, const PerfRecordSample& sample)
448 {
449     PRINT_INDENT(indent, "\n callchain: %zu\n", sample.callFrames_.size());
450     if (sample.callFrames_.size() > 0) {
451         indent += indent + 1;
452         for (auto frameIt = sample.callFrames_.begin(); frameIt != sample.callFrames_.end();
453              frameIt++) {
454             PRINT_INDENT(indent, "%02zd:%s\n", std::distance(frameIt, sample.callFrames_.end()),
455                          frameIt->ToSymbolString().c_str());
456         }
457     }
458 }
459 
DumpDataPortion(const int indent)460 void SubCommandDump::DumpDataPortion(const int indent)
461 {
462     int recordCount = 0;
463     auto recordcCallback = [&](PerfEventRecord& record) {
464         CHECK_TRUE(record.GetName() != nullptr, false, 0, ""); // return false in callback can stop the read process
465 
466         // for UT
467         if (exportSampleIndex_ > 0) {
468             ExportUserData(record);
469         }
470 
471         // tell process tree what happend for rebuild symbols
472         vr_.UpdateFromRecord(record);
473 
474         recordCount++;
475         record.Dump(indent, outputFilename_, g_outputDump);
476 
477         if (record.GetType() == PERF_RECORD_SAMPLE) {
478             DumpCallChain(indent, static_cast<PerfRecordSample&>(record));
479         }
480 
481         return true;
482     };
483 
484     reader_->ReadDataSection(recordcCallback);
485 
486     PRINT_INDENT(indent, "\n ======= there are %d records ======== \n", recordCount);
487 }
488 
PrintSymbolFile(const int & indent,const SymbolFileStruct & symbolFileStruct)489 void SubCommandDump::PrintSymbolFile(const int &indent, const SymbolFileStruct &symbolFileStruct)
490 {
491     PRINT_INDENT(indent + INDENT_TWO, "filePath:%s\n", symbolFileStruct.filePath_.c_str());
492     PRINT_INDENT(indent + INDENT_TWO, "symbolType:%u\n", symbolFileStruct.symbolType_);
493     PRINT_INDENT(indent + INDENT_TWO, "minExecAddr:0x%" PRIx64 "\n", symbolFileStruct.textExecVaddr_);
494     PRINT_INDENT(indent + INDENT_TWO, "minExecAddrFileOffset:0x%08" PRIx64 "\n",
495                 symbolFileStruct.textExecVaddrFileOffset_);
496     if (!symbolFileStruct.buildId_.empty()) {
497         PRINT_INDENT(indent + INDENT_TWO, "buildId:'%s'\n", symbolFileStruct.buildId_.c_str());
498     }
499     PRINT_INDENT(indent + INDENT_TWO, "symbol number: %zu\n", symbolFileStruct.symbolStructs_.size());
500     int symbolid = 0;
501     for (auto &symbolStruct : symbolFileStruct.symbolStructs_) {
502         PRINT_INDENT(indent + 3, "%05d [0x%016" PRIx64 "@0x%08x]  %s\n", symbolid, symbolStruct.vaddr_,
503                      symbolStruct.len_, symbolStruct.symbolName_.c_str());
504         symbolid++;
505     }
506 }
507 
PrintFeatureEventdesc(const int indent,const PerfFileSectionEventDesc & sectionEventdesc)508 void SubCommandDump::PrintFeatureEventdesc(const int indent,
509                                            const PerfFileSectionEventDesc &sectionEventdesc)
510 {
511     PRINT_INDENT(indent + INDENT_TWO, "Event descriptions: %zu\n", sectionEventdesc.eventDesces_.size());
512     for (size_t i = 0; i < sectionEventdesc.eventDesces_.size(); i++) {
513         const AttrWithId &desc = sectionEventdesc.eventDesces_[i];
514         PRINT_INDENT(indent + INDENT_TWO, "event name[%zu]: %s ids: %s\n", i, desc.name.c_str(),
515                      VectorToString(desc.ids).c_str());
516 
517         // attr is duplicated the attrs section
518     }
519     PRINT_INDENT(indent + INDENT_TWO, "\n");
520 }
521 
DumpFeaturePortion(const int indent)522 void SubCommandDump::DumpFeaturePortion(const int indent)
523 {
524     PRINT_INDENT(indent, "\n ==== features ====\n");
525     auto features = reader_->GetFeatures();
526     for (auto feature : features) {
527         PRINT_INDENT(indent + 1, "feature %d:%s\n", feature,
528                      PerfFileSection::GetFeatureName(feature).c_str());
529     }
530 
531     const auto &featureSections = reader_->GetFeatureSections();
532     HLOGV("featureSections: %zu ", featureSections.size());
533 
534     PRINT_INDENT(indent, "\n ==== feature sections ====\n");
535 
536     for (auto &featureSection : featureSections) {
537         PRINT_INDENT(indent + 1, "feature %d:%s content: \n", featureSection.get()->featureId_,
538                      PerfFileSection::GetFeatureName(featureSection.get()->featureId_).c_str());
539         if (reader_->IsFeatrureStringSection(featureSection.get()->featureId_)) {
540             const PerfFileSectionString *sectionString =
541                 static_cast<const PerfFileSectionString *>(featureSection.get());
542             PRINT_INDENT(indent + INDENT_TWO, "%s\n", sectionString->ToString().c_str());
543             continue;
544         } else if (featureSection.get()->featureId_ == FEATURE::EVENT_DESC) {
545             PrintFeatureEventdesc(
546                 indent, *static_cast<const PerfFileSectionEventDesc *>(featureSection.get()));
547             continue;
548         } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_SYMBOL) {
549             const PerfFileSectionSymbolsFiles *sectionSymbolsFiles =
550                 static_cast<const PerfFileSectionSymbolsFiles *>(featureSection.get());
551             if (sectionSymbolsFiles != nullptr) {
552                 PRINT_INDENT(indent + INDENT_TWO, "SymbolFiles:%zu\n",
553                              sectionSymbolsFiles->symbolFileStructs_.size());
554 
555                 int fileid = 0;
556                 for (auto &symbolFileStruct : sectionSymbolsFiles->symbolFileStructs_) {
557                     PRINT_INDENT(indent + INDENT_TWO, "\n");
558                     PRINT_INDENT(indent + INDENT_TWO, "fileid:%d\n", fileid);
559                     fileid++;
560                     // symbol file info
561                     PrintSymbolFile(indent, symbolFileStruct);
562                 }
563             } else {
564                 PRINT_INDENT(indent + INDENT_TWO, "get SymbolFiles failed\n");
565             }
566             continue;
567         } else if (featureSection.get()->featureId_ == FEATURE::HIPERF_FILES_UNISTACK_TABLE) {
568             const PerfFileSectionUniStackTable *sectioniStackTable =
569                 static_cast<PerfFileSectionUniStackTable *>(const_cast<PerfFileSection *>(featureSection.get()));
570             if (sectioniStackTable != nullptr) {
571                 DumpUniqueStackTableNode(indent + 1, *sectioniStackTable);
572             } else {
573                 PRINT_INDENT(indent + INDENT_TWO, "get StackTable failed\n");
574             }
575             continue;
576         } else {
577             PRINT_INDENT(indent + INDENT_TWO, "not support dump this feature(%d).\n", featureSection.get()->featureId_);
578         }
579     }
580 }
581 
DumpUniqueStackTableNode(const int indent,const PerfFileSectionUniStackTable & uniStackTable)582 void SubCommandDump::DumpUniqueStackTableNode(const int indent, const PerfFileSectionUniStackTable &uniStackTable)
583 {
584     int tableid = 0;
585     PRINT_INDENT(indent + 1, "TableNums: %zu\n\n", uniStackTable.uniStackTableInfos_.size());
586     for (const auto& uniStackTableInfo : uniStackTable.uniStackTableInfos_) {
587         PRINT_INDENT(indent + INDENT_TWO, "tableid: %d\n", tableid);
588         PRINT_INDENT(indent + INDENT_TWO, "pid: %" PRIu32 "\n", uniStackTableInfo.pid);
589         PRINT_INDENT(indent + INDENT_TWO, "tableSize: %" PRIu32 "\n", uniStackTableInfo.tableSize);
590         PRINT_INDENT(indent + INDENT_TWO, "numNodes: %" PRIu32 "\n", uniStackTableInfo.numNodes);
591         PRINT_INDENT(indent + INDENT_TWO, "%-7s %-7s %-8s\n", "no", "index", "node");
592         for (size_t i = 0; i < uniStackTableInfo.nodes.size(); i++) {
593             UniStackNode node = uniStackTableInfo.nodes[i];
594             PRINT_INDENT(indent + INDENT_TWO, "%-7zu %-7" PRIu32 " 0x%-8" PRIx64 "\n", i, node.index, node.node.value);
595         }
596         tableid++;
597     }
598 }
599 
RegisterSubCommandDump()600 bool SubCommandDump::RegisterSubCommandDump()
601 {
602     return SubCommand::RegisterSubCommand("dump", SubCommandDump::GetInstance);
603 }
604 
SetHM()605 void SubCommandDump::SetHM()
606 {
607     std::string os = reader_->GetFeatureString(FEATURE::OSRELEASE);
608     isHM_ = os.find(HMKERNEL) != std::string::npos;
609     vr_.SetHM(isHM_);
610     HLOGD("Set isHM_: %d", isHM_);
611     if (isHM_) {
612         pid_t devhost = -1;
613         std::string str = reader_->GetFeatureString(FEATURE::HIPERF_HM_DEVHOST);
614         if (IsNumeric(str)) {
615             devhost = std::stoll(str);
616         }
617         vr_.SetDevhostPid(devhost);
618     }
619 }
620 
DumpSpeReport()621 void SubCommandDump::DumpSpeReport()
622 {
623 #if defined(is_ohos) && is_ohos
624     std::string cmdline = reader_->GetFeatureString(FEATURE::CMDLINE);
625     if (cmdline.find("-e arm_spe_0") != std::string::npos) {
626         HLOGD("dump spe report data");
627         UpdateHeating();
628         DumpSpeReportData(indent_, g_outputDump);
629     }
630 #endif
631 }
632 
GetInstance()633 SubCommand& SubCommandDump::GetInstance()
634 {
635     static SubCommandDump subCommand;
636     return subCommand;
637 }
638 
639 } // namespace HiPerf
640 } // namespace Developtools
641 } // namespace OHOS
642