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