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 if (featureSection == nullptr) {
363 return;
364 }
365 const PerfFileSectionEventDesc §ionEventdesc =
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_ and 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_ and cpuOffids_.size() > 0 and 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 if (!recordFileReader_->ReadFeatureSection()) {
470 printf("record format error.\n");
471 return false;
472 }
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 std::bind(&SubCommandReport::RecordCallBack, this, std::placeholders::_1));
486 if (cpuOffMode_) {
487 FlushCacheRecord();
488 }
489 HLOGD("process record completed");
490
491 LoadPerfDataCompleted();
492 return true;
493 }
494
OutputStd()495 bool SubCommandReport::OutputStd()
496 {
497 if (fprintf(output_, "<<Hiperf Report%s>>\n", diffMode_ ? " Diff" : "") < 0) {
498 return false;
499 }
500
501 // feature string:
502 const auto &featureSections = recordFileReader_->GetFeatureSections();
503 HLOGV("featureSections: %zu ", featureSections.size());
504
505 for (auto &featureSection : featureSections) {
506 if (recordFileReader_->IsFeatrureStringSection(featureSection->featureId_)) {
507 const PerfFileSectionString *sectionString =
508 static_cast<const PerfFileSectionString *>(featureSection.get());
509
510 fprintf(output_, "%s: %s\n",
511 PerfFileSection::GetFeatureName(featureSection.get()->featureId_).c_str(),
512 sectionString->ToString().c_str());
513 }
514 }
515
516 if (cpuOffMode_) {
517 fprintf(output_, "cpu off mode: enabled\n");
518 }
519
520 if (!diffMode_) {
521 GetReport(FIRST).AdjustReportItems();
522 GetReport(FIRST).OutputStd(output_);
523 } else {
524 GetReport(FIRST).AdjustReportItems();
525 GetReport(SECOND).AdjustReportItems();
526 GetReport(FIRST).OutputStdDiff(output_, GetReport(SECOND));
527 }
528
529 return true;
530 }
531
OutputReport()532 bool SubCommandReport::OutputReport()
533 {
534 if (output_ == nullptr) {
535 HLOGD("nothing need output");
536 return true; //
537 } else if (jsonFormat_) {
538 HLOGD("report as json");
539 return reportJsonFile_->OutputJson(output_);
540 } else {
541 return OutputStd();
542 }
543 }
544
PrepareOutput()545 bool SubCommandReport::PrepareOutput()
546 {
547 if (protobufFormat_) {
548 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
549 // check if file exist
550 if (access(recordFile_[index_].c_str(), F_OK) != 0) {
551 printf("Can not access data file %s\n", recordFile_[index_].c_str());
552 return false;
553 }
554 printf("save to protobuf file: '%s'\n", reportFile_.c_str());
555 protobufOutputFileWriter_ = std::make_unique<ReportProtobufFileWriter>();
556 protobufOutputFileWriter_->Create(reportFile_);
557 #endif
558 return true;
559 }
560
561 if (!reportFile_.empty()) {
562 std::string resolvedPath = CanonicalizeSpecPath(reportFile_.c_str());
563 output_ = fopen(resolvedPath.c_str(), "w");
564 if (output_ == nullptr) {
565 printf("unable open file to '%s' because '%d'\n", reportFile_.c_str(), errno);
566 return false;
567 } else {
568 printf("report will save at '%s'\n", reportFile_.c_str());
569 }
570 } else {
571 output_ = stdout;
572 }
573
574 return true;
575 }
576
~SubCommandReport()577 SubCommandReport::~SubCommandReport()
578 {
579 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
580 if (protobufOutputFileWriter_ != nullptr) {
581 protobufOutputFileWriter_->Close();
582 }
583 #endif
584 if (output_ != nullptr && output_ != stdout) {
585 fclose(output_);
586 }
587
588 SymbolsFile::onRecording_ = true; // back to default for UT
589 }
590
OnSubCommand(std::vector<std::string> & args)591 bool SubCommandReport::OnSubCommand(std::vector<std::string> &args)
592 {
593 if (!PrepareOutput()) {
594 return false;
595 }
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 false;
603 }
604
605 if (diffMode_) {
606 // we are in diff mode
607 index_ = SECOND;
608 // load again with second file
609 if (!LoadPerfData()) {
610 return false;
611 }
612 // back to first
613 index_ = FIRST;
614 }
615 printf("prepare report\n");
616 if (!OutputReport()) {
617 HLOGD("OutputReport failed");
618 return false;
619 }
620 #ifdef HIPERF_DEBUG_TIME
621 printf("SymbolicRecordTimes: %0.3f ms\n",
622 GetReport(FIRST).virtualRuntime_.symbolicRecordTimes_.count() / MS_DURATION);
623 #endif
624
625 printf("report done\n");
626 return true;
627 }
628
RegisterSubCommandReport()629 bool SubCommandReport::RegisterSubCommandReport()
630 {
631 std::unique_ptr<SubCommand> cmd = std::make_unique<SubCommandReport>();
632 return SubCommand::RegisterSubCommand("report", std::move(cmd));
633 }
634
SetHM()635 void SubCommandReport::SetHM()
636 {
637 std::string os = recordFileReader_->GetFeatureString(FEATURE::OSRELEASE);
638 isHM_ = os.find(HMKERNEL) != std::string::npos;
639 GetReport().virtualRuntime_.SetHM(isHM_);
640 HLOGD("Set isHM_: %d", isHM_);
641 if (isHM_) {
642 pid_t devhost = -1;
643 std::string str = recordFileReader_->GetFeatureString(FEATURE::HIPERF_HM_DEVHOST);
644 if (str != EMPTY_STRING) {
645 devhost = std::stoll(str);
646 }
647 GetReport().virtualRuntime_.SetDevhostPid(devhost);
648 }
649 }
650 } // namespace HiPerf
651 } // namespace Developtools
652 } // namespace OHOS
653