xref: /illumos-gate/usr/src/tools/smatch/src/graph.c (revision 332e84f52cdee3d0232bd5ac5306c42177959c96)
1 /* Copyright � International Business Machines Corp., 2006
2  *              Adelard LLP, 2007
3  *
4  * Author: Josh Triplett <josh@freedesktop.org>
5  *         Dan Sheridan <djs@adelard.com>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 
33 #include "lib.h"
34 #include "allocate.h"
35 #include "token.h"
36 #include "parse.h"
37 #include "symbol.h"
38 #include "expression.h"
39 #include "linearize.h"
40 
41 
42 /* Draw the subgraph for a given entrypoint. Includes details of loads
43  * and stores for globals, and marks return bbs */
44 static void graph_ep(struct entrypoint *ep)
45 {
46 	struct basic_block *bb;
47 	struct instruction *insn;
48 
49 	const char *fname, *sname;
50 
51 	fname = show_ident(ep->name->ident);
52 	sname = stream_name(ep->entry->bb->pos.stream);
53 
54 	printf("subgraph cluster%p {\n"
55 	       "    color=blue;\n"
56 	       "    label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">\n"
57 	       "             <TR><TD>%s</TD></TR>\n"
58 	       "             <TR><TD><FONT POINT-SIZE=\"21\">%s()</FONT></TD></TR>\n"
59 	       "           </TABLE>>;\n"
60 	       "    file=\"%s\";\n"
61 	       "    fun=\"%s\";\n"
62 	       "    ep=bb%p;\n",
63 	       ep, sname, fname, sname, fname, ep->entry->bb);
64 
65 	FOR_EACH_PTR(ep->bbs, bb) {
66 		struct basic_block *child;
67 		int ret = 0;
68 		const char * s = ", ls=\"[";
69 
70 		/* Node for the bb */
71 		printf("    bb%p [shape=ellipse,label=%d,line=%d,col=%d",
72 		       bb, bb->pos.line, bb->pos.line, bb->pos.pos);
73 
74 
75 		/* List loads and stores */
76 		FOR_EACH_PTR(bb->insns, insn) {
77 			switch(insn->opcode) {
78 			case OP_STORE:
79 				if (insn->symbol->type == PSEUDO_SYM) {
80 				  printf("%s store(%s)", s, show_ident(insn->symbol->sym->ident));
81 				  s = ",";
82 				}
83 				break;
84 
85 			case OP_LOAD:
86 				if (insn->symbol->type == PSEUDO_SYM) {
87 				  printf("%s load(%s)", s, show_ident(insn->symbol->sym->ident));
88 				  s = ",";
89 				}
90 				break;
91 
92 			case OP_RET:
93 				ret = 1;
94 				break;
95 
96 			}
97 		} END_FOR_EACH_PTR(insn);
98 		if (s[1] == 0)
99 			printf("]\"");
100 		if (ret)
101 			printf(",op=ret");
102 		printf("];\n");
103 
104 		/* Edges between bbs; lower weight for upward edges */
105 		FOR_EACH_PTR(bb->children, child) {
106 			printf("    bb%p -> bb%p [op=br, %s];\n", bb, child,
107 			       (bb->pos.line > child->pos.line) ? "weight=5" : "weight=10");
108 		} END_FOR_EACH_PTR(child);
109 	} END_FOR_EACH_PTR(bb);
110 
111 	printf("}\n");
112 }
113 
114 
115 /* Insert edges for intra- or inter-file calls, depending on the value
116  * of internal. Bold edges are used for calls with destinations;
117  * dashed for calls to external functions */
118 static void graph_calls(struct entrypoint *ep, int internal)
119 {
120 	struct basic_block *bb;
121 	struct instruction *insn;
122 
123 	show_ident(ep->name->ident);
124 	stream_name(ep->entry->bb->pos.stream);
125 
126 	FOR_EACH_PTR(ep->bbs, bb) {
127 		if (!bb)
128 			continue;
129 		if (!bb->parents && !bb->children && !bb->insns && verbose < 2)
130 			continue;
131 
132 		FOR_EACH_PTR(bb->insns, insn) {
133 			if (insn->opcode == OP_CALL &&
134 			    internal == !(insn->func->sym->ctype.modifiers & MOD_EXTERN)) {
135 
136 				/* Find the symbol for the callee's definition */
137 				struct symbol * sym;
138 				if (insn->func->type == PSEUDO_SYM) {
139 					for (sym = insn->func->sym->ident->symbols;
140 					     sym; sym = sym->next_id) {
141 						if (sym->namespace & NS_SYMBOL && sym->ep)
142 							break;
143 					}
144 
145 					if (sym)
146 						printf("bb%p -> bb%p"
147 						       "[label=%d,line=%d,col=%d,op=call,style=bold,weight=30];\n",
148 						       bb, sym->ep->entry->bb,
149 						       insn->pos.line, insn->pos.line, insn->pos.pos);
150 					else
151 						printf("bb%p -> \"%s\" "
152 						       "[label=%d,line=%d,col=%d,op=extern,style=dashed];\n",
153 						       bb, show_pseudo(insn->func),
154 						       insn->pos.line, insn->pos.line, insn->pos.pos);
155 				}
156 			}
157 		} END_FOR_EACH_PTR(insn);
158 	} END_FOR_EACH_PTR(bb);
159 }
160 
161 int main(int argc, char **argv)
162 {
163 	struct string_list *filelist = NULL;
164 	char *file;
165 	struct symbol *sym;
166 
167 	struct symbol_list *fsyms, *all_syms=NULL;
168 
169 	printf("digraph call_graph {\n");
170 	fsyms = sparse_initialize(argc, argv, &filelist);
171 	concat_symbol_list(fsyms, &all_syms);
172 
173 	/* Linearize all symbols, graph internal basic block
174 	 * structures and intra-file calls */
175 	FOR_EACH_PTR_NOTAG(filelist, file) {
176 
177 		fsyms = sparse(file);
178 		concat_symbol_list(fsyms, &all_syms);
179 
180 		FOR_EACH_PTR(fsyms, sym) {
181 			expand_symbol(sym);
182 			linearize_symbol(sym);
183 		} END_FOR_EACH_PTR(sym);
184 
185 		FOR_EACH_PTR(fsyms, sym) {
186 			if (sym->ep) {
187 				graph_ep(sym->ep);
188 				graph_calls(sym->ep, 1);
189 			}
190 		} END_FOR_EACH_PTR_NOTAG(sym);
191 
192 	} END_FOR_EACH_PTR_NOTAG(file);
193 
194 	/* Graph inter-file calls */
195 	FOR_EACH_PTR(all_syms, sym) {
196 		if (sym->ep)
197 			graph_calls(sym->ep, 0);
198 	} END_FOR_EACH_PTR_NOTAG(sym);
199 
200 	printf("}\n");
201 	return 0;
202 }
203