diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -1000,6 +1000,10 @@ /// Print all sections. void printSections(raw_ostream &OS) const; + /// Return a map from section names to the address range. + StringMap> + getSectionNameToAddressRangeMap() const; + /// Return largest section containing the given \p Address. These /// functions only work for allocatable sections, i.e. ones with non-zero /// addresses. 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 @@ -9,6 +9,8 @@ #ifndef BOLT_PROFILE_HEATMAP_H #define BOLT_PROFILE_HEATMAP_H +#include "bolt/Core/BinaryContext.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include #include @@ -18,6 +20,13 @@ 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,14 +43,21 @@ /// 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); + return (Address > MaxAddress) || (Address < MinAddress) || + (!TextSections.empty() && + (Address > TextSections.back().EndAddress || + Address < TextSections.front().BeginAddress)); } /// Register a single sample at \p Address. @@ -65,6 +81,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/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp --- a/bolt/lib/Core/BinaryContext.cpp +++ b/bolt/lib/Core/BinaryContext.cpp @@ -1768,6 +1768,16 @@ return *Section; } +StringMap> +BinaryContext::getSectionNameToAddressRangeMap() const { + StringMap> M; + for (const auto &elem : AddressToSection) { + M[elem.second->getName()] = + std::make_pair(elem.second->getAddress(), elem.second->getEndAddress()); + } + return M; +} + BinarySection &BinaryContext::registerSection(SectionRef Section) { return registerSection(new BinarySection(*this, Section)); } 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 @@ -111,6 +111,24 @@ } // namespace opts +static 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; +} + namespace { const char TimerGroupName[] = "aggregator"; @@ -1292,7 +1310,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 +1392,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 @@ -251,5 +251,53 @@ Counts.clear(); } +void Heatmap::printSectionHotness(StringRef FileName) const { + if (TextSections.empty()) { + errs() << "No text sections captured from the binary."; + exit(1); + } + 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; + int TextSectionIndex = 0; + + for (const std::pair &KV : Map) { + // Take the middle of the bucket as the representative address. + auto Address = KV.first * BucketSize; + while (Address >= TextSections[TextSectionIndex].EndAddress) + TextSectionIndex++; + if (Address + BucketSize < TextSections[TextSectionIndex].BeginAddress) { + errs() << "Couldn't map the address bucket [0x" + << Twine::utohexstr(Address) << ", 0x" + << Twine::utohexstr(Address + BucketSize) + << "] to a text section in the binary."; + continue; + } + SectionHotness[TextSections[TextSectionIndex].Name] += KV.second; + NumTotalCounts += 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"; + } +} } // namespace bolt } // namespace llvm