1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
|
//===--- PseudoProbe.cpp - Pseudo probe decoding utilities ------*- 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
//
//===----------------------------------------------------------------------===//
#include "PseudoProbe.h"
#include "ErrorHandling.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/raw_ostream.h"
#include <limits>
#include <memory>
using namespace llvm;
using namespace sampleprof;
using namespace support;
namespace llvm {
namespace sampleprof {
static StringRef getProbeFNameForGUID(const GUIDProbeFunctionMap &GUID2FuncMAP,
uint64_t GUID) {
auto It = GUID2FuncMAP.find(GUID);
assert(It != GUID2FuncMAP.end() &&
"Probe function must exist for a valid GUID");
return It->second.FuncName;
}
void PseudoProbeFuncDesc::print(raw_ostream &OS) {
OS << "GUID: " << FuncGUID << " Name: " << FuncName << "\n";
OS << "Hash: " << FuncHash << "\n";
}
void PseudoProbe::getInlineContext(SmallVectorImpl<std::string> &ContextStack,
const GUIDProbeFunctionMap &GUID2FuncMAP,
bool ShowName) const {
uint32_t Begin = ContextStack.size();
PseudoProbeInlineTree *Cur = InlineTree;
// It will add the string of each node's inline site during iteration.
// Note that it won't include the probe's belonging function(leaf location)
while (Cur->hasInlineSite()) {
std::string ContextStr;
if (ShowName) {
StringRef FuncName =
getProbeFNameForGUID(GUID2FuncMAP, std::get<0>(Cur->ISite));
ContextStr += FuncName.str();
} else {
ContextStr += Twine(std::get<0>(Cur->ISite)).str();
}
ContextStr += ":";
ContextStr += Twine(std::get<1>(Cur->ISite)).str();
ContextStack.emplace_back(ContextStr);
Cur = Cur->Parent;
}
// Make the ContextStack in caller-callee order
std::reverse(ContextStack.begin() + Begin, ContextStack.end());
}
std::string
PseudoProbe::getInlineContextStr(const GUIDProbeFunctionMap &GUID2FuncMAP,
bool ShowName) const {
std::ostringstream OContextStr;
SmallVector<std::string, 16> ContextStack;
getInlineContext(ContextStack, GUID2FuncMAP, ShowName);
for (auto &CxtStr : ContextStack) {
if (OContextStr.str().size())
OContextStr << " @ ";
OContextStr << CxtStr;
}
return OContextStr.str();
}
static const char *PseudoProbeTypeStr[3] = {"Block", "IndirectCall",
"DirectCall"};
void PseudoProbe::print(raw_ostream &OS,
const GUIDProbeFunctionMap &GUID2FuncMAP,
bool ShowName) {
OS << "FUNC: ";
if (ShowName) {
StringRef FuncName = getProbeFNameForGUID(GUID2FuncMAP, GUID);
OS << FuncName.str() << " ";
} else {
OS << GUID << " ";
}
OS << "Index: " << Index << " ";
OS << "Type: " << PseudoProbeTypeStr[static_cast<uint8_t>(Type)] << " ";
if (isDangling()) {
OS << "Dangling ";
}
if (isTailCall()) {
OS << "TailCall ";
}
std::string InlineContextStr = getInlineContextStr(GUID2FuncMAP, ShowName);
if (InlineContextStr.size()) {
OS << "Inlined: @ ";
OS << InlineContextStr;
}
OS << "\n";
}
template <typename T> T PseudoProbeDecoder::readUnencodedNumber() {
if (Data + sizeof(T) > End) {
exitWithError("Decode unencoded number error in " + SectionName +
" section");
}
T Val = endian::readNext<T, little, unaligned>(Data);
return Val;
}
template <typename T> T PseudoProbeDecoder::readUnsignedNumber() {
unsigned NumBytesRead = 0;
uint64_t Val = decodeULEB128(Data, &NumBytesRead);
if (Val > std::numeric_limits<T>::max() || (Data + NumBytesRead > End)) {
exitWithError("Decode number error in " + SectionName + " section");
}
Data += NumBytesRead;
return static_cast<T>(Val);
}
template <typename T> T PseudoProbeDecoder::readSignedNumber() {
unsigned NumBytesRead = 0;
int64_t Val = decodeSLEB128(Data, &NumBytesRead);
if (Val > std::numeric_limits<T>::max() || (Data + NumBytesRead > End)) {
exitWithError("Decode number error in " + SectionName + " section");
}
Data += NumBytesRead;
return static_cast<T>(Val);
}
StringRef PseudoProbeDecoder::readString(uint32_t Size) {
StringRef Str(reinterpret_cast<const char *>(Data), Size);
if (Data + Size > End) {
exitWithError("Decode string error in " + SectionName + " section");
}
Data += Size;
return Str;
}
void PseudoProbeDecoder::buildGUID2FuncDescMap(const uint8_t *Start,
std::size_t Size) {
// The pseudo_probe_desc section has a format like:
// .section .pseudo_probe_desc,"",@progbits
// .quad -5182264717993193164 // GUID
// .quad 4294967295 // Hash
// .uleb 3 // Name size
// .ascii "foo" // Name
// .quad -2624081020897602054
// .quad 174696971957
// .uleb 34
// .ascii "main"
#ifndef NDEBUG
SectionName = "pseudo_probe_desc";
#endif
Data = Start;
End = Data + Size;
while (Data < End) {
uint64_t GUID = readUnencodedNumber<uint64_t>();
uint64_t Hash = readUnencodedNumber<uint64_t>();
uint32_t NameSize = readUnsignedNumber<uint32_t>();
StringRef Name = readString(NameSize);
// Initialize PseudoProbeFuncDesc and populate it into GUID2FuncDescMap
GUID2FuncDescMap.emplace(GUID, PseudoProbeFuncDesc(GUID, Hash, Name));
}
assert(Data == End && "Have unprocessed data in pseudo_probe_desc section");
}
void PseudoProbeDecoder::buildAddress2ProbeMap(const uint8_t *Start,
std::size_t Size) {
// The pseudo_probe section encodes an inline forest and each tree has a
// format like:
// FUNCTION BODY (one for each uninlined function present in the text
// section)
// GUID (uint64)
// GUID of the function
// NPROBES (ULEB128)
// Number of probes originating from this function.
// NUM_INLINED_FUNCTIONS (ULEB128)
// Number of callees inlined into this function, aka number of
// first-level inlinees
// PROBE RECORDS
// A list of NPROBES entries. Each entry contains:
// INDEX (ULEB128)
// TYPE (uint4)
// 0 - block probe, 1 - indirect call, 2 - direct call
// ATTRIBUTE (uint3)
// 1 - tail call, 2 - dangling
// ADDRESS_TYPE (uint1)
// 0 - code address, 1 - address delta
// CODE_ADDRESS (uint64 or ULEB128)
// code address or address delta, depending on Flag
// INLINED FUNCTION RECORDS
// A list of NUM_INLINED_FUNCTIONS entries describing each of the
// inlined callees. Each record contains:
// INLINE SITE
// GUID of the inlinee (uint64)
// Index of the callsite probe (ULEB128)
// FUNCTION BODY
// A FUNCTION BODY entry describing the inlined function.
#ifndef NDEBUG
SectionName = "pseudo_probe";
#endif
Data = Start;
End = Data + Size;
PseudoProbeInlineTree *Root = &DummyInlineRoot;
PseudoProbeInlineTree *Cur = &DummyInlineRoot;
uint64_t LastAddr = 0;
uint32_t Index = 0;
// A DFS-based decoding
while (Data < End) {
// Read inline site for inlinees
if (Root != Cur) {
Index = readUnsignedNumber<uint32_t>();
}
// Switch/add to a new tree node(inlinee)
Cur = Cur->getOrAddNode(std::make_tuple(Cur->GUID, Index));
// Read guid
Cur->GUID = readUnencodedNumber<uint64_t>();
// Read number of probes in the current node.
uint32_t NodeCount = readUnsignedNumber<uint32_t>();
// Read number of direct inlinees
Cur->ChildrenToProcess = readUnsignedNumber<uint32_t>();
// Read all probes in this node
for (std::size_t I = 0; I < NodeCount; I++) {
// Read index
uint32_t Index = readUnsignedNumber<uint32_t>();
// Read type | flag.
uint8_t Value = readUnencodedNumber<uint8_t>();
uint8_t Kind = Value & 0xf;
uint8_t Attr = (Value & 0x70) >> 4;
// Read address
uint64_t Addr = 0;
if (Value & 0x80) {
int64_t Offset = readSignedNumber<int64_t>();
Addr = LastAddr + Offset;
} else {
Addr = readUnencodedNumber<int64_t>();
}
// Populate Address2ProbesMap
std::vector<PseudoProbe> &ProbeVec = Address2ProbesMap[Addr];
ProbeVec.emplace_back(Addr, Cur->GUID, Index, PseudoProbeType(Kind), Attr,
Cur);
Cur->addProbes(&ProbeVec.back());
LastAddr = Addr;
}
// Look for the parent for the next node by subtracting the current
// node count from tree counts along the parent chain. The first node
// in the chain that has a non-zero tree count is the target.
while (Cur != Root) {
if (Cur->ChildrenToProcess == 0) {
Cur = Cur->Parent;
if (Cur != Root) {
assert(Cur->ChildrenToProcess > 0 &&
"Should have some unprocessed nodes");
Cur->ChildrenToProcess -= 1;
}
} else {
break;
}
}
}
assert(Data == End && "Have unprocessed data in pseudo_probe section");
assert(Cur == Root &&
" Cur should point to root when the forest is fully built up");
}
void PseudoProbeDecoder::printGUID2FuncDescMap(raw_ostream &OS) {
OS << "Pseudo Probe Desc:\n";
// Make the output deterministic
std::map<uint64_t, PseudoProbeFuncDesc> OrderedMap(GUID2FuncDescMap.begin(),
GUID2FuncDescMap.end());
for (auto &I : OrderedMap) {
I.second.print(OS);
}
}
void PseudoProbeDecoder::printProbeForAddress(raw_ostream &OS,
uint64_t Address) {
auto It = Address2ProbesMap.find(Address);
if (It != Address2ProbesMap.end()) {
for (auto &Probe : It->second) {
OS << " [Probe]:\t";
Probe.print(OS, GUID2FuncDescMap, true);
}
}
}
const PseudoProbe *
PseudoProbeDecoder::getCallProbeForAddr(uint64_t Address) const {
auto It = Address2ProbesMap.find(Address);
if (It == Address2ProbesMap.end())
return nullptr;
const std::vector<PseudoProbe> &Probes = It->second;
const PseudoProbe *CallProbe = nullptr;
for (const auto &Probe : Probes) {
if (Probe.isCall()) {
assert(!CallProbe &&
"There should be only one call probe corresponding to address "
"which is a callsite.");
CallProbe = &Probe;
}
}
return CallProbe;
}
const PseudoProbeFuncDesc *
PseudoProbeDecoder::getFuncDescForGUID(uint64_t GUID) const {
auto It = GUID2FuncDescMap.find(GUID);
assert(It != GUID2FuncDescMap.end() && "Function descriptor doesn't exist");
return &It->second;
}
void PseudoProbeDecoder::getInlineContextForProbe(
const PseudoProbe *Probe, SmallVectorImpl<std::string> &InlineContextStack,
bool IncludeLeaf) const {
Probe->getInlineContext(InlineContextStack, GUID2FuncDescMap, true);
if (!IncludeLeaf)
return;
// Note that the context from probe doesn't include leaf frame,
// hence we need to retrieve and prepend leaf if requested.
const auto *FuncDesc = getFuncDescForGUID(Probe->GUID);
InlineContextStack.emplace_back(FuncDesc->FuncName + ":" +
Twine(Probe->Index).str());
}
const PseudoProbeFuncDesc *
PseudoProbeDecoder::getInlinerDescForProbe(const PseudoProbe *Probe) const {
PseudoProbeInlineTree *InlinerNode = Probe->InlineTree;
if (!InlinerNode->hasInlineSite())
return nullptr;
return getFuncDescForGUID(std::get<0>(InlinerNode->ISite));
}
} // end namespace sampleprof
} // end namespace llvm
|