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 16 #ifndef REPORT_H 17 #define REPORT_H 18 19 #include <algorithm> 20 #include <cstdlib> 21 #include <functional> 22 #include <map> 23 #include <stdio.h> 24 25 #include "debug_logger.h" 26 // remove me latter 27 #include "report_json_file.h" 28 #include "utilities.h" 29 #include "virtual_runtime.h" 30 31 namespace OHOS { 32 namespace Developtools { 33 namespace HiPerf { 34 class ReportItemCallFrame { 35 public: 36 std::string_view func_; 37 uint64_t vaddr_; 38 std::string_view dso_; 39 uint64_t eventCount_ = 0; // call chain event 40 uint64_t selfEventCount_ = 0; // call chain event end in this function 41 std::vector<ReportItemCallFrame> childs; ReportItemCallFrame(std::string_view func,uint64_t vaddr,std::string_view dso,uint64_t eventCount,uint64_t selfEventCount)42 ReportItemCallFrame(std::string_view func, uint64_t vaddr, std::string_view dso, 43 uint64_t eventCount, uint64_t selfEventCount) 44 : func_(func), 45 vaddr_(vaddr), 46 dso_(dso), 47 eventCount_(eventCount), 48 selfEventCount_(selfEventCount) 49 { 50 } 51 52 bool operator==(const ReportItemCallFrame &b) const 53 { 54 return Same(b); 55 } 56 57 bool operator!=(const ReportItemCallFrame &b) const 58 { 59 return !Same(b); 60 } 61 CompareSortingEventCount(const ReportItemCallFrame & a,const ReportItemCallFrame & b)62 static int CompareSortingEventCount(const ReportItemCallFrame &a, const ReportItemCallFrame &b) 63 { 64 return a.eventCount_ > b.eventCount_; 65 } 66 67 static void OrderCallFrames(std::vector<ReportItemCallFrame> &callframes, int indent = 2) 68 { 69 int i = 2; 70 if (callframes.size() > 0) { 71 std::sort(callframes.begin(), callframes.end(), 72 &ReportItemCallFrame::CompareSortingEventCount); 73 74 for (auto &callframe : callframes) { 75 HLOGDUMMY("%*s%s", indent, "", callframe.ToDebugString().c_str()); 76 if (callframe.childs.size() > 0) { 77 OrderCallFrames(callframe.childs, indent + i); 78 } 79 } 80 } 81 } 82 83 // just a log 84 static void DumpCallFrames(std::vector<ReportItemCallFrame> &callframes, int indent = 2) 85 { 86 int y = 2; 87 if (callframes.size() > 0) { 88 for (auto &callframe : callframes) { 89 HLOGV("%*s%s", indent, "", callframe.ToDebugString().c_str()); 90 if (callframe.childs.size() > 0) { 91 DumpCallFrames(callframe.childs, indent + y); 92 } 93 } 94 } 95 } 96 ToDebugString()97 const std::string ToDebugString() const 98 { 99 return StringPrintf("%" PRIu64 "(%" PRIu64 ")%s(%s+0x%" PRIx64 ") child %zu", eventCount_, 100 selfEventCount_, func_.data(), dso_.data(), vaddr_, childs.size()); 101 } 102 103 private: Same(const ReportItemCallFrame & b)104 bool Same(const ReportItemCallFrame &b) const 105 { 106 return (func_ == b.func_) and (vaddr_ == b.vaddr_) and (dso_ == b.dso_); 107 } 108 }; 109 110 // one item or one line in report 111 class ReportItem { 112 public: 113 pid_t pid_ = 0; 114 pid_t tid_ = 0; 115 std::string_view comm_ = ""; 116 std::string_view dso_ = ""; 117 std::string_view fromDso_ = ""; 118 std::string_view fromFunc_ = ""; 119 std::string_view func_ = ""; 120 uint64_t vaddr_ = 0; 121 uint64_t eventCount_ = 0; // event count 122 std::vector<ReportItemCallFrame> callStacks_; 123 float heat = 0.0f; 124 static unsigned long long allIndex_; // debug only 125 unsigned long long index_; 126 127 // only for ut test ReportItem(pid_t pid,pid_t tid,const char * comm,const char * dso,const char * func,uint64_t vaddr,uint64_t eventCount)128 ReportItem(pid_t pid, pid_t tid, const char *comm, const char *dso, const char *func, 129 uint64_t vaddr, uint64_t eventCount) 130 : pid_(pid), 131 tid_(tid), 132 comm_(comm), 133 dso_(dso), 134 func_(func), 135 vaddr_(vaddr), 136 eventCount_(eventCount) 137 { 138 HLOG_ASSERT(comm != nullptr); 139 index_ = allIndex_++; 140 } 141 ReportItem(pid_t pid,pid_t tid,std::string & comm,const std::string_view & dso,const std::string_view & func,uint64_t vaddr,uint64_t eventCount)142 ReportItem(pid_t pid, pid_t tid, std::string &comm, const std::string_view &dso, 143 const std::string_view &func, uint64_t vaddr, uint64_t eventCount) 144 : pid_(pid), 145 tid_(tid), 146 comm_(comm), 147 dso_(dso), 148 func_(func), 149 vaddr_(vaddr), 150 eventCount_(eventCount) 151 { 152 HLOG_ASSERT(!comm.empty()); 153 index_ = allIndex_++; 154 } 155 156 bool operator==(const ReportItem &b) const 157 { 158 return Same(b); 159 } 160 161 bool operator!=(const ReportItem &b) const 162 { 163 return !Same(b); 164 } 165 166 // debug only ToDebugString()167 const std::string ToDebugString() const 168 { 169 return StringPrintf("%d:%d:%s-%s(%s):%zu i:%llu", pid_, tid_, comm_.data(), func_.data(), 170 dso_.data(), eventCount_, index_); 171 } 172 173 // Count CompareEventCount(const ReportItem & a,const ReportItem & b)174 static int CompareEventCount(const ReportItem &a, const ReportItem &b) 175 { 176 if (a.eventCount_ != b.eventCount_) { 177 return (a.eventCount_ > b.eventCount_) ? 1 : -1; 178 } else { 179 return 0; 180 } 181 } 182 CompareSortingEventCount(const ReportItem & a,const ReportItem & b)183 static int CompareSortingEventCount(const ReportItem &a, const ReportItem &b) 184 { 185 return a.eventCount_ > b.eventCount_; 186 } 187 GetEventCount(const ReportItem & a,size_t len,const std::string & format)188 static const std::string GetEventCount(const ReportItem &a, size_t len, 189 const std::string &format) 190 { 191 return StringPrintf(format.c_str(), len, a.eventCount_); 192 } 193 194 // Pid ComparePid(const ReportItem & a,const ReportItem & b)195 static int ComparePid(const ReportItem &a, const ReportItem &b) 196 { 197 if (a.pid_ != b.pid_) { 198 return (a.pid_ > b.pid_) ? 1 : -1; 199 } else { 200 return 0; 201 } 202 } GetPid(const ReportItem & a,size_t len,const std::string & format)203 static const std::string GetPid(const ReportItem &a, size_t len, const std::string &format) 204 { 205 return StringPrintf(format.c_str(), len, a.pid_); 206 } 207 208 // Tid CompareTid(const ReportItem & a,const ReportItem & b)209 static int CompareTid(const ReportItem &a, const ReportItem &b) 210 { 211 if (a.tid_ != b.tid_) { 212 return (a.tid_ > b.tid_) ? 1 : -1; 213 } else { 214 return 0; 215 } 216 } GetTid(const ReportItem & a,size_t len,const std::string & format)217 static const std::string GetTid(const ReportItem &a, size_t len, const std::string &format) 218 { 219 return StringPrintf(format.c_str(), len, a.tid_); 220 } 221 222 // Comm CompareComm(const ReportItem & a,const ReportItem & b)223 static int CompareComm(const ReportItem &a, const ReportItem &b) 224 { 225 int result = a.comm_.compare(b.comm_); 226 return result; 227 } GetComm(const ReportItem & a,size_t len,const std::string & format)228 static const std::string GetComm(const ReportItem &a, size_t len, const std::string &format) 229 { 230 return StringPrintf(format.c_str(), len, a.comm_.data()); 231 } 232 233 // Func CompareFunc(const ReportItem & a,const ReportItem & b)234 static int CompareFunc(const ReportItem &a, const ReportItem &b) 235 { 236 return a.func_.compare(b.func_); 237 } GetFunc(const ReportItem & a,size_t len,const std::string & format)238 static const std::string GetFunc(const ReportItem &a, size_t len, const std::string &format) 239 { 240 return StringPrintf(format.c_str(), len, a.func_.data()); 241 } 242 243 // Dso CompareDso(const ReportItem & a,const ReportItem & b)244 static int CompareDso(const ReportItem &a, const ReportItem &b) 245 { 246 return a.dso_.compare(b.dso_); 247 } GetDso(const ReportItem & a,size_t len,const std::string & format)248 static const std::string GetDso(const ReportItem &a, size_t len, const std::string &format) 249 { 250 return StringPrintf(format.c_str(), len, a.dso_.data()); 251 } 252 253 // fromDso CompareFromDso(const ReportItem & a,const ReportItem & b)254 static int CompareFromDso(const ReportItem &a, const ReportItem &b) 255 { 256 return a.fromDso_.compare(b.fromDso_); 257 } GetFromDso(const ReportItem & a,size_t len,const std::string & format)258 static const std::string GetFromDso(const ReportItem &a, size_t len, const std::string &format) 259 { 260 return StringPrintf(format.c_str(), len, a.fromDso_.data()); 261 } 262 263 // fromFunc CompareFromFunc(const ReportItem & a,const ReportItem & b)264 static int CompareFromFunc(const ReportItem &a, const ReportItem &b) 265 { 266 return a.fromFunc_.compare(b.fromFunc_); 267 } GetFromFunc(const ReportItem & a,size_t len,const std::string & format)268 static const std::string GetFromFunc(const ReportItem &a, size_t len, const std::string &format) 269 { 270 return StringPrintf(format.c_str(), len, a.fromFunc_.data()); 271 } 272 273 private: Same(const ReportItem & b)274 bool Same(const ReportItem &b) const 275 { 276 return (comm_ == b.comm_) && (pid_ == b.pid_) && (tid_ == b.tid_) && (func_ == b.func_) && 277 (dso_ == b.dso_) && (vaddr_ == b.vaddr_); 278 } 279 }; 280 281 using ReportKeyCompareFunction = int(const ReportItem &, const ReportItem &); 282 using ReportKeyGetFunction = const std::string(const ReportItem &, size_t, const std::string &); 283 284 constexpr const int MAX_FILED_LEN = 20; 285 constexpr const int CALLSTACK_INDENT = 4; 286 struct ReportKey { 287 const std::string keyName_; 288 const std::string valueFormat_; 289 size_t maxLen_ = 0u; 290 std::string maxValue_; 291 ReportKeyCompareFunction &compareFunction_; 292 ReportKeyGetFunction &GetFunction_; 293 const std::vector<std::string> &displayFilter_; 294 ReportKeyReportKey295 ReportKey(const std::string keyName, ReportKeyCompareFunction &compareFunction, 296 ReportKeyGetFunction &GetFunction, const std::string valueFormat, 297 const std::vector<std::string> &displayFilter) 298 : keyName_(keyName), 299 valueFormat_(valueFormat), 300 compareFunction_(compareFunction), 301 GetFunction_(GetFunction), 302 displayFilter_(displayFilter) 303 { 304 maxLen_ = keyName.size(); 305 } 306 UpdateValueMaxLenReportKey307 void UpdateValueMaxLen(const std::string &value) 308 { 309 size_t newMaxLen = std::max(maxLen_, value.size()); 310 if (maxLen_ < newMaxLen) { 311 maxValue_ = value; 312 maxLen_ = newMaxLen; 313 } 314 } 315 UpdateValueMaxLenReportKey316 void UpdateValueMaxLen(size_t value) 317 { 318 size_t newMaxLen = std::max(maxLen_, std::to_string(value).size()); 319 if (maxLen_ < newMaxLen) { 320 maxValue_ = std::to_string(value); 321 maxLen_ = newMaxLen; 322 } 323 } 324 GetValueReportKey325 std::string GetValue(const ReportItem &i) 326 { 327 return GetFunction_(i, maxLen_, valueFormat_); 328 } 329 ShouldDisplayReportKey330 bool ShouldDisplay(const ReportItem &i) 331 { 332 if (displayFilter_.size() == 0) { 333 return true; 334 } else { 335 std::string value = GetFunction_(i, 0, valueFormat_); 336 auto it = find(displayFilter_.begin(), displayFilter_.end(), value); 337 if (it == displayFilter_.end()) { 338 HLOGV(" not found '%s' in %s", value.c_str(), 339 VectorToString(displayFilter_).c_str()); 340 } 341 return (it != displayFilter_.end()); 342 } 343 } 344 }; 345 346 using ReportItems = std::vector<ReportItem>; 347 using ReportItemsIt = ReportItems::iterator; 348 using ReportItemsConstIt = ReportItems::const_iterator; 349 350 struct ReportOption { 351 float heatLimit_ = 0.0f; 352 float callStackHeatLimit_ = 0.0f; 353 354 // display filter 355 std::vector<std::string> displayComms_ {}; 356 std::vector<std::string> displayPids_ {}; 357 std::vector<std::string> displayTids_ {}; 358 std::vector<std::string> displayDsos_ {}; 359 std::vector<std::string> displayFromDsos_ {}; 360 std::vector<std::string> displayFuncs_ {}; 361 std::vector<std::string> displayFromFuncs_ {}; 362 std::vector<std::string> displayDummy_ {}; 363 364 std::vector<std::string> sortKeys_ = {"comm", "pid", "tid", "dso", "func"}; 365 366 bool debug_ = false; 367 bool hideCount_ = false; 368 }; 369 370 class Report { 371 public: Report()372 Report() : option_(defaultOption_), virtualRuntime_(false) 373 { 374 // works for ut test 375 } Report(ReportOption & option)376 Report(ReportOption &option) : option_(option), virtualRuntime_(false) {} 377 bool MultiLevelSame(const ReportItem &a, const ReportItem &b); 378 void AdjustReportItems(); 379 void AddReportItem(const PerfRecordSample &sample, bool includeCallStack); 380 void AddReportItemBranch(const PerfRecordSample &sample); 381 void OutputStd(FILE *output); 382 void OutputStdDiff(FILE *output, Report &other); 383 384 ReportOption &option_; 385 386 VirtualRuntime virtualRuntime_; 387 388 std::map<std::string, ReportKey> reportKeyMap_ = { 389 { 390 "count", 391 { 392 "count", 393 ReportItem::CompareEventCount, 394 ReportItem::GetEventCount, 395 "%*" PRIu64 "", 396 option_.displayDummy_, 397 }, 398 }, 399 { 400 "comm", 401 { 402 "comm", 403 ReportItem::CompareComm, 404 ReportItem::GetComm, 405 "%-*s", 406 option_.displayComms_, 407 }, 408 }, 409 { 410 "pid", 411 { 412 "pid", 413 ReportItem::ComparePid, 414 ReportItem::GetPid, 415 "%*d", 416 option_.displayPids_, 417 }, 418 }, 419 { 420 "tid", 421 { 422 "tid", 423 ReportItem::CompareTid, 424 ReportItem::GetTid, 425 "%*d", 426 option_.displayTids_, 427 }, 428 }, 429 { 430 "dso", 431 { 432 "dso", 433 ReportItem::CompareDso, 434 ReportItem::GetDso, 435 "%-*s", 436 option_.displayDsos_, 437 }, 438 }, 439 { 440 "from_dso", 441 { 442 "from_dso", 443 ReportItem::CompareFromDso, 444 ReportItem::GetFromDso, 445 "%-*s", 446 option_.displayFromDsos_, 447 }, 448 }, 449 { 450 "func", 451 { 452 "func", 453 ReportItem::CompareFunc, 454 ReportItem::GetFunc, 455 "%-*s", 456 option_.displayFuncs_, 457 }, 458 }, 459 { 460 "from_func", 461 { 462 "from_func", 463 ReportItem::CompareFromFunc, 464 ReportItem::GetFromFunc, 465 "%-*s", 466 option_.displayFromFuncs_, 467 }, 468 }, 469 }; 470 struct ReportEventConfigItem { 471 ReportEventConfigItem(const ReportEventConfigItem &) = delete; 472 ReportEventConfigItem &operator=(const ReportEventConfigItem &) = delete; 473 ReportEventConfigItem(ReportEventConfigItem &&) = default; 474 std::string eventName_; 475 uint64_t sampleCount_ = 0; 476 uint64_t eventCount_ = 0; 477 std::vector<ReportItem> reportItems_; 478 uint32_t type_; 479 uint64_t config_; 480 std::vector<uint64_t> ids_; 481 482 bool coutMode_ = true; // use cout or time ? 483 bool operator==(const ReportEventConfigItem &o) const 484 { 485 return (type_ == o.type_) && (config_ == o.config_); 486 } 487 bool operator!=(const ReportEventConfigItem &o) const 488 { 489 return !(operator==(o)); 490 } toDebugStringReportEventConfigItem491 std::string toDebugString() 492 { 493 return StringPrintf("%s(%" PRIu32 "-%" PRIu64 "):PRIu64", eventName_.c_str(), type_, 494 config_, sampleCount_); 495 } 496 ReportEventConfigItem(std::string eventName, uint32_t type, uint64_t config, 497 bool coutMode = true) eventName_ReportEventConfigItem498 : eventName_(eventName), type_(type), config_(config), coutMode_(coutMode) 499 { 500 } 501 }; 502 std::vector<ReportEventConfigItem> configs_; ~Report()503 virtual ~Report() {} 504 505 std::map<uint64_t, size_t> configIdIndexMaps_; // index of configNames_ GetConfigName(uint64_t id)506 std::string GetConfigName(uint64_t id) 507 { 508 return configs_[GetConfigIndex(id)].eventName_; 509 } GetConfigIndex(uint64_t id)510 size_t GetConfigIndex(uint64_t id) 511 { 512 HLOG_ASSERT_MESSAGE(configIdIndexMaps_.find(id) != configIdIndexMaps_.end(), 513 "unable found id %" PRIx64 "", id); 514 return configIdIndexMaps_.at(id); 515 } 516 517 private: 518 FILE *output_ = nullptr; 519 const std::string TEXT_RED = "\x1b[31m"; 520 const std::string TEXT_GREEN = "\x1b[32m"; 521 const std::string TEXT_RESET = "\033[0m"; 522 const unsigned int ConsoleDefaultWidth = 80; 523 524 // sometime caller don't give the option 525 ReportOption defaultOption_; 526 527 std::vector<std::string> displayKeyNames_; 528 529 // use virtual only for gmock test 530 bool MultiLevelSorting(const ReportItem &a, const ReportItem &b); 531 bool MultiLevelSameAndUpdateCount(ReportItem &l, ReportItem &r); 532 void MergeCallFrameCount(ReportItem &l, ReportItem &r); 533 virtual int MultiLevelCompare(const ReportItem &a, const ReportItem &b); 534 535 void StatisticsRecords(); 536 void FilterDisplayRecords(); 537 void UpdateReportItemsAfterAdjust(); 538 539 // std out 540 unsigned int consoleWidth_ = 0; 541 void PrepareConsole(); 542 543 void OutputStdStatistics(ReportEventConfigItem &); 544 bool OutputStdStatistics(ReportEventConfigItem &config, ReportEventConfigItem &otherConfig); 545 546 void OutputStdHead(ReportEventConfigItem &, bool diffMode = false); 547 548 void OutputStdContent(ReportEventConfigItem &); 549 void OutputStdContentDiff(ReportEventConfigItem &, ReportEventConfigItem &); 550 551 void OutputStdContentItem(const ReportItem &reportItem); 552 553 void OutputStdCallFrames(int indent, const ReportItemCallFrame &callFrames, 554 uint64_t totalEventCount); 555 bool OutputStdCallFrame(int indent, const std::string_view &funcName, uint64_t eventCount, 556 uint64_t totalEventCount); 557 void OutputStdItemHeating(float heat, float heat2); 558 559 FRIEND_TEST(ReportTest, MultiLevelSorting); 560 FRIEND_TEST(ReportTest, MultiLevelSameAndUpdateCount); 561 FRIEND_TEST(ReportTest, MergeCallFrameCount); 562 FRIEND_TEST(ReportTest, MultiLevelCompare); 563 FRIEND_TEST(ReportTest, PrepareConsole); 564 }; 565 } // namespace HiPerf 566 } // namespace Developtools 567 } // namespace OHOS 568 #endif // REPORT_H 569