Updates for changes in mono_assembly_open () interface.
[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 const 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, token;
30         MonoTableInfo *t;
31         MonoClass *child;
32
33         if (depth++ > max_depth)
34                 return;
35
36         t = &image->tables [MONO_TABLE_TYPEDEF];
37         
38         token = mono_metadata_token_index (class->type_token);
39         token <<= TYPEDEFORREF_BITS;
40         token |= TYPEDEFORREF_TYPEDEF;
41
42         /* use a subgraph? */
43         for (i = 0; i < t->rows; ++i) {
44                 if (token == 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, const char* cname) {
54         MonoClass *class;
55         MonoClass *parent;
56         MonoClass *child;
57         const 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, const char* cname) {
89         MonoClass *class;
90         MonoClass *child;
91         const char *name_space;
92         char *p;
93         guint32 cols [MONO_INTERFACEIMPL_SIZE];
94         guint32 token, 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         token = mono_metadata_token_index (class->type_token);
116         token <<= TYPEDEFORREF_BITS;
117         token |= 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 (token == 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, const 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                         if (!block_end)
360                                 link_bblock (bb, target);
361                         bb = target;
362                         block_end = FALSE;
363                 }
364                 if (block_end) {
365                         /*fprintf (stderr, "processing bbclok at IL_%04x\n", ip - header->code);*/
366                         if (!(bb = g_hash_table_lookup (table, ip))) {
367                                 bb = g_new0 (MonoBasicBlock, 1);
368                                 bb->cil_code = ip;
369                                 g_ptr_array_add (result, bb);
370                                 g_hash_table_insert (table, (char*)ip, bb);
371                         }
372                         block_end = FALSE;
373                 }
374                 if (*ip == 0xfe) {
375                         ++ip;
376                         i = *ip + 256;
377                 } else {
378                         i = *ip;
379                 }
380
381                 opcode = &mono_opcodes [i];
382                 switch (opcode->flow_type) {
383                 case MONO_FLOW_RETURN:
384                         link_bblock (bb, end_bb);
385                 case MONO_FLOW_ERROR:
386                         block_end = 1;
387                         break;
388                 case MONO_FLOW_BRANCH: /* we handle branch when checking the argument type */
389                 case MONO_FLOW_COND_BRANCH:
390                 case MONO_FLOW_CALL:
391                 case MONO_FLOW_NEXT:
392                 case MONO_FLOW_META:
393                         break;
394                 default:
395                         g_assert_not_reached ();
396                 }
397                 switch (opcode->argument) {
398                 case MonoInlineNone:
399                         ++ip;
400                         break;
401                 case MonoInlineType:
402                 case MonoInlineField:
403                 case MonoInlineMethod:
404                 case MonoInlineTok:
405                 case MonoInlineString:
406                 case MonoInlineSig:
407                 case MonoShortInlineR:
408                 case MonoInlineI:
409                         ip += 5;
410                         break;
411                 case MonoInlineVar:
412                         ip += 3;
413                         break;
414                 case MonoShortInlineVar:
415                 case MonoShortInlineI:
416                         ip += 2;
417                         break;
418                 case MonoShortInlineBrTarget:
419                 case MonoInlineBrTarget:
420                         ip++;
421                         if (opcode->argument == MonoShortInlineBrTarget) {
422                                 i = (signed char)*ip;
423                                 ip++;
424                         } else {
425                                 i = (gint32) read32 (ip);
426                                 ip += 4;
427                         }
428                         if (opcode->flow_type == MONO_FLOW_COND_BRANCH) {
429                                 if (!(target = g_hash_table_lookup (table, ip))) {
430                                         target = g_new0 (MonoBasicBlock, 1);
431                                         target->cil_code = ip;
432                                         g_ptr_array_add (result, target);
433                                         g_hash_table_insert (table, (char*)ip, target);
434                                 }
435                                 link_bblock (bb, target);
436                         }
437                         if (!(target = g_hash_table_lookup (table, ip + i))) {
438                                 target = g_new0 (MonoBasicBlock, 1);
439                                 target->cil_code = ip + i;
440                                 g_ptr_array_add (result, target);
441                                 g_hash_table_insert (table, (char*)ip + i, target);
442                         }
443                         link_bblock (bb, target);
444                         block_end = 1;
445                         break;
446                 case MonoInlineSwitch: {
447                         gint32 n;
448                         const char *itarget, *st;
449                         ++ip;
450                         n = read32 (ip);
451                         ip += 4;
452                         st = (const char*)ip + 4 * n;
453
454                         for (i = 0; i < n; i++) {
455                                 itarget = st + read32 (ip);
456                                 ip += 4;
457                                 if (!(target = g_hash_table_lookup (table, itarget))) {
458                                         target = g_new0 (MonoBasicBlock, 1);
459                                         target->cil_code = itarget;
460                                         g_ptr_array_add (result, target);
461                                         g_hash_table_insert (table, itarget, target);
462                                 }
463                                 link_bblock (bb, target);
464                         }
465                         /*
466                          * Note: the code didn't set block_end in switch.
467                          */
468                         break;
469                 }
470                 case MonoInlineR:
471                 case MonoInlineI8:
472                         ip += 9;
473                         break;
474                 default:
475                         g_assert_not_reached ();
476                 }
477
478         }
479         g_hash_table_destroy (table);
480         qsort (result->pdata, result->len, sizeof (gpointer), compare_bblock);
481         /* skip entry and end */
482         bb = target = NULL;
483         for (i = 2; i < result->len; ++i) {
484                 bb = (MonoBasicBlock*)g_ptr_array_index (result, i);
485                 if (target)
486                         target->cil_length = bb->cil_code - target->cil_code;
487                 target = bb;
488                 /*fprintf (stderr, "bblock %d at IL_%04x:\n", i, bb->cil_code - header->code);*/
489         }
490         bb->cil_length = header->code + header->code_size - bb->cil_code;
491         return result;
492 }
493
494 static char*
495 indenter (MonoDisHelper *dh, MonoMethod *method, guint32 ip_offset)
496 {
497         return g_strdup (" ");
498 }
499
500 static MonoDisHelper graph_dh = {
501         "\\l",
502         NULL,
503         "IL_%04x",
504         indenter, 
505         NULL,
506         NULL
507 };
508
509 static void
510 df_visit (MonoBasicBlock *bb, int *dfn, const unsigned char* code)
511 {
512         GList *tmp;
513         MonoBasicBlock *next;
514         
515         if (bb->dfn)
516                 return;
517         ++(*dfn);
518         bb->dfn = *dfn;
519         for (tmp = bb->out_bb; tmp; tmp = tmp->next) {
520                 next = tmp->data;
521                 if (!next->dfn) {
522                         if (!bb->cil_code)
523                                 fprintf (output, "\t\"DF entry\" -> \"IL_%04x (%d)\"\n", next->cil_code - code, *dfn + 1);
524                         else
525                                 fprintf (output, "\t\"IL_%04x (%d)\" -> \"IL_%04x (%d)\"\n", bb->cil_code - code, bb->dfn, next->cil_code - code, *dfn + 1);
526                         df_visit (next, dfn, code);
527                 }
528         }
529 }
530
531 static void
532 print_method_cfg (MonoMethod *method) {
533         GPtrArray *bblocks;
534         GList *tmp;
535         MonoBasicBlock *bb, *target;
536         MonoMethodHeader *header;
537         int i, dfn;
538         char *code;
539
540         header = ((MonoMethodNormal*)method)->header;
541         bblocks = mono_method_find_bblocks (header);
542         for (i = 0; i < bblocks->len; ++i) {
543                 bb = (MonoBasicBlock*)g_ptr_array_index (bblocks, i);
544                 if (i == 0)
545                         fprintf (output, "\tB%p [shape=record,label=\"entry\"]\n", bb);
546                 else if (i == 1)
547                         fprintf (output, "\tB%p [shape=record,label=\"end\"]\n", bb);
548                 else {
549                         code = mono_disasm_code (&graph_dh, method, bb->cil_code, bb->cil_code + bb->cil_length);
550                         fprintf (output, "\tB%p [shape=record,label=\"IL_%04x\\n%s\"]\n", bb, bb->cil_code - header->code, code);
551                         g_free (code);
552                 }
553         }
554         for (i = 0; i < bblocks->len; ++i) {
555                 bb = (MonoBasicBlock*)g_ptr_array_index (bblocks, i);
556                 for (tmp = bb->out_bb; tmp; tmp = tmp->next) {
557                         target = tmp->data;
558                         fprintf (output, "\tB%p -> B%p\n", bb, target);
559                 }
560         }
561 #if 0
562         for (i = 0; i < bblocks->len; ++i) {
563                 bb = (MonoBasicBlock*)g_ptr_array_index (bblocks, i);
564                 bb->dfn = 0;
565         }
566         dfn = 0;
567         for (i = 0; i < bblocks->len; ++i) {
568                 bb = (MonoBasicBlock*)g_ptr_array_index (bblocks, i);
569                 df_visit (bb, &dfn, header->code);
570         }
571 #endif
572 }
573
574 /*
575  * TODO: change to create the DF tree, dominance relation etc.
576  */
577 static void
578 method_cfg (MonoImage *image, const char *name) {
579         MonoMethod *method = NULL;
580         const static char *cfg_graph_properties = "\tnode [fontsize=8.0]\n\tedge [len=1.5,color=red]\n";
581         
582         if (!name) {
583                 guint32 token = ((MonoCLIImageInfo*)image->image_info)->cli_cli_header.ch_entry_point;
584                 if (!token || !(method = mono_get_method (image, token, NULL))) {
585                         g_print ("Cannot find entry point in %s: specify an explict method name.\n", image->name);
586                         exit (1);
587                 }
588         } else {
589                 /* search the method */
590                 MonoMethodDesc *desc;
591
592                 desc = mono_method_desc_new (name, include_namespace);
593                 if (!desc) {
594                         g_print ("Invalid method name %s\n", name);
595                         exit (1);
596                 }
597                 method = mono_method_desc_search_in_image (desc, image);
598                 if (!method) {
599                         g_print ("Cannot find method %s\n", name);
600                         exit (1);
601                 }
602         }
603         fprintf (output, "digraph blah {\n");
604         fprintf (output, "%s", cfg_graph_properties);
605
606         print_method_cfg (method);
607         
608         fprintf (output, "}\n");
609 }
610
611 static void
612 usage (void) {
613         printf ("monograph 0.2 Copyright (c) 2002 Ximian, Inc\n");
614         printf ("Create call graph or type hierarchy information from CIL assemblies.\n");
615         printf ("Usage: monograph [options] [assembly [typename|methodname]]\n");
616         printf ("Valid options are:\n");
617         printf ("\t-c|--call             output call graph instead of type hierarchy\n");
618         printf ("\t-C|--control-flow     output control flow of methodname\n");
619         printf ("\t-d|--depth num        max depth recursion (default: 6)\n");
620         printf ("\t-o|--output filename  write graph to file filename (default: stdout)\n");
621         printf ("\t-f|--fullname         include namespace in type and method names\n");
622         printf ("\t-n|--neato            invoke neato directly\n");
623         printf ("\t-v|--verbose          verbose operation\n");
624         printf ("The default assembly is corlib.dll. The default method for\n");
625         printf ("the --call and --control-flow options is the entrypoint.\n");
626         printf ("When the --neato option is used the output type info is taken\n");
627         printf ("from the output filename extension. You need the graphviz package installed\n");
628         printf ("to be able to use this option.\n");
629         printf ("Sample runs:\n");
630         printf ("\tmonograph -n -o vt.png corlib.dll System.ValueType\n");
631         printf ("\tmonograph -n -o expr.png mcs.exe Mono.CSharp.Expression\n");
632         printf ("\tmonograph -n -o cfg.png -C mcs.exe Driver:Main\n");
633         printf ("\tmonograph -d 3 -n -o callgraph.png -c mis.exe\n");
634         exit (1);
635 }
636
637 enum {
638         GRAPH_TYPES,
639         GRAPH_CALL,
640         GRAPH_INTERFACE,
641         GRAPH_CONTROL_FLOW
642 };
643
644 /*
645  * TODO:
646  * * virtual method calls as explained above
647  * * maybe track field references
648  * * track what exceptions a method could throw?
649  * * for some inputs neato appears to hang or take a long time: kill it?
650  * * allow passing additional command-line arguments to neato
651  * * allow setting different graph/node/edge options directly
652  * * option to avoid specialname methods
653  * * make --neato option the default?
654  * * use multiple classes/method roots?
655  * * write a manpage
656  * * reverse call graph: given a method what methods call it?
657  */
658 int
659 main (int argc, char *argv[]) {
660         MonoAssembly *assembly;
661         const char *cname = NULL;
662         const char *aname = NULL;
663         char *outputfile = NULL;
664         int graphtype = GRAPH_TYPES;
665         int callneato = 0;
666         int i;
667         
668         mono_init (argv [0]);
669         output = stdout;
670
671         for (i = 1; i < argc; ++i) {
672                 if (argv [i] [0] != '-')
673                         break;
674                 if (strcmp (argv [i], "--call") == 0 || strcmp (argv [i], "-c") == 0) {
675                         graphtype = GRAPH_CALL;
676                 } else if (strcmp (argv [i], "--control-flow") == 0 || strcmp (argv [i], "-C") == 0) {
677                         graphtype = GRAPH_CONTROL_FLOW;
678                 } else if (strcmp (argv [i], "--interface") == 0 || strcmp (argv [i], "-i") == 0) {
679                         graphtype = GRAPH_INTERFACE;
680                 } else if (strcmp (argv [i], "--fullname") == 0 || strcmp (argv [i], "-f") == 0) {
681                         include_namespace = 1;
682                 } else if (strcmp (argv [i], "--neato") == 0 || strcmp (argv [i], "-n") == 0) {
683                         callneato = 1;
684                 } else if (strcmp (argv [i], "--verbose") == 0 || strcmp (argv [i], "-v") == 0) {
685                         verbose++;
686                 } else if (strcmp (argv [i], "--output") == 0 || strcmp (argv [i], "-o") == 0) {
687                         if (i + 1 >= argc)
688                                 usage ();
689                         outputfile = argv [++i];
690                 } else if (strcmp (argv [i], "--depth") == 0 || strcmp (argv [i], "-d") == 0) {
691                         if (i + 1 >= argc)
692                                 usage ();
693                         max_depth = atoi (argv [++i]);
694                 } else {
695                         usage ();
696                 }
697                 
698         }
699         if (argc > i)
700                 aname = argv [i];
701         if (argc > i + 1)
702                 cname = argv [i + 1];
703         if (!aname)
704                 aname = "corlib.dll";
705         if (!cname && (graphtype == GRAPH_TYPES))
706                 cname = "System.Object";
707
708         assembly = mono_assembly_open (aname, NULL);
709         if (!assembly) {
710                 g_print ("cannot open assembly %s\n", aname);
711                 exit (1);
712         }
713
714         if (callneato) {
715                 GString *command = g_string_new ("neato");
716                 char *type = NULL;
717
718                 if (outputfile) {
719                         type = strrchr (outputfile, '.');
720                         g_string_sprintfa (command, " -o %s", outputfile);
721                 }
722                 if (type)
723                         g_string_sprintfa (command, " -T%s", type + 1);
724                 output = popen (command->str, "w");
725                 if (!output) {
726                         g_print ("Cannot run neato: you may need to install the graphviz package.\n");
727                         exit (1);
728                 }
729         } else if (outputfile) {
730                 output = fopen (outputfile, "w");
731                 if (!output) {
732                         g_print ("Cannot open file: %s\n", outputfile);
733                         exit (1);
734                 }
735         }
736         /* if it looks like a method name, we want a callgraph. */
737         if (cname && strchr (cname, ':') && graphtype == GRAPH_TYPES)
738                 graphtype = GRAPH_CALL;
739
740         switch (graphtype) {
741         case GRAPH_TYPES:
742                 type_graph (assembly->image, cname);
743                 break;
744         case GRAPH_CALL:
745                 method_graph (assembly->image, cname);
746                 break;
747         case GRAPH_INTERFACE:
748                 interface_graph (assembly->image, cname);
749                 break;
750         case GRAPH_CONTROL_FLOW:
751                 method_cfg (assembly->image, cname);
752                 break;
753         default:
754                 g_error ("wrong graph type");
755         }
756         
757         if (callneato) {
758                 if (verbose)
759                         g_print ("waiting for neato.\n");
760                 pclose (output);
761         } else if (outputfile)
762                 fclose (output);
763         return 0;
764 }
765
766