reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
//===-- 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