// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #pragma once #include #include #include namespace vespamalloc { template class CallGraphNode { public: CallGraphNode() : _callers(nullptr), _next(nullptr), _content(), _count(0) { } const CallGraphNode *next() const { return _next; } const CallGraphNode *callers() const { return _callers; } const T & content() const { return _content; } CallGraphNode *next() { return _next; } CallGraphNode *callers() { return _callers; } T & content() { return _content; } size_t count() const { return _count; } void content(const T & v) { _content = v; } template bool addStack(const T * stack, size_t nelem, Store & store); template void traverseDepth(size_t depth, size_t width, Object func); template void traverseWidth(size_t depth, size_t width, Object & func); friend asciistream & operator << (asciistream & os, const CallGraphNode & v) { return os << v._content << '(' << v._count << ')'; } private: CallGraphNode * _callers; CallGraphNode * _next; T _content; AddSub _count; }; template template bool CallGraphNode::addStack(const T * stack, size_t nelem, Store & store) { bool retval(false); if (nelem == 0) { retval = true; } else if (_content == stack[0]) { _count++; if (nelem > 1) { if (_callers == nullptr) { _callers = store.alloc(); if (_callers != nullptr) { _callers->content(stack[1]); } } if (_callers) { retval = _callers->addStack(stack+1, nelem-1, store); } } else { retval = true; } } else { if (_next == nullptr) { _next = store.alloc(); if (_next != nullptr) { _next->content(stack[0]); } } if (_next) { retval = _next->addStack(stack, nelem, store); } } return retval; } template template void CallGraphNode::traverseDepth(size_t depth, size_t width, Object func) { Object newFunc(func); newFunc.handle(*this); if (_callers) { _callers->traverseDepth(depth+1, width, newFunc); } if (_next) { _next->traverseDepth(depth, width+1, func); } } template template void CallGraphNode::traverseWidth(size_t depth, size_t width, Object & func) { Object newFunc(func); newFunc.handle(*this); if (_next) { _next->traverseWidth(depth, width+1, newFunc); } if (_callers) { _callers->traverseWidth(depth+1, width, func); } } template class ArrayStore { public: ArrayStore() : _used(0) { } T * alloc() { return (_used < MaxElem) ? &_array[_used++] : nullptr; } AddSub size() const { return _used; } private: AddSub _used; T _array[MaxElem]; }; template class CallGraph { public: using Node = CallGraphNode; CallGraph() : _root(nullptr), _nodeStore(std::make_unique()) { } CallGraph(Content root) : _root(nullptr), _nodeStore(std::make_unique()) { checkOrSetRoot(root); } bool addStack(const Content * stack, size_t nelem) { checkOrSetRoot(stack[0]); return _root->addStack(stack, nelem, *_nodeStore); } template void traverseDepth(Object func) { if (_root) { _root->traverseDepth(0, 0, func); } } template void traverseWidth(Object func) { if (_root) {_root->traverseWidth(0, 0, func); } } size_t size() const { return _nodeStore->size(); } bool empty() const { return size()==0; } private: CallGraph(const CallGraph &); CallGraph & operator = (const CallGraph &); bool checkOrSetRoot(const Content & root) { if (_root == nullptr) { _root = _nodeStore->alloc(); _root->content(root); } return (_root != nullptr); } typedef ArrayStore NodeStore; Node * _root; std::unique_ptr _nodeStore; }; }