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