├── .images ├── 1.png ├── 2.png └── 3.png ├── LICENSE ├── README.md ├── clang-mapper ├── CMakeLists.txt ├── CallGraph.cpp ├── CallGraph.h ├── CallGraphAction.cpp ├── CallGraphAction.h ├── ClangMapper.cpp └── Commons.h └── release └── clang-mapper /.images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/L-Zephyr/clang-mapper/70caa8d28f8f6f7a4fdbfadda65f34c37d9acf16/.images/1.png -------------------------------------------------------------------------------- /.images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/L-Zephyr/clang-mapper/70caa8d28f8f6f7a4fdbfadda65f34c37d9acf16/.images/2.png -------------------------------------------------------------------------------- /.images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/L-Zephyr/clang-mapper/70caa8d28f8f6f7a4fdbfadda65f34c37d9acf16/.images/3.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 L-Zephyr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **This project was deprecated, please go to [Drafter](https://github.com/L-Zephyr/Drafter)** 2 | 3 | # clang-mapper 4 | `clang-mapper` is a standalone tool build for automatic generate [Call Graph](https://en.wikipedia.org/wiki/Call_graph) in iOS project 5 | 6 | **[中文版](http://www.jianshu.com/p/e19aafbaddca)** 7 | 8 | ## Compile the source code 9 | You can just use the pre-compiled binary file in the `relaese` folder (Build with Clang5.0.0). Or you can compile the source code in `clang-mapper` folder by yourself 10 | 11 | Follow the guide in [Tutorial for building tools using LibTooling and LibASTMatchers](https://clang.llvm.org/docs/LibASTMatchersTutorial.html). 12 | 1. Download the `LLVM` and `Clang` source code, then copy the folder `clang-mapper` to `llvm/tools/clang/tools/`. 13 | 2. Edit the file `llvm/tools/clang/tools/CMakeLists.txt` and add `add_clang_subdirectory(clang-mapper)` to the end. 14 | 3. Then build it with cmake and you will get a binary file `clang-mapper` in the `bin` folder, now copy it to `/usr/local/bin/` 15 | 16 | ## How to use 17 | Before you use `clang-mapper`, make sure you have installed `Graphviz`. 18 | 19 | ### Generate Call Graph 20 | `clang-mapper` accept an arbitrary number of file or folder arguments, then it wil automatically deal with all code files and generate Call Graph in current path. 21 | 22 | For example, i make a folder *CallGraph* in the same path as *AFNetworking* 23 | ``` 24 | $ mkdir CallGraph 25 | ``` 26 | ![](./.images/1.png) 27 | Then run command in *CallGraph* folder 28 | ``` 29 | $ cd CallGraph 30 | $ clang-mapper ../AFNetworking -- 31 | ``` 32 | The `clang-mapper` will traverse the *../AFNetworking* folder and generate all Call Graph in *CallGraph* folder 33 | ![All Call Graph](./.images/2.png) 34 | ![AFHTTPSessionManager Call Graph](./.images/3.png) 35 | 36 | ### Optional Command line Option 37 | - **-graph-only** : Only generate *.png* files, this is a default option 38 | - **-dot-only** : Only generate *.dot* files 39 | - **-dot-graph** : Generate both *.png* and *.dot* files 40 | - **-ignore-header** : Ignore *.h* file in the given folder 41 | 42 | -------------------------------------------------------------------------------- /clang-mapper/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LLVM_LINK_COMPONENTS support) 2 | 3 | add_clang_executable(clang-mapper 4 | ClangMapper.cpp 5 | CallGraphAction.cpp 6 | CallGraphAction.h 7 | CallGraph.cpp 8 | CallGraph.h 9 | Commons.h 10 | ) 11 | target_link_libraries(clang-mapper 12 | clangTooling 13 | clangBasic 14 | clangASTMatchers 15 | ) 16 | -------------------------------------------------------------------------------- /clang-mapper/CallGraph.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LZephyr on 2017/3/15. 3 | // 4 | 5 | #include "CallGraph.h" 6 | 7 | #include "clang/AST/ASTContext.h" 8 | #include "clang/AST/Decl.h" 9 | #include "clang/AST/StmtVisitor.h" 10 | #include "llvm/ADT/PostOrderIterator.h" 11 | #include "llvm/ADT/Statistic.h" 12 | #include "llvm/Support/FileSystem.h" 13 | #include "llvm/Support/raw_os_ostream.h" 14 | #include "llvm/Support/Program.h" 15 | #include "llvm/Support/GraphWriter.h" 16 | #include 17 | #include 18 | #include 19 | 20 | using namespace clang; 21 | using namespace llvm; 22 | 23 | #define DEBUG_TYPE "CallGraph" 24 | 25 | /// Split a string 26 | template 27 | void split(const std::string &s, char delim, Out result) { 28 | std::stringstream ss; 29 | ss.str(s); 30 | std::string item; 31 | while (std::getline(ss, item, delim)) { 32 | *(result++) = item; 33 | } 34 | } 35 | 36 | std::vector split(const std::string &s, char delim) { 37 | std::vector elems; 38 | split(s, delim, std::back_inserter(elems)); 39 | return elems; 40 | } 41 | 42 | namespace clang { 43 | /// A helper class, which walks the AST and locates all the call sites in the 44 | /// given function body. 45 | class CGBuilder : public StmtVisitor { 46 | CallGraph *G; 47 | CallGraphNode *CallerNode; 48 | ASTContext &Context; 49 | public: 50 | CGBuilder(CallGraph *g, CallGraphNode *N, ASTContext &context) 51 | : G(g), CallerNode(N), Context(context) {} 52 | 53 | void VisitStmt(Stmt *S) { 54 | VisitChildren(S); 55 | } 56 | 57 | void VisitCallExpr(CallExpr *CE) { 58 | if (Decl *D = getDeclFromCall(CE)) { 59 | if (G->isInSystem(D)) { 60 | return; 61 | } 62 | addCalledDecl(D); 63 | } 64 | VisitChildren(CE); 65 | } 66 | 67 | // Adds may-call edges for the ObjC message sends. 68 | void VisitObjCMessageExpr(ObjCMessageExpr *ME) { 69 | if (ObjCInterfaceDecl *IDecl = ME->getReceiverInterface()) { 70 | if (G->isInSystem(IDecl)) { 71 | return; 72 | } 73 | 74 | Selector Sel = ME->getSelector(); 75 | 76 | // Find the callee definition within the same translation unit. 77 | Decl *D = nullptr; 78 | if (ME->isInstanceMessage()) { 79 | D = IDecl->lookupPrivateMethod(Sel); 80 | } else { 81 | D = IDecl->lookupPrivateClassMethod(Sel); 82 | } 83 | 84 | // if not found, create a ObjCMethodDecl with Selector and loc 85 | if (!D) { 86 | D = ObjCMethodDecl::Create(Context, 87 | ME->getLocStart(), 88 | ME->getLocEnd(), 89 | ME->getSelector(), 90 | QualType(), 91 | nullptr, 92 | nullptr); 93 | } 94 | addCalledDecl(D); 95 | } 96 | } 97 | 98 | // get callee Decl 99 | Decl *getDeclFromCall(CallExpr *CE) { 100 | if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) 101 | return CalleeDecl; 102 | 103 | // Simple detection of a call through a block. 104 | Expr *CEE = CE->getCallee()->IgnoreParenImpCasts(); 105 | if (BlockExpr *Block = dyn_cast(CEE)) { 106 | return Block->getBlockDecl(); 107 | } 108 | 109 | return nullptr; 110 | } 111 | 112 | // add a callee node to caller node 113 | void addCalledDecl(Decl *D) { 114 | if (G->canIncludeInGraph(D)) { 115 | CallGraphNode *CalleeNode = G->getOrInsertNode(D); 116 | CallerNode->addCallee(CalleeNode); 117 | } 118 | } 119 | 120 | void VisitChildren(Stmt *S) { 121 | for (Stmt *SubStmt : S->children()) 122 | if (SubStmt) 123 | this->Visit(SubStmt); 124 | } 125 | }; 126 | 127 | } // end clang namespace 128 | 129 | CallGraph::CallGraph(ASTContext &context, std::string filePath, std::string basePath): 130 | Context(context), FullPath(filePath), BasePath(basePath) { 131 | } 132 | 133 | CallGraph::~CallGraph() {} 134 | 135 | bool CallGraph::canIncludeInGraph(const Decl *D) { 136 | assert(D); 137 | 138 | if (const FunctionDecl *FD = dyn_cast(D)) { 139 | // We skip function template definitions, as their semantics is 140 | // only determined when they are instantiated. 141 | if (FD->isDependentContext()) 142 | return false; 143 | 144 | IdentifierInfo *II = FD->getIdentifier(); 145 | if (II && II->getName().startswith("__inline")) 146 | return false; 147 | } 148 | 149 | return true; 150 | } 151 | 152 | bool CallGraph::canBeCallerInGraph(const Decl *D) { 153 | assert(D); 154 | // ignore empty Decl 155 | if (!D->hasBody()) 156 | return false; 157 | 158 | if (const FunctionDecl *FD = dyn_cast(D)) { 159 | // We skip function template definitions, as their semantics is 160 | // only determined when they are instantiated. 161 | if (FD->isDependentContext()) 162 | return false; 163 | 164 | IdentifierInfo *II = FD->getIdentifier(); 165 | if (II && II->getName().startswith("__inline")) 166 | return false; 167 | } 168 | 169 | return true; 170 | } 171 | 172 | //void CallGraph::addRootNodesForBlocks(DeclContext *D) { 173 | // if (BlockDecl *BD = dyn_cast(D)) 174 | // addRootNodeForDecl(BD, true); 175 | // 176 | // for (auto *I : D->decls()) 177 | // if (auto *DC = dyn_cast(I)) 178 | // addRootNodesForBlocks(DC); 179 | //} 180 | // 181 | //void CallGraph::addRootNodeForDecl(Decl* D, bool IsGlobal) { 182 | // assert(D); 183 | // 184 | // // Allocate a new node, mark it as root, and process it's calls. 185 | // CallGraphNode *Node = getOrInsertNode(D); 186 | // 187 | // // Process all the calls by this function as well. 188 | // CGBuilder builder(this, Node, this->context); 189 | // if (Stmt *Body = D->getBody()) 190 | // builder.Visit(Body); 191 | //} 192 | 193 | void CallGraph::addRootNode(Decl *decl) { 194 | if (decl && !isa(decl)) { 195 | decl = decl->getCanonicalDecl(); 196 | } 197 | 198 | CallGraphNode *Node = getOrInsertNode(decl); 199 | 200 | // Process all the calls by this function as well. 201 | CGBuilder builder(this, Node, Context); 202 | if (Stmt *Body = decl->getBody()) 203 | builder.Visit(Body); 204 | } 205 | 206 | CallGraphNode *CallGraph::getNode(Decl *D) { 207 | iterator it = Roots.find(D); 208 | if (it == Roots.end()) { 209 | return nullptr; 210 | } 211 | return it->second.get(); 212 | } 213 | 214 | CallGraphNode *CallGraph::getOrInsertNode(Decl *decl) { 215 | if (decl && !isa(decl)) { 216 | decl = decl->getCanonicalDecl(); 217 | } 218 | shared_ptr &Node = Roots[decl]; 219 | if (!Node) { 220 | Node = shared_ptr(new CallGraphNode(decl)); 221 | } 222 | return Node.get(); 223 | } 224 | 225 | void CallGraph::print(raw_ostream &OS) const { 226 | OS << " --- Call graph Dump --- \n"; 227 | 228 | // traversal root nodes 229 | for (const_iterator it = this->begin(); it != Roots.end(); ++it) { 230 | CallGraphNode *node = it->second.get(); 231 | OS << " Function: "; 232 | node->print(OS); 233 | OS << " calls: "; 234 | 235 | // traversal called node 236 | for (CallGraphNode::const_iterator CI = node->begin(), 237 | CE = node->end(); CI != CE; ++CI) { 238 | (*CI)->print(OS); 239 | OS << " "; 240 | } 241 | OS << '\n'; 242 | } 243 | 244 | OS.flush(); 245 | } 246 | 247 | LLVM_DUMP_METHOD void CallGraph::dump() const { 248 | print(llvm::errs()); 249 | } 250 | 251 | void CallGraph::output() const { 252 | // Get related path 253 | vector baseComponent = split(BasePath, '/'); 254 | vector fullComponent = split(FullPath, '/'); 255 | vector related; 256 | 257 | vector::iterator BI; 258 | vector::iterator FI; 259 | for (BI = baseComponent.begin(), FI = fullComponent.begin(); 260 | BI != baseComponent.end() && FI != fullComponent.end(); 261 | ++BI, ++FI) { 262 | if (*BI != *FI) { 263 | break; 264 | } 265 | } 266 | 267 | string outputPath = "./"; 268 | while (FI + 1 != fullComponent.end()) { 269 | outputPath.append(*FI); 270 | if (!sys::fs::exists(*FI)) { 271 | sys::fs::create_directory(outputPath); 272 | } 273 | outputPath.append("/"); 274 | FI++; 275 | } 276 | outputPath.append(fullComponent.back()); 277 | outputPath.append(".dot"); 278 | 279 | // Write .dot 280 | std::error_code EC; 281 | raw_fd_ostream O(outputPath, EC, sys::fs::F_RW); 282 | 283 | if (EC) { 284 | llvm::errs() << "Error: " << EC.message() << "\n"; 285 | return; 286 | } 287 | 288 | llvm::WriteGraph(O, this); 289 | if (Option != O_GraphOnly) { 290 | errs() << "Write to " << outputPath << "\n"; 291 | } 292 | 293 | O.flush(); 294 | if (Option == O_GraphOnly) { 295 | if (generateGraphFile(outputPath)) { 296 | sys::fs::remove(outputPath); 297 | } else { 298 | llvm::errs() << "Generate graph file fail: " << outputPath << "\n"; 299 | } 300 | } else if (Option == O_DotAndGraph) { 301 | if (!generateGraphFile(outputPath)) { 302 | llvm::errs() << "Generate graph file fail: " << outputPath << "\n"; 303 | } 304 | } 305 | } 306 | 307 | /// Generate graph file in dotFile's dir 308 | bool CallGraph::generateGraphFile(std::string dotFile) const { 309 | ErrorOr target = llvm::sys::findProgramByName("Graphviz"); 310 | if (!target) { 311 | target = llvm::sys::findProgramByName("dot"); 312 | } 313 | 314 | if (target) { 315 | std::string programPath = *target; 316 | std::vector args; 317 | std::string graphPath = dotFile.substr(0, dotFile.length() - 3); // remove suffix 'dot' 318 | graphPath.append("png"); 319 | 320 | // command line arg 321 | args.push_back(programPath.c_str()); 322 | args.push_back(dotFile.c_str()); 323 | args.push_back("-T"); 324 | args.push_back("png"); 325 | args.push_back("-o"); 326 | args.push_back(graphPath.c_str()); 327 | args.push_back(nullptr); 328 | 329 | std::string ErrMsg; 330 | if (sys::ExecuteAndWait(programPath, args.data(), nullptr, nullptr, 0, 0, &ErrMsg)) { 331 | errs() << "Error: " << ErrMsg << "\n"; 332 | return false; 333 | } else { 334 | errs() << "Write to " << graphPath << "\n" << ErrMsg; 335 | } 336 | } else { 337 | llvm::errs() << "Graphviz not found! Please install Graphviz first\n"; 338 | return false; 339 | } 340 | return true; 341 | } 342 | 343 | void CallGraphNode::print(raw_ostream &os) const { 344 | std::string name = getNameAsString(); 345 | if (name.length() == 0) { 346 | os << "< >"; 347 | } else { 348 | os << name; 349 | } 350 | } 351 | 352 | LLVM_DUMP_METHOD void CallGraphNode::dump() const { 353 | print(llvm::errs()); 354 | } 355 | 356 | std::string CallGraphNode::getNameAsString() const { 357 | if (FunctionDecl *decl = dyn_cast_or_null(FD)) { 358 | return decl->getNameAsString(); 359 | } else if (ObjCMethodDecl *decl = dyn_cast_or_null(FD)) { 360 | return decl->getNameAsString(); 361 | } else { 362 | return ""; 363 | } 364 | } 365 | 366 | namespace llvm { 367 | template <> 368 | struct DOTGraphTraits : public DefaultDOTGraphTraits { 369 | 370 | DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} 371 | 372 | static std::string getNodeLabel(const CallGraphNode *Node, 373 | const CallGraph *CG) { 374 | if (const NamedDecl *ND = dyn_cast_or_null(Node->getDecl())) 375 | return ND->getNameAsString(); 376 | else 377 | return "< >"; 378 | } 379 | 380 | static bool isNodeHidden(const CallGraphNode *Node) { 381 | return false; 382 | } 383 | 384 | static std::string getGraphName(const CallGraph *CG) { 385 | return split(CG->getFullPath(), '/').back(); 386 | } 387 | }; 388 | } 389 | -------------------------------------------------------------------------------- /clang-mapper/CallGraph.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LZephyr on 2017/3/15. 3 | // 4 | 5 | #ifndef LIBTOOLING_CALLGRAPHBUILDER_H 6 | #define LIBTOOLING_CALLGRAPHBUILDER_H 7 | 8 | #include "clang/AST/DeclBase.h" 9 | #include "clang/AST/RecursiveASTVisitor.h" 10 | #include "llvm/ADT/DenseMap.h" 11 | #include "llvm/ADT/GraphTraits.h" 12 | #include "llvm/ADT/SetVector.h" 13 | #include 14 | #include 15 | #include "CallGraphAction.h" 16 | #include "Commons.h" 17 | 18 | using namespace std; 19 | 20 | namespace clang { 21 | class CallGraphNode; 22 | class CallGraphAction; 23 | 24 | class CallGraph : public RecursiveASTVisitor { 25 | friend class CallGraphNode; 26 | typedef llvm::DenseMap> 27 | RootsMapType; 28 | 29 | ASTContext &Context; 30 | std::string FullPath; // full path for this code file 31 | std::string BasePath; 32 | CallGraphOption Option; 33 | 34 | /// owns all caller node 35 | RootsMapType Roots; 36 | 37 | public: 38 | CallGraph(ASTContext &context, std::string filePath, std::string basePath); 39 | 40 | ~CallGraph(); 41 | 42 | /// \brief Populate the call graph with the functions in the given 43 | /// declaration. 44 | /// 45 | /// Recursively walks the declaration to find all the dependent Decls as well. 46 | void addToCallGraph(Decl *D) { 47 | TraverseDecl(D); 48 | } 49 | 50 | void setOption(CallGraphOption option) { 51 | this->Option = option; 52 | } 53 | 54 | /// \brief Determine if a declaration should be included in the graph. 55 | static bool canIncludeInGraph(const Decl *D); 56 | 57 | /// \brief Determine if a decl can be a caller node in the graph 58 | static bool canBeCallerInGraph(const Decl *D); 59 | 60 | /// \brief Lookup the node for the given declaration. 61 | CallGraphNode *getNode(Decl *); 62 | 63 | /// \brief Lookup the node for the given declaration. If none found, insert 64 | /// one into the graph. 65 | CallGraphNode *getOrInsertNode(Decl *); 66 | 67 | // void insertNode(Decl *); 68 | 69 | /// Iterators through all the elements in the graph. Note, this gives 70 | /// non-deterministic order. 71 | typedef RootsMapType::iterator iterator; 72 | typedef RootsMapType::const_iterator const_iterator; 73 | 74 | iterator begin() { return Roots.begin(); } 75 | iterator end() { return Roots.end(); } 76 | const_iterator begin() const { return Roots.begin(); } 77 | const_iterator end() const { return Roots.end(); } 78 | 79 | /// \brief Get the number of root nodes in the graph. 80 | unsigned size() const { return Roots.size(); } 81 | 82 | /// Iterators through all the nodes of the graph that have no parent. These 83 | /// are the unreachable nodes, which are either unused or are due to us 84 | /// failing to add a call edge due to the analysis imprecision. 85 | typedef llvm::SetVector::iterator nodes_iterator; 86 | typedef llvm::SetVector::const_iterator const_nodes_iterator; 87 | 88 | std::string getFullPath() const { 89 | return FullPath; 90 | } 91 | 92 | void print(raw_ostream &os) const; 93 | void dump() const; 94 | void output() const; 95 | bool generateGraphFile(std::string dotFile) const; 96 | 97 | /// Part of recursive declaration visitation. We recursively visit all the 98 | /// declarations to collect the root functions. 99 | bool VisitFunctionDecl(FunctionDecl *FD) { 100 | if (isInSystem(FD)) { 101 | return true; 102 | } 103 | 104 | // We skip function template definitions, as their semantics is 105 | // only determined when they are instantiated. 106 | if (canBeCallerInGraph(FD) && FD->isThisDeclarationADefinition()) { 107 | addRootNode(FD); 108 | } 109 | return true; 110 | } 111 | 112 | /// Part of recursive declaration visitation. 113 | bool VisitObjCMethodDecl(ObjCMethodDecl *MD) { 114 | if (isInSystem(MD)) { 115 | return true; 116 | } 117 | 118 | if (canBeCallerInGraph(MD)) { 119 | addRootNode(MD); 120 | } 121 | return true; 122 | } 123 | 124 | // We are only collecting the declarations, so do not step into the bodies. 125 | bool TraverseStmt(Stmt *S) { 126 | return true; 127 | } 128 | 129 | bool shouldWalkTypesOfTypeLocs() const { 130 | return false; 131 | } 132 | 133 | bool isInSystem(Decl *decl) { 134 | if (Context.getSourceManager().isInSystemHeader(decl->getLocation()) || 135 | Context.getSourceManager().isInExternCSystemHeader(decl->getLocation())) { 136 | return true; 137 | } 138 | return false; 139 | } 140 | 141 | private: 142 | /// Add a root node to call graph 143 | void addRootNode(Decl *decl); 144 | }; 145 | 146 | class CallGraphNode { 147 | public: 148 | typedef CallGraphNode* CallRecord; 149 | 150 | private: 151 | /// \brief The function/method declaration. 152 | Decl *FD; 153 | 154 | /// \brief The list of functions called from this node. 155 | SmallVector CalledFunctions; 156 | 157 | public: 158 | CallGraphNode(Decl *D) : FD(D) {} 159 | 160 | typedef SmallVectorImpl::iterator iterator; 161 | typedef SmallVectorImpl::const_iterator const_iterator; 162 | 163 | /// Iterators through all the callees/children of the node. 164 | inline iterator begin() { return CalledFunctions.begin(); } 165 | inline iterator end() { return CalledFunctions.end(); } 166 | inline const_iterator begin() const { return CalledFunctions.begin(); } 167 | inline const_iterator end() const { return CalledFunctions.end(); } 168 | 169 | bool operator==(const CallGraphNode &node) { 170 | return this->getDecl() == node.getDecl(); 171 | } 172 | bool operator!=(const CallGraphNode &node) { 173 | return this->getDecl() != node.getDecl(); 174 | } 175 | 176 | inline bool empty() const {return CalledFunctions.empty(); } 177 | inline unsigned size() const {return CalledFunctions.size(); } 178 | 179 | void addCallee(CallGraphNode *N) { 180 | if (std::find(CalledFunctions.begin(), CalledFunctions.end(), N) != CalledFunctions.end()) { 181 | return; 182 | } 183 | CalledFunctions.push_back(N); 184 | } 185 | 186 | Decl *getDecl() const { return FD; } 187 | 188 | void print(raw_ostream &os) const; 189 | void dump() const; 190 | 191 | std::string getNameAsString() const; 192 | }; 193 | 194 | } // end clang namespace 195 | 196 | // Graph traits for iteration, viewing. 197 | namespace llvm { 198 | template <> struct GraphTraits { 199 | typedef clang::CallGraphNode NodeType; 200 | typedef clang::CallGraphNode *NodeRef; 201 | typedef NodeType::iterator ChildIteratorType; 202 | 203 | static NodeType *getEntryNode(clang::CallGraphNode *CGN) { return CGN; } 204 | static inline ChildIteratorType child_begin(NodeType *N) { return N->begin(); } 205 | static inline ChildIteratorType child_end(NodeType *N) { return N->end(); } 206 | }; 207 | 208 | template <> struct GraphTraits { 209 | typedef const clang::CallGraphNode NodeType; 210 | typedef const clang::CallGraphNode *NodeRef; 211 | typedef NodeType::const_iterator ChildIteratorType; 212 | 213 | static NodeType *getEntryNode(const clang::CallGraphNode *CGN) { return CGN; } 214 | static inline ChildIteratorType child_begin(NodeType *N) { return N->begin();} 215 | static inline ChildIteratorType child_end(NodeType *N) { return N->end(); } 216 | }; 217 | 218 | template <> struct GraphTraits 219 | : public GraphTraits { 220 | static clang::CallGraphNode * 221 | CGGetValue(clang::CallGraph::const_iterator::value_type &P) { 222 | return P.second.get(); 223 | } 224 | 225 | // nodes_iterator/begin/end - Allow iteration over all nodes in the graph 226 | typedef mapped_iterator 227 | nodes_iterator; 228 | 229 | static nodes_iterator nodes_begin (clang::CallGraph *CG) { 230 | return nodes_iterator(CG->begin(), &CGGetValue); 231 | } 232 | static nodes_iterator nodes_end (clang::CallGraph *CG) { 233 | return nodes_iterator(CG->end(), &CGGetValue); 234 | } 235 | 236 | static unsigned size(clang::CallGraph *CG) { 237 | return CG->size(); 238 | } 239 | }; 240 | 241 | template <> struct GraphTraits : 242 | public GraphTraits { 243 | static clang::CallGraphNode * 244 | CGGetValue(clang::CallGraph::const_iterator::value_type &P) { 245 | return P.second.get(); 246 | } 247 | 248 | // nodes_iterator/begin/end - Allow iteration over all nodes in the graph 249 | typedef mapped_iterator 251 | nodes_iterator; 252 | 253 | static nodes_iterator nodes_begin(const clang::CallGraph *CG) { 254 | return nodes_iterator(CG->begin(), &CGGetValue); 255 | } 256 | 257 | static nodes_iterator nodes_end(const clang::CallGraph *CG) { 258 | return nodes_iterator(CG->end(), &CGGetValue); 259 | } 260 | 261 | static unsigned size(const clang::CallGraph *CG) { 262 | return CG->size(); 263 | } 264 | }; 265 | 266 | } // end llvm namespace 267 | 268 | #endif //LIBTOOLING_CALLGRAPHBUILDER_H 269 | -------------------------------------------------------------------------------- /clang-mapper/CallGraphAction.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LZephyr on 2017/2/27. 3 | // 4 | 5 | #include "CallGraphAction.h" 6 | #include "CallGraph.h" 7 | 8 | void CallGraphConsumer::HandleTranslationUnit(clang::ASTContext &Context) { 9 | visitor->addToCallGraph(Context.getTranslationUnitDecl()); 10 | visitor->dump(); 11 | visitor->output(); 12 | } 13 | 14 | CallGraphConsumer::CallGraphConsumer(CompilerInstance &CI, std::string filename, std::string basePath, CallGraphOption option) { 15 | this->visitor = new CallGraph(CI.getASTContext(), filename, basePath); 16 | this->visitor->setOption(option); 17 | } 18 | 19 | std::unique_ptr CallGraphAction::CreateASTConsumer( 20 | clang::CompilerInstance &Compiler, llvm::StringRef InFile) { 21 | Compiler.getDiagnostics().setClient(new IgnoringDiagConsumer()); 22 | return std::unique_ptr(new CallGraphConsumer(Compiler, InFile, BasePath, option)); 23 | } 24 | 25 | std::unique_ptr CallGraphAction::newASTConsumer(clang::CompilerInstance &CI, StringRef InFile) { 26 | llvm::errs() << "Scan " << InFile << "\n"; 27 | CI.getDiagnostics().setClient(new IgnoringDiagConsumer()); 28 | return llvm::make_unique(CI, InFile, BasePath, option); 29 | } 30 | -------------------------------------------------------------------------------- /clang-mapper/CallGraphAction.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LZephyr on 2017/2/27. 3 | // 4 | 5 | #ifndef LIBTOOLING_CALLGRAPHACTION_H 6 | #define LIBTOOLING_CALLGRAPHACTION_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "Commons.h" 16 | 17 | using namespace clang; 18 | using namespace std; 19 | 20 | namespace clang { 21 | class CallGraph; 22 | class CallGraphAction; 23 | class CallGraphConsumer; 24 | 25 | class CallGraphConsumer : public clang::ASTConsumer { 26 | public: 27 | explicit CallGraphConsumer(CompilerInstance &CI, std::string filename, std::string basePath, CallGraphOption option); 28 | virtual void HandleTranslationUnit(clang::ASTContext &Context); 29 | private: 30 | CallGraph *visitor; 31 | }; 32 | 33 | class CallGraphAction : public clang::ASTFrontendAction { 34 | private: 35 | CallGraphOption option; 36 | std::string BasePath; 37 | public: 38 | virtual std::unique_ptr CreateASTConsumer( 39 | clang::CompilerInstance &Compiler, llvm::StringRef InFile); 40 | 41 | void setOption(CallGraphOption option) { 42 | this->option = option; 43 | } 44 | 45 | void setBasePath(std::string basePath) { 46 | this->BasePath = basePath; 47 | } 48 | 49 | std::unique_ptr newASTConsumer(clang::CompilerInstance &CI, StringRef InFile); 50 | }; 51 | } 52 | 53 | #endif //LIBTOOLING_CALLGRAPHACTION_H 54 | -------------------------------------------------------------------------------- /clang-mapper/ClangMapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "llvm/Option/OptTable.h" 6 | #include "clang/Driver/Options.h" 7 | #include "llvm/Support/FileSystem.h" 8 | #include "CallGraphAction.h" 9 | #include 10 | #include 11 | #include 12 | #include "Commons.h" 13 | 14 | using namespace std; 15 | using namespace clang::tooling; 16 | using namespace llvm; 17 | 18 | static llvm::cl::OptionCategory MyToolCategory("clang-mapper option"); 19 | static std::unique_ptr Options(clang::driver::createDriverOptTable()); 20 | 21 | static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); 22 | 23 | static cl::opt 24 | DotOnly("dot-only", cl::desc("Generate dot file only"), cl::cat(MyToolCategory)); 25 | static cl::opt 26 | GraphOnly("graph-only", cl::desc("Generate graph file only"), cl::cat(MyToolCategory)); 27 | static cl::opt 28 | DotAndGraph("dot-graph", cl::desc("Generate both dot and graph file"), cl::cat(MyToolCategory)); 29 | static cl::opt 30 | IgnoreHeader("ignore-header", cl::desc("Ignore header file in the directory"), cl::cat(MyToolCategory)); 31 | 32 | /// Specification `newFrontendActionFactory` 33 | template <> 34 | inline std::unique_ptr clang::tooling::newFrontendActionFactory( 35 | clang::CallGraphAction *ConsumerFactory, SourceFileCallbacks *Callbacks) { 36 | 37 | class FrontendActionFactoryAdapter : public FrontendActionFactory { 38 | public: 39 | explicit FrontendActionFactoryAdapter(clang::CallGraphAction *ConsumerFactory, 40 | SourceFileCallbacks *Callbacks) 41 | : ConsumerFactory(ConsumerFactory), Callbacks(Callbacks) {} 42 | 43 | clang::FrontendAction *create() override { 44 | return new ConsumerFactoryAdaptor(ConsumerFactory, Callbacks); 45 | } 46 | 47 | private: 48 | class ConsumerFactoryAdaptor : public clang::ASTFrontendAction { 49 | public: 50 | ConsumerFactoryAdaptor(clang::CallGraphAction *ConsumerFactory, 51 | SourceFileCallbacks *Callbacks) 52 | : ConsumerFactory(ConsumerFactory), Callbacks(Callbacks) {} 53 | 54 | std::unique_ptr 55 | CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override { 56 | return ConsumerFactory->newASTConsumer(CI, InFile); 57 | } 58 | 59 | protected: 60 | bool BeginSourceFileAction(CompilerInstance &CI, 61 | StringRef Filename) override { 62 | if (!clang::ASTFrontendAction::BeginSourceFileAction(CI, Filename)) 63 | return false; 64 | if (Callbacks) 65 | return Callbacks->handleBeginSource(CI, Filename); 66 | return true; 67 | } 68 | void EndSourceFileAction() override { 69 | if (Callbacks) 70 | Callbacks->handleEndSource(); 71 | clang::ASTFrontendAction::EndSourceFileAction(); 72 | } 73 | 74 | private: 75 | clang::CallGraphAction *ConsumerFactory; 76 | SourceFileCallbacks *Callbacks; 77 | }; 78 | clang::CallGraphAction *ConsumerFactory; 79 | SourceFileCallbacks *Callbacks; 80 | }; 81 | 82 | return std::unique_ptr( 83 | new FrontendActionFactoryAdapter(ConsumerFactory, Callbacks)); 84 | } 85 | 86 | /// Convert related path to absolute path 87 | std::string getAbsolutePath(std::string relatedPath) { 88 | char cStr[PATH_MAX]; 89 | realpath(relatedPath.c_str(), cStr); 90 | return string(cStr); 91 | } 92 | 93 | /// Return if filePath a code file(.h/.m/.c/.cpp) 94 | bool hasSuffix(string rawStr, string suffix) { 95 | return rawStr.find(suffix) == (rawStr.length() - suffix.length()); 96 | } 97 | 98 | bool isCodeFile(string filePath, bool ignoreHeader) { 99 | if (hasSuffix(filePath, ".h")) { 100 | return !ignoreHeader; 101 | } else { 102 | return hasSuffix(filePath, ".m") || 103 | hasSuffix(filePath, ".mm") || 104 | hasSuffix(filePath, ".c") || 105 | hasSuffix(filePath, ".cpp"); 106 | } 107 | } 108 | 109 | /// Get all .h/.m/.cpp/.c files in rootDir 110 | void getFilesInDir(string rootDir, vector &files, bool ignoreHeader) { 111 | if (sys::fs::exists(rootDir)) { 112 | std::error_code code; 113 | sys::fs::recursive_directory_iterator end; 114 | for (auto it = sys::fs::recursive_directory_iterator(rootDir, code); it != end; it.increment(code)) { 115 | if (code) { 116 | llvm::errs() << "Error: " << code.message() << "\n"; 117 | break; 118 | } 119 | 120 | string path = (*it).path(); 121 | if (isCodeFile(path, ignoreHeader)) { 122 | files.push_back(getAbsolutePath(path)); 123 | } 124 | } 125 | 126 | } else { 127 | llvm::errs() << "Directory " << rootDir << " not exists!\n"; 128 | } 129 | } 130 | 131 | int main(int argc, const char **argv) { 132 | // recursive get all files in directory path arg 133 | bool ignoreHeader = false; 134 | for (int i = 0; i < argc; i++) { 135 | if (strcmp("-ignore-header", argv[i]) == 0) { 136 | ignoreHeader = true; 137 | } 138 | } 139 | 140 | vector commands; 141 | string outputRootPath = "./"; 142 | for (int i = 0; i < argc; ++i) { 143 | const char *arg = argv[i]; 144 | if (sys::fs::is_directory(arg)) { 145 | outputRootPath = string(arg); 146 | getFilesInDir(arg, commands, ignoreHeader); 147 | } else if (sys::fs::is_regular_file(arg)) { 148 | string fullPath = string(argv[i]); 149 | commands.push_back(getAbsolutePath(fullPath)); 150 | } else { 151 | commands.push_back(string(argv[i])); 152 | } 153 | } 154 | 155 | // convert commmands to `const char **` 156 | const char **argList = new const char*[commands.size()]; 157 | for (vector::size_type i = 0; i < commands.size(); ++i) { 158 | argList[i] = commands[i].c_str(); 159 | } 160 | argc = commands.size(); 161 | 162 | CommonOptionsParser OptionsParser(argc, argList, MyToolCategory); 163 | ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); 164 | 165 | clang::CallGraphAction action; 166 | action.setBasePath(getAbsolutePath(outputRootPath)); 167 | if (DotOnly) { 168 | action.setOption(O_DotOnly); 169 | } else if (DotAndGraph) { 170 | action.setOption(O_DotAndGraph); 171 | } else { 172 | action.setOption(O_GraphOnly); 173 | } 174 | 175 | Tool.run(newFrontendActionFactory(&action).get()); 176 | return 0; 177 | } 178 | -------------------------------------------------------------------------------- /clang-mapper/Commons.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by LZephyr on 2017/3/21. 3 | // 4 | 5 | #ifndef LIBTOOLING_COMMONS_H 6 | #define LIBTOOLING_COMMONS_H 7 | 8 | enum CallGraphOption: unsigned { 9 | /// Only generate graph file 10 | O_GraphOnly = 0, 11 | 12 | /// Only generate dot file 13 | O_DotOnly, 14 | 15 | /// Generate both dot and graph file 16 | O_DotAndGraph 17 | }; 18 | 19 | #endif //LIBTOOLING_COMMONS_H 20 | -------------------------------------------------------------------------------- /release/clang-mapper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/L-Zephyr/clang-mapper/70caa8d28f8f6f7a4fdbfadda65f34c37d9acf16/release/clang-mapper --------------------------------------------------------------------------------