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