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