[BOLT] Report per-section hotness in bolt-heatmap.
This patch adds a new feature to bolt heatmap to print the hotness of each section in terms of the percentage of samples within that section. Sample output generated for the clang binary: Section Name, Begin Address, End Address, Percentage Hotness .text, 0x1a7b9b0, 0x20a2cc0, 1.4709 .init, 0x20a2cc0, 0x20a2ce1, 0.0001 .fini, 0x20a2ce4, 0x20a2cf2, 0.0000 .text.unlikely, 0x20a2d00, 0x431990c, 0.3061 .text.hot, 0x4319910, 0x4bc6927, 97.2197 .text.startup, 0x4bc6930, 0x4c10c89, 0.0058 .plt, 0x4c10c90, 0x4c12010, 0.9974 Reviewed By: rafauler Differential Revision: https://reviews.llvm.org/D124412
This commit is contained in:
parent
f6dff93641
commit
733dc3e50b
|
@ -12,12 +12,20 @@
|
|||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
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 @@ class Heatmap {
|
|||
/// Map buckets to the number of samples.
|
||||
std::map<uint64_t, uint64_t> Map;
|
||||
|
||||
/// Map section names to their address range.
|
||||
const std::vector<SectionNameAndRange> TextSections;
|
||||
|
||||
public:
|
||||
explicit Heatmap(uint64_t BucketSize = 4096, uint64_t MinAddress = 0,
|
||||
uint64_t MaxAddress = std::numeric_limits<uint64_t>::max())
|
||||
: BucketSize(BucketSize), MinAddress(MinAddress),
|
||||
MaxAddress(MaxAddress){};
|
||||
uint64_t MaxAddress = std::numeric_limits<uint64_t>::max(),
|
||||
std::vector<SectionNameAndRange> 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 @@ public:
|
|||
|
||||
void printCDF(raw_ostream &OS) const;
|
||||
|
||||
void printSectionHotness(StringRef Filename) const;
|
||||
|
||||
void printSectionHotness(raw_ostream &OS) const;
|
||||
|
||||
size_t size() const { return Map.size(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -116,6 +116,22 @@ namespace {
|
|||
const char TimerGroupName[] = "aggregator";
|
||||
const char TimerGroupDesc[] = "Aggregator";
|
||||
|
||||
std::vector<SectionNameAndRange> getTextSections(const BinaryContext *BC) {
|
||||
std::vector<SectionNameAndRange> 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 @@ std::error_code DataAggregator::printLBRHeatMap() {
|
|||
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 @@ std::error_code DataAggregator::printLBRHeatMap() {
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -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 @@ void Heatmap::printCDF(raw_ostream &OS) const {
|
|||
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<uint64_t> 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<const uint64_t, uint64_t> &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
|
||||
|
|
Loading…
Reference in a new issue