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