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