fixed end block handling
[libfirm] / ir / ana2 / ecg.c
1 /* -*- c -*- */
2
3 /*
4  * Project:     libFIRM
5  * File name:   ir/ana/ecg.c
6  * Purpose:     Extended Call Graph
7  * Author:      Florian
8  * Modified by:
9  * Created:     14.09.2004
10  * CVS-ID:      $$
11  * Copyright:   (c) 1999-2004 Universität Karlsruhe
12  * Licence:     This file is protected by GPL -  GNU GENERAL PUBLIC LICENSE.
13  */
14
15 #ifdef HAVE_CONFIG_H
16 # include <config.h>
17 #endif
18
19 /**
20    Erweiterter Aufrufgraph.
21  */
22
23 #include "irnode.h"
24 #include "pmap.h"
25 /* #include "eset.h" */
26 #include "irgwalk.h"
27 #include "irgmod.h"
28 #include "irvrfy.h"
29 #include "trvrfy.h"
30 #include "xmalloc.h"
31
32 # ifndef TRUE
33 #  define TRUE 1
34 #  define FALSE 0
35 # endif /* not defined TRUE */
36
37 # define BUF_SIZE 1024
38
39 # include "ecg.h"
40 # include "typalise.h"
41 # include "lset.h"
42
43 /*
44    le flag
45 */
46 /* static int verbose     = 0; */
47 static int do_typalise = 0;
48
49 /*
50    globals
51 */
52
53 /* mapping from method graphs (callR) to method graphs (lset_ts of callEds) */
54 /* static pmap *calls; */
55 static pmap *graph_infos;
56
57 /** Counters for ecg_ecg and friends */
58 static long _graphs = 0;
59 static long _calls  = 0;
60 static long _allocs = 0;
61
62 static int _depth = 0;
63 static int _max_depth = 0;
64
65 static int _max_callEds = 0;
66 static entity* _max_callEds_callR = NULL;
67
68 /* ====================
69   Alloc stuff
70   ==================== */
71 static void append_alloc (graph_info_t *ginfo, ir_node *alloc, type *tp)
72 {
73   alloc_info_t *ainfo = (alloc_info_t*) xmalloc (sizeof (alloc_info_t));
74
75   ainfo->graph = ginfo->graph;
76   ainfo->alloc = alloc;
77   ainfo->tp    = tp;
78
79   ainfo->prev = ginfo->allocs;
80   ginfo->allocs = ainfo;
81 }
82
83
84 /* ====================
85   CallEd stuff
86   ==================== */
87 /**
88    Append the given callEd to the given callEd info.
89 */
90 static callEd_info_t *append_callEd_info (callEd_info_t *ced, ir_graph *callEd)
91 {
92   callEd_info_t *nced = (callEd_info_t*) xmalloc (sizeof (sizeof (callEd_info_t)));
93
94   nced->callEd = callEd;
95   nced->prev = ced;
96
97   return (nced);
98 }
99
100 /**
101    Append all callEd methods of the given (call) node to the given graph_info.
102 */
103 static void append_calls (graph_info_t *info, ir_node *call, lset_t *callEds)
104 {
105   call_info_t *cinfo = (call_info_t*) xmalloc (sizeof (call_info_t));
106
107   /* setup */
108   cinfo->call = call;
109   cinfo->prev = info->calls;
110   info->calls = cinfo;
111   cinfo->callEds = NULL;
112
113   /* enter */
114   ir_graph *callEd = lset_first (callEds);
115   while (callEd) {
116     cinfo->callEds = append_callEd_info (cinfo->callEds, callEd);
117
118     callEd = lset_next (callEds);
119   }
120 }
121
122 /**
123    Append the (single) callEd to the given (call) node of the given graph_info.
124 */
125 static void append_call (graph_info_t *info, ir_node *call, ir_graph *callEd)
126 {
127   call_info_t *cinfo = (call_info_t*) xmalloc (sizeof (call_info_t));
128
129   cinfo->call = call;
130   cinfo->prev = info->calls;
131   info->calls = cinfo;
132
133   cinfo->callEds = append_callEd_info (cinfo->callEds, callEd);
134 }
135
136 /**
137    Given a method, find the firm graph that implements that method.
138    Return NULL for abstract and native methods.
139 */
140 static ir_graph *_get_implementing_graph (entity *method)
141 {
142   ir_graph *graph = NULL;
143
144   /* What's up with the fenced out stuff in rta? */
145   if (peculiarity_existent == get_entity_peculiarity (method)) {
146     if (visibility_external_allocated == get_entity_visibility (method)) {
147       /* Todo: native implementation */
148
149       return (NULL);
150     } else {
151       graph = get_entity_irg (get_SymConst_entity (get_atomic_ent_value (method)));
152       assert (graph && "no graph");
153
154       return (graph);
155     }
156   } else if (0 && (peculiarity_description == get_entity_peculiarity (method))) {
157     /* abstract --- can't find an implementation */
158     graph = get_entity_irg (method);
159     assert (!graph && "graph in abstract method");
160
161     return (NULL);
162   } else if ((peculiarity_description == get_entity_peculiarity (method)) ||
163              (peculiarity_inherited == get_entity_peculiarity (method))) {
164     /* search UPWARDS */
165     int i;
166     int n_over = get_entity_n_overwrites (method);
167
168     assert (!graph);
169
170     for (i = 0; (NULL == graph) && (i < n_over); i ++) {
171       entity *over = get_entity_overwrites (method, i);
172
173       graph = _get_implementing_graph (over);
174     }
175   } else {
176     assert (0 && "invalid peculiarity");
177   }
178
179
180   return (graph);
181 }
182
183 /**
184    Collect all graphs of 'method' in the given set.
185 */
186 static void _collect_implementing_graphs (entity *method, lset_t *set)
187 {
188   /* search DOWN-wards in clazz hierarchy */
189   int i;
190   int n_over = get_entity_n_overwrittenby (method);
191   ir_graph *graph = get_entity_irg (method);
192
193   if (NULL == graph) {
194     graph = _get_implementing_graph (method);
195   }
196
197   if (graph) {
198     lset_insert (set, graph);
199   }
200
201   for (i = 0; i < n_over; i ++) {
202     entity *over = get_entity_overwrittenby (method, i);
203
204     _collect_implementing_graphs (over, set);
205   }
206 }
207
208
209 /**
210    Collect all graphs that could possibly be executed when 'method' is called.
211 */
212 static lset_t *get_implementing_graphs (entity *method, ir_node *select)
213 {
214   lset_t *set = lset_create ();
215   {
216     ir_graph *impl = _get_implementing_graph (method);
217
218     if (NULL != impl) {
219       lset_insert (set, impl);
220     } else {
221       /* actually, abstract OR native */
222     }
223   }
224
225   _collect_implementing_graphs (method, set);
226
227   if (lset_empty (set)) {
228     /* then it's a method which is only implemented natively, and we
229        don' bother to analyse anything */
230     return (set);
231   }
232
233   /* void *tmp = lset_first (set); */
234   int n_graphs = lset_n_entries (set);
235
236   /* typalise select_in */
237   if (do_typalise) {
238     ir_node *select_in = get_Sel_ptr (select);
239     typalise_t *ta = typalise (select_in);
240     assert (ta && "typalise failed (go figure)");
241
242     /* const char *res = ta_name (ta); */
243
244     /* fprintf (stdout, "typalyse res = %s\n", res); */
245
246     if (1 != n_graphs) {
247       set = filter_for_ta (set, ta);
248
249       int n_filtered_graphs = lset_n_entries (set);
250
251       /*
252       fprintf (stdout, "%s: %02d %02d\n",
253                __FUNCTION__,
254                n_graphs,
255                n_filtered_graphs,
256                n_graphs - n_filtered_graphs);
257       */
258       n_graphs = n_filtered_graphs;
259     }
260   }
261
262   if (n_graphs > _max_callEds) {
263     _max_callEds = n_graphs;
264     _max_callEds_callR = method;
265   }
266
267
268   if (visibility_external_allocated != get_entity_visibility (method)) {
269     if (0 == n_graphs) {
270       /* fprintf (stdout, "no graphs for method %s\n", get_entity_name (method)); */
271       assert (n_graphs && "no graphs for method");
272     }
273   }
274
275   return (set);
276 }
277
278 /**
279    Action for the graph.
280 */
281 static void ecg_calls_act (ir_node *node, void *env)
282 {
283   opcode op = get_irn_opcode (node);
284   graph_info_t *graph_info = (graph_info_t*) env;
285
286   if (iro_Call == op) {         /* CALL */
287     entity *ent = NULL;
288     ir_node *ptr = get_Call_ptr (node);
289
290     /* CALL SEL */
291     if (iro_Sel == get_irn_opcode (ptr)) {
292       ent = get_Sel_entity (ptr);
293       lset_t *graphs = get_implementing_graphs (ent, ptr);
294
295       append_calls (graph_info, node, graphs);
296     } else if (iro_SymConst == get_irn_opcode (ptr)) {
297       if (get_SymConst_kind (ptr) == symconst_addr_ent) {
298         ent = get_SymConst_entity (ptr);
299         ir_graph *graph = get_entity_irg (ent);
300
301         if (graph) {
302           append_call (graph_info, node, graph);
303         } else {
304           /* it's an externally allocated thingy */
305         }
306       } else if (get_SymConst_kind (ptr) == symconst_addr_name) {
307         /* If this SymConst refers to a method the method is external_visible
308            and therefore must be considered live anyways. */
309         if (get_SymConst_name (ptr) != new_id_from_str ("iro_Catch")) {
310           assert (ent && "couldn't determine entity of call to symConst");
311         }
312       } else {
313         /* other symconst. */
314         assert (0 && "This SymConst can not be an address for a method call.");
315       }
316
317       /* STRANGE, no less ... */
318     } else {
319       DDMN (ptr);
320       assert (0 && "Unexpected address expression");
321     }
322   } else if (iro_Alloc == op) {
323     type *tp = get_Alloc_type (node);
324     /* const char *name = get_type_name (tp); */
325
326     append_alloc (graph_info, node, tp);
327
328     /* fprintf (stdout, "NEW \"%s\"\n", name); */
329   }
330 }
331
332 /**
333    Collect called graphs for the given graph.
334 */
335 static void ecg_fill_graph_calls (ir_graph *graph)
336 {
337   graph_info_t *graph_info = (graph_info_t*) xmalloc (sizeof (graph_info_t));
338
339   graph_info->graph = graph;
340   graph_info->calls = NULL;
341   graph_info->ecg_seen = 0;
342
343   /* entity *method  = get_irg_entity (graph); */
344   /* type   *clazz   = get_entity_owner (method); */
345
346   irg_walk_graph (graph, ecg_calls_act, NULL, graph_info);
347
348   pmap_insert (graph_infos, graph, graph_info);
349 }
350
351 /**
352    For each graph, collect called graphs, and enter them into calls.
353 */
354 static void ecg_fill_calls (void)
355 {
356   int i;
357
358   for (i = 0; i < get_irp_n_irgs (); i++) {
359     ir_graph *graph = get_irp_irg (i);
360
361     ecg_fill_graph_calls (graph);
362   }
363 }
364
365 /* ====================
366   ECG stuff
367   ==================== */
368
369 /*
370   get the call infos for the given graph
371 */
372 graph_info_t *ecg_get_info (ir_graph *graph)
373 {
374   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
375
376   assert (ginfo && "no info for graph");
377
378   return (ginfo);
379 }
380
381
382
383 /**
384    Dump the given graph and it's calls and it's calls callEds to the given file.
385 */
386 static int ecg_ecg_graph (FILE *dot, ir_graph *graph)
387 {
388   const char *name = get_irg_entity (graph) ?
389     get_entity_name (get_irg_entity (graph)) : "noEntity";
390   const char *color =
391     (get_entity_stickyness
392      (get_irg_entity (graph)) == stickyness_sticky) ?
393     "red" : "lightyellow";
394
395   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
396
397   if (0 != ginfo->ecg_seen) {
398     fprintf (dot, "\t/* recursive call to \"%s\" (%d) */\n",
399              name, (int) ginfo->ecg_seen);
400 # if 0
401     fprintf (dot, "\t/* recursive call to \"%s\" (0x%08x) */\n",
402              name, (int) graph);
403 # endif /* 0 */
404     return (ginfo->ecg_seen);
405   }
406
407   assert (0L <= _graphs);
408
409   const int graph_no = _graphs ++;
410   ginfo->ecg_seen = graph_no;
411
412   fprintf (dot, "\t/* Graph of \"%s.%s\" */\n",
413            get_type_name (get_entity_owner (get_irg_entity (graph))),
414            name);
415   fprintf (dot, "\tgraph_%i [label=\"%s\\l%s\", color=\"%s\"];\n",
416            graph_no,
417            get_type_name (get_entity_owner (get_irg_entity (graph))),
418            name,
419            color);
420   fprintf (dot, "\n");
421
422   if (visibility_external_allocated ==
423       get_entity_visibility (get_irg_entity (graph))) {
424     fprintf (dot, "\t/* graph \"%s\" is external */\n", name);
425
426     return (graph_no);
427   }
428
429   call_info_t *cinfo = ginfo->calls;
430   while (NULL != cinfo) {
431     ir_node *call = cinfo->call;
432     callEd_info_t *ced = cinfo->callEds;
433     const int call_no = _calls ++;
434
435     fprintf (dot, "\t/* Call 0x%08x */\n", (int) call);
436     fprintf (dot, "\tcall_%i [label=\"call\\l0x%08x\"];\n",
437              call_no, (int) call);
438     fprintf (dot, "\tgraph_%i -> call_%i [color=\"black\"];\n", graph_no, call_no);
439
440     while (NULL != ced) {
441       ir_graph *callEd_graph = ced->callEd;
442       const int callEd_no = ecg_ecg_graph (dot, callEd_graph);
443       const char *callEd_name = get_irg_entity (callEd_graph) ?
444         get_entity_name (get_irg_entity (callEd_graph)) : "noEntity";
445       const char *direction = (callEd_no <= graph_no) ? "forward" : "forward";
446       const char *callEd_color     = (callEd_no <= graph_no) ? "red" : "black";
447
448       fprintf (dot, "\t/* Call from graph \"%s\" to graph \"%s\" */\n",
449                name,
450                callEd_name);
451       /* Check for recursive calls */
452       /* if (callEd_no > graph_no) */ { /* do recursive calls (for now) */
453         fprintf (dot, "\tcall_%i -> graph_%i [color=\"%s\", dir=\"%s\"];\n",
454                  call_no, callEd_no, callEd_color, direction);
455       }
456
457       ced = ced->prev;
458       /* ced = NULL; */
459     } /* done all calEds (call) */
460
461     cinfo = cinfo->prev;
462   } /* done all calls (graph) */
463
464   /* now the allocs */
465   alloc_info_t *ainfo = ginfo->allocs;
466   if (ainfo) {
467     fprintf (dot, "\t/* now the allocs */\n");
468   } else {
469     fprintf (dot, "\t/* no allocs */\n");
470   }
471
472   while (NULL != ainfo) {
473     ir_node *alloc = ainfo->alloc;
474     const char *name = get_type_name (ainfo->tp);
475     const char *color = "red1";
476
477     /* if (0 == ginfo->allocs_seen) { */
478     _allocs ++;
479       fprintf (dot, "\talloc_0x%08x_%i [label=\"%s\", color=\"%s\"]\n",
480                (int) alloc, graph_no, name, color);
481     /* } */
482
483     fprintf (dot, "\tgraph_%i -> alloc_0x%08x_%i\n", graph_no, (int) alloc, graph_no);
484
485     ainfo = ainfo->prev;
486   }
487
488   if (0 == ginfo->allocs_seen) {
489     ginfo->allocs_seen = 1;
490   }
491
492   fprintf (dot, "\t/* done with graph of \"%s\" */\n\n", name);
493
494   fflush (dot);
495   ginfo->ecg_seen = 0;
496
497   return (graph_no);
498 }
499
500 /**
501    Count how many nodes the ECG will have
502 */
503 static char spaces [BUF_SIZE];
504
505 static void ecg_ecg_count (ir_graph *graph)
506 {
507   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
508
509   if (0 != ginfo->ecg_seen) {
510     return;
511   }
512
513   _depth ++;
514   if (_depth > _max_depth) {
515     _max_depth = _depth;
516
517     /*
518       fprintf (stdout, "_max_depth = %i\n", _max_depth);
519       fprintf (stdout, "\tn_graphs: %i\n", _graphs);
520     */
521   }
522
523   assert (0L <= _graphs);
524
525   /*
526     if (0 == (_graphs % 1000000)) {
527     fprintf (stdout, "\tn_graphs: %i\n", _graphs);
528     fprintf (stdout, "_depth = %i\n", _depth);
529     }
530   */
531
532   const int graph_no = _graphs ++;
533   ginfo->ecg_seen = graph_no;
534
535   fprintf (stdout, "%sMethod \"%s.%s\"\n",
536            spaces + BUF_SIZE - _depth,
537            get_type_name (get_entity_owner (get_irg_entity (graph))),
538            get_entity_name (get_irg_entity (graph)));
539
540   call_info_t *cinfo = ginfo->calls;
541   while (NULL != cinfo) {
542
543     callEd_info_t *ced = cinfo->callEds;
544
545     fprintf (stdout, "%sCall \"0x%08x\"\n",
546              spaces + BUF_SIZE - _depth,
547              (int) cinfo->call);
548
549     while (NULL != ced) {
550       ir_graph *callEd_graph = ced->callEd;
551
552       fprintf (stdout, "%sCall Target \"%s.%s\"\n",
553                spaces + BUF_SIZE - _depth,
554                get_type_name (get_entity_owner (get_irg_entity (callEd_graph))),
555                get_entity_name (get_irg_entity (callEd_graph)));
556
557       ecg_ecg_count (callEd_graph);
558
559       ced = ced->prev;
560     } /* done all calEds (call) */
561     cinfo = cinfo->prev;
562   } /* done all calls (graph) */
563
564   ginfo->ecg_seen = 0;
565   _depth --;
566 }
567
568 /* ====================
569   Public Interface
570   ==================== */
571
572 /**
573    Initialise our data structures.
574 */
575 void ecg_init (int typalise)
576 {
577   do_typalise = typalise;
578
579   graph_infos = pmap_create ();
580
581   ecg_fill_calls ();
582 }
583
584 /**
585    Clean up our mess.
586 */
587 void ecg_cleanup ()
588 {
589   int i;
590
591   for (i = 0; i < get_irp_n_irgs (); i++) {
592     ir_graph *graph = get_irp_irg (i);
593
594     graph_info_t *info = pmap_get (graph_infos, graph);
595     call_info_t *cinfo = info->calls;
596
597     while (NULL != cinfo) {
598       free (cinfo->callEds);
599       cinfo->call = NULL;
600
601       callEd_info_t *ced = cinfo->callEds;
602
603       while (NULL != ced) {
604         callEd_info_t *nced = ced->prev;
605         free (ced);
606         ced->prev = NULL;
607         ced->callEd = NULL;
608         ced = nced;
609       }
610
611       /* Todo: delete callEds */
612       cinfo->callEds = NULL;
613
614       free (cinfo);
615       cinfo = cinfo->prev;
616     }
617
618     free (info);
619     pmap_insert (graph_infos, graph, NULL);
620   }
621
622
623   pmap_destroy (graph_infos);
624
625   /* BEGIN mild paranoia mode */
626   graph_infos = NULL;
627   /* END mild paranoia mode */
628 }
629
630 /**
631    Show what we have found.
632 */
633 void ecg_report ()
634 {
635   int i;
636
637   FILE *dot = fopen ("calls.dot", "w");
638
639   fprintf (dot, "digraph \"calls\" {\n");
640   fprintf (dot, "\tnode [shape = \"record\", style = \"filled\"];\n");
641   fprintf (dot, "\tedge [color = \"black\"];\n");
642   fprintf (dot, "\n");
643   fprintf (dot, "\tsize = \"11, 7\";\n");
644   fprintf (dot, "\trotate = \"90\";\n");
645   fprintf (dot, "\tratio = \"fill\";\n");
646   fprintf (dot, "\trankdir = \"LR\";\n");
647   fprintf (dot, "\n");
648
649   for (i = 0; i < get_irp_n_irgs (); i++) {
650     ir_graph *graph = get_irp_irg (i);
651     graph_info_t *info = (graph_info_t*) pmap_get (graph_infos, graph);
652
653     const char *name = get_irg_entity (graph) ?
654       get_entity_name (get_irg_entity (graph)) : "noEntity";
655
656     const char *oname = get_type_name
657       (get_entity_owner (get_irg_entity (graph)));
658
659     const char *color =
660       (get_entity_stickyness
661        (get_irg_entity (graph)) == stickyness_sticky) ?
662       "red3" : "lightyellow";
663
664     fprintf (dot, "\t/* graph_0x%08x (\"%s\") */\n", (int) graph, name);
665     fprintf (dot,
666              "\tgraph_0x%08x [label=\"%s\\l%s\", color=\"%s\"];\n",
667              (int) graph, oname, name, color);
668     fprintf (dot, "\n");
669
670     call_info_t *cinfo = info->calls;
671     if (cinfo) {
672       fprintf (dot, "\t/* now the calls */\n");
673     } else {
674       fprintf (dot, "\t/* no calls, nothing to see, move along! */\n");
675     }
676
677     while (NULL != cinfo) {
678       ir_node *call = cinfo->call;
679
680       fprintf (dot, "\t/* call_0x%08x */\n", (int) call);
681       fprintf (dot, "\tcall_0x%08x [label=\"call\\l0x%08x\"];\n",
682                (int) call, (int) call);
683       fprintf (dot, "\tgraph_0x%08x -> call_0x%08x;\n",
684                (int) graph, (int) call);
685
686       callEd_info_t *ced = cinfo->callEds;
687       while (NULL != ced) {
688         fprintf (dot, "\tcall_0x%08x -> graph_0x%08x;\n",
689                  (int) call, (int) ced->callEd);
690         ced = ced->prev;
691       }
692       fprintf (dot, "\n");
693
694       cinfo = cinfo->prev;
695     }
696     fprintf (dot, "\n");
697
698     alloc_info_t *ainfo = info->allocs;
699     if (ainfo) {
700       fprintf (dot, "\t/* now the allocs */\n");
701     } else {
702       fprintf (dot, "\t/* no allocs */\n");
703     }
704
705
706     while (NULL != ainfo) {
707       ir_node *alloc = ainfo->alloc;
708       const char *name = get_type_name (ainfo->tp);
709       const char *color = "green3";
710
711       fprintf (dot, "\talloc_0x%08x [label=\"%s\", color=\"%s\"]\n",
712                (int) alloc, name, color);
713       fprintf (dot, "\tgraph_0x%08x -> alloc_0x%08x\n",
714                (int) graph, (int) alloc);
715
716       ainfo = ainfo->prev;
717     }
718   }
719   fprintf (dot, "}\n");
720
721   /*
722     fprintf (stdout, " max_callEds: %i\n", _max_callEds);
723     fprintf (stdout, " max_callEds_callR: \"%s\"\n",
724     get_entity_name (_max_callEds_callR));
725   */
726   fclose (dot);
727
728   ecg_ecg ();
729 }
730
731 /**
732    Experimental:  Print the ecg
733 */
734 void ecg_ecg ()
735 {
736   _graphs = 0;
737   _calls  = 0;
738
739   ir_graph *main_graph = get_irp_main_irg ();
740
741   /*
742   memset (spaces, '.', BUF_SIZE);
743   spaces [BUF_SIZE-1] = '\0';
744
745   ecg_ecg_count (main_graph);
746   fprintf (stdout, "n_graphs: %i\n", _graphs);
747   fprintf (stdout, "max_depth = %i\n", _max_depth);
748   */
749
750   /* return; */
751
752   _graphs = 0;
753   _calls  = 0;
754
755   FILE *dot = fopen ("ecg.dot", "w");
756
757   fprintf (dot, "digraph \"ecg\" {\n");
758   fprintf (dot, "\tnode [shape = \"record\", style = \"filled\"];\n");
759   fprintf (dot, "\tedge [color = \"black\"];\n");
760   fprintf (dot, "\n");
761   fprintf (dot, "\tsize = \"11, 7\";\n");
762   fprintf (dot, "\trotate = \"90\";\n");
763   fprintf (dot, "\tratio = \"fill\";\n");
764   fprintf (dot, "\trankdir = \"LR\";\n");
765   fprintf (dot, "\n");
766
767   /* ir_graph *main_graph = get_irp_main_irg (); */
768   ecg_ecg_graph (dot, main_graph);
769
770   fprintf (dot, "\t/* Grand Total: */\n");
771   fprintf (dot, "\t/* calls:  %i */\n", (int) _calls);
772   fprintf (dot, "\t/* graphs: %i */\n", (int) _graphs);
773   fprintf (dot, "\t/* allocs: %i */\n", (int) _allocs);
774   fprintf (dot, "\t/* (sales tax not included) */\n");
775
776   fprintf (dot, "}\n");
777
778   fclose (dot);
779 }
780
781 \f
782
783 /*
784   $Log$
785   Revision 1.3  2004/11/04 14:54:44  liekweg
786   Nicer Colors
787
788   Revision 1.2  2004/10/21 11:09:37  liekweg
789   Moved memwalk stuf into irmemwalk
790   Moved lset stuff into lset
791   Moved typalise stuff into typalise
792
793   Revision 1.1  2004/10/20 14:59:41  liekweg
794   Added ana2, added ecg and pto
795
796   Revision 1.6  2004/10/18 12:47:19  liekweg
797   minor fix
798
799   Revision 1.5  2004/10/14 11:31:28  liekweg
800   SHUTUP_GCC
801
802   Revision 1.4  2004/10/12 11:02:01  liekweg
803   wtf?
804
805 */