1//===- xray-account.h - XRay Function Call Accounting ---------------------===//
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// This file defines the interface for performing some basic function call
10// accounting from an XRay trace.
11//
12//===----------------------------------------------------------------------===//
13#ifndef LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H
14#define LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H
15
16#include <utility>
17
18#include "func-id-helper.h"
19#include "llvm/ADT/Bitfields.h"
20#include "llvm/Support/Program.h"
21#include "llvm/Support/raw_ostream.h"
22#include "llvm/XRay/XRayRecord.h"
23
24namespace llvm {
25namespace xray {
26
27class LatencyAccountant {
28public:
29 typedef llvm::DenseMap<int32_t, llvm::SmallVector<uint64_t, 0>>
30 FunctionLatencyMap;
31 typedef llvm::DenseMap<uint32_t, std::pair<uint64_t, uint64_t>>
32 PerThreadMinMaxTSCMap;
33 typedef llvm::DenseMap<uint8_t, std::pair<uint64_t, uint64_t>>
34 PerCPUMinMaxTSCMap;
35 struct FunctionStack {
36 llvm::SmallVector<std::pair<int32_t, uint64_t>, 32> Stack;
37 class RecursionStatus {
38 uint32_t Storage = 0;
39 using Depth = Bitfield::Element<int32_t, 0, 31>; // Low 31 bits.
40 using IsRecursive = Bitfield::Element<bool, 31, 1>; // Sign bit.
41 public:
42 RecursionStatus &operator++();
43 RecursionStatus &operator--();
44 bool isRecursive() const;
45 };
46 std::optional<llvm::DenseMap<int32_t, RecursionStatus>> RecursionDepth;
47 };
48 typedef llvm::DenseMap<uint32_t, FunctionStack> PerThreadFunctionStackMap;
49
50private:
51 PerThreadFunctionStackMap PerThreadFunctionStack;
52 FunctionLatencyMap FunctionLatencies;
53 PerThreadMinMaxTSCMap PerThreadMinMaxTSC;
54 PerCPUMinMaxTSCMap PerCPUMinMaxTSC;
55 FuncIdConversionHelper &FuncIdHelper;
56
57 bool RecursiveCallsOnly = false;
58 bool DeduceSiblingCalls = false;
59 uint64_t CurrentMaxTSC = 0;
60
61 void recordLatency(int32_t FuncId, uint64_t Latency) {
62 FunctionLatencies[FuncId].push_back(Elt: Latency);
63 }
64
65public:
66 explicit LatencyAccountant(FuncIdConversionHelper &FuncIdHelper,
67 bool RecursiveCallsOnly, bool DeduceSiblingCalls)
68 : FuncIdHelper(FuncIdHelper), RecursiveCallsOnly(RecursiveCallsOnly),
69 DeduceSiblingCalls(DeduceSiblingCalls) {}
70
71 const FunctionLatencyMap &getFunctionLatencies() const {
72 return FunctionLatencies;
73 }
74
75 const PerThreadMinMaxTSCMap &getPerThreadMinMaxTSC() const {
76 return PerThreadMinMaxTSC;
77 }
78
79 const PerCPUMinMaxTSCMap &getPerCPUMinMaxTSC() const {
80 return PerCPUMinMaxTSC;
81 }
82
83 /// Returns false in case we fail to account the provided record. This happens
84 /// in the following cases:
85 ///
86 /// - An exit record does not match any entry records for the same function.
87 /// If we've been set to deduce sibling calls, we try walking up the stack
88 /// and recording times for the higher level functions.
89 /// - A record has a TSC that's before the latest TSC that has been
90 /// recorded. We still record the TSC for the min-max.
91 ///
92 bool accountRecord(const XRayRecord &Record);
93
94 const PerThreadFunctionStackMap &getPerThreadFunctionStack() const {
95 return PerThreadFunctionStack;
96 }
97
98 // Output Functions
99 // ================
100
101 void exportStatsAsText(raw_ostream &OS, const XRayFileHeader &Header) const;
102 void exportStatsAsCSV(raw_ostream &OS, const XRayFileHeader &Header) const;
103
104private:
105 // Internal helper to implement common parts of the exportStatsAs...
106 // functions.
107 template <class F> void exportStats(const XRayFileHeader &Header, F fn) const;
108};
109
110} // namespace xray
111} // namespace llvm
112
113#endif // LLVM_TOOLS_LLVM_XRAY_XRAY_ACCOUNT_H
114