Skip to content

Commit 95946d2

Browse files
committed
[InstrProf] Add Correlator class to read debug info
Extend `llvm-profdata` to read in a `.proflite` file and also a debug info file to generate a normal `.profdata` profile. This reduces the binary size by 8.4% when building an instrumented Clang binary without value profiling (164 MB vs 179 MB). This work is part of the "lightweight instrumentation" RFC: https://groups.google.com/g/llvm-dev/c/r03Z6JoN7d4 Reviewed By: kyulee Differential Revision: https://reviews.llvm.org/D114566
1 parent 9a2308e commit 95946d2

File tree

12 files changed

+638
-65
lines changed

12 files changed

+638
-65
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Value profiling is currently not supported in lightweight mode.
2+
// RUN: %clang_pgogen -o %t.normal -mllvm --disable-vp=true %s
3+
// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal
4+
// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw
5+
6+
// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %s
7+
// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
8+
// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t.dSYM %t.proflite
9+
10+
// RUN: diff %t.normal.profdata %t.profdata
11+
12+
int foo(int a) {
13+
if (a % 2)
14+
return 4 * a + 1;
15+
return 0;
16+
}
17+
18+
int bar(int a) {
19+
while (a > 100)
20+
a /= 2;
21+
return a;
22+
}
23+
24+
typedef int (*FP)(int);
25+
FP Fps[3] = {foo, bar};
26+
27+
int main() {
28+
for (int i = 0; i < 5; i++)
29+
Fps[i % 2](i);
30+
return 0;
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Value profiling is currently not supported in lightweight mode.
2+
// RUN: %clang_pgogen -o %t.normal -mllvm --disable-vp=true %s
3+
// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal
4+
// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw
5+
6+
// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %s
7+
// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
8+
// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t %t.proflite
9+
10+
// RUN: diff %t.normal.profdata %t.profdata
11+
12+
int foo(int a) {
13+
if (a % 2)
14+
return 4 * a + 1;
15+
return 0;
16+
}
17+
18+
int bar(int a) {
19+
while (a > 100)
20+
a /= 2;
21+
return a;
22+
}
23+
24+
typedef int (*FP)(int);
25+
FP Fps[3] = {foo, bar};
26+
27+
int main() {
28+
for (int i = 0; i < 5; i++)
29+
Fps[i % 2](i);
30+
return 0;
31+
}

llvm/include/llvm/ProfileData/InstrProf.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,10 @@ enum class instrprof_error {
290290
too_large,
291291
truncated,
292292
malformed,
293+
missing_debug_info_for_correlation,
294+
unexpected_debug_info_for_correlation,
295+
unable_to_correlate_profile,
296+
unsupported_debug_format,
293297
unknown_function,
294298
invalid_prof,
295299
hash_mismatch,
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//===- InstrProfCorrelator.h ------------------------------------*- 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+
// This file defines InstrProfCorrelator used to generate PGO profiles from
9+
// raw profile data and debug info.
10+
//===----------------------------------------------------------------------===//
11+
12+
#ifndef LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
13+
#define LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
14+
15+
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
16+
#include "llvm/Object/Binary.h"
17+
#include "llvm/Object/ObjectFile.h"
18+
#include "llvm/ProfileData/InstrProf.h"
19+
#include "llvm/Support/Casting.h"
20+
#include "llvm/Support/Error.h"
21+
#include "llvm/Support/MemoryBuffer.h"
22+
#include <vector>
23+
24+
namespace llvm {
25+
26+
/// InstrProfCorrelator - A base class used to create raw instrumentation data
27+
/// to their functions.
28+
class InstrProfCorrelator {
29+
public:
30+
static llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
31+
get(StringRef DebugInfoFilename);
32+
33+
/// Construct a ProfileData vector used to correlate raw instrumentation data
34+
/// to their functions.
35+
virtual Error correlateProfileData() = 0;
36+
37+
static const char *FunctionNameAttributeName;
38+
static const char *CFGHashAttributeName;
39+
static const char *NumCountersAttributeName;
40+
41+
enum InstrProfCorrelatorKind { CK_32Bit, CK_64Bit };
42+
InstrProfCorrelatorKind getKind() const { return Kind; }
43+
virtual ~InstrProfCorrelator() {}
44+
45+
protected:
46+
struct Context {
47+
static llvm::Expected<std::unique_ptr<Context>>
48+
get(std::unique_ptr<MemoryBuffer> Buffer, const object::ObjectFile &Obj);
49+
std::unique_ptr<MemoryBuffer> Buffer;
50+
/// The address range of the __llvm_prf_cnts section.
51+
uint64_t CountersSectionStart;
52+
uint64_t CountersSectionEnd;
53+
/// True if target and host have different endian orders.
54+
bool ShouldSwapBytes;
55+
};
56+
const std::unique_ptr<InstrProfCorrelator::Context> Ctx;
57+
58+
InstrProfCorrelator(InstrProfCorrelatorKind K, std::unique_ptr<Context> Ctx)
59+
: Ctx(std::move(Ctx)), Kind(K) {}
60+
61+
private:
62+
static llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
63+
get(std::unique_ptr<MemoryBuffer> Buffer);
64+
65+
const InstrProfCorrelatorKind Kind;
66+
};
67+
68+
/// InstrProfCorrelatorImpl - A child of InstrProfCorrelator with a template
69+
/// pointer type so that the ProfileData vector can be materialized.
70+
template <class IntPtrT>
71+
class InstrProfCorrelatorImpl : public InstrProfCorrelator {
72+
public:
73+
InstrProfCorrelatorImpl(std::unique_ptr<InstrProfCorrelator::Context> Ctx);
74+
static bool classof(const InstrProfCorrelator *C);
75+
76+
/// Return a pointer to the underlying ProfileData vector that this class
77+
/// constructs.
78+
const RawInstrProf::ProfileData<IntPtrT> *getDataPointer() const {
79+
return Data.empty() ? nullptr : Data.data();
80+
}
81+
82+
/// Return the number of ProfileData elements.
83+
size_t getDataSize() const { return Data.size(); }
84+
85+
/// Return a pointer to the compressed names string that this class
86+
/// constructs.
87+
const char *getCompressedNamesPointer() const {
88+
return CompressedNames.c_str();
89+
}
90+
91+
/// Return the number of bytes in the compressed names string.
92+
size_t getCompressedNamesSize() const { return CompressedNames.size(); }
93+
94+
static llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>>
95+
get(std::unique_ptr<InstrProfCorrelator::Context> Ctx,
96+
const object::ObjectFile &Obj);
97+
98+
protected:
99+
std::vector<RawInstrProf::ProfileData<IntPtrT>> Data;
100+
std::string CompressedNames;
101+
102+
Error correlateProfileData() override;
103+
virtual void correlateProfileDataImpl() = 0;
104+
105+
void addProbe(StringRef FunctionName, uint64_t CFGHash, IntPtrT CounterOffset,
106+
IntPtrT FunctionPtr, uint32_t NumCounters);
107+
108+
private:
109+
InstrProfCorrelatorImpl(InstrProfCorrelatorKind Kind,
110+
std::unique_ptr<InstrProfCorrelator::Context> Ctx)
111+
: InstrProfCorrelator(Kind, std::move(Ctx)){};
112+
std::vector<std::string> Names;
113+
114+
// Byte-swap the value if necessary.
115+
template <class T> T maybeSwap(T Value) const {
116+
return Ctx->ShouldSwapBytes ? sys::getSwappedBytes(Value) : Value;
117+
}
118+
};
119+
120+
/// DwarfInstrProfCorrelator - A child of InstrProfCorrelatorImpl that takes
121+
/// DWARF debug info as input to correlate profiles.
122+
template <class IntPtrT>
123+
class DwarfInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
124+
public:
125+
DwarfInstrProfCorrelator(std::unique_ptr<DWARFContext> DICtx,
126+
std::unique_ptr<InstrProfCorrelator::Context> Ctx)
127+
: InstrProfCorrelatorImpl<IntPtrT>(std::move(Ctx)),
128+
DICtx(std::move(DICtx)) {}
129+
130+
private:
131+
std::unique_ptr<DWARFContext> DICtx;
132+
133+
/// Return the address of the object that the provided DIE symbolizes.
134+
llvm::Optional<uint64_t> getLocation(const DWARFDie &Die) const;
135+
136+
/// Returns true if the provided DIE symbolizes an instrumentation probe
137+
/// symbol.
138+
static bool isDIEOfProbe(const DWARFDie &Die);
139+
140+
/// Iterate over DWARF DIEs to find those that symbolize instrumentation
141+
/// probes and construct the ProfileData vector and CompressedNames string.
142+
///
143+
/// Here is some example DWARF for an instrumentation probe we are looking
144+
/// for:
145+
/// \code
146+
/// DW_TAG_subprogram
147+
/// DW_AT_low_pc (0x0000000000000000)
148+
/// DW_AT_high_pc (0x0000000000000014)
149+
/// DW_AT_name ("foo")
150+
/// DW_TAG_variable
151+
/// DW_AT_name ("__profc_foo")
152+
/// DW_AT_location (DW_OP_addr 0x0)
153+
/// DW_TAG_LLVM_annotation
154+
/// DW_AT_name ("Function Name")
155+
/// DW_AT_const_value ("foo")
156+
/// DW_TAG_LLVM_annotation
157+
/// DW_AT_name ("CFG Hash")
158+
/// DW_AT_const_value (12345678)
159+
/// DW_TAG_LLVM_annotation
160+
/// DW_AT_name ("Num Counters")
161+
/// DW_AT_const_value (2)
162+
/// NULL
163+
/// NULL
164+
/// \endcode
165+
void correlateProfileDataImpl() override;
166+
};
167+
168+
} // end namespace llvm
169+
170+
#endif // LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H

llvm/include/llvm/ProfileData/InstrProfReader.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/ADT/StringRef.h"
1919
#include "llvm/IR/ProfileSummary.h"
2020
#include "llvm/ProfileData/InstrProf.h"
21+
#include "llvm/ProfileData/InstrProfCorrelator.h"
2122
#include "llvm/Support/Endian.h"
2223
#include "llvm/Support/Error.h"
2324
#include "llvm/Support/LineIterator.h"
@@ -96,6 +97,9 @@ class InstrProfReader {
9697

9798
virtual bool instrEntryBBEnabled() const = 0;
9899

100+
/// Return true if we must provide debug info to create PGO profiles.
101+
virtual bool useDebugInfoCorrelate() const { return false; }
102+
99103
/// Return the PGO symtab. There are three different readers:
100104
/// Raw, Text, and Indexed profile readers. The first two types
101105
/// of readers are used only by llvm-profdata tool, while the indexed
@@ -150,10 +154,12 @@ class InstrProfReader {
150154

151155
/// Factory method to create an appropriately typed reader for the given
152156
/// instrprof file.
153-
static Expected<std::unique_ptr<InstrProfReader>> create(const Twine &Path);
157+
static Expected<std::unique_ptr<InstrProfReader>>
158+
create(const Twine &Path, const InstrProfCorrelator *Correlator = nullptr);
154159

155160
static Expected<std::unique_ptr<InstrProfReader>>
156-
create(std::unique_ptr<MemoryBuffer> Buffer);
161+
create(std::unique_ptr<MemoryBuffer> Buffer,
162+
const InstrProfCorrelator *Correlator = nullptr);
157163
};
158164

159165
/// Reader for the simple text based instrprof format.
@@ -215,6 +221,9 @@ class RawInstrProfReader : public InstrProfReader {
215221
private:
216222
/// The profile data file contents.
217223
std::unique_ptr<MemoryBuffer> DataBuffer;
224+
/// If available, this hold the ProfileData array used to correlate raw
225+
/// instrumentation data to their functions.
226+
const InstrProfCorrelatorImpl<IntPtrT> *Correlator;
218227
bool ShouldSwapBytes;
219228
// The value of the version field of the raw profile data header. The lower 56
220229
// bits specifies the format version and the most significant 8 bits specify
@@ -226,7 +235,7 @@ class RawInstrProfReader : public InstrProfReader {
226235
const RawInstrProf::ProfileData<IntPtrT> *DataEnd;
227236
const uint64_t *CountersStart;
228237
const char *NamesStart;
229-
uint64_t NamesSize;
238+
const char *NamesEnd;
230239
// After value profile is all read, this pointer points to
231240
// the header of next profile data (if exists)
232241
const uint8_t *ValueDataStart;
@@ -237,8 +246,11 @@ class RawInstrProfReader : public InstrProfReader {
237246
const uint8_t *BinaryIdsStart;
238247

239248
public:
240-
RawInstrProfReader(std::unique_ptr<MemoryBuffer> DataBuffer)
241-
: DataBuffer(std::move(DataBuffer)) {}
249+
RawInstrProfReader(std::unique_ptr<MemoryBuffer> DataBuffer,
250+
const InstrProfCorrelator *Correlator)
251+
: DataBuffer(std::move(DataBuffer)),
252+
Correlator(dyn_cast_or_null<const InstrProfCorrelatorImpl<IntPtrT>>(
253+
Correlator)) {}
242254
RawInstrProfReader(const RawInstrProfReader &) = delete;
243255
RawInstrProfReader &operator=(const RawInstrProfReader &) = delete;
244256

@@ -259,6 +271,10 @@ class RawInstrProfReader : public InstrProfReader {
259271
return (Version & VARIANT_MASK_INSTR_ENTRY) != 0;
260272
}
261273

274+
bool useDebugInfoCorrelate() const override {
275+
return (Version & VARIANT_MASK_DBG_CORRELATE) != 0;
276+
}
277+
262278
InstrProfSymtab &getSymtab() override {
263279
assert(Symtab.get());
264280
return *Symtab.get();

llvm/lib/ProfileData/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_llvm_component_library(LLVMProfileData
22
GCOV.cpp
33
InstrProf.cpp
4+
InstrProfCorrelator.cpp
45
InstrProfReader.cpp
56
InstrProfWriter.cpp
67
ProfileSummaryBuilder.cpp
@@ -19,6 +20,8 @@ add_llvm_component_library(LLVMProfileData
1920
Core
2021
Support
2122
Demangle
23+
Object
24+
DebugInfoDWARF
2225
)
2326

2427
add_subdirectory(Coverage)

llvm/lib/ProfileData/InstrProf.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,18 @@ static std::string getInstrProfErrString(instrprof_error Err,
110110
case instrprof_error::malformed:
111111
OS << "malformed instrumentation profile data";
112112
break;
113+
case instrprof_error::missing_debug_info_for_correlation:
114+
OS << "debug info for correlation is required";
115+
break;
116+
case instrprof_error::unexpected_debug_info_for_correlation:
117+
OS << "debug info for correlation is not necessary";
118+
break;
119+
case instrprof_error::unable_to_correlate_profile:
120+
OS << "unable to correlate profile";
121+
break;
122+
case instrprof_error::unsupported_debug_format:
123+
OS << "unsupported debug info format (only DWARF is supported)";
124+
break;
113125
case instrprof_error::invalid_prof:
114126
OS << "invalid profile created. Please file a bug "
115127
"at: " BUG_REPORT_URL

0 commit comments

Comments
 (0)