blob: e6e31cec2cd142625be03b3219c9494b409305da [file] [log] [blame]
Dean Michael Berris980d93d2018-05-15 00:42:36 +00001//===-- xray_function_call_trie.h ------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of XRay, a dynamic runtime instrumentation system.
11//
12// This file defines the interface for a function call trie.
13//
14//===----------------------------------------------------------------------===//
15#ifndef XRAY_FUNCTION_CALL_TRIE_H
16#define XRAY_FUNCTION_CALL_TRIE_H
17
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +000018#include "sanitizer_common/sanitizer_allocator_internal.h"
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +000019#include "xray_profiling_flags.h"
Dean Michael Berris980d93d2018-05-15 00:42:36 +000020#include "xray_segmented_array.h"
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +000021#include <memory> // For placement new.
Dean Michael Berris980d93d2018-05-15 00:42:36 +000022#include <utility>
Dean Michael Berris980d93d2018-05-15 00:42:36 +000023
24namespace __xray {
25
26/// A FunctionCallTrie represents the stack traces of XRay instrumented
27/// functions that we've encountered, where a node corresponds to a function and
28/// the path from the root to the node its stack trace. Each node in the trie
29/// will contain some useful values, including:
30///
31/// * The cumulative amount of time spent in this particular node/stack.
32/// * The number of times this stack has appeared.
33/// * A histogram of latencies for that particular node.
34///
35/// Each node in the trie will also contain a list of callees, represented using
36/// a Array<NodeIdPair> -- each NodeIdPair instance will contain the function
37/// ID of the callee, and a pointer to the node.
38///
39/// If we visualise this data structure, we'll find the following potential
40/// representation:
41///
42/// [function id node] -> [callees] [cumulative time]
43/// [call counter] [latency histogram]
44///
45/// As an example, when we have a function in this pseudocode:
46///
47/// func f(N) {
48/// g()
49/// h()
50/// for i := 1..N { j() }
51/// }
52///
53/// We may end up with a trie of the following form:
54///
55/// f -> [ g, h, j ] [...] [1] [...]
56/// g -> [ ... ] [...] [1] [...]
57/// h -> [ ... ] [...] [1] [...]
58/// j -> [ ... ] [...] [N] [...]
59///
60/// If for instance the function g() called j() like so:
61///
62/// func g() {
63/// for i := 1..10 { j() }
64/// }
65///
66/// We'll find the following updated trie:
67///
68/// f -> [ g, h, j ] [...] [1] [...]
69/// g -> [ j' ] [...] [1] [...]
70/// h -> [ ... ] [...] [1] [...]
71/// j -> [ ... ] [...] [N] [...]
72/// j' -> [ ... ] [...] [10] [...]
73///
74/// Note that we'll have a new node representing the path `f -> g -> j'` with
75/// isolated data. This isolation gives us a means of representing the stack
76/// traces as a path, as opposed to a key in a table. The alternative
77/// implementation here would be to use a separate table for the path, and use
78/// hashes of the path as an identifier to accumulate the information. We've
79/// moved away from this approach as it takes a lot of time to compute the hash
80/// every time we need to update a function's call information as we're handling
81/// the entry and exit events.
82///
83/// This approach allows us to maintain a shadow stack, which represents the
84/// currently executing path, and on function exits quickly compute the amount
85/// of time elapsed from the entry, then update the counters for the node
86/// already represented in the trie. This necessitates an efficient
87/// representation of the various data structures (the list of callees must be
88/// cache-aware and efficient to look up, and the histogram must be compact and
89/// quick to update) to enable us to keep the overheads of this implementation
90/// to the minimum.
91class FunctionCallTrie {
92public:
93 struct Node;
94
95 // We use a NodeIdPair type instead of a std::pair<...> to not rely on the
96 // standard library types in this header.
97 struct NodeIdPair {
98 Node *NodePtr;
99 int32_t FId;
100
101 // Constructor for inplace-construction.
102 NodeIdPair(Node *N, int32_t F) : NodePtr(N), FId(F) {}
103 };
104
105 using NodeIdPairArray = Array<NodeIdPair>;
106 using NodeIdPairAllocatorType = NodeIdPairArray::AllocatorType;
107
108 // A Node in the FunctionCallTrie gives us a list of callees, the cumulative
109 // number of times this node actually appeared, the cumulative amount of time
110 // for this particular node including its children call times, and just the
111 // local time spent on this node. Each Node will have the ID of the XRay
112 // instrumented function that it is associated to.
113 struct Node {
114 Node *Parent;
115 NodeIdPairArray Callees;
116 int64_t CallCount;
117 int64_t CumulativeLocalTime; // Typically in TSC deltas, not wall-time.
118 int32_t FId;
119
120 // We add a constructor here to allow us to inplace-construct through
121 // Array<...>'s AppendEmplace.
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000122 Node(Node *P, NodeIdPairAllocatorType &A, ChunkAllocator &CA, int64_t CC,
123 int64_t CLT, int32_t F)
124 : Parent(P), Callees(A, CA), CallCount(CC), CumulativeLocalTime(CLT),
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000125 FId(F) {}
126
127 // TODO: Include the compact histogram.
128 };
129
130private:
131 struct ShadowStackEntry {
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000132 uint64_t EntryTSC;
133 Node *NodePtr;
134
135 // We add a constructor here to allow us to inplace-construct through
136 // Array<...>'s AppendEmplace.
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000137 ShadowStackEntry(uint64_t T, Node *N) : EntryTSC{T}, NodePtr{N} {}
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000138 };
139
140 using NodeArray = Array<Node>;
141 using RootArray = Array<Node *>;
142 using ShadowStackArray = Array<ShadowStackEntry>;
143
144public:
145 // We collate the allocators we need into a single struct, as a convenience to
146 // allow us to initialize these as a group.
147 struct Allocators {
148 using NodeAllocatorType = NodeArray::AllocatorType;
149 using RootAllocatorType = RootArray::AllocatorType;
150 using ShadowStackAllocatorType = ShadowStackArray::AllocatorType;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000151
152 NodeAllocatorType *NodeAllocator = nullptr;
153 RootAllocatorType *RootAllocator = nullptr;
154 ShadowStackAllocatorType *ShadowStackAllocator = nullptr;
155 NodeIdPairAllocatorType *NodeIdPairAllocator = nullptr;
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000156 ChunkAllocator *ChunkAlloc = nullptr;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000157
158 Allocators() {}
159 Allocators(const Allocators &) = delete;
160 Allocators &operator=(const Allocators &) = delete;
161
162 Allocators(Allocators &&O)
163 : NodeAllocator(O.NodeAllocator), RootAllocator(O.RootAllocator),
164 ShadowStackAllocator(O.ShadowStackAllocator),
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000165 NodeIdPairAllocator(O.NodeIdPairAllocator), ChunkAlloc(O.ChunkAlloc) {
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000166 O.NodeAllocator = nullptr;
167 O.RootAllocator = nullptr;
168 O.ShadowStackAllocator = nullptr;
169 O.NodeIdPairAllocator = nullptr;
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000170 O.ChunkAlloc = nullptr;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000171 }
172
173 Allocators &operator=(Allocators &&O) {
174 {
175 auto Tmp = O.NodeAllocator;
176 O.NodeAllocator = this->NodeAllocator;
177 this->NodeAllocator = Tmp;
178 }
179 {
180 auto Tmp = O.RootAllocator;
181 O.RootAllocator = this->RootAllocator;
182 this->RootAllocator = Tmp;
183 }
184 {
185 auto Tmp = O.ShadowStackAllocator;
186 O.ShadowStackAllocator = this->ShadowStackAllocator;
187 this->ShadowStackAllocator = Tmp;
188 }
189 {
190 auto Tmp = O.NodeIdPairAllocator;
191 O.NodeIdPairAllocator = this->NodeIdPairAllocator;
192 this->NodeIdPairAllocator = Tmp;
193 }
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000194 {
195 auto Tmp = O.ChunkAlloc;
196 O.ChunkAlloc = this->ChunkAlloc;
197 this->ChunkAlloc = Tmp;
198 }
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000199 return *this;
200 }
201
202 ~Allocators() {
203 // Note that we cannot use delete on these pointers, as they need to be
204 // returned to the sanitizer_common library's internal memory tracking
205 // system.
206 if (NodeAllocator != nullptr) {
207 NodeAllocator->~NodeAllocatorType();
208 InternalFree(NodeAllocator);
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000209 NodeAllocator = nullptr;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000210 }
211 if (RootAllocator != nullptr) {
212 RootAllocator->~RootAllocatorType();
213 InternalFree(RootAllocator);
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000214 RootAllocator = nullptr;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000215 }
216 if (ShadowStackAllocator != nullptr) {
217 ShadowStackAllocator->~ShadowStackAllocatorType();
218 InternalFree(ShadowStackAllocator);
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000219 ShadowStackAllocator = nullptr;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000220 }
221 if (NodeIdPairAllocator != nullptr) {
222 NodeIdPairAllocator->~NodeIdPairAllocatorType();
223 InternalFree(NodeIdPairAllocator);
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000224 NodeIdPairAllocator = nullptr;
225 }
226 if (ChunkAlloc != nullptr) {
227 ChunkAlloc->~ChunkAllocator();
228 InternalFree(ChunkAlloc);
229 ChunkAlloc = nullptr;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000230 }
231 }
232 };
233
234 // TODO: Support configuration of options through the arguments.
235 static Allocators InitAllocators() {
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000236 return InitAllocatorsCustom(profilingFlags()->per_thread_allocator_max);
237 }
238
239 static Allocators InitAllocatorsCustom(uptr Max) {
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000240 Allocators A;
241 auto NodeAllocator = reinterpret_cast<Allocators::NodeAllocatorType *>(
242 InternalAlloc(sizeof(Allocators::NodeAllocatorType)));
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000243 new (NodeAllocator) Allocators::NodeAllocatorType(Max);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000244 A.NodeAllocator = NodeAllocator;
245
246 auto RootAllocator = reinterpret_cast<Allocators::RootAllocatorType *>(
247 InternalAlloc(sizeof(Allocators::RootAllocatorType)));
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000248 new (RootAllocator) Allocators::RootAllocatorType(Max);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000249 A.RootAllocator = RootAllocator;
250
251 auto ShadowStackAllocator =
252 reinterpret_cast<Allocators::ShadowStackAllocatorType *>(
253 InternalAlloc(sizeof(Allocators::ShadowStackAllocatorType)));
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000254 new (ShadowStackAllocator) Allocators::ShadowStackAllocatorType(Max);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000255 A.ShadowStackAllocator = ShadowStackAllocator;
256
Dean Michael Berrisd1fe5062018-05-31 05:25:47 +0000257 auto NodeIdPairAllocator = reinterpret_cast<NodeIdPairAllocatorType *>(
258 InternalAlloc(sizeof(NodeIdPairAllocatorType)));
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000259 new (NodeIdPairAllocator) NodeIdPairAllocatorType(Max);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000260 A.NodeIdPairAllocator = NodeIdPairAllocator;
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000261
262 auto ChunkAlloc = reinterpret_cast<ChunkAllocator *>(
263 InternalAlloc(sizeof(ChunkAllocator)));
264 new (ChunkAlloc) ChunkAllocator(Max);
265 A.ChunkAlloc = ChunkAlloc;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000266 return A;
267 }
268
269private:
270 NodeArray Nodes;
271 RootArray Roots;
272 ShadowStackArray ShadowStack;
273 NodeIdPairAllocatorType *NodeIdPairAllocator = nullptr;
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000274 ChunkAllocator *ChunkAlloc = nullptr;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000275
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000276public:
277 explicit FunctionCallTrie(const Allocators &A)
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000278 : Nodes(*A.NodeAllocator, *A.ChunkAlloc),
279 Roots(*A.RootAllocator, *A.ChunkAlloc),
280 ShadowStack(*A.ShadowStackAllocator, *A.ChunkAlloc),
281 NodeIdPairAllocator(A.NodeIdPairAllocator), ChunkAlloc(A.ChunkAlloc) {}
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000282
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000283 void enterFunction(const int32_t FId, uint64_t TSC) {
284 DCHECK_NE(FId, 0);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000285 // This function primarily deals with ensuring that the ShadowStack is
286 // consistent and ready for when an exit event is encountered.
287 if (UNLIKELY(ShadowStack.empty())) {
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000288 auto NewRoot = Nodes.AppendEmplace(nullptr, *NodeIdPairAllocator,
289 *ChunkAlloc, 0, 0, FId);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000290 if (UNLIKELY(NewRoot == nullptr))
291 return;
292 Roots.Append(NewRoot);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000293 ShadowStack.AppendEmplace(TSC, NewRoot);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000294 return;
295 }
296
297 auto &Top = ShadowStack.back();
298 auto TopNode = Top.NodePtr;
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000299 DCHECK_NE(TopNode, nullptr);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000300
301 // If we've seen this callee before, then we just access that node and place
302 // that on the top of the stack.
303 auto Callee = TopNode->Callees.find_element(
304 [FId](const NodeIdPair &NR) { return NR.FId == FId; });
305 if (Callee != nullptr) {
306 CHECK_NE(Callee->NodePtr, nullptr);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000307 ShadowStack.AppendEmplace(TSC, Callee->NodePtr);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000308 return;
309 }
310
311 // This means we've never seen this stack before, create a new node here.
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000312 auto NewNode = Nodes.AppendEmplace(TopNode, *NodeIdPairAllocator,
313 *ChunkAlloc, 0, 0, FId);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000314 if (UNLIKELY(NewNode == nullptr))
315 return;
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000316 DCHECK_NE(NewNode, nullptr);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000317 TopNode->Callees.AppendEmplace(NewNode, FId);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000318 ShadowStack.AppendEmplace(TSC, NewNode);
319 DCHECK_NE(ShadowStack.back().NodePtr, nullptr);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000320 return;
321 }
322
323 void exitFunction(int32_t FId, uint64_t TSC) {
324 // When we exit a function, we look up the ShadowStack to see whether we've
325 // entered this function before. We do as little processing here as we can,
326 // since most of the hard work would have already been done at function
327 // entry.
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000328 uint64_t CumulativeTreeTime = 0;
329 while (!ShadowStack.empty()) {
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000330 const auto &Top = ShadowStack.back();
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000331 auto TopNode = Top.NodePtr;
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000332 DCHECK_NE(TopNode, nullptr);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000333 auto LocalTime = TSC - Top.EntryTSC;
334 TopNode->CallCount++;
335 TopNode->CumulativeLocalTime += LocalTime - CumulativeTreeTime;
336 CumulativeTreeTime += LocalTime;
337 ShadowStack.trim(1);
338
339 // TODO: Update the histogram for the node.
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000340 if (TopNode->FId == FId)
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000341 break;
342 }
343 }
344
345 const RootArray &getRoots() const { return Roots; }
346
347 // The deepCopyInto operation will update the provided FunctionCallTrie by
348 // re-creating the contents of this particular FunctionCallTrie in the other
349 // FunctionCallTrie. It will do this using a Depth First Traversal from the
350 // roots, and while doing so recreating the traversal in the provided
351 // FunctionCallTrie.
352 //
353 // This operation will *not* destroy the state in `O`, and thus may cause some
354 // duplicate entries in `O` if it is not empty.
355 //
356 // This function is *not* thread-safe, and may require external
357 // synchronisation of both "this" and |O|.
358 //
359 // This function must *not* be called with a non-empty FunctionCallTrie |O|.
360 void deepCopyInto(FunctionCallTrie &O) const {
361 DCHECK(O.getRoots().empty());
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000362
363 // We then push the root into a stack, to use as the parent marker for new
364 // nodes we push in as we're traversing depth-first down the call tree.
365 struct NodeAndParent {
366 FunctionCallTrie::Node *Node;
367 FunctionCallTrie::Node *NewNode;
368 };
369 using Stack = Array<NodeAndParent>;
370
371 typename Stack::AllocatorType StackAllocator(
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000372 profilingFlags()->stack_allocator_max);
373 ChunkAllocator StackChunkAllocator(profilingFlags()->stack_allocator_max);
374 Stack DFSStack(StackAllocator, StackChunkAllocator);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000375
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000376 for (const auto Root : getRoots()) {
377 // Add a node in O for this root.
378 auto NewRoot = O.Nodes.AppendEmplace(
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000379 nullptr, *O.NodeIdPairAllocator, *O.ChunkAlloc, Root->CallCount,
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000380 Root->CumulativeLocalTime, Root->FId);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000381
382 // Because we cannot allocate more memory we should bail out right away.
383 if (UNLIKELY(NewRoot == nullptr))
384 return;
385
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000386 O.Roots.Append(NewRoot);
387
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000388 // TODO: Figure out what to do if we fail to allocate any more stack
389 // space. Maybe warn or report once?
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000390 DFSStack.AppendEmplace(Root, NewRoot);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000391 while (!DFSStack.empty()) {
392 NodeAndParent NP = DFSStack.back();
393 DCHECK_NE(NP.Node, nullptr);
394 DCHECK_NE(NP.NewNode, nullptr);
395 DFSStack.trim(1);
396 for (const auto Callee : NP.Node->Callees) {
397 auto NewNode = O.Nodes.AppendEmplace(
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000398 NP.NewNode, *O.NodeIdPairAllocator, *O.ChunkAlloc,
399 Callee.NodePtr->CallCount, Callee.NodePtr->CumulativeLocalTime,
400 Callee.FId);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000401 if (UNLIKELY(NewNode == nullptr))
402 return;
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000403 NP.NewNode->Callees.AppendEmplace(NewNode, Callee.FId);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000404 DFSStack.AppendEmplace(Callee.NodePtr, NewNode);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000405 }
406 }
407 }
408 }
409
410 // The mergeInto operation will update the provided FunctionCallTrie by
411 // traversing the current trie's roots and updating (i.e. merging) the data in
412 // the nodes with the data in the target's nodes. If the node doesn't exist in
413 // the provided trie, we add a new one in the right position, and inherit the
414 // data from the original (current) trie, along with all its callees.
415 //
416 // This function is *not* thread-safe, and may require external
417 // synchronisation of both "this" and |O|.
418 void mergeInto(FunctionCallTrie &O) const {
419 struct NodeAndTarget {
420 FunctionCallTrie::Node *OrigNode;
421 FunctionCallTrie::Node *TargetNode;
422 };
423 using Stack = Array<NodeAndTarget>;
424 typename Stack::AllocatorType StackAllocator(
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000425 profilingFlags()->stack_allocator_max);
426 ChunkAllocator CA(profilingFlags()->stack_allocator_max);
427 Stack DFSStack(StackAllocator, CA);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000428
429 for (const auto Root : getRoots()) {
430 Node *TargetRoot = nullptr;
431 auto R = O.Roots.find_element(
432 [&](const Node *Node) { return Node->FId == Root->FId; });
433 if (R == nullptr) {
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000434 TargetRoot = O.Nodes.AppendEmplace(nullptr, *O.NodeIdPairAllocator,
435 *O.ChunkAlloc, 0, 0, Root->FId);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000436 if (UNLIKELY(TargetRoot == nullptr))
437 return;
438
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000439 O.Roots.Append(TargetRoot);
440 } else {
441 TargetRoot = *R;
442 }
443
444 DFSStack.Append(NodeAndTarget{Root, TargetRoot});
445 while (!DFSStack.empty()) {
446 NodeAndTarget NT = DFSStack.back();
447 DCHECK_NE(NT.OrigNode, nullptr);
448 DCHECK_NE(NT.TargetNode, nullptr);
449 DFSStack.trim(1);
450 // TODO: Update the histogram as well when we have it ready.
451 NT.TargetNode->CallCount += NT.OrigNode->CallCount;
452 NT.TargetNode->CumulativeLocalTime += NT.OrigNode->CumulativeLocalTime;
453 for (const auto Callee : NT.OrigNode->Callees) {
454 auto TargetCallee = NT.TargetNode->Callees.find_element(
455 [&](const FunctionCallTrie::NodeIdPair &C) {
456 return C.FId == Callee.FId;
457 });
458 if (TargetCallee == nullptr) {
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000459 auto NewTargetNode =
460 O.Nodes.AppendEmplace(NT.TargetNode, *O.NodeIdPairAllocator,
461 *O.ChunkAlloc, 0, 0, Callee.FId);
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000462
463 if (UNLIKELY(NewTargetNode == nullptr))
464 return;
465
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000466 TargetCallee =
467 NT.TargetNode->Callees.AppendEmplace(NewTargetNode, Callee.FId);
468 }
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000469 DFSStack.AppendEmplace(Callee.NodePtr, TargetCallee->NodePtr);
Dean Michael Berris980d93d2018-05-15 00:42:36 +0000470 }
471 }
472 }
473 }
474};
475
476} // namespace __xray
477
478#endif // XRAY_FUNCTION_CALL_TRIE_H