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