From 1e1ef44f102bdfb44ddad14f48bcf10258c6610b Mon Sep 17 00:00:00 2001 From: Amir Ayupov Date: Thu, 15 May 2025 15:12:47 -0700 Subject: [PATCH 1/6] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?= =?UTF-8?q?itial=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.4 --- bolt/include/bolt/Profile/Heatmap.h | 3 +++ bolt/lib/Profile/DataAggregator.cpp | 15 +++++++++++++++ bolt/lib/Profile/Heatmap.cpp | 15 ++++++++++++++- bolt/test/X86/heatmap-preagg.test | 14 +++++++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/bolt/include/bolt/Profile/Heatmap.h b/bolt/include/bolt/Profile/Heatmap.h index 9813e7fed486d..bf3d1c91c0aa5 100644 --- a/bolt/include/bolt/Profile/Heatmap.h +++ b/bolt/include/bolt/Profile/Heatmap.h @@ -85,6 +85,9 @@ class Heatmap { void printSectionHotness(raw_ostream &OS) const; size_t size() const { return Map.size(); } + + /// Increase bucket size to \p TargetSize, recomputing the heatmap. + bool resizeBucket(uint64_t TargetSize); }; } // namespace bolt diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp index 6beb60741406e..aa681e633c0d8 100644 --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -68,6 +68,12 @@ FilterPID("pid", cl::Optional, cl::cat(AggregatorCategory)); +static cl::list + HeatmapZoomOut("heatmap-zoom-out", cl::CommaSeparated, + cl::desc("print secondary heatmaps with given bucket sizes"), + cl::value_desc("bucket_size"), cl::Optional, + cl::cat(HeatmapCategory)); + static cl::opt IgnoreBuildID("ignore-build-id", cl::desc("continue even if build-ids in input binary and perf.data mismatch"), @@ -1365,6 +1371,15 @@ std::error_code DataAggregator::printLBRHeatMap() { HM.printCDF(opts::HeatmapOutput + ".csv"); HM.printSectionHotness(opts::HeatmapOutput + "-section-hotness.csv"); } + // Provide coarse-grained heatmap if requested via --heatmap-zoom-out + for (const uint64_t NewBucketSize : opts::HeatmapZoomOut) { + if (!HM.resizeBucket(NewBucketSize)) + break; + if (opts::HeatmapOutput == "-") + HM.print(opts::HeatmapOutput); + else + HM.print(formatv("{0}-{1}", opts::HeatmapOutput, NewBucketSize).str()); + } return std::error_code(); } diff --git a/bolt/lib/Profile/Heatmap.cpp b/bolt/lib/Profile/Heatmap.cpp index c66c2e5487613..4aaf6dc344a85 100644 --- a/bolt/lib/Profile/Heatmap.cpp +++ b/bolt/lib/Profile/Heatmap.cpp @@ -81,7 +81,7 @@ void Heatmap::print(raw_ostream &OS) const { // the Address. auto startLine = [&](uint64_t Address, bool Empty = false) { changeColor(DefaultColor); - const uint64_t LineAddress = Address / BytesPerLine * BytesPerLine; + const uint64_t LineAddress = alignTo(Address, BytesPerLine); if (MaxAddress > 0xffffffff) OS << format("0x%016" PRIx64 ": ", LineAddress); @@ -364,5 +364,18 @@ void Heatmap::printSectionHotness(raw_ostream &OS) const { OS << formatv("[unmapped], 0x0, 0x0, {0:f4}, 0, 0\n", 100.0 * UnmappedHotness / NumTotalCounts); } + +bool Heatmap::resizeBucket(uint64_t TargetSize) { + if (TargetSize <= BucketSize) + return false; + std::map NewMap; + for (const auto [Bucket, Count] : Map) { + const uint64_t Address = Bucket * BucketSize; + NewMap[Address / TargetSize] += Count; + } + Map = NewMap; + BucketSize = TargetSize; + return true; +} } // namespace bolt } // namespace llvm diff --git a/bolt/test/X86/heatmap-preagg.test b/bolt/test/X86/heatmap-preagg.test index 306e74800a353..9539269ff0d47 100644 --- a/bolt/test/X86/heatmap-preagg.test +++ b/bolt/test/X86/heatmap-preagg.test @@ -3,8 +3,11 @@ RUN: yaml2obj %p/Inputs/blarge_new.yaml &> %t.exe ## Non-BOLTed input binary RUN: llvm-bolt-heatmap %t.exe -o %t --pa -p %p/Inputs/blarge_new.preagg.txt \ -RUN: 2>&1 | FileCheck --check-prefix CHECK-HEATMAP %s +RUN: --heatmap-zoom-out 128,1024 2>&1 | FileCheck --check-prefix CHECK-HEATMAP %s RUN: FileCheck %s --check-prefix CHECK-SEC-HOT --input-file %t-section-hotness.csv +RUN: FileCheck %s --check-prefix CHECK-HM-64 --input-file %t +RUN: FileCheck %s --check-prefix CHECK-HM-128 --input-file %t-128 +RUN: FileCheck %s --check-prefix CHECK-HM-1024 --input-file %t-1024 ## BOLTed input binary RUN: llvm-bolt %t.exe -o %t.out --pa -p %p/Inputs/blarge_new.preagg.txt \ @@ -24,6 +27,15 @@ CHECK-SEC-HOT-NEXT: .plt, 0x401020, 0x4010b0, 4.7583, 66.6667, 0.0317 CHECK-SEC-HOT-NEXT: .text, 0x4010b0, 0x401c25, 78.3872, 85.1064, 0.6671 CHECK-SEC-HOT-NEXT: .fini, 0x401c28, 0x401c35, 0.0000, 0.0000, 0.0000 +# Only check start addresses – can't check colors, and FileCheck doesn't strip +# color codes by default. Reference output: +# HM-64: 0x00404000: ABBcccccccccccccccCCCCCCCCCccccCCCCCCCCcc....CC +# HM-128: 0x00408000: ABCCCCCCCCCCCCCCCCCCc.CC +# HM-1024: 0x00440000: ACC +CHECK-HM-64: 0x00404000: +CHECK-HM-128: 0x00408000: +CHECK-HM-1024: 0x00440000: + CHECK-HEATMAP-BAT: PERF2BOLT: read 79 aggregated LBR entries CHECK-HEATMAP-BAT: HEATMAP: invalid traces: 2 From 0bc316a2bcab62c0d06177f92b098b78a4690e14 Mon Sep 17 00:00:00 2001 From: Amir Ayupov Date: Thu, 15 May 2025 15:14:06 -0700 Subject: [PATCH 2/6] drop alignTo Created using spr 1.3.4 --- bolt/lib/Profile/Heatmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bolt/lib/Profile/Heatmap.cpp b/bolt/lib/Profile/Heatmap.cpp index 4aaf6dc344a85..a73468ff0479c 100644 --- a/bolt/lib/Profile/Heatmap.cpp +++ b/bolt/lib/Profile/Heatmap.cpp @@ -81,7 +81,7 @@ void Heatmap::print(raw_ostream &OS) const { // the Address. auto startLine = [&](uint64_t Address, bool Empty = false) { changeColor(DefaultColor); - const uint64_t LineAddress = alignTo(Address, BytesPerLine); + const uint64_t LineAddress = Address / BytesPerLine * BytesPerLine; if (MaxAddress > 0xffffffff) OS << format("0x%016" PRIx64 ": ", LineAddress); From 5ab0498f44efe36df080daea598438a68e5763a6 Mon Sep 17 00:00:00 2001 From: Amir Ayupov Date: Fri, 16 May 2025 11:12:20 -0700 Subject: [PATCH 3/6] suggested values Created using spr 1.3.4 --- bolt/docs/Heatmaps.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bolt/docs/Heatmaps.md b/bolt/docs/Heatmaps.md index 92e00bc9dcb2d..9045ef4fbee4f 100644 --- a/bolt/docs/Heatmaps.md +++ b/bolt/docs/Heatmaps.md @@ -90,9 +90,12 @@ Some useful options are: ``` -block-size= - heatmap bucket size in bytes (default 64) --line-size= - number of entries per line (default 256) +-line-size= - number of entries per line (default 256). + Use 128 if the heatmap doesn't fit screen horizontally. -max-address= - maximum address considered valid for heatmap (default 4GB) -print-mappings - print mappings in the legend, between characters/blocks and text sections (default false) -heatmap-zoom-out=,... - print zoomed out heatmaps with given block sizes, - must be multiples of block-size in ascending order + must be multiples of block-size in ascending order. + Suggested values: 4096 (default page size), 16384 (16k page), + 1048576 (1MB for XL workloads). ``` From 9f665208e441f8d5b3460bbe3912ebbfd09cb8d2 Mon Sep 17 00:00:00 2001 From: Amir Ayupov Date: Fri, 23 May 2025 15:36:46 -0700 Subject: [PATCH 4/6] clang-format Created using spr 1.3.4 --- bolt/include/bolt/Utils/CommandLineOpts.h | 2 +- bolt/lib/Utils/CommandLineOpts.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h index 1e6a5be10a434..cbfa75299710c 100644 --- a/bolt/include/bolt/Utils/CommandLineOpts.h +++ b/bolt/include/bolt/Utils/CommandLineOpts.h @@ -24,7 +24,7 @@ enum HeatmapModeKind { }; struct HeatmapBlockSpec { - unsigned Initial{0}; // Initial block size in bytes. + unsigned Initial{0}; // Initial block size in bytes. llvm::SmallVector Scales; // Pow2 zoom factors applied cumulatively. }; diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp index 83ca5a2f9e2b2..e78355e0b58b7 100644 --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -107,8 +107,7 @@ bool HeatmapBlockSpecParser::parse(cl::Option &O, StringRef ArgName, StringRef Arg, HeatmapBlockSpec &Val) { auto [InitialPart, ScalesPart] = Arg.split(':'); if (InitialPart.getAsInteger(10, Val.Initial)) { - O.error("'" + Arg + - "' value invalid for block-size initial_size argument"); + O.error("'" + Arg + "' value invalid for block-size initial_size argument"); return true; } // Scales part is optional. From 1d54ac167c6dc9afe354c2359759749a4c4924d0 Mon Sep 17 00:00:00 2001 From: Amir Ayupov Date: Fri, 30 May 2025 11:37:29 -0700 Subject: [PATCH 5/6] usage message Created using spr 1.3.4 --- bolt/tools/heatmap/heatmap.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/bolt/tools/heatmap/heatmap.cpp b/bolt/tools/heatmap/heatmap.cpp index b2dd586a4c6f7..43167558b6758 100644 --- a/bolt/tools/heatmap/heatmap.cpp +++ b/bolt/tools/heatmap/heatmap.cpp @@ -59,7 +59,26 @@ static std::string GetExecutablePath(const char *Argv0) { int main(int argc, char **argv) { cl::HideUnrelatedOptions(ArrayRef(opts::HeatmapCategories)); - cl::ParseCommandLineOptions(argc, argv, ""); + cl::ParseCommandLineOptions( + argc, argv, + " BOLT Code Heatmap tool\n\n" + " Produces code heatmaps using sampled profile\n\n" + + " Inputs:\n" + " - Binary (supports BOLT-optimized binaries),\n" + " - Sampled profile collected from the binary:\n" + " - perf data or pre-aggregated profile data (instrumentation profile " + "not supported)\n" + " - perf data can have basic (IP) or branch-stack (LBR) samples\n\n" + + " Outputs:\n" + " - Heatmaps: colored ASCII (requires a color-capable terminal or a" + " conversion tool like `aha`)\n" + " Multiple heatmaps are produced by default with different " + "granularities (set by `block-size` option)\n" + " - Section hotness: per-section samples% and utilization%\n" + " - Cumulative distribution: working set size corresponding to a " + "given percentile of samples\n"); if (opts::PerfData.empty()) { errs() << ToolName << ": expected -perfdata= option.\n"; From 4b28ffeb40c0d74c7e60f8c16870c6a1b35bce01 Mon Sep 17 00:00:00 2001 From: Amir Ayupov Date: Fri, 30 May 2025 15:19:09 -0700 Subject: [PATCH 6/6] enforce block sizes to be multiples Created using spr 1.3.4 --- bolt/lib/Utils/CommandLineOpts.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp index 375263ad30482..5635da476451d 100644 --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -124,20 +124,25 @@ bool HeatmapBlockSpecParser::parse(cl::Option &O, StringRef ArgName, SmallVector Sizes; Arg.split(Sizes, ','); + unsigned PreviousSize = 0; for (StringRef Size : Sizes) { + StringRef OrigSize = Size; unsigned &SizeVal = Val.emplace_back(0); - if (!Size.consumeInteger(10, SizeVal)) { - if (std::optional ShiftAmt = parseSuffix(Size)) { - SizeVal <<= *ShiftAmt; - continue; - } + if (Size.consumeInteger(10, SizeVal)) { + O.error("'" + OrigSize + "' value can't be parsed as an integer"); + return true; } - O.error("'" + Size + "' value invalid for size argument"); - return true; - } - if (!llvm::is_sorted(Val)) { - O.error("'" + Arg + "' value must be sorted"); - return true; + if (std::optional ShiftAmt = parseSuffix(Size)) { + SizeVal <<= *ShiftAmt; + } else { + O.error("'" + Size + "' value can't be parsed as a suffix"); + return true; + } + if (SizeVal <= PreviousSize || (PreviousSize && SizeVal % PreviousSize)) { + O.error("'" + OrigSize + "' must be a multiple of previous value"); + return true; + } + PreviousSize = SizeVal; } return false; }