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 31using namespace std::placeholders; 32namespace OHOS { 33namespace Developtools { 34namespace HiPerf { 35unsigned long long ReportItem::allIndex_ = 0; 36void 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 93void 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 118void 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 138void 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 169void 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 190void 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 230int 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 253bool Report::MultiLevelSame(const ReportItem &a, const ReportItem &b) 254{ 255 return MultiLevelCompare(a, b) == 0; 256} 257 258void 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 297bool 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 314bool 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 335void 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 355void 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 395bool 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 420void 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 442void 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 480void 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 506void 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 520void 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 549void 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 598void 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 610void 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