• 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 "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