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 #ifndef DBUS_HAVE_INT64
42 #error "gcov support can't be built without 64-bit integer support"
46 die (const char *message)
48 fprintf (stderr, "%s", message);
52 /* This bizarro function is from gcov-io.h in gcc source tree */
54 fetch_long (long *dest,
61 for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
62 if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
66 value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
68 if ((source[bytes - 1] & 128) && (value > 0))
76 fetch_long64 (dbus_int64_t *dest,
80 dbus_int64_t value = 0;
83 for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--)
84 if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 ))
88 value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255));
90 if ((source[bytes - 1] & 128) && (value > 0))
97 #define BB_FILENAME (-1)
98 #define BB_FUNCTION (-2)
99 #define BB_ENDOFLIST 0
102 string_get_int (const DBusString *str,
108 if ((_dbus_string_get_length (str) - start) < 4)
111 p = _dbus_string_get_const_data (str);
115 fetch_long (val, p, 4);
121 string_get_int64 (const DBusString *str,
127 if ((_dbus_string_get_length (str) - start) < 8)
130 p = _dbus_string_get_const_data (str);
134 fetch_long64 (val, p, 8);
140 string_get_string (const DBusString *str,
150 while (string_get_int (str, i, &n))
162 _dbus_string_append_byte (val, b);
166 _dbus_string_append_byte (val, b);
167 b = (n >> 16) & 0xff;
170 _dbus_string_append_byte (val, b);
171 b = (n >> 24) & 0xff;
173 _dbus_string_append_byte (val, b);
184 #ifdef DBUS_HAVE_GCC33_GCOV
185 /* In gcc33 .bbg files, there's a function name of the form:
186 * -1, length, name (padded to 4), -1, checksum
189 string_get_function (const DBusString *str,
191 DBusString *funcname,
201 if (!string_get_int (str, i, &val))
202 die ("no room for -1 before function name\n");
207 die ("value before function name is not -1\n");
209 if (!string_get_int (str, i, &val))
210 die ("no length found for function name\n");
215 if (end > _dbus_string_get_length (str))
216 die ("Function name length points past end of file\n");
218 if (!_dbus_string_append (funcname,
219 _dbus_string_get_const_data (str) + i))
222 /* skip alignment padding the length doesn't include the nul so add 1
224 i = _DBUS_ALIGN_VALUE (end + 1, 4);
226 if (!string_get_int (str, i, &val) ||
228 die ("-1 at end of function name not found\n");
232 if (!string_get_int (str, i, &val))
233 die ("no checksum found at end of function name\n");
243 #endif /* DBUS_HAVE_GCC33_GCOV */
246 dump_bb_file (const DBusString *contents)
254 while (string_get_int (contents, i, &val))
264 if (!_dbus_string_init (&f))
267 if (string_get_string (contents, i,
271 printf ("File %s\n", _dbus_string_get_const_data (&f));
273 _dbus_string_free (&f);
279 if (!_dbus_string_init (&f))
282 if (string_get_string (contents, i,
286 printf ("Function %s\n", _dbus_string_get_const_data (&f));
288 _dbus_string_free (&f);
294 printf ("End of block\n");
297 printf ("Line %ld\n", val);
302 printf ("%d functions in file\n", n_functions);
305 #define FLAG_ON_TREE 0x1
306 #define FLAG_FAKE 0x2
307 #define FLAG_FALL_THROUGH 0x4
310 dump_bbg_file (const DBusString *contents)
324 while (i < _dbus_string_get_length (contents))
326 long n_blocks_in_func;
330 #ifdef DBUS_HAVE_GCC33_GCOV
331 /* In gcc33 .bbg files, there's a function name of the form:
332 * -1, length, name (padded to 4), -1, checksum
333 * after that header on each function description, it's
334 * the same as in gcc32
341 if (!_dbus_string_init (&funcname))
344 if (!string_get_function (contents, i,
345 &funcname, &checksum, &i))
346 die ("could not read function name\n");
348 printf ("Function name is \"%s\" checksum %d\n",
349 _dbus_string_get_const_data (&funcname),
352 _dbus_string_free (&funcname);
354 #endif /* DBUS_HAVE_GCC33_GCOV */
356 if (!string_get_int (contents, i, &val))
357 die ("no count of blocks in func found\n");
361 n_blocks_in_func = val;
363 if (!string_get_int (contents, i, &n_arcs_in_func))
368 printf ("Function has %ld blocks and %ld arcs\n",
369 n_blocks_in_func, n_arcs_in_func);
372 n_blocks += n_blocks_in_func;
373 n_arcs += n_arcs_in_func;
376 while (j < n_blocks_in_func)
378 long n_arcs_in_block;
381 if (!string_get_int (contents, i, &n_arcs_in_block))
386 printf (" Block has %ld arcs\n", n_arcs_in_block);
389 while (k < n_arcs_in_block)
391 long destination_block;
394 if (!string_get_int (contents, i, &destination_block))
399 if (!string_get_int (contents, i, &flags))
404 printf (" Arc has destination block %ld flags 0x%lx\n",
405 destination_block, flags);
407 if ((flags & FLAG_ON_TREE) == 0)
408 n_arcs_off_tree += 1;
413 if (k < n_arcs_in_block)
419 if (j < n_blocks_in_func)
422 if (!string_get_int (contents, i, &val))
428 die ("-1 separator not found\n");
431 printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
432 n_functions, n_blocks, n_arcs, n_arcs_off_tree);
435 #ifndef DBUS_HAVE_GCC33_GCOV
438 * The da file contains first a count of arcs in the file,
439 * then a count of executions for all "off tree" arcs
443 dump_da_file (const DBusString *contents)
451 if (!string_get_int64 (contents, i, &val))
456 printf ("%ld arcs in file\n", (long) val);
457 claimed_n_arcs = val;
460 while (string_get_int64 (contents, i, &val))
464 printf ("%ld executions of arc %d\n",
470 if (n_arcs != claimed_n_arcs)
472 printf ("File claimed to have %d arcs but only had %d\n",
473 claimed_n_arcs, n_arcs);
477 #else /* DBUS_HAVE_GCC33_GCOV */
480 * The da file is more complex than 3.2.
482 * We have a magic value of "-123" only it isn't really
483 * -123, it's -123 as encoded by the crackass gcov-io.h
484 * routines. Anyway, 4 bytes.
488 * - 4 byte count of how many functions in the following list
489 * - 4 byte length of random extra data
490 * - the random extra data, just skip it, info pages have some
491 * details on what might be in there or see __bb_exit_func in gcc
492 * - then for each function (number of functions given above):
493 * . -1, length, funcname, alignment padding, -1
495 * . 4 byte number of arcs in function
496 * . 8 bytes each, a count of execution for each arc
498 * Now, the whole thing *starting with the magic* can repeat.
499 * This is caused by multiple runs of the profiled app appending
503 dump_da_file (const DBusString *contents)
515 while (i < _dbus_string_get_length (contents))
517 int claimed_n_functions;
521 printf (".da file section %d\n", n_sections);
523 if (!string_get_int (contents, i, &val))
524 die ("no magic found in .da file\n");
529 die ("wrong file magic in .da file\n");
531 if (!string_get_int (contents, i, &val))
532 die ("no function count in .da file\n");
534 claimed_n_functions = val;
536 printf ("%d functions expected in section %d of .da file\n",
537 claimed_n_functions, n_sections);
539 if (!string_get_int (contents, i, &val))
540 die ("no extra data length in .da file\n");
548 while (n_functions < claimed_n_functions)
555 if (!_dbus_string_init (&funcname))
558 if (!string_get_function (contents, i,
559 &funcname, &checksum, &i))
560 die ("could not read function name\n");
562 if (!string_get_int (contents, i, &val))
563 die ("no arc count for function\n");
566 claimed_n_arcs = val;
568 printf (" %d arcs in function %d %s checksum %d\n",
569 claimed_n_arcs, n_functions,
570 _dbus_string_get_const_data (&funcname),
574 while (n_arcs < claimed_n_arcs)
576 if (!string_get_int64 (contents, i, &v64))
577 die ("did not get execution count for arc\n");
581 printf (" %ld executions of arc %d (total arcs %d)\n",
582 (long) v64, n_arcs, total_arcs + n_arcs);
587 _dbus_string_free (&funcname);
589 total_arcs += n_arcs;
593 printf ("total of %d functions and %d arcs in section %d\n",
594 n_functions, total_arcs, n_sections);
596 total_functions += n_functions;
600 printf ("%d total function sections in %d total .da file sections\n",
601 total_functions, n_sections);
604 #endif /* DBUS_HAVE_GCC33_GCOV */
606 typedef struct Arc Arc;
607 typedef struct Block Block;
608 typedef struct Function Function;
609 typedef struct File File;
610 typedef struct Line Line;
616 dbus_int64_t arc_count;
617 unsigned int count_valid : 1;
618 unsigned int on_tree : 1;
619 unsigned int fake : 1;
620 unsigned int fall_through : 1;
629 dbus_int64_t succ_count;
630 dbus_int64_t pred_count;
631 dbus_int64_t exec_count;
633 unsigned int count_valid : 1;
634 unsigned int on_tree : 1;
635 unsigned int inside_dbus_build_tests : 1;
644 /* number of blocks in DBUS_BUILD_TESTS */
646 int n_test_blocks_executed;
647 /* number of blocks outside DBUS_BUILD_TESTS */
648 int n_nontest_blocks;
649 int n_nontest_blocks_executed;
650 /* Summary result flags */
651 unsigned int unused : 1;
652 unsigned int inside_dbus_build_tests : 1;
653 unsigned int partial : 1; /* only some of the blocks were executed */
661 unsigned int inside_dbus_build_tests : 1;
662 unsigned int partial : 1; /* only some of the blocks were executed */
674 function_add_arc (Function *function,
681 arc = dbus_new0 (Arc, 1);
685 arc->target = target;
686 arc->source = source;
688 arc->succ_next = function->block_graph[source].succ;
689 function->block_graph[source].succ = arc;
690 function->block_graph[source].succ_count += 1;
692 arc->pred_next = function->block_graph[target].pred;
693 function->block_graph[target].pred = arc;
694 function->block_graph[target].pred_count += 1;
696 if ((flags & FLAG_ON_TREE) != 0)
699 if ((flags & FLAG_FAKE) != 0)
702 if ((flags & FLAG_FALL_THROUGH) != 0)
703 arc->fall_through = TRUE;
708 reverse_arcs (Arc *arc)
710 struct Arc *prev = 0;
713 for ( ; arc; arc = next)
715 next = arc->succ_next;
716 arc->succ_next = prev;
724 function_reverse_succ_arcs (Function *func)
726 /* Must reverse the order of all succ arcs, to ensure that they match
727 * the order of the data in the .da file.
731 for (i = 0; i < func->n_blocks; i++)
732 if (func->block_graph[i].succ)
733 func->block_graph[i].succ = reverse_arcs (func->block_graph[i].succ);
737 get_functions_from_bbg (const DBusString *contents,
738 DBusList **functions)
748 printf ("Loading arcs and blocks from .bbg file\n");
756 while (i < _dbus_string_get_length (contents))
759 long n_blocks_in_func;
763 #ifdef DBUS_HAVE_GCC33_GCOV
767 /* In gcc33 .bbg files, there's a function name of the form:
768 * -1, length, name (padded to 4), -1, checksum
769 * after that header on each function description, it's
770 * the same as in gcc32
772 if (!_dbus_string_init (&funcname))
775 if (!string_get_function (contents, i,
776 &funcname, &checksum, &i))
777 die ("could not read function name\n");
778 #endif /* DBUS_HAVE_GCC33_GCOV */
780 if (!string_get_int (contents, i, &val))
783 n_blocks_in_func = val;
787 if (!string_get_int (contents, i, &n_arcs_in_func))
793 n_blocks += n_blocks_in_func;
794 n_arcs += n_arcs_in_func;
796 func = dbus_new0 (Function, 1);
800 #ifdef DBUS_HAVE_GCC33_GCOV
801 func->name = _dbus_strdup (_dbus_string_get_const_data (&funcname));
802 func->checksum = checksum;
803 _dbus_string_free (&funcname);
806 func->block_graph = dbus_new0 (Block, n_blocks_in_func);
807 func->n_blocks = n_blocks_in_func;
810 while (j < n_blocks_in_func)
812 long n_arcs_in_block;
815 if (!string_get_int (contents, i, &n_arcs_in_block))
821 while (k < n_arcs_in_block)
823 long destination_block;
826 if (!string_get_int (contents, i, &destination_block))
831 if (!string_get_int (contents, i, &flags))
836 if ((flags & FLAG_ON_TREE) == 0)
837 n_arcs_off_tree += 1;
839 function_add_arc (func, j, destination_block,
845 if (k < n_arcs_in_block)
851 if (j < n_blocks_in_func)
854 function_reverse_succ_arcs (func);
856 if (!_dbus_list_append (functions, func))
859 if (!string_get_int (contents, i, &val))
865 die ("-1 separator not found in .bbg file\n");
869 printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n",
870 n_functions, n_blocks, n_arcs, n_arcs_off_tree);
873 _dbus_assert (n_functions == _dbus_list_get_length (functions));
876 #ifdef DBUS_HAVE_GCC33_GCOV
878 add_counts_from_da (const DBusString *contents,
879 DBusList **functions)
886 Function *current_func;
893 while (i < _dbus_string_get_length (contents))
895 int claimed_n_functions;
898 if (!string_get_int (contents, i, &val))
899 die ("no magic found in .da file\n");
904 die ("wrong file magic in .da file\n");
906 if (!string_get_int (contents, i, &val))
907 die ("no function count in .da file\n");
909 claimed_n_functions = val;
911 if (!string_get_int (contents, i, &val))
912 die ("no extra data length in .da file\n");
918 link = _dbus_list_get_first_link (functions);
920 goto no_more_functions;
923 while (n_functions < claimed_n_functions && link != NULL)
930 current_func = link->data;
932 current_arc = current_func->block_graph[current_block].succ;
934 if (!_dbus_string_init (&funcname))
937 if (!string_get_function (contents, i,
938 &funcname, &checksum, &i))
939 die ("could not read function name\n");
941 if (!_dbus_string_equal_c_str (&funcname, current_func->name))
943 fprintf (stderr, "Expecting .da info for %s but got %s\n",
945 _dbus_string_get_const_data (&funcname));
949 if (checksum != current_func->checksum)
950 die (".da file checksum doesn't match checksum from .bbg file\n");
952 if (!string_get_int (contents, i, &val))
953 die ("no arc count for function\n");
956 claimed_n_arcs = val;
958 /* For each arc in the profile, find the corresponding
959 * arc in the function and increment its count
962 while (n_arcs < claimed_n_arcs)
964 if (!string_get_int64 (contents, i, &v64))
965 die ("did not get execution count for arc\n");
969 /* Find the next arc in the function that isn't on tree */
970 while (current_arc == NULL ||
971 current_arc->on_tree)
973 if (current_arc == NULL)
977 if (current_block >= current_func->n_blocks)
978 die ("too many blocks in function\n");
980 current_arc = current_func->block_graph[current_block].succ;
984 current_arc = current_arc->succ_next;
988 _dbus_assert (current_arc != NULL);
989 _dbus_assert (!current_arc->on_tree);
991 current_arc->arc_count = v64;
992 current_arc->count_valid = TRUE;
993 current_func->block_graph[current_block].succ_count -= 1;
994 current_func->block_graph[current_arc->target].pred_count -= 1;
998 current_arc = current_arc->succ_next;
1001 _dbus_string_free (&funcname);
1003 link = _dbus_list_get_next_link (functions, link);
1006 if (link == NULL && n_functions < claimed_n_functions)
1008 fprintf (stderr, "Ran out of functions loading .da file\n");
1009 goto no_more_functions;
1018 #else /* DBUS_HAVE_GCC33_GCOV */
1020 add_counts_from_da (const DBusString *contents,
1021 DBusList **functions)
1028 Function *current_func;
1033 printf ("Loading execution count for each arc from .da file\n");
1037 if (!string_get_int64 (contents, i, &val))
1042 claimed_n_arcs = val;
1044 link = _dbus_list_get_first_link (functions);
1048 current_func = link->data;
1050 current_arc = current_func->block_graph[current_block].succ;
1053 while (string_get_int64 (contents, i, &val))
1057 while (current_arc == NULL ||
1058 current_arc->on_tree)
1060 if (current_arc == NULL)
1064 if (current_block == current_func->n_blocks)
1066 link = _dbus_list_get_next_link (functions, link);
1069 fprintf (stderr, "Ran out of functions loading .da file\n");
1072 current_func = link->data;
1076 current_arc = current_func->block_graph[current_block].succ;
1080 current_arc = current_arc->succ_next;
1084 _dbus_assert (current_arc != NULL);
1085 _dbus_assert (!current_arc->on_tree);
1087 current_arc->arc_count = val;
1088 current_arc->count_valid = TRUE;
1089 current_func->block_graph[current_block].succ_count -= 1;
1090 current_func->block_graph[current_arc->target].pred_count -= 1;
1094 current_arc = current_arc->succ_next;
1099 if (n_arcs != claimed_n_arcs)
1101 fprintf (stderr, "File claimed to have %d arcs but only had %d\n",
1102 claimed_n_arcs, n_arcs);
1107 printf ("%d arcs in file\n", n_arcs);
1113 function_solve_graph (Function *func)
1115 int passes, changes;
1123 printf ("Solving function graph\n");
1126 n_blocks = func->n_blocks;
1127 block_graph = func->block_graph;
1129 /* For every block in the file,
1130 - if every exit/entrance arc has a known count, then set the block count
1131 - if the block count is known, and every exit/entrance arc but one has
1132 a known execution count, then set the count of the remaining arc
1134 As arc counts are set, decrement the succ/pred count, but don't delete
1135 the arc, that way we can easily tell when all arcs are known, or only
1136 one arc is unknown. */
1138 /* The order that the basic blocks are iterated through is important.
1139 Since the code that finds spanning trees starts with block 0, low numbered
1140 arcs are put on the spanning tree in preference to high numbered arcs.
1141 Hence, most instrumented arcs are at the end. Graph solving works much
1142 faster if we propagate numbers from the end to the start.
1144 This takes an average of slightly more than 3 passes. */
1153 for (i = n_blocks - 1; i >= 0; i--)
1155 if (! block_graph[i].count_valid)
1157 if (block_graph[i].succ_count == 0)
1160 for (arc = block_graph[i].succ; arc;
1161 arc = arc->succ_next)
1162 total += arc->arc_count;
1163 block_graph[i].exec_count = total;
1164 block_graph[i].count_valid = 1;
1167 else if (block_graph[i].pred_count == 0)
1170 for (arc = block_graph[i].pred; arc;
1171 arc = arc->pred_next)
1172 total += arc->arc_count;
1173 block_graph[i].exec_count = total;
1174 block_graph[i].count_valid = 1;
1178 if (block_graph[i].count_valid)
1180 if (block_graph[i].succ_count == 1)
1183 /* One of the counts will be invalid, but it is zero,
1184 so adding it in also doesn't hurt. */
1185 for (arc = block_graph[i].succ; arc;
1186 arc = arc->succ_next)
1187 total += arc->arc_count;
1188 /* Calculate count for remaining arc by conservation. */
1189 total = block_graph[i].exec_count - total;
1190 /* Search for the invalid arc, and set its count. */
1191 for (arc = block_graph[i].succ; arc;
1192 arc = arc->succ_next)
1193 if (! arc->count_valid)
1196 die ("arc == NULL\n");
1197 arc->count_valid = 1;
1198 arc->arc_count = total;
1199 block_graph[i].succ_count -= 1;
1201 block_graph[arc->target].pred_count -= 1;
1204 if (block_graph[i].pred_count == 1)
1207 /* One of the counts will be invalid, but it is zero,
1208 so adding it in also doesn't hurt. */
1209 for (arc = block_graph[i].pred; arc;
1210 arc = arc->pred_next)
1211 total += arc->arc_count;
1212 /* Calculate count for remaining arc by conservation. */
1213 total = block_graph[i].exec_count - total;
1214 /* Search for the invalid arc, and set its count. */
1215 for (arc = block_graph[i].pred; arc;
1216 arc = arc->pred_next)
1217 if (! arc->count_valid)
1220 die ("arc == NULL\n");
1221 arc->count_valid = 1;
1222 arc->arc_count = total;
1223 block_graph[i].pred_count -= 1;
1225 block_graph[arc->source].succ_count -= 1;
1232 /* If the graph has been correctly solved, every block will have a
1233 * succ and pred count of zero.
1236 dbus_bool_t header = FALSE;
1237 for (i = 0; i < n_blocks; i++)
1239 if (block_graph[i].succ_count || block_graph[i].pred_count)
1243 fprintf (stderr, "WARNING: Block graph solved incorrectly for function %s\n",
1245 fprintf (stderr, " this error reflects a bug in decode-gcov.c or perhaps bogus data\n");
1248 fprintf (stderr, " block %d has succ_count = %d pred_count = %d\n",
1249 i, (int) block_graph[i].succ_count, (int) block_graph[i].pred_count);
1256 solve_graphs (DBusList **functions)
1260 link = _dbus_list_get_first_link (functions);
1261 while (link != NULL)
1263 Function *func = link->data;
1265 function_solve_graph (func);
1267 link = _dbus_list_get_next_link (functions, link);
1272 load_functions_for_c_file (const DBusString *filename,
1273 DBusList **functions)
1275 DBusString bbg_filename;
1276 DBusString da_filename;
1277 DBusString contents;
1280 dbus_error_init (&error);
1282 if (!_dbus_string_init (&bbg_filename) ||
1283 !_dbus_string_init (&da_filename) ||
1284 !_dbus_string_copy (filename, 0, &bbg_filename, 0) ||
1285 !_dbus_string_copy (filename, 0, &da_filename, 0) ||
1286 !_dbus_string_init (&contents))
1287 die ("no memory\n");
1289 _dbus_string_shorten (&bbg_filename, 2);
1290 _dbus_string_shorten (&da_filename, 2);
1292 if (!_dbus_string_append (&bbg_filename, ".bbg") ||
1293 !_dbus_string_append (&da_filename, ".da"))
1294 die ("no memory\n");
1296 if (!_dbus_file_get_contents (&contents, &bbg_filename,
1299 fprintf (stderr, "Could not open file: %s\n",
1304 get_functions_from_bbg (&contents, functions);
1306 _dbus_string_set_length (&contents, 0);
1308 if (!_dbus_file_get_contents (&contents, &da_filename,
1311 /* Try .libs/file.da */
1314 if (_dbus_string_find_byte_backward (&da_filename,
1315 _dbus_string_get_length (&da_filename),
1320 _dbus_string_init_const (&libs, "/.libs");
1322 if (!_dbus_string_copy (&libs, 0, &da_filename, slash))
1325 dbus_error_free (&error);
1326 if (!_dbus_file_get_contents (&contents, &da_filename,
1329 fprintf (stderr, "Could not open file: %s\n",
1336 fprintf (stderr, "Could not open file: %s\n",
1342 add_counts_from_da (&contents, functions);
1344 solve_graphs (functions);
1346 _dbus_string_free (&contents);
1347 _dbus_string_free (&da_filename);
1348 _dbus_string_free (&bbg_filename);
1352 get_lines_from_bb_file (const DBusString *contents,
1358 dbus_bool_t in_our_file;
1364 printf ("Getting line numbers for blocks from .bb file\n");
1367 /* There's this "filename" field in the .bb file which
1368 * mysteriously comes *after* the first function in the
1369 * file in the .bb file; and every .bb file seems to
1370 * have only one filename. I don't understand
1371 * what's going on here, so just set in_our_file = TRUE
1372 * at the start categorically.
1378 link = _dbus_list_get_first_link (&fl->functions);
1381 while (string_get_int (contents, i, &val))
1391 if (!_dbus_string_init (&f))
1392 die ("no memory\n");
1394 if (string_get_string (contents, i,
1398 /* fl->name is a full path and the filename in .bb is
1403 _dbus_string_init_const (&tmp_str, fl->name);
1405 if (_dbus_string_ends_with_c_str (&tmp_str,
1406 _dbus_string_get_const_data (&f)))
1409 in_our_file = FALSE;
1413 "File %s in .bb, looking for %s, in_our_file = %d\n",
1414 _dbus_string_get_const_data (&f),
1419 _dbus_string_free (&f);
1425 if (!_dbus_string_init (&f))
1426 die ("no memory\n");
1428 if (string_get_string (contents, i,
1433 fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f));
1442 fprintf (stderr, "No function object for function %s\n",
1443 _dbus_string_get_const_data (&f));
1448 link = _dbus_list_get_next_link (&fl->functions, link);
1450 if (func->name == NULL)
1452 if (!_dbus_string_copy_data (&f, &func->name))
1453 die ("no memory\n");
1457 if (!_dbus_string_equal_c_str (&f, func->name))
1459 fprintf (stderr, "got function name \"%s\" (%d) from .bbg file, but \"%s\" (%d) from .bb file\n",
1460 func->name, strlen (func->name),
1461 _dbus_string_get_const_data (&f),
1462 _dbus_string_get_length (&f));
1469 _dbus_string_free (&f);
1479 fprintf (stderr, "Line %ld\n", val);
1482 if (val >= fl->n_lines)
1484 fprintf (stderr, "Line %ld but file only has %d lines\n",
1487 else if (func != NULL)
1489 val -= 1; /* To convert the 1-based line number to 0-based */
1490 _dbus_assert (val >= 0);
1492 if (block < func->n_blocks)
1494 if (!_dbus_list_append (&func->block_graph[block].lines,
1496 die ("no memory\n");
1499 if (!_dbus_list_append (&fl->lines[val].blocks,
1500 &func->block_graph[block]))
1501 die ("no memory\n");
1505 fprintf (stderr, "Line number for block %d but function only has %d blocks\n",
1506 block, func->n_blocks);
1511 fprintf (stderr, "Line %ld given outside of any function\n",
1520 printf ("%d functions in file\n", n_functions);
1526 load_block_line_associations (const DBusString *filename,
1529 DBusString bb_filename;
1530 DBusString contents;
1533 dbus_error_init (&error);
1535 if (!_dbus_string_init (&bb_filename) ||
1536 !_dbus_string_copy (filename, 0, &bb_filename, 0) ||
1537 !_dbus_string_init (&contents))
1538 die ("no memory\n");
1540 _dbus_string_shorten (&bb_filename, 2);
1542 if (!_dbus_string_append (&bb_filename, ".bb"))
1543 die ("no memory\n");
1545 if (!_dbus_file_get_contents (&contents, &bb_filename,
1548 fprintf (stderr, "Could not open file: %s\n",
1553 get_lines_from_bb_file (&contents, f);
1555 _dbus_string_free (&contents);
1556 _dbus_string_free (&bb_filename);
1560 count_lines_in_string (const DBusString *str)
1566 const char *last_line_end;
1569 printf ("Counting lines in source file\n");
1574 p = _dbus_string_get_const_data (str);
1575 end = p + _dbus_string_get_length (str);
1579 /* too lazy to handle \r\n as one linebreak */
1580 if (*p == '\n' || *p == '\r')
1583 last_line_end = p + 1;
1590 if (last_line_end != p)
1597 fill_line_content (const DBusString *str,
1604 const char *last_line_end;
1607 printf ("Saving contents of each line in source file\n");
1612 p = _dbus_string_get_const_data (str);
1613 end = p + _dbus_string_get_length (str);
1617 if (*p == '\n' || *p == '\r')
1619 lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1);
1620 if (lines[n_lines].text == NULL)
1621 die ("no memory\n");
1623 memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1624 lines[n_lines].number = n_lines + 1;
1628 last_line_end = p + 1;
1635 if (p != last_line_end)
1637 memcpy (lines[n_lines].text, last_line_end, p - last_line_end);
1643 mark_inside_dbus_build_tests (File *f)
1651 while (i < f->n_lines)
1653 Line *l = &f->lines[i];
1654 dbus_bool_t is_verbose;
1656 is_verbose = strstr (l->text, "_dbus_verbose") != NULL;
1658 if (inside_depth == 0)
1662 a = strstr (l->text, "#if");
1663 b = strstr (l->text, "DBUS_BUILD_TESTS");
1664 if (a && b && (a < b))
1669 if (strstr (l->text, "#if") != NULL)
1671 else if (strstr (l->text, "#endif") != NULL)
1675 if (inside_depth > 0 || is_verbose)
1677 /* Mark the line and its blocks */
1680 l->inside_dbus_build_tests = TRUE;
1682 blink = _dbus_list_get_first_link (&l->blocks);
1683 while (blink != NULL)
1685 Block *b = blink->data;
1687 b->inside_dbus_build_tests = TRUE;
1689 blink = _dbus_list_get_next_link (&l->blocks, blink);
1696 /* Now mark functions where for all blocks that are associated
1697 * with a source line, the block is inside_dbus_build_tests.
1699 link = _dbus_list_get_first_link (&f->functions);
1700 while (link != NULL)
1702 Function *func = link->data;
1704 /* The issue is that some blocks aren't associated with a source line.
1705 * Assume they are inside/outside tests according to the source
1706 * line of the preceding block. For the first block, make it
1707 * match the first following block with a line associated.
1709 if (func->block_graph[0].lines == NULL)
1711 /* find first following line */
1713 while (i < func->n_blocks)
1715 if (func->block_graph[i].lines != NULL)
1717 func->block_graph[0].inside_dbus_build_tests =
1718 func->block_graph[i].inside_dbus_build_tests;
1726 /* Now mark all blocks but the first */
1728 while (i < func->n_blocks)
1730 if (func->block_graph[i].lines == NULL)
1732 func->block_graph[i].inside_dbus_build_tests =
1733 func->block_graph[i-1].inside_dbus_build_tests;
1740 while (i < func->n_blocks)
1742 /* Break as soon as any block is not a test block */
1743 if (func->block_graph[i].lines != NULL &&
1744 !func->block_graph[i].inside_dbus_build_tests)
1750 if (i == func->n_blocks)
1751 func->inside_dbus_build_tests = TRUE;
1753 link = _dbus_list_get_next_link (&f->functions, link);
1758 mark_coverage (File *f)
1764 while (i < f->n_lines)
1766 Line *l = &f->lines[i];
1769 int n_blocks_executed;
1772 n_blocks_executed = 0;
1773 blink = _dbus_list_get_first_link (&l->blocks);
1774 while (blink != NULL)
1776 Block *b = blink->data;
1778 if (b->exec_count > 0)
1779 n_blocks_executed += 1;
1783 blink = _dbus_list_get_next_link (&l->blocks, blink);
1786 if (n_blocks_executed > 0 &&
1787 n_blocks_executed < n_blocks)
1793 link = _dbus_list_get_first_link (&f->functions);
1794 while (link != NULL)
1796 Function *func = link->data;
1799 int n_test_blocks_executed;
1800 int n_nontest_blocks;
1801 int n_nontest_blocks_executed;
1804 n_test_blocks_executed = 0;
1805 n_nontest_blocks = 0;
1806 n_nontest_blocks_executed = 0;
1809 while (i < func->n_blocks)
1811 if (!func->block_graph[i].inside_dbus_build_tests)
1813 n_nontest_blocks += 1;
1815 if (func->block_graph[i].exec_count > 0)
1816 n_nontest_blocks_executed += 1;
1822 if (func->block_graph[i].exec_count > 0)
1823 n_test_blocks_executed += 1;
1829 if (n_nontest_blocks_executed > 0 &&
1830 n_nontest_blocks_executed < n_nontest_blocks)
1831 func->partial = TRUE;
1833 if (n_nontest_blocks_executed == 0 &&
1834 n_nontest_blocks > 0)
1835 func->unused = TRUE;
1837 func->n_test_blocks = n_test_blocks;
1838 func->n_test_blocks_executed = n_test_blocks_executed;
1839 func->n_nontest_blocks = n_nontest_blocks;
1840 func->n_nontest_blocks_executed = n_nontest_blocks_executed;
1842 link = _dbus_list_get_next_link (&f->functions, link);
1847 load_c_file (const DBusString *filename)
1849 DBusString contents;
1853 f = dbus_new0 (File, 1);
1855 die ("no memory\n");
1857 if (!_dbus_string_copy_data (filename, &f->name))
1858 die ("no memory\n");
1860 if (!_dbus_string_init (&contents))
1861 die ("no memory\n");
1863 dbus_error_init (&error);
1865 if (!_dbus_file_get_contents (&contents, filename,
1868 fprintf (stderr, "Could not open file: %s\n",
1870 dbus_error_free (&error);
1874 load_functions_for_c_file (filename, &f->functions);
1876 f->n_lines = count_lines_in_string (&contents);
1877 f->lines = dbus_new0 (Line, f->n_lines);
1878 if (f->lines == NULL)
1879 die ("no memory\n");
1881 fill_line_content (&contents, f->lines);
1883 _dbus_string_free (&contents);
1885 load_block_line_associations (filename, f);
1887 mark_inside_dbus_build_tests (f);
1893 typedef struct Stats Stats;
1898 int n_blocks_executed;
1899 int n_blocks_inside_dbus_build_tests;
1901 int n_lines; /* lines that have blocks on them */
1902 int n_lines_executed;
1903 int n_lines_partial;
1904 int n_lines_inside_dbus_build_tests;
1907 int n_functions_executed;
1908 int n_functions_partial;
1909 int n_functions_inside_dbus_build_tests;
1913 line_was_executed (Line *l)
1917 link = _dbus_list_get_first_link (&l->blocks);
1918 while (link != NULL)
1920 Block *b = link->data;
1922 if (b->exec_count > 0)
1925 link = _dbus_list_get_next_link (&l->blocks, link);
1933 line_exec_count (Line *l)
1939 link = _dbus_list_get_first_link (&l->blocks);
1940 while (link != NULL)
1942 Block *b = link->data;
1944 total += b->exec_count;
1946 link = _dbus_list_get_next_link (&l->blocks, link);
1953 merge_stats_for_file (Stats *stats,
1959 for (i = 0; i < f->n_lines; ++i)
1961 Line *l = &f->lines[i];
1963 if (l->inside_dbus_build_tests)
1965 stats->n_lines_inside_dbus_build_tests += 1;
1969 if (line_was_executed (l))
1970 stats->n_lines_executed += 1;
1972 if (l->blocks != NULL)
1973 stats->n_lines += 1;
1976 stats->n_lines_partial += 1;
1979 link = _dbus_list_get_first_link (&f->functions);
1980 while (link != NULL)
1982 Function *func = link->data;
1984 if (func->inside_dbus_build_tests)
1985 stats->n_functions_inside_dbus_build_tests += 1;
1988 stats->n_functions += 1;
1991 stats->n_functions_executed += 1;
1994 stats->n_functions_partial += 1;
1997 stats->n_blocks_inside_dbus_build_tests +=
1998 func->n_test_blocks;
2000 stats->n_blocks_executed +=
2001 func->n_nontest_blocks_executed;
2004 func->n_nontest_blocks;
2006 link = _dbus_list_get_next_link (&f->functions, link);
2010 /* The output of this matches gcov exactly ("diff" shows no difference) */
2012 print_annotated_source_gcov_format (File *f)
2017 while (i < f->n_lines)
2019 Line *l = &f->lines[i];
2021 if (l->blocks != NULL)
2025 exec_count = line_exec_count (l);
2028 printf ("%12d %s\n",
2029 exec_count, l->text);
2031 printf (" ###### %s\n", l->text);
2035 printf ("\t\t%s\n", l->text);
2043 print_annotated_source (File *f)
2048 while (i < f->n_lines)
2050 Line *l = &f->lines[i];
2052 if (l->inside_dbus_build_tests)
2057 if (l->blocks != NULL)
2061 exec_count = line_exec_count (l);
2064 printf ("%12d %s\n",
2065 exec_count, l->text);
2067 printf (" ###### %s\n", l->text);
2071 printf ("\t\t%s\n", l->text);
2079 print_block_superdetails (File *f)
2084 link = _dbus_list_get_first_link (&f->functions);
2085 while (link != NULL)
2087 Function *func = link->data;
2089 printf ("=== %s():\n", func->name);
2092 while (i < func->n_blocks)
2094 Block *b = &func->block_graph[i];
2097 printf (" %5d executed %d times%s\n", i,
2098 (int) b->exec_count,
2099 b->inside_dbus_build_tests ?
2100 " [inside DBUS_BUILD_TESTS]" : "");
2102 l = _dbus_list_get_first_link (&b->lines);
2105 Line *line = l->data;
2107 printf ("4%d\t%s\n", line->number, line->text);
2109 l = _dbus_list_get_next_link (&b->lines, l);
2115 link = _dbus_list_get_next_link (&f->functions, link);
2120 print_one_file (const DBusString *filename)
2122 if (_dbus_string_ends_with_c_str (filename, ".bb"))
2124 DBusString contents;
2127 if (!_dbus_string_init (&contents))
2128 die ("no memory\n");
2130 dbus_error_init (&error);
2132 if (!_dbus_file_get_contents (&contents, filename,
2135 fprintf (stderr, "Could not open file: %s\n",
2137 dbus_error_free (&error);
2141 dump_bb_file (&contents);
2143 _dbus_string_free (&contents);
2145 else if (_dbus_string_ends_with_c_str (filename, ".bbg"))
2147 DBusString contents;
2150 if (!_dbus_string_init (&contents))
2151 die ("no memory\n");
2153 dbus_error_init (&error);
2155 if (!_dbus_file_get_contents (&contents, filename,
2158 fprintf (stderr, "Could not open file: %s\n",
2160 dbus_error_free (&error);
2164 dump_bbg_file (&contents);
2166 _dbus_string_free (&contents);
2168 else if (_dbus_string_ends_with_c_str (filename, ".da"))
2170 DBusString contents;
2173 if (!_dbus_string_init (&contents))
2174 die ("no memory\n");
2176 dbus_error_init (&error);
2178 if (!_dbus_file_get_contents (&contents, filename,
2181 fprintf (stderr, "Could not open file: %s\n",
2183 dbus_error_free (&error);
2187 dump_da_file (&contents);
2189 _dbus_string_free (&contents);
2191 else if (_dbus_string_ends_with_c_str (filename, ".c"))
2195 f = load_c_file (filename);
2197 print_annotated_source (f);
2201 fprintf (stderr, "Unknown file type %s\n",
2202 _dbus_string_get_const_data (filename));
2208 print_untested_functions (File *f)
2214 link = _dbus_list_get_first_link (&f->functions);
2215 while (link != NULL)
2217 Function *func = link->data;
2220 !func->inside_dbus_build_tests)
2223 link = _dbus_list_get_next_link (&f->functions, link);
2229 printf ("Untested functions in %s\n", f->name);
2230 printf ("=======\n");
2232 link = _dbus_list_get_first_link (&f->functions);
2233 while (link != NULL)
2235 Function *func = link->data;
2238 !func->inside_dbus_build_tests)
2239 printf (" %s\n", func->name);
2241 link = _dbus_list_get_next_link (&f->functions, link);
2248 print_poorly_tested_functions (File *f,
2254 #define TEST_FRACTION(function) ((function)->n_nontest_blocks_executed / (double) (function)->n_nontest_blocks)
2256 #define AVERAGE_COVERAGE ((stats)->n_blocks_executed / (double) (stats)->n_blocks)
2258 #define POORLY_TESTED(function) (!(function)->unused && \
2259 (function)->n_nontest_blocks > 0 && \
2260 TEST_FRACTION (function) < AVERAGE_COVERAGE)
2263 link = _dbus_list_get_first_link (&f->functions);
2264 while (link != NULL)
2266 Function *func = link->data;
2268 if (POORLY_TESTED (func))
2271 link = _dbus_list_get_next_link (&f->functions, link);
2277 printf ("Below average functions in %s\n", f->name);
2278 printf ("=======\n");
2280 link = _dbus_list_get_first_link (&f->functions);
2281 while (link != NULL)
2283 Function *func = link->data;
2285 if (POORLY_TESTED (func))
2286 printf (" %s (%d%%)\n", func->name,
2287 (int) (TEST_FRACTION (func) * 100));
2289 link = _dbus_list_get_next_link (&f->functions, link);
2296 func_cmp (const void *a,
2299 Function *af = *(Function**) a;
2300 Function *bf = *(Function**) b;
2301 int a_untested = af->n_nontest_blocks - af->n_nontest_blocks_executed;
2302 int b_untested = bf->n_nontest_blocks - bf->n_nontest_blocks_executed;
2304 /* Sort by number of untested blocks */
2305 return b_untested - a_untested;
2309 print_n_untested_blocks_by_function (File *f,
2318 link = _dbus_list_get_first_link (&f->functions);
2319 while (link != NULL)
2321 Function *func = link->data;
2323 if (func->n_nontest_blocks_executed <
2324 func->n_nontest_blocks)
2327 link = _dbus_list_get_next_link (&f->functions, link);
2333 /* make an array so we can use qsort */
2335 funcs = dbus_new (Function*, n_found);
2340 link = _dbus_list_get_first_link (&f->functions);
2341 while (link != NULL)
2343 Function *func = link->data;
2345 if (func->n_nontest_blocks_executed <
2346 func->n_nontest_blocks)
2352 link = _dbus_list_get_next_link (&f->functions, link);
2355 _dbus_assert (i == n_found);
2357 qsort (funcs, n_found, sizeof (Function*),
2360 printf ("Incomplete functions in %s\n", f->name);
2361 printf ("=======\n");
2366 Function *func = funcs[i];
2368 printf (" %s (%d/%d untested blocks)\n",
2370 func->n_nontest_blocks - func->n_nontest_blocks_executed,
2371 func->n_nontest_blocks);
2382 print_stats (Stats *stats,
2383 const char *of_what)
2387 printf ("Summary (%s)\n", of_what);
2388 printf ("=======\n");
2389 printf (" %g%% blocks executed (%d of %d)\n",
2390 (stats->n_blocks_executed / (double) stats->n_blocks) * 100.0,
2391 stats->n_blocks_executed,
2394 printf (" (ignored %d blocks of test-only/debug-only code)\n",
2395 stats->n_blocks_inside_dbus_build_tests);
2397 printf (" %g%% functions executed (%d of %d)\n",
2398 (stats->n_functions_executed / (double) stats->n_functions) * 100.0,
2399 stats->n_functions_executed,
2400 stats->n_functions);
2402 completely = stats->n_functions_executed - stats->n_functions_partial;
2403 printf (" %g%% functions completely executed (%d of %d)\n",
2404 (completely / (double) stats->n_functions) * 100.0,
2406 stats->n_functions);
2408 printf (" (ignored %d functions of test-only/debug-only code)\n",
2409 stats->n_functions_inside_dbus_build_tests);
2411 printf (" %g%% lines executed (%d of %d)\n",
2412 (stats->n_lines_executed / (double) stats->n_lines) * 100.0,
2413 stats->n_lines_executed,
2416 completely = stats->n_lines_executed - stats->n_lines_partial;
2417 printf (" %g%% lines completely executed (%d of %d)\n",
2418 (completely / (double) stats->n_lines) * 100.0,
2422 printf (" (ignored %d lines of test-only/debug-only code)\n",
2423 stats->n_lines_inside_dbus_build_tests);
2437 main (int argc, char **argv)
2439 DBusString filename;
2445 fprintf (stderr, "Must specify files on command line\n");
2452 if (strcmp (argv[i], "--report") == 0)
2457 else if (strcmp (argv[i], "--blocks") == 0)
2462 else if (strcmp (argv[i], "--gcov") == 0)
2471 fprintf (stderr, "Must specify files on command line\n");
2475 if (m == MODE_PRINT)
2479 _dbus_string_init_const (&filename, argv[i]);
2481 print_one_file (&filename);
2486 else if (m == MODE_BLOCKS || m == MODE_GCOV)
2492 _dbus_string_init_const (&filename, argv[i]);
2494 f = load_c_file (&filename);
2496 if (m == MODE_BLOCKS)
2497 print_block_superdetails (f);
2498 else if (m == MODE_GCOV)
2499 print_annotated_source_gcov_format (f);
2504 else if (m == MODE_REPORT)
2506 Stats stats = { 0, };
2509 DBusHashTable *stats_by_dir;
2515 _dbus_string_init_const (&filename, argv[i]);
2517 if (_dbus_string_ends_with_c_str (&filename, ".c"))
2521 f = load_c_file (&filename);
2523 if (!_dbus_list_append (&files, f))
2524 die ("no memory\n");
2528 fprintf (stderr, "Unknown file type %s\n",
2529 _dbus_string_get_const_data (&filename));
2536 link = _dbus_list_get_first_link (&files);
2537 while (link != NULL)
2539 File *f = link->data;
2541 merge_stats_for_file (&stats, f);
2543 link = _dbus_list_get_next_link (&files, link);
2546 print_stats (&stats, "all files");
2548 stats_by_dir = _dbus_hash_table_new (DBUS_HASH_STRING,
2549 dbus_free, dbus_free);
2551 link = _dbus_list_get_first_link (&files);
2552 while (link != NULL)
2554 File *f = link->data;
2559 _dbus_string_init_const (&filename, f->name);
2561 if (!_dbus_string_init (&dirname))
2562 die ("no memory\n");
2564 if (!_dbus_string_get_dirname (&filename, &dirname) ||
2565 !_dbus_string_copy_data (&dirname, &dirname_c))
2566 die ("no memory\n");
2568 dir_stats = _dbus_hash_table_lookup_string (stats_by_dir,
2571 if (dir_stats == NULL)
2573 dir_stats = dbus_new0 (Stats, 1);
2574 if (!_dbus_hash_table_insert_string (stats_by_dir, dirname_c,
2576 die ("no memory\n");
2579 dbus_free (dirname_c);
2581 merge_stats_for_file (dir_stats, f);
2583 link = _dbus_list_get_next_link (&files, link);
2586 _dbus_hash_iter_init (stats_by_dir, &iter);
2587 while (_dbus_hash_iter_next (&iter))
2589 const char *dirname = _dbus_hash_iter_get_string_key (&iter);
2590 Stats *dir_stats = _dbus_hash_iter_get_value (&iter);
2592 print_stats (dir_stats, dirname);
2595 _dbus_hash_table_unref (stats_by_dir);
2597 link = _dbus_list_get_first_link (&files);
2598 while (link != NULL)
2600 File *f = link->data;
2602 print_untested_functions (f);
2604 link = _dbus_list_get_next_link (&files, link);
2607 link = _dbus_list_get_first_link (&files);
2608 while (link != NULL)
2610 File *f = link->data;
2612 print_poorly_tested_functions (f, &stats);
2614 link = _dbus_list_get_next_link (&files, link);
2617 link = _dbus_list_get_first_link (&files);
2618 while (link != NULL)
2620 File *f = link->data;
2622 print_n_untested_blocks_by_function (f, &stats);
2624 link = _dbus_list_get_next_link (&files, link);