diff --git a/llvm/test/tools/llvm-profdata/Inputs/sample-hot-func-list.proftext b/llvm/test/tools/llvm-profdata/Inputs/sample-hot-func-list.proftext new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/Inputs/sample-hot-func-list.proftext @@ -0,0 +1,41 @@ +_Z3bari:20301:1437 + 1: 1437 +_Z3fooi:7711:610 + 1: 610 +main:184019:0 + 4: 534 + 4.2: 534 + 5: 1075 + 5.1: 1075 + 6: 2080 + 7: 534 + 9: 2300 _Z3bari:1471 _Z3fooi:631 + 10: inline1:1000 + 1: 1000 + 10: inline2:2000 + 1: 2000 +_Z3bazi:20305:1000 + 1: 1000 +Func1:1523:169 + 1: 169 + 7: 563 +Func2:17043:1594 + 1: 1594 + 3: 1594 + 6: 2009 Func1:150 Func3:1789 + 13: 3105 + 17: 3105 + 19: 1594 +Func3:97401:3035 + 1: 3035 + 5: 7344 + 9: 10640 + 11: 10640 + 15: 3035 +Func4:465:210 + 1: 210 +Func5:6948:470 + 1: 470 + 3: 3507 +Func6:310:102 + 1: 102 diff --git a/llvm/test/tools/llvm-profdata/sample-hot-func-list.test b/llvm/test/tools/llvm-profdata/sample-hot-func-list.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/sample-hot-func-list.test @@ -0,0 +1,12 @@ +; RUN: llvm-profdata show --sample --hot-func-list %S/Inputs/sample-hot-func-list.proftext | FileCheck %s +; CHECK: 8 out of 10 functions with profile (80.00%) are considered hot functions (max sample >= 470). +; CHECK-NEXT: 355251 out of 356026 samples (99.78%) are from hot functions. +; CHECK-NEXT: Total sample (%) Max sample Entry sample Function name +; CHECK-NEXT: 184019 (51.69%) 2300 534 main +; CHECK-NEXT: 97401 (27.36%) 10640 3035 Func3 +; CHECK-NEXT: 20305 (5.70%) 1000 1000 _Z3bazi +; CHECK-NEXT: 20301 (5.70%) 1437 1437 _Z3bari +; CHECK-NEXT: 17043 (4.79%) 3105 1594 Func2 +; CHECK-NEXT: 7711 (2.17%) 610 610 _Z3fooi +; CHECK-NEXT: 6948 (1.95%) 3507 470 Func5 +; CHECK-NEXT: 1523 (0.43%) 563 169 Func1 diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -23,11 +23,12 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" -#include "llvm/Support/Threading.h" #include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include @@ -1026,11 +1027,111 @@ } } +// Return the maximum of sample counts in a function body including functions +// inlined in it. +static uint64_t +getInlineeMaxCount(const sampleprof::FunctionSamples &FuncProf) { + using namespace sampleprof; + + uint64_t MaxCount = 0; + for (const auto &L : FuncProf.getBodySamples()) { + MaxCount = std::max(MaxCount, L.second.getSamples()); + } + + for (const auto &C : FuncProf.getCallsiteSamples()) { + for (const auto &F : C.second) { + MaxCount = std::max(MaxCount, getInlineeMaxCount(F.second)); + } + } + + return MaxCount; +} + +static int +showHotFunctionList(const StringMap &Profiles, + ProfileSummary &PS, raw_fd_ostream &OS) { + using namespace sampleprof; + + auto &SummaryVector = PS.getDetailedSummary(); + uint64_t MinCountThreshold = 0; + for (const auto &SummaryEntry : SummaryVector) { + if (SummaryEntry.Cutoff == 990000) { + MinCountThreshold = SummaryEntry.MinCount; + break; + } + } + assert(MinCountThreshold != 0); + + // Traverse all functions in the profile and keep only hot functions. + // The following loop also calculates the sum of total samples of all + // functions. + std::multimap, + std::greater> + HotFunc; + uint64_t ProfileTotalSample = 0; + uint64_t HotFuncSample = 0; + uint64_t HotFuncCount = 0; + uint64_t MaxCount = 0; + for (const auto &I : Profiles) { + auto &FuncProf = I.second; + ProfileTotalSample += FuncProf.getTotalSamples(); + + MaxCount = getInlineeMaxCount(FuncProf); + + // MinCountThreshold is a block/line threshold computed for a given cutoff. + // We intentionally compare the maximum sample count in a function with this + // threshold to get an approximate threshold for hot functions. + if (MaxCount >= MinCountThreshold) { + HotFunc.emplace(FuncProf.getTotalSamples(), + std::make_pair(&(I.second), MaxCount)); + HotFuncSample += FuncProf.getTotalSamples(); + HotFuncCount++; + } + } + + const int MaxSampleCol = 24; // column offset for output formatting + const int EntrySampleCol = 42; + const int FuncNameCol = 58; + formatted_raw_ostream FOS(OS); + FOS << HotFuncCount << " out of " << Profiles.size() + << " functions with profile (" + << format("%.2f%%", (((double)HotFuncCount) / Profiles.size() * 100)) + << ") are considered hot functions (max sample >= " << MinCountThreshold + << ").\n"; + FOS << HotFuncSample << " out of " << ProfileTotalSample << " samples (" + << format("%.2f%%", (((double)HotFuncSample) / ProfileTotalSample * 100)) + << ") are from hot functions.\n"; + FOS << "Total sample (%)"; + FOS.PadToColumn(MaxSampleCol); + FOS << "Max sample"; + FOS.PadToColumn(EntrySampleCol); + FOS << "Entry sample"; + FOS.PadToColumn(FuncNameCol); + FOS << "Function name\n"; + + for (const auto &FuncPair : HotFunc) { + auto FuncPtr = FuncPair.second.first; + FOS << FuncPtr->getTotalSamples() << " (" + << format("%.2f%%", (((double)FuncPtr->getTotalSamples()) / + ProfileTotalSample * 100)) + << ")"; + FOS.PadToColumn(MaxSampleCol); + FOS << FuncPair.second.second; + FOS.PadToColumn(EntrySampleCol); + FOS << FuncPtr->getEntrySamples(); + FOS.PadToColumn(FuncNameCol); + FOS << FuncPtr->getFuncName() << "\n"; + } + + return 0; +} + static int showSampleProfile(const std::string &Filename, bool ShowCounts, bool ShowAllFunctions, bool ShowDetailedSummary, const std::string &ShowFunction, bool ShowProfileSymbolList, - bool ShowSectionInfoOnly, raw_fd_ostream &OS) { + bool ShowSectionInfoOnly, bool ShowHotFuncList, + raw_fd_ostream &OS) { using namespace sampleprof; LLVMContext Context; auto ReaderOrErr = SampleProfileReader::create(Filename, Context); @@ -1064,6 +1165,9 @@ PS.printDetailedSummary(OS); } + if (ShowHotFuncList) + showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS); + return 0; } @@ -1090,6 +1194,9 @@ cl::desc( "Cutoff percentages (times 10000) for generating detailed summary"), cl::value_desc("800000,901000,999999")); + cl::opt ShowHotFuncList( + "hot-func-list", cl::init(false), + cl::desc("Show profile summary of a list of hot functions")); cl::opt ShowAllFunctions("all-functions", cl::init(false), cl::desc("Details for every function")); cl::opt ShowCS("showcs", cl::init(false), @@ -1153,7 +1260,8 @@ else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ShowDetailedSummary, ShowFunction, - ShowProfileSymbolList, ShowSectionInfoOnly, OS); + ShowProfileSymbolList, ShowSectionInfoOnly, + ShowHotFuncList, OS); } int main(int argc, const char *argv[]) {