Synopsis - Cross-Reference
File: /sandbox/call_graph/call-graph.cc1// 2// Copyright (C) 2005 Stefan Seefeld 3// All rights reserved. 4// Licensed to the public under the terms of the GNU LGPL (>= 2), 5// see the file COPYING for details. 6// 7#include <Synopsis/Buffer.hh> 8#include <Synopsis/Lexer.hh> 9#include <Synopsis/Parser.hh> 10#include <Synopsis/PTree.hh> 11#include <Synopsis/SymbolLookup.hh> 12#include <Synopsis/PTree/Writer.hh> 13#include <Synopsis/Trace.hh> 14#include <fstream> 15#include <set> 16 17using namespace Synopsis; 18namespace PT = Synopsis::PTree; 19namespace SL = Synopsis::SymbolLookup; 20 21class EnclosingFunctionFinder : private SL::ScopeVisitor 22{ 23public: 24 //. Find the nearest enclosing scope that is either 25 //. a function or namespace (i.e. global scope). 26 SL::Scope const *find(SL::Scope const *s) 27 { 28 my_scope = 0; 29 const_cast<SL::Scope *>(s)->accept(this); 30 return my_scope; 31 } 32private: 33 34 virtual void visit(SL::LocalScope *s) 35 { const_cast<SL::Scope *>(s->outer_scope())->accept(this);} 36 virtual void visit(SL::FunctionScope *s) { my_scope = s;} 37 virtual void visit(SL::Namespace *s) { my_scope = s;} 38 39 SL::Scope const *my_scope; 40}; 41 42//. A simple call graph generator. 43//. This class finds function call expressions and 44//. reports the (function) scope of the call together 45//. with the name of the callee. 46//. As this is only a demo of part of the Synopsis API 47//. no attempt has been made for completeness, i.e. there 48//. is no pointer analysis, etc. 49//. Further, only C sources are supported as for C++ 50//. type analysis would be needed to do proper 51//. overload resolution, which is not yet implemented. 52class CallGraphGenerator : private SL::Walker 53{ 54public: 55 CallGraphGenerator(std::ostream &os, SL::Scope *symbols) 56 : Walker(symbols), my_os(os) {} 57 void generate(PT::Node const *node) 58 { 59 my_os << "digraph CallGraph\n{\n" 60 << "node[fillcolor=\"#ffffcc\", pencolor=\"#424242\" style=\"filled\"];\n"; 61 const_cast<PT::Node *>(node)->accept(this); 62 my_os << '}' << std::endl; 63 } 64private: 65 void visit(PT::FuncallExpr *func) 66 { 67 PT::Node *name = PT::first(func); 68 69 EnclosingFunctionFinder function_finder; 70 SL::Scope const *scope = function_finder.find(current_scope()); 71 std::string caller = "<global>"; 72 if (SL::FunctionScope const *func = dynamic_cast<SL::FunctionScope const *>(scope)) 73 caller = func->name(); 74 std::string callee = PT::reify(name); 75 if (my_cache.find(caller) == my_cache.end()) 76 { 77 my_cache.insert(caller); 78 my_os << '"' << caller << "\" [label=\"" << caller << "\"];\n"; 79 } 80 if (my_cache.find(callee) == my_cache.end()) 81 { 82 my_cache.insert(callee); 83 my_os << '"' << callee << "\" [label=\"" << callee << "\"];\n"; 84 } 85 my_os << '"' << caller << "\" -> \"" << callee << '"' << std::endl; 86 } 87 88 std::set<std::string> my_cache; 89 std::ostream & my_os; 90}; 91 92int usage(const char *command) 93{ 94 std::cerr << "Usage: " << command 95 << " [-C] [-d] [-o <output>] <input>" << std::endl; 96 return -1; 97} 98 99int main(int argc, char **argv) 100{ 101 const char *input = argv[1]; 102 std::ofstream output; 103 if (argc == 1) return usage(argv[0]); 104 input = argv[argc - 1]; 105 bool cxx = false; 106 for (size_t i = 1; i != argc - 1; ++i) 107 { 108 if (argv[i] == std::string("-C")) cxx = true; 109 else if (argv[i] == std::string("-d")) Trace::enable(); 110 else if (argv[i] == std::string("-o") && i < argc - 1) 111 output.open(argv[++i]); 112 else return usage(argv[0]); 113 } 114 if (!output.is_open()) 115 { 116 output.copyfmt(std::cout); 117 output.clear(std::cout.rdstate()); 118 static_cast<std::basic_ios<char> &>(output).rdbuf(std::cout.rdbuf()); 119 } 120// try 121 { 122 // FIXME: For now we have to initialize the Encoding... 123 PT::Encoding::do_init_static(); 124 125 std::ifstream ifs(input); 126 Buffer buffer(ifs.rdbuf(), input); 127 int tokenset = Lexer::C | Lexer::GCC | Lexer::MSVC; 128 int ruleset = 0; 129 if (cxx) 130 { 131 tokenset |= Lexer::CXX; 132 ruleset = Parser::CXX | Parser::MSVC; 133 } 134 Lexer lexer(&buffer, tokenset); 135 SymbolFactory symbols; 136 Parser parser(lexer, symbols, ruleset); 137 PT::Node *node = parser.parse(); 138 const Parser::ErrorList &errors = parser.errors(); 139 for (Parser::ErrorList::const_iterator i = errors.begin(); i != errors.end(); ++i) 140 (*i)->write(std::cerr); 141 142 if (!node) return -1; 143 CallGraphGenerator generator(output, symbols.current_scope()); 144 generator.generate(node); 145 } 146// catch (const std::exception &e) 147// { 148// std::cerr << "Caught exception : " << e.what() << std::endl; 149// throw; 150// } 151}