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 §ionEventdesc)
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