db6fef6086e9ebf1a59f522a62183286387effad
[platform/upstream/dbus.git] / test / decode-gcov.c
1  /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* decode-gcov.c gcov decoder program
3  *
4  * Copyright (C) 2003  Red Hat Inc.
5  *
6  * Partially derived from gcov,
7  * Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
8  * 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
9  *
10  * This file is NOT licensed under the Academic Free License
11  * as it is largely derived from gcov.c and gcov-io.h in the
12  * gcc source code.
13  * 
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  *
28  */
29
30 #define DBUS_COMPILATION /* cheat */
31 #include <dbus/dbus-list.h>
32 #include <dbus/dbus-string.h>
33 #include <dbus/dbus-sysdeps.h>
34 #include <dbus/dbus-marshal.h>
35 #include <dbus/dbus-hash.h>
36 #undef DBUS_COMPILATION
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 static void
42 die (const char *message)
43 {
44   fprintf (stderr, "%s", message);
45   exit (1);
46 }
47
48 /* This bizarro function is from gcov-io.h in gcc source tree */
49 static int
50 fetch_long (long        *dest,
51             const char  *source,
52             size_t       bytes)
53 {
54   long value = 0;
55   int i;
56                                                                                 
57   for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
58     if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
59       return 1;
60                                                                                 
61   for (; i >= 0; i--)
62     value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
63                                                                                 
64   if ((source[bytes - 1] & 128) && (value > 0))
65     value = - value;
66                                                                                 
67   *dest = value;
68   return 0;
69 }
70
71 typedef DBUS_INT64_TYPE dbus_int64_t;
72
73 static int
74 fetch_long64 (dbus_int64_t *dest,
75               const char   *source,
76               size_t        bytes)
77 {
78   dbus_int64_t value = 0;
79   int i;
80                                                                                 
81   for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
82     if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
83       return 1;
84                                                                                 
85   for (; i >= 0; i--)
86     value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
87                                                                                 
88   if ((source[bytes - 1] & 128) && (value > 0))
89     value = - value;
90                                                                                 
91   *dest = value;
92   return 0;
93 }
94
95 #define BB_FILENAME     (-1)
96 #define BB_FUNCTION     (-2)
97 #define BB_ENDOFLIST    0
98
99 static dbus_bool_t
100 string_get_int (const DBusString *str,
101                 int               start,
102                 long             *val)
103 {
104   const char *p;
105   
106   if ((_dbus_string_get_length (str) - start) < 4)
107     return FALSE;
108
109   p = _dbus_string_get_const_data (str);
110
111   p += start;
112
113   fetch_long (val, p, 4);
114   
115   return TRUE;
116 }
117
118 static dbus_bool_t
119 string_get_int64 (const DBusString *str,
120                   int               start,
121                   dbus_int64_t     *val)
122 {
123   const char *p;
124   
125   if ((_dbus_string_get_length (str) - start) < 8)
126     return FALSE;
127
128   p = _dbus_string_get_const_data (str);
129
130   p += start;
131
132   fetch_long64 (val, p, 8);
133   
134   return TRUE;
135 }
136
137 static dbus_bool_t
138 string_get_string (const DBusString *str,
139                    int               start,
140                    long              terminator,
141                    DBusString       *val,
142                    int              *end)
143 {
144   int i;
145   long n;
146   
147   i = start;
148   while (string_get_int (str, i, &n))
149     {
150       i += 4;
151       
152       if (n == terminator)
153         break;
154
155       _dbus_string_append_byte (val, n & 0xff);
156       _dbus_string_append_byte (val, (n >> 8) & 0xff);
157       _dbus_string_append_byte (val, (n >> 16) & 0xff);
158       _dbus_string_append_byte (val, (n >> 24) & 0xff);
159     }
160
161   *end = i;
162   
163   return TRUE;
164 }
165
166 static void
167 dump_bb_file (const DBusString *contents)
168 {
169   int i;
170   long val;
171   int n_functions;
172
173   n_functions = 0;
174   i = 0;
175   while (string_get_int (contents, i, &val))
176     {
177       i += 4;
178       
179       switch (val)
180         {
181         case BB_FILENAME:
182           {
183             DBusString f;
184
185             if (!_dbus_string_init (&f))
186               die ("no memory\n");
187
188             if (string_get_string (contents, i,
189                                    BB_FILENAME,
190                                    &f, &i))
191               {
192                 printf ("File %s\n", _dbus_string_get_const_data (&f));
193               }
194             _dbus_string_free (&f);
195           }
196           break;
197         case BB_FUNCTION:
198           {
199             DBusString f;
200             if (!_dbus_string_init (&f))
201               die ("no memory\n");
202
203             if (string_get_string (contents, i,
204                                    BB_FUNCTION,
205                                    &f, &i))
206               {
207                 printf ("Function %s\n", _dbus_string_get_const_data (&f));
208               }
209             _dbus_string_free (&f);
210
211             n_functions += 1;
212           }
213           break;
214         case BB_ENDOFLIST:
215           printf ("End of block\n");
216           break;
217         default:
218           printf ("Line %ld\n", val);
219           break;
220         }
221     }
222
223   printf ("%d functions in file\n", n_functions);
224 }
225
226 #define FLAG_ON_TREE 0x1
227 #define FLAG_FAKE 0x2
228 #define FLAG_FALL_THROUGH 0x4
229
230 static void
231 dump_bbg_file (const DBusString *contents)
232 {
233   int i;
234   long val;
235   int n_functions;
236   int n_arcs;
237   int n_blocks;
238   int n_arcs_off_tree;
239
240   n_arcs_off_tree = 0;
241   n_blocks = 0;
242   n_arcs = 0;
243   n_functions = 0;
244   i = 0;
245   while (string_get_int (contents, i, &val))
246     {
247       long n_blocks_in_func;
248       long n_arcs_in_func; 
249       int j;
250       
251       n_blocks_in_func = val;
252       
253       i += 4;
254
255       if (!string_get_int (contents, i, &n_arcs_in_func))
256         break;
257
258       i += 4;
259
260       printf ("Function has %ld blocks and %ld arcs\n",
261               n_blocks_in_func, n_arcs_in_func);
262
263       n_functions += 1;
264       n_blocks += n_blocks_in_func;
265       n_arcs += n_arcs_in_func;
266       
267       j = 0;
268       while (j < n_blocks_in_func)
269         {
270           long n_arcs_in_block;
271           int k;
272           
273           if (!string_get_int (contents, i, &n_arcs_in_block))
274             break;
275
276           i += 4;
277
278           printf ("  Block has %ld arcs\n", n_arcs_in_block);
279           
280           k = 0;
281           while (k < n_arcs_in_block)
282             {
283               long destination_block;
284               long flags;
285               
286               if (!string_get_int (contents, i, &destination_block))
287                 break;
288
289               i += 4;
290               
291               if (!string_get_int (contents, i, &flags))
292                 break;
293
294               i += 4;
295
296               printf ("    Arc has destination block %ld flags 0x%lx\n",
297                       destination_block, flags);
298
299               if ((flags & FLAG_ON_TREE) == 0)
300                 n_arcs_off_tree += 1;
301               
302               ++k;
303             }
304
305           if (k < n_arcs_in_block)
306             break;
307           
308           ++j;
309         }
310
311       if (j < n_blocks_in_func)
312         break;
313
314       if (!string_get_int (contents, i, &val))
315         break;
316
317       i += 4;
318
319       if (val != -1)
320         die ("-1 separator not found\n");
321     }
322
323   printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
324           n_functions, n_blocks, n_arcs, n_arcs_off_tree);
325 }
326
327 /* The da file contains first a count of arcs in the file,
328  * then a count of executions for all "off tree" arcs
329  * in the file.
330  */
331 static void
332 dump_da_file (const DBusString *contents)
333 {
334   int i;
335   dbus_int64_t val;
336   int n_arcs;
337   int claimed_n_arcs;
338
339   i = 0;
340   if (!string_get_int64 (contents, i, &val))
341     return;
342
343   i += 8;
344   
345   printf ("%ld arcs in file\n", (long) val);
346   claimed_n_arcs = val;
347   
348   n_arcs = 0;
349   while (string_get_int64 (contents, i, &val))
350     {
351       i += 8;
352
353       printf ("%ld executions of arc %d\n",
354               (long) val, n_arcs);
355
356       ++n_arcs;
357     }
358
359   if (n_arcs != claimed_n_arcs)
360     {
361       printf ("File claimed to have %d arcs but only had %d\n",
362               claimed_n_arcs, n_arcs);
363     }
364 }
365
366 typedef struct Arc Arc;
367 typedef struct Block Block;
368 typedef struct Function Function;
369 typedef struct File File;
370 typedef struct Line Line;
371
372 struct Arc
373 {
374   int source;
375   int target;
376   dbus_int64_t arc_count;
377   unsigned int count_valid : 1;
378   unsigned int on_tree : 1;
379   unsigned int fake : 1;
380   unsigned int fall_through : 1;
381   Arc *pred_next;
382   Arc *succ_next;
383 };
384
385 struct Block
386 {
387   Arc *succ;
388   Arc *pred;
389   dbus_int64_t succ_count;
390   dbus_int64_t pred_count;
391   dbus_int64_t exec_count;
392   DBusList *lines;
393   unsigned int count_valid : 1;
394   unsigned int on_tree : 1;
395   unsigned int inside_dbus_build_tests : 1;
396 };
397
398 struct Function
399 {
400   char *name;
401   Block *block_graph;
402   int n_blocks;
403   unsigned int unused : 1;
404   unsigned int inside_dbus_build_tests : 1;
405   unsigned int partial : 1; /* only some of the blocks were executed */
406 };
407
408 struct Line
409 {
410   int    number;
411   char  *text;
412   DBusList *blocks;
413   unsigned int inside_dbus_build_tests : 1;
414   unsigned int partial : 1; /* only some of the blocks were executed */
415 };
416
417 struct File
418 {
419   char *name;
420   Line *lines;
421   int   n_lines;
422   DBusList *functions;
423 };
424
425 static void
426 function_add_arc (Function *function,
427                   long      source,
428                   long      target,
429                   long      flags)
430 {
431   Arc *arc;
432
433   arc = dbus_new0 (Arc, 1);
434   if (arc == NULL)
435     die ("no memory\n");
436   
437   arc->target = target;
438   arc->source = source;
439
440   arc->succ_next = function->block_graph[source].succ;
441   function->block_graph[source].succ = arc;
442   function->block_graph[source].succ_count += 1;
443
444   arc->pred_next = function->block_graph[target].pred;
445   function->block_graph[target].pred = arc;
446   function->block_graph[target].pred_count += 1;
447
448   if ((flags & FLAG_ON_TREE) != 0)
449     arc->on_tree = TRUE;
450
451   if ((flags & FLAG_FAKE) != 0)
452     arc->fake = TRUE;
453
454   if ((flags & FLAG_FALL_THROUGH) != 0)
455     arc->fall_through = TRUE;
456 }
457
458
459 static Arc*
460 reverse_arcs (Arc *arc)
461 {
462   struct Arc *prev = 0;
463   struct Arc *next;
464
465   for ( ; arc; arc = next)
466     {
467       next = arc->succ_next;
468       arc->succ_next = prev;
469       prev = arc;
470     }
471
472   return prev;
473 }
474
475 static void
476 function_reverse_succ_arcs (Function *func)
477 {
478   /* Must reverse the order of all succ arcs, to ensure that they match
479    * the order of the data in the .da file.
480    */
481   int i;
482   
483   for (i = 0; i < func->n_blocks; i++)
484     if (func->block_graph[i].succ)
485       func->block_graph[i].succ = reverse_arcs (func->block_graph[i].succ);
486 }
487
488 static void
489 get_functions_from_bbg (const DBusString  *contents,
490                         DBusList         **functions)
491 {
492   int i;
493   long val;
494   int n_functions;
495   int n_arcs;
496   int n_blocks;
497   int n_arcs_off_tree;
498
499 #if 0
500   printf ("Loading arcs and blocks from .bbg file\n");
501 #endif
502   
503   n_arcs_off_tree = 0;
504   n_blocks = 0;
505   n_arcs = 0;
506   n_functions = 0;
507   i = 0;
508   while (string_get_int (contents, i, &val))
509     {
510       Function *func;
511       long n_blocks_in_func;
512       long n_arcs_in_func; 
513       int j;
514       
515       n_blocks_in_func = val;
516       
517       i += 4;
518
519       if (!string_get_int (contents, i, &n_arcs_in_func))
520         break;
521
522       i += 4;
523
524       n_functions += 1;
525       n_blocks += n_blocks_in_func;
526       n_arcs += n_arcs_in_func;
527
528       func = dbus_new0 (Function, 1);
529       if (func == NULL)
530         die ("no memory\n");
531
532       func->block_graph = dbus_new0 (Block, n_blocks_in_func);
533       func->n_blocks = n_blocks_in_func;
534       
535       j = 0;
536       while (j < n_blocks_in_func)
537         {
538           long n_arcs_in_block;
539           int k;
540           
541           if (!string_get_int (contents, i, &n_arcs_in_block))
542             break;
543
544           i += 4;
545           
546           k = 0;
547           while (k < n_arcs_in_block)
548             {
549               long destination_block;
550               long flags;
551               
552               if (!string_get_int (contents, i, &destination_block))
553                 break;
554
555               i += 4;
556               
557               if (!string_get_int (contents, i, &flags))
558                 break;
559
560               i += 4;
561
562               if ((flags & FLAG_ON_TREE) == 0)
563                 n_arcs_off_tree += 1;
564
565               function_add_arc (func, j, destination_block,
566                                 flags);
567               
568               ++k;
569             }
570
571           if (k < n_arcs_in_block)
572             break;
573           
574           ++j;
575         }
576
577       if (j < n_blocks_in_func)
578         break;
579
580       function_reverse_succ_arcs (func);
581       
582       if (!_dbus_list_append (functions, func))
583         die ("no memory\n");
584       
585       if (!string_get_int (contents, i, &val))
586         break;
587
588       i += 4;
589
590       if (val != -1)
591         die ("-1 separator not found\n");
592     }
593
594 #if 0
595   printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
596           n_functions, n_blocks, n_arcs, n_arcs_off_tree);
597 #endif
598   
599   _dbus_assert (n_functions == _dbus_list_get_length (functions));
600 }
601
602 static void
603 add_counts_from_da (const DBusString  *contents,
604                     DBusList         **functions)
605 {
606   int i;
607   dbus_int64_t val;
608   int n_arcs;
609   int claimed_n_arcs;
610   DBusList *link;
611   Function *current_func;  
612   int current_block;
613   Arc *current_arc;
614
615 #if 0
616   printf ("Loading execution count for each arc from .da file\n");
617 #endif
618   
619   i = 0;
620   if (!string_get_int64 (contents, i, &val))
621     return;
622
623   i += 8;
624   
625   claimed_n_arcs = val;
626
627   link = _dbus_list_get_first_link (functions);
628   if (link == NULL)
629     goto done;
630
631   current_func = link->data;
632   current_block = 0;
633   current_arc = current_func->block_graph[current_block].succ;
634   
635   n_arcs = 0;
636   while (string_get_int64 (contents, i, &val))
637     {
638       i += 8;
639
640       while (current_arc == NULL ||
641              current_arc->on_tree)
642         {
643           if (current_arc == NULL)
644             {
645               ++current_block;
646               
647               if (current_block == current_func->n_blocks)
648                 {
649                   link = _dbus_list_get_next_link (functions, link);
650                   if (link == NULL)
651                     {
652                       fprintf (stderr, "Ran out of functions loading .da file\n");
653                       goto done;
654                     }
655                   current_func = link->data;
656                   current_block = 0;
657                 }
658               
659               current_arc = current_func->block_graph[current_block].succ;
660             }
661           else
662             {
663               current_arc = current_arc->succ_next;
664             }
665         }
666
667       _dbus_assert (current_arc != NULL);
668       _dbus_assert (!current_arc->on_tree);
669
670       current_arc->arc_count = val;
671       current_arc->count_valid = TRUE;
672       current_func->block_graph[current_block].succ_count -= 1;
673       current_func->block_graph[current_arc->target].pred_count -= 1;
674       
675       ++n_arcs;
676
677       current_arc = current_arc->succ_next;
678     }
679
680  done:
681   
682   if (n_arcs != claimed_n_arcs)
683     {
684       fprintf (stderr, "File claimed to have %d arcs but only had %d\n",
685                claimed_n_arcs, n_arcs);
686       exit (1);
687     }
688
689 #if 0
690   printf ("%d arcs in file\n", n_arcs);
691 #endif
692 }
693
694 static void
695 function_solve_graph (Function *func)
696 {
697   int passes, changes;
698   dbus_int64_t total;
699   int i;
700   Arc *arc;
701   Block *block_graph;
702   int n_blocks;
703
704 #if 0
705   printf ("Solving function graph\n");
706 #endif
707   
708   n_blocks = func->n_blocks;
709   block_graph = func->block_graph;
710
711   /* For every block in the file,
712      - if every exit/entrance arc has a known count, then set the block count
713      - if the block count is known, and every exit/entrance arc but one has
714      a known execution count, then set the count of the remaining arc
715
716      As arc counts are set, decrement the succ/pred count, but don't delete
717      the arc, that way we can easily tell when all arcs are known, or only
718      one arc is unknown.  */
719
720   /* The order that the basic blocks are iterated through is important.
721      Since the code that finds spanning trees starts with block 0, low numbered
722      arcs are put on the spanning tree in preference to high numbered arcs.
723      Hence, most instrumented arcs are at the end.  Graph solving works much
724      faster if we propagate numbers from the end to the start.
725
726      This takes an average of slightly more than 3 passes.  */
727
728   changes = 1;
729   passes = 0;
730   while (changes)
731     {
732       passes++;
733       changes = 0;
734
735       for (i = n_blocks - 1; i >= 0; i--)
736         {
737           if (! block_graph[i].count_valid)
738             {
739               if (block_graph[i].succ_count == 0)
740                 {
741                   total = 0;
742                   for (arc = block_graph[i].succ; arc;
743                        arc = arc->succ_next)
744                     total += arc->arc_count;
745                   block_graph[i].exec_count = total;
746                   block_graph[i].count_valid = 1;
747                   changes = 1;
748                 }
749               else if (block_graph[i].pred_count == 0)
750                 {
751                   total = 0;
752                   for (arc = block_graph[i].pred; arc;
753                        arc = arc->pred_next)
754                     total += arc->arc_count;
755                   block_graph[i].exec_count = total;
756                   block_graph[i].count_valid = 1;
757                   changes = 1;
758                 }
759             }
760           if (block_graph[i].count_valid)
761             {
762               if (block_graph[i].succ_count == 1)
763                 {
764                   total = 0;
765                   /* One of the counts will be invalid, but it is zero,
766                      so adding it in also doesn't hurt.  */
767                   for (arc = block_graph[i].succ; arc;
768                        arc = arc->succ_next)
769                     total += arc->arc_count;
770                   /* Calculate count for remaining arc by conservation.  */
771                   total = block_graph[i].exec_count - total;
772                   /* Search for the invalid arc, and set its count.  */
773                   for (arc = block_graph[i].succ; arc;
774                        arc = arc->succ_next)
775                     if (! arc->count_valid)
776                       break;
777                   if (! arc)
778                     die ("arc == NULL\n");
779                   arc->count_valid = 1;
780                   arc->arc_count = total;
781                   block_graph[i].succ_count -= 1;
782
783                   block_graph[arc->target].pred_count -= 1;
784                   changes = 1;
785                 }
786               if (block_graph[i].pred_count == 1)
787                 {
788                   total = 0;
789                   /* One of the counts will be invalid, but it is zero,
790                      so adding it in also doesn't hurt.  */
791                   for (arc = block_graph[i].pred; arc;
792                        arc = arc->pred_next)
793                     total += arc->arc_count;
794                   /* Calculate count for remaining arc by conservation.  */
795                   total = block_graph[i].exec_count - total;
796                   /* Search for the invalid arc, and set its count.  */
797                   for (arc = block_graph[i].pred; arc;
798                        arc = arc->pred_next)
799                     if (! arc->count_valid)
800                       break;
801                   if (! arc)
802                     die ("arc == NULL\n");
803                   arc->count_valid = 1;
804                   arc->arc_count = total;
805                   block_graph[i].pred_count -= 1;
806
807                   block_graph[arc->source].succ_count -= 1;
808                   changes = 1;
809                 }
810             }
811         }
812     }
813
814   /* If the graph has been correctly solved, every block will have a
815    * succ and pred count of zero.
816    */
817   for (i = 0; i < n_blocks; i++)
818     {
819       if (block_graph[i].succ_count || block_graph[i].pred_count)
820         {
821           fprintf (stderr, "Block graph solved incorrectly\n");
822           fprintf (stderr, " block %d has succ_count = %d pred_count = %d\n",
823                    i, (int) block_graph[i].succ_count, (int) block_graph[i].pred_count);
824           exit (1);
825         }
826     }
827 }
828
829 static void
830 solve_graphs (DBusList **functions)
831 {
832   DBusList *link;
833
834   link = _dbus_list_get_first_link (functions);
835   while (link != NULL)
836     {
837       Function *func = link->data;
838
839       function_solve_graph (func);
840       
841       link = _dbus_list_get_next_link (functions, link);
842     }
843 }
844
845 static void
846 load_functions_for_c_file (const DBusString *filename,
847                            DBusList        **functions)
848 {
849   DBusString bbg_filename;
850   DBusString da_filename;
851   DBusString contents;
852   DBusError error;
853
854   dbus_error_init (&error);
855   
856   if (!_dbus_string_init (&bbg_filename) ||
857       !_dbus_string_init (&da_filename) ||
858       !_dbus_string_copy (filename, 0, &bbg_filename, 0) ||
859       !_dbus_string_copy (filename, 0, &da_filename, 0) ||
860       !_dbus_string_init (&contents))
861     die ("no memory\n");
862
863   _dbus_string_shorten (&bbg_filename, 2);
864   _dbus_string_shorten (&da_filename, 2);
865
866   if (!_dbus_string_append (&bbg_filename, ".bbg") ||
867       !_dbus_string_append (&da_filename, ".da"))
868     die ("no memory\n");
869       
870   if (!_dbus_file_get_contents (&contents, &bbg_filename,
871                                 &error))
872     {
873       fprintf (stderr, "Could not open file: %s\n",
874                error.message);
875       exit (1);
876     }
877
878   get_functions_from_bbg (&contents, functions);
879
880   _dbus_string_set_length (&contents, 0);
881
882   if (!_dbus_file_get_contents (&contents, &da_filename,
883                                 &error))
884     {
885       fprintf (stderr, "Could not open file: %s\n",
886                error.message);
887       exit (1);
888     }
889   
890   add_counts_from_da (&contents, functions);
891   
892   solve_graphs (functions);
893
894   _dbus_string_free (&contents);
895   _dbus_string_free (&da_filename);
896   _dbus_string_free (&bbg_filename);
897 }
898
899 static void
900 get_lines_from_bb_file (const DBusString *contents,
901                         File             *fl)
902 {
903   int i;
904   long val;
905   int n_functions;
906   dbus_bool_t in_our_file;
907   DBusList *link;
908   Function *func;
909   int block;
910
911 #if 0
912   printf ("Getting line numbers for blocks from .bb file\n");
913 #endif
914   
915   /* There's this "filename" field in the .bb file which
916    * mysteriously comes *after* the first function in the
917    * file in the .bb file; and every .bb file seems to
918    * have only one filename. I don't understand
919    * what's going on here, so just set in_our_file = TRUE
920    * at the start categorically.
921    */
922   
923   block = 0;
924   func = NULL;
925   in_our_file = TRUE;
926   link = _dbus_list_get_first_link (&fl->functions);
927   n_functions = 0;
928   i = 0;
929   while (string_get_int (contents, i, &val))
930     {
931       i += 4;
932       
933       switch (val)
934         {
935         case BB_FILENAME:
936           {
937             DBusString f;
938
939             if (!_dbus_string_init (&f))
940               die ("no memory\n");
941
942             if (string_get_string (contents, i,
943                                    BB_FILENAME,
944                                    &f, &i))
945               {
946                 /* fl->name is a full path and the filename in .bb is
947                  * not.
948                  */
949                 DBusString tmp_str;
950
951                 _dbus_string_init_const (&tmp_str, fl->name);
952                 
953                 if (_dbus_string_ends_with_c_str (&tmp_str,
954                                                   _dbus_string_get_const_data (&f)))
955                   in_our_file = TRUE;
956                 else
957                   in_our_file = FALSE;
958                 
959 #if 0
960                 fprintf (stderr,
961                          "File %s in .bb, looking for %s, in_our_file = %d\n",
962                          _dbus_string_get_const_data (&f),
963                          fl->name,
964                          in_our_file);
965 #endif
966               }
967             _dbus_string_free (&f);
968           }
969           break;
970         case BB_FUNCTION:
971           {
972             DBusString f;
973             if (!_dbus_string_init (&f))
974               die ("no memory\n");
975
976             if (string_get_string (contents, i,
977                                    BB_FUNCTION,
978                                    &f, &i))
979               {
980 #if 0
981                 fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f));
982 #endif
983
984                 block = 0;
985                 
986                 if (in_our_file)
987                   {
988                     if (link == NULL)
989                       {
990                         fprintf (stderr, "No function object for function %s\n",
991                                  _dbus_string_get_const_data (&f));
992                       }
993                     else
994                       {
995                         func = link->data;
996                         link = _dbus_list_get_next_link (&fl->functions, link);
997                         
998                         if (func->name == NULL)
999                           {
1000                             if (!_dbus_string_copy_data (&f, &func->name))
1001                               die ("no memory\n");
1002                           }
1003                         else
1004                           {
1005                             die ("got two names for function?\n");
1006                           }
1007                       }
1008                   }
1009               }
1010             _dbus_string_free (&f);
1011
1012             n_functions += 1;
1013           }
1014           break;
1015         case BB_ENDOFLIST:
1016           block += 1;
1017           break;
1018         default:
1019 #if 0
1020           fprintf (stderr, "Line %ld\n", val);
1021 #endif
1022
1023           if (val >= fl->n_lines)
1024             {
1025               fprintf (stderr, "Line %ld but file only has %d lines\n",
1026                        val, fl->n_lines);
1027             }
1028           else if (func != NULL)
1029             {
1030               val -= 1; /* To convert the 1-based line number to 0-based */
1031               _dbus_assert (val >= 0);
1032               
1033               if (block < func->n_blocks)
1034                 {
1035                   if (!_dbus_list_append (&func->block_graph[block].lines,
1036                                           &fl->lines[val]))
1037                     die ("no memory\n");
1038                   
1039                   
1040                   if (!_dbus_list_append (&fl->lines[val].blocks,
1041                                           &func->block_graph[block]))
1042                     die ("no memory\n");
1043                 }
1044               else
1045                 {
1046                   fprintf (stderr, "Line number for block %d but function only has %d blocks\n",
1047                            block, func->n_blocks);
1048                 }
1049             }
1050           else
1051             {
1052               fprintf (stderr, "Line %ld given outside of any function\n",
1053                        val);
1054             }
1055           
1056           break;
1057         }
1058     }
1059
1060 #if 0
1061   printf ("%d functions in file\n", n_functions);
1062 #endif
1063 }
1064
1065
1066 static void
1067 load_block_line_associations (const DBusString *filename,
1068                               File             *f)
1069 {
1070   DBusString bb_filename;
1071   DBusString contents;
1072   DBusError error;
1073
1074   dbus_error_init (&error);
1075   
1076   if (!_dbus_string_init (&bb_filename) ||
1077       !_dbus_string_copy (filename, 0, &bb_filename, 0) ||
1078       !_dbus_string_init (&contents))
1079     die ("no memory\n");
1080
1081   _dbus_string_shorten (&bb_filename, 2);
1082
1083   if (!_dbus_string_append (&bb_filename, ".bb"))
1084     die ("no memory\n");
1085       
1086   if (!_dbus_file_get_contents (&contents, &bb_filename,
1087                                 &error))
1088     {
1089       fprintf (stderr, "Could not open file: %s\n",
1090                error.message);
1091       exit (1);
1092     }
1093   
1094   get_lines_from_bb_file (&contents, f);
1095
1096   _dbus_string_free (&contents);
1097   _dbus_string_free (&bb_filename);
1098 }
1099
1100 static int
1101 count_lines_in_string (const DBusString *str)
1102 {
1103   int n_lines;
1104   const char *p;
1105   const char *prev;
1106   const char *end;
1107   const char *last_line_end;
1108
1109 #if 0
1110   printf ("Counting lines in source file\n");
1111 #endif
1112   
1113   n_lines = 0;  
1114   prev = NULL;
1115   p = _dbus_string_get_const_data (str);
1116   end = p + _dbus_string_get_length (str);
1117   last_line_end = p;
1118   while (p != end)
1119     {
1120       /* too lazy to handle \r\n as one linebreak */
1121       if (*p == '\n' || *p == '\r')
1122         {
1123           ++n_lines;
1124           last_line_end = p + 1;
1125         }
1126
1127       prev = p;
1128       ++p;
1129     }
1130
1131   if (last_line_end != p)
1132     ++n_lines;
1133   
1134   return n_lines;
1135 }
1136
1137 static void
1138 fill_line_content (const DBusString *str,
1139                    Line             *lines)
1140 {
1141   int n_lines;
1142   const char *p;
1143   const char *prev;
1144   const char *end;
1145   const char *last_line_end;
1146
1147 #if 0
1148   printf ("Saving contents of each line in source file\n");
1149 #endif
1150   
1151   n_lines = 0;
1152   prev = NULL;
1153   p = _dbus_string_get_const_data (str);
1154   end = p + _dbus_string_get_length (str);
1155   last_line_end = p;
1156   while (p != end)
1157     {
1158       if (*p == '\n' || *p == '\r')
1159         {
1160           lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1);
1161           if (lines[n_lines].text == NULL)
1162             die ("no memory\n");
1163
1164           memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1165           lines[n_lines].number = n_lines + 1;
1166           
1167           ++n_lines;
1168
1169           last_line_end = p + 1;
1170         }
1171
1172       prev = p;
1173       ++p;
1174     }
1175
1176   if (p != last_line_end)
1177     {
1178       memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1179       ++n_lines;
1180     }
1181 }
1182
1183 static void
1184 mark_unused_functions (File  *f)
1185 {
1186   int i;
1187   DBusList *link;
1188
1189   link = _dbus_list_get_first_link (&f->functions);
1190   while (link != NULL)
1191     {
1192       Function *func = link->data;
1193       dbus_bool_t used;
1194
1195       used = FALSE;
1196       i = 0;
1197       while (i < func->n_blocks)
1198         {
1199           if (func->block_graph[i].exec_count > 0)
1200             used = TRUE;
1201           
1202           ++i;
1203         }
1204
1205       if (!used)
1206         func->unused = TRUE;
1207
1208       link = _dbus_list_get_next_link (&f->functions, link);
1209     }
1210 }
1211
1212 static void
1213 mark_inside_dbus_build_tests (File  *f)
1214 {
1215   int i;
1216   DBusList *link;
1217   int inside_depth;
1218
1219   inside_depth = 0;
1220   i = 0;
1221   while (i < f->n_lines)
1222     {
1223       Line *l = &f->lines[i];
1224
1225       if (inside_depth == 0)
1226         {
1227           const char *a, *b;
1228           
1229           a = strstr (l->text, "#ifdef");
1230           b = strstr (l->text, "DBUS_BUILD_TESTS");
1231           if (a && b)
1232             inside_depth += 1;
1233         }
1234       else
1235         {
1236           if (strstr (l->text, "#if") != NULL)
1237             inside_depth += 1;
1238           else if (strstr (l->text, "#endif") != NULL)
1239             inside_depth -= 1;
1240         }
1241
1242       if (inside_depth > 0)
1243         {
1244           /* Mark the line and its blocks */
1245           DBusList *blink;
1246
1247           l->inside_dbus_build_tests = TRUE;
1248           
1249           blink = _dbus_list_get_first_link (&l->blocks);
1250           while (blink != NULL)
1251             {
1252               Block *b = blink->data;
1253
1254               b->inside_dbus_build_tests = TRUE;
1255               
1256               blink = _dbus_list_get_next_link (&l->blocks, blink);
1257             }
1258         }
1259       
1260       ++i;
1261     }
1262
1263   /* Now mark functions where for all blocks that are associated
1264    * with a source line, the block is inside_dbus_build_tests.
1265    */
1266   link = _dbus_list_get_first_link (&f->functions);
1267   while (link != NULL)
1268     {
1269       Function *func = link->data;
1270       
1271       i = 0;
1272       while (i < func->n_blocks)
1273         {
1274           /* Break as soon as any block is not a test block */
1275           if (func->block_graph[i].lines != NULL &&
1276               !func->block_graph[i].inside_dbus_build_tests)
1277             break;
1278           
1279           ++i;
1280         }
1281
1282       if (i == func->n_blocks)
1283         func->inside_dbus_build_tests = TRUE;
1284       
1285       link = _dbus_list_get_next_link (&f->functions, link);
1286     } 
1287 }
1288
1289 static void
1290 mark_partials (File  *f)
1291 {
1292   int i;
1293   DBusList *link;
1294   
1295   i = 0;
1296   while (i < f->n_lines)
1297     {
1298       Line *l = &f->lines[i];
1299       DBusList *blink;
1300       int n_blocks;
1301       int n_blocks_executed;
1302
1303       n_blocks = 0;
1304       n_blocks_executed = 0;
1305       blink = _dbus_list_get_first_link (&l->blocks);
1306       while (blink != NULL)
1307         {
1308           Block *b = blink->data;
1309           
1310           if (b->exec_count > 0)
1311             n_blocks_executed += 1;
1312
1313           n_blocks += 1;
1314           
1315           blink = _dbus_list_get_next_link (&l->blocks, blink);
1316         }
1317
1318       if (n_blocks_executed > 0 &&
1319           n_blocks_executed < n_blocks)
1320         l->partial = TRUE;
1321
1322       ++i;
1323     }
1324
1325   link = _dbus_list_get_first_link (&f->functions);
1326   while (link != NULL)
1327     {
1328       Function *func = link->data;
1329       int i;
1330       int n_blocks;
1331       int n_blocks_executed;
1332
1333       n_blocks = 0;
1334       n_blocks_executed = 0;
1335       i = 0;
1336       while (i < func->n_blocks)
1337         {
1338           /* Break as soon as any block is not a test block */
1339           if (func->block_graph[i].exec_count > 0)
1340             n_blocks_executed += 1;
1341
1342           n_blocks += 1;
1343           ++i;
1344         }
1345
1346       if (n_blocks_executed > 0 &&
1347           n_blocks_executed < n_blocks)
1348         func->partial = TRUE;
1349       
1350       link = _dbus_list_get_next_link (&f->functions, link);
1351     }
1352 }
1353
1354 static File*
1355 load_c_file (const DBusString *filename)
1356 {
1357   DBusString contents;
1358   DBusError error;
1359   File *f;
1360   
1361   f = dbus_new0 (File, 1);
1362   if (f == NULL)
1363     die ("no memory\n");
1364
1365   if (!_dbus_string_copy_data (filename, &f->name))
1366     die ("no memory\n");
1367   
1368   if (!_dbus_string_init (&contents))
1369     die ("no memory\n");
1370       
1371   dbus_error_init (&error);
1372
1373   if (!_dbus_file_get_contents (&contents, filename,
1374                                 &error))
1375     {
1376       fprintf (stderr, "Could not open file: %s\n",
1377                error.message);
1378       dbus_error_free (&error);
1379       exit (1);
1380     }
1381       
1382   load_functions_for_c_file (filename, &f->functions);
1383
1384   f->n_lines = count_lines_in_string (&contents);
1385   f->lines = dbus_new0 (Line, f->n_lines);
1386   if (f->lines == NULL)
1387     die ("no memory\n");
1388
1389   fill_line_content (&contents, f->lines);
1390   
1391   _dbus_string_free (&contents);
1392
1393   load_block_line_associations (filename, f);
1394
1395   mark_unused_functions (f);
1396   mark_inside_dbus_build_tests (f);
1397   mark_partials (f);
1398   
1399   return f;
1400 }
1401
1402 typedef struct Stats Stats;
1403
1404 struct Stats
1405 {
1406   int n_blocks;
1407   int n_blocks_executed;
1408   int n_blocks_inside_dbus_build_tests;
1409   
1410   int n_lines; /* lines that have blocks on them */
1411   int n_lines_executed;
1412   int n_lines_partial;
1413   int n_lines_inside_dbus_build_tests;
1414   
1415   int n_functions;
1416   int n_functions_executed;
1417   int n_functions_partial;
1418   int n_functions_inside_dbus_build_tests;
1419 };
1420
1421 static dbus_bool_t
1422 line_was_executed (Line *l)
1423 {
1424   DBusList *link;
1425
1426   link = _dbus_list_get_first_link (&l->blocks);
1427   while (link != NULL)
1428     {
1429       Block *b = link->data;
1430
1431       if (b->exec_count > 0)
1432         return TRUE;
1433       
1434       link = _dbus_list_get_next_link (&l->blocks, link);
1435     }
1436
1437   return FALSE;
1438 }
1439
1440
1441 static int
1442 line_exec_count (Line *l)
1443 {
1444   DBusList *link;
1445   dbus_int64_t total;
1446
1447   total = 0;
1448   link = _dbus_list_get_first_link (&l->blocks);
1449   while (link != NULL)
1450     {
1451       Block *b = link->data;
1452
1453       total += b->exec_count;
1454       
1455       link = _dbus_list_get_next_link (&l->blocks, link);
1456     }
1457
1458   return total;
1459 }
1460
1461 static void
1462 merge_stats_for_file (Stats *stats,
1463                       File  *f)
1464 {
1465   int i;
1466   DBusList *link;
1467   
1468   for (i = 0; i < f->n_lines; ++i)
1469     {
1470       Line *l = &f->lines[i];
1471       
1472       if (l->inside_dbus_build_tests)
1473         {
1474           stats->n_lines_inside_dbus_build_tests += 1;
1475           continue;
1476         }
1477       
1478       if (line_was_executed (l))
1479         stats->n_lines_executed += 1;
1480
1481       if (l->blocks != NULL)
1482         stats->n_lines += 1;
1483
1484       if (l->partial)
1485         stats->n_lines_partial += 1;
1486     }
1487
1488   link = _dbus_list_get_first_link (&f->functions);
1489   while (link != NULL)
1490     {
1491       Function *func = link->data;
1492
1493       if (func->inside_dbus_build_tests)
1494         stats->n_functions_inside_dbus_build_tests += 1;
1495       else
1496         {
1497           stats->n_functions += 1;
1498
1499           if (!func->unused)
1500             stats->n_functions_executed += 1;
1501           
1502           if (func->partial)
1503             stats->n_functions_partial += 1;
1504         }
1505           
1506       i = 0;
1507       while (i < func->n_blocks)
1508         {
1509           Block *b = &func->block_graph[i];
1510
1511           if (b->inside_dbus_build_tests)
1512             stats->n_blocks_inside_dbus_build_tests += 1;
1513           else
1514             {
1515               if (b->exec_count > 0)
1516                 stats->n_blocks_executed += 1;
1517               
1518               stats->n_blocks += 1;
1519             }
1520           
1521           ++i;
1522         }
1523
1524       link = _dbus_list_get_next_link (&f->functions, link);
1525     }
1526 }
1527
1528 /* The output of this matches gcov exactly ("diff" shows no difference) */
1529 static void
1530 print_annotated_source_gcov_format (File *f)
1531 {
1532   int i;
1533   
1534   i = 0;
1535   while (i < f->n_lines)
1536     {
1537       Line *l = &f->lines[i];
1538
1539       if (l->blocks != NULL)
1540         {
1541           int exec_count;
1542           
1543           exec_count = line_exec_count (l);
1544           
1545           if (exec_count > 0)
1546             printf ("%12d    %s\n",
1547                     exec_count, l->text);
1548           else
1549             printf ("      ######    %s\n", l->text);
1550         }
1551       else
1552         {
1553           printf ("\t\t%s\n", l->text);
1554         }
1555           
1556       ++i;
1557     }
1558 }
1559
1560 static void
1561 print_annotated_source (File *f)
1562 {
1563   int i;
1564   
1565   i = 0;
1566   while (i < f->n_lines)
1567     {
1568       Line *l = &f->lines[i];
1569
1570       if (l->inside_dbus_build_tests)
1571         printf ("*");
1572       else
1573         printf (" ");
1574       
1575       if (l->blocks != NULL)
1576         {
1577           int exec_count;
1578           
1579           exec_count = line_exec_count (l);
1580           
1581           if (exec_count > 0)
1582             printf ("%12d    %s\n",
1583                     exec_count, l->text);
1584           else
1585             printf ("      ######    %s\n", l->text);
1586         }
1587       else
1588         {
1589           printf ("\t\t%s\n", l->text);
1590         }
1591           
1592       ++i;
1593     }
1594 }
1595
1596 static void
1597 print_block_superdetails (File *f)
1598 {
1599   DBusList *link;
1600   int i;
1601   
1602   link = _dbus_list_get_first_link (&f->functions);
1603   while (link != NULL)
1604     {
1605       Function *func = link->data;
1606
1607       printf ("=== %s():\n", func->name);
1608
1609       i = 0;
1610       while (i < func->n_blocks)
1611         {
1612           Block *b = &func->block_graph[i];
1613           DBusList *l;
1614           
1615           printf ("  %5d executed %d times%s\n", i,
1616                   (int) b->exec_count,
1617                   b->inside_dbus_build_tests ?
1618                   " [inside DBUS_BUILD_TESTS]" : "");
1619                   
1620           l = _dbus_list_get_first_link (&b->lines);
1621           while (l != NULL)
1622             {
1623               Line *line = l->data;
1624
1625               printf ("4%d\t%s\n", line->number, line->text);
1626
1627               l = _dbus_list_get_next_link (&b->lines, l);
1628             }
1629           
1630           ++i;
1631         }
1632       
1633       link = _dbus_list_get_next_link (&f->functions, link);
1634     }
1635 }
1636
1637 static void
1638 print_one_file (const DBusString *filename)
1639 {
1640   if (_dbus_string_ends_with_c_str (filename, ".bb"))
1641     {
1642       DBusString contents;
1643       DBusError error;
1644       
1645       if (!_dbus_string_init (&contents))
1646         die ("no memory\n");
1647       
1648       dbus_error_init (&error);
1649
1650       if (!_dbus_file_get_contents (&contents, filename,
1651                                     &error))
1652         {
1653           fprintf (stderr, "Could not open file: %s\n",
1654                    error.message);
1655           dbus_error_free (&error);
1656           exit (1);
1657         }
1658       
1659       dump_bb_file (&contents);
1660
1661       _dbus_string_free (&contents);
1662     }
1663   else if (_dbus_string_ends_with_c_str (filename, ".bbg"))
1664     {
1665       DBusString contents;
1666       DBusError error;
1667       
1668       if (!_dbus_string_init (&contents))
1669         die ("no memory\n");
1670       
1671       dbus_error_init (&error);
1672
1673       if (!_dbus_file_get_contents (&contents, filename,
1674                                     &error))
1675         {
1676           fprintf (stderr, "Could not open file: %s\n",
1677                    error.message);
1678           dbus_error_free (&error);
1679           exit (1);
1680         }
1681       
1682       dump_bbg_file (&contents);
1683
1684       _dbus_string_free (&contents);
1685     }
1686   else if (_dbus_string_ends_with_c_str (filename, ".da"))
1687     {
1688       DBusString contents;
1689       DBusError error;
1690       
1691       if (!_dbus_string_init (&contents))
1692         die ("no memory\n");
1693       
1694       dbus_error_init (&error);
1695
1696       if (!_dbus_file_get_contents (&contents, filename,
1697                                     &error))
1698         {
1699           fprintf (stderr, "Could not open file: %s\n",
1700                    error.message);
1701           dbus_error_free (&error);
1702           exit (1);
1703         }
1704       
1705       dump_da_file (&contents);
1706
1707       _dbus_string_free (&contents);
1708     }
1709   else if (_dbus_string_ends_with_c_str (filename, ".c"))
1710     {
1711       File *f;
1712       
1713       f = load_c_file (filename);
1714
1715       print_annotated_source (f);
1716     }
1717   else
1718     {
1719       fprintf (stderr, "Unknown file type %s\n",
1720                _dbus_string_get_const_data (filename));
1721       exit (1);
1722     }
1723 }
1724
1725 static void
1726 print_untested_functions (File *f)
1727 {
1728   DBusList *link;
1729   dbus_bool_t found;
1730
1731   found = FALSE;
1732   link = _dbus_list_get_first_link (&f->functions);
1733   while (link != NULL)
1734     {
1735       Function *func = link->data;
1736
1737       if (func->unused &&
1738           !func->inside_dbus_build_tests)
1739         found = TRUE;
1740       
1741       link = _dbus_list_get_next_link (&f->functions, link);
1742     }
1743
1744   if (!found)
1745     return;
1746
1747   printf ("Untested functions in %s\n", f->name);
1748   printf ("=======\n");
1749   
1750   link = _dbus_list_get_first_link (&f->functions);
1751   while (link != NULL)
1752     {
1753       Function *func = link->data;
1754
1755       if (func->unused &&
1756           !func->inside_dbus_build_tests)
1757         printf ("  %s\n", func->name);
1758       
1759       link = _dbus_list_get_next_link (&f->functions, link);
1760     }
1761
1762   printf ("\n");
1763 }
1764
1765 static void
1766 print_stats (Stats      *stats,
1767              const char *of_what)
1768 {
1769   int completely;
1770   
1771   printf ("Summary (%s)\n", of_what);
1772   printf ("=======\n");
1773   printf ("  %g%% blocks executed (%d of %d)\n",
1774           (stats->n_blocks_executed / (double) stats->n_blocks) * 100.0,
1775           stats->n_blocks_executed,
1776           stats->n_blocks);
1777
1778   printf ("     (ignored %d blocks inside DBUS_BUILD_TESTS)\n",
1779           stats->n_blocks_inside_dbus_build_tests);
1780       
1781   printf ("  %g%% functions executed (%d of %d)\n",
1782           (stats->n_functions_executed / (double) stats->n_functions) * 100.0,
1783           stats->n_functions_executed,
1784           stats->n_functions);
1785
1786   completely = stats->n_functions_executed - stats->n_functions_partial;
1787   printf ("  %g%% functions completely executed (%d of %d)\n",
1788           (completely / (double) stats->n_functions) * 100.0,
1789           completely,
1790           stats->n_functions);
1791
1792   printf ("     (ignored %d functions inside DBUS_BUILD_TESTS)\n",
1793           stats->n_functions_inside_dbus_build_tests);
1794       
1795   printf ("  %g%% lines executed (%d of %d)\n",
1796           (stats->n_lines_executed / (double) stats->n_lines) * 100.0,
1797           stats->n_lines_executed,
1798           stats->n_lines);
1799
1800   completely = stats->n_lines_executed - stats->n_lines_partial;
1801   printf ("  %g%% lines completely executed (%d of %d)\n",
1802           (completely / (double) stats->n_lines) * 100.0,
1803           completely,
1804           stats->n_lines);
1805
1806   printf ("     (ignored %d lines inside DBUS_BUILD_TESTS)\n",
1807           stats->n_lines_inside_dbus_build_tests);
1808
1809   printf ("\n");
1810 }
1811
1812 typedef enum
1813 {
1814   MODE_PRINT,
1815   MODE_REPORT,
1816   MODE_BLOCKS,
1817   MODE_GCOV
1818 } Mode;
1819
1820 int
1821 main (int argc, char **argv)
1822 {
1823   DBusString filename;
1824   int i;
1825   Mode m;
1826   
1827   if (argc < 2)
1828     {
1829       fprintf (stderr, "Must specify files on command line\n");
1830       return 1;
1831     }
1832
1833   m = MODE_PRINT;
1834   i = 1;
1835
1836   if (strcmp (argv[i], "--report") == 0)
1837     {
1838       m = MODE_REPORT;
1839       ++i;
1840     }
1841   else if (strcmp (argv[i], "--blocks") == 0)
1842     {
1843       m = MODE_BLOCKS;
1844       ++i;
1845     }
1846   else if (strcmp (argv[i], "--gcov") == 0)
1847     {
1848       m = MODE_GCOV;
1849       ++i;
1850     }
1851
1852   
1853   if (i == argc)
1854     {
1855       fprintf (stderr, "Must specify files on command line\n");
1856       return 1;
1857     }
1858
1859   if (m == MODE_PRINT)
1860     {
1861       while (i < argc)
1862         {
1863           _dbus_string_init_const (&filename, argv[i]);
1864           
1865           print_one_file (&filename);
1866           
1867           ++i;
1868         }
1869     }
1870   else if (m == MODE_BLOCKS || m == MODE_GCOV)
1871     {
1872       while (i < argc)
1873         {
1874           File *f;
1875           
1876           _dbus_string_init_const (&filename, argv[i]);
1877       
1878           f = load_c_file (&filename);
1879
1880           if (m == MODE_BLOCKS)
1881             print_block_superdetails (f);
1882           else if (m == MODE_GCOV)
1883             print_annotated_source_gcov_format (f);
1884           
1885           ++i;
1886         }
1887     }
1888   else if (m == MODE_REPORT)
1889     {
1890       Stats stats = { 0, };
1891       DBusList *files;
1892       DBusList *link;
1893       DBusHashTable *stats_by_dir;
1894       DBusHashIter iter;
1895       
1896       files = NULL;
1897       while (i < argc)
1898         {
1899           _dbus_string_init_const (&filename, argv[i]);
1900
1901           if (_dbus_string_ends_with_c_str (&filename, ".c"))
1902             {
1903               File *f;
1904               
1905               f = load_c_file (&filename);
1906               
1907               if (!_dbus_list_append (&files, f))
1908                 die ("no memory\n");
1909             }
1910           else
1911             {
1912               fprintf (stderr, "Unknown file type %s\n",
1913                        _dbus_string_get_const_data (&filename));
1914               exit (1);
1915             }
1916           
1917           ++i;
1918         }
1919
1920       link = _dbus_list_get_first_link (&files);
1921       while (link != NULL)
1922         {
1923           File *f = link->data;
1924
1925           merge_stats_for_file (&stats, f);
1926           
1927           link = _dbus_list_get_next_link (&files, link);
1928         }
1929
1930       print_stats (&stats, "all files");
1931
1932       stats_by_dir = _dbus_hash_table_new (DBUS_HASH_STRING,
1933                                            dbus_free, dbus_free);
1934       
1935       link = _dbus_list_get_first_link (&files);
1936       while (link != NULL)
1937         {
1938           File *f = link->data;
1939           DBusString dirname;
1940           char *dirname_c;
1941           Stats *dir_stats;
1942           
1943           _dbus_string_init_const (&filename, f->name);
1944             
1945           if (!_dbus_string_init (&dirname))
1946             die ("no memory\n");
1947
1948           if (!_dbus_string_get_dirname (&filename, &dirname) ||
1949               !_dbus_string_copy_data (&dirname, &dirname_c))
1950             die ("no memory\n");
1951
1952           dir_stats = _dbus_hash_table_lookup_string (stats_by_dir,
1953                                                       dirname_c);
1954
1955           if (dir_stats == NULL)
1956             {
1957               dir_stats = dbus_new0 (Stats, 1);
1958               if (!_dbus_hash_table_insert_string (stats_by_dir, dirname_c,
1959                                                    dir_stats))
1960                 die ("no memory\n");
1961             }
1962           else
1963             dbus_free (dirname_c);
1964           
1965           merge_stats_for_file (dir_stats, f);
1966           
1967           link = _dbus_list_get_next_link (&files, link);
1968         }
1969
1970       _dbus_hash_iter_init (stats_by_dir, &iter);
1971       while (_dbus_hash_iter_next (&iter))
1972         {
1973           const char *dirname = _dbus_hash_iter_get_string_key (&iter);
1974           Stats *dir_stats = _dbus_hash_iter_get_value (&iter);
1975
1976           print_stats (dir_stats, dirname);
1977         }
1978
1979       _dbus_hash_table_unref (stats_by_dir);
1980
1981       link = _dbus_list_get_first_link (&files);
1982       while (link != NULL)
1983         {
1984           File *f = link->data;
1985
1986           print_untested_functions (f);
1987           
1988           link = _dbus_list_get_next_link (&files, link);
1989         }
1990     }
1991   
1992   return 0;
1993 }