xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-xray/trie-node.h (revision e25152834cdf3b353892835a4f3b157e066a8ed4)
10b57cec5SDimitry Andric //===- trie-node.h - XRay Call Stack Data Structure -----------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file provides a data structure and routines for working with call stacks
100b57cec5SDimitry Andric // of instrumented functions.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #ifndef LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H
150b57cec5SDimitry Andric #define LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include <forward_list>
180b57cec5SDimitry Andric #include <numeric>
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
210b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
220b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric /// A type to represent a trie of invocations. It is useful to construct a
250b57cec5SDimitry Andric /// graph of these nodes from reading an XRay trace, such that each function
260b57cec5SDimitry Andric /// call can be placed in a larger context.
270b57cec5SDimitry Andric ///
280b57cec5SDimitry Andric /// The template parameter allows users of the template to attach their own
290b57cec5SDimitry Andric /// data elements to each node in the invocation graph.
300b57cec5SDimitry Andric template <typename AssociatedData> struct TrieNode {
310b57cec5SDimitry Andric   /// The function ID.
320b57cec5SDimitry Andric   int32_t FuncId;
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric   /// The caller of this function.
350b57cec5SDimitry Andric   TrieNode<AssociatedData> *Parent;
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric   /// The callees from this function.
380b57cec5SDimitry Andric   llvm::SmallVector<TrieNode<AssociatedData> *, 4> Callees;
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric   /// Additional parameterized data on each node.
410b57cec5SDimitry Andric   AssociatedData ExtraData;
420b57cec5SDimitry Andric };
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric /// Merges together two TrieNodes with like function ids, aggregating their
450b57cec5SDimitry Andric /// callee lists and durations. The caller must provide storage where new merged
460b57cec5SDimitry Andric /// nodes can be allocated in the form of a linked list.
470b57cec5SDimitry Andric template <typename T, typename Callable>
480b57cec5SDimitry Andric TrieNode<T> *
mergeTrieNodes(const TrieNode<T> & Left,const TrieNode<T> & Right,std::remove_reference_t<TrieNode<T> * > NewParent,std::forward_list<TrieNode<T>> & NodeStore,Callable && MergeCallable)490b57cec5SDimitry Andric mergeTrieNodes(const TrieNode<T> &Left, const TrieNode<T> &Right,
500b57cec5SDimitry Andric                /*Non-deduced pointer type for nullptr compatibility*/
51*5ffd83dbSDimitry Andric                std::remove_reference_t<TrieNode<T> *> NewParent,
520b57cec5SDimitry Andric                std::forward_list<TrieNode<T>> &NodeStore,
530b57cec5SDimitry Andric                Callable &&MergeCallable) {
540b57cec5SDimitry Andric   llvm::function_ref<T(const T &, const T &)> MergeFn(
550b57cec5SDimitry Andric       std::forward<Callable>(MergeCallable));
560b57cec5SDimitry Andric   assert(Left.FuncId == Right.FuncId);
570b57cec5SDimitry Andric   NodeStore.push_front(TrieNode<T>{
580b57cec5SDimitry Andric       Left.FuncId, NewParent, {}, MergeFn(Left.ExtraData, Right.ExtraData)});
590b57cec5SDimitry Andric   auto I = NodeStore.begin();
600b57cec5SDimitry Andric   auto *Node = &*I;
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric   // Build a map of callees from the left side.
630b57cec5SDimitry Andric   llvm::DenseMap<int32_t, TrieNode<T> *> LeftCalleesByFuncId;
640b57cec5SDimitry Andric   for (auto *Callee : Left.Callees) {
650b57cec5SDimitry Andric     LeftCalleesByFuncId[Callee->FuncId] = Callee;
660b57cec5SDimitry Andric   }
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric   // Iterate through the right side, either merging with the map values or
690b57cec5SDimitry Andric   // directly adding to the Callees vector. The iteration also removes any
700b57cec5SDimitry Andric   // merged values from the left side map.
710b57cec5SDimitry Andric   // TODO: Unroll into iterative and explicit stack for efficiency.
720b57cec5SDimitry Andric   for (auto *Callee : Right.Callees) {
730b57cec5SDimitry Andric     auto iter = LeftCalleesByFuncId.find(Callee->FuncId);
740b57cec5SDimitry Andric     if (iter != LeftCalleesByFuncId.end()) {
750b57cec5SDimitry Andric       Node->Callees.push_back(
760b57cec5SDimitry Andric           mergeTrieNodes(*(iter->second), *Callee, Node, NodeStore, MergeFn));
770b57cec5SDimitry Andric       LeftCalleesByFuncId.erase(iter);
780b57cec5SDimitry Andric     } else {
790b57cec5SDimitry Andric       Node->Callees.push_back(Callee);
800b57cec5SDimitry Andric     }
810b57cec5SDimitry Andric   }
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric   // Add any callees that weren't found in the right side.
840b57cec5SDimitry Andric   for (auto MapPairIter : LeftCalleesByFuncId) {
850b57cec5SDimitry Andric     Node->Callees.push_back(MapPairIter.second);
860b57cec5SDimitry Andric   }
870b57cec5SDimitry Andric 
880b57cec5SDimitry Andric   return Node;
890b57cec5SDimitry Andric }
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric #endif // LLVM_TOOLS_LLVM_XRAY_STACK_TRIE_H
92