diff --git a/bolt/include/bolt/Profile/Heatmap.h b/bolt/include/bolt/Profile/Heatmap.h --- a/bolt/include/bolt/Profile/Heatmap.h +++ b/bolt/include/bolt/Profile/Heatmap.h @@ -12,12 +12,20 @@ #include "llvm/ADT/StringRef.h" #include #include +#include namespace llvm { class raw_ostream; namespace bolt { +/// Struct representing a section name and its address range in the binary. +struct SectionNameAndRange { + StringRef Name; + uint64_t BeginAddress; + uint64_t EndAddress; +}; + class Heatmap { /// Number of bytes per entry in the heat map. size_t BucketSize; @@ -34,11 +42,15 @@ /// Map buckets to the number of samples. std::map Map; + /// Map section names to their address range. + const std::vector TextSections; + public: explicit Heatmap(uint64_t BucketSize = 4096, uint64_t MinAddress = 0, - uint64_t MaxAddress = std::numeric_limits::max()) - : BucketSize(BucketSize), MinAddress(MinAddress), - MaxAddress(MaxAddress){}; + uint64_t MaxAddress = std::numeric_limits::max(), + std::vector TextSections = {}) + : BucketSize(BucketSize), MinAddress(MinAddress), MaxAddress(MaxAddress), + TextSections(TextSections) {} inline bool ignoreAddress(uint64_t Address) const { return (Address > MaxAddress) || (Address < MinAddress); @@ -65,6 +77,10 @@ void printCDF(raw_ostream &OS) const; + void printSectionHotness(StringRef Filename) const; + + void printSectionHotness(raw_ostream &OS) const; + size_t size() const { return Map.size(); } }; diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -116,6 +116,22 @@ const char TimerGroupName[] = "aggregator"; const char TimerGroupDesc[] = "Aggregator"; +std::vector getTextSections(const BinaryContext *BC) { + std::vector sections; + for (BinarySection &Section : BC->sections()) { + if (!Section.isText()) + continue; + if (Section.getSize() == 0) + continue; + sections.push_back( + {Section.getName(), Section.getAddress(), Section.getEndAddress()}); + } + std::sort(sections.begin(), sections.end(), + [](const SectionNameAndRange &A, const SectionNameAndRange &B) { + return A.BeginAddress < B.BeginAddress; + }); + return sections; +} } constexpr uint64_t DataAggregator::KernelBaseAddr; @@ -1292,7 +1308,7 @@ opts::HeatmapMinAddress = KernelBaseAddr; } Heatmap HM(opts::HeatmapBlock, opts::HeatmapMinAddress, - opts::HeatmapMaxAddress); + opts::HeatmapMaxAddress, getTextSections(BC)); uint64_t NumTotalSamples = 0; if (opts::BasicAggregation) { @@ -1374,6 +1390,10 @@ HM.printCDF(opts::OutputFilename); else HM.printCDF(opts::OutputFilename + ".csv"); + if (opts::OutputFilename == "-") + HM.printSectionHotness(opts::OutputFilename); + else + HM.printSectionHotness(opts::OutputFilename + "-section-hotness.csv"); return std::error_code(); } diff --git a/bolt/lib/Profile/Heatmap.cpp b/bolt/lib/Profile/Heatmap.cpp --- a/bolt/lib/Profile/Heatmap.cpp +++ b/bolt/lib/Profile/Heatmap.cpp @@ -8,6 +8,7 @@ #include "bolt/Profile/Heatmap.h" #include "bolt/Utils/CommandLineOpts.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -251,5 +252,64 @@ Counts.clear(); } +void Heatmap::printSectionHotness(StringRef FileName) const { + std::error_code EC; + raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::OF_None); + if (EC) { + errs() << "error opening output file: " << EC.message() << '\n'; + exit(1); + } + printSectionHotness(OS); +} + +void Heatmap::printSectionHotness(raw_ostream &OS) const { + uint64_t NumTotalCounts = 0; + StringMap SectionHotness; + unsigned TextSectionIndex = 0; + + if (TextSections.empty()) + return; + + uint64_t UnmappedHotness = 0; + auto RecordUnmappedBucket = [&](uint64_t Address, uint64_t Frequency) { + errs() << "Couldn't map the address bucket [0x" << Twine::utohexstr(Address) + << ", 0x" << Twine::utohexstr(Address + BucketSize) + << "] containing " << Frequency + << " samples to a text section in the binary."; + UnmappedHotness += Frequency; + }; + + for (const std::pair &KV : Map) { + NumTotalCounts += KV.second; + // We map an address bucket to the first section (lowest address) + // overlapping with that bucket. + auto Address = KV.first * BucketSize; + while (TextSectionIndex < TextSections.size() && + Address >= TextSections[TextSectionIndex].EndAddress) + TextSectionIndex++; + if (TextSectionIndex >= TextSections.size() || + Address + BucketSize < TextSections[TextSectionIndex].BeginAddress) { + RecordUnmappedBucket(Address, KV.second); + continue; + } + SectionHotness[TextSections[TextSectionIndex].Name] += KV.second; + } + + assert(NumTotalCounts > 0 && + "total number of heatmap buckets should be greater than 0"); + + OS << "Section Name, Begin Address, End Address, Percentage Hotness\n"; + for (auto &TextSection : TextSections) { + OS << TextSection.Name << ", 0x" + << Twine::utohexstr(TextSection.BeginAddress) << ", 0x" + << Twine::utohexstr(TextSection.EndAddress) << ", " + << format("%.4f", + 100.0 * SectionHotness[TextSection.Name] / NumTotalCounts) + << "\n"; + } + if (UnmappedHotness > 0) + OS << "[unmapped], 0x0, 0x0, " + << format("%.4f", 100.0 * UnmappedHotness / NumTotalCounts) << "\n"; +} } // namespace bolt } // namespace llvm