Skip to content

[Demo only] Illustrate the llvm-profgen changes to process PEBS memory load events into <IP, DataAddr, Count> Tuples. #142007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/tools/llvm-profgen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_tool(llvm-profgen
llvm-profgen.cpp
PerfReader.cpp
DataAccessPerfReader.cpp
CSPreInliner.cpp
ProfiledBinary.cpp
ProfileGenerator.cpp
Expand Down
92 changes: 92 additions & 0 deletions llvm/tools/llvm-profgen/DataAccessPerfReader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#include "DataAccessPerfReader.h"
#include "ErrorHandling.h"
#include "PerfReader.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"

#include <regex>

static llvm::Regex IPSampleRegex(": 0x[a-fA-F0-9]+ period:");
static llvm::Regex DataAddressRegex("addr: 0x[a-fA-F0-9]+");

namespace llvm {

void DataAccessPerfReader::parsePerfTraces() {
parsePerfTrace(PerfTraceFilename);
}

// Ignore mmap events.
void DataAccessPerfReader::parsePerfTrace(StringRef PerfTrace) {
std::regex logRegex(
R"(^.*?PERF_RECORD_SAMPLE\(.*?\):\s*(\d+)\/(\d+):\s*(0x[0-9a-fA-F]+)\s+period:\s*\d+\s+addr:\s*(0x[0-9a-fA-F]+)$)");

auto BufferOrErr = MemoryBuffer::getFile(PerfTrace);
std::error_code EC = BufferOrErr.getError();
if (EC)
exitWithError("Failed to open perf trace file: " + PerfTrace);

line_iterator LineIt(*BufferOrErr.get(), true);
for (; !LineIt.is_at_eof(); ++LineIt) {
StringRef Line = *LineIt;

// Parse MMAP event from perf trace.
// Parse MMAP event from perf trace.
// Construct a binary from the binary file path.
PerfScriptReader::MMapEvent MMap;
if (Line.contains("PERF_RECORD_MMAP2")) {
if (PerfScriptReader::extractMMapEventForBinary(Binary, Line, MMap)) {
// TODO: This is a hack to avoid mapping binary address for data section
// mappings.
if (MMap.Offset == 0) {
updateBinaryAddress(MMap);
errs() << "Binary base address is "
<< format("0x%" PRIx64, Binary->getBaseAddress())
<< " and preferred base address is "
<< format("0x%" PRIx64, Binary->getPreferredBaseAddress())
<< " and first loadable address is "
<< format("0x%" PRIx64, Binary->getFirstLoadableAddress())
<< "\n";
}
}
continue;
}

if (!Line.contains("PERF_RECORD_SAMPLE")) {
// Skip lines that do not contain "PERF_RECORD_SAMPLE".
continue;
}

std::smatch matches;
const std::string LineStr = Line.str();

if (std::regex_search(LineStr.begin(), LineStr.end(), matches, logRegex)) {
if (matches.size() != 5)
continue;

uint64_t DataAddress = std::stoull(matches[4].str(), nullptr, 16);

// Skip addresses out of the specified PT_LOAD section for data.
if (DataAddress < DataMMap.Address ||
DataAddress >= DataMMap.Address + DataMMap.Size)
continue;

int32_t PID = std::stoi(matches[1].str());
// Check if the PID matches the filter.

if (PIDFilter && *PIDFilter != PID) {
continue;
}

uint64_t IP = std::stoull(matches[3].str(), nullptr, 16);
// Extract the address and count.
uint64_t CanonicalDataAddress =
canonicalizeDataAddress(DataAddress, *Binary, DataMMap, DataSegment);

uint64_t CanonicalIPAddress = Binary->canonicalizeVirtualAddress(IP);

AddressMap[CanonicalIPAddress][CanonicalDataAddress] += 1;
}
}
}

} // namespace llvm
108 changes: 108 additions & 0 deletions llvm/tools/llvm-profgen/DataAccessPerfReader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//===-- DataAccessPerfReader.h - perfscript reader for data access profiles -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_TOOLS_LLVM_PROFGEN_DATAACCESSPERFREADER_H
#define LLVM_TOOLS_LLVM_PROFGEN_DATAACCESSPERFREADER_H

#include "PerfReader.h"
#include "ProfiledBinary.h"
#include "llvm/ADT/MapVector.h"

