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 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 Symbol symbol_to =
91 virtualRuntime_.GetSymbol(sample.data_.lbr[i].to, sample.data_.pid, sample.data_.tid);
92 Symbol 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.Name(),
98 symbol_to.funcVaddr_, 1u);
99
100 item.fromDso_ = symbol_from.module_;
101 item.fromFunc_ = symbol_from.Name();
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 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
OutputStdContentDiffOneSide(bool leftOnly,ReportItem & reportItem)609 void Report::OutputStdContentDiffOneSide(bool leftOnly, ReportItem &reportItem)
610 {
611 if (reportItem.heat > option_.heatLimit_) {
612 if (leftOnly) {
613 OutputStdItemHeating(reportItem.heat, 0.0f);
614 } else {
615 OutputStdItemHeating(0.0f, reportItem.heat);
616 }
617 OutputStdContentItem(reportItem);
618 }
619 }
620
OutputStd(FILE * output)621 void Report::OutputStd(FILE *output)
622 {
623 output_ = output;
624 PrepareConsole();
625
626 for (auto &config : configs_) {
627 OutputStdStatistics(config);
628 OutputStdHead(config);
629 OutputStdContent(config);
630 }
631 }
632
OutputStdDiff(FILE * output,Report & other)633 void Report::OutputStdDiff(FILE *output, Report &other)
634 {
635 output_ = output;
636 PrepareConsole();
637
638 auto left = configs_.begin();
639 while (left != configs_.end()) {
640 auto right = other.configs_.begin();
641 while (right != other.configs_.end()) {
642 if (*left == *right) {
643 OutputStdStatistics(*left);
644 OutputStdHead(*left, true);
645 OutputStdContentDiff(*left, *right);
646 break; // check next left
647 }
648 right++;
649 }
650 left++; // go on left
651 }
652 }
653 } // namespace HiPerf
654 } // namespace Developtools
655 } // namespace OHOS
656