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