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