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 "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
31 using namespace std::placeholders;
32 namespace OHOS {
33 namespace Developtools {
34 namespace HiPerf {
35 unsigned long long ReportItem::allIndex_ = 0;
AddReportItem(const PerfRecordSample & sample,bool includeCallStack)36 void Report::AddReportItem(const PerfRecordSample &sample, bool includeCallStack)
37 {
38 size_t configIndex = GetConfigIndex(sample.data_.id);
39 HLOG_ASSERT_MESSAGE(configs_.size() > configIndex,
40 "in %zu configs found index %zu, from ids %llu", configs_.size(),
41 configIndex, sample.data_.id);
42 VirtualThread &thread = virtualRuntime_.GetThread(sample.data_.pid, sample.data_.tid);
43 HLOG_ASSERT(sample.callFrames_.size() > 0);
44 // if we need callstack ?
45 if (includeCallStack) {
46 // we will use caller mode , from last to first
47 auto frameIt = sample.callFrames_.rbegin();
48 ReportItem &item = configs_[configIndex].reportItems_.emplace_back(
49 sample.data_.pid, sample.data_.tid, thread.name_, frameIt->mapName,
50 frameIt->funcName, frameIt->funcOffset, sample.data_.period);
51 HLOGD("ReportItem: %s", item.ToDebugString().c_str());
52 HLOG_ASSERT(!item.func_.empty());
53
54 std::vector<ReportItemCallFrame> *currentCallFrames = &item.callStacks_;
55 for (frameIt = sample.callFrames_.rbegin(); frameIt != sample.callFrames_.rend();
56 frameIt++) {
57 HLOG_ASSERT(frameIt->pc < PERF_CONTEXT_MAX);
58 // in add items case , right one should only have 1 callstack
59 // so just new callfames and move to next level
60 ReportItemCallFrame &nextCallFrame = currentCallFrames->emplace_back(
61 frameIt->funcName, frameIt->funcOffset, frameIt->mapName,
62 sample.data_.period,
63 (std::next(frameIt) == sample.callFrames_.rend()) ? sample.data_.period : 0);
64 HLOGV("add callframe %s", nextCallFrame.ToDebugString().c_str());
65 currentCallFrames = &nextCallFrame.childs;
66 }
67 HLOGV("callstack %zu", item.callStacks_.size());
68 if (item.callStacks_.size() > 0) {
69 HLOGV("callstack 2nd level %zu", item.callStacks_[0].childs.size());
70 }
71 } else {
72 auto frameIt = sample.callFrames_.begin();
73 if (frameIt != sample.callFrames_.end()) {
74 HLOG_ASSERT(frameIt->pc < PERF_CONTEXT_MAX);
75 // for arkjs frame, skip the stub.an frame
76 if (StringEndsWith(frameIt->mapName, "stub.an") && sample.callFrames_.size() > 1) {
77 HLOGV("stub.an frame, go to next, mapname %s", frameIt->mapName.c_str());
78 frameIt++;
79 }
80 ReportItem &item = configs_[configIndex].reportItems_.emplace_back(
81 sample.data_.pid, sample.data_.tid, thread.name_, frameIt->mapName,
82 frameIt->funcName, frameIt->funcOffset, sample.data_.period);
83 HLOGV("%s", item.ToDebugString().c_str());
84 HLOG_ASSERT(!item.func_.empty());
85 }
86 }
87 configs_[configIndex].sampleCount_++;
88 configs_[configIndex].eventCount_ += sample.data_.period;
89 }
90
AddReportItemBranch(const PerfRecordSample & sample)91 void Report::AddReportItemBranch(const PerfRecordSample &sample)
92 {
93 size_t configIndex = GetConfigIndex(sample.data_.id);
94 HLOG_ASSERT(configs_.size() > configIndex);
95 VirtualThread &thread = virtualRuntime_.GetThread(sample.data_.pid, sample.data_.tid);
96 for (u64 i = 0; i < sample.data_.bnr; i++) {
97 DfxSymbol symbolTo =
98 virtualRuntime_.GetSymbol(sample.data_.lbr[i].to, sample.data_.pid, sample.data_.tid);
99 DfxSymbol symbolFrom =
100 virtualRuntime_.GetSymbol(sample.data_.lbr[i].from, sample.data_.pid, sample.data_.tid);
101
102 // branch only have 1 time only for period
103 ReportItem &item = configs_[configIndex].reportItems_.emplace_back(
104 sample.data_.pid, sample.data_.tid, thread.name_, symbolTo.module_, symbolTo.GetName(),
105 symbolTo.funcVaddr_, 1u);
106
107 item.fromDso_ = symbolFrom.module_;
108 item.fromFunc_ = symbolFrom.GetName();
109
110 HLOGV("%s 0x%" PRIx64 "", item.ToDebugString().c_str(), symbolTo.taskVaddr_);
111 }
112 configs_[configIndex].sampleCount_++;
113 configs_[configIndex].eventCount_ += sample.data_.bnr;
114 }
115
StatisticsRecords()116 void Report::StatisticsRecords()
117 {
118 for (auto &config : configs_) {
119 size_t duplicates = 0;
120 size_t totalReportCount = config.reportItems_.size();
121 // merge duplicate
122 HLOGD("uniquing %zu", totalReportCount);
123 auto last = std::unique(config.reportItems_.begin(), config.reportItems_.end(),
124 [this] (ReportItem &l, ReportItem &r) -> bool {
125 return this->MultiLevelSameAndUpdateCount(l, r);
126 });
127
128 config.reportItems_.erase(last, config.reportItems_.end());
129
130 duplicates = totalReportCount - config.reportItems_.size();
131 HLOGD("duplicates %zu, %zu -> %zu", duplicates, totalReportCount,
132 config.reportItems_.size());
133 }
134 }
135
FilterDisplayRecords()136 void Report::FilterDisplayRecords()
137 {
138 // remove the item with not in fliter
139 for (auto &config : configs_) {
140 size_t filterOuts = 0;
141 size_t totalReportCount = config.reportItems_.size();
142 for (const auto &reportKeyPair : reportKeyMap_) {
143 auto reportKey = reportKeyPair.second;
144 if (reportKey.displayFilter_.size() != 0) {
145 auto itemIt = config.reportItems_.begin();
146 while (itemIt != config.reportItems_.end()) {
147 if (!reportKey.ShouldDisplay(*itemIt)) {
148 HLOGM("filter out %s", itemIt->ToDebugString().c_str());
149
150 // we need recalc the heating ,so also remove in total count
151 config.eventCount_ -= itemIt->eventCount_;
152
153 // after update total eventCount remove this
154 itemIt = config.reportItems_.erase(itemIt);
155 filterOuts++;
156 } else {
157 itemIt++;
158 }
159 }
160 }
161 }
162 HLOGD("filter out %zu, %zu -> %zu", filterOuts, totalReportCount,
163 config.reportItems_.size());
164 }
165 }
166
UpdateReportItemsAfterAdjust()167 void Report::UpdateReportItemsAfterAdjust()
168 {
169 for (auto &config : configs_) {
170 HLOGV("percentage %zu items", config.reportItems_.size());
171 uint64_t totalEventCount = 0; // just for debug check
172 for (auto &item : config.reportItems_) {
173 item.heat = Percentage(item.eventCount_, config.eventCount_);
174 totalEventCount += item.eventCount_;
175 HLOGM("%s percentage from %5.2f%% %" PRIu64 "/ %" PRIu64 "",
176 item.ToDebugString().c_str(), item.heat, item.eventCount_, config.eventCount_);
177 for (auto keyPair : reportKeyMap_) {
178 reportKeyMap_.at(keyPair.first).UpdateValueMaxLen(keyPair.second.GetValue(item));
179 }
180 }
181 // check again
182 HLOGV("recalc totalEventCount is %" PRIu64 " old totalEventCount is %" PRIu64 "",
183 totalEventCount, config.eventCount_);
184 HLOG_ASSERT(totalEventCount == config.eventCount_);
185 }
186 }
187
AdjustReportItems()188 void Report::AdjustReportItems()
189 {
190 HLOGD("Adjust Record Order ....");
191 for (auto &config : configs_) {
192 uint64_t totalReportCount = config.reportItems_.size();
193 if (option_.debug_) {
194 for (auto &reportItem : config.reportItems_) {
195 HLOGV("reportItem %s", reportItem.ToDebugString().c_str());
196 }
197 }
198 // sort first.
199 HLOGD("MultiLevelSorting %" PRIu64 "", totalReportCount);
200 std::sort(config.reportItems_.begin(), config.reportItems_.end(),
201 [this] (const ReportItem &a, const ReportItem &b) -> bool {
202 return this->MultiLevelSorting(a, b);
203 });
204 HLOGD("MultiLevelSorting %" PRIu64 " done", totalReportCount);
205 // reorder the callstack
206 if (option_.debug_) {
207 for (auto &reportItem : config.reportItems_) {
208 HLOGV("reportItem %s", reportItem.ToDebugString().c_str());
209 }
210 }
211 StatisticsRecords();
212 FilterDisplayRecords();
213
214 // reorder by count
215 std::sort(config.reportItems_.begin(), config.reportItems_.end(),
216 &ReportItem::CompareSortingEventCount);
217
218 // reorder the callstack
219 for (auto &reportItem : config.reportItems_) {
220 ReportItemCallFrame::OrderCallFrames(reportItem.callStacks_);
221 }
222 HLOGD("afater sorting and unique, we have %zu report items,", config.reportItems_.size());
223 }
224 // udpate percentage
225 UpdateReportItemsAfterAdjust();
226 }
227
MultiLevelCompare(const ReportItem & a,const ReportItem & b)228 int Report::MultiLevelCompare(const ReportItem &a, const ReportItem &b)
229 {
230 HLOGM("MultiLevelCompare %s vs %s sort order %s", a.ToDebugString().c_str(),
231 b.ToDebugString().c_str(), VectorToString(option_.sortKeys_).c_str());
232
233 // check each key user care
234 for (auto it = option_.sortKeys_.begin(); it != option_.sortKeys_.end(); ++it) {
235 int result = reportKeyMap_.at(*it).compareFunction_(a, b);
236 if (result == 0) {
237 // this key is same , check the next one
238 continue;
239 } else {
240 // if onekey is not same , returl as not same
241 HLOGM("not same because %s %d : %s vs %s", it->c_str(), result,
242 reportKeyMap_.at(*it).GetValue(a).c_str(),
243 reportKeyMap_.at(*it).GetValue(b).c_str());
244 return result;
245 }
246 }
247 // all the key is same
248 return 0;
249 }
250
MultiLevelSame(const ReportItem & a,const ReportItem & b)251 bool Report::MultiLevelSame(const ReportItem &a, const ReportItem &b)
252 {
253 return MultiLevelCompare(a, b) == 0;
254 }
255
MergeCallFrameCount(ReportItem & leftItem,ReportItem & rightItem)256 void Report::MergeCallFrameCount(ReportItem &leftItem, ReportItem &rightItem)
257 {
258 // add to left (right to left)
259 std::vector<ReportItemCallFrame> *leftCallFrames = &leftItem.callStacks_;
260 const std::vector<ReportItemCallFrame> *rightCallFrames = &rightItem.callStacks_;
261 uint64_t maxEventCount = leftItem.eventCount_;
262 // right should only have one call stack
263 int level = 0;
264 while (rightCallFrames->size() != 0) {
265 HLOG_ASSERT(rightCallFrames->size() == 1u);
266 const ReportItemCallFrame &rightFrame = rightCallFrames->at(0);
267 auto leftFrameIt = std::find(leftCallFrames->begin(), leftCallFrames->end(), rightFrame);
268 if (leftFrameIt == leftCallFrames->end()) {
269 // new callfames
270 auto &leftCallFrame = leftCallFrames->emplace_back(rightFrame);
271 HLOGV("%*s create frame %s in %s", level, "", leftCallFrame.ToDebugString().c_str(),
272 leftItem.ToDebugString().c_str());
273 HLOG_ASSERT(leftCallFrame.eventCount_ <= maxEventCount);
274 // this is a new call stack ,
275 // all the child in rightFrame has been copy to left.
276 break;
277 } else {
278 // already have , add count
279 leftFrameIt->eventCount_ += rightFrame.eventCount_;
280 leftFrameIt->selfEventCount_ += rightFrame.selfEventCount_;
281 // left move to next
282 leftCallFrames = &(leftFrameIt->childs);
283 HLOGM("%*s udpate frame +%" PRIu64 " %s in %s", level, "", rightFrame.eventCount_,
284 leftFrameIt->ToDebugString().c_str(), leftItem.ToDebugString().c_str());
285 HLOG_ASSERT_MESSAGE(leftFrameIt->eventCount_ <= maxEventCount,
286 " maxEventCount is %" PRIu64 "", maxEventCount);
287 maxEventCount = leftFrameIt->eventCount_;
288 }
289 // move to next level
290 rightCallFrames = &(rightFrame.childs);
291 level++;
292 }
293 }
294
MultiLevelSameAndUpdateCount(ReportItem & l,ReportItem & r)295 bool Report::MultiLevelSameAndUpdateCount(ReportItem &l, ReportItem &r)
296 {
297 if (MultiLevelCompare(l, r) == 0) {
298 l.eventCount_ += r.eventCount_;
299 HLOGM("l %" PRIu64 " %s c:%zu vs r %" PRIu64 " %s c:%zu", l.eventCount_, l.func_.data(),
300 l.callStacks_.size(), r.eventCount_, r.func_.data(), r.callStacks_.size());
301 // if it have call stack?
302 if (r.callStacks_.size() != 0) {
303 // add to left (right to left)
304 MergeCallFrameCount(l, r);
305 }
306 return true;
307 } else {
308 return false;
309 }
310 }
311
MultiLevelSorting(const ReportItem & a,const ReportItem & b)312 bool Report::MultiLevelSorting(const ReportItem &a, const ReportItem &b)
313 {
314 /*
315 The value returned indicates whether the element passed as first argument is
316 considered to go before the second in the specific strict weak ordering it defines.
317 */
318 bool result = MultiLevelCompare(a, b) > 0;
319 #ifdef HIPERF_DEBUG
320 if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
321 bool result2 = MultiLevelCompare(b, a) > 0;
322 if (result && result == result2) {
323 HLOGE("MultiLevelSorting a->b %d vs b->a %d", result, result2);
324 HLOGE("left %s", a.ToDebugString().c_str());
325 HLOGE("right %s", b.ToDebugString().c_str());
326 HLOG_ASSERT(false);
327 }
328 }
329 #endif
330 return result;
331 }
332
OutputStdStatistics(ReportEventConfigItem & config)333 void Report::OutputStdStatistics(ReportEventConfigItem &config)
334 {
335 if (fprintf(output_, "\n") < 0) {
336 return;
337 } // make a blank line for new event
338 if (fprintf(output_, "Event: %s (type %" PRIu32 " id %" PRIu64 ")\n", config.eventName_.c_str(),
339 config.type_, config.config_) < 0) {
340 return;
341 }
342 if (fprintf(output_, "Samples Count: %" PRIu64 "\n", config.sampleCount_) < 0) {
343 return;
344 }
345 if (!config.coutMode_) {
346 fprintf(output_, "Time in ns: ");
347 } else {
348 fprintf(output_, "Event Count: ");
349 }
350 fprintf(output_, "%" PRIu64 "\n", config.eventCount_);
351 }
352
OutputStdHead(ReportEventConfigItem & config,bool diffMode)353 void Report::OutputStdHead(ReportEventConfigItem &config, bool diffMode)
354 {
355 // head print
356 const std::string head = "Heating";
357 if (fprintf(output_, "%-*s ", FULL_PERCENTAGE_LEN, head.c_str()) < 0) {
358 return;
359 }
360
361 if (diffMode) {
362 const std::string diff = "Diff";
363 fprintf(output_, "%-*s ", FULL_PERCENTAGE_DIFF_LEN, diff.c_str());
364 }
365
366 // merge sort key and no-sort key (like count)
367
368 displayKeyNames_ = option_.sortKeys_;
369 if (!option_.hideCount_) {
370 displayKeyNames_.insert(displayKeyNames_.begin(), "count");
371 }
372
373 unsigned int remainingWidth = consoleWidth_;
374 // sort key head
375 for (auto &keyName : displayKeyNames_) {
376 auto &key = reportKeyMap_.at(keyName);
377 remainingWidth -= key.maxLen_;
378 if (remainingWidth == 0) {
379 key.maxLen_ = 0;
380 }
381 if (fprintf(output_, "%-*s ", (remainingWidth > 0) ? static_cast<unsigned int>(key.maxLen_) : 0,
382 key.keyName_.c_str()) < 0) {
383 return;
384 }
385 HLOGD("'%s' max len %zu(from '%s') console width %d", key.keyName_.c_str(), key.maxLen_,
386 key.maxValue_.c_str(), remainingWidth);
387 }
388 if (fprintf(output_, "\n") < 0) {
389 return;
390 }
391 }
392
OutputStdCallFrame(int indent,const std::string_view & funcName,uint64_t eventCount,uint64_t totalEventCount)393 bool Report::OutputStdCallFrame(int indent, const std::string_view &funcName, uint64_t eventCount,
394 uint64_t totalEventCount)
395 {
396 float heat = Percentage(eventCount, totalEventCount);
397 float num = 100.0;
398 HLOGV("frame %f indent %d at %s", heat, indent, funcName.data());
399
400 CHECK_TRUE(heat < option_.callStackHeatLimit_, false, 0, ""); // don't print this three anymore
401
402 if (abs(heat - num) < ALMOST_ZERO) {
403 fprintf(output_, "%*s", indent, " ");
404 fprintf(output_, "%*s ", FULL_PERCENTAGE_NUM_LEN, " ");
405 } else {
406 fprintf(output_, "%*s", indent, "|- ");
407 fprintf(output_, "%*.2f%% ", FULL_PERCENTAGE_NUM_LEN, heat);
408 }
409 if (option_.debug_) {
410 fprintf(output_, "%" PRIu64 "/%" PRIu64 " %s\n", eventCount, totalEventCount,
411 funcName.data());
412 } else {
413 fprintf(output_, "%s\n", funcName.data());
414 }
415 return true;
416 }
417
PrepareConsole()418 void Report::PrepareConsole()
419 {
420 #if defined(is_mingw) && is_mingw
421 CONSOLE_SCREEN_BUFFER_INFO csbi;
422 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
423 consoleWidth_ = static_cast<unsigned int>(csbi.srWindow.Right - csbi.srWindow.Left + 1);
424 const auto handle = GetStdHandle(STD_OUTPUT_HANDLE);
425 DWORD mode;
426 GetConsoleMode(handle, &mode);
427 mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
428 SetConsoleMode(handle, mode);
429 #else
430 struct winsize w = {0, 0, 0, 0};
431 ioctl(fileno(stdout), TIOCGWINSZ, &w);
432 consoleWidth_ = static_cast<unsigned int>(w.ws_col);
433 #endif
434 if (consoleWidth_ == 0) {
435 consoleWidth_ = ConsoleDefaultWidth;
436 }
437 HLOGD("consoleWidth_:%d", consoleWidth_);
438 }
439
OutputStdCallFrames(int indent,const ReportItemCallFrame & callFrame,uint64_t totalEventCount)440 void Report::OutputStdCallFrames(int indent, const ReportItemCallFrame &callFrame, uint64_t totalEventCount)
441 {
442 /*
443 90% a
444 |- 80% b
445 c
446 d
447 |- 50% e
448 |- 50% f
449 g
450 */
451 // this is the first call frame
452 // this tree will skipped.
453 CHECK_TRUE(!OutputStdCallFrame(indent, callFrame.func_, callFrame.eventCount_, totalEventCount),
454 NO_RETVAL, 0, "");
455
456 // print it self
457 if (callFrame.selfEventCount_ != 0 && callFrame.selfEventCount_ != callFrame.eventCount_) {
458 OutputStdCallFrame(indent + CALLSTACK_INDENT, "[run in self function]",
459 callFrame.selfEventCount_, callFrame.eventCount_);
460 }
461
462 // printf children
463 // if only one children
464 if (callFrame.childs.size() == 1u &&
465 callFrame.childs[0].eventCount_ == callFrame.eventCount_) {
466 HLOGV("childCallFream %*c %s", indent, ' ', callFrame.childs[0].func_.data());
467 // don't indent if same count (only one 100% children)
468 OutputStdCallFrames(indent, callFrame.childs[0], callFrame.eventCount_);
469 } else {
470 // else a lot children
471 for (const ReportItemCallFrame &childCallFrame : callFrame.childs) {
472 HLOGV("childCallFream %*c %s", indent, ' ', childCallFrame.func_.data());
473 OutputStdCallFrames(indent + CALLSTACK_INDENT, childCallFrame, callFrame.eventCount_);
474 }
475 }
476 }
477
OutputStdContent(ReportEventConfigItem & config)478 void Report::OutputStdContent(ReportEventConfigItem &config)
479 {
480 // content print
481 auto it = config.reportItems_.begin();
482 while (it != config.reportItems_.end()) {
483 const ReportItem &reportItem = it.operator*();
484 // if we need skip it ?
485 if (reportItem.heat < option_.heatLimit_) {
486 it++;
487 continue; // below limit
488 } else {
489 fprintf(output_, "%*.2f%% ", FULL_PERCENTAGE_NUM_LEN, reportItem.heat);
490 }
491 OutputStdContentItem(reportItem);
492 if (reportItem.callStacks_.size() != 0) {
493 HLOGV("reportItem.callStacks_ %zu %s", reportItem.callStacks_.size(),
494 reportItem.ToDebugString().c_str());
495 HLOG_ASSERT(reportItem.callStacks_.size() == 1u);
496 for (auto &callFrame : reportItem.callStacks_) {
497 OutputStdCallFrames(CALLSTACK_INDENT, callFrame, reportItem.eventCount_);
498 }
499 }
500 it++;
501 }
502 }
503
OutputStdContentItem(const ReportItem & reportItem)504 void Report::OutputStdContentItem(const ReportItem &reportItem)
505 {
506 // output by sort keys
507 for (auto sortKey : displayKeyNames_) {
508 ReportKey &reportKey = Report::reportKeyMap_.at(sortKey);
509 if (fprintf(output_, "%s ", reportKey.GetValue(reportItem).c_str()) < 0) {
510 return;
511 }
512 }
513 if (fprintf(output_, "\n") < 0) {
514 return;
515 }
516 }
517
OutputStdItemHeating(float heat,float heat2)518 void Report::OutputStdItemHeating(float heat, float heat2)
519 {
520 if (heat == heat2 && heat == 0.0f) {
521 fprintf(output_, "something error , all it is end.\n");
522 } else if (heat2 == 0) {
523 // only have first
524 fprintf(output_, "%*.2f%% ", FULL_PERCENTAGE_NUM_LEN, heat);
525 fprintf(output_, "%*s ", FULL_PERCENTAGE_DIFF_LEN, "");
526 } else if (heat == 0) {
527 // only have second
528 fprintf(output_, "%*s ", FULL_PERCENTAGE_LEN, "");
529 fprintf(output_, "%+*.2f%% ", FULL_PERCENTAGE_DIFF_NUM_LEN, heat2);
530 } else if (heat2 > heat) {
531 fprintf(output_, "%s%*.2f%%%s ", TEXT_RED.c_str(), FULL_PERCENTAGE_NUM_LEN, heat,
532 TEXT_RESET.c_str());
533 fprintf(output_, "%s%+*.2f%%%s ", TEXT_GREEN.c_str(), FULL_PERCENTAGE_DIFF_NUM_LEN,
534 heat2 - heat, TEXT_RESET.c_str());
535 } else if (heat2 < heat) {
536 fprintf(output_, "%s%*.2f%%%s ", TEXT_GREEN.c_str(), FULL_PERCENTAGE_NUM_LEN, heat,
537 TEXT_RESET.c_str());
538 fprintf(output_, "%s%+*.2f%%%s ", TEXT_RED.c_str(), FULL_PERCENTAGE_DIFF_NUM_LEN,
539 heat2 - heat, TEXT_RESET.c_str());
540 } else {
541 // same heating
542 fprintf(output_, "%*.2f%% ", FULL_PERCENTAGE_NUM_LEN, heat);
543 fprintf(output_, "%+*.2f%% ", FULL_PERCENTAGE_DIFF_NUM_LEN, heat2 - heat);
544 }
545 }
546
OutputStdContentDiff(ReportEventConfigItem & left,ReportEventConfigItem & right)547 void Report::OutputStdContentDiff(ReportEventConfigItem &left, ReportEventConfigItem &right)
548 {
549 // first we need found the match config
550 HLOGD("first count %zu second count %zu", left.reportItems_.size(), right.reportItems_.size());
551 ReportItemsConstIt it = left.reportItems_.begin();
552 ReportItemsConstIt it2 = right.reportItems_.begin();
553 while (it != left.reportItems_.end()) {
554 // still have it2 ?
555 if (it2 != right.reportItems_.end()) {
556 // find the same item in it2 by same sort key
557 while (it2 != right.reportItems_.end()) {
558 if (MultiLevelSame(*it, *it2)) {
559 // we found the same item
560 // output the diff heating
561 if (it->heat > option_.heatLimit_ && it2->heat > option_.heatLimit_) {
562 OutputStdItemHeating(it->heat, it2->heat);
563 OutputStdContentItem(*it);
564 }
565 it++;
566 it2++;
567 break; // next it
568 } else {
569 // only print it2 item
570 if (it2->heat > option_.heatLimit_) {
571 OutputStdItemHeating(0.0f, it2->heat);
572 OutputStdContentItem(*it2);
573 }
574 it2++;
575 continue; // next it2
576 }
577 }
578 } else {
579 // no more it2, go on print all the it
580 if (it->heat > option_.heatLimit_) {
581 OutputStdItemHeating(it->heat, 0.0f);
582 OutputStdContentItem(*it);
583 }
584 it++;
585 continue; // next it
586 }
587 }
588 while (it2 != right.reportItems_.end()) {
589 // if diff still have some item in it2 ,print it
590 OutputStdItemHeating(0, it2->heat);
591 OutputStdContentItem(*it2);
592 it2++;
593 }
594 }
595
OutputStd(FILE * output)596 void Report::OutputStd(FILE *output)
597 {
598 output_ = output;
599 PrepareConsole();
600
601 for (auto &config : configs_) {
602 OutputStdStatistics(config);
603 OutputStdHead(config);
604 OutputStdContent(config);
605 }
606 }
607
OutputStdDiff(FILE * output,Report & other)608 void Report::OutputStdDiff(FILE *output, Report &other)
609 {
610 output_ = output;
611 PrepareConsole();
612
613 auto left = configs_.begin();
614 while (left != configs_.end()) {
615 auto right = other.configs_.begin();
616 while (right != other.configs_.end()) {
617 if (*left == *right) {
618 OutputStdStatistics(*left);
619 OutputStdHead(*left, true);
620 OutputStdContentDiff(*left, *right);
621 break; // check next left
622 }
623 right++;
624 }
625 left++; // go on left
626 }
627 }
628 } // namespace HiPerf
629 } // namespace Developtools
630 } // namespace OHOS
631