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