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