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