Skip to content

Commit dab9c01

Browse files
Support data access profile in llvm-profgen
1 parent ec7a509 commit dab9c01

File tree

6 files changed

+255
-28
lines changed

6 files changed

+255
-28
lines changed

llvm/tools/llvm-profgen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ set(LLVM_LINK_COMPONENTS
1818
add_llvm_tool(llvm-profgen
1919
llvm-profgen.cpp
2020
PerfReader.cpp
21+
DataAccessPerfReader.cpp
2122
CSPreInliner.cpp
2223
ProfiledBinary.cpp
2324
ProfileGenerator.cpp
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include "DataAccessPerfReader.h"
2+
#include "ErrorHandling.h"
3+
#include "PerfReader.h"
4+
#include "llvm/Support/LineIterator.h"
5+
#include "llvm/Support/MemoryBuffer.h"
6+
7+
#include <regex>
8+
9+
static llvm::Regex IPSampleRegex(": 0x[a-fA-F0-9]+ period:");
10+
static llvm::Regex DataAddressRegex("addr: 0x[a-fA-F0-9]+");
11+
12+
namespace llvm {
13+
14+
void DataAccessPerfReader::parsePerfTraces() {
15+
parsePerfTrace(PerfTraceFilename);
16+
}
17+
18+
static void testPerfSampleRecordRegex() {
19+
std::regex logRegex(
20+
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]+)$)");
21+
22+
std::smatch testMatch;
23+
const std::string testLine =
24+
"2193330181938979 0xa88 [0x48]: PERF_RECORD_SAMPLE(IP, 0x4002): "
25+
"1807344/1807344: 0x260b45 period: 100 addr: 0x200630";
26+
if (std::regex_search(testLine, testMatch, logRegex)) {
27+
if (testMatch.size() != 5) {
28+
exitWithError("Regex did not match expected number of groups.");
29+
}
30+
for (size_t i = 0; i < testMatch.size(); ++i) {
31+
errs() << "Group " << i << ": " << testMatch[i] << "\n";
32+
}
33+
// errs() << "Test line matched successfully.\n";
34+
} else {
35+
exitWithError("Test line did not match regex.");
36+
}
37+
}
38+
39+
// Ignore mmap events.
40+
void DataAccessPerfReader::parsePerfTrace(StringRef PerfTrace) {
41+
std::regex logRegex(
42+
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]+)$)");
43+
uint64_t UnmatchedLine = 0, MatchedLine = 0;
44+
45+
auto BufferOrErr = MemoryBuffer::getFile(PerfTrace);
46+
std::error_code EC = BufferOrErr.getError();
47+
if (EC)
48+
exitWithError("Failed to open perf trace file: " + PerfTrace);
49+
50+
line_iterator LineIt(*BufferOrErr.get(), true);
51+
for (; !LineIt.is_at_eof(); ++LineIt) {
52+
StringRef Line = *LineIt;
53+
54+
// Parse MMAP event from perf trace.
55+
// Construct a binary from the binary file path.
56+
PerfScriptReader::MMapEvent MMap;
57+
if (Line.contains("PERF_RECORD_MMAP2")) {
58+
if (PerfScriptReader::extractMMapEventForBinary(Binary, Line, MMap)) {
59+
errs() << "MMap event found: "
60+
<< "PID: " << MMap.PID
61+
<< ", Address: " << format("0x%llx", MMap.Address)
62+
<< ", Size: " << MMap.Size << ", Offset: " << MMap.Offset
63+
<< ", Binary Path: " << MMap.BinaryPath << "\n";
64+
if (MMap.Offset == 0) {
65+
updateBinaryAddress(MMap);
66+
}
67+
}
68+
continue;
69+
}
70+
71+
if (!Line.contains("PERF_RECORD_SAMPLE")) {
72+
// Skip lines that do not contain "PERF_RECORD_SAMPLE".
73+
continue;
74+
}
75+
// errs() << "Processing line: " << Line << "\n";
76+
77+
// if (IPSampleRegex.match(Line, &Matches)) {
78+
// errs() << "IP Captured: " << Matches.size() << "\n";
79+
// }
80+
// if (DataAddressRegex.match(Line, &Matches)) {
81+
// errs() << "Data Address Captured: " << Matches.size() << "\n";
82+
// }
83+
84+
std::smatch matches;
85+
const std::string LineStr = Line.str();
86+
87+
if (std::regex_search(LineStr.begin(), LineStr.end(), matches, logRegex)) {
88+
if (matches.size() != 5)
89+
continue;
90+
91+
uint64_t DataAddress = std::stoull(matches[4].str(), nullptr, 16);
92+
uint64_t IP = std::stoull(matches[3].str(), nullptr, 16);
93+
int32_t PID = std::stoi(matches[1].str());
94+
// if (DataAddress == 0x200630) {
95+
// errs() << "Find data address at 0x200630, IP: " << format("0x%llx",
96+
// IP)
97+
// << " pid is " << PID << "\n";
98+
// }
99+
100+
// errs() << matches.size() << " matches found in line: " << LineStr <<
101+
// "\n"; for (const auto &Match : matches) {
102+
// errs() << "Match: " << Match.str() << "\n";
103+
// }
104+
// Check if the PID matches the filter.
105+
106+
if (PIDFilter && *PIDFilter != PID) {
107+
continue;
108+
}
109+
110+
// Extract the address and count.
111+
112+
uint64_t CanonicalDataAddress =
113+
Binary->canonicalizeVirtualAddress(DataAddress);
114+
// errs() << "Data address is " << format("0x" PRIx64 ":", DataAddress)
115+
// << " Canonical data address is "
116+
// << format("0x" PRIx64 ":", CanonicalDataAddress) << "\n";
117+
AddressToCount[CanonicalDataAddress] += 1;
118+
MatchedLine++;
119+
} else {
120+
// errs() << "\tNo match found for line: " << Line << "\n";
121+
UnmatchedLine++;
122+
}
123+
}
124+
125+
errs() << "Total unmatched lines: " << UnmatchedLine << "\t"
126+
<< "Matched lines: " << MatchedLine << "\n";
127+
}
128+
129+
} // namespace llvm
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===-- DataAccessPerfReader.h - perfscript reader for data access profiles -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_TOOLS_LLVM_PROFGEN_DATAACCESSPERFREADER_H
10+
#define LLVM_TOOLS_LLVM_PROFGEN_DATAACCESSPERFREADER_H
11+
12+
#include "PerfReader.h"
13+
#include "ProfiledBinary.h"
14+
#include "llvm/ADT/MapVector.h"
15+
16+
namespace llvm {
17+
18+
class DataAccessPerfReader : public PerfScriptReader {
19+
public:
20+
DataAccessPerfReader(ProfiledBinary *Binary, StringRef PerfTrace,
21+
std::optional<int32_t> PID)
22+
: PerfScriptReader(Binary, PerfTrace, PID), PerfTraceFilename(PerfTrace) {
23+
}
24+
25+
// Entry of the reader to parse multiple perf traces
26+
void parsePerfTraces() override;
27+
28+
auto getAddressToCount() const {
29+
return AddressToCount.getArrayRef();
30+
}
31+
32+
void print() const {
33+
auto addrCountArray = AddressToCount.getArrayRef();
34+
std::vector<std::pair<uint64_t, uint64_t>> SortedEntries(
35+
addrCountArray.begin(), addrCountArray.end());
36+
llvm::sort(SortedEntries, [](const auto &A, const auto &B) {
37+
return A.second > B.second;
38+
});
39+
for (const auto &Entry : SortedEntries) {
40+
if (Entry.second == 0)
41+
continue; // Skip entries with zero count
42+
dbgs() << "Address: " << format("0x%llx", Entry.first)
43+
<< ", Count: " << Entry.second << "\n";
44+
}
45+
}
46+
47+
private:
48+
void parsePerfTrace(StringRef PerfTrace);
49+
50+
MapVector<uint64_t, uint64_t> AddressToCount;
51+
52+
StringRef PerfTraceFilename;
53+
};
54+
55+
} // namespace llvm
56+
57+
#endif // LLVM_TOOLS_LLVM_PROFGEN_DATAACCESSPERFREADER_H

