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