namespace llvm {

class DataAccessPerfReader : public PerfScriptReader {
public:
class DataSegment {
public:
uint64_t FileOffset;
uint64_t VirtualAddress;
};
DataAccessPerfReader(ProfiledBinary *Binary, StringRef PerfTrace,
std::optional<int32_t> PID)
: PerfScriptReader(Binary, PerfTrace, PID), PerfTraceFilename(PerfTrace) {
hackMMapEventAndDataSegment(DataMMap, DataSegment, *Binary);
}

// The MMapEvent is hard-coded as a hack to illustrate the change.
static void
hackMMapEventAndDataSegment(PerfScriptReader::MMapEvent &MMap,
DataSegment &DataSegment,
const ProfiledBinary &ProfiledBinary) {
// The PERF_RECORD_MMAP2 event is
// 0 0x4e8 [0xa0]: PERF_RECORD_MMAP2 1849842/1849842:
// [0x55d977426000(0x1000) @ 0x1000 fd:01 20869534 0]: r--p /path/to/binary
MMap.PID = 1849842; // Example PID
MMap.BinaryPath = ProfiledBinary.getPath();
MMap.Address = 0x55d977426000;
MMap.Size = 0x1000;
MMap.Offset = 0x1000; // File Offset in the binary.

// TODO: Set binary fields to do address canonicalization, and compute
// static data address range.
DataSegment.FileOffset =
0x1180; // The byte offset of the segment start in the binary.
DataSegment.VirtualAddress =
0x3180; // The virtual address of the segment start in the binary.
}

uint64_t canonicalizeDataAddress(uint64_t Address,
const ProfiledBinary &ProfiledBinary,
const PerfScriptReader::MMapEvent &MMap,
const DataSegment &DataSegment) {
// virtual-addr = segment.virtual-addr (0x3180) + (runtime-addr -
// map.adddress - segment.file-offset (0x1180) + map.file-offset (0x1000))
return DataSegment.VirtualAddress +
(Address - MMap.Address - (DataSegment.FileOffset - MMap.Offset));
}

// Entry of the reader to parse multiple perf traces
void parsePerfTraces() override;

struct ProfiledInfo {
ProfiledInfo(uint64_t InstructionAddr, uint64_t DataAddr, uint64_t Count)
: InstructionAddr(InstructionAddr), DataAddr(DataAddr), Count(Count) {}
uint64_t InstructionAddr;
uint64_t DataAddr;
uint64_t Count;
};

// A hack to demonstrate the symbolized output of vtable type profiling.
void print() const {

std::vector<ProfiledInfo> Entries;
Entries.reserve(AddressMap.size());
for (const auto &[IpAddr, DataCount] : AddressMap) {
for (const auto [DataAddr, Count] : DataCount) {
Entries.emplace_back(ProfiledInfo(IpAddr, DataAddr, Count));
}
}
llvm::sort(Entries,
[](const auto &A, const auto &B) { return A.Count > B.Count; });
for (const auto &Entry : Entries) {
if (Entry.Count == 0)
continue; // Skip entries with zero count
dbgs() << "Address: " << format("0x%llx", Entry.InstructionAddr)
<< " Data Address: " << format("0x%llx", Entry.DataAddr)
<< " Count: " << Entry.Count << "\n";
}
}

private:
void parsePerfTrace(StringRef PerfTrace);

DenseMap<uint64_t, DenseMap<uint64_t, uint64_t>> AddressMap;

StringRef PerfTraceFilename;

PerfScriptReader::MMapEvent DataMMap;
DataSegment DataSegment;
};

} // namespace llvm

