1343171Sdim//===- Profile.h - XRay Profile Abstraction -------------------------------===// 2343171Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6343171Sdim// 7343171Sdim//===----------------------------------------------------------------------===// 8343171Sdim// 9343171Sdim// Defines the XRay Profile class representing the latency profile generated by 10343171Sdim// XRay's profiling mode. 11343171Sdim// 12343171Sdim//===----------------------------------------------------------------------===// 13343171Sdim#ifndef LLVM_XRAY_PROFILE_H 14343171Sdim#define LLVM_XRAY_PROFILE_H 15343171Sdim 16343171Sdim#include "llvm/ADT/DenseMap.h" 17343171Sdim#include "llvm/ADT/SmallVector.h" 18343171Sdim#include "llvm/ADT/StringRef.h" 19343171Sdim#include "llvm/Support/Error.h" 20343171Sdim#include <list> 21343171Sdim#include <utility> 22343171Sdim#include <vector> 23343171Sdim 24343171Sdimnamespace llvm { 25343171Sdimnamespace xray { 26343171Sdim 27343171Sdimclass Profile; 28343171Sdim 29343171Sdim// We forward declare the Trace type for turning a Trace into a Profile. 30343171Sdimclass Trace; 31343171Sdim 32343171Sdim/// This function will attempt to load an XRay Profiling Mode profile from the 33343171Sdim/// provided |Filename|. 34343171Sdim/// 35343171Sdim/// For any errors encountered in the loading of the profile data from 36343171Sdim/// |Filename|, this function will return an Error condition appropriately. 37343171SdimExpected<Profile> loadProfile(StringRef Filename); 38343171Sdim 39343171Sdim/// This algorithm will merge two Profile instances into a single Profile 40343171Sdim/// instance, aggregating blocks by Thread ID. 41343171SdimProfile mergeProfilesByThread(const Profile &L, const Profile &R); 42343171Sdim 43343171Sdim/// This algorithm will merge two Profile instances into a single Profile 44343171Sdim/// instance, aggregating blocks by function call stack. 45343171SdimProfile mergeProfilesByStack(const Profile &L, const Profile &R); 46343171Sdim 47343171Sdim/// This function takes a Trace and creates a Profile instance from it. 48343171SdimExpected<Profile> profileFromTrace(const Trace &T); 49343171Sdim 50343171Sdim/// Profile instances are thread-compatible. 51343171Sdimclass Profile { 52343171Sdimpublic: 53343171Sdim using ThreadID = uint64_t; 54343171Sdim using PathID = unsigned; 55343171Sdim using FuncID = int32_t; 56343171Sdim 57343171Sdim struct Data { 58343171Sdim uint64_t CallCount; 59343171Sdim uint64_t CumulativeLocalTime; 60343171Sdim }; 61343171Sdim 62343171Sdim struct Block { 63343171Sdim ThreadID Thread; 64343171Sdim std::vector<std::pair<PathID, Data>> PathData; 65343171Sdim }; 66343171Sdim 67343171Sdim /// Provides a sequence of function IDs from a previously interned PathID. 68343171Sdim /// 69343171Sdim /// Returns an error if |P| had not been interned before into the Profile. 70343171Sdim /// 71343171Sdim Expected<std::vector<FuncID>> expandPath(PathID P) const; 72343171Sdim 73343171Sdim /// The stack represented in |P| must be in stack order (leaf to root). This 74343171Sdim /// will always return the same PathID for |P| that has the same sequence. 75343171Sdim PathID internPath(ArrayRef<FuncID> P); 76343171Sdim 77343171Sdim /// Appends a fully-formed Block instance into the Profile. 78343171Sdim /// 79343171Sdim /// Returns an error condition in the following cases: 80343171Sdim /// 81343171Sdim /// - The PathData component of the Block is empty 82343171Sdim /// 83343171Sdim Error addBlock(Block &&B); 84343171Sdim 85343171Sdim Profile() = default; 86343171Sdim ~Profile() = default; 87343171Sdim 88343171Sdim Profile(Profile &&O) noexcept 89343171Sdim : Blocks(std::move(O.Blocks)), NodeStorage(std::move(O.NodeStorage)), 90343171Sdim Roots(std::move(O.Roots)), PathIDMap(std::move(O.PathIDMap)), 91343171Sdim NextID(O.NextID) {} 92343171Sdim 93343171Sdim Profile &operator=(Profile &&O) noexcept { 94343171Sdim Blocks = std::move(O.Blocks); 95343171Sdim NodeStorage = std::move(O.NodeStorage); 96343171Sdim Roots = std::move(O.Roots); 97343171Sdim PathIDMap = std::move(O.PathIDMap); 98343171Sdim NextID = O.NextID; 99343171Sdim return *this; 100343171Sdim } 101343171Sdim 102343171Sdim Profile(const Profile &); 103343171Sdim Profile &operator=(const Profile &); 104343171Sdim 105343171Sdim friend void swap(Profile &L, Profile &R) { 106343171Sdim using std::swap; 107343171Sdim swap(L.Blocks, R.Blocks); 108343171Sdim swap(L.NodeStorage, R.NodeStorage); 109343171Sdim swap(L.Roots, R.Roots); 110343171Sdim swap(L.PathIDMap, R.PathIDMap); 111343171Sdim swap(L.NextID, R.NextID); 112343171Sdim } 113343171Sdim 114343171Sdimprivate: 115343171Sdim using BlockList = std::list<Block>; 116343171Sdim 117343171Sdim struct TrieNode { 118343171Sdim FuncID Func = 0; 119343171Sdim std::vector<TrieNode *> Callees{}; 120343171Sdim TrieNode *Caller = nullptr; 121343171Sdim PathID ID = 0; 122343171Sdim }; 123343171Sdim 124343171Sdim // List of blocks associated with a Profile. 125343171Sdim BlockList Blocks; 126343171Sdim 127343171Sdim // List of TrieNode elements we've seen. 128343171Sdim std::list<TrieNode> NodeStorage; 129343171Sdim 130343171Sdim // List of call stack roots. 131343171Sdim SmallVector<TrieNode *, 4> Roots; 132343171Sdim 133343171Sdim // Reverse mapping between a PathID to a TrieNode*. 134343171Sdim DenseMap<PathID, TrieNode *> PathIDMap; 135343171Sdim 136343171Sdim // Used to identify paths. 137343171Sdim PathID NextID = 1; 138343171Sdim 139343171Sdimpublic: 140343171Sdim using const_iterator = BlockList::const_iterator; 141343171Sdim const_iterator begin() const { return Blocks.begin(); } 142343171Sdim const_iterator end() const { return Blocks.end(); } 143343171Sdim bool empty() const { return Blocks.empty(); } 144343171Sdim}; 145343171Sdim 146343171Sdim} // namespace xray 147343171Sdim} // namespace llvm 148343171Sdim 149343171Sdim#endif 150