diff --git a/include/llvm/XRay/Graph.h b/include/llvm/XRay/Graph.h deleted file mode 100644 index a4d34a8a4be..00000000000 --- a/include/llvm/XRay/Graph.h +++ /dev/null @@ -1,494 +0,0 @@ -//===-- Graph.h - XRay Graph Class ------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// A Graph Datatype for XRay. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_XRAY_GRAPH_T_H -#define LLVM_XRAY_GRAPH_T_H - -#include -#include -#include -#include - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/iterator.h" -#include "llvm/Support/Error.h" - -namespace llvm { -namespace xray { - -/// A Graph object represents a Directed Graph and is used in XRay to compute -/// and store function call graphs and associated statistical information. -/// -/// The graph takes in four template parameters, these are: -/// - VertexAttribute, this is a structure which is stored for each vertex. -/// Must be DefaultConstructible, CopyConstructible, CopyAssignable and -/// Destructible. -/// - EdgeAttribute, this is a structure which is stored for each edge -/// Must be DefaultConstructible, CopyConstructible, CopyAssignable and -/// Destructible. -/// - EdgeAttribute, this is a structure which is stored for each variable -/// - VI, this is a type over which DenseMapInfo is defined and is the type -/// used look up strings, available as VertexIdentifier. -/// - If the built in DenseMapInfo is not defined, provide a specialization -/// class type here. -/// -/// Graph is CopyConstructible, CopyAssignable, MoveConstructible and -/// MoveAssignable but is not EqualityComparible or LessThanComparible. -/// -/// Usage Example Graph with weighted edges and vertices: -/// Graph G; -/// -/// G[1] = 0; -/// G[2] = 2; -/// G[{1,2}] = 1; -/// G[{2,1}] = -1; -/// for(const auto &v : G.vertices()){ -/// // Do something with the vertices in the graph; -/// } -/// for(const auto &e : G.edges()){ -/// // Do something with the edges in the graph; -/// } -/// -/// Usage Example with StrRef keys. -/// Graph StrG; -/// char va[] = "Vertex A"; -/// char vaa[] = "Vertex A"; -/// char vb[] = "Vertex B"; // Vertices are referenced by String Refs. -/// G[va] = 0; -/// G[vb] = 1; -/// G[{va, vb}] = 1.0; -/// cout() << G[vaa] << " " << G[{vaa, vb}]; //prints "0 1.0". -/// -template -class Graph { -public: - /// These objects are used to name edges and vertices in the graph. - typedef VI VertexIdentifier; - typedef std::pair EdgeIdentifier; - - /// This type is the value_type of all iterators which range over vertices, - /// Determined by the Vertices DenseMap - using VertexValueType = - detail::DenseMapPair; - - /// This type is the value_type of all iterators which range over edges, - /// Determined by the Edges DenseMap. - using EdgeValueType = detail::DenseMapPair; - - using size_type = std::size_t; - -private: - /// The type used for storing the EdgeAttribute for each edge in the graph - using EdgeMapT = DenseMap; - - /// The type used for storing the VertexAttribute for each vertex in - /// the graph. - using VertexMapT = DenseMap; - - /// The type used for storing the edges entering a vertex. Indexed by - /// the VertexIdentifier of the start of the edge. Only used to determine - /// where the incoming edges are, the EdgeIdentifiers are stored in an - /// InnerEdgeMapT. - using NeighborSetT = DenseSet; - - /// The type storing the InnerInvGraphT corresponding to each vertex in - /// the graph (When a vertex has an incoming edge incident to it) - using NeighborLookupT = DenseMap; - -private: - /// Stores the map from the start and end vertex of an edge to it's - /// EdgeAttribute - EdgeMapT Edges; - - /// Stores the map from VertexIdentifier to VertexAttribute - VertexMapT Vertices; - - /// Allows fast lookup for the incoming edge set of any given vertex. - NeighborLookupT InNeighbors; - - /// Allows fast lookup for the outgoing edge set of any given vertex. - NeighborLookupT OutNeighbors; - - /// An Iterator adapter using an InnerInvGraphT::iterator as a base iterator, - /// and storing the VertexIdentifier the iterator range comes from. The - /// dereference operator is then performed using a pointer to the graph's edge - /// set. - template ::type> - class NeighborEdgeIteratorT - : public iterator_adaptor_base< - NeighborEdgeIteratorT, BaseIt, - typename std::iterator_traits::iterator_category, T> { - using InternalEdgeMapT = - typename std::conditional::type; - - friend class NeighborEdgeIteratorT; - friend class NeighborEdgeIteratorT; - - InternalEdgeMapT *MP; - VertexIdentifier SI; - - public: - template ::type> - operator NeighborEdgeIteratorT() const { - return NeighborEdgeIteratorT(this->I, MP, SI); - } - - NeighborEdgeIteratorT() = default; - NeighborEdgeIteratorT(BaseIt _I, InternalEdgeMapT *_MP, - VertexIdentifier _SI) - : iterator_adaptor_base< - NeighborEdgeIteratorT, BaseIt, - typename std::iterator_traits::iterator_category, T>(_I), - MP(_MP), SI(_SI) {} - - T &operator*() const { - if (!IsOut) - return *(MP->find({*(this->I), SI})); - else - return *(MP->find({SI, *(this->I)})); - } - }; - -public: - /// A const iterator type for iterating through the set of edges entering a - /// vertex. - /// - /// Has a const EdgeValueType as its value_type - using ConstInEdgeIterator = NeighborEdgeIteratorT; - - /// An iterator type for iterating through the set of edges leaving a vertex. - /// - /// Has an EdgeValueType as its value_type - using InEdgeIterator = NeighborEdgeIteratorT; - - /// A const iterator type for iterating through the set of edges entering a - /// vertex. - /// - /// Has a const EdgeValueType as its value_type - using ConstOutEdgeIterator = NeighborEdgeIteratorT; - - /// An iterator type for iterating through the set of edges leaving a vertex. - /// - /// Has an EdgeValueType as its value_type - using OutEdgeIterator = NeighborEdgeIteratorT; - - /// A class for ranging over the incoming edges incident to a vertex. - /// - /// Like all views in this class it provides methods to get the beginning and - /// past the range iterators for the range, as well as methods to determine - /// the number of elements in the range and whether the range is empty. - template class InOutEdgeView { - public: - using iterator = NeighborEdgeIteratorT; - using const_iterator = NeighborEdgeIteratorT; - using GraphT = typename std::conditional::type; - using InternalEdgeMapT = - typename std::conditional::type; - - private: - InternalEdgeMapT &M; - const VertexIdentifier A; - const NeighborLookupT &NL; - - public: - iterator begin() { - auto It = NL.find(A); - if (It == NL.end()) - return iterator(); - return iterator(It->second.begin(), &M, A); - } - - const_iterator cbegin() const { - auto It = NL.find(A); - if (It == NL.end()) - return const_iterator(); - return const_iterator(It->second.begin(), &M, A); - } - - const_iterator begin() const { return cbegin(); } - - iterator end() { - auto It = NL.find(A); - if (It == NL.end()) - return iterator(); - return iterator(It->second.end(), &M, A); - } - const_iterator cend() const { - auto It = NL.find(A); - if (It == NL.end()) - return const_iterator(); - return const_iterator(It->second.end(), &M, A); - } - - const_iterator end() const { return cend(); } - - size_type size() const { - auto I = NL.find(A); - if (I == NL.end()) - return 0; - else - return I->second.size(); - } - - bool empty() const { return NL.count(A) == 0; }; - - InOutEdgeView(GraphT &G, VertexIdentifier A) - : M(G.Edges), A(A), NL(isOut ? G.OutNeighbors : G.InNeighbors) {} - }; - - /// A const iterator type for iterating through the whole vertex set of the - /// graph. - /// - /// Has a const VertexValueType as its value_type - using ConstVertexIterator = typename VertexMapT::const_iterator; - - /// An iterator type for iterating through the whole vertex set of the graph. - /// - /// Has a VertexValueType as its value_type - using VertexIterator = typename VertexMapT::iterator; - - /// A class for ranging over the vertices in the graph. - /// - /// Like all views in this class it provides methods to get the beginning and - /// past the range iterators for the range, as well as methods to determine - /// the number of elements in the range and whether the range is empty. - template class VertexView { - public: - using iterator = typename std::conditional::type; - using const_iterator = ConstVertexIterator; - using GraphT = typename std::conditional::type; - - private: - GraphT &G; - - public: - iterator begin() { return G.Vertices.begin(); } - iterator end() { return G.Vertices.end(); } - const_iterator cbegin() const { return G.Vertices.cbegin(); } - const_iterator cend() const { return G.Vertices.cend(); } - const_iterator begin() const { return G.Vertices.begin(); } - const_iterator end() const { return G.Vertices.end(); } - size_type size() const { return G.Vertices.size(); } - bool empty() const { return G.Vertices.empty(); } - VertexView(GraphT &_G) : G(_G) {} - }; - - /// A const iterator for iterating through the entire edge set of the graph. - /// - /// Has a const EdgeValueType as its value_type - using ConstEdgeIterator = typename EdgeMapT::const_iterator; - - /// An iterator for iterating through the entire edge set of the graph. - /// - /// Has an EdgeValueType as its value_type - using EdgeIterator = typename EdgeMapT::iterator; - - /// A class for ranging over all the edges in the graph. - /// - /// Like all views in this class it provides methods to get the beginning and - /// past the range iterators for the range, as well as methods to determine - /// the number of elements in the range and whether the range is empty. - template class EdgeView { - public: - using iterator = typename std::conditional::type; - using const_iterator = ConstEdgeIterator; - using GraphT = typename std::conditional::type; - - private: - GraphT &G; - - public: - iterator begin() { return G.Edges.begin(); } - iterator end() { return G.Edges.end(); } - const_iterator cbegin() const { return G.Edges.cbegin(); } - const_iterator cend() const { return G.Edges.cend(); } - const_iterator begin() const { return G.Edges.begin(); } - const_iterator end() const { return G.Edges.end(); } - size_type size() const { return G.Edges.size(); } - bool empty() const { return G.Edges.empty(); } - EdgeView(GraphT &_G) : G(_G) {} - }; - -public: - // TODO: implement constructor to enable Graph Initialisation.\ - // Something like: - // Graph G( - // {1, 2, 3, 4, 5}, - // {{1, 2}, {2, 3}, {3, 4}}); - - /// Empty the Graph - void clear() { - Edges.clear(); - Vertices.clear(); - InNeighbors.clear(); - OutNeighbors.clear(); - } - - /// Returns a view object allowing iteration over the vertices of the graph. - /// also allows access to the size of the vertex set. - VertexView vertices() { return VertexView(*this); } - - VertexView vertices() const { return VertexView(*this); } - - /// Returns a view object allowing iteration over the edges of the graph. - /// also allows access to the size of the edge set. - EdgeView edges() { return EdgeView(*this); } - - EdgeView edges() const { return EdgeView(*this); } - - /// Returns a view object allowing iteration over the edges which start at - /// a vertex I. - InOutEdgeView outEdges(const VertexIdentifier I) { - return InOutEdgeView(*this, I); - } - - InOutEdgeView outEdges(const VertexIdentifier I) const { - return InOutEdgeView(*this, I); - } - - /// Returns a view object allowing iteration over the edges which point to - /// a vertex I. - InOutEdgeView inEdges(const VertexIdentifier I) { - return InOutEdgeView(*this, I); - } - - InOutEdgeView inEdges(const VertexIdentifier I) const { - return InOutEdgeView(*this, I); - } - - /// Looks up the vertex with identifier I, if it does not exist it default - /// constructs it. - VertexAttribute &operator[](const VertexIdentifier &I) { - return Vertices.FindAndConstruct(I).second; - } - - /// Looks up the edge with identifier I, if it does not exist it default - /// constructs it, if it's endpoints do not exist it also default constructs - /// them. - EdgeAttribute &operator[](const EdgeIdentifier &I) { - auto &P = Edges.FindAndConstruct(I); - Vertices.FindAndConstruct(I.first); - Vertices.FindAndConstruct(I.second); - InNeighbors[I.second].insert(I.first); - OutNeighbors[I.first].insert(I.second); - return P.second; - } - - /// Looks up a vertex with Identifier I, or an error if it does not exist. - Expected at(const VertexIdentifier &I) { - auto It = Vertices.find(I); - if (It == Vertices.end()) - return make_error( - "Vertex Identifier Does Not Exist", - std::make_error_code(std::errc::invalid_argument)); - return It->second; - } - - Expected at(const VertexIdentifier &I) const { - auto It = Vertices.find(I); - if (It == Vertices.end()) - return make_error( - "Vertex Identifier Does Not Exist", - std::make_error_code(std::errc::invalid_argument)); - return It->second; - } - - /// Looks up an edge with Identifier I, or an error if it does not exist. - Expected at(const EdgeIdentifier &I) { - auto It = Edges.find(I); - if (It == Edges.end()) - return make_error( - "Edge Identifier Does Not Exist", - std::make_error_code(std::errc::invalid_argument)); - return It->second; - } - - Expected at(const EdgeIdentifier &I) const { - auto It = Edges.find(I); - if (It == Edges.end()) - return make_error( - "Edge Identifier Does Not Exist", - std::make_error_code(std::errc::invalid_argument)); - return It->second; - } - - /// Looks for a vertex with identifier I, returns 1 if one exists, and - /// 0 otherwise - size_type count(const VertexIdentifier &I) const { - return Vertices.count(I); - } - - /// Looks for an edge with Identifier I, returns 1 if one exists and 0 - /// otherwise - size_type count(const EdgeIdentifier &I) const { return Edges.count(I); } - - /// Inserts a vertex into the graph with Identifier Val.first, and - /// Attribute Val.second. - std::pair - insert(const std::pair &Val) { - return Vertices.insert(Val); - } - - std::pair - insert(std::pair &&Val) { - return Vertices.insert(std::move(Val)); - } - - /// Inserts an edge into the graph with Identifier Val.first, and - /// Attribute Val.second. If the key is already in the map, it returns false - /// and doesn't update the value. - std::pair - insert(const std::pair &Val) { - const auto &p = Edges.insert(Val); - if (p.second) { - const auto &EI = Val.first; - Vertices.FindAndConstruct(EI.first); - Vertices.FindAndConstruct(EI.second); - InNeighbors[EI.second].insert(EI.first); - OutNeighbors[EI.first].insert(EI.second); - }; - - return p; - } - - /// Inserts an edge into the graph with Identifier Val.first, and - /// Attribute Val.second. If the key is already in the map, it returns false - /// and doesn't update the value. - std::pair - insert(std::pair &&Val) { - auto EI = Val.first; - const auto &p = Edges.insert(std::move(Val)); - if (p.second) { - Vertices.FindAndConstruct(EI.first); - Vertices.FindAndConstruct(EI.second); - InNeighbors[EI.second].insert(EI.first); - OutNeighbors[EI.first].insert(EI.second); - }; - - return p; - } -}; -} -} -#endif diff --git a/tools/llvm-xray/xray-graph.cc b/tools/llvm-xray/xray-graph.cc index e16837e722d..3780ce8672e 100644 --- a/tools/llvm-xray/xray-graph.cc +++ b/tools/llvm-xray/xray-graph.cc @@ -1,4 +1,4 @@ -//===-- xray-graph.cc - XRay Function Call Graph Renderer -----------------===// +//===-- xray-graph.c - XRay Function Call Graph Renderer ------------------===// // // The LLVM Compiler Infrastructure // @@ -30,47 +30,45 @@ using namespace llvm; using namespace llvm::xray; // Setup llvm-xray graph subcommand and its options. -static cl::SubCommand GraphC("graph", "Generate function-call graph"); +static cl::SubCommand Graph("graph", "Generate function-call graph"); static cl::opt GraphInput(cl::Positional, cl::desc(""), - cl::Required, cl::sub(GraphC)); + cl::Required, cl::sub(Graph)); static cl::opt GraphKeepGoing("keep-going", cl::desc("Keep going on errors encountered"), - cl::sub(GraphC), cl::init(false)); + cl::sub(Graph), cl::init(false)); static cl::alias GraphKeepGoing2("k", cl::aliasopt(GraphKeepGoing), cl::desc("Alias for -keep-going"), - cl::sub(GraphC)); + cl::sub(Graph)); static cl::opt GraphOutput("output", cl::value_desc("Output file"), cl::init("-"), - cl::desc("output file; use '-' for stdout"), cl::sub(GraphC)); + cl::desc("output file; use '-' for stdout"), cl::sub(Graph)); static cl::alias GraphOutput2("o", cl::aliasopt(GraphOutput), - cl::desc("Alias for -output"), cl::sub(GraphC)); + cl::desc("Alias for -output"), cl::sub(Graph)); -static cl::opt - GraphInstrMap("instr_map", - cl::desc("binary with the instrumrntation map, or " - "a separate instrumentation map"), - cl::value_desc("binary with xray_instr_map"), cl::sub(GraphC), - cl::init("")); +static cl::opt GraphInstrMap( + "instr_map", cl::desc("binary with the instrumrntation map, or " + "a separate instrumentation map"), + cl::value_desc("binary with xray_instr_map"), cl::sub(Graph), cl::init("")); static cl::alias GraphInstrMap2("m", cl::aliasopt(GraphInstrMap), cl::desc("alias for -instr_map"), - cl::sub(GraphC)); + cl::sub(Graph)); static cl::opt GraphDeduceSiblingCalls( "deduce-sibling-calls", cl::desc("Deduce sibling calls when unrolling function call stacks"), - cl::sub(GraphC), cl::init(false)); + cl::sub(Graph), cl::init(false)); static cl::alias GraphDeduceSiblingCalls2("d", cl::aliasopt(GraphDeduceSiblingCalls), cl::desc("Alias for -deduce-sibling-calls"), - cl::sub(GraphC)); + cl::sub(Graph)); static cl::opt GraphEdgeLabel("edge-label", cl::desc("Output graphs with edges labeled with this field"), - cl::value_desc("field"), cl::sub(GraphC), + cl::value_desc("field"), cl::sub(Graph), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", "Do not label Edges"), @@ -90,12 +88,12 @@ static cl::opt "sum of call durations"))); static cl::alias GraphEdgeLabel2("e", cl::aliasopt(GraphEdgeLabel), cl::desc("Alias for -edge-label"), - cl::sub(GraphC)); + cl::sub(Graph)); static cl::opt GraphVertexLabel( "vertex-label", cl::desc("Output graphs with vertices labeled with this field"), - cl::value_desc("field"), cl::sub(GraphC), + cl::value_desc("field"), cl::sub(Graph), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", "Do not label Edges"), @@ -115,12 +113,12 @@ static cl::opt GraphVertexLabel( "sum of call durations"))); static cl::alias GraphVertexLabel2("v", cl::aliasopt(GraphVertexLabel), cl::desc("Alias for -edge-label"), - cl::sub(GraphC)); + cl::sub(Graph)); static cl::opt GraphEdgeColorType( "color-edges", cl::desc("Output graphs with edge colors determined by this field"), - cl::value_desc("field"), cl::sub(GraphC), + cl::value_desc("field"), cl::sub(Graph), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", "Do not label Edges"), @@ -140,12 +138,12 @@ static cl::opt GraphEdgeColorType( "sum of call durations"))); static cl::alias GraphEdgeColorType2("c", cl::aliasopt(GraphEdgeColorType), cl::desc("Alias for -color-edges"), - cl::sub(GraphC)); + cl::sub(Graph)); static cl::opt GraphVertexColorType( "color-vertices", cl::desc("Output graphs with vertex colors determined by this field"), - cl::value_desc("field"), cl::sub(GraphC), + cl::value_desc("field"), cl::sub(Graph), cl::init(GraphRenderer::StatType::NONE), cl::values(clEnumValN(GraphRenderer::StatType::NONE, "none", "Do not label Edges"), @@ -165,7 +163,7 @@ static cl::opt GraphVertexColorType( "sum of call durations"))); static cl::alias GraphVertexColorType2("b", cl::aliasopt(GraphVertexColorType), cl::desc("Alias for -edge-label"), - cl::sub(GraphC)); + cl::sub(Graph)); template T diff(T L, T R) { return std::max(L, R) - std::min(L, R); } @@ -210,13 +208,14 @@ Error GraphRenderer::accountRecord(const XRayRecord &Record) { auto &ThreadStack = PerThreadFunctionStack[Record.TId]; switch (Record.Type) { case RecordTypes::ENTER: { - if (G.count(Record.FuncId) == 0) - G[Record.FuncId].SymbolName = FuncIdHelper.SymbolOrNumber(Record.FuncId); + if (VertexAttrs.count(Record.FuncId) == 0) + VertexAttrs[Record.FuncId].SymbolName = + FuncIdHelper.SymbolOrNumber(Record.FuncId); ThreadStack.push_back({Record.FuncId, Record.TSC}); break; } case RecordTypes::EXIT: { - // FIXME: Refactor this and the account subcommand to reduce code + // FIXME: Refactor this and the account subcommand to reducr code // duplication if (ThreadStack.size() == 0 || ThreadStack.back().FuncId != Record.FuncId) { if (!DeduceSiblingCalls) @@ -231,25 +230,23 @@ Error GraphRenderer::accountRecord(const XRayRecord &Record) { make_error_code(errc::invalid_argument)); // There is no matching // Function for this exit. while (ThreadStack.back().FuncId != Record.FuncId) { - TimestampT D = diff(ThreadStack.back().TSC, Record.TSC); - VertexIdentifier TopFuncId = ThreadStack.back().FuncId; + uint64_t D = diff(ThreadStack.back().TSC, Record.TSC); + int32_t TopFuncId = ThreadStack.back().FuncId; ThreadStack.pop_back(); assert(ThreadStack.size() != 0); - EdgeIdentifier EI(ThreadStack.back().FuncId, TopFuncId); - auto &EA = G[EI]; + auto &EA = Graph[ThreadStack.back().FuncId][TopFuncId]; EA.Timings.push_back(D); updateStat(EA.S, D); - updateStat(G[TopFuncId].S, D); + updateStat(VertexAttrs[TopFuncId].S, D); } } uint64_t D = diff(ThreadStack.back().TSC, Record.TSC); ThreadStack.pop_back(); - VertexIdentifier VI = ThreadStack.empty() ? 0 : ThreadStack.back().FuncId; - EdgeIdentifier EI(VI, Record.FuncId); - auto &EA = G[EI]; + auto &V = Graph[ThreadStack.empty() ? 0 : ThreadStack.back().FuncId]; + auto &EA = V[Record.FuncId]; EA.Timings.push_back(D); updateStat(EA.S, D); - updateStat(G[Record.FuncId].S, D); + updateStat(VertexAttrs[Record.FuncId].S, D); break; } } @@ -283,35 +280,39 @@ void GraphRenderer::updateMaxStats(const GraphRenderer::TimeStat &S, } void GraphRenderer::calculateEdgeStatistics() { - assert(!G.edges().empty()); - for (auto &E : G.edges()) { - auto &A = E.second; - assert(!A.Timings.empty()); - assert((A.Timings[0] > 0)); - getStats(A.Timings.begin(), A.Timings.end(), A.S); - assert(A.S.Sum > 0); - updateMaxStats(A.S, G.GraphEdgeMax); + for (auto &V : Graph) { + for (auto &E : V.second) { + auto &A = E.second; + getStats(A.Timings.begin(), A.Timings.end(), A.S); + updateMaxStats(A.S, GraphEdgeMax); + } } } void GraphRenderer::calculateVertexStatistics() { - std::vector TempTimings; - for (auto &V : G.vertices()) { - assert(V.first == 0 || - G[V.first].S.Sum != 0 && - "Every non-root vertex should have at least one call"); - if (V.first != 0) { - for (auto &E : G.inEdges(V.first)) { - auto &A = E.second; - TempTimings.insert(TempTimings.end(), A.Timings.begin(), - A.Timings.end()); - } - assert(!TempTimings.empty() && TempTimings[0] > 0); - getStats(TempTimings.begin(), TempTimings.end(), G[V.first].S); - updateMaxStats(G[V.first].S, G.GraphVertexMax); - TempTimings.clear(); + DenseMap>> + IncommingEdges; + uint64_t MaxCount = 0; + for (auto &V : Graph) { + for (auto &E : V.second) { + auto &IEV = IncommingEdges[E.first]; + IEV.second.push_back(&E.second); + IEV.first += E.second.S.Count; + if (IEV.first > MaxCount) + MaxCount = IEV.first; } } + std::vector TempTimings; + TempTimings.reserve(MaxCount); + for (auto &V : IncommingEdges) { + for (auto &P : V.second.second) { + TempTimings.insert(TempTimings.end(), P->Timings.begin(), + P->Timings.end()); + } + getStats(TempTimings.begin(), TempTimings.end(), VertexAttrs[V.first].S); + updateMaxStats(VertexAttrs[V.first].S, GraphVertexMax); + TempTimings.clear(); + } } // A Helper function for normalizeStatistics which normalises a single @@ -328,17 +329,19 @@ static void normalizeTimeStat(GraphRenderer::TimeStat &S, // Normalises the statistics in the graph for a given TSC frequency. void GraphRenderer::normalizeStatistics(double CycleFrequency) { - for (auto &E : G.edges()) { - auto &S = E.second.S; - normalizeTimeStat(S, CycleFrequency); + for (auto &V : Graph) { + for (auto &E : V.second) { + auto &S = E.second.S; + normalizeTimeStat(S, CycleFrequency); + } } - for (auto &V : G.vertices()) { + for (auto &V : VertexAttrs) { auto &S = V.second.S; normalizeTimeStat(S, CycleFrequency); } - normalizeTimeStat(G.GraphEdgeMax, CycleFrequency); - normalizeTimeStat(G.GraphVertexMax, CycleFrequency); + normalizeTimeStat(GraphEdgeMax, CycleFrequency); + normalizeTimeStat(GraphVertexMax, CycleFrequency); } // Returns a string containing the value of statistic field T @@ -474,11 +477,8 @@ double GraphRenderer::TimeStat::compare(StatType T, const TimeStat &O) const { void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, StatType ET, StatType EC, StatType VT, StatType VC) { - G.GraphEdgeMax = {}; - G.GraphVertexMax = {}; calculateEdgeStatistics(); calculateVertexStatistics(); - if (H.CycleFrequency) normalizeStatistics(H.CycleFrequency); @@ -487,19 +487,18 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, if (VT != StatType::NONE) OS << "node [shape=record];\n"; - for (const auto &E : G.edges()) { - const auto &S = E.second.S; - OS << "F" << E.first.first << " -> " - << "F" << E.first.second << " [label=\"" << S.getAsString(ET) << "\""; - if (EC != StatType::NONE) - OS << " color=\"" << getColor(S.compare(EC, G.GraphEdgeMax)) << "\""; - OS << "];\n"; - } + for (const auto &V : Graph) + for (const auto &E : V.second) { + const auto &S = E.second.S; + OS << "F" << V.first << " -> " + << "F" << E.first << " [label=\"" << S.getAsString(ET) << "\""; + if (EC != StatType::NONE) + OS << " color=\"" << getColor(S.compare(EC, GraphEdgeMax)) << "\""; + OS << "];\n"; + } - for (const auto &V : G.vertices()) { + for (const auto &V : VertexAttrs) { const auto &VA = V.second; - if (V.first == 0) - continue; OS << "F" << V.first << " [label=\"" << (VT != StatType::NONE ? "{" : "") << (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..." : VA.SymbolName); @@ -508,7 +507,7 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, else OS << "\""; if (VC != StatType::NONE) - OS << " color=\"" << getColor(VA.S.compare(VC, G.GraphVertexMax)) << "\""; + OS << " color=\"" << getColor(VA.S.compare(VC, GraphVertexMax)) << "\""; OS << "];\n"; } OS << "}\n"; @@ -522,7 +521,7 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, const XRayFileHeader &H, // // FIXME: include additional filtering and annalysis passes to provide more // specific useful information. -static CommandRegistration Unused(&GraphC, []() -> Error { +static CommandRegistration Unused(&Graph, []() -> Error { InstrumentationMap Map; if (!GraphInstrMap.empty()) { auto InstrumentationMapOrError = loadInstrumentationMap(GraphInstrMap); @@ -582,6 +581,7 @@ static CommandRegistration Unused(&GraphC, []() -> Error { handleAllErrors(std::move(E), [&](const ErrorInfoBase &E) { E.log(errs()); }); } + GR.exportGraphAsDOT(OS, Header, GraphEdgeLabel, GraphEdgeColorType, GraphVertexLabel, GraphVertexColorType); return Error::success(); diff --git a/tools/llvm-xray/xray-graph.h b/tools/llvm-xray/xray-graph.h index fd8ac17f902..8b0e2082520 100644 --- a/tools/llvm-xray/xray-graph.h +++ b/tools/llvm-xray/xray-graph.h @@ -24,7 +24,6 @@ #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" @@ -50,22 +49,21 @@ public: std::string getAsString(StatType T) const; double compare(StatType T, const TimeStat &Other) const; }; - typedef uint64_t TimestampT; /// 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 { + struct EdgeAttribute { TimeStat S; - std::vector Timings; + std::vector 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 { + struct VertexAttribute { std::string SymbolName; TimeStat S; }; @@ -80,15 +78,17 @@ public: typedef DenseMap PerThreadFunctionStackMap; - class GraphT : public Graph { - public: - TimeStat GraphEdgeMax = {}; - TimeStat GraphVertexMax = {}; - }; +private: + /// The Graph stored in an edge-list like format, with the edges also having + /// An attached set of attributes. + DenseMap> Graph; - GraphT G; - typedef typename decltype(G)::VertexIdentifier VertexIdentifier; - typedef typename decltype(G)::EdgeIdentifier EdgeIdentifier; + /// Graph Vertex Attributes. These are presently stored seperate from the + /// main graph. + DenseMap VertexAttrs; + + TimeStat GraphEdgeMax; + TimeStat GraphVertexMax; /// Use a Map to store the Function stack for each thread whilst building the /// graph. @@ -99,7 +99,7 @@ public: /// Usefull object for getting human readable Symbol Names. FuncIdConversionHelper &FuncIdHelper; bool DeduceSiblingCalls = false; - TimestampT CurrentMaxTSC = 0; + uint64_t CurrentMaxTSC = 0; /// A private function to help implement the statistic generation functions; template @@ -121,9 +121,7 @@ public: /// Takes in a reference to a FuncIdHelper in order to have ready access to /// Symbol names. explicit GraphRenderer(FuncIdConversionHelper &FuncIdHelper, bool DSC) - : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC) { - G[0] = {}; - } + : FuncIdHelper(FuncIdHelper), DeduceSiblingCalls(DSC) {} /// Process an Xray record and expand the graph. /// @@ -134,7 +132,7 @@ public: /// FIXME: Make this more robust against small irregularities. Error accountRecord(const XRayRecord &Record); - const PerThreadFunctionStackMap &getPerThreadFunctionStack() const { + const PerThreadFunctionStackMap getPerThreadFunctionStack() const { return PerThreadFunctionStack; } @@ -145,13 +143,6 @@ public: StatType EdgeColor = StatType::NONE, StatType VertexLabel = StatType::NONE, StatType VertexColor = StatType::NONE); - - /// Get a reference to the internal graph. - const GraphT &getGraph() { - calculateEdgeStatistics(); - calculateVertexStatistics(); - return G; - } }; } } diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 8e40f141463..8dbca211d02 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -24,4 +24,3 @@ add_subdirectory(ProfileData) add_subdirectory(Support) add_subdirectory(Target) add_subdirectory(Transforms) -add_subdirectory(XRay) diff --git a/unittests/XRay/CMakeLists.txt b/unittests/XRay/CMakeLists.txt deleted file mode 100644 index 30bccd1bbe6..00000000000 --- a/unittests/XRay/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -set(LLVM_LINK_COMPONENTS - Support - ) - -set(XRAYSources - GraphTest.cpp - ) - -add_llvm_unittest(XRayTests - ${XRAYSources} - ) - -add_dependencies(XRayTests intrinsics_gen) diff --git a/unittests/XRay/GraphTest.cpp b/unittests/XRay/GraphTest.cpp deleted file mode 100644 index 89d13b1ef58..00000000000 --- a/unittests/XRay/GraphTest.cpp +++ /dev/null @@ -1,256 +0,0 @@ -//===- llvm/unittest/XRay/GraphTest.cpp - XRay Graph unit tests -*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/XRay/Graph.h" -#include "gtest/gtest.h" -#include -#include -#include - -using namespace llvm; -using namespace xray; - -namespace { -struct VA { - unsigned VA; -}; -struct EA { - unsigned EA; -}; -typedef Graph GraphT; -typedef typename GraphT::VertexIdentifier VI; -typedef typename GraphT::EdgeIdentifier EI; - -// Test Fixture -template class GraphTest : public testing::Test { -protected: - T Graph = getTestGraph(); - -private: - static T getTestGraph() { - using std::make_pair; - typename std::remove_const::type G; - G.insert(make_pair(1u, VA({3u}))); - G.insert(make_pair(2u, VA({5u}))); - G.insert(make_pair(3u, VA({7u}))); - G.insert(make_pair(4u, VA({11u}))); - G.insert(make_pair(5u, VA({13u}))); - G.insert(make_pair(6u, VA({17u}))); - - G.insert(std::make_pair(EI(1u, 2u), EA({3u * 5u}))); - G.insert(std::make_pair(EI(2u, 3u), EA({5u * 7u}))); - G.insert(std::make_pair(EI(6u, 3u), EA({2u * 7u * 17u}))); - G.insert(std::make_pair(EI(4u, 6u), EA({11u * 17u}))); - G.insert(std::make_pair(EI(2u, 4u), EA({5u * 11u}))); - G.insert(std::make_pair(EI(2u, 5u), EA({5u * 13u}))); - G.insert(std::make_pair(EI(4u, 5u), EA({11u * 13u}))); - - return G; - } -}; - -typedef ::testing::Types GraphTestTypes; - -using VVT = typename GraphT::VertexValueType; -using EVT = typename GraphT::EdgeValueType; - -TYPED_TEST_CASE(GraphTest, GraphTestTypes); - -template void graphVertexTester(T &G) { - std::set V({1u, 2u, 3u, 4u, 5u, 6u}); - std::vector VA({0u, 3u, 5u, 7u, 11u, 13u, 17u}); - - EXPECT_EQ(V.size(), G.vertices().size()); - EXPECT_FALSE(G.vertices().empty()); - for (unsigned u : V) { - auto EVV = G.at(u); - ASSERT_TRUE(bool{EVV}); - EXPECT_EQ(1u, G.count(u)); - EXPECT_EQ(VA[u], EVV->VA); - EXPECT_NE(G.vertices().end(), - std::find_if(G.vertices().begin(), G.vertices().end(), - [&](const VVT &VV) { return VV.first == u; })); - consumeError(EVV.takeError()); - } - - for (auto &VVT : G.vertices()) { - EXPECT_EQ(1u, V.count(VVT.first)); - EXPECT_EQ(VA[VVT.first], VVT.second.VA); - } -} - -template void graphEdgeTester(T &G) { - std::set V({1u, 2u, 3u, 4u, 5u, 6u}); - - std::set> E( - {{1u, 2u}, {2u, 3u}, {6u, 3u}, {4u, 6u}, {2u, 4u}, {2u, 5u}, {4u, 5u}}); - std::vector VA({0u, 3u, 5u, 7u, 11u, 13u, 17u}); - - EXPECT_EQ(E.size(), G.edges().size()); - EXPECT_FALSE(G.edges().empty()); - for (std::pair u : E) { - auto EEV = G.at(u); - ASSERT_TRUE(bool{EEV}); - EXPECT_EQ(1u, G.count(u)); - EXPECT_EQ(VA[u.first] * VA[u.second] * ((u.first > u.second) ? 2 : 1), - EEV->EA); - EXPECT_NE(G.edges().end(), - std::find_if(G.edges().begin(), G.edges().end(), - [&](const EVT &EV) { return EV.first == u; })); - consumeError(EEV.takeError()); - } - - for (auto &EV : G.edges()) { - EXPECT_EQ(1u, E.count(EV.first)); - EXPECT_EQ(VA[EV.first.first] * VA[EV.first.second] * - ((EV.first.first > EV.first.second) ? 2 : 1), - EV.second.EA); - const auto &IE = G.inEdges(EV.first.second); - const auto &OE = G.outEdges(EV.first.first); - EXPECT_NE(IE.size(), 0u); - EXPECT_NE(OE.size(), 0u); - EXPECT_NE(IE.begin(), IE.end()); - EXPECT_NE(OE.begin(), OE.end()); - EXPECT_NE( - G.inEdges(EV.first.second).end(), - std::find_if(G.inEdges(EV.first.second).begin(), - G.inEdges(EV.first.second).end(), - [&](const EVT &EVI) { return EVI.first == EV.first; })); - EXPECT_EQ( - G.inEdges(EV.first.first).end(), - std::find_if(G.inEdges(EV.first.first).begin(), - G.inEdges(EV.first.first).end(), - [&](const EVT &EVI) { return EVI.first == EV.first; })); - EXPECT_EQ( - G.outEdges(EV.first.second).end(), - std::find_if(G.outEdges(EV.first.second).begin(), - G.outEdges(EV.first.second).end(), - [&](const EVT &EVI) { return EVI.first == EV.first; })); - EXPECT_NE( - G.outEdges(EV.first.first).end(), - std::find_if(G.outEdges(EV.first.first).begin(), - G.outEdges(EV.first.first).end(), - [&](const EVT &EVI) { return EVI.first == EV.first; })); - } -} - -TYPED_TEST(GraphTest, TestGraphEdge) { - auto &G = this->Graph; - - graphEdgeTester(G); -} - -TYPED_TEST(GraphTest, TestGraphVertex) { - auto &G = this->Graph; - - graphVertexTester(G); -} - -TYPED_TEST(GraphTest, TestCopyConstructor) { - TypeParam G(this->Graph); - - graphEdgeTester(G); - graphVertexTester(G); -} - -TYPED_TEST(GraphTest, TestCopyAssign) { - TypeParam G = this->Graph; - - graphEdgeTester(G); - graphVertexTester(G); -} - -TYPED_TEST(GraphTest, TestMoveConstructor) { - TypeParam G(std::move(this->Graph)); - - graphEdgeTester(G); - graphVertexTester(G); -} - -// Tests the incremental Construction of a graph -TEST(GraphTest, TestConstruction) { - GraphT MG; - const GraphT &G = MG; - EXPECT_EQ(0u, G.count(0u)); - EXPECT_EQ(0u, G.count({0u, 1u})); - auto VE = G.at(0); - auto EE = G.at({0, 0}); - EXPECT_FALSE(VE); // G.at[0] returns an error - EXPECT_FALSE(EE); // G.at[{0,0}] returns an error - consumeError(VE.takeError()); - consumeError(EE.takeError()); - EXPECT_TRUE(G.vertices().empty()); - EXPECT_TRUE(G.edges().empty()); - EXPECT_EQ(G.vertices().begin(), G.vertices().end()); - EXPECT_EQ(G.edges().begin(), G.edges().end()); -} - -TEST(GraphTest, TestiVertexAccessOperator) { - GraphT MG; - const GraphT &G = MG; - - MG[0u] = {1u}; - EXPECT_EQ(1u, MG[0u].VA); - EXPECT_EQ(1u, G.count(0u)); - EXPECT_EQ(0u, G.count(1u)); - EXPECT_EQ(1u, MG[0u].VA); - auto T = G.at(0u); - EXPECT_TRUE(bool{T}); - EXPECT_EQ(1u, T->VA); - - EXPECT_EQ(1u, G.vertices().size()); - EXPECT_EQ(0u, G.edges().size()); - EXPECT_FALSE(G.vertices().empty()); - EXPECT_TRUE(G.edges().empty()); - EXPECT_NE(G.vertices().begin(), G.vertices().end()); - EXPECT_EQ(G.edges().begin(), G.edges().end()); - EXPECT_EQ(1u, G.vertices().begin()->second.VA); - EXPECT_EQ(0u, G.vertices().begin()->first); - EXPECT_EQ(0u, G.outEdges(0u).size()); - EXPECT_TRUE(G.outEdges(0u).empty()); - EXPECT_EQ(G.outEdges(0u).begin(), G.outEdges(0u).end()); - EXPECT_EQ(0u, G.inEdges(0u).size()); - EXPECT_TRUE(G.inEdges(0u).empty()); - EXPECT_EQ(G.inEdges(0u).begin(), G.inEdges(0u).end()); -} - -TEST(GraphTest, TestEdgeAccessOperator) { - GraphT MG; - const GraphT &G = MG; - - MG[{0u, 0u}] = {2u}; - EI EdgeIdent({0u, 0u}); - EXPECT_EQ(2u, MG[EdgeIdent].EA); - EXPECT_EQ(1u, G.count({0u, 0u})); - EXPECT_EQ(0u, G.count({0u, 1u})); - EXPECT_EQ(1u, G.count(0u)); - EXPECT_NE(1u, G.count(1u)); - auto T = G.at({0u, 0u}); - EXPECT_TRUE(T && T->EA == 2u); - EXPECT_EQ(1u, G.edges().size()); - EXPECT_EQ(1u, G.vertices().size()); - EXPECT_FALSE(G.edges().empty()); - EXPECT_FALSE(G.vertices().empty()); - EXPECT_NE(G.edges().begin(), G.edges().end()); - EXPECT_EQ(EI(0u, 0u), G.edges().begin()->first); - EXPECT_EQ(2u, G.edges().begin()->second.EA); - EXPECT_EQ(1u, G.outEdges(0u).size()); - EXPECT_FALSE(G.outEdges(0u).empty()); - EXPECT_NE(G.outEdges(0u).begin(), G.outEdges(0u).end()); - EXPECT_EQ(EI(0u, 0u), G.outEdges(0u).begin()->first); - EXPECT_EQ(2u, G.outEdges(0u).begin()->second.EA); - EXPECT_EQ(++(G.outEdges(0u).begin()), G.outEdges(0u).end()); - EXPECT_EQ(1u, G.inEdges(0u).size()); - EXPECT_FALSE(G.inEdges(0u).empty()); - EXPECT_NE(G.inEdges(0u).begin(), G.inEdges(0u).end()); - EXPECT_EQ(EI(0u, 0u), G.inEdges(0u).begin()->first); - EXPECT_EQ(2u, G.inEdges(0u).begin()->second.EA); - EXPECT_EQ(++(G.inEdges(0u).begin()), G.inEdges(0u).end()); -} -}