1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* decode-gcov.c gcov decoder program
4 * Copyright (C) 2003 Red Hat Inc.
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.
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
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.
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.
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
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
41 #ifdef DBUS_HAVE_GCC33_GCOV
42 #error "gcov support not yet implemented for gcc 3.3 and greater; the file format changed"
45 #ifndef DBUS_HAVE_INT64
46 #error "gcov support can't be built without 64-bit integer support"
50 die (const char *message)
52 fprintf (stderr, "%s", message);
56 /* This bizarro function is from gcov-io.h in gcc source tree */
58 fetch_long (long *dest,
65 for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
66 if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
70 value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
72 if ((source[bytes - 1] & 128) && (value > 0))
80 fetch_long64 (dbus_int64_t *dest,
84 dbus_int64_t value = 0;
87 for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
88 if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
92 value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
94 if ((source[bytes - 1] & 128) && (value > 0))
101 #define BB_FILENAME (-1)
102 #define BB_FUNCTION (-2)
103 #define BB_ENDOFLIST 0
106 string_get_int (const DBusString *str,
112 if ((_dbus_string_get_length (str) - start) < 4)
115 p = _dbus_string_get_const_data (str);
119 fetch_long (val, p, 4);
125 string_get_int64 (const DBusString *str,
131 if ((_dbus_string_get_length (str) - start) < 8)
134 p = _dbus_string_get_const_data (str);
138 fetch_long64 (val, p, 8);
144 string_get_string (const DBusString *str,
154 while (string_get_int (str, i, &n))
161 _dbus_string_append_byte (val, n & 0xff);
162 _dbus_string_append_byte (val, (n >> 8) & 0xff);
163 _dbus_string_append_byte (val, (n >> 16) & 0xff);
164 _dbus_string_append_byte (val, (n >> 24) & 0xff);
173 dump_bb_file (const DBusString *contents)
181 while (string_get_int (contents, i, &val))
191 if (!_dbus_string_init (&f))
194 if (string_get_string (contents, i,
198 printf ("File %s\n", _dbus_string_get_const_data (&f));
200 _dbus_string_free (&f);
206 if (!_dbus_string_init (&f))
209 if (string_get_string (contents, i,
213 printf ("Function %s\n", _dbus_string_get_const_data (&f));
215 _dbus_string_free (&f);
221 printf ("End of block\n");
224 printf ("Line %ld\n", val);
229 printf ("%d functions in file\n", n_functions);
232 #define FLAG_ON_TREE 0x1
233 #define FLAG_FAKE 0x2
234 #define FLAG_FALL_THROUGH 0x4
237 dump_bbg_file (const DBusString *contents)
251 while (string_get_int (contents, i, &val))
253 long n_blocks_in_func;
257 n_blocks_in_func = val;
261 if (!string_get_int (contents, i, &n_arcs_in_func))
266 printf ("Function has %ld blocks and %ld arcs\n",
267 n_blocks_in_func, n_arcs_in_func);
270 n_blocks += n_blocks_in_func;
271 n_arcs += n_arcs_in_func;
274 while (j < n_blocks_in_func)
276 long n_arcs_in_block;
279 if (!string_get_int (contents, i, &n_arcs_in_block))
284 printf (" Block has %ld arcs\n", n_arcs_in_block);
287 while (k < n_arcs_in_block)
289 long destination_block;
292 if (!string_get_int (contents, i, &destination_block))
297 if (!string_get_int (contents, i, &flags))
302 printf (" Arc has destination block %ld flags 0x%lx\n",
303 destination_block, flags);
305 if ((flags & FLAG_ON_TREE) == 0)
306 n_arcs_off_tree += 1;
311 if (k < n_arcs_in_block)
317 if (j < n_blocks_in_func)
320 if (!string_get_int (contents, i, &val))
326 die ("-1 separator not found\n");
329 printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
330 n_functions, n_blocks, n_arcs, n_arcs_off_tree);
333 /* The da file contains first a count of arcs in the file,
334 * then a count of executions for all "off tree" arcs
338 dump_da_file (const DBusString *contents)
346 if (!string_get_int64 (contents, i, &val))
351 printf ("%ld arcs in file\n", (long) val);
352 claimed_n_arcs = val;
355 while (string_get_int64 (contents, i, &val))
359 printf ("%ld executions of arc %d\n",
365 if (n_arcs != claimed_n_arcs)
367 printf ("File claimed to have %d arcs but only had %d\n",
368 claimed_n_arcs, n_arcs);
372 typedef struct Arc Arc;
373 typedef struct Block Block;
374 typedef struct Function Function;
375 typedef struct File File;
376 typedef struct Line Line;
382 dbus_int64_t arc_count;
383 unsigned int count_valid : 1;
384 unsigned int on_tree : 1;
385 unsigned int fake : 1;
386 unsigned int fall_through : 1;
395 dbus_int64_t succ_count;
396 dbus_int64_t pred_count;
397 dbus_int64_t exec_count;
399 unsigned int count_valid : 1;
400 unsigned int on_tree : 1;
401 unsigned int inside_dbus_build_tests : 1;
409 /* number of blocks in DBUS_BUILD_TESTS */
411 int n_test_blocks_executed;
412 /* number of blocks outside DBUS_BUILD_TESTS */
413 int n_nontest_blocks;
414 int n_nontest_blocks_executed;
415 /* Summary result flags */
416 unsigned int unused : 1;
417 unsigned int inside_dbus_build_tests : 1;
418 unsigned int partial : 1; /* only some of the blocks were executed */
426 unsigned int inside_dbus_build_tests : 1;
427 unsigned int partial : 1; /* only some of the blocks were executed */
439 function_add_arc (Function *function,
446 arc = dbus_new0 (Arc, 1);
450 arc->target = target;
451 arc->source = source;
453 arc->succ_next = function->block_graph[source].succ;
454 function->block_graph[source].succ = arc;
455 function->block_graph[source].succ_count += 1;
457 arc->pred_next = function->block_graph[target].pred;
458 function->block_graph[target].pred = arc;
459 function->block_graph[target].pred_count += 1;
461 if ((flags & FLAG_ON_TREE) != 0)
464 if ((flags & FLAG_FAKE) != 0)
467 if ((flags & FLAG_FALL_THROUGH) != 0)
468 arc->fall_through = TRUE;
473 reverse_arcs (Arc *arc)
475 struct Arc *prev = 0;
478 for ( ; arc; arc = next)
480 next = arc->succ_next;
481 arc->succ_next = prev;
489 function_reverse_succ_arcs (Function *func)
491 /* Must reverse the order of all succ arcs, to ensure that they match
492 * the order of the data in the .da file.
496 for (i = 0; i < func->n_blocks; i++)
497 if (func->block_graph[i].succ)
498 func->block_graph[i].succ = reverse_arcs (func->block_graph[i].succ);
502 get_functions_from_bbg (const DBusString *contents,
503 DBusList **functions)
513 printf ("Loading arcs and blocks from .bbg file\n");
521 while (string_get_int (contents, i, &val))
524 long n_blocks_in_func;
528 n_blocks_in_func = val;
532 if (!string_get_int (contents, i, &n_arcs_in_func))
538 n_blocks += n_blocks_in_func;
539 n_arcs += n_arcs_in_func;
541 func = dbus_new0 (Function, 1);
545 func->block_graph = dbus_new0 (Block, n_blocks_in_func);
546 func->n_blocks = n_blocks_in_func;
549 while (j < n_blocks_in_func)
551 long n_arcs_in_block;
554 if (!string_get_int (contents, i, &n_arcs_in_block))
560 while (k < n_arcs_in_block)
562 long destination_block;
565 if (!string_get_int (contents, i, &destination_block))
570 if (!string_get_int (contents, i, &flags))
575 if ((flags & FLAG_ON_TREE) == 0)
576 n_arcs_off_tree += 1;
578 function_add_arc (func, j, destination_block,
584 if (k < n_arcs_in_block)
590 if (j < n_blocks_in_func)
593 function_reverse_succ_arcs (func);
595 if (!_dbus_list_append (functions, func))
598 if (!string_get_int (contents, i, &val))
604 die ("-1 separator not found\n");
608 printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
609 n_functions, n_blocks, n_arcs, n_arcs_off_tree);
612 _dbus_assert (n_functions == _dbus_list_get_length (functions));
616 add_counts_from_da (const DBusString *contents,
617 DBusList **functions)
624 Function *current_func;
629 printf ("Loading execution count for each arc from .da file\n");
633 if (!string_get_int64 (contents, i, &val))
638 claimed_n_arcs = val;
640 link = _dbus_list_get_first_link (functions);
644 current_func = link->data;
646 current_arc = current_func->block_graph[current_block].succ;
649 while (string_get_int64 (contents, i, &val))
653 while (current_arc == NULL ||
654 current_arc->on_tree)
656 if (current_arc == NULL)
660 if (current_block == current_func->n_blocks)
662 link = _dbus_list_get_next_link (functions, link);
665 fprintf (stderr, "Ran out of functions loading .da file\n");
668 current_func = link->data;
672 current_arc = current_func->block_graph[current_block].succ;
676 current_arc = current_arc->succ_next;
680 _dbus_assert (current_arc != NULL);
681 _dbus_assert (!current_arc->on_tree);
683 current_arc->arc_count = val;
684 current_arc->count_valid = TRUE;
685 current_func->block_graph[current_block].succ_count -= 1;
686 current_func->block_graph[current_arc->target].pred_count -= 1;
690 current_arc = current_arc->succ_next;
695 if (n_arcs != claimed_n_arcs)
697 fprintf (stderr, "File claimed to have %d arcs but only had %d\n",
698 claimed_n_arcs, n_arcs);
703 printf ("%d arcs in file\n", n_arcs);
708 function_solve_graph (Function *func)
718 printf ("Solving function graph\n");
721 n_blocks = func->n_blocks;
722 block_graph = func->block_graph;
724 /* For every block in the file,
725 - if every exit/entrance arc has a known count, then set the block count
726 - if the block count is known, and every exit/entrance arc but one has
727 a known execution count, then set the count of the remaining arc
729 As arc counts are set, decrement the succ/pred count, but don't delete
730 the arc, that way we can easily tell when all arcs are known, or only
731 one arc is unknown. */
733 /* The order that the basic blocks are iterated through is important.
734 Since the code that finds spanning trees starts with block 0, low numbered
735 arcs are put on the spanning tree in preference to high numbered arcs.
736 Hence, most instrumented arcs are at the end. Graph solving works much
737 faster if we propagate numbers from the end to the start.
739 This takes an average of slightly more than 3 passes. */
748 for (i = n_blocks - 1; i >= 0; i--)
750 if (! block_graph[i].count_valid)
752 if (block_graph[i].succ_count == 0)
755 for (arc = block_graph[i].succ; arc;
756 arc = arc->succ_next)
757 total += arc->arc_count;
758 block_graph[i].exec_count = total;
759 block_graph[i].count_valid = 1;
762 else if (block_graph[i].pred_count == 0)
765 for (arc = block_graph[i].pred; arc;
766 arc = arc->pred_next)
767 total += arc->arc_count;
768 block_graph[i].exec_count = total;
769 block_graph[i].count_valid = 1;
773 if (block_graph[i].count_valid)
775 if (block_graph[i].succ_count == 1)
778 /* One of the counts will be invalid, but it is zero,
779 so adding it in also doesn't hurt. */
780 for (arc = block_graph[i].succ; arc;
781 arc = arc->succ_next)
782 total += arc->arc_count;
783 /* Calculate count for remaining arc by conservation. */
784 total = block_graph[i].exec_count - total;
785 /* Search for the invalid arc, and set its count. */
786 for (arc = block_graph[i].succ; arc;
787 arc = arc->succ_next)
788 if (! arc->count_valid)
791 die ("arc == NULL\n");
792 arc->count_valid = 1;
793 arc->arc_count = total;
794 block_graph[i].succ_count -= 1;
796 block_graph[arc->target].pred_count -= 1;
799 if (block_graph[i].pred_count == 1)
802 /* One of the counts will be invalid, but it is zero,
803 so adding it in also doesn't hurt. */
804 for (arc = block_graph[i].pred; arc;
805 arc = arc->pred_next)
806 total += arc->arc_count;
807 /* Calculate count for remaining arc by conservation. */
808 total = block_graph[i].exec_count - total;
809 /* Search for the invalid arc, and set its count. */
810 for (arc = block_graph[i].pred; arc;
811 arc = arc->pred_next)
812 if (! arc->count_valid)
815 die ("arc == NULL\n");
816 arc->count_valid = 1;
817 arc->arc_count = total;
818 block_graph[i].pred_count -= 1;
820 block_graph[arc->source].succ_count -= 1;
827 /* If the graph has been correctly solved, every block will have a
828 * succ and pred count of zero.
830 for (i = 0; i < n_blocks; i++)
832 if (block_graph[i].succ_count || block_graph[i].pred_count)
834 fprintf (stderr, "WARNING: Block graph solved incorrectly\n");
835 fprintf (stderr, " block %d has succ_count = %d pred_count = %d\n",
836 i, (int) block_graph[i].succ_count, (int) block_graph[i].pred_count);
837 fprintf (stderr, " this error reflects a bug in decode-gcov.c\n");
843 solve_graphs (DBusList **functions)
847 link = _dbus_list_get_first_link (functions);
850 Function *func = link->data;
852 function_solve_graph (func);
854 link = _dbus_list_get_next_link (functions, link);
859 load_functions_for_c_file (const DBusString *filename,
860 DBusList **functions)
862 DBusString bbg_filename;
863 DBusString da_filename;
867 dbus_error_init (&error);
869 if (!_dbus_string_init (&bbg_filename) ||
870 !_dbus_string_init (&da_filename) ||
871 !_dbus_string_copy (filename, 0, &bbg_filename, 0) ||
872 !_dbus_string_copy (filename, 0, &da_filename, 0) ||
873 !_dbus_string_init (&contents))
876 _dbus_string_shorten (&bbg_filename, 2);
877 _dbus_string_shorten (&da_filename, 2);
879 if (!_dbus_string_append (&bbg_filename, ".bbg") ||
880 !_dbus_string_append (&da_filename, ".da"))
883 if (!_dbus_file_get_contents (&contents, &bbg_filename,
886 fprintf (stderr, "Could not open file: %s\n",
891 get_functions_from_bbg (&contents, functions);
893 _dbus_string_set_length (&contents, 0);
895 if (!_dbus_file_get_contents (&contents, &da_filename,
898 fprintf (stderr, "Could not open file: %s\n",
903 add_counts_from_da (&contents, functions);
905 solve_graphs (functions);
907 _dbus_string_free (&contents);
908 _dbus_string_free (&da_filename);
909 _dbus_string_free (&bbg_filename);
913 get_lines_from_bb_file (const DBusString *contents,
919 dbus_bool_t in_our_file;
925 printf ("Getting line numbers for blocks from .bb file\n");
928 /* There's this "filename" field in the .bb file which
929 * mysteriously comes *after* the first function in the
930 * file in the .bb file; and every .bb file seems to
931 * have only one filename. I don't understand
932 * what's going on here, so just set in_our_file = TRUE
933 * at the start categorically.
939 link = _dbus_list_get_first_link (&fl->functions);
942 while (string_get_int (contents, i, &val))
952 if (!_dbus_string_init (&f))
955 if (string_get_string (contents, i,
959 /* fl->name is a full path and the filename in .bb is
964 _dbus_string_init_const (&tmp_str, fl->name);
966 if (_dbus_string_ends_with_c_str (&tmp_str,
967 _dbus_string_get_const_data (&f)))
974 "File %s in .bb, looking for %s, in_our_file = %d\n",
975 _dbus_string_get_const_data (&f),
980 _dbus_string_free (&f);
986 if (!_dbus_string_init (&f))
989 if (string_get_string (contents, i,
994 fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f));
1003 fprintf (stderr, "No function object for function %s\n",
1004 _dbus_string_get_const_data (&f));
1009 link = _dbus_list_get_next_link (&fl->functions, link);
1011 if (func->name == NULL)
1013 if (!_dbus_string_copy_data (&f, &func->name))
1014 die ("no memory\n");
1018 die ("got two names for function?\n");
1023 _dbus_string_free (&f);
1033 fprintf (stderr, "Line %ld\n", val);
1036 if (val >= fl->n_lines)
1038 fprintf (stderr, "Line %ld but file only has %d lines\n",
1041 else if (func != NULL)
1043 val -= 1; /* To convert the 1-based line number to 0-based */
1044 _dbus_assert (val >= 0);
1046 if (block < func->n_blocks)
1048 if (!_dbus_list_append (&func->block_graph[block].lines,
1050 die ("no memory\n");
1053 if (!_dbus_list_append (&fl->lines[val].blocks,
1054 &func->block_graph[block]))
1055 die ("no memory\n");
1059 fprintf (stderr, "Line number for block %d but function only has %d blocks\n",
1060 block, func->n_blocks);
1065 fprintf (stderr, "Line %ld given outside of any function\n",
1074 printf ("%d functions in file\n", n_functions);
1080 load_block_line_associations (const DBusString *filename,
1083 DBusString bb_filename;
1084 DBusString contents;
1087 dbus_error_init (&error);
1089 if (!_dbus_string_init (&bb_filename) ||
1090 !_dbus_string_copy (filename, 0, &bb_filename, 0) ||
1091 !_dbus_string_init (&contents))
1092 die ("no memory\n");
1094 _dbus_string_shorten (&bb_filename, 2);
1096 if (!_dbus_string_append (&bb_filename, ".bb"))
1097 die ("no memory\n");
1099 if (!_dbus_file_get_contents (&contents, &bb_filename,
1102 fprintf (stderr, "Could not open file: %s\n",
1107 get_lines_from_bb_file (&contents, f);
1109 _dbus_string_free (&contents);
1110 _dbus_string_free (&bb_filename);
1114 count_lines_in_string (const DBusString *str)
1120 const char *last_line_end;
1123 printf ("Counting lines in source file\n");
1128 p = _dbus_string_get_const_data (str);
1129 end = p + _dbus_string_get_length (str);
1133 /* too lazy to handle \r\n as one linebreak */
1134 if (*p == '\n' || *p == '\r')
1137 last_line_end = p + 1;
1144 if (last_line_end != p)
1151 fill_line_content (const DBusString *str,
1158 const char *last_line_end;
1161 printf ("Saving contents of each line in source file\n");
1166 p = _dbus_string_get_const_data (str);
1167 end = p + _dbus_string_get_length (str);
1171 if (*p == '\n' || *p == '\r')
1173 lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1);
1174 if (lines[n_lines].text == NULL)
1175 die ("no memory\n");
1177 memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1178 lines[n_lines].number = n_lines + 1;
1182 last_line_end = p + 1;
1189 if (p != last_line_end)
1191 memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1197 mark_inside_dbus_build_tests (File *f)
1205 while (i < f->n_lines)
1207 Line *l = &f->lines[i];
1208 dbus_bool_t is_verbose;
1210 is_verbose = strstr (l->text, "_dbus_verbose") != NULL;
1212 if (inside_depth == 0)
1216 a = strstr (l->text, "#if");
1217 b = strstr (l->text, "DBUS_BUILD_TESTS");
1218 if (a && b && (a < b))
1223 if (strstr (l->text, "#if") != NULL)
1225 else if (strstr (l->text, "#endif") != NULL)
1229 if (inside_depth > 0 || is_verbose)
1231 /* Mark the line and its blocks */
1234 l->inside_dbus_build_tests = TRUE;
1236 blink = _dbus_list_get_first_link (&l->blocks);
1237 while (blink != NULL)
1239 Block *b = blink->data;
1241 b->inside_dbus_build_tests = TRUE;
1243 blink = _dbus_list_get_next_link (&l->blocks, blink);
1250 /* Now mark functions where for all blocks that are associated
1251 * with a source line, the block is inside_dbus_build_tests.
1253 link = _dbus_list_get_first_link (&f->functions);
1254 while (link != NULL)
1256 Function *func = link->data;
1258 /* The issue is that some blocks aren't associated with a source line.
1259 * Assume they are inside/outside tests according to the source
1260 * line of the preceding block. For the first block, make it
1261 * match the first following block with a line associated.
1263 if (func->block_graph[0].lines == NULL)
1265 /* find first following line */
1267 while (i < func->n_blocks)
1269 if (func->block_graph[i].lines != NULL)
1271 func->block_graph[0].inside_dbus_build_tests =
1272 func->block_graph[i].inside_dbus_build_tests;
1280 /* Now mark all blocks but the first */
1282 while (i < func->n_blocks)
1284 if (func->block_graph[i].lines == NULL)
1286 func->block_graph[i].inside_dbus_build_tests =
1287 func->block_graph[i-1].inside_dbus_build_tests;
1294 while (i < func->n_blocks)
1296 /* Break as soon as any block is not a test block */
1297 if (func->block_graph[i].lines != NULL &&
1298 !func->block_graph[i].inside_dbus_build_tests)
1304 if (i == func->n_blocks)
1305 func->inside_dbus_build_tests = TRUE;
1307 link = _dbus_list_get_next_link (&f->functions, link);
1312 mark_coverage (File *f)
1318 while (i < f->n_lines)
1320 Line *l = &f->lines[i];
1323 int n_blocks_executed;
1326 n_blocks_executed = 0;
1327 blink = _dbus_list_get_first_link (&l->blocks);
1328 while (blink != NULL)
1330 Block *b = blink->data;
1332 if (b->exec_count > 0)
1333 n_blocks_executed += 1;
1337 blink = _dbus_list_get_next_link (&l->blocks, blink);
1340 if (n_blocks_executed > 0 &&
1341 n_blocks_executed < n_blocks)
1347 link = _dbus_list_get_first_link (&f->functions);
1348 while (link != NULL)
1350 Function *func = link->data;
1353 int n_test_blocks_executed;
1354 int n_nontest_blocks;
1355 int n_nontest_blocks_executed;
1358 n_test_blocks_executed = 0;
1359 n_nontest_blocks = 0;
1360 n_nontest_blocks_executed = 0;
1363 while (i < func->n_blocks)
1365 if (!func->block_graph[i].inside_dbus_build_tests)
1367 n_nontest_blocks += 1;
1369 if (func->block_graph[i].exec_count > 0)
1370 n_nontest_blocks_executed += 1;
1376 if (func->block_graph[i].exec_count > 0)
1377 n_test_blocks_executed += 1;
1383 if (n_nontest_blocks_executed > 0 &&
1384 n_nontest_blocks_executed < n_nontest_blocks)
1385 func->partial = TRUE;
1387 if (n_nontest_blocks_executed == 0 &&
1388 n_nontest_blocks > 0)
1389 func->unused = TRUE;
1391 func->n_test_blocks = n_test_blocks;
1392 func->n_test_blocks_executed = n_test_blocks_executed;
1393 func->n_nontest_blocks = n_nontest_blocks;
1394 func->n_nontest_blocks_executed = n_nontest_blocks_executed;
1396 link = _dbus_list_get_next_link (&f->functions, link);
1401 load_c_file (const DBusString *filename)
1403 DBusString contents;
1407 f = dbus_new0 (File, 1);
1409 die ("no memory\n");
1411 if (!_dbus_string_copy_data (filename, &f->name))
1412 die ("no memory\n");
1414 if (!_dbus_string_init (&contents))
1415 die ("no memory\n");
1417 dbus_error_init (&error);
1419 if (!_dbus_file_get_contents (&contents, filename,
1422 fprintf (stderr, "Could not open file: %s\n",
1424 dbus_error_free (&error);
1428 load_functions_for_c_file (filename, &f->functions);
1430 f->n_lines = count_lines_in_string (&contents);
1431 f->lines = dbus_new0 (Line, f->n_lines);
1432 if (f->lines == NULL)
1433 die ("no memory\n");
1435 fill_line_content (&contents, f->lines);
1437 _dbus_string_free (&contents);
1439 load_block_line_associations (filename, f);
1441 mark_inside_dbus_build_tests (f);
1447 typedef struct Stats Stats;
1452 int n_blocks_executed;
1453 int n_blocks_inside_dbus_build_tests;
1455 int n_lines; /* lines that have blocks on them */
1456 int n_lines_executed;
1457 int n_lines_partial;
1458 int n_lines_inside_dbus_build_tests;
1461 int n_functions_executed;
1462 int n_functions_partial;
1463 int n_functions_inside_dbus_build_tests;
1467 line_was_executed (Line *l)
1471 link = _dbus_list_get_first_link (&l->blocks);
1472 while (link != NULL)
1474 Block *b = link->data;
1476 if (b->exec_count > 0)
1479 link = _dbus_list_get_next_link (&l->blocks, link);
1487 line_exec_count (Line *l)
1493 link = _dbus_list_get_first_link (&l->blocks);
1494 while (link != NULL)
1496 Block *b = link->data;
1498 total += b->exec_count;
1500 link = _dbus_list_get_next_link (&l->blocks, link);
1507 merge_stats_for_file (Stats *stats,
1513 for (i = 0; i < f->n_lines; ++i)
1515 Line *l = &f->lines[i];
1517 if (l->inside_dbus_build_tests)
1519 stats->n_lines_inside_dbus_build_tests += 1;
1523 if (line_was_executed (l))
1524 stats->n_lines_executed += 1;
1526 if (l->blocks != NULL)
1527 stats->n_lines += 1;
1530 stats->n_lines_partial += 1;
1533 link = _dbus_list_get_first_link (&f->functions);
1534 while (link != NULL)
1536 Function *func = link->data;
1538 if (func->inside_dbus_build_tests)
1539 stats->n_functions_inside_dbus_build_tests += 1;
1542 stats->n_functions += 1;
1545 stats->n_functions_executed += 1;
1548 stats->n_functions_partial += 1;
1551 stats->n_blocks_inside_dbus_build_tests +=
1552 func->n_test_blocks;
1554 stats->n_blocks_executed +=
1555 func->n_nontest_blocks_executed;
1558 func->n_nontest_blocks;
1560 link = _dbus_list_get_next_link (&f->functions, link);
1564 /* The output of this matches gcov exactly ("diff" shows no difference) */
1566 print_annotated_source_gcov_format (File *f)
1571 while (i < f->n_lines)
1573 Line *l = &f->lines[i];
1575 if (l->blocks != NULL)
1579 exec_count = line_exec_count (l);
1582 printf ("%12d %s\n",
1583 exec_count, l->text);
1585 printf (" ###### %s\n", l->text);
1589 printf ("\t\t%s\n", l->text);
1597 print_annotated_source (File *f)
1602 while (i < f->n_lines)
1604 Line *l = &f->lines[i];
1606 if (l->inside_dbus_build_tests)
1611 if (l->blocks != NULL)
1615 exec_count = line_exec_count (l);
1618 printf ("%12d %s\n",
1619 exec_count, l->text);
1621 printf (" ###### %s\n", l->text);
1625 printf ("\t\t%s\n", l->text);
1633 print_block_superdetails (File *f)
1638 link = _dbus_list_get_first_link (&f->functions);
1639 while (link != NULL)
1641 Function *func = link->data;
1643 printf ("=== %s():\n", func->name);
1646 while (i < func->n_blocks)
1648 Block *b = &func->block_graph[i];
1651 printf (" %5d executed %d times%s\n", i,
1652 (int) b->exec_count,
1653 b->inside_dbus_build_tests ?
1654 " [inside DBUS_BUILD_TESTS]" : "");
1656 l = _dbus_list_get_first_link (&b->lines);
1659 Line *line = l->data;
1661 printf ("4%d\t%s\n", line->number, line->text);
1663 l = _dbus_list_get_next_link (&b->lines, l);
1669 link = _dbus_list_get_next_link (&f->functions, link);
1674 print_one_file (const DBusString *filename)
1676 if (_dbus_string_ends_with_c_str (filename, ".bb"))
1678 DBusString contents;
1681 if (!_dbus_string_init (&contents))
1682 die ("no memory\n");
1684 dbus_error_init (&error);
1686 if (!_dbus_file_get_contents (&contents, filename,
1689 fprintf (stderr, "Could not open file: %s\n",
1691 dbus_error_free (&error);
1695 dump_bb_file (&contents);
1697 _dbus_string_free (&contents);
1699 else if (_dbus_string_ends_with_c_str (filename, ".bbg"))
1701 DBusString contents;
1704 if (!_dbus_string_init (&contents))
1705 die ("no memory\n");
1707 dbus_error_init (&error);
1709 if (!_dbus_file_get_contents (&contents, filename,
1712 fprintf (stderr, "Could not open file: %s\n",
1714 dbus_error_free (&error);
1718 dump_bbg_file (&contents);
1720 _dbus_string_free (&contents);
1722 else if (_dbus_string_ends_with_c_str (filename, ".da"))
1724 DBusString contents;
1727 if (!_dbus_string_init (&contents))
1728 die ("no memory\n");
1730 dbus_error_init (&error);
1732 if (!_dbus_file_get_contents (&contents, filename,
1735 fprintf (stderr, "Could not open file: %s\n",
1737 dbus_error_free (&error);
1741 dump_da_file (&contents);
1743 _dbus_string_free (&contents);
1745 else if (_dbus_string_ends_with_c_str (filename, ".c"))
1749 f = load_c_file (filename);
1751 print_annotated_source (f);
1755 fprintf (stderr, "Unknown file type %s\n",
1756 _dbus_string_get_const_data (filename));
1762 print_untested_functions (File *f)
1768 link = _dbus_list_get_first_link (&f->functions);
1769 while (link != NULL)
1771 Function *func = link->data;
1774 !func->inside_dbus_build_tests)
1777 link = _dbus_list_get_next_link (&f->functions, link);
1783 printf ("Untested functions in %s\n", f->name);
1784 printf ("=======\n");
1786 link = _dbus_list_get_first_link (&f->functions);
1787 while (link != NULL)
1789 Function *func = link->data;
1792 !func->inside_dbus_build_tests)
1793 printf (" %s\n", func->name);
1795 link = _dbus_list_get_next_link (&f->functions, link);
1802 print_poorly_tested_functions (File *f,
1808 #define TEST_FRACTION(function) ((function)->n_nontest_blocks_executed / (double) (function)->n_nontest_blocks)
1810 #define AVERAGE_COVERAGE ((stats)->n_blocks_executed / (double) (stats)->n_blocks)
1812 #define POORLY_TESTED(function) (!(function)->unused && \
1813 (function)->n_nontest_blocks > 0 && \
1814 TEST_FRACTION (function) < AVERAGE_COVERAGE)
1817 link = _dbus_list_get_first_link (&f->functions);
1818 while (link != NULL)
1820 Function *func = link->data;
1822 if (POORLY_TESTED (func))
1825 link = _dbus_list_get_next_link (&f->functions, link);
1831 printf ("Below average functions in %s\n", f->name);
1832 printf ("=======\n");
1834 link = _dbus_list_get_first_link (&f->functions);
1835 while (link != NULL)
1837 Function *func = link->data;
1839 if (POORLY_TESTED (func))
1840 printf (" %s (%d%%)\n", func->name,
1841 (int) (TEST_FRACTION (func) * 100));
1843 link = _dbus_list_get_next_link (&f->functions, link);
1850 func_cmp (const void *a,
1853 Function *af = *(Function**) a;
1854 Function *bf = *(Function**) b;
1855 int a_untested = af->n_nontest_blocks - af->n_nontest_blocks_executed;
1856 int b_untested = bf->n_nontest_blocks - bf->n_nontest_blocks_executed;
1858 /* Sort by number of untested blocks */
1859 return b_untested - a_untested;
1863 print_n_untested_blocks_by_function (File *f,
1872 link = _dbus_list_get_first_link (&f->functions);
1873 while (link != NULL)
1875 Function *func = link->data;
1877 if (func->n_nontest_blocks_executed <
1878 func->n_nontest_blocks)
1881 link = _dbus_list_get_next_link (&f->functions, link);
1887 /* make an array so we can use qsort */
1889 funcs = dbus_new (Function*, n_found);
1894 link = _dbus_list_get_first_link (&f->functions);
1895 while (link != NULL)
1897 Function *func = link->data;
1899 if (func->n_nontest_blocks_executed <
1900 func->n_nontest_blocks)
1906 link = _dbus_list_get_next_link (&f->functions, link);
1909 _dbus_assert (i == n_found);
1911 qsort (funcs, n_found, sizeof (Function*),
1914 printf ("Incomplete functions in %s\n", f->name);
1915 printf ("=======\n");
1920 Function *func = funcs[i];
1922 printf (" %s (%d/%d untested blocks)\n",
1924 func->n_nontest_blocks - func->n_nontest_blocks_executed,
1925 func->n_nontest_blocks);
1936 print_stats (Stats *stats,
1937 const char *of_what)
1941 printf ("Summary (%s)\n", of_what);
1942 printf ("=======\n");
1943 printf (" %g%% blocks executed (%d of %d)\n",
1944 (stats->n_blocks_executed / (double) stats->n_blocks) * 100.0,
1945 stats->n_blocks_executed,
1948 printf (" (ignored %d blocks of test-only/debug-only code)\n",
1949 stats->n_blocks_inside_dbus_build_tests);
1951 printf (" %g%% functions executed (%d of %d)\n",
1952 (stats->n_functions_executed / (double) stats->n_functions) * 100.0,
1953 stats->n_functions_executed,
1954 stats->n_functions);
1956 completely = stats->n_functions_executed - stats->n_functions_partial;
1957 printf (" %g%% functions completely executed (%d of %d)\n",
1958 (completely / (double) stats->n_functions) * 100.0,
1960 stats->n_functions);
1962 printf (" (ignored %d functions of test-only/debug-only code)\n",
1963 stats->n_functions_inside_dbus_build_tests);
1965 printf (" %g%% lines executed (%d of %d)\n",
1966 (stats->n_lines_executed / (double) stats->n_lines) * 100.0,
1967 stats->n_lines_executed,
1970 completely = stats->n_lines_executed - stats->n_lines_partial;
1971 printf (" %g%% lines completely executed (%d of %d)\n",
1972 (completely / (double) stats->n_lines) * 100.0,
1976 printf (" (ignored %d lines of test-only/debug-only code)\n",
1977 stats->n_lines_inside_dbus_build_tests);
1991 main (int argc, char **argv)
1993 DBusString filename;
1999 fprintf (stderr, "Must specify files on command line\n");
2006 if (strcmp (argv[i], "--report") == 0)
2011 else if (strcmp (argv[i], "--blocks") == 0)
2016 else if (strcmp (argv[i], "--gcov") == 0)
2025 fprintf (stderr, "Must specify files on command line\n");
2029 if (m == MODE_PRINT)
2033 _dbus_string_init_const (&filename, argv[i]);
2035 print_one_file (&filename);
2040 else if (m == MODE_BLOCKS || m == MODE_GCOV)
2046 _dbus_string_init_const (&filename, argv[i]);
2048 f = load_c_file (&filename);
2050 if (m == MODE_BLOCKS)
2051 print_block_superdetails (f);
2052 else if (m == MODE_GCOV)
2053 print_annotated_source_gcov_format (f);
2058 else if (m == MODE_REPORT)
2060 Stats stats = { 0, };
2063 DBusHashTable *stats_by_dir;
2069 _dbus_string_init_const (&filename, argv[i]);
2071 if (_dbus_string_ends_with_c_str (&filename, ".c"))
2075 f = load_c_file (&filename);
2077 if (!_dbus_list_append (&files, f))
2078 die ("no memory\n");
2082 fprintf (stderr, "Unknown file type %s\n",
2083 _dbus_string_get_const_data (&filename));
2090 link = _dbus_list_get_first_link (&files);
2091 while (link != NULL)
2093 File *f = link->data;
2095 merge_stats_for_file (&stats, f);
2097 link = _dbus_list_get_next_link (&files, link);
2100 print_stats (&stats, "all files");
2102 stats_by_dir = _dbus_hash_table_new (DBUS_HASH_STRING,
2103 dbus_free, dbus_free);
2105 link = _dbus_list_get_first_link (&files);
2106 while (link != NULL)
2108 File *f = link->data;
2113 _dbus_string_init_const (&filename, f->name);
2115 if (!_dbus_string_init (&dirname))
2116 die ("no memory\n");
2118 if (!_dbus_string_get_dirname (&filename, &dirname) ||
2119 !_dbus_string_copy_data (&dirname, &dirname_c))
2120 die ("no memory\n");
2122 dir_stats = _dbus_hash_table_lookup_string (stats_by_dir,
2125 if (dir_stats == NULL)
2127 dir_stats = dbus_new0 (Stats, 1);
2128 if (!_dbus_hash_table_insert_string (stats_by_dir, dirname_c,
2130 die ("no memory\n");
2133 dbus_free (dirname_c);
2135 merge_stats_for_file (dir_stats, f);
2137 link = _dbus_list_get_next_link (&files, link);
2140 _dbus_hash_iter_init (stats_by_dir, &iter);
2141 while (_dbus_hash_iter_next (&iter))
2143 const char *dirname = _dbus_hash_iter_get_string_key (&iter);
2144 Stats *dir_stats = _dbus_hash_iter_get_value (&iter);
2146 print_stats (dir_stats, dirname);
2149 _dbus_hash_table_unref (stats_by_dir);
2151 link = _dbus_list_get_first_link (&files);
2152 while (link != NULL)
2154 File *f = link->data;
2156 print_untested_functions (f);
2158 link = _dbus_list_get_next_link (&files, link);
2161 link = _dbus_list_get_first_link (&files);
2162 while (link != NULL)
2164 File *f = link->data;
2166 print_poorly_tested_functions (f, &stats);
2168 link = _dbus_list_get_next_link (&files, link);
2171 link = _dbus_list_get_first_link (&files);
2172 while (link != NULL)
2174 File *f = link->data;
2176 print_n_untested_blocks_by_function (f, &stats);
2178 link = _dbus_list_get_next_link (&files, link);