LLVM 24.0.0git
MemProfRadixTree.h
Go to the documentation of this file.
1//===- MemProfRadixTree.h - MemProf format support ------------*- 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// A custom Radix Tree builder for memprof data to optimize for space.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_PROFILEDATA_MEMPROFRADIXTREE_H
14#define LLVM_PROFILEDATA_MEMPROFRADIXTREE_H
15
16#include "llvm/ADT/BitVector.h"
20
21#include <optional>
22
23namespace llvm {
24namespace memprof {
25
26// A function object that returns a frame for a given FrameId.
27template <typename MapTy> struct FrameIdConverter {
28 std::optional<FrameId> LastUnmappedId;
29 MapTy &Map;
30
31 FrameIdConverter() = delete;
33
34 // Delete the copy constructor and copy assignment operator to avoid a
35 // situation where a copy of FrameIdConverter gets an error in LastUnmappedId
36 // while the original instance doesn't.
39
41 auto Iter = Map.find(Id);
42 if (Iter == Map.end()) {
43 LastUnmappedId = Id;
44 return Frame();
45 }
46 return Iter->second;
47 }
48};
49
50// A function object that returns a call stack for a given CallStackId.
51template <typename MapTy> struct CallStackIdConverter {
52 std::optional<CallStackId> LastUnmappedId;
53 MapTy &Map;
55
60
61 // Delete the copy constructor and copy assignment operator to avoid a
62 // situation where a copy of CallStackIdConverter gets an error in
63 // LastUnmappedId while the original instance doesn't.
66
67 std::vector<Frame> operator()(CallStackId CSId) {
68 std::vector<Frame> Frames;
69 auto CSIter = Map.find(CSId);
70 if (CSIter == Map.end()) {
71 LastUnmappedId = CSId;
72 } else {
73 ArrayRef<FrameId> CS = CSIter->second;
74 Frames.reserve(CS.size());
75 for (FrameId Id : CS)
76 Frames.push_back(FrameIdToFrame(Id));
77 }
78 return Frames;
79 }
80};
81
82// A function object that returns a Frame stored at a given index into the Frame
83// array in the profile.
85 const unsigned char *FrameBase;
86
88 LinearFrameIdConverter(const unsigned char *FrameBase)
90
92 uint64_t Offset = static_cast<uint64_t>(LinearId) * Frame::serializedSize();
94 }
95};
96
97// A function object that returns a call stack stored at a given index into the
98// call stack array in the profile.
100 const unsigned char *CallStackBase;
102
108
109 std::vector<Frame> operator()(LinearCallStackId LinearCSId) {
110 std::vector<Frame> Frames;
111
112 const unsigned char *Ptr =
114 static_cast<uint64_t>(LinearCSId) * sizeof(LinearFrameId);
115 uint32_t NumFrames =
117 Frames.reserve(NumFrames);
118 for (; NumFrames; --NumFrames) {
119 LinearFrameId Elem =
121 // Follow a pointer to the parent, if any. See comments below on
122 // CallStackRadixTreeBuilder for the description of the radix tree format.
123 if (static_cast<std::make_signed_t<LinearFrameId>>(Elem) < 0) {
124 Ptr += (-Elem) * sizeof(LinearFrameId);
125 Elem =
127 }
128 // We shouldn't encounter another pointer.
129 assert(static_cast<std::make_signed_t<LinearFrameId>>(Elem) >= 0);
130 Frames.push_back(FrameIdToFrame(Elem));
131 Ptr += sizeof(LinearFrameId);
132 }
133
134 return Frames;
135 }
136};
137
138// Used to extract caller-callee pairs from the call stack array. The leaf
139// frame is assumed to call a heap allocation function with GUID 0. The
140// resulting pairs are accumulated in CallerCalleePairs. Users can take it
141// with:
142//
143// auto Pairs = std::move(Extractor.CallerCalleePairs);
145 // The base address of the radix tree array.
146 const unsigned char *CallStackBase;
147 // A functor to convert a linear FrameId to a Frame.
149 // A map from caller GUIDs to lists of call sites in respective callers.
151
152 // The set of linear call stack IDs that we've visited.
154
157 const unsigned char *CallStackBase,
159 unsigned RadixTreeSize)
161 Visited(RadixTreeSize) {}
162
163 void operator()(LinearCallStackId LinearCSId) {
164 const unsigned char *Ptr =
166 static_cast<uint64_t>(LinearCSId) * sizeof(LinearFrameId);
167 uint32_t NumFrames =
169 // The leaf frame calls a function with GUID 0.
170 uint64_t CalleeGUID = 0;
171 for (; NumFrames; --NumFrames) {
172 LinearFrameId Elem =
174 // Follow a pointer to the parent, if any. See comments below on
175 // CallStackRadixTreeBuilder for the description of the radix tree format.
176 if (static_cast<std::make_signed_t<LinearFrameId>>(Elem) < 0) {
177 Ptr += (-Elem) * sizeof(LinearFrameId);
178 Elem =
180 }
181 // We shouldn't encounter another pointer.
182 assert(static_cast<std::make_signed_t<LinearFrameId>>(Elem) >= 0);
183
184 // Add a new caller-callee pair.
185 Frame F = FrameIdToFrame(Elem);
186 uint64_t CallerGUID = F.Function;
187 LineLocation Loc(F.LineOffset, F.Column);
188 CallerCalleePairs[CallerGUID].emplace_back(Loc, CalleeGUID);
189
190 // Keep track of the indices we've visited. If we've already visited the
191 // current one, terminate the traversal. We will not discover any new
192 // caller-callee pair by continuing the traversal.
193 unsigned Offset =
194 std::distance(CallStackBase, Ptr) / sizeof(LinearFrameId);
195 if (Visited.test(Offset))
196 break;
197 Visited.set(Offset);
198
199 Ptr += sizeof(LinearFrameId);
200 CalleeGUID = CallerGUID;
201 }
202 }
203};
204
205// A convenience wrapper around FrameIdConverter and CallStackIdConverter for
206// tests.
210 : FrameIdConv(MemProfData.Frames),
211 CSIdConv(MemProfData.CallStacks, FrameIdConv) {}
212
213 // Delete the copy constructor and copy assignment operator to avoid a
214 // situation where a copy of IndexedCallstackIdConverter gets an error in
215 // LastUnmappedId while the original instance doesn't.
219
220 std::vector<Frame> operator()(CallStackId CSId) { return CSIdConv(CSId); }
221
224};
225
226struct FrameStat {
227 // The number of occurrences of a given FrameId.
229 // The sum of indexes where a given FrameId shows up.
231};
232
233// Compute a histogram of Frames in call stacks.
234template <typename FrameIdTy>
237 &MemProfCallStackData);
238
239// Construct a radix tree of call stacks.
240//
241// A set of call stacks might look like:
242//
243// CallStackId 1: f1 -> f2 -> f3
244// CallStackId 2: f1 -> f2 -> f4 -> f5
245// CallStackId 3: f1 -> f2 -> f4 -> f6
246// CallStackId 4: f7 -> f8 -> f9
247//
248// where each fn refers to a stack frame.
249//
250// Since we expect a lot of common prefixes, we can compress the call stacks
251// into a radix tree like:
252//
253// CallStackId 1: f1 -> f2 -> f3
254// |
255// CallStackId 2: +---> f4 -> f5
256// |
257// CallStackId 3: +---> f6
258//
259// CallStackId 4: f7 -> f8 -> f9
260//
261// Now, we are interested in retrieving call stacks for a given CallStackId, so
262// we just need a pointer from a given call stack to its parent. For example,
263// CallStackId 2 would point to CallStackId 1 as a parent.
264//
265// We serialize the radix tree above into a single array along with the length
266// of each call stack and pointers to the parent call stacks.
267//
268// Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
269// Array: L3 f9 f8 f7 L4 f6 J3 L4 f5 f4 J3 L3 f3 f2 f1
270// ^ ^ ^ ^
271// | | | |
272// CallStackId 4: 0 --+ | | |
273// CallStackId 3: 4 --------------+ | |
274// CallStackId 2: 7 -----------------------+ |
275// CallStackId 1: 11 -----------------------------------+
276//
277// - LN indicates the length of a call stack, encoded as ordinary integer N.
278//
279// - JN indicates a pointer to the parent, encoded as -N.
280//
281// The radix tree allows us to reconstruct call stacks in the leaf-to-root
282// order as we scan the array from left ro right while following pointers to
283// parents along the way.
284//
285// For example, if we are decoding CallStackId 2, we start a forward traversal
286// at Index 7, noting the call stack length of 4 and obtaining f5 and f4. When
287// we see J3 at Index 10, we resume a forward traversal at Index 13 = 10 + 3,
288// picking up f2 and f1. We are done after collecting 4 frames as indicated at
289// the beginning of the traversal.
290//
291// On-disk IndexedMemProfRecord will refer to call stacks by their indexes into
292// the radix tree array, so we do not explicitly encode mappings like:
293// "CallStackId 1 -> 11".
294template <typename FrameIdTy> class CallStackRadixTreeBuilder {
295 // The radix tree array.
296 std::vector<LinearFrameId> RadixArray;
297
298 // Mapping from CallStackIds to indexes into RadixArray.
300
301 // In build, we partition a given call stack into two parts -- the prefix
302 // that's common with the previously encoded call stack and the frames beyond
303 // the common prefix -- the unique portion. Then we want to find out where
304 // the common prefix is stored in RadixArray so that we can link the unique
305 // portion to the common prefix. Indexes, declared below, helps with our
306 // needs. Intuitively, Indexes tells us where each of the previously encoded
307 // call stack is stored in RadixArray. More formally, Indexes satisfies:
308 //
309 // RadixArray[Indexes[I]] == Prev[I]
310 //
311 // for every I, where Prev is the the call stack in the root-to-leaf order
312 // previously encoded by build. (Note that Prev, as passed to
313 // encodeCallStack, is in the leaf-to-root order.)
314 //
315 // For example, if the call stack being encoded shares 5 frames at the root of
316 // the call stack with the previously encoded call stack,
317 // RadixArray[Indexes[0]] is the root frame of the common prefix.
318 // RadixArray[Indexes[5 - 1]] is the last frame of the common prefix.
319 std::vector<LinearCallStackId> Indexes;
320
321 using CSIdPair = std::pair<CallStackId, llvm::SmallVector<FrameIdTy>>;
322
323 // Encode a call stack into RadixArray. Return the starting index within
324 // RadixArray.
325 LinearCallStackId encodeCallStack(
328 const llvm::DenseMap<FrameIdTy, LinearFrameId> *MemProfFrameIndexes);
329
330public:
332
333 // Build a radix tree array.
334 void
336 &&MemProfCallStackData,
337 const llvm::DenseMap<FrameIdTy, LinearFrameId> *MemProfFrameIndexes,
339
340 ArrayRef<LinearFrameId> getRadixArray() const { return RadixArray; }
341
343 return std::move(CallStackPos);
344 }
345};
346
347// Defined in MemProfRadixTree.cpp
349extern template class LLVM_TEMPLATE_ABI
351
352} // namespace memprof
353} // namespace llvm
354#endif // LLVM_PROFILEDATA_MEMPROFRADIXTREE_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
This file implements the BitVector class.
#define LLVM_TEMPLATE_ABI
Definition Compiler.h:212
#define F(x, y, z)
Definition MD5.cpp:54
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An efficient, type-erasing, non-owning reference to a callable.
void build(llvm::MapVector< CallStackId, llvm::SmallVector< FrameIdTy > > &&MemProfCallStackData, const llvm::DenseMap< FrameIdTy, LinearFrameId > *MemProfFrameIndexes, llvm::DenseMap< FrameIdTy, FrameStat > &FrameHistogram)
ArrayRef< LinearFrameId > getRadixArray() const
llvm::DenseMap< CallStackId, LinearCallStackId > takeCallStackPos()
Helper class to iterate through stack ids in both metadata (memprof MIB and callsite) and the corresp...
uint32_t LinearCallStackId
Definition MemProf.h:356
template class LLVM_TEMPLATE_ABI CallStackRadixTreeBuilder< FrameId >
uint32_t LinearFrameId
Definition MemProf.h:236
uint64_t CallStackId
Definition MemProf.h:353
uint64_t FrameId
Definition MemProf.h:234
template class LLVM_TEMPLATE_ABI CallStackRadixTreeBuilder< LinearFrameId >
llvm::DenseMap< FrameIdTy, FrameStat > computeFrameHistogram(llvm::MapVector< CallStackId, llvm::SmallVector< FrameIdTy > > &MemProfCallStackData)
value_type read(const void *memory, endianness endian)
Read a value of a particular endianness from memory.
Definition Endian.h:53
value_type readNext(const CharT *&memory, endianness endian)
Read a value of a particular endianness from a buffer, and increment the buffer past that value.
Definition Endian.h:67
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
CallStackIdConverter & operator=(const CallStackIdConverter &)=delete
llvm::function_ref< Frame(FrameId)> FrameIdToFrame
std::optional< CallStackId > LastUnmappedId
CallStackIdConverter(MapTy &Map, llvm::function_ref< Frame(FrameId)> FrameIdToFrame)
std::vector< Frame > operator()(CallStackId CSId)
CallStackIdConverter(const CallStackIdConverter &)=delete
void operator()(LinearCallStackId LinearCSId)
llvm::function_ref< Frame(LinearFrameId)> FrameIdToFrame
DenseMap< uint64_t, SmallVector< CallEdgeTy, 0 > > CallerCalleePairs
CallerCalleePairExtractor(const unsigned char *CallStackBase, llvm::function_ref< Frame(LinearFrameId)> FrameIdToFrame, unsigned RadixTreeSize)
FrameIdConverter(const FrameIdConverter &)=delete
std::optional< FrameId > LastUnmappedId
FrameIdConverter & operator=(const FrameIdConverter &)=delete
static constexpr size_t serializedSize()
Definition MemProf.h:336
static Frame deserialize(const unsigned char *Ptr)
Definition MemProf.h:321
CallStackIdConverter< decltype(IndexedMemProfData::CallStacks)> CSIdConv
std::vector< Frame > operator()(CallStackId CSId)
FrameIdConverter< decltype(IndexedMemProfData::Frames)> FrameIdConv
IndexedCallstackIdConverter(const IndexedCallstackIdConverter &)=delete
IndexedCallstackIdConverter & operator=(const IndexedCallstackIdConverter &)=delete
IndexedCallstackIdConverter(IndexedMemProfData &MemProfData)
llvm::MapVector< CallStackId, llvm::SmallVector< FrameId > > CallStacks
llvm::MapVector< FrameId, Frame > Frames
llvm::function_ref< Frame(LinearFrameId)> FrameIdToFrame
std::vector< Frame > operator()(LinearCallStackId LinearCSId)
LinearCallStackIdConverter(const unsigned char *CallStackBase, llvm::function_ref< Frame(LinearFrameId)> FrameIdToFrame)
LinearFrameIdConverter(const unsigned char *FrameBase)
Frame operator()(LinearFrameId LinearId)