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