#endif // LLVM_TOOLS_LLVM_PROFGEN_DATAACCESSPERFREADER_H
2 changes: 2 additions & 0 deletions llvm/tools/llvm-profgen/PerfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,8 @@ void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
// Only update for the first executable segment and assume all other
// segments are loaded at consecutive memory addresses, which is the case on
// X64.
errs() << "Setting " << Binary->getPath() << " base address to "
<< format("0x%" PRIx64, Event.Address) << "\n";
Binary->setBaseAddress(Event.Address);
Binary->setIsLoadedByMMap(true);
} else {
Expand Down
4 changes: 4 additions & 0 deletions llvm/tools/llvm-profgen/ProfiledBinary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ static cl::opt<bool>
KernelBinary("kernel",
cl::desc("Generate the profile for Linux kernel binary."));

static cl::opt<bool> RecordDataSegment("record-data-segment", cl::init(false),
cl::desc("Record data segment size "
"in the profile."));

extern cl::opt<bool> ShowDetailedWarning;
extern cl::opt<bool> InferMissingFrames;

Expand Down
2 changes: 2 additions & 0 deletions llvm/tools/llvm-profgen/ProfiledBinary.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ class ProfiledBinary {
// The file offset of each executable segment.
std::vector<uint64_t> TextSegmentOffsets;

std::vector<uint64_t> ReadOnlyDataSegmentOffsets;

// Mutiple MC component info
std::unique_ptr<const MCRegisterInfo> MRI;
std::unique_ptr<const MCAsmInfo> AsmInfo;
Expand Down
83 changes: 55 additions & 28 deletions llvm/tools/llvm-profgen/llvm-profgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "DataAccessPerfReader.h"
#include "ErrorHandling.h"
#include "PerfReader.h"
#include "ProfileGenerator.h"
Expand All @@ -21,6 +22,13 @@
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/VirtualFileSystem.h"

namespace {
enum ProfileKinds {
SamplePGO,
DataAccessProfile,
};
} // namespace

static cl::OptionCategory ProfGenCategory("ProfGen Options");

static cl::opt<std::string> PerfScriptFilename(
Expand Down Expand Up @@ -67,6 +75,11 @@ static cl::opt<std::string> DebugBinPath(
"from it instead of the executable binary."),
cl::cat(ProfGenCategory));

static cl::opt<ProfileKinds> ProfileKind(
"profile-kind", cl::value_desc("profile-kind"),
cl::desc("Profile kind to be generated, default is sample profile."),
cl::init(DataAccessProfile), cl::cat(ProfGenCategory));

extern cl::opt<bool> ShowDisassemblyOnly;
extern cl::opt<bool> ShowSourceLocations;
extern cl::opt<bool> SkipSymbolization;
Expand Down Expand Up @@ -156,37 +169,51 @@ int main(int argc, const char *argv[]) {
if (ShowDisassemblyOnly)
return EXIT_SUCCESS;

if (SampleProfFilename.getNumOccurrences()) {
LLVMContext Context;
auto FS = vfs::getRealFileSystem();
auto ReaderOrErr =
SampleProfileReader::create(SampleProfFilename, Context, *FS);
std::unique_ptr<sampleprof::SampleProfileReader> Reader =
std::move(ReaderOrErr.get());
Reader->read();
std::unique_ptr<ProfileGeneratorBase> Generator =
ProfileGeneratorBase::create(Binary.get(), Reader->getProfiles(),
Reader->profileIsCS());
Generator->generateProfile();
Generator->write();
if (ProfileKind == SamplePGO) {
if (SampleProfFilename.getNumOccurrences()) {
LLVMContext Context;
auto FS = vfs::getRealFileSystem();
auto ReaderOrErr =
SampleProfileReader::create(SampleProfFilename, Context, *FS);
std::unique_ptr<sampleprof::SampleProfileReader> Reader =
std::move(ReaderOrErr.get());
Reader->read();
std::unique_ptr<ProfileGeneratorBase> Generator =
ProfileGeneratorBase::create(Binary.get(), Reader->getProfiles(),
Reader->profileIsCS());
Generator->generateProfile();
Generator->write();
} else {
std::optional<uint32_t> PIDFilter;
if (ProcessId.getNumOccurrences())
PIDFilter = ProcessId;
PerfInputFile PerfFile = getPerfInputFile();
std::unique_ptr<PerfReaderBase> Reader =
PerfReaderBase::create(Binary.get(), PerfFile, PIDFilter);
// Parse perf events and samples
Reader->parsePerfTraces();

if (SkipSymbolization)
return EXIT_SUCCESS;

std::unique_ptr<ProfileGeneratorBase> Generator =
ProfileGeneratorBase::create(Binary.get(),
&Reader->getSampleCounters(),
Reader->profileIsCS());
Generator->generateProfile();
Generator->write();
}
} else {
std::optional<uint32_t> PIDFilter;
if (ProcessId.getNumOccurrences())
PIDFilter = ProcessId;
PerfInputFile PerfFile = getPerfInputFile();
std::unique_ptr<PerfReaderBase> Reader =
PerfReaderBase::create(Binary.get(), PerfFile, PIDFilter);
// Parse perf events and samples
assert(Binary.get() &&
"Binary should be initialized for data access profile");

// data access profile.
SmallVector<StringRef, 4> PerfTraces{PerfScriptFilename};
auto Reader = std::make_unique<DataAccessPerfReader>(
Binary.get(), PerfScriptFilename, std::nullopt);
Reader->parsePerfTraces();

if (SkipSymbolization)
return EXIT_SUCCESS;

std::unique_ptr<ProfileGeneratorBase> Generator =
ProfileGeneratorBase::create(Binary.get(), &Reader->getSampleCounters(),
Reader->profileIsCS());
Generator->generateProfile();
Generator->write();
Reader->print();
}

return EXIT_SUCCESS;
Expand Down
Loading