3 #include "mono/metadata/class.h"
4 #include "mono/metadata/assembly.h"
5 #include "mono/metadata/tokentype.h"
6 #include "mono/metadata/opcodes.h"
7 #include "mono/metadata/tabledefs.h"
8 #include "mono/metadata/cil-coff.h" /* MonoCLIImageInfo */
9 #include "mono/metadata/mono-endian.h"
10 #include "mono/metadata/appdomain.h" /* mono_init */
13 static int include_namespace = 0;
14 static int max_depth = 6;
15 static int verbose = 0;
16 static char *graph_properties = "\tnode [fontsize=8.0]\n\tedge [len=2,color=red]\n";
19 output_type_edge (MonoClass *first, MonoClass *second) {
20 if (include_namespace)
21 fprintf (output, "\t\"%s.%s\" -> \"%s.%s\"\n", first->name_space, first->name, second->name_space, second->name);
23 fprintf (output, "\t\"%s\" -> \"%s\"\n", first->name, second->name);
27 print_subtypes (MonoImage *image, MonoClass *class, int depth) {
32 if (depth++ > max_depth)
35 t = &image->tables [MONO_TABLE_TYPEDEF];
37 index = mono_metadata_token_index (class->type_token);
38 index <<= TYPEDEFORREF_BITS;
39 index |= TYPEDEFORREF_TYPEDEF;
42 for (i = 0; i < t->rows; ++i) {
43 if (index == mono_metadata_decode_row_col (t, i, MONO_TYPEDEF_EXTENDS)) {
44 child = mono_class_get (image, MONO_TOKEN_TYPE_DEF | (i + 1));
45 output_type_edge (class, child);
46 print_subtypes (image, child, depth);
52 type_graph (MonoImage *image, char* cname) {
60 cname = g_strdup (cname);
61 p = strrchr (cname, '.');
69 class = mono_class_from_name (image, name_space, cname);
71 g_error ("class %s.%s not found", name_space, cname);
72 fprintf (output, "digraph blah {\n");
73 fprintf (output, "%s", graph_properties);
75 /* go back and print the parents for the node as well: not sure it's a good idea */
76 for (parent = class->parent; parent; parent = parent->parent) {
77 output_type_edge (parent, child);
80 print_subtypes (image, class, depth);
81 fprintf (output, "}\n");
85 interface_graph (MonoImage *image, char* cname) {
90 guint32 cols [MONO_INTERFACEIMPL_SIZE];
91 guint32 index, i, count = 0;
92 MonoTableInfo *intf = &image->tables [MONO_TABLE_INTERFACEIMPL];
94 cname = g_strdup (cname);
95 p = strrchr (cname, '.');
103 class = mono_class_from_name (image, name_space, cname);
105 g_error ("interface %s.%s not found", name_space, cname);
106 /* chek if it's really an interface... */
107 fprintf (output, "digraph interface {\n");
108 fprintf (output, "%s", graph_properties);
109 /* TODO: handle inetrface defined in one image and class defined in another. */
110 index = mono_metadata_token_index (class->type_token);
111 index <<= TYPEDEFORREF_BITS;
112 index |= TYPEDEFORREF_TYPEDEF;
113 for (i = 0; i < intf->rows; ++i) {
114 mono_metadata_decode_row (intf, i, cols, MONO_INTERFACEIMPL_SIZE);
115 /*g_print ("index: %d [%d implements %d]\n", index, cols [MONO_INTERFACEIMPL_CLASS], cols [MONO_INTERFACEIMPL_INTERFACE]);*/
116 if (index == cols [MONO_INTERFACEIMPL_INTERFACE]) {
117 child = mono_class_get (image, MONO_TOKEN_TYPE_DEF | cols [MONO_INTERFACEIMPL_CLASS]);
118 output_type_edge (class, child);
122 fprintf (output, "}\n");
123 if (verbose && !count)
124 g_print ("No class implements %s.%s\n", class->name_space, class->name);
129 get_type (GString *res, MonoType *type) {
130 switch (type->type) {
132 g_string_append (res, "void"); break;
134 g_string_append (res, "char"); break;
135 case MONO_TYPE_BOOLEAN:
136 g_string_append (res, "bool"); break;
138 g_string_append (res, "byte"); break;
140 g_string_append (res, "sbyte"); break;
142 g_string_append (res, "uint16"); break;
144 g_string_append (res, "int16"); break;
146 g_string_append (res, "int"); break;
148 g_string_append (res, "uint"); break;
150 g_string_append (res, "ulong"); break;
152 g_string_append (res, "long"); break;
153 case MONO_TYPE_FNPTR: /* who cares for the exact signature? */
154 g_string_append (res, "*()"); break;
156 g_string_append (res, "intptr"); break;
158 g_string_append (res, "uintptr"); break;
160 g_string_append (res, "single"); break;
162 g_string_append (res, "double"); break;
163 case MONO_TYPE_STRING:
164 g_string_append (res, "string"); break;
165 case MONO_TYPE_OBJECT:
166 g_string_append (res, "object"); break;
168 get_type (res, type->data.type);
169 g_string_append_c (res, '*');
171 case MONO_TYPE_ARRAY:
172 get_type (res, type->data.array->type);
173 g_string_append (res, "[,]"); /* not the full array info.. */
175 case MONO_TYPE_SZARRAY:
176 get_type (res, type->data.type);
177 g_string_append (res, "[]");
179 case MONO_TYPE_CLASS:
180 case MONO_TYPE_VALUETYPE: {
181 MonoClass *class = type->data.klass;
183 g_string_append (res, "Unknown");
186 if (include_namespace && *(class->name_space))
187 g_string_sprintfa (res, "%s.", class->name_space);
188 g_string_sprintfa (res, "%s", class->name);
195 g_string_append_c (res, '&');
199 get_signature (MonoMethod *method) {
201 static GHashTable *hash = NULL;
206 hash = g_hash_table_new (g_direct_hash, g_direct_equal);
207 if ((result = g_hash_table_lookup (hash, method)))
210 res = g_string_new ("");
211 if (include_namespace && *(method->klass->name_space))
212 g_string_sprintfa (res, "%s.", method->klass->name_space);
213 g_string_sprintfa (res, "%s:%s(", method->klass->name, method->name);
214 for (i = 0; i < method->signature->param_count; ++i) {
216 g_string_append_c (res, ',');
217 get_type (res, method->signature->params [i]);
219 g_string_sprintfa (res, ")");
220 g_hash_table_insert (hash, method, res->str);
223 g_string_free (res, FALSE);
229 output_method_edge (MonoMethod *first, MonoMethod *second) {
230 char * f = get_signature (first);
231 char * s = get_signature (second);
233 fprintf (output, "\t\"%s\" -> \"%s\"\n", f, s);
237 * We need to handle virtual calls is derived types.
238 * We could check what types implement the method and
239 * disassemble all of them: this can make the graph to explode.
240 * We could try and keep track of the 'this' pointer type and
241 * consider only the methods in the classes derived from that:
242 * this can reduce the graph complexity somewhat (and it would
243 * be the more correct approach).
246 print_method (MonoMethod *method, int depth) {
247 const MonoOpcode *opcode;
248 MonoMethodHeader *header;
250 const unsigned char *ip;
253 if (depth++ > max_depth)
255 if (method->info) /* avoid recursion */
257 method->info = method;
259 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME))
261 if (method->flags & (METHOD_ATTRIBUTE_PINVOKE_IMPL | METHOD_ATTRIBUTE_ABSTRACT))
264 header = ((MonoMethodNormal *)method)->header;
267 hash = g_hash_table_new (g_direct_hash, g_direct_equal);
269 while (ip < (header->code + header->code_size)) {
277 opcode = &mono_opcodes [i];
279 switch (opcode->argument) {
284 case MonoInlineField:
286 case MonoInlineString:
288 case MonoShortInlineR:
290 case MonoInlineBrTarget:
296 case MonoShortInlineVar:
297 case MonoShortInlineI:
298 case MonoShortInlineBrTarget:
301 case MonoInlineSwitch: {
313 case MonoInlineMethod: {
319 called = mono_get_method (method->klass->image, token, NULL);
322 if (g_hash_table_lookup (hash, called))
324 g_hash_table_insert (hash, called, called);
325 output_method_edge (method, called);
326 print_method (called, depth);
330 g_assert_not_reached ();
333 g_hash_table_destroy (hash);
337 method_graph (MonoImage *image, char *name) {
339 MonoMethod *method = NULL;
342 method = mono_get_method (image, ((MonoCLIImageInfo*)image->image_info)->cli_cli_header.ch_entry_point, NULL);
344 g_error ("Cannot find entry point");
346 /* search the method */
347 MonoTableInfo *tdef = &image->tables [MONO_TABLE_TYPEDEF];
348 MonoTableInfo *methods = &image->tables [MONO_TABLE_METHOD];
349 char *class_name, *class_nspace, *method_name, *use_args;
350 int use_namespace, i;
352 class_nspace = g_strdup (name);
353 use_args = strchr (class_nspace, '(');
356 method_name = strrchr (class_nspace, ':');
358 g_error ("Invalid method name %s", name);
360 class_name = strrchr (class_nspace, '.');
365 class_name = class_nspace;
368 for (i = 0; i < methods->rows; ++i) {
369 guint32 index = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
371 const char *n = mono_metadata_string_heap (image, index);
373 if (strcmp (n, method_name))
375 index = mono_metadata_typedef_from_method (image, i + 1);
376 idx = mono_metadata_decode_row_col (tdef, index - 1, MONO_TYPEDEF_NAME);
377 n = mono_metadata_string_heap (image, idx);
378 if (strcmp (n, class_name))
381 idx = mono_metadata_decode_row_col (tdef, index - 1, MONO_TYPEDEF_NAMESPACE);
382 n = mono_metadata_string_heap (image, idx);
383 if (strcmp (n, class_nspace))
386 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
388 /* check the signature */
389 n = get_signature (method);
390 if (strcmp (n, name) == 0)
393 g_print ("signature check failed: '%s' != '%s'.\n", n, name);
398 g_error ("Cannot find method %s", name);
399 g_free (class_nspace);
401 fprintf (output, "digraph blah {\n");
402 fprintf (output, "%s", graph_properties);
404 print_method (method, depth);
406 fprintf (output, "}\n");
411 printf ("monograph 0.1 Copyright (c) 2002 Ximian, Inc\n");
412 printf ("Create call graph or type hierarchy information from CIL assemblies.\n");
413 printf ("Usage: monograph [options] [assembly [typename|methodname]]\n");
414 printf ("Valid options are:\n");
415 printf ("\t-c|--call output call graph instead of type hierarchy\n");
416 printf ("\t-d|--depth num max depth recursion (default: 6)\n");
417 printf ("\t-o|--output filename write graph to file filename (default: stdout)\n");
418 printf ("\t-f|--fullname include namespace in type and method names\n");
419 printf ("\t-n|--neato invoke neato directly\n");
420 printf ("\t-v|--verbose verbose operation\n");
421 printf ("The default assembly is corlib.dll. The default method for\n");
422 printf ("the --call option is the entrypoint.\n");
423 printf ("When the --neato option is used the output type info is taken\n");
424 printf ("from the output filename extension.\n");
425 printf ("Sample runs:\n");
426 printf ("\tmonograph -n -o vt.png corlib.dll System.ValueType\n");
427 printf ("\tmonograph -n -o expr.png mcs.exe Mono.CSharp.Expression\n");
428 printf ("\tmonograph -d 3 -n -o callgraph.png -c mis.exe\n");
440 * * virtual method calls as explained above
441 * * maybe track field references
442 * * track what exceptions a method could throw?
443 * * for some inputs neato appears to hang or take a long time: kill it?
444 * * allow passing additional command-line arguments to neato
445 * * allow setting different graph/node/edge options directly
446 * * option to avoid specialname methods
447 * * make --neato option the default?
448 * * use multiple classes/method roots?
450 * * reverse call graph: given a method what methods call it?
453 main (int argc, char *argv[]) {
454 MonoAssembly *assembly;
457 char *outputfile = NULL;
458 int graphtype = GRAPH_TYPES;
462 mono_init (argv [0]);
465 for (i = 1; i < argc; ++i) {
466 if (argv [i] [0] != '-')
468 if (strcmp (argv [i], "--call") == 0 || strcmp (argv [i], "-c") == 0) {
469 graphtype = GRAPH_CALL;
470 } else if (strcmp (argv [i], "--interface") == 0 || strcmp (argv [i], "-i") == 0) {
471 graphtype = GRAPH_INTERFACE;
472 } else if (strcmp (argv [i], "--fullname") == 0 || strcmp (argv [i], "-f") == 0) {
473 include_namespace = 1;
474 } else if (strcmp (argv [i], "--neato") == 0 || strcmp (argv [i], "-n") == 0) {
476 } else if (strcmp (argv [i], "--verbose") == 0 || strcmp (argv [i], "-v") == 0) {
478 } else if (strcmp (argv [i], "--output") == 0 || strcmp (argv [i], "-o") == 0) {
481 outputfile = argv [++i];
482 } else if (strcmp (argv [i], "--depth") == 0 || strcmp (argv [i], "-d") == 0) {
485 max_depth = atoi (argv [++i]);
494 cname = argv [i + 1];
496 aname = "corlib.dll";
497 if (!cname && (graphtype != GRAPH_CALL))
498 cname = "System.Object";
500 assembly = mono_assembly_open (aname, NULL, NULL);
502 g_error ("cannot open assembly %s", aname);
505 GString *command = g_string_new ("neato");
509 type = strrchr (outputfile, '.');
510 g_string_sprintfa (command, " -o %s", outputfile);
513 g_string_sprintfa (command, " -T%s", type + 1);
514 output = popen (command->str, "w");
516 g_error ("Cannot run neato");
517 } else if (outputfile) {
518 output = fopen (outputfile, "w");
520 g_error ("Cannot open file: %s", outputfile);
522 /* if it looks like a method name, we want a callgraph. */
523 if (cname && strchr (cname, ':'))
524 graphtype = GRAPH_CALL;
528 type_graph (assembly->image, cname);
531 method_graph (assembly->image, cname);
533 case GRAPH_INTERFACE:
534 interface_graph (assembly->image, cname);
537 g_error ("wrong graph type");
542 g_print ("waiting for neato.\n");
544 } else if (outputfile)