Mon Mar 11 11:58:27 CET 2002 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / monograph / monograph.c
1 #include <glib.h>
2 #include <string.h>
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 */
11 #include "mono/metadata/debug-helpers.h"
12
13 static FILE *output;
14 static int include_namespace = 0;
15 static int max_depth = 6;
16 static int verbose = 0;
17 static char *graph_properties = "\tnode [fontsize=8.0]\n\tedge [len=2,color=red]\n";
18
19 static void
20 output_type_edge (MonoClass *first, MonoClass *second) {
21         if (include_namespace)
22                 fprintf (output, "\t\"%s.%s\" -> \"%s.%s\"\n", first->name_space, first->name, second->name_space, second->name);
23         else
24                 fprintf (output, "\t\"%s\" -> \"%s\"\n", first->name, second->name);
25 }
26
27 static void
28 print_subtypes (MonoImage *image, MonoClass *class, int depth) {
29         int i, index;
30         MonoTableInfo *t;
31         MonoClass *child;
32
33         if (depth++ > max_depth)
34                 return;
35
36         t = &image->tables [MONO_TABLE_TYPEDEF];
37         
38         index = mono_metadata_token_index (class->type_token);
39         index <<= TYPEDEFORREF_BITS;
40         index |= TYPEDEFORREF_TYPEDEF;
41
42         /* use a subgraph? */
43         for (i = 0; i < t->rows; ++i) {
44                 if (index == mono_metadata_decode_row_col (t, i, MONO_TYPEDEF_EXTENDS)) {
45                         child = mono_class_get (image, MONO_TOKEN_TYPE_DEF | (i + 1));
46                         output_type_edge (class, child);
47                         print_subtypes (image, child, depth);
48                 }
49         }
50 }
51
52 static void
53 type_graph (MonoImage *image, char* cname) {
54         MonoClass *class;
55         MonoClass *parent;
56         MonoClass *child;
57         char *name_space;
58         char *p;
59         int depth = 0;
60
61         cname = g_strdup (cname);
62         p = strrchr (cname, '.');
63         if (p) {
64                 name_space = cname;
65                 *p++ = 0;
66                 cname = p;
67         } else {
68                 name_space = "";
69         }
70         class = mono_class_from_name (image, name_space, cname);
71         if (!class) {
72                 g_print ("class %s.%s not found\n", name_space, cname);
73                 exit (1);
74         }
75         fprintf (output, "digraph blah {\n");
76         fprintf (output, "%s", graph_properties);
77         child = class;
78         /* go back and print the parents for the node as well: not sure it's a good idea */
79         for (parent = class->parent; parent; parent = parent->parent) {
80                 output_type_edge (parent, child);
81                 child = parent;
82         }
83         print_subtypes (image, class, depth);
84         fprintf (output, "}\n");
85 }
86
87 static void
88 interface_graph (MonoImage *image, char* cname) {
89         MonoClass *class;
90         MonoClass *child;
91         char *name_space;
92         char *p;
93         guint32 cols [MONO_INTERFACEIMPL_SIZE];
94         guint32 index, i, count = 0;
95         MonoTableInfo *intf = &image->tables [MONO_TABLE_INTERFACEIMPL];
96
97         cname = g_strdup (cname);
98         p = strrchr (cname, '.');
99         if (p) {
100                 name_space = cname;
101                 *p++ = 0;
102                 cname = p;
103         } else {
104                 name_space = "";
105         }
106         class = mono_class_from_name (image, name_space, cname);
107         if (!class) {
108                 g_print ("interface %s.%s not found\n", name_space, cname);
109                 exit (1);
110         }
111         /* chek if it's really an interface... */
112         fprintf (output, "digraph interface {\n");
113         fprintf (output, "%s", graph_properties);
114         /* TODO: handle inetrface defined in one image and class defined in another. */
115         index = mono_metadata_token_index (class->type_token);
116         index <<= TYPEDEFORREF_BITS;
117         index |= TYPEDEFORREF_TYPEDEF;
118         for (i = 0; i < intf->rows; ++i) {
119                 mono_metadata_decode_row (intf, i, cols, MONO_INTERFACEIMPL_SIZE);
120                 /*g_print ("index: %d [%d implements %d]\n", index, cols [MONO_INTERFACEIMPL_CLASS], cols [MONO_INTERFACEIMPL_INTERFACE]);*/
121                 if (index == cols [MONO_INTERFACEIMPL_INTERFACE]) {
122                         child = mono_class_get (image, MONO_TOKEN_TYPE_DEF | cols [MONO_INTERFACEIMPL_CLASS]);
123                         output_type_edge (class, child);
124                         count++;
125                 }
126         }
127         fprintf (output, "}\n");
128         if (verbose && !count)
129                 g_print ("No class implements %s.%s\n", class->name_space, class->name);
130
131 }
132
133 static char *
134 get_signature (MonoMethod *method) {
135         GString *res;
136         static GHashTable *hash = NULL;
137         char *result;
138
139         if (!hash)
140                 hash = g_hash_table_new (g_direct_hash, g_direct_equal);
141         if ((result = g_hash_table_lookup (hash, method)))
142                 return result;
143
144         res = g_string_new ("");
145         if (include_namespace && *(method->klass->name_space))
146                 g_string_sprintfa (res, "%s.", method->klass->name_space);
147         result = mono_signature_get_desc (method->signature, include_namespace);
148         g_string_sprintfa (res, "%s:%s(%s)", method->klass->name, method->name, result);
149         g_free (result);
150         g_hash_table_insert (hash, method, res->str);
151
152         result = res->str;
153         g_string_free (res, FALSE);
154         return result;
155                 
156 }
157
158 static void
159 output_method_edge (MonoMethod *first, MonoMethod *second) {
160         char * f = get_signature (first);
161         char * s = get_signature (second);
162         
163         fprintf (output, "\t\"%s\" -> \"%s\"\n", f, s);
164 }
165
166 /*
167  * We need to handle virtual calls is derived types.
168  * We could check what types implement the method and
169  * disassemble all of them: this can make the graph to explode.
170  * We could try and keep track of the 'this' pointer type and
171  * consider only the methods in the classes derived from that:
172  * this can reduce the graph complexity somewhat (and it would 
173  * be the more correct approach).
174  */
175 static void
176 print_method (MonoMethod *method, int depth) {
177         const MonoOpcode *opcode;
178         MonoMethodHeader *header;
179         GHashTable *hash;
180         const unsigned char *ip;
181         int i;
182
183         if (depth++ > max_depth)
184                 return;
185         if (method->info) /* avoid recursion */
186                 return;
187         method->info = method;
188
189         if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME))
190                 return;
191         if (method->flags & (METHOD_ATTRIBUTE_PINVOKE_IMPL | METHOD_ATTRIBUTE_ABSTRACT))
192                 return;
193
194         header = ((MonoMethodNormal *)method)->header;
195         ip = header->code;
196
197         hash = g_hash_table_new (g_direct_hash, g_direct_equal);
198         
199         while (ip < (header->code + header->code_size)) {
200                 if (*ip == 0xfe) {
201                         ++ip;
202                         i = *ip + 256;
203                 } else {
204                         i = *ip;
205                 }
206
207                 opcode = &mono_opcodes [i];
208
209                 switch (opcode->argument) {
210                 case MonoInlineNone:
211                         ++ip;
212                         break;
213                 case MonoInlineType:
214                 case MonoInlineField:
215                 case MonoInlineTok:
216                 case MonoInlineString:
217                 case MonoInlineSig:
218                 case MonoShortInlineR:
219                 case MonoInlineI:
220                 case MonoInlineBrTarget:
221                         ip += 5;
222                         break;
223                 case MonoInlineVar:
224                         ip += 3;
225                         break;
226                 case MonoShortInlineVar:
227                 case MonoShortInlineI:
228                 case MonoShortInlineBrTarget:
229                         ip += 2;
230                         break;
231                 case MonoInlineSwitch: {
232                         gint32 n;
233                         ++ip;
234                         n = read32 (ip);
235                         ip += 4;
236                         ip += 4 * n;
237                         break;
238                 }
239                 case MonoInlineR:
240                 case MonoInlineI8:
241                         ip += 9;
242                         break;
243                 case MonoInlineMethod: {
244                         guint32 token;
245                         MonoMethod *called;
246                         ip++;
247                         token = read32 (ip);
248                         ip += 4;
249                         called = mono_get_method (method->klass->image, token, NULL);
250                         if (!called)
251                                 break; /* warn? */
252                         if (g_hash_table_lookup (hash, called))
253                                 break;
254                         g_hash_table_insert (hash, called, called);
255                         output_method_edge (method, called);
256                         print_method (called, depth);
257                         break;
258                 }
259                 default:
260                         g_assert_not_reached ();
261                 }
262         }
263         g_hash_table_destroy (hash);
264 }
265
266 static void
267 method_graph (MonoImage *image, char *name) {
268         int depth = 0;
269         MonoMethod *method = NULL;
270         
271         if (!name) {
272                 guint32 token = ((MonoCLIImageInfo*)image->image_info)->cli_cli_header.ch_entry_point;
273                 if (!token || !(method = mono_get_method (image, token, NULL))) {
274                         g_print ("Cannot find entry point in %s: specify an explict method name.\n", image->name);
275                         exit (1);
276                 }
277         } else {
278                 /* search the method */
279                 MonoMethodDesc *desc;
280
281                 desc = mono_method_desc_new (name, include_namespace);
282                 if (!desc) {
283                         g_print ("Invalid method name %s\n", name);
284                         exit (1);
285                 }
286                 method = mono_method_desc_search_in_image (desc, image);
287                 if (!method) {
288                         g_print ("Cannot find method %s\n", name);
289                         exit (1);
290                 }
291         }
292         fprintf (output, "digraph blah {\n");
293         fprintf (output, "%s", graph_properties);
294
295         print_method (method, depth);
296         
297         fprintf (output, "}\n");
298 }
299
300 typedef struct MonoBasicBlock MonoBasicBlock;
301
302 struct MonoBasicBlock {
303         const unsigned char* cil_code;
304         gint32 cil_length;
305         gint dfn;
306         GList *in_bb;
307         GList *out_bb;
308 };
309
310 static const unsigned char *debug_start;
311
312 static void
313 link_bblock (MonoBasicBlock *from, MonoBasicBlock* to)
314 {
315         from->out_bb = g_list_prepend (from->out_bb, to);
316         to->in_bb = g_list_prepend (to->in_bb, from);
317         /*fprintf (stderr, "linking IL_%04x to IL_%04x\n", from->cil_code-debug_start, to->cil_code-debug_start);*/
318 }
319
320 static int
321 compare_bblock (void *a, void *b)
322 {
323         MonoBasicBlock **ab = a;
324         MonoBasicBlock **bb = b;
325
326         return (*ab)->cil_code - (*bb)->cil_code;
327 }
328
329 static GPtrArray*
330 mono_method_find_bblocks (MonoMethodHeader *header)
331 {
332         const unsigned char *ip, *end, *start;
333         const MonoOpcode *opcode;
334         int i, block_end = 0;
335         GPtrArray *result = g_ptr_array_new ();
336         GHashTable *table = g_hash_table_new (g_direct_hash, g_direct_equal);
337         MonoBasicBlock *entry_bb, *end_bb, *bb, *target;
338
339         ip = header->code;
340         end = ip + header->code_size;
341         debug_start = ip;
342
343         entry_bb = g_new0 (MonoBasicBlock, 1);
344         end_bb = g_new0 (MonoBasicBlock, 1);
345         g_ptr_array_add (result, entry_bb);
346         g_ptr_array_add (result, end_bb);
347
348         bb = g_new0 (MonoBasicBlock, 1);
349         bb->cil_code = ip;
350         g_ptr_array_add (result, bb);
351         link_bblock (entry_bb, bb);
352         g_hash_table_insert (table, (char*)ip, bb);
353         block_end = TRUE;
354
355         /* handle exception code blocks... */
356         while (ip < end) {
357                 start = ip;
358                 if ((target = g_hash_table_lookup (table, ip)) && target != bb) {
359                         link_bblock (bb, target);
360                         bb = target;
361                         block_end = FALSE;
362                 }
363                 if (block_end) {
364                         /*fprintf (stderr, "processing bbclok at IL_%04x\n", ip - header->code);*/
365                         if (!(bb = g_hash_table_lookup (table, ip))) {
366                                 bb = g_new0 (MonoBasicBlock, 1);
367                                 bb->cil_code = ip;
368                                 g_ptr_array_add (result, bb);
369                                 g_hash_table_insert (table, (char*)ip, bb);
370                         }
371                         block_end = FALSE;
372                 }
373                 if (*ip == 0xfe) {
374                         ++ip;
375                         i = *ip + 256;
376                 } else {
377                         i = *ip;
378                 }
379
380                 opcode = &mono_opcodes [i];
381                 switch (opcode->flow_type) {
382                 case MONO_FLOW_RETURN:
383                         link_bblock (bb, end_bb);
384                 case MONO_FLOW_ERROR:
385                         block_end = 1;
386                         break;
387                 case MONO_FLOW_BRANCH: /* we handle branch when checking the argument type */
388                 case MONO_FLOW_COND_BRANCH:
389                 case MONO_FLOW_CALL:
390                 case MONO_FLOW_NEXT:
391                 case MONO_FLOW_META:
392                         break;
393                 default:
394                         g_assert_not_reached ();
395                 }
396                 switch (opcode->argument) {
397                 case MonoInlineNone:
398                         ++ip;
399                         break;
400                 case MonoInlineType:
401                 case MonoInlineField:
402                 case MonoInlineMethod:
403                 case MonoInlineTok:
404                 case MonoInlineString:
405                 case MonoInlineSig:
406                 case MonoShortInlineR:
407                 case MonoInlineI:
408                         ip += 5;
409                         break;
410                 case MonoInlineVar:
411                         ip += 3;
412                         break;
413                 case MonoShortInlineVar:
414                 case MonoShortInlineI:
415                         ip += 2;
416                         break;
417                 case MonoShortInlineBrTarget:
418                 case MonoInlineBrTarget:
419                         ip++;
420                         if (opcode->argument == MonoShortInlineBrTarget) {
421                                 i = (signed char)*ip;
422                                 ip++;
423                         } else {
424                                 i = (gint32) read32 (ip);
425                                 ip += 4;
426                         }
427                         if (opcode->flow_type == MONO_FLOW_COND_BRANCH) {
428                                 if (!(target = g_hash_table_lookup (table, ip))) {
429                                         target = g_new0 (MonoBasicBlock, 1);
430                                         target->cil_code = ip;
431                                         g_ptr_array_add (result, target);
432                                         g_hash_table_insert (table, (char*)ip, target);
433                                 }
434                                 link_bblock (bb, target);
435                         }
436                         if (!(target = g_hash_table_lookup (table, ip + i))) {
437                                 target = g_new0 (MonoBasicBlock, 1);
438                                 target->cil_code = ip + i;
439                                 g_ptr_array_add (result, target);
440                                 g_hash_table_insert (table, (char*)ip + i, target);
441                         }
442                         link_bblock (bb, target);
443                         block_end = 1;
444                         break;
445                 case MonoInlineSwitch: {
446                         gint32 n;
447                         char *itarget, *st;
448                         ++ip;
449                         n = read32 (ip);
450                         ip += 4;
451                         st = (char*)ip + 4 * n;
452
453                         for (i = 0; i < n; i++) {
454                                 itarget = st + read32 (ip);
455                                 ip += 4;
456                                 if (!(target = g_hash_table_lookup (table, itarget))) {
457                                         target = g_new0 (MonoBasicBlock, 1);
458                                         target->cil_code = itarget;
459                                         g_ptr_array_add (result, target);
460                                         g_hash_table_insert (table, itarget, target);
461                                 }
462                                 link_bblock (bb, target);
463                         }
464                         /*
465                          * Note: the code didn't set block_end in switch.
466                          */
467                         break;
468                 }
469                 case MonoInlineR:
470                 case MonoInlineI8:
471                         ip += 9;
472                         break;
473                 default:
474                         g_assert_not_reached ();
475                 }
476
477         }
478         g_hash_table_destroy (table);
479         qsort (result->pdata, result->len, sizeof (gpointer), compare_bblock);
480         /* skip entry and end */
481         bb = target = NULL;
482         for (i = 2; i < result->len; ++i) {
483                 bb = (MonoBasicBlock*)g_ptr_array_index (result, i);
484                 if (target)
485                         target->cil_length = bb->cil_code - target->cil_code;
486                 target = bb;
487                 /*fprintf (stderr, "bblock %d at IL_%04x:\n", i, bb->cil_code - header->code);*/
488         }
489         bb->cil_length = header->code + header->code_size - bb->cil_code;
490         return result;
491 }
492
493 static char*
494 indenter (MonoDisHelper *dh, MonoMethod *method, guint32 ip_offset)
495 {
496         return g_strdup (" ");
497 }
498
499 static MonoDisHelper graph_dh = {
500         "\\l",
501         NULL,
502         "IL_%04x",
503         indenter, 
504         NULL,
505         NULL
506 };
507
508 static void
509 df_visit (MonoBasicBlock *bb, int *dfn, const unsigned char* code)
510 {
511         GList *tmp;
512         MonoBasicBlock *next;
513         
514         if (bb->dfn)
515                 return;
516         ++(*dfn);
517         bb->dfn = *dfn;
518         for (tmp = bb->out_bb; tmp; tmp = tmp->next) {
519                 next = tmp->data;
520                 if (!next->dfn) {
521                         if (!bb->cil_code)
522                                 fprintf (output, "\t\"DF entry\" -> \"IL_%04x (%d)\"\n", next->cil_code - code, *dfn + 1);
523                         else
524                                 fprintf (output, "\t\"IL_%04x (%d)\" -> \"IL_%04x (%d)\"\n", bb->cil_code - code, bb->dfn, next->cil_code - code, *dfn + 1);
525                         df_visit (next, dfn, code);
526                 }
527         }
528 }
529
530 static void
531 print_method_cfg (MonoMethod *method) {
532         GPtrArray *bblocks;
533         GList *tmp;
534         MonoBasicBlock *bb, *target;
535         MonoMethodHeader *header;
536         int i, dfn;
537         char *code;
538
539         header = ((MonoMethodNormal*)method)->header;
540         bblocks = mono_method_find_bblocks (header);
541         for (i = 0; i < bblocks->len; ++i) {
542                 bb = (MonoBasicBlock*)g_ptr_array_index (bblocks, i);
543                 if (i == 0)
544                         fprintf (output, "\tB%p [shape=record,label=\"entry\"]\n", bb);
545                 else if (i == 1)
546                         fprintf (output, "\tB%p [shape=record,label=\"end\"]\n", bb);
547                 else {
548                         code = mono_disasm_code (&graph_dh, method, bb->cil_code, bb->cil_code + bb->cil_length);
549                         fprintf (output, "\tB%p [shape=record,label=\"IL_%04x\\n%s\"]\n", bb, bb->cil_code - header->code, code);
550                         g_free (code);
551                 }
552         }
553         for (i = 0; i < bblocks->len; ++i) {
554                 bb = (MonoBasicBlock*)g_ptr_array_index (bblocks, i);
555                 for (tmp = bb->out_bb; tmp; tmp = tmp->next) {
556                         target = tmp->data;
557                         fprintf (output, "\tB%p -> B%p\n", bb, target);
558                 }
559         }
560 #if 0
561         for (i = 0; i < bblocks->len; ++i) {
562                 bb = (MonoBasicBlock*)g_ptr_array_index (bblocks, i);
563                 bb->dfn = 0;
564         }
565         dfn = 0;
566         for (i = 0; i < bblocks->len; ++i) {
567                 bb = (MonoBasicBlock*)g_ptr_array_index (bblocks, i);
568                 df_visit (bb, &dfn, header->code);
569         }
570 #endif
571 }
572
573 /*
574  * TODO: change to create the DF tree, dominance relation etc.
575  */
576 static void
577 method_cfg (MonoImage *image, char *name) {
578         MonoMethod *method = NULL;
579         static char *cfg_graph_properties = "\tnode [fontsize=8.0]\n\tedge [len=1.5,color=red]\n";
580         
581         if (!name) {
582                 guint32 token = ((MonoCLIImageInfo*)image->image_info)->cli_cli_header.ch_entry_point;
583                 if (!token || !(method = mono_get_method (image, token, NULL))) {
584                         g_print ("Cannot find entry point in %s: specify an explict method name.\n", image->name);
585                         exit (1);
586                 }
587         } else {
588                 /* search the method */
589                 MonoMethodDesc *desc;
590
591                 desc = mono_method_desc_new (name, include_namespace);
592                 if (!desc) {
593                         g_print ("Invalid method name %s\n", name);
594                         exit (1);
595                 }
596                 method = mono_method_desc_search_in_image (desc, image);
597                 if (!method) {
598                         g_print ("Cannot find method %s\n", name);
599                         exit (1);
600                 }
601         }
602         fprintf (output, "digraph blah {\n");
603         fprintf (output, "%s", cfg_graph_properties);
604
605         print_method_cfg (method);
606         
607         fprintf (output, "}\n");
608 }
609
610 static void
611 usage () {
612         printf ("monograph 0.2 Copyright (c) 2002 Ximian, Inc\n");
613         printf ("Create call graph or type hierarchy information from CIL assemblies.\n");
614         printf ("Usage: monograph [options] [assembly [typename|methodname]]\n");
615         printf ("Valid options are:\n");
616         printf ("\t-c|--call             output call graph instead of type hierarchy\n");
617         printf ("\t-C|--control-flow     output control flow of methodname\n");
618         printf ("\t-d|--depth num        max depth recursion (default: 6)\n");
619         printf ("\t-o|--output filename  write graph to file filename (default: stdout)\n");
620         printf ("\t-f|--fullname         include namespace in type and method names\n");
621         printf ("\t-n|--neato            invoke neato directly\n");
622         printf ("\t-v|--verbose          verbose operation\n");
623         printf ("The default assembly is corlib.dll. The default method for\n");
624         printf ("the --call and --control-flow options is the entrypoint.\n");
625         printf ("When the --neato option is used the output type info is taken\n");
626         printf ("from the output filename extension. You need the graphviz package installed\n");
627         printf ("to be able to use this option.\n");
628         printf ("Sample runs:\n");
629         printf ("\tmonograph -n -o vt.png corlib.dll System.ValueType\n");
630         printf ("\tmonograph -n -o expr.png mcs.exe Mono.CSharp.Expression\n");
631         printf ("\tmonograph -n -o cfg.png -C mcs.exe Driver:Main\n");
632         printf ("\tmonograph -d 3 -n -o callgraph.png -c mis.exe\n");
633         exit (1);
634 }
635
636 enum {
637         GRAPH_TYPES,
638         GRAPH_CALL,
639         GRAPH_INTERFACE,
640         GRAPH_CONTROL_FLOW
641 };
642
643 /*
644  * TODO:
645  * * virtual method calls as explained above
646  * * maybe track field references
647  * * track what exceptions a method could throw?
648  * * for some inputs neato appears to hang or take a long time: kill it?
649  * * allow passing additional command-line arguments to neato
650  * * allow setting different graph/node/edge options directly
651  * * option to avoid specialname methods
652  * * make --neato option the default?
653  * * use multiple classes/method roots?
654  * * write a manpage
655  * * reverse call graph: given a method what methods call it?
656  */
657 int
658 main (int argc, char *argv[]) {
659         MonoAssembly *assembly;
660         char *cname = NULL;
661         char *aname = NULL;
662         char *outputfile = NULL;
663         int graphtype = GRAPH_TYPES;
664         int callneato = 0;
665         int i;
666         
667         mono_init (argv [0]);
668         output = stdout;
669
670         for (i = 1; i < argc; ++i) {
671                 if (argv [i] [0] != '-')
672                         break;
673                 if (strcmp (argv [i], "--call") == 0 || strcmp (argv [i], "-c") == 0) {
674                         graphtype = GRAPH_CALL;
675                 } else if (strcmp (argv [i], "--control-flow") == 0 || strcmp (argv [i], "-C") == 0) {
676                         graphtype = GRAPH_CONTROL_FLOW;
677                 } else if (strcmp (argv [i], "--interface") == 0 || strcmp (argv [i], "-i") == 0) {
678                         graphtype = GRAPH_INTERFACE;
679                 } else if (strcmp (argv [i], "--fullname") == 0 || strcmp (argv [i], "-f") == 0) {
680                         include_namespace = 1;
681                 } else if (strcmp (argv [i], "--neato") == 0 || strcmp (argv [i], "-n") == 0) {
682                         callneato = 1;
683                 } else if (strcmp (argv [i], "--verbose") == 0 || strcmp (argv [i], "-v") == 0) {
684                         verbose++;
685                 } else if (strcmp (argv [i], "--output") == 0 || strcmp (argv [i], "-o") == 0) {
686                         if (i + 1 >= argc)
687                                 usage ();
688                         outputfile = argv [++i];
689                 } else if (strcmp (argv [i], "--depth") == 0 || strcmp (argv [i], "-d") == 0) {
690                         if (i + 1 >= argc)
691                                 usage ();
692                         max_depth = atoi (argv [++i]);
693                 } else {
694                         usage ();
695                 }
696                 
697         }
698         if (argc > i)
699                 aname = argv [i];
700         if (argc > i + 1)
701                 cname = argv [i + 1];
702         if (!aname)
703                 aname = "corlib.dll";
704         if (!cname && (graphtype == GRAPH_TYPES))
705                 cname = "System.Object";
706
707         assembly = mono_assembly_open (aname, NULL, NULL);
708         if (!assembly) {
709                 g_print ("cannot open assembly %s\n", aname);
710                 exit (1);
711         }
712
713         if (callneato) {
714                 GString *command = g_string_new ("neato");
715                 char *type = NULL;
716
717                 if (outputfile) {
718                         type = strrchr (outputfile, '.');
719                         g_string_sprintfa (command, " -o %s", outputfile);
720                 }
721                 if (type)
722                         g_string_sprintfa (command, " -T%s", type + 1);
723                 output = popen (command->str, "w");
724                 if (!output) {
725                         g_print ("Cannot run neato: you may need to install the graphviz package.\n");
726                         exit (1);
727                 }
728         } else if (outputfile) {
729                 output = fopen (outputfile, "w");
730                 if (!output) {
731                         g_print ("Cannot open file: %s\n", outputfile);
732                         exit (1);
733                 }
734         }
735         /* if it looks like a method name, we want a callgraph. */
736         if (cname && strchr (cname, ':') && graphtype == GRAPH_TYPES)
737                 graphtype = GRAPH_CALL;
738
739         switch (graphtype) {
740         case GRAPH_TYPES:
741                 type_graph (assembly->image, cname);
742                 break;
743         case GRAPH_CALL:
744                 method_graph (assembly->image, cname);
745                 break;
746         case GRAPH_INTERFACE:
747                 interface_graph (assembly->image, cname);
748                 break;
749         case GRAPH_CONTROL_FLOW:
750                 method_cfg (assembly->image, cname);
751                 break;
752         default:
753                 g_error ("wrong graph type");
754         }
755         
756         if (callneato) {
757                 if (verbose)
758                         g_print ("waiting for neato.\n");
759                 pclose (output);
760         } else if (outputfile)
761                 fclose (output);
762         return 0;
763 }
764
765