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 "Report"
16
17 #include "subcommand_report.h"
18
19 #include <memory>
20 #include <set>
21 #include <sstream>
22
23 #if defined(is_mingw) && is_mingw
24 #include <windows.h>
25 #else
26 #include <sys/ioctl.h>
27 #endif
28
29 #include "hiperf_hilog.h"
30 #include "perf_events.h"
31 #include "register.h"
32 #include "utilities.h"
33
34 namespace OHOS {
35 namespace Developtools {
36 namespace HiPerf {
ParseOption(std::vector<std::string> & args)37 bool SubCommandReport::ParseOption(std::vector<std::string> &args)
38 {
39 if (!Option::GetOptionValue(args, "-i", recordFile_[FIRST])) {
40 return false;
41 }
42 if (!Option::GetOptionValue(args, "-o", reportFile_)) {
43 return false;
44 }
45 if (!Option::GetOptionValue(args, "--diff", recordFile_[SECOND])) {
46 return false;
47 }
48 if (!recordFile_[SECOND].empty()) {
49 // remove tid pid
50 reportOption_.sortKeys_ = {"comm", "dso", "func"};
51 }
52 if (!Option::GetOptionValue(args, "--sort", reportOption_.sortKeys_)) {
53 return false;
54 }
55
56 if (!Option::GetOptionValue(args, "--symbol-dir", symbolsPaths_)) {
57 return false;
58 }
59 if (!Option::GetOptionValue(args, "--limit-percent", reportOption_.heatLimit_)) {
60 return false;
61 }
62
63 if (!Option::GetOptionValue(args, "-s", showCallStack_)) {
64 return false;
65 }
66 if (!Option::GetOptionValue(args, "--call-stack", showCallStack_)) {
67 return false;
68 }
69
70 if (!Option::GetOptionValue(args, "--call-stack-limit-percent",
71 reportOption_.callStackHeatLimit_)) {
72 return false;
73 }
74
75 if (!Option::GetOptionValue(args, "--comms", reportOption_.displayComms_)) {
76 return false;
77 }
78 if (!Option::GetOptionValue(args, "--pids", reportOption_.displayPids_)) {
79 return false;
80 }
81 if (!Option::GetOptionValue(args, "--tids", reportOption_.displayTids_)) {
82 return false;
83 }
84 if (!Option::GetOptionValue(args, "--dsos", reportOption_.displayDsos_)) {
85 return false;
86 }
87 if (!Option::GetOptionValue(args, "--funcs", reportOption_.displayFuncs_)) {
88 return false;
89 }
90 if (!Option::GetOptionValue(args, "--from_dsos", reportOption_.displayFuncs_)) {
91 return false;
92 }
93 if (!Option::GetOptionValue(args, "--from_funcs", reportOption_.displayFuncs_)) {
94 return false;
95 }
96 if (!Option::GetOptionValue(args, "--proto", protobufFormat_)) {
97 return false;
98 }
99 if (!Option::GetOptionValue(args, "--json", jsonFormat_)) {
100 return false;
101 }
102 if (!Option::GetOptionValue(args, "--debug", debug_)) {
103 return false;
104 }
105 if (!Option::GetOptionValue(args, "--branch", branch_)) {
106 return false;
107 }
108 // this is a hidden option for compare result
109 if (!Option::GetOptionValue(args, "--hide_count", reportOption_.hideCount_)) {
110 return false;
111 }
112 return VerifyOption();
113 }
114
DumpOptions() const115 void SubCommandReport::DumpOptions() const
116 {
117 printf("DumpOptions:\n");
118 printf(" recordFile_:\t%s\n", recordFile_[FIRST].c_str());
119 printf(" recordFile_:\t%s\n", recordFile_[SECOND].c_str());
120 printf(" reportFile_:\t%s\n", reportFile_.c_str());
121 printf(" sortKeys:\t%s\n", VectorToString(reportOption_.sortKeys_).c_str());
122 }
VerifyDisplayOption()123 bool SubCommandReport::VerifyDisplayOption()
124 {
125 for (std::string &number : reportOption_.displayPids_) {
126 if (!IsDigits(number) || number.front() == '-') {
127 printf("error number for pid '%s'\n", number.c_str());
128 return false;
129 }
130 }
131
132 for (std::string &number : reportOption_.displayTids_) {
133 if (!IsDigits(number) || number.front() == '-') {
134 printf("error number for tid '%s'\n", number.c_str());
135 return false;
136 }
137 }
138 return true;
139 }
140
VerifyOption()141 bool SubCommandReport::VerifyOption()
142 {
143 for (auto key : reportOption_.sortKeys_) {
144 if (key == "count") {
145 printf("unknown sort key name '%s'\n", key.c_str());
146 return false;
147 } else if (GetReport().reportKeyMap_.count(key) == 0) {
148 printf("unknown sort key name '%s'\n", key.c_str());
149 return false;
150 }
151 }
152 const float min = 0.0;
153 const float max = 100.0;
154 if (reportOption_.heatLimit_ < min || reportOption_.heatLimit_ > max) {
155 printf("head limit error. must in (0 <= limit < 100).\n");
156 return false;
157 }
158 if (reportOption_.callStackHeatLimit_ < min || reportOption_.callStackHeatLimit_ > max) {
159 printf("head limit error. must in (0 <= limit < 100).\n");
160 return false;
161 }
162 if (recordFile_[FIRST].empty()) {
163 printf("input file name can't be empty\n");
164 return false;
165 }
166 if (!recordFile_[SECOND].empty()) {
167 if (protobufFormat_ || jsonFormat_ || showCallStack_) {
168 printf("diff don't support any export mode(like json , flame or proto)\n");
169 } else {
170 diffMode_ = true;
171 }
172 }
173 if (!IsValidOutPath(reportFile_)) {
174 printf("Invalid output file path, permission denied\n");
175 return false;
176 }
177 // default report file name
178 if (reportFile_.empty()) {
179 if (protobufFormat_) {
180 reportFile_ = "perf.proto";
181 } else if (jsonFormat_) {
182 reportFile_ = "perf.json";
183 }
184 }
185
186 // misc config
187 reportOption_.debug_ = debug_;
188 ReportJsonFile::debug_ = debug_;
189
190 return VerifyDisplayOption();
191 }
192
BroadcastSample(std::unique_ptr<PerfRecordSample> & sample)193 void SubCommandReport::BroadcastSample(std::unique_ptr<PerfRecordSample> &sample)
194 {
195 // this func use for cpuoff mode , it will Broadcast the sampe to every event config
196 for (auto &config : GetReport().configs_) {
197 HLOGM("resend as id %" PRIu64 "", config.ids_[0]);
198 sample->data_.id = config.ids_[0];
199 ProcessSample(sample);
200 }
201 }
202
ProcessSample(std::unique_ptr<PerfRecordSample> & sample)203 void SubCommandReport::ProcessSample(std::unique_ptr<PerfRecordSample> &sample)
204 {
205 sample->DumpLog(__FUNCTION__);
206 if (jsonFormat_) {
207 reportJsonFile_->UpdateReportSample(sample->data_.id, sample->data_.pid, sample->data_.tid,
208 sample->data_.period);
209 reportJsonFile_->UpdateReportCallStack(sample->data_.id, sample->data_.pid,
210 sample->data_.tid, sample->data_.period,
211 sample->callFrames_);
212 } else if (protobufFormat_) {
213 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
214 // make some cook
215 // redesgin here
216 protobufOutputFileWriter_->ProcessSampleRecord(
217 *sample, static_cast<uint32_t>(GetReport().GetConfigIndex(sample->data_.id)),
218 GetReport().virtualRuntime_.GetSymbolsFiles());
219 #endif
220 } else {
221 if (branch_) {
222 GetReport().AddReportItemBranch(*sample);
223 } else {
224 GetReport().AddReportItem(*sample, showCallStack_);
225 }
226 }
227 }
228
RecordCallBack(PerfEventRecord & record)229 bool SubCommandReport::RecordCallBack(PerfEventRecord& record)
230 {
231 // tell process tree what happend for rebuild symbols
232 GetReport().virtualRuntime_.UpdateFromRecord(record);
233
234 if (record.GetType() == PERF_RECORD_SAMPLE) {
235 std::unique_ptr<PerfRecordSample> sample
236 = std::make_unique<PerfRecordSample>(static_cast<PerfRecordSample&>(record));
237 std::unique_ptr<PerfRecordSample> prevSample = nullptr;
238 if (cpuOffMode_) {
239 auto prevIt = prevSampleCache_.find(sample->data_.tid);
240 if (prevIt == prevSampleCache_.end()) {
241 // this thread first sample
242 prevSampleCache_[sample->data_.tid] = std::move(sample);
243 // do nothing because we unable to calc the period
244 return true;
245 } else {
246 // we have prev sample
247 prevSample = std::move(prevIt->second);
248 HLOGV("calc time %llu - %llu", sample->data_.time, prevSample->data_.time);
249 if (sample->data_.time > prevSample->data_.time) {
250 prevSample->data_.period = sample->data_.time - prevSample->data_.time;
251 } else {
252 prevSample->data_.period = 1u;
253 }
254
255 // current move the prev
256 prevIt->second = std::move(sample);
257 // go on with prevSample
258 sample = std::move(prevSample);
259
260 HLOGV("current sample period %llu ", sample->data_.period);
261 }
262 }
263 if (cpuOffMode_ && cpuOffids_.size() > 0 && cpuOffids_.count(sample->data_.id) > 0) {
264 BroadcastSample(sample);
265 } else {
266 ProcessSample(sample);
267 }
268 } else {
269 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
270 if (protobufFormat_) {
271 protobufOutputFileWriter_->ProcessRecord(record);
272 }
273 #endif
274 }
275 return true;
276 }
277
LoadPerfDataCompleted()278 void SubCommandReport::LoadPerfDataCompleted()
279 {
280 if (jsonFormat_) {
281 reportJsonFile_->UpdateCallNodeEventCount();
282 }
283 HLOGV("load perf data done");
284 }
285
ProcessSymbolsData()286 void SubCommandReport::ProcessSymbolsData()
287 {
288 GetReport().virtualRuntime_.SetSymbolsPaths(symbolsPaths_);
289 // we need unwind it (for function name match) even not give us path
290 GetReport().virtualRuntime_.SetDisableUnwind(false);
291
292 // found symbols in file
293 const auto featureSection = recordFileReader_->GetFeatureSection(FEATURE::HIPERF_FILES_SYMBOL);
294 if (featureSection != nullptr) {
295 const PerfFileSectionSymbolsFiles *sectionSymbolsFiles =
296 static_cast<const PerfFileSectionSymbolsFiles *>(featureSection);
297 GetReport().virtualRuntime_.UpdateFromPerfData(sectionSymbolsFiles->symbolFileStructs_);
298 }
299 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
300 // we have load the elf
301 // write it to proto first
302 if (protobufFormat_) {
303 protobufOutputFileWriter_->ProcessSymbolsFiles(
304 GetReport().virtualRuntime_.GetSymbolsFiles());
305 }
306 #endif
307 if (jsonFormat_) {
308 reportJsonFile_->ProcessSymbolsFiles(GetReport().virtualRuntime_.GetSymbolsFiles());
309 }
310 }
311
ProcessUniStackTableData()312 void SubCommandReport::ProcessUniStackTableData()
313 {
314 auto featureSection = recordFileReader_->GetFeatureSection(FEATURE::HIPERF_FILES_UNISTACK_TABLE);
315 if (featureSection != nullptr) {
316 PerfFileSectionUniStackTable *sectioniStackTable =
317 static_cast<PerfFileSectionUniStackTable *>(const_cast<PerfFileSection *>(featureSection));
318 GetReport().virtualRuntime_.ImportUniqueStackNodes(sectioniStackTable->uniStackTableInfos_);
319 GetReport().virtualRuntime_.SetDedupStack();
320 }
321 }
322
UpdateReportInfo()323 void SubCommandReport::UpdateReportInfo()
324 {
325 // get some meta info for protobuf
326 if (protobufFormat_) {
327 // workload
328 const PerfFileSection *featureSection =
329 recordFileReader_->GetFeatureSection(FEATURE::HIPERF_WORKLOAD_CMD);
330 std::string workloader = "";
331 if (featureSection != nullptr) {
332 HLOGV("found HIPERF_META_WORKLOAD_CMD");
333 const PerfFileSectionString *sectionString =
334 static_cast<const PerfFileSectionString *>(featureSection);
335 workloader = sectionString->ToString();
336 } else {
337 HLOGW("NOT found HIPERF_META_WORKLOAD_CMD");
338 }
339 protobufOutputFileWriter_->ProcessReportInfo(configNames_, workloader);
340 }
341 }
342
LoadEventConfigData()343 void SubCommandReport::LoadEventConfigData()
344 {
345 auto features = recordFileReader_->GetFeatures();
346 cpuOffMode_ = find(features.begin(), features.end(), FEATURE::HIPERF_CPU_OFF) != features.end();
347 if (cpuOffMode_) {
348 HLOGD("this is cpuOffMode ");
349 }
350 const PerfFileSection *featureSection =
351 recordFileReader_->GetFeatureSection(FEATURE::EVENT_DESC);
352 if (featureSection != nullptr) {
353 HLOGV("have EVENT_DESC");
354 LoadEventDesc();
355 } else {
356 HLOGV("have Attr Section");
357 LoadAttrSection();
358 }
359 HLOG_ASSERT(GetReport().configs_.size() > 0);
360 HLOGV("record %d have %zu configs", index_, GetReport().configs_.size());
361 }
362
LoadEventDesc()363 void SubCommandReport::LoadEventDesc()
364 {
365 const PerfFileSection *featureSection =
366 recordFileReader_->GetFeatureSection(FEATURE::EVENT_DESC);
367 CHECK_TRUE(featureSection != nullptr, NO_RETVAL, 1, "featureSection invalid");
368 const PerfFileSectionEventDesc §ionEventdesc =
369 *static_cast<const PerfFileSectionEventDesc *>(featureSection);
370 HLOGV("Event descriptions: %zu", sectionEventdesc.eventDesces_.size());
371 for (size_t i = 0; i < sectionEventdesc.eventDesces_.size(); i++) {
372 const AttrWithId &fileAttr = sectionEventdesc.eventDesces_[i];
373
374 HLOGV("event name[%zu]: %s ids: %s", i, fileAttr.name.c_str(),
375 VectorToString(fileAttr.ids).c_str());
376 if (cpuOffMode_ && fileAttr.name == cpuOffEventName) {
377 // found cpuoff event id
378 std::set<uint64_t> cpuOffids(fileAttr.ids.begin(), fileAttr.ids.end());
379 cpuOffids_ = cpuOffids;
380 HLOGV("this is cpu off event");
381 } else {
382 // don't add cpuoff event
383 if (protobufFormat_) {
384 configNames_.emplace_back(fileAttr.name);
385 }
386 for (uint64_t id : fileAttr.ids) {
387 GetReport().configIdIndexMaps_[id] = GetReport().configs_.size(); // setup index
388 HLOGV("add config id map %" PRIu64 " to %zu", id, GetReport().configs_.size());
389 }
390 // when cpuOffMode_ , don't use count mode , use time mode.
391 auto &config =
392 GetReport().configs_.emplace_back(fileAttr.name, fileAttr.attr.type,
393 fileAttr.attr.config, cpuOffMode_ ? false : true);
394 config.ids_ = fileAttr.ids;
395 HLOG_ASSERT(config.ids_.size() > 0);
396 if (jsonFormat_) {
397 reportJsonFile_->reportConfigItems_.emplace(
398 fileAttr.ids,
399 ReportConfigItem(reportJsonFile_->reportConfigItems_.size(), fileAttr.name));
400 }
401 }
402 }
403 }
404
LoadAttrSection()405 void SubCommandReport::LoadAttrSection()
406 {
407 std::vector<AttrWithId> attrIds = recordFileReader_->GetAttrSection();
408 for (size_t i = 0; i < attrIds.size(); ++i) {
409 const AttrWithId &fileAttr = attrIds[i];
410 std::string name = PerfEvents::GetStaticConfigName(
411 static_cast<perf_type_id>(fileAttr.attr.type), fileAttr.attr.config);
412 configNames_.emplace_back(name);
413 for (uint64_t id : fileAttr.ids) {
414 GetReport().configIdIndexMaps_[id] = GetReport().configs_.size(); // setup index
415 HLOGV("add config id map %" PRIu64 " to %zu", id, GetReport().configs_.size());
416 }
417 auto &config = GetReport().configs_.emplace_back(fileAttr.name, fileAttr.attr.type,
418 fileAttr.attr.config);
419 config.ids_ = fileAttr.ids;
420 HLOG_ASSERT(config.ids_.size() > 0);
421 if (jsonFormat_) {
422 reportJsonFile_->reportConfigItems_.emplace(
423 fileAttr.ids, ReportConfigItem(reportJsonFile_->reportConfigItems_.size(), name));
424 }
425 HLOGV("event name[%zu]: %s ids: %s", i, name_.c_str(),
426 VectorToString(fileAttr.ids).c_str());
427 }
428 }
429
ProcessFeaturesData()430 void SubCommandReport::ProcessFeaturesData()
431 {
432 LoadEventConfigData();
433
434 // update device arch from feature
435 SetDeviceArch(GetArchTypeFromUname(recordFileReader_->GetFeatureString(FEATURE::ARCH)));
436
437 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
438 UpdateReportInfo();
439 #endif
440 }
441
FlushCacheRecord()442 void SubCommandReport::FlushCacheRecord()
443 {
444 for (auto &pair : prevSampleCache_) {
445 std::unique_ptr<PerfRecordSample> sample = std::move(pair.second);
446 sample->data_.period = 1u;
447 if (cpuOffMode_ && cpuOffids_.size() > 0 && cpuOffids_.count(sample->data_.id) > 0) {
448 BroadcastSample(sample);
449 } else {
450 ProcessSample(sample);
451 }
452 }
453 prevSampleCache_.clear();
454 }
455
LoadPerfData()456 bool SubCommandReport::LoadPerfData()
457 {
458 // check if file exist
459 if (access(recordFile_[index_].c_str(), F_OK) != 0) {
460 // file not exists
461 printf("Can not access data file %s\n", recordFile_[index_].c_str());
462 return false;
463 }
464
465 // try load the file
466 recordFileReader_ = PerfFileReader::Instance(recordFile_[index_]);
467 if (recordFileReader_ == nullptr) {
468 HLOGE("FileReader::Instance(%s) return null", recordFile_[index_].c_str());
469 return false;
470 }
471
472 CHECK_TRUE(recordFileReader_->ReadFeatureSection(), false, LOG_TYPE_PRINTF, "record format error.\n");
473 if (jsonFormat_) {
474 reportJsonFile_ =
475 std::make_unique<ReportJsonFile>(recordFileReader_, GetReport().virtualRuntime_);
476 }
477
478 ProcessFeaturesData();
479 ProcessSymbolsData();
480 ProcessUniStackTableData();
481 HLOGD("process record");
482 // before load data section
483 SetHM();
484 recordFileReader_->ReadDataSection(
485 [this] (PerfEventRecord& record) -> bool {
486 return this->RecordCallBack(record);
487 });
488 if (cpuOffMode_) {
489 FlushCacheRecord();
490 }
491 HLOGD("process record completed");
492
493 LoadPerfDataCompleted();
494 return true;
495 }
496
OutputStd()497 bool SubCommandReport::OutputStd()
498 {
499 if (fprintf(output_, "<<Hiperf Report%s>>\n", diffMode_ ? " Diff" : "") < 0) {
500 return false;
501 }
502
503 // feature string:
504 const auto &featureSections = recordFileReader_->GetFeatureSections();
505 HLOGV("featureSections: %zu ", featureSections.size());
506
507 for (auto &featureSection : featureSections) {
508 if (recordFileReader_->IsFeatrureStringSection(featureSection->featureId_)) {
509 const PerfFileSectionString *sectionString =
510 static_cast<const PerfFileSectionString *>(featureSection.get());
511
512 fprintf(output_, "%s: %s\n",
513 PerfFileSection::GetFeatureName(featureSection.get()->featureId_).c_str(),
514 sectionString->ToString().c_str());
515 }
516 }
517
518 if (cpuOffMode_) {
519 fprintf(output_, "cpu off mode: enabled\n");
520 }
521
522 if (!diffMode_) {
523 GetReport(FIRST).AdjustReportItems();
524 GetReport(FIRST).OutputStd(output_);
525 } else {
526 GetReport(FIRST).AdjustReportItems();
527 GetReport(SECOND).AdjustReportItems();
528 GetReport(FIRST).OutputStdDiff(output_, GetReport(SECOND));
529 }
530
531 return true;
532 }
533
OutputReport()534 bool SubCommandReport::OutputReport()
535 {
536 if (output_ == nullptr) {
537 HLOGD("nothing need output");
538 return true; //
539 } else if (jsonFormat_) {
540 HLOGD("report as json");
541 return reportJsonFile_->OutputJson(output_);
542 } else {
543 return OutputStd();
544 }
545 }
546
PrepareOutput()547 bool SubCommandReport::PrepareOutput()
548 {
549 if (protobufFormat_) {
550 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
551 // check if file exist
552 if (access(recordFile_[index_].c_str(), F_OK) != 0) {
553 printf("Can not access data file %s\n", recordFile_[index_].c_str());
554 return false;
555 }
556 printf("save to protobuf file: '%s'\n", reportFile_.c_str());
557 protobufOutputFileWriter_ = std::make_unique<ReportProtobufFileWriter>();
558 protobufOutputFileWriter_->Create(reportFile_);
559 #endif
560 return true;
561 }
562
563 if (!reportFile_.empty()) {
564 std::string resolvedPath = CanonicalizeSpecPath(reportFile_.c_str());
565 output_ = fopen(resolvedPath.c_str(), "w");
566 if (output_ == nullptr) {
567 printf("unable open file to '%s' because '%d'\n", reportFile_.c_str(), errno);
568 return false;
569 } else {
570 printf("report will save at '%s'\n", reportFile_.c_str());
571 }
572 } else {
573 output_ = stdout;
574 }
575
576 return true;
577 }
578
~SubCommandReport()579 SubCommandReport::~SubCommandReport()
580 {
581 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
582 if (protobufOutputFileWriter_ != nullptr) {
583 protobufOutputFileWriter_->Close();
584 }
585 #endif
586 if (output_ != nullptr && output_ != stdout) {
587 fclose(output_);
588 }
589
590 SymbolsFile::onRecording_ = true; // back to default for UT
591 }
592
OnSubCommand(std::vector<std::string> & args)593 HiperfError SubCommandReport::OnSubCommand(std::vector<std::string>& args)
594 {
595 RETURN_IF(!PrepareOutput(), HiperfError::PREPARE_OUTPUT_FAIL);
596
597 // any way tell symbols this is not on recording
598 SymbolsFile::onRecording_ = false;
599
600 printf("loading data\n");
601 if (!LoadPerfData()) {
602 return HiperfError::LOAD_PERF_DATA_FAIL;
603 }
604
605 if (diffMode_) {
606 // we are in diff mode
607 index_ = SECOND;
608 // load again with second file
609 CHECK_TRUE(LoadPerfData(), HiperfError::LOAD_SECOND_PERF_DATA_FAIL, 0, "");
610 // back to first
611 index_ = FIRST;
612 }
613 printf("prepare report\n");
614 CHECK_TRUE(OutputReport(), HiperfError::OUTPUT_REPORT_FAIL, 1, "OutputReport failed");
615 #ifdef HIPERF_DEBUG_TIME
616 printf("SymbolicRecordTimes: %0.3f ms\n",
617 GetReport(FIRST).virtualRuntime_.symbolicRecordTimes_.count() / MS_DURATION);
618 #endif
619
620 printf("report done\n");
621 AgeHiperflogFiles();
622 return HiperfError::NO_ERR;
623 }
624
RegisterSubCommandReport()625 bool SubCommandReport::RegisterSubCommandReport()
626 {
627 return SubCommand::RegisterSubCommand("report", SubCommandReport::GetInstance);
628 }
629
SetHM()630 void SubCommandReport::SetHM()
631 {
632 std::string os = recordFileReader_->GetFeatureString(FEATURE::OSRELEASE);
633 isHM_ = os.find(HMKERNEL) != std::string::npos;
634 GetReport().virtualRuntime_.SetHM(isHM_);
635 HLOGD("Set isHM_: %d", isHM_);
636 if (isHM_) {
637 pid_t devhost = -1;
638 std::string str = recordFileReader_->GetFeatureString(FEATURE::HIPERF_HM_DEVHOST);
639 if (str != EMPTY_STRING && IsNumeric(str)) {
640 devhost = std::stoll(str);
641 }
642 GetReport().virtualRuntime_.SetDevhostPid(devhost);
643 }
644 }
645
GetInstance()646 SubCommand& SubCommandReport::GetInstance()
647 {
648 static SubCommandReport subCommand;
649 return subCommand;
650 }
651 } // namespace HiPerf
652 } // namespace Developtools
653 } // namespace OHOS
654