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