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