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 <cstdio> 21 #include <cstdlib> 22 #include <functional> 23 #include <map> 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 func_; 37 uint64_t vaddr_; 38 std::string 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 func,uint64_t vaddr,std::string dso,uint64_t eventCount,uint64_t selfEventCount)42 ReportItemCallFrame(std::string func, uint64_t vaddr, std::string 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_(StringViewHold::Get().Hold(dso)), 148 func_(StringViewHold::Get().Hold(func)), 149 vaddr_(vaddr), 150 eventCount_(eventCount) 151 { 152 index_ = allIndex_++; 153 } 154 155 bool operator==(const ReportItem &b) const 156 { 157 return Same(b); 158 } 159 160 bool operator!=(const ReportItem &b) const 161 { 162 return !Same(b); 163 } 164 165 // debug only ToDebugString()166 const std::string ToDebugString() const 167 { 168 return StringPrintf("%d:%d:%s-%s(%s):%zu i:%llu", pid_, tid_, comm_.data(), func_.data(), 169 dso_.data(), eventCount_, index_); 170 } 171 172 // Count CompareEventCount(const ReportItem & a,const ReportItem & b)173 static int CompareEventCount(const ReportItem &a, const ReportItem &b) 174 { 175 if (a.eventCount_ != b.eventCount_) { 176 return (a.eventCount_ > b.eventCount_) ? 1 : -1; 177 } else { 178 return 0; 179 } 180 } 181 CompareSortingEventCount(const ReportItem & a,const ReportItem & b)182 static int CompareSortingEventCount(const ReportItem &a, const ReportItem &b) 183 { 184 return a.eventCount_ > b.eventCount_; 185 } 186 GetEventCount(const ReportItem & a,size_t len,const std::string & format)187 static const std::string GetEventCount(const ReportItem &a, size_t len, 188 const std::string &format) 189 { 190 return StringPrintf(format.c_str(), len, a.eventCount_); 191 } 192 193 // Pid ComparePid(const ReportItem & a,const ReportItem & b)194 static int ComparePid(const ReportItem &a, const ReportItem &b) 195 { 196 if (a.pid_ != b.pid_) { 197 return (a.pid_ > b.pid_) ? 1 : -1; 198 } else { 199 return 0; 200 } 201 } GetPid(const ReportItem & a,size_t len,const std::string & format)202 static const std::string GetPid(const ReportItem &a, size_t len, const std::string &format) 203 { 204 return StringPrintf(format.c_str(), len, a.pid_); 205 } 206 207 // Tid CompareTid(const ReportItem & a,const ReportItem & b)208 static int CompareTid(const ReportItem &a, const ReportItem &b) 209 { 210 if (a.tid_ != b.tid_) { 211 return (a.tid_ > b.tid_) ? 1 : -1; 212 } else { 213 return 0; 214 } 215 } GetTid(const ReportItem & a,size_t len,const std::string & format)216 static const std::string GetTid(const ReportItem &a, size_t len, const std::string &format) 217 { 218 return StringPrintf(format.c_str(), len, a.tid_); 219 } 220 221 // Comm CompareComm(const ReportItem & a,const ReportItem & b)222 static int CompareComm(const ReportItem &a, const ReportItem &b) 223 { 224 int result = a.comm_.compare(b.comm_); 225 return result; 226 } GetComm(const ReportItem & a,size_t len,const std::string & format)227 static const std::string GetComm(const ReportItem &a, size_t len, const std::string &format) 228 { 229 return StringPrintf(format.c_str(), len, a.comm_.data()); 230 } 231 232 // Func CompareFunc(const ReportItem & a,const ReportItem & b)233 static int CompareFunc(const ReportItem &a, const ReportItem &b) 234 { 235 return a.func_.compare(b.func_); 236 } GetFunc(const ReportItem & a,size_t len,const std::string & format)237 static const std::string GetFunc(const ReportItem &a, size_t len, const std::string &format) 238 { 239 return StringPrintf(format.c_str(), len, a.func_.data()); 240 } 241 242 // Dso CompareDso(const ReportItem & a,const ReportItem & b)243 static int CompareDso(const ReportItem &a, const ReportItem &b) 244 { 245 return a.dso_.compare(b.dso_); 246 } GetDso(const ReportItem & a,size_t len,const std::string & format)247 static const std::string GetDso(const ReportItem &a, size_t len, const std::string &format) 248 { 249 return StringPrintf(format.c_str(), len, a.dso_.data()); 250 } 251 252 // fromDso CompareFromDso(const ReportItem & a,const ReportItem & b)253 static int CompareFromDso(const ReportItem &a, const ReportItem &b) 254 { 255 return a.fromDso_.compare(b.fromDso_); 256 } GetFromDso(const ReportItem & a,size_t len,const std::string & format)257 static const std::string GetFromDso(const ReportItem &a, size_t len, const std::string &format) 258 { 259 return StringPrintf(format.c_str(), len, a.fromDso_.data()); 260 } 261 262 // fromFunc CompareFromFunc(const ReportItem & a,const ReportItem & b)263 static int CompareFromFunc(const ReportItem &a, const ReportItem &b) 264 { 265 return a.fromFunc_.compare(b.fromFunc_); 266 } GetFromFunc(const ReportItem & a,size_t len,const std::string & format)267 static const std::string GetFromFunc(const ReportItem &a, size_t len, const std::string &format) 268 { 269 return StringPrintf(format.c_str(), len, a.fromFunc_.data()); 270 } 271 272 private: Same(const ReportItem & b)273 bool Same(const ReportItem &b) const 274 { 275 return (comm_ == b.comm_) && (pid_ == b.pid_) && (tid_ == b.tid_) && (func_ == b.func_) && 276 (dso_ == b.dso_) && (vaddr_ == b.vaddr_); 277 } 278 }; 279 280 using ReportKeyCompareFunction = int(const ReportItem &, const ReportItem &); 281 using ReportKeyGetFunction = const std::string(const ReportItem &, size_t, const std::string &); 282 283 constexpr const int MAX_FILED_LEN = 20; 284 constexpr const int CALLSTACK_INDENT = 4; 285 struct ReportKey { 286 const std::string keyName_; 287 const std::string valueFormat_; 288 size_t maxLen_ = 0u; 289 std::string maxValue_; 290 ReportKeyCompareFunction &compareFunction_; 291 ReportKeyGetFunction &GetFunction_; 292 const std::vector<std::string> &displayFilter_; 293 ReportKeyReportKey294 ReportKey(const std::string keyName, ReportKeyCompareFunction &compareFunction, 295 ReportKeyGetFunction &GetFunction, const std::string valueFormat, 296 const std::vector<std::string> &displayFilter) 297 : keyName_(keyName), 298 valueFormat_(valueFormat), 299 compareFunction_(compareFunction), 300 GetFunction_(GetFunction), 301 displayFilter_(displayFilter) 302 { 303 maxLen_ = keyName.size(); 304 } 305 UpdateValueMaxLenReportKey306 void UpdateValueMaxLen(const std::string &value) 307 { 308 size_t newMaxLen = std::max(maxLen_, value.size()); 309 if (maxLen_ < newMaxLen) { 310 maxValue_ = value; 311 maxLen_ = newMaxLen; 312 } 313 } 314 UpdateValueMaxLenReportKey315 void UpdateValueMaxLen(size_t value) 316 { 317 size_t newMaxLen = std::max(maxLen_, std::to_string(value).size()); 318 if (maxLen_ < newMaxLen) { 319 maxValue_ = std::to_string(value); 320 maxLen_ = newMaxLen; 321 } 322 } 323 GetValueReportKey324 std::string GetValue(const ReportItem &i) 325 { 326 return GetFunction_(i, maxLen_, valueFormat_); 327 } 328 ShouldDisplayReportKey329 bool ShouldDisplay(const ReportItem &i) 330 { 331 if (displayFilter_.size() == 0) { 332 return true; 333 } else { 334 std::string value = GetFunction_(i, 0, valueFormat_); 335 auto it = find(displayFilter_.begin(), displayFilter_.end(), value); 336 if (it == displayFilter_.end()) { 337 HLOGV(" not found '%s' in %s", value.c_str(), 338 VectorToString(displayFilter_).c_str()); 339 } 340 return (it != displayFilter_.end()); 341 } 342 } 343 }; 344 345 using ReportItems = std::vector<ReportItem>; 346 using ReportItemsIt = ReportItems::iterator; 347 using ReportItemsConstIt = ReportItems::const_iterator; 348 349 struct ReportOption { 350 float heatLimit_ = 0.0f; 351 float callStackHeatLimit_ = 0.0f; 352 353 // display filter 354 std::vector<std::string> displayComms_ {}; 355 std::vector<std::string> displayPids_ {}; 356 std::vector<std::string> displayTids_ {}; 357 std::vector<std::string> displayDsos_ {}; 358 std::vector<std::string> displayFromDsos_ {}; 359 std::vector<std::string> displayFuncs_ {}; 360 std::vector<std::string> displayFromFuncs_ {}; 361 std::vector<std::string> displayDummy_ {}; 362 363 std::vector<std::string> sortKeys_ = {"comm", "pid", "tid", "dso", "func"}; 364 365 bool debug_ = false; 366 bool hideCount_ = false; 367 }; 368 369 class Report { 370 public: Report()371 Report() : option_(defaultOption_), virtualRuntime_(false) 372 { 373 // works for ut test 374 } Report(ReportOption & option)375 Report(ReportOption &option) : option_(option), virtualRuntime_(false) {} 376 bool MultiLevelSame(const ReportItem &a, const ReportItem &b); 377 void AdjustReportItems(); 378 void AddReportItem(const PerfRecordSample &sample, bool includeCallStack); 379 void AddReportItemBranch(const PerfRecordSample &sample); 380 void OutputStd(FILE *output); 381 void OutputStdDiff(FILE *output, Report &other); 382 383 ReportOption &option_; 384 385 VirtualRuntime virtualRuntime_; 386 387 std::map<std::string, ReportKey> reportKeyMap_ = { 388 { 389 "count", 390 { 391 "count", 392 ReportItem::CompareEventCount, 393 ReportItem::GetEventCount, 394 "%*" PRIu64 "", 395 option_.displayDummy_, 396 }, 397 }, 398 { 399 "comm", 400 { 401 "comm", 402 ReportItem::CompareComm, 403 ReportItem::GetComm, 404 "%-*s", 405 option_.displayComms_, 406 }, 407 }, 408 { 409 "pid", 410 { 411 "pid", 412 ReportItem::ComparePid, 413 ReportItem::GetPid, 414 "%*d", 415 option_.displayPids_, 416 }, 417 }, 418 { 419 "tid", 420 { 421 "tid", 422 ReportItem::CompareTid, 423 ReportItem::GetTid, 424 "%*d", 425 option_.displayTids_, 426 }, 427 }, 428 { 429 "dso", 430 { 431 "dso", 432 ReportItem::CompareDso, 433 ReportItem::GetDso, 434 "%-*s", 435 option_.displayDsos_, 436 }, 437 }, 438 { 439 "from_dso", 440 { 441 "from_dso", 442 ReportItem::CompareFromDso, 443 ReportItem::GetFromDso, 444 "%-*s", 445 option_.displayFromDsos_, 446 }, 447 }, 448 { 449 "func", 450 { 451 "func", 452 ReportItem::CompareFunc, 453 ReportItem::GetFunc, 454 "%-*s", 455 option_.displayFuncs_, 456 }, 457 }, 458 { 459 "from_func", 460 { 461 "from_func", 462 ReportItem::CompareFromFunc, 463 ReportItem::GetFromFunc, 464 "%-*s", 465 option_.displayFromFuncs_, 466 }, 467 }, 468 }; 469 struct ReportEventConfigItem { 470 ReportEventConfigItem(const ReportEventConfigItem &) = delete; 471 ReportEventConfigItem &operator=(const ReportEventConfigItem &) = delete; 472 ReportEventConfigItem(ReportEventConfigItem &&) = default; 473 ReportEventConfigItem &operator=(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 size_t index = GetConfigIndex(id); 509 HIPERF_ASSERT(index < configs_.size(), "unable found config index %zu\n", index); 510 return configs_[index].eventName_; 511 } GetConfigIndex(uint64_t id)512 size_t GetConfigIndex(uint64_t id) 513 { 514 HIPERF_ASSERT(configIdIndexMaps_.find(id) != configIdIndexMaps_.end(), "unable found id %" PRIx64 "\n", id); 515 return configIdIndexMaps_.at(id); 516 } 517 518 private: 519 FILE *output_ = nullptr; 520 const std::string TEXT_RED = "\x1b[31m"; 521 const std::string TEXT_GREEN = "\x1b[32m"; 522 const std::string TEXT_RESET = "\033[0m"; 523 const unsigned int ConsoleDefaultWidth = 80; 524 525 // sometime caller don't give the option 526 ReportOption defaultOption_; 527 528 std::vector<std::string> displayKeyNames_; 529 530 // use virtual only for gmock test 531 bool MultiLevelSorting(const ReportItem &a, const ReportItem &b); 532 bool MultiLevelSameAndUpdateCount(ReportItem &l, ReportItem &r); 533 void MergeCallFrameCount(ReportItem &leftItem, ReportItem &rightItem); 534 virtual int MultiLevelCompare(const ReportItem &a, const ReportItem &b); 535 536 void StatisticsRecords(); 537 void FilterDisplayRecords(); 538 void UpdateReportItemsAfterAdjust(); 539 540 // std out 541 unsigned int consoleWidth_ = 0; 542 void PrepareConsole(); 543 544 void OutputStdStatistics(ReportEventConfigItem &config); 545 546 void OutputStdHead(ReportEventConfigItem &config, bool diffMode = false); 547 548 void OutputStdContent(ReportEventConfigItem &config); 549 void OutputStdContentDiff(ReportEventConfigItem &left, ReportEventConfigItem &right); 550 551 void OutputStdContentItem(const ReportItem &reportItem); 552 553 void OutputStdCallFrames(int indent, const ReportItemCallFrame &callFrame, uint64_t totalEventCount); 554 bool OutputStdCallFrame(int indent, const std::string_view &funcName, uint64_t eventCount, 555 uint64_t totalEventCount); 556 void OutputStdItemHeating(float heat, float heat2); 557 }; 558 } // namespace HiPerf 559 } // namespace Developtools 560 } // namespace OHOS 561 #endif // REPORT_H 562