llvm/tools/llvm-profgen/PerfReader.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,8 @@ void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
478478
// Only update for the first executable segment and assume all other
479479
// segments are loaded at consecutive memory addresses, which is the case on
480480
// X64.
481+
errs() << "Setting " << Binary->getPath() << " base address to "
482+
<< format("0x%" PRIx64, Event.Address) << "\n";
481483
Binary->setBaseAddress(Event.Address);
482484
Binary->setIsLoadedByMMap(true);
483485
} else {

llvm/tools/llvm-profgen/ProfiledBinary.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ static cl::opt<bool>
6060
KernelBinary("kernel",
6161
cl::desc("Generate the profile for Linux kernel binary."));
6262

63+
static cl::opt<bool> RecordDataSegment("record-data-segment", cl::init(false),
64+
cl::desc("Record data segment size "
65+
"in the profile."));
66+
6367
extern cl::opt<bool> ShowDetailedWarning;
6468
extern cl::opt<bool> InferMissingFrames;
6569

@@ -337,6 +341,12 @@ void ProfiledBinary::setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj,
337341
~(PageSize - 1U));
338342
TextSegmentOffsets.push_back(Phdr.p_offset & ~(PageSize - 1U));
339343
}
344+
// else if ((Phdr.p_flags & ELF::PF_R) && !TextSegmentOffsets.empty()) {
345+
// if (RecordDataSegment) {
346+
// ReadOnlyDataSegmentOffsets.push_back(Phdr.p_offset &
347+
// ~(PageSize - 1U));
348+
// }
349+
// }
340350
}
341351
}
342352

llvm/tools/llvm-profgen/llvm-profgen.cpp

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "DataAccessPerfReader.h"
1314
#include "ErrorHandling.h"
1415
#include "PerfReader.h"
1516
#include "ProfileGenerator.h"
@@ -21,6 +22,13 @@
2122
#include "llvm/Support/TargetSelect.h"
2223
#include "llvm/Support/VirtualFileSystem.h"
2324

25+
namespace {
26+
enum ProfileKinds {
27+
SamplePGO,
28+
DataAccessProfile,
29+
};
30+
} // namespace
31+
2432
static cl::OptionCategory ProfGenCategory("ProfGen Options");
2533

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

78+
static cl::opt<ProfileKinds> ProfileKind(
79+
"profile-kind", cl::value_desc("profile-kind"),
80+
cl::desc("Profile kind to be generated, default is sample profile."),
81+
cl::init(DataAccessProfile), cl::cat(ProfGenCategory));
82+
7083
extern cl::opt<bool> ShowDisassemblyOnly;
7184
extern cl::opt<bool> ShowSourceLocations;
7285
extern cl::opt<bool> SkipSymbolization;
@@ -156,37 +169,52 @@ int main(int argc, const char *argv[]) {
156169
if (ShowDisassemblyOnly)
157170
return EXIT_SUCCESS;
158171

159-
if (SampleProfFilename.getNumOccurrences()) {
160-
LLVMContext Context;
161-
auto FS = vfs::getRealFileSystem();
162-
auto ReaderOrErr =
163-
SampleProfileReader::create(SampleProfFilename, Context, *FS);
164-
std::unique_ptr<sampleprof::SampleProfileReader> Reader =
165-
std::move(ReaderOrErr.get());
166-
Reader->read();
167-
std::unique_ptr<ProfileGeneratorBase> Generator =
168-
ProfileGeneratorBase::create(Binary.get(), Reader->getProfiles(),
169-
Reader->profileIsCS());
170-
Generator->generateProfile();
171-
Generator->write();
172+
if (ProfileKind == SamplePGO) {
173+
if (SampleProfFilename.getNumOccurrences()) {
174+
LLVMContext Context;
175+
auto FS = vfs::getRealFileSystem();
176+
auto ReaderOrErr =
177+
SampleProfileReader::create(SampleProfFilename, Context, *FS);
178+
std::unique_ptr<sampleprof::SampleProfileReader> Reader =
179+
std::move(ReaderOrErr.get());
180+
Reader->read();
181+
std::unique_ptr<ProfileGeneratorBase> Generator =
182+
ProfileGeneratorBase::create(Binary.get(), Reader->getProfiles(),
183+
Reader->profileIsCS());
184+
Generator->generateProfile();
185+
Generator->write();
186+
} else {
187+
std::optional<uint32_t> PIDFilter;
188+
if (ProcessId.getNumOccurrences())
189+
PIDFilter = ProcessId;
190+
PerfInputFile PerfFile = getPerfInputFile();
191+
std::unique_ptr<PerfReaderBase> Reader =
192+
PerfReaderBase::create(Binary.get(), PerfFile, PIDFilter);
193+
// Parse perf events and samples
194+
Reader->parsePerfTraces();
195+
196+
if (SkipSymbolization)
197+
return EXIT_SUCCESS;
198+
199+
std::unique_ptr<ProfileGeneratorBase> Generator =
200+
ProfileGeneratorBase::create(Binary.get(),
201+
&Reader->getSampleCounters(),
202+
Reader->profileIsCS());
203+
Generator->generateProfile();
204+
Generator->write();
205+
}
172206
} else {
173-
std::optional<uint32_t> PIDFilter;
174-
if (ProcessId.getNumOccurrences())
175-
PIDFilter = ProcessId;
176-
PerfInputFile PerfFile = getPerfInputFile();
177-
std::unique_ptr<PerfReaderBase> Reader =
178-
PerfReaderBase::create(Binary.get(), PerfFile, PIDFilter);
179-
// Parse perf events and samples
207+
assert(Binary.get() &&
208+
"Binary should be initialized for data access profile");
209+
errs() << "binary text segment offset is "
210+
<< format("0x%" PRIx64 ":", Binary->getTextSegmentOffset()) << "\n";
211+
// data access profile.
212+
SmallVector<StringRef, 4> PerfTraces{PerfScriptFilename};
213+
auto Reader = std::make_unique<DataAccessPerfReader>(
214+
Binary.get(), PerfScriptFilename, std::nullopt);
180215
Reader->parsePerfTraces();
181216

182-
if (SkipSymbolization)
183-
return EXIT_SUCCESS;
184-
185-
std::unique_ptr<ProfileGeneratorBase> Generator =
186-
ProfileGeneratorBase::create(Binary.get(), &Reader->getSampleCounters(),
187-
Reader->profileIsCS());
188-
Generator->generateProfile();
189-
Generator->write();
217+
Reader->print();
190218
}
191219

192220
return EXIT_SUCCESS;

0 commit comments

Comments
 (0)