• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
174     // default report file name
175     if (reportFile_.empty()) {
176         if (protobufFormat_) {
177             reportFile_ = "perf.proto";
178         } else if (jsonFormat_) {
179             reportFile_ = "perf.json";
180         }
181     }
182 
183     // misc config
184     reportOption_.debug_ = debug_;
185     ReportJsonFile::debug_ = debug_;
186 
187     return VerifyDisplayOption();
188 }
189 
BroadcastSample(std::unique_ptr<PerfRecordSample> & sample)190 void SubCommandReport::BroadcastSample(std::unique_ptr<PerfRecordSample> &sample)
191 {
192     // this func use for cpuoff mode , it will Broadcast the sampe to every event config
193     for (auto &config : GetReport().configs_) {
194         HLOGM("resend as id %" PRIu64 "", config.ids_[0]);
195         sample->data_.id = config.ids_[0];
196         ProcessSample(sample);
197     }
198 }
199 
ProcessSample(std::unique_ptr<PerfRecordSample> & sample)200 void SubCommandReport::ProcessSample(std::unique_ptr<PerfRecordSample> &sample)
201 {
202     sample->DumpLog(__FUNCTION__);
203     if (jsonFormat_) {
204         reportJsonFile_->UpdateReportSample(sample->data_.id, sample->data_.pid, sample->data_.tid,
205                                             sample->data_.period);
206         reportJsonFile_->UpdateReportCallStack(sample->data_.id, sample->data_.pid,
207                                                sample->data_.tid, sample->data_.period,
208                                                sample->callFrames_);
209     } else if (protobufFormat_) {
210 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
211         // make some cook
212         // redesgin here
213         protobufOutputFileWriter_->ProcessSampleRecord(
214             *sample, static_cast<uint32_t>(GetReport().GetConfigIndex(sample->data_.id)),
215             GetReport().virtualRuntime_.GetSymbolsFiles());
216 #endif
217     } else {
218         if (branch_) {
219             GetReport().AddReportItemBranch(*sample);
220         } else {
221             GetReport().AddReportItem(*sample, showCallStack_);
222         }
223     }
224 }
225 
RecordCallBack(PerfEventRecord & record)226 bool SubCommandReport::RecordCallBack(PerfEventRecord& record)
227 {
228     // tell process tree what happend for rebuild symbols
229     GetReport().virtualRuntime_.UpdateFromRecord(record);
230 
231     if (record.GetType() == PERF_RECORD_SAMPLE) {
232         std::unique_ptr<PerfRecordSample> sample
233             = std::make_unique<PerfRecordSample>(static_cast<PerfRecordSample&>(record));
234         std::unique_ptr<PerfRecordSample> prevSample = nullptr;
235         if (cpuOffMode_) {
236             auto prevIt = prevSampleCache_.find(sample->data_.tid);
237             if (prevIt == prevSampleCache_.end()) {
238                 // this thread first sample
239                 prevSampleCache_[sample->data_.tid] = std::move(sample);
240                 // do nothing because we unable to calc the period
241                 return true;
242             } else {
243                 // we have prev sample
244                 prevSample = std::move(prevIt->second);
245                 HLOGV("calc time %llu - %llu", sample->data_.time, prevSample->data_.time);
246                 if (sample->data_.time > prevSample->data_.time) {
247                     prevSample->data_.period = sample->data_.time - prevSample->data_.time;
248                 } else {
249                     prevSample->data_.period = 1u;
250                 }
251 
252                 // current move the prev
253                 prevIt->second = std::move(sample);
254                 // go on with prevSample
255                 sample = std::move(prevSample);
256 
257                 HLOGV("current sample period %llu ", sample->data_.period);
258             }
259         }
260         if (cpuOffMode_ && cpuOffids_.size() > 0 && cpuOffids_.count(sample->data_.id) > 0) {
261             BroadcastSample(sample);
262         } else {
263             ProcessSample(sample);
264         }
265     } else {
266 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
267         if (protobufFormat_) {
268             protobufOutputFileWriter_->ProcessRecord(record);
269         }
270 #endif
271     }
272     return true;
273 }
274 
LoadPerfDataCompleted()275 void SubCommandReport::LoadPerfDataCompleted()
276 {
277     if (jsonFormat_) {
278         reportJsonFile_->UpdateCallNodeEventCount();
279     }
280     HLOGV("load perf data done");
281 }
282 
ProcessSymbolsData()283 void SubCommandReport::ProcessSymbolsData()
284 {
285     GetReport().virtualRuntime_.SetSymbolsPaths(symbolsPaths_);
286     // we need unwind it (for function name match) even not give us path
287     GetReport().virtualRuntime_.SetDisableUnwind(false);
288 
289     // found symbols in file
290     const auto featureSection = recordFileReader_->GetFeatureSection(FEATURE::HIPERF_FILES_SYMBOL);
291     if (featureSection != nullptr) {
292         const PerfFileSectionSymbolsFiles *sectionSymbolsFiles =
293             static_cast<const PerfFileSectionSymbolsFiles *>(featureSection);
294         GetReport().virtualRuntime_.UpdateFromPerfData(sectionSymbolsFiles->symbolFileStructs_);
295     }
296 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
297     // we have load the elf
298     // write it to proto first
299     if (protobufFormat_) {
300         protobufOutputFileWriter_->ProcessSymbolsFiles(
301             GetReport().virtualRuntime_.GetSymbolsFiles());
302     }
303 #endif
304     if (jsonFormat_) {
305         reportJsonFile_->ProcessSymbolsFiles(GetReport().virtualRuntime_.GetSymbolsFiles());
306     }
307 }
308 
ProcessUniStackTableData()309 void SubCommandReport::ProcessUniStackTableData()
310 {
311     auto featureSection = recordFileReader_->GetFeatureSection(FEATURE::HIPERF_FILES_UNISTACK_TABLE);
312     if (featureSection != nullptr) {
313         PerfFileSectionUniStackTable *sectioniStackTable =
314             static_cast<PerfFileSectionUniStackTable *>(const_cast<PerfFileSection *>(featureSection));
315         GetReport().virtualRuntime_.ImportUniqueStackNodes(sectioniStackTable->uniStackTableInfos_);
316         GetReport().virtualRuntime_.SetDedupStack();
317     }
318 }
319 
UpdateReportInfo()320 void SubCommandReport::UpdateReportInfo()
321 {
322     // get some meta info for protobuf
323     if (protobufFormat_) {
324         // workload
325         const PerfFileSection *featureSection =
326             recordFileReader_->GetFeatureSection(FEATURE::HIPERF_WORKLOAD_CMD);
327         std::string workloader = "";
328         if (featureSection != nullptr) {
329             HLOGV("found HIPERF_META_WORKLOAD_CMD");
330             const PerfFileSectionString *sectionString =
331                 static_cast<const PerfFileSectionString *>(featureSection);
332             workloader = sectionString->ToString();
333         } else {
334             HLOGW("NOT found HIPERF_META_WORKLOAD_CMD");
335         }
336         protobufOutputFileWriter_->ProcessReportInfo(configNames_, workloader);
337     }
338 }
339 
LoadEventConfigData()340 void SubCommandReport::LoadEventConfigData()
341 {
342     auto features = recordFileReader_->GetFeatures();
343     cpuOffMode_ = find(features.begin(), features.end(), FEATURE::HIPERF_CPU_OFF) != features.end();
344     if (cpuOffMode_) {
345         HLOGD("this is cpuOffMode ");
346     }
347     const PerfFileSection *featureSection =
348         recordFileReader_->GetFeatureSection(FEATURE::EVENT_DESC);
349     if (featureSection != nullptr) {
350         HLOGV("have EVENT_DESC");
351         LoadEventDesc();
352     } else {
353         HLOGV("have Attr Section");
354         LoadAttrSection();
355     }
356     HLOG_ASSERT(GetReport().configs_.size() > 0);
357     HLOGV("record %d have %zu configs", index_, GetReport().configs_.size());
358 }
359 
LoadEventDesc()360 void SubCommandReport::LoadEventDesc()
361 {
362     const PerfFileSection *featureSection =
363         recordFileReader_->GetFeatureSection(FEATURE::EVENT_DESC);
364     CHECK_TRUE(featureSection == nullptr, NO_RETVAL, 1, "featureSection invalid");
365     const PerfFileSectionEventDesc &sectionEventdesc =
366         *static_cast<const PerfFileSectionEventDesc *>(featureSection);
367     HLOGV("Event descriptions: %zu", sectionEventdesc.eventDesces_.size());
368     for (size_t i = 0; i < sectionEventdesc.eventDesces_.size(); i++) {
369         const AttrWithId &fileAttr = sectionEventdesc.eventDesces_[i];
370 
371         HLOGV("event name[%zu]: %s ids: %s", i, fileAttr.name.c_str(),
372               VectorToString(fileAttr.ids).c_str());
373         if (cpuOffMode_ && fileAttr.name == cpuOffEventName) {
374             // found cpuoff event id
375             std::set<uint64_t> cpuOffids(fileAttr.ids.begin(), fileAttr.ids.end());
376             cpuOffids_ = cpuOffids;
377             HLOGV("this is cpu off event");
378         } else {
379             // don't add cpuoff event
380             if (protobufFormat_) {
381                 configNames_.emplace_back(fileAttr.name);
382             }
383             for (uint64_t id : fileAttr.ids) {
384                 GetReport().configIdIndexMaps_[id] = GetReport().configs_.size(); // setup index
385                 HLOGV("add config id map %" PRIu64 " to %zu", id, GetReport().configs_.size());
386             }
387             // when cpuOffMode_ , don't use count mode , use time mode.
388             auto &config =
389                 GetReport().configs_.emplace_back(fileAttr.name, fileAttr.attr.type,
390                                                   fileAttr.attr.config, cpuOffMode_ ? false : true);
391             config.ids_ = fileAttr.ids;
392             HLOG_ASSERT(config.ids_.size() > 0);
393             if (jsonFormat_) {
394                 reportJsonFile_->reportConfigItems_.emplace(
395                     fileAttr.ids,
396                     ReportConfigItem(reportJsonFile_->reportConfigItems_.size(), fileAttr.name));
397             }
398         }
399     }
400 }
401 
LoadAttrSection()402 void SubCommandReport::LoadAttrSection()
403 {
404     std::vector<AttrWithId> attrIds = recordFileReader_->GetAttrSection();
405     for (size_t i = 0; i < attrIds.size(); ++i) {
406         const AttrWithId &fileAttr = attrIds[i];
407         std::string name = PerfEvents::GetStaticConfigName(
408             static_cast<perf_type_id>(fileAttr.attr.type), fileAttr.attr.config);
409         configNames_.emplace_back(name);
410         for (uint64_t id : fileAttr.ids) {
411             GetReport().configIdIndexMaps_[id] = GetReport().configs_.size(); // setup index
412             HLOGV("add config id map %" PRIu64 " to %zu", id, GetReport().configs_.size());
413         }
414         auto &config = GetReport().configs_.emplace_back(fileAttr.name, fileAttr.attr.type,
415                                                          fileAttr.attr.config);
416         config.ids_ = fileAttr.ids;
417         HLOG_ASSERT(config.ids_.size() > 0);
418         if (jsonFormat_) {
419             reportJsonFile_->reportConfigItems_.emplace(
420                 fileAttr.ids, ReportConfigItem(reportJsonFile_->reportConfigItems_.size(), name));
421         }
422         HLOGV("event name[%zu]: %s ids: %s", i, name_.c_str(),
423               VectorToString(fileAttr.ids).c_str());
424     }
425 }
426 
ProcessFeaturesData()427 void SubCommandReport::ProcessFeaturesData()
428 {
429     LoadEventConfigData();
430 
431     // update device arch from feature
432     SetDeviceArch(GetArchTypeFromUname(recordFileReader_->GetFeatureString(FEATURE::ARCH)));
433 
434 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
435     UpdateReportInfo();
436 #endif
437 }
438 
FlushCacheRecord()439 void SubCommandReport::FlushCacheRecord()
440 {
441     for (auto &pair : prevSampleCache_) {
442         std::unique_ptr<PerfRecordSample> sample = std::move(pair.second);
443         sample->data_.period = 1u;
444         if (cpuOffMode_ && cpuOffids_.size() > 0 && cpuOffids_.count(sample->data_.id) > 0) {
445             BroadcastSample(sample);
446         } else {
447             ProcessSample(sample);
448         }
449     }
450     prevSampleCache_.clear();
451 }
452 
LoadPerfData()453 bool SubCommandReport::LoadPerfData()
454 {
455     // check if file exist
456     if (access(recordFile_[index_].c_str(), F_OK) != 0) {
457         // file not exists
458         printf("Can not access data file %s\n", recordFile_[index_].c_str());
459         return false;
460     }
461 
462     // try load the file
463     recordFileReader_ = PerfFileReader::Instance(recordFile_[index_]);
464     if (recordFileReader_ == nullptr) {
465         HLOGE("FileReader::Instance(%s) return null", recordFile_[index_].c_str());
466         return false;
467     }
468 
469     CHECK_TRUE(!recordFileReader_->ReadFeatureSection(), false, LOG_TYPE_PRINTF, "record format error.\n");
470     if (jsonFormat_) {
471         reportJsonFile_ =
472             std::make_unique<ReportJsonFile>(recordFileReader_, GetReport().virtualRuntime_);
473     }
474 
475     ProcessFeaturesData();
476     ProcessSymbolsData();
477     ProcessUniStackTableData();
478     HLOGD("process record");
479     // before load data section
480     SetHM();
481     recordFileReader_->ReadDataSection(
482         [this] (PerfEventRecord& record) -> bool {
483             return this->RecordCallBack(record);
484         });
485     if (cpuOffMode_) {
486         FlushCacheRecord();
487     }
488     HLOGD("process record completed");
489 
490     LoadPerfDataCompleted();
491     return true;
492 }
493 
OutputStd()494 bool SubCommandReport::OutputStd()
495 {
496     if (fprintf(output_, "<<Hiperf Report%s>>\n", diffMode_ ? " Diff" : "") < 0) {
497         return false;
498     }
499 
500     // feature string:
501     const auto &featureSections = recordFileReader_->GetFeatureSections();
502     HLOGV("featureSections: %zu ", featureSections.size());
503 
504     for (auto &featureSection : featureSections) {
505         if (recordFileReader_->IsFeatrureStringSection(featureSection->featureId_)) {
506             const PerfFileSectionString *sectionString =
507                 static_cast<const PerfFileSectionString *>(featureSection.get());
508 
509             fprintf(output_, "%s: %s\n",
510                     PerfFileSection::GetFeatureName(featureSection.get()->featureId_).c_str(),
511                     sectionString->ToString().c_str());
512         }
513     }
514 
515     if (cpuOffMode_) {
516         fprintf(output_, "cpu off mode: enabled\n");
517     }
518 
519     if (!diffMode_) {
520         GetReport(FIRST).AdjustReportItems();
521         GetReport(FIRST).OutputStd(output_);
522     } else {
523         GetReport(FIRST).AdjustReportItems();
524         GetReport(SECOND).AdjustReportItems();
525         GetReport(FIRST).OutputStdDiff(output_, GetReport(SECOND));
526     }
527 
528     return true;
529 }
530 
OutputReport()531 bool SubCommandReport::OutputReport()
532 {
533     if (output_ == nullptr) {
534         HLOGD("nothing need output");
535         return true; //
536     } else if (jsonFormat_) {
537         HLOGD("report as json");
538         return reportJsonFile_->OutputJson(output_);
539     } else {
540         return OutputStd();
541     }
542 }
543 
PrepareOutput()544 bool SubCommandReport::PrepareOutput()
545 {
546     if (protobufFormat_) {
547 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
548         // check if file exist
549         if (access(recordFile_[index_].c_str(), F_OK) != 0) {
550             printf("Can not access data file %s\n", recordFile_[index_].c_str());
551             return false;
552         }
553         printf("save to protobuf file: '%s'\n", reportFile_.c_str());
554         protobufOutputFileWriter_ = std::make_unique<ReportProtobufFileWriter>();
555         protobufOutputFileWriter_->Create(reportFile_);
556 #endif
557         return true;
558     }
559 
560     if (!reportFile_.empty()) {
561         std::string resolvedPath = CanonicalizeSpecPath(reportFile_.c_str());
562         output_ = fopen(resolvedPath.c_str(), "w");
563         if (output_ == nullptr) {
564             printf("unable open file to '%s' because '%d'\n", reportFile_.c_str(), errno);
565             return false;
566         } else {
567             printf("report will save at '%s'\n", reportFile_.c_str());
568         }
569     } else {
570         output_ = stdout;
571     }
572 
573     return true;
574 }
575 
~SubCommandReport()576 SubCommandReport::~SubCommandReport()
577 {
578 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
579     if (protobufOutputFileWriter_ != nullptr) {
580         protobufOutputFileWriter_->Close();
581     }
582 #endif
583     if (output_ != nullptr && output_ != stdout) {
584         fclose(output_);
585     }
586 
587     SymbolsFile::onRecording_ = true; // back to default for UT
588 }
589 
OnSubCommand(std::vector<std::string> & args)590 HiperfError SubCommandReport::OnSubCommand(std::vector<std::string>& args)
591 {
592     RETURN_IF(!PrepareOutput(), HiperfError::PREPARE_OUTPUT_FAIL);
593 
594     // any way tell symbols this is not on recording
595     SymbolsFile::onRecording_ = false;
596 
597     printf("loading data\n");
598     if (!LoadPerfData()) {
599         return HiperfError::LOAD_PERF_DATA_FAIL;
600     }
601 
602     if (diffMode_) {
603         // we are in diff mode
604         index_ = SECOND;
605         // load again with second file
606         CHECK_TRUE(!LoadPerfData(), HiperfError::LOAD_SECOND_PERF_DATA_FAIL, 0, "");
607         // back to first
608         index_ = FIRST;
609     }
610     printf("prepare report\n");
611     CHECK_TRUE(!OutputReport(), HiperfError::OUTPUT_REPORT_FAIL, 1, "OutputReport failed");
612 #ifdef HIPERF_DEBUG_TIME
613     printf("SymbolicRecordTimes: %0.3f ms\n",
614            GetReport(FIRST).virtualRuntime_.symbolicRecordTimes_.count() / MS_DURATION);
615 #endif
616 
617     printf("report done\n");
618     return HiperfError::NO_ERR;
619 }
620 
RegisterSubCommandReport()621 bool SubCommandReport::RegisterSubCommandReport()
622 {
623     return SubCommand::RegisterSubCommand("report", SubCommandReport::GetInstance);
624 }
625 
SetHM()626 void SubCommandReport::SetHM()
627 {
628     std::string os = recordFileReader_->GetFeatureString(FEATURE::OSRELEASE);
629     isHM_ = os.find(HMKERNEL) != std::string::npos;
630     GetReport().virtualRuntime_.SetHM(isHM_);
631     HLOGD("Set isHM_: %d", isHM_);
632     if (isHM_) {
633         pid_t devhost = -1;
634         std::string str = recordFileReader_->GetFeatureString(FEATURE::HIPERF_HM_DEVHOST);
635         if (str != EMPTY_STRING && IsNumeric(str)) {
636             devhost = std::stoll(str);
637         }
638         GetReport().virtualRuntime_.SetDevhostPid(devhost);
639     }
640 }
641 
GetInstance()642 SubCommand& SubCommandReport::GetInstance()
643 {
644     static SubCommandReport subCommand;
645     return subCommand;
646 }
647 } // namespace HiPerf
648 } // namespace Developtools
649 } // namespace OHOS
650