|  | //===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // Generate a DOT file to represent the function call graph encountered in | 
|  | // the trace. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #ifndef XRAY_GRAPH_H | 
|  | #define XRAY_GRAPH_H | 
|  |  | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "func-id-helper.h" | 
|  | #include "xray-color-helper.h" | 
|  | #include "llvm/ADT/DenseMap.h" | 
|  | #include "llvm/ADT/SmallVector.h" | 
|  | #include "llvm/Support/Errc.h" | 
|  | #include "llvm/Support/Program.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include "llvm/XRay/Graph.h" | 
|  | #include "llvm/XRay/Trace.h" | 
|  | #include "llvm/XRay/XRayRecord.h" | 
|  |  | 
|  | namespace llvm { | 
|  | namespace xray { | 
|  |  | 
|  | /// A class encapsulating the logic related to analyzing XRay traces, producting | 
|  | /// Graphs from them and then exporting those graphs for review. | 
|  | class GraphRenderer { | 
|  | public: | 
|  | /// An enum for enumerating the various statistics gathered on latencies | 
|  | enum class StatType { NONE, COUNT, MIN, MED, PCT90, PCT99, MAX, SUM }; | 
|  |  | 
|  | /// An inner struct for common timing statistics information | 
|  | struct TimeStat { | 
|  | int64_t Count; | 
|  | double Min; | 
|  | double Median; | 
|  | double Pct90; | 
|  | double Pct99; | 
|  | double Max; | 
|  | double Sum; | 
|  |  | 
|  | std::string getString(StatType T) const; | 
|  | double getDouble(StatType T) const; | 
|  | }; | 
|  | using TimestampT = uint64_t; | 
|  |  | 
|  | /// An inner struct for storing edge attributes for our graph. Here the | 
|  | /// attributes are mainly function call statistics. | 
|  | /// | 
|  | /// FIXME: expand to contain more information eg call latencies. | 
|  | struct CallStats { | 
|  | TimeStat S; | 
|  | std::vector<TimestampT> Timings; | 
|  | }; | 
|  |  | 
|  | /// An Inner Struct for storing vertex attributes, at the moment just | 
|  | /// SymbolNames, however in future we could store bulk function statistics. | 
|  | /// | 
|  | /// FIXME: Store more attributes based on instrumentation map. | 
|  | struct FunctionStats { | 
|  | std::string SymbolName; | 
|  | TimeStat S = {}; | 
|  | }; | 
|  |  | 
|  | struct FunctionAttr { | 
|  | int32_t FuncId; | 
|  | uint64_t TSC; | 
|  | }; | 
|  |  | 
|  | using FunctionStack = SmallVector<FunctionAttr, 4>; | 
|  |  | 
|  | using PerThreadFunctionStackMap = DenseMap<uint32_t, FunctionStack>; | 
|  |  | 
|  | class GraphT : public Graph<FunctionStats, CallStats, int32_t> { | 
|  | public: | 
|  | TimeStat GraphEdgeMax = {}; | 
|  | TimeStat GraphVertexMax = {}; | 
|  | }; | 
|  |  | 
|  | GraphT G; | 
|  | using VertexIdentifier = typename decltype(G)::VertexIdentifier; | 
|  | using EdgeIdentifier = decltype(G)::EdgeIdentifier; | 
|  |  | 
|  | /// Use a Map to store the Function stack for each thread whilst building the | 
|  | /// graph. | 
|  | /// | 
|  | /// FIXME: Perhaps we can Build this into LatencyAccountant? or vise versa? | 
|  | PerThreadFunctionStackMap PerThreadFunctionStack; | 
|  |  | 
|  | /// Usefull object for getting human readable Symbol Names. | 
|  | FuncIdConversionHelper FuncIdHelper; | 
|  | bool DeduceSiblingCalls = false; | 
|  | TimestampT CurrentMaxTSC = 0; | 
|  |  | 
|  | /// A private function to help implement the statistic generation functions; | 
|  | template <typename U> | 
|  | void getStats(U begin, U end, GraphRenderer::TimeStat &S); | 
|  | void updateMaxStats(const TimeStat &S, TimeStat &M); | 
|  |  | 
|  | /// Calculates latency statistics for each edge and stores the data in the | 
|  | /// Graph | 
|  | void calculateEdgeStatistics(); | 
|  |  | 
|  | /// Calculates latency statistics for each vertex and stores the data in the | 
|  | /// Graph | 
|  | void calculateVertexStatistics(); | 
|  |  | 
|  | /// Normalises latency statistics for each edge and vertex by CycleFrequency; | 
|  | void normalizeStatistics(double CycleFrequency); | 
|  |  | 
|  | /// An object to color gradients | 
|  | ColorHelper CHelper; | 
|  |  | 
|  | public: | 
|  | /// Takes in a reference to a FuncIdHelper in order to have ready access to | 
|  | /// Symbol names. | 
|  | explicit GraphRenderer(const FuncIdConversionHelper &FuncIdHelper, bool DSC) | 
|  | : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC), | 
|  | CHelper(ColorHelper::SequentialScheme::OrRd) { | 
|  | G[0] = {}; | 
|  | } | 
|  |  | 
|  | /// Process an Xray record and expand the graph. | 
|  | /// | 
|  | /// This Function will return true on success, or false if records are not | 
|  | /// presented in per-thread call-tree DFS order. (That is for each thread the | 
|  | /// Records should be in order runtime on an ideal system.) | 
|  | /// | 
|  | /// FIXME: Make this more robust against small irregularities. | 
|  | Error accountRecord(const XRayRecord &Record); | 
|  |  | 
|  | const PerThreadFunctionStackMap &getPerThreadFunctionStack() const { | 
|  | return PerThreadFunctionStack; | 
|  | } | 
|  |  | 
|  | class Factory { | 
|  | public: | 
|  | bool KeepGoing; | 
|  | bool DeduceSiblingCalls; | 
|  | std::string InstrMap; | 
|  | ::llvm::xray::Trace Trace; | 
|  | Expected<GraphRenderer> getGraphRenderer(); | 
|  | }; | 
|  |  | 
|  | /// Output the Embedded graph in DOT format on \p OS, labeling the edges by | 
|  | /// \p T | 
|  | void exportGraphAsDOT(raw_ostream &OS, StatType EdgeLabel = StatType::NONE, | 
|  | StatType EdgeColor = StatType::NONE, | 
|  | StatType VertexLabel = StatType::NONE, | 
|  | StatType VertexColor = StatType::NONE); | 
|  |  | 
|  | /// Get a reference to the internal graph. | 
|  | const GraphT &getGraph() { return G; } | 
|  | }; | 
|  |  | 
|  | /// Vector Sum of TimeStats | 
|  | inline GraphRenderer::TimeStat operator+(const GraphRenderer::TimeStat &A, | 
|  | const GraphRenderer::TimeStat &B) { | 
|  | return {A.Count + B.Count, A.Min + B.Min,     A.Median + B.Median, | 
|  | A.Pct90 + B.Pct90, A.Pct99 + B.Pct99, A.Max + B.Max, | 
|  | A.Sum + B.Sum}; | 
|  | } | 
|  |  | 
|  | /// Vector Difference of Timestats | 
|  | inline GraphRenderer::TimeStat operator-(const GraphRenderer::TimeStat &A, | 
|  | const GraphRenderer::TimeStat &B) { | 
|  |  | 
|  | return {A.Count - B.Count, A.Min - B.Min,     A.Median - B.Median, | 
|  | A.Pct90 - B.Pct90, A.Pct99 - B.Pct99, A.Max - B.Max, | 
|  | A.Sum - B.Sum}; | 
|  | } | 
|  |  | 
|  | /// Scalar Diference of TimeStat and double | 
|  | inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A, | 
|  | double B) { | 
|  |  | 
|  | return {static_cast<int64_t>(A.Count / B), | 
|  | A.Min / B, | 
|  | A.Median / B, | 
|  | A.Pct90 / B, | 
|  | A.Pct99 / B, | 
|  | A.Max / B, | 
|  | A.Sum / B}; | 
|  | } | 
|  |  | 
|  | /// Scalar product of TimeStat and Double | 
|  | inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A, | 
|  | double B) { | 
|  | return {static_cast<int64_t>(A.Count * B), | 
|  | A.Min * B, | 
|  | A.Median * B, | 
|  | A.Pct90 * B, | 
|  | A.Pct99 * B, | 
|  | A.Max * B, | 
|  | A.Sum * B}; | 
|  | } | 
|  |  | 
|  | /// Scalar product of double TimeStat | 
|  | inline GraphRenderer::TimeStat operator*(double A, | 
|  | const GraphRenderer::TimeStat &B) { | 
|  | return B * A; | 
|  | } | 
|  |  | 
|  | /// Hadamard Product of TimeStats | 
|  | inline GraphRenderer::TimeStat operator*(const GraphRenderer::TimeStat &A, | 
|  | const GraphRenderer::TimeStat &B) { | 
|  | return {A.Count * B.Count, A.Min * B.Min,     A.Median * B.Median, | 
|  | A.Pct90 * B.Pct90, A.Pct99 * B.Pct99, A.Max * B.Max, | 
|  | A.Sum * B.Sum}; | 
|  | } | 
|  |  | 
|  | /// Hadamard Division of TimeStats | 
|  | inline GraphRenderer::TimeStat operator/(const GraphRenderer::TimeStat &A, | 
|  | const GraphRenderer::TimeStat &B) { | 
|  | return {A.Count / B.Count, A.Min / B.Min,     A.Median / B.Median, | 
|  | A.Pct90 / B.Pct90, A.Pct99 / B.Pct99, A.Max / B.Max, | 
|  | A.Sum / B.Sum}; | 
|  | } | 
|  | } // namespace xray | 
|  | } // namespace llvm | 
|  |  | 
|  | #endif // XRAY_GRAPH_H |