sl@0: sl@0: /* -*- mode: C; c-file-style: "gnu" -*- */ sl@0: /* decode-gcov.c gcov decoder program sl@0: * sl@0: * Portion Copyright © 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. sl@0: * Copyright (C) 2003 Red Hat Inc. sl@0: * sl@0: * Partially derived from gcov, sl@0: * Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, sl@0: * 1999, 2000, 2001, 2002 Free Software Foundation, Inc. sl@0: * sl@0: * This file is NOT licensed under the Academic Free License sl@0: * as it is largely derived from gcov.c and gcov-io.h in the sl@0: * gcc source code. sl@0: * sl@0: * This program is free software; you can redistribute it and/or modify sl@0: * it under the terms of the GNU General Public License as published by sl@0: * the Free Software Foundation; either version 2 of the License, or sl@0: * (at your option) any later version. sl@0: * sl@0: * This program is distributed in the hope that it will be useful, sl@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of sl@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the sl@0: * GNU General Public License for more details. sl@0: * sl@0: * You should have received a copy of the GNU General Public License sl@0: * along with this program; if not, write to the Free Software sl@0: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA sl@0: * sl@0: */ sl@0: sl@0: #define DBUS_COMPILATION /* cheat */ sl@0: #ifndef __SYMBIAN32__ sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #else sl@0: #include "dbus-list.h" sl@0: #include "dbus-string.h" sl@0: #include "dbus-sysdeps.h" sl@0: #include "dbus-hash.h" sl@0: #endif sl@0: sl@0: sl@0: #undef DBUS_COMPILATION sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: #ifdef SYMBIAN sl@0: #define DBUS_HAVE_INT64 1 sl@0: #endif sl@0: sl@0: #ifndef DBUS_HAVE_INT64 sl@0: #error "gcov support can't be built without 64-bit integer support" sl@0: #endif sl@0: sl@0: static void sl@0: die (const char *message) sl@0: { sl@0: fprintf (stderr, "%s", message); sl@0: exit (1); sl@0: } sl@0: sl@0: /* This bizarro function is from gcov-io.h in gcc source tree */ sl@0: static int sl@0: fetch_long (long *dest, sl@0: const char *source, sl@0: size_t bytes) sl@0: { sl@0: long value = 0; sl@0: int i; sl@0: sl@0: for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--) sl@0: if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 )) sl@0: return 1; sl@0: sl@0: for (; i >= 0; i--) sl@0: value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255)); sl@0: sl@0: if ((source[bytes - 1] & 128) && (value > 0)) sl@0: value = - value; sl@0: sl@0: *dest = value; sl@0: return 0; sl@0: } sl@0: sl@0: static int sl@0: fetch_long64 (dbus_int64_t *dest, sl@0: const char *source, sl@0: size_t bytes) sl@0: { sl@0: dbus_int64_t value = 0; sl@0: int i; sl@0: sl@0: for (i = bytes - 1; (size_t) i > (sizeof (*dest) - 1); i--) sl@0: if (source[i] & ((size_t) i == (bytes - 1) ? 127 : 255 )) sl@0: return 1; sl@0: sl@0: for (; i >= 0; i--) sl@0: value = value * 256 + (source[i] & ((size_t)i == (bytes - 1) ? 127 : 255)); sl@0: sl@0: if ((source[bytes - 1] & 128) && (value > 0)) sl@0: value = - value; sl@0: sl@0: *dest = value; sl@0: return 0; sl@0: } sl@0: sl@0: #define BB_FILENAME (-1) sl@0: #define BB_FUNCTION (-2) sl@0: #define BB_ENDOFLIST 0 sl@0: sl@0: static dbus_bool_t sl@0: string_get_int (const DBusString *str, sl@0: int start, sl@0: long *val) sl@0: { sl@0: const char *p; sl@0: sl@0: if ((_dbus_string_get_length (str) - start) < 4) sl@0: return FALSE; sl@0: sl@0: p = _dbus_string_get_const_data (str); sl@0: sl@0: p += start; sl@0: sl@0: fetch_long (val, p, 4); sl@0: sl@0: return TRUE; sl@0: } sl@0: sl@0: static dbus_bool_t sl@0: string_get_int64 (const DBusString *str, sl@0: int start, sl@0: dbus_int64_t *val) sl@0: { sl@0: const char *p; sl@0: sl@0: if ((_dbus_string_get_length (str) - start) < 8) sl@0: return FALSE; sl@0: sl@0: p = _dbus_string_get_const_data (str); sl@0: sl@0: p += start; sl@0: sl@0: fetch_long64 (val, p, 8); sl@0: sl@0: return TRUE; sl@0: } sl@0: sl@0: static dbus_bool_t sl@0: string_get_string (const DBusString *str, sl@0: int start, sl@0: long terminator, sl@0: DBusString *val, sl@0: int *end) sl@0: { sl@0: int i; sl@0: long n; sl@0: sl@0: i = start; sl@0: while (string_get_int (str, i, &n)) sl@0: { sl@0: unsigned char b; sl@0: sl@0: i += 4; sl@0: sl@0: if (n == terminator) sl@0: break; sl@0: sl@0: b = n & 0xff; sl@0: if (b) sl@0: { sl@0: _dbus_string_append_byte (val, b); sl@0: b = (n >> 8) & 0xff; sl@0: if (b) sl@0: { sl@0: _dbus_string_append_byte (val, b); sl@0: b = (n >> 16) & 0xff; sl@0: if (b) sl@0: { sl@0: _dbus_string_append_byte (val, b); sl@0: b = (n >> 24) & 0xff; sl@0: if (b) sl@0: _dbus_string_append_byte (val, b); sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: *end = i; sl@0: sl@0: return TRUE; sl@0: } sl@0: sl@0: #ifdef DBUS_HAVE_GCC33_GCOV sl@0: /* In gcc33 .bbg files, there's a function name of the form: sl@0: * -1, length, name (padded to 4), -1, checksum sl@0: */ sl@0: static dbus_bool_t sl@0: string_get_function (const DBusString *str, sl@0: int start, sl@0: DBusString *funcname, sl@0: int *checksum, sl@0: int *next) sl@0: { sl@0: int end; sl@0: long val; sl@0: int i; sl@0: sl@0: i = start; sl@0: sl@0: if (!string_get_int (str, i, &val)) sl@0: die ("no room for -1 before function name\n"); sl@0: sl@0: i += 4; sl@0: sl@0: if (val != -1) sl@0: die ("value before function name is not -1\n"); sl@0: sl@0: if (!string_get_int (str, i, &val)) sl@0: die ("no length found for function name\n"); sl@0: sl@0: i += 4; sl@0: sl@0: end = i + val; sl@0: if (end > _dbus_string_get_length (str)) sl@0: die ("Function name length points past end of file\n"); sl@0: sl@0: if (!_dbus_string_append (funcname, sl@0: _dbus_string_get_const_data (str) + i)) sl@0: die ("no memory\n"); sl@0: sl@0: /* skip alignment padding the length doesn't include the nul so add 1 sl@0: */ sl@0: i = _DBUS_ALIGN_VALUE (end + 1, 4); sl@0: sl@0: if (!string_get_int (str, i, &val) || sl@0: val != -1) sl@0: die ("-1 at end of function name not found\n"); sl@0: sl@0: i += 4; sl@0: sl@0: if (!string_get_int (str, i, &val)) sl@0: die ("no checksum found at end of function name\n"); sl@0: sl@0: i += 4; sl@0: sl@0: *checksum = val; sl@0: sl@0: *next = i; sl@0: sl@0: return TRUE; sl@0: } sl@0: #endif /* DBUS_HAVE_GCC33_GCOV */ sl@0: sl@0: static void sl@0: dump_bb_file (const DBusString *contents) sl@0: { sl@0: int i; sl@0: long val; sl@0: int n_functions; sl@0: sl@0: n_functions = 0; sl@0: i = 0; sl@0: while (string_get_int (contents, i, &val)) sl@0: { sl@0: i += 4; sl@0: sl@0: switch (val) sl@0: { sl@0: case BB_FILENAME: sl@0: { sl@0: DBusString f; sl@0: sl@0: if (!_dbus_string_init (&f)) sl@0: die ("no memory\n"); sl@0: sl@0: if (string_get_string (contents, i, sl@0: BB_FILENAME, sl@0: &f, &i)) sl@0: { sl@0: printf ("File %s\n", _dbus_string_get_const_data (&f)); sl@0: } sl@0: _dbus_string_free (&f); sl@0: } sl@0: break; sl@0: case BB_FUNCTION: sl@0: { sl@0: DBusString f; sl@0: if (!_dbus_string_init (&f)) sl@0: die ("no memory\n"); sl@0: sl@0: if (string_get_string (contents, i, sl@0: BB_FUNCTION, sl@0: &f, &i)) sl@0: { sl@0: printf ("Function %s\n", _dbus_string_get_const_data (&f)); sl@0: } sl@0: _dbus_string_free (&f); sl@0: sl@0: n_functions += 1; sl@0: } sl@0: break; sl@0: case BB_ENDOFLIST: sl@0: printf ("End of block\n"); sl@0: break; sl@0: default: sl@0: printf ("Line %ld\n", val); sl@0: break; sl@0: } sl@0: } sl@0: sl@0: printf ("%d functions in file\n", n_functions); sl@0: } sl@0: sl@0: #define FLAG_ON_TREE 0x1 sl@0: #define FLAG_FAKE 0x2 sl@0: #define FLAG_FALL_THROUGH 0x4 sl@0: sl@0: static void sl@0: dump_bbg_file (const DBusString *contents) sl@0: { sl@0: int i; sl@0: long val; sl@0: int n_functions; sl@0: int n_arcs; sl@0: int n_blocks; sl@0: int n_arcs_off_tree; sl@0: sl@0: n_arcs_off_tree = 0; sl@0: n_blocks = 0; sl@0: n_arcs = 0; sl@0: n_functions = 0; sl@0: i = 0; sl@0: while (i < _dbus_string_get_length (contents)) sl@0: { sl@0: long n_blocks_in_func; sl@0: long n_arcs_in_func; sl@0: int j; sl@0: sl@0: #ifdef DBUS_HAVE_GCC33_GCOV sl@0: /* In gcc33 .bbg files, there's a function name of the form: sl@0: * -1, length, name (padded to 4), -1, checksum sl@0: * after that header on each function description, it's sl@0: * the same as in gcc32 sl@0: */ sl@0: sl@0: { sl@0: DBusString funcname; sl@0: int checksum; sl@0: sl@0: if (!_dbus_string_init (&funcname)) sl@0: die ("no memory\n"); sl@0: sl@0: if (!string_get_function (contents, i, sl@0: &funcname, &checksum, &i)) sl@0: die ("could not read function name\n"); sl@0: sl@0: printf ("Function name is \"%s\" checksum %d\n", sl@0: _dbus_string_get_const_data (&funcname), sl@0: checksum); sl@0: sl@0: _dbus_string_free (&funcname); sl@0: } sl@0: #endif /* DBUS_HAVE_GCC33_GCOV */ sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no count of blocks in func found\n"); sl@0: sl@0: i += 4; sl@0: sl@0: n_blocks_in_func = val; sl@0: sl@0: if (!string_get_int (contents, i, &n_arcs_in_func)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: printf ("Function has %ld blocks and %ld arcs\n", sl@0: n_blocks_in_func, n_arcs_in_func); sl@0: sl@0: n_functions += 1; sl@0: n_blocks += n_blocks_in_func; sl@0: n_arcs += n_arcs_in_func; sl@0: sl@0: j = 0; sl@0: while (j < n_blocks_in_func) sl@0: { sl@0: long n_arcs_in_block; sl@0: int k; sl@0: sl@0: if (!string_get_int (contents, i, &n_arcs_in_block)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: printf (" Block has %ld arcs\n", n_arcs_in_block); sl@0: sl@0: k = 0; sl@0: while (k < n_arcs_in_block) sl@0: { sl@0: long destination_block; sl@0: long flags; sl@0: sl@0: if (!string_get_int (contents, i, &destination_block)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: if (!string_get_int (contents, i, &flags)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: printf (" Arc has destination block %ld flags 0x%lx\n", sl@0: destination_block, flags); sl@0: sl@0: if ((flags & FLAG_ON_TREE) == 0) sl@0: n_arcs_off_tree += 1; sl@0: sl@0: ++k; sl@0: } sl@0: sl@0: if (k < n_arcs_in_block) sl@0: break; sl@0: sl@0: ++j; sl@0: } sl@0: sl@0: if (j < n_blocks_in_func) sl@0: break; sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: if (val != -1) sl@0: die ("-1 separator not found\n"); sl@0: } sl@0: sl@0: printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n", sl@0: n_functions, n_blocks, n_arcs, n_arcs_off_tree); sl@0: } sl@0: sl@0: #ifndef DBUS_HAVE_GCC33_GCOV sl@0: sl@0: /* gcc 3.2 version: sl@0: * The da file contains first a count of arcs in the file, sl@0: * then a count of executions for all "off tree" arcs sl@0: * in the file. sl@0: */ sl@0: static void sl@0: dump_da_file (const DBusString *contents) sl@0: { sl@0: int i; sl@0: dbus_int64_t val; sl@0: int n_arcs; sl@0: int claimed_n_arcs; sl@0: sl@0: i = 0; sl@0: if (!string_get_int64 (contents, i, &val)) sl@0: return; sl@0: sl@0: i += 8; sl@0: sl@0: printf ("%ld arcs in file\n", (long) val); sl@0: claimed_n_arcs = val; sl@0: sl@0: n_arcs = 0; sl@0: while (string_get_int64 (contents, i, &val)) sl@0: { sl@0: i += 8; sl@0: sl@0: printf ("%ld executions of arc %d\n", sl@0: (long) val, n_arcs); sl@0: sl@0: ++n_arcs; sl@0: } sl@0: sl@0: if (n_arcs != claimed_n_arcs) sl@0: { sl@0: printf ("File claimed to have %d arcs but only had %d\n", sl@0: claimed_n_arcs, n_arcs); sl@0: } sl@0: } sl@0: sl@0: #else /* DBUS_HAVE_GCC33_GCOV */ sl@0: sl@0: /* gcc 3.3 version: sl@0: * The da file is more complex than 3.2. sl@0: * sl@0: * We have a magic value of "-123" only it isn't really sl@0: * -123, it's -123 as encoded by the crackass gcov-io.h sl@0: * routines. Anyway, 4 bytes. sl@0: * sl@0: * We then have: sl@0: * sl@0: * - 4 byte count of how many functions in the following list sl@0: * - 4 byte length of random extra data sl@0: * - the random extra data, just skip it, info pages have some sl@0: * details on what might be in there or see __bb_exit_func in gcc sl@0: * - then for each function (number of functions given above): sl@0: * . -1, length, funcname, alignment padding, -1 sl@0: * . checksum sl@0: * . 4 byte number of arcs in function sl@0: * . 8 bytes each, a count of execution for each arc sl@0: * sl@0: * Now, the whole thing *starting with the magic* can repeat. sl@0: * This is caused by multiple runs of the profiled app appending sl@0: * to the file. sl@0: */ sl@0: static void sl@0: dump_da_file (const DBusString *contents) sl@0: { sl@0: int i; sl@0: dbus_int64_t v64; sl@0: long val; sl@0: int n_sections; sl@0: int total_functions; sl@0: sl@0: total_functions = 0; sl@0: n_sections = 0; sl@0: sl@0: i = 0; sl@0: while (i < _dbus_string_get_length (contents)) sl@0: { sl@0: int claimed_n_functions; sl@0: int n_functions; sl@0: int total_arcs; sl@0: sl@0: printf (".da file section %d\n", n_sections); sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no magic found in .da file\n"); sl@0: sl@0: i += 4; sl@0: sl@0: if (val != -123) sl@0: die ("wrong file magic in .da file\n"); sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no function count in .da file\n"); sl@0: i += 4; sl@0: claimed_n_functions = val; sl@0: sl@0: printf ("%d functions expected in section %d of .da file\n", sl@0: claimed_n_functions, n_sections); sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no extra data length in .da file\n"); sl@0: sl@0: i += 4; sl@0: sl@0: i += val; sl@0: sl@0: total_arcs = 0; sl@0: n_functions = 0; sl@0: while (n_functions < claimed_n_functions) sl@0: { sl@0: DBusString funcname; sl@0: int checksum; sl@0: int claimed_n_arcs; sl@0: int n_arcs; sl@0: sl@0: if (!_dbus_string_init (&funcname)) sl@0: die ("no memory\n"); sl@0: sl@0: if (!string_get_function (contents, i, sl@0: &funcname, &checksum, &i)) sl@0: die ("could not read function name\n"); sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no arc count for function\n"); sl@0: sl@0: i += 4; sl@0: claimed_n_arcs = val; sl@0: sl@0: printf (" %d arcs in function %d %s checksum %d\n", sl@0: claimed_n_arcs, n_functions, sl@0: _dbus_string_get_const_data (&funcname), sl@0: checksum); sl@0: sl@0: n_arcs = 0; sl@0: while (n_arcs < claimed_n_arcs) sl@0: { sl@0: if (!string_get_int64 (contents, i, &v64)) sl@0: die ("did not get execution count for arc\n"); sl@0: sl@0: i += 8; sl@0: sl@0: printf (" %ld executions of arc %d (total arcs %d)\n", sl@0: (long) v64, n_arcs, total_arcs + n_arcs); sl@0: sl@0: ++n_arcs; sl@0: } sl@0: sl@0: _dbus_string_free (&funcname); sl@0: sl@0: total_arcs += n_arcs; sl@0: ++n_functions; sl@0: } sl@0: sl@0: printf ("total of %d functions and %d arcs in section %d\n", sl@0: n_functions, total_arcs, n_sections); sl@0: sl@0: total_functions += n_functions; sl@0: ++n_sections; sl@0: } sl@0: sl@0: printf ("%d total function sections in %d total .da file sections\n", sl@0: total_functions, n_sections); sl@0: } sl@0: sl@0: #endif /* DBUS_HAVE_GCC33_GCOV */ sl@0: sl@0: typedef struct Arc Arc; sl@0: typedef struct Block Block; sl@0: typedef struct Function Function; sl@0: typedef struct File File; sl@0: typedef struct Line Line; sl@0: sl@0: struct Arc sl@0: { sl@0: int source; sl@0: int target; sl@0: dbus_int64_t arc_count; sl@0: unsigned int count_valid : 1; sl@0: unsigned int on_tree : 1; sl@0: unsigned int fake : 1; sl@0: unsigned int fall_through : 1; sl@0: Arc *pred_next; sl@0: Arc *succ_next; sl@0: }; sl@0: sl@0: struct Block sl@0: { sl@0: Arc *succ; sl@0: Arc *pred; sl@0: dbus_int64_t succ_count; sl@0: dbus_int64_t pred_count; sl@0: dbus_int64_t exec_count; sl@0: DBusList *lines; sl@0: unsigned int count_valid : 1; sl@0: unsigned int on_tree : 1; sl@0: unsigned int inside_dbus_build_tests : 1; sl@0: }; sl@0: sl@0: struct Function sl@0: { sl@0: char *name; sl@0: int checksum; sl@0: Block *block_graph; sl@0: int n_blocks; sl@0: /* number of blocks in DBUS_BUILD_TESTS */ sl@0: int n_test_blocks; sl@0: int n_test_blocks_executed; sl@0: /* number of blocks outside DBUS_BUILD_TESTS */ sl@0: int n_nontest_blocks; sl@0: int n_nontest_blocks_executed; sl@0: /* Summary result flags */ sl@0: unsigned int unused : 1; sl@0: unsigned int inside_dbus_build_tests : 1; sl@0: unsigned int partial : 1; /* only some of the blocks were executed */ sl@0: }; sl@0: sl@0: struct Line sl@0: { sl@0: int number; sl@0: char *text; sl@0: DBusList *blocks; sl@0: unsigned int inside_dbus_build_tests : 1; sl@0: unsigned int partial : 1; /* only some of the blocks were executed */ sl@0: }; sl@0: sl@0: struct File sl@0: { sl@0: char *name; sl@0: Line *lines; sl@0: int n_lines; sl@0: DBusList *functions; sl@0: }; sl@0: sl@0: static void sl@0: function_add_arc (Function *function, sl@0: long source, sl@0: long target, sl@0: long flags) sl@0: { sl@0: Arc *arc; sl@0: sl@0: arc = dbus_new0 (Arc, 1); sl@0: if (arc == NULL) sl@0: die ("no memory\n"); sl@0: sl@0: arc->target = target; sl@0: arc->source = source; sl@0: sl@0: arc->succ_next = function->block_graph[source].succ; sl@0: function->block_graph[source].succ = arc; sl@0: function->block_graph[source].succ_count += 1; sl@0: sl@0: arc->pred_next = function->block_graph[target].pred; sl@0: function->block_graph[target].pred = arc; sl@0: function->block_graph[target].pred_count += 1; sl@0: sl@0: if ((flags & FLAG_ON_TREE) != 0) sl@0: arc->on_tree = TRUE; sl@0: sl@0: if ((flags & FLAG_FAKE) != 0) sl@0: arc->fake = TRUE; sl@0: sl@0: if ((flags & FLAG_FALL_THROUGH) != 0) sl@0: arc->fall_through = TRUE; sl@0: } sl@0: sl@0: sl@0: static Arc* sl@0: reverse_arcs (Arc *arc) sl@0: { sl@0: struct Arc *prev = 0; sl@0: struct Arc *next; sl@0: sl@0: for ( ; arc; arc = next) sl@0: { sl@0: next = arc->succ_next; sl@0: arc->succ_next = prev; sl@0: prev = arc; sl@0: } sl@0: sl@0: return prev; sl@0: } sl@0: sl@0: static void sl@0: function_reverse_succ_arcs (Function *func) sl@0: { sl@0: /* Must reverse the order of all succ arcs, to ensure that they match sl@0: * the order of the data in the .da file. sl@0: */ sl@0: int i; sl@0: sl@0: for (i = 0; i < func->n_blocks; i++) sl@0: if (func->block_graph[i].succ) sl@0: func->block_graph[i].succ = reverse_arcs (func->block_graph[i].succ); sl@0: } sl@0: sl@0: static void sl@0: get_functions_from_bbg (const DBusString *contents, sl@0: DBusList **functions) sl@0: { sl@0: int i; sl@0: long val; sl@0: int n_functions; sl@0: int n_arcs; sl@0: int n_blocks; sl@0: int n_arcs_off_tree; sl@0: sl@0: #if 0 sl@0: printf ("Loading arcs and blocks from .bbg file\n"); sl@0: #endif sl@0: sl@0: n_arcs_off_tree = 0; sl@0: n_blocks = 0; sl@0: n_arcs = 0; sl@0: n_functions = 0; sl@0: i = 0; sl@0: while (i < _dbus_string_get_length (contents)) sl@0: { sl@0: Function *func; sl@0: long n_blocks_in_func; sl@0: long n_arcs_in_func; sl@0: int j; sl@0: sl@0: #ifdef DBUS_HAVE_GCC33_GCOV sl@0: DBusString funcname; sl@0: int checksum; sl@0: sl@0: /* In gcc33 .bbg files, there's a function name of the form: sl@0: * -1, length, name (padded to 4), -1, checksum sl@0: * after that header on each function description, it's sl@0: * the same as in gcc32 sl@0: */ sl@0: if (!_dbus_string_init (&funcname)) sl@0: die ("no memory\n"); sl@0: sl@0: if (!string_get_function (contents, i, sl@0: &funcname, &checksum, &i)) sl@0: die ("could not read function name\n"); sl@0: #endif /* DBUS_HAVE_GCC33_GCOV */ sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: break; sl@0: sl@0: n_blocks_in_func = val; sl@0: sl@0: i += 4; sl@0: sl@0: if (!string_get_int (contents, i, &n_arcs_in_func)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: n_functions += 1; sl@0: n_blocks += n_blocks_in_func; sl@0: n_arcs += n_arcs_in_func; sl@0: sl@0: func = dbus_new0 (Function, 1); sl@0: if (func == NULL) sl@0: die ("no memory\n"); sl@0: sl@0: #ifdef DBUS_HAVE_GCC33_GCOV sl@0: func->name = _dbus_strdup (_dbus_string_get_const_data (&funcname)); sl@0: func->checksum = checksum; sl@0: _dbus_string_free (&funcname); sl@0: #endif sl@0: sl@0: func->block_graph = dbus_new0 (Block, n_blocks_in_func); sl@0: func->n_blocks = n_blocks_in_func; sl@0: sl@0: j = 0; sl@0: while (j < n_blocks_in_func) sl@0: { sl@0: long n_arcs_in_block; sl@0: int k; sl@0: sl@0: if (!string_get_int (contents, i, &n_arcs_in_block)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: k = 0; sl@0: while (k < n_arcs_in_block) sl@0: { sl@0: long destination_block; sl@0: long flags; sl@0: sl@0: if (!string_get_int (contents, i, &destination_block)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: if (!string_get_int (contents, i, &flags)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: if ((flags & FLAG_ON_TREE) == 0) sl@0: n_arcs_off_tree += 1; sl@0: sl@0: function_add_arc (func, j, destination_block, sl@0: flags); sl@0: sl@0: ++k; sl@0: } sl@0: sl@0: if (k < n_arcs_in_block) sl@0: break; sl@0: sl@0: ++j; sl@0: } sl@0: sl@0: if (j < n_blocks_in_func) sl@0: break; sl@0: sl@0: function_reverse_succ_arcs (func); sl@0: sl@0: if (!_dbus_list_append (functions, func)) sl@0: die ("no memory\n"); sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: break; sl@0: sl@0: i += 4; sl@0: sl@0: if (val != -1) sl@0: die ("-1 separator not found in .bbg file\n"); sl@0: } sl@0: sl@0: #if 0 sl@0: printf ("%d functions %d blocks %d arcs %d off-tree arcs in file\n", sl@0: n_functions, n_blocks, n_arcs, n_arcs_off_tree); sl@0: #endif sl@0: sl@0: _dbus_assert (n_functions == _dbus_list_get_length (functions)); sl@0: } sl@0: sl@0: #ifdef DBUS_HAVE_GCC33_GCOV sl@0: static void sl@0: add_counts_from_da (const DBusString *contents, sl@0: DBusList **functions) sl@0: { sl@0: int i; sl@0: dbus_int64_t v64; sl@0: long val; sl@0: int n_sections; sl@0: DBusList *link; sl@0: Function *current_func; sl@0: int current_block; sl@0: Arc *current_arc; sl@0: sl@0: n_sections = 0; sl@0: sl@0: i = 0; sl@0: while (i < _dbus_string_get_length (contents)) sl@0: { sl@0: int claimed_n_functions; sl@0: int n_functions; sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no magic found in .da file\n"); sl@0: sl@0: i += 4; sl@0: sl@0: if (val != -123) sl@0: die ("wrong file magic in .da file\n"); sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no function count in .da file\n"); sl@0: i += 4; sl@0: claimed_n_functions = val; sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no extra data length in .da file\n"); sl@0: sl@0: i += 4; sl@0: sl@0: i += val; sl@0: sl@0: link = _dbus_list_get_first_link (functions); sl@0: if (link == NULL) sl@0: goto no_more_functions; sl@0: sl@0: n_functions = 0; sl@0: while (n_functions < claimed_n_functions && link != NULL) sl@0: { sl@0: DBusString funcname; sl@0: int checksum; sl@0: int claimed_n_arcs; sl@0: int n_arcs; sl@0: sl@0: current_func = link->data; sl@0: current_block = 0; sl@0: current_arc = current_func->block_graph[current_block].succ; sl@0: sl@0: if (!_dbus_string_init (&funcname)) sl@0: die ("no memory\n"); sl@0: sl@0: if (!string_get_function (contents, i, sl@0: &funcname, &checksum, &i)) sl@0: die ("could not read function name\n"); sl@0: sl@0: if (!_dbus_string_equal_c_str (&funcname, current_func->name)) sl@0: { sl@0: fprintf (stderr, "Expecting .da info for %s but got %s\n", sl@0: current_func->name, sl@0: _dbus_string_get_const_data (&funcname)); sl@0: exit (1); sl@0: } sl@0: sl@0: if (checksum != current_func->checksum) sl@0: die (".da file checksum doesn't match checksum from .bbg file\n"); sl@0: sl@0: if (!string_get_int (contents, i, &val)) sl@0: die ("no arc count for function\n"); sl@0: sl@0: i += 4; sl@0: claimed_n_arcs = val; sl@0: sl@0: /* For each arc in the profile, find the corresponding sl@0: * arc in the function and increment its count sl@0: */ sl@0: n_arcs = 0; sl@0: while (n_arcs < claimed_n_arcs) sl@0: { sl@0: if (!string_get_int64 (contents, i, &v64)) sl@0: die ("did not get execution count for arc\n"); sl@0: sl@0: i += 8; sl@0: sl@0: /* Find the next arc in the function that isn't on tree */ sl@0: while (current_arc == NULL || sl@0: current_arc->on_tree) sl@0: { sl@0: if (current_arc == NULL) sl@0: { sl@0: ++current_block; sl@0: sl@0: if (current_block >= current_func->n_blocks) sl@0: die ("too many blocks in function\n"); sl@0: sl@0: current_arc = current_func->block_graph[current_block].succ; sl@0: } sl@0: else sl@0: { sl@0: current_arc = current_arc->succ_next; sl@0: } sl@0: } sl@0: sl@0: _dbus_assert (current_arc != NULL); sl@0: _dbus_assert (!current_arc->on_tree); sl@0: sl@0: current_arc->arc_count = v64; sl@0: current_arc->count_valid = TRUE; sl@0: current_func->block_graph[current_block].succ_count -= 1; sl@0: current_func->block_graph[current_arc->target].pred_count -= 1; sl@0: sl@0: ++n_arcs; sl@0: sl@0: current_arc = current_arc->succ_next; sl@0: } sl@0: sl@0: _dbus_string_free (&funcname); sl@0: sl@0: link = _dbus_list_get_next_link (functions, link); sl@0: ++n_functions; sl@0: sl@0: if (link == NULL && n_functions < claimed_n_functions) sl@0: { sl@0: fprintf (stderr, "Ran out of functions loading .da file\n"); sl@0: goto no_more_functions; sl@0: } sl@0: } sl@0: sl@0: no_more_functions: sl@0: sl@0: ++n_sections; sl@0: } sl@0: } sl@0: #else /* DBUS_HAVE_GCC33_GCOV */ sl@0: static void sl@0: add_counts_from_da (const DBusString *contents, sl@0: DBusList **functions) sl@0: { sl@0: int i; sl@0: dbus_int64_t val; sl@0: int n_arcs; sl@0: int claimed_n_arcs; sl@0: DBusList *link; sl@0: Function *current_func; sl@0: int current_block; sl@0: Arc *current_arc; sl@0: sl@0: #if 0 sl@0: printf ("Loading execution count for each arc from .da file\n"); sl@0: #endif sl@0: sl@0: i = 0; sl@0: if (!string_get_int64 (contents, i, &val)) sl@0: return; sl@0: sl@0: i += 8; sl@0: sl@0: claimed_n_arcs = val; sl@0: sl@0: link = _dbus_list_get_first_link (functions); sl@0: if (link == NULL) sl@0: goto done; sl@0: sl@0: current_func = link->data; sl@0: current_block = 0; sl@0: current_arc = current_func->block_graph[current_block].succ; sl@0: sl@0: n_arcs = 0; sl@0: while (string_get_int64 (contents, i, &val)) sl@0: { sl@0: i += 8; sl@0: sl@0: while (current_arc == NULL || sl@0: current_arc->on_tree) sl@0: { sl@0: if (current_arc == NULL) sl@0: { sl@0: ++current_block; sl@0: sl@0: if (current_block == current_func->n_blocks) sl@0: { sl@0: link = _dbus_list_get_next_link (functions, link); sl@0: if (link == NULL) sl@0: { sl@0: fprintf (stderr, "Ran out of functions loading .da file\n"); sl@0: goto done; sl@0: } sl@0: current_func = link->data; sl@0: current_block = 0; sl@0: } sl@0: sl@0: current_arc = current_func->block_graph[current_block].succ; sl@0: } sl@0: else sl@0: { sl@0: current_arc = current_arc->succ_next; sl@0: } sl@0: } sl@0: sl@0: _dbus_assert (current_arc != NULL); sl@0: _dbus_assert (!current_arc->on_tree); sl@0: sl@0: current_arc->arc_count = val; sl@0: current_arc->count_valid = TRUE; sl@0: current_func->block_graph[current_block].succ_count -= 1; sl@0: current_func->block_graph[current_arc->target].pred_count -= 1; sl@0: sl@0: ++n_arcs; sl@0: sl@0: current_arc = current_arc->succ_next; sl@0: } sl@0: sl@0: done: sl@0: sl@0: if (n_arcs != claimed_n_arcs) sl@0: { sl@0: fprintf (stderr, "File claimed to have %d arcs but only had %d\n", sl@0: claimed_n_arcs, n_arcs); sl@0: exit (1); sl@0: } sl@0: sl@0: #if 0 sl@0: printf ("%d arcs in file\n", n_arcs); sl@0: #endif sl@0: } sl@0: #endif sl@0: sl@0: static void sl@0: function_solve_graph (Function *func) sl@0: { sl@0: int passes, changes; sl@0: dbus_int64_t total; sl@0: int i; sl@0: Arc *arc; sl@0: Block *block_graph; sl@0: int n_blocks; sl@0: sl@0: #if 0 sl@0: printf ("Solving function graph\n"); sl@0: #endif sl@0: sl@0: n_blocks = func->n_blocks; sl@0: block_graph = func->block_graph; sl@0: sl@0: /* For every block in the file, sl@0: - if every exit/entrance arc has a known count, then set the block count sl@0: - if the block count is known, and every exit/entrance arc but one has sl@0: a known execution count, then set the count of the remaining arc sl@0: sl@0: As arc counts are set, decrement the succ/pred count, but don't delete sl@0: the arc, that way we can easily tell when all arcs are known, or only sl@0: one arc is unknown. */ sl@0: sl@0: /* The order that the basic blocks are iterated through is important. sl@0: Since the code that finds spanning trees starts with block 0, low numbered sl@0: arcs are put on the spanning tree in preference to high numbered arcs. sl@0: Hence, most instrumented arcs are at the end. Graph solving works much sl@0: faster if we propagate numbers from the end to the start. sl@0: sl@0: This takes an average of slightly more than 3 passes. */ sl@0: sl@0: changes = 1; sl@0: passes = 0; sl@0: while (changes) sl@0: { sl@0: passes++; sl@0: changes = 0; sl@0: sl@0: for (i = n_blocks - 1; i >= 0; i--) sl@0: { sl@0: if (! block_graph[i].count_valid) sl@0: { sl@0: if (block_graph[i].succ_count == 0) sl@0: { sl@0: total = 0; sl@0: for (arc = block_graph[i].succ; arc; sl@0: arc = arc->succ_next) sl@0: total += arc->arc_count; sl@0: block_graph[i].exec_count = total; sl@0: block_graph[i].count_valid = 1; sl@0: changes = 1; sl@0: } sl@0: else if (block_graph[i].pred_count == 0) sl@0: { sl@0: total = 0; sl@0: for (arc = block_graph[i].pred; arc; sl@0: arc = arc->pred_next) sl@0: total += arc->arc_count; sl@0: block_graph[i].exec_count = total; sl@0: block_graph[i].count_valid = 1; sl@0: changes = 1; sl@0: } sl@0: } sl@0: if (block_graph[i].count_valid) sl@0: { sl@0: if (block_graph[i].succ_count == 1) sl@0: { sl@0: total = 0; sl@0: /* One of the counts will be invalid, but it is zero, sl@0: so adding it in also doesn't hurt. */ sl@0: for (arc = block_graph[i].succ; arc; sl@0: arc = arc->succ_next) sl@0: total += arc->arc_count; sl@0: /* Calculate count for remaining arc by conservation. */ sl@0: total = block_graph[i].exec_count - total; sl@0: /* Search for the invalid arc, and set its count. */ sl@0: for (arc = block_graph[i].succ; arc; sl@0: arc = arc->succ_next) sl@0: if (! arc->count_valid) sl@0: break; sl@0: if (! arc) sl@0: die ("arc == NULL\n"); sl@0: arc->count_valid = 1; sl@0: arc->arc_count = total; sl@0: block_graph[i].succ_count -= 1; sl@0: sl@0: block_graph[arc->target].pred_count -= 1; sl@0: changes = 1; sl@0: } sl@0: if (block_graph[i].pred_count == 1) sl@0: { sl@0: total = 0; sl@0: /* One of the counts will be invalid, but it is zero, sl@0: so adding it in also doesn't hurt. */ sl@0: for (arc = block_graph[i].pred; arc; sl@0: arc = arc->pred_next) sl@0: total += arc->arc_count; sl@0: /* Calculate count for remaining arc by conservation. */ sl@0: total = block_graph[i].exec_count - total; sl@0: /* Search for the invalid arc, and set its count. */ sl@0: for (arc = block_graph[i].pred; arc; sl@0: arc = arc->pred_next) sl@0: if (! arc->count_valid) sl@0: break; sl@0: if (! arc) sl@0: die ("arc == NULL\n"); sl@0: arc->count_valid = 1; sl@0: arc->arc_count = total; sl@0: block_graph[i].pred_count -= 1; sl@0: sl@0: block_graph[arc->source].succ_count -= 1; sl@0: changes = 1; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* If the graph has been correctly solved, every block will have a sl@0: * succ and pred count of zero. sl@0: */ sl@0: { sl@0: dbus_bool_t header = FALSE; sl@0: for (i = 0; i < n_blocks; i++) sl@0: { sl@0: if (block_graph[i].succ_count || block_graph[i].pred_count) sl@0: { sl@0: if (!header) sl@0: { sl@0: fprintf (stderr, "WARNING: Block graph solved incorrectly for function %s\n", sl@0: func->name); sl@0: fprintf (stderr, " this error reflects a bug in decode-gcov.c or perhaps bogus data\n"); sl@0: header = TRUE; sl@0: } sl@0: fprintf (stderr, " block %d has succ_count = %d pred_count = %d\n", sl@0: i, (int) block_graph[i].succ_count, (int) block_graph[i].pred_count); sl@0: } sl@0: } sl@0: } sl@0: } sl@0: sl@0: static void sl@0: solve_graphs (DBusList **functions) sl@0: { sl@0: DBusList *link; sl@0: sl@0: link = _dbus_list_get_first_link (functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: function_solve_graph (func); sl@0: sl@0: link = _dbus_list_get_next_link (functions, link); sl@0: } sl@0: } sl@0: sl@0: static void sl@0: load_functions_for_c_file (const DBusString *filename, sl@0: DBusList **functions) sl@0: { sl@0: DBusString bbg_filename; sl@0: DBusString da_filename; sl@0: DBusString gcno_filename; sl@0: DBusString gcda_filename; sl@0: DBusString contents; sl@0: DBusString *name; sl@0: DBusError error; sl@0: sl@0: /* With latest gcc it's .gcno instead of .bbg and sl@0: * gcda instead of .da sl@0: */ sl@0: sl@0: dbus_error_init (&error); sl@0: sl@0: if (!_dbus_string_init (&bbg_filename) || sl@0: !_dbus_string_init (&da_filename) || sl@0: !_dbus_string_init (&gcno_filename) || sl@0: !_dbus_string_init (&gcda_filename) || sl@0: !_dbus_string_copy (filename, 0, &bbg_filename, 0) || sl@0: !_dbus_string_copy (filename, 0, &da_filename, 0) || sl@0: !_dbus_string_copy (filename, 0, &gcno_filename, 0) || sl@0: !_dbus_string_copy (filename, 0, &gcda_filename, 0) || sl@0: !_dbus_string_init (&contents)) sl@0: die ("no memory\n"); sl@0: sl@0: _dbus_string_shorten (&bbg_filename, 2); sl@0: _dbus_string_shorten (&da_filename, 2); sl@0: sl@0: if (!_dbus_string_append (&bbg_filename, ".bbg") || sl@0: !_dbus_string_append (&da_filename, ".da") || sl@0: !_dbus_string_append (&bbg_filename, ".gcno") || sl@0: !_dbus_string_append (&bbg_filename, ".gcda")) sl@0: die ("no memory\n"); sl@0: sl@0: if (_dbus_file_exists (_dbus_string_get_const_data (&gcno_filename))) sl@0: name = &gcno_filename; sl@0: else sl@0: name = &bbg_filename; sl@0: sl@0: if (!_dbus_file_get_contents (&contents, name, sl@0: &error)) sl@0: { sl@0: fprintf (stderr, "Could not open file: %s\n", sl@0: error.message); sl@0: exit (1); sl@0: } sl@0: sl@0: get_functions_from_bbg (&contents, functions); sl@0: sl@0: _dbus_string_set_length (&contents, 0); sl@0: sl@0: if (_dbus_file_exists (_dbus_string_get_const_data (&gcda_filename))) sl@0: name = &gcda_filename; sl@0: else sl@0: name = &da_filename; sl@0: sl@0: if (!_dbus_file_get_contents (&contents, name, sl@0: &error)) sl@0: { sl@0: /* Try .libs/file.da */ sl@0: int slash; sl@0: sl@0: if (_dbus_string_find_byte_backward (name, sl@0: _dbus_string_get_length (name), sl@0: '/', sl@0: &slash)) sl@0: { sl@0: DBusString libs; sl@0: _dbus_string_init_const (&libs, "/.libs"); sl@0: sl@0: if (!_dbus_string_copy (&libs, 0, name, slash)) sl@0: die ("no memory"); sl@0: sl@0: dbus_error_free (&error); sl@0: if (!_dbus_file_get_contents (&contents, name, sl@0: &error)) sl@0: { sl@0: fprintf (stderr, "Could not open file: %s\n", sl@0: error.message); sl@0: exit (1); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: fprintf (stderr, "Could not open file: %s\n", sl@0: error.message); sl@0: exit (1); sl@0: } sl@0: } sl@0: sl@0: add_counts_from_da (&contents, functions); sl@0: sl@0: solve_graphs (functions); sl@0: sl@0: _dbus_string_free (&contents); sl@0: _dbus_string_free (&da_filename); sl@0: _dbus_string_free (&bbg_filename); sl@0: } sl@0: sl@0: static void sl@0: get_lines_from_bb_file (const DBusString *contents, sl@0: File *fl) sl@0: { sl@0: int i; sl@0: long val; sl@0: int n_functions; sl@0: dbus_bool_t in_our_file; sl@0: DBusList *link; sl@0: Function *func; sl@0: int block; sl@0: sl@0: #if 0 sl@0: printf ("Getting line numbers for blocks from .bb file\n"); sl@0: #endif sl@0: sl@0: /* There's this "filename" field in the .bb file which sl@0: * mysteriously comes *after* the first function in the sl@0: * file in the .bb file; and every .bb file seems to sl@0: * have only one filename. I don't understand sl@0: * what's going on here, so just set in_our_file = TRUE sl@0: * at the start categorically. sl@0: */ sl@0: sl@0: block = 0; sl@0: func = NULL; sl@0: in_our_file = TRUE; sl@0: link = _dbus_list_get_first_link (&fl->functions); sl@0: n_functions = 0; sl@0: i = 0; sl@0: while (string_get_int (contents, i, &val)) sl@0: { sl@0: i += 4; sl@0: sl@0: switch (val) sl@0: { sl@0: case BB_FILENAME: sl@0: { sl@0: DBusString f; sl@0: sl@0: if (!_dbus_string_init (&f)) sl@0: die ("no memory\n"); sl@0: sl@0: if (string_get_string (contents, i, sl@0: BB_FILENAME, sl@0: &f, &i)) sl@0: { sl@0: /* fl->name is a full path and the filename in .bb is sl@0: * not. sl@0: */ sl@0: DBusString tmp_str; sl@0: sl@0: _dbus_string_init_const (&tmp_str, fl->name); sl@0: sl@0: if (_dbus_string_ends_with_c_str (&tmp_str, sl@0: _dbus_string_get_const_data (&f))) sl@0: in_our_file = TRUE; sl@0: else sl@0: in_our_file = FALSE; sl@0: sl@0: #if 0 sl@0: fprintf (stderr, sl@0: "File %s in .bb, looking for %s, in_our_file = %d\n", sl@0: _dbus_string_get_const_data (&f), sl@0: fl->name, sl@0: in_our_file); sl@0: #endif sl@0: } sl@0: _dbus_string_free (&f); sl@0: } sl@0: break; sl@0: case BB_FUNCTION: sl@0: { sl@0: DBusString f; sl@0: if (!_dbus_string_init (&f)) sl@0: die ("no memory\n"); sl@0: sl@0: if (string_get_string (contents, i, sl@0: BB_FUNCTION, sl@0: &f, &i)) sl@0: { sl@0: #if 0 sl@0: fprintf (stderr, "Function %s\n", _dbus_string_get_const_data (&f)); sl@0: #endif sl@0: sl@0: block = 0; sl@0: sl@0: if (in_our_file) sl@0: { sl@0: if (link == NULL) sl@0: { sl@0: fprintf (stderr, "No function object for function %s\n", sl@0: _dbus_string_get_const_data (&f)); sl@0: } sl@0: else sl@0: { sl@0: func = link->data; sl@0: link = _dbus_list_get_next_link (&fl->functions, link); sl@0: sl@0: if (func->name == NULL) sl@0: { sl@0: if (!_dbus_string_copy_data (&f, &func->name)) sl@0: die ("no memory\n"); sl@0: } sl@0: else sl@0: { sl@0: if (!_dbus_string_equal_c_str (&f, func->name)) sl@0: { sl@0: fprintf (stderr, "got function name \"%s\" (%d) from .bbg file, but \"%s\" (%d) from .bb file\n", sl@0: func->name, strlen (func->name), sl@0: _dbus_string_get_const_data (&f), sl@0: _dbus_string_get_length (&f)); sl@0: sl@0: } sl@0: } sl@0: } sl@0: } sl@0: } sl@0: _dbus_string_free (&f); sl@0: sl@0: n_functions += 1; sl@0: } sl@0: break; sl@0: case BB_ENDOFLIST: sl@0: block += 1; sl@0: break; sl@0: default: sl@0: #if 0 sl@0: fprintf (stderr, "Line %ld\n", val); sl@0: #endif sl@0: sl@0: if (val >= fl->n_lines) sl@0: { sl@0: fprintf (stderr, "Line %ld but file only has %d lines\n", sl@0: val, fl->n_lines); sl@0: } sl@0: else if (func != NULL) sl@0: { sl@0: val -= 1; /* To convert the 1-based line number to 0-based */ sl@0: _dbus_assert (val >= 0); sl@0: sl@0: if (block < func->n_blocks) sl@0: { sl@0: if (!_dbus_list_append (&func->block_graph[block].lines, sl@0: &fl->lines[val])) sl@0: die ("no memory\n"); sl@0: sl@0: sl@0: if (!_dbus_list_append (&fl->lines[val].blocks, sl@0: &func->block_graph[block])) sl@0: die ("no memory\n"); sl@0: } sl@0: else sl@0: { sl@0: fprintf (stderr, "Line number for block %d but function only has %d blocks\n", sl@0: block, func->n_blocks); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: fprintf (stderr, "Line %ld given outside of any function\n", sl@0: val); sl@0: } sl@0: sl@0: break; sl@0: } sl@0: } sl@0: sl@0: #if 0 sl@0: printf ("%d functions in file\n", n_functions); sl@0: #endif sl@0: } sl@0: sl@0: sl@0: static void sl@0: load_block_line_associations (const DBusString *filename, sl@0: File *f) sl@0: { sl@0: DBusString bb_filename; sl@0: DBusString contents; sl@0: DBusError error; sl@0: sl@0: dbus_error_init (&error); sl@0: sl@0: if (!_dbus_string_init (&bb_filename) || sl@0: !_dbus_string_copy (filename, 0, &bb_filename, 0) || sl@0: !_dbus_string_init (&contents)) sl@0: die ("no memory\n"); sl@0: sl@0: _dbus_string_shorten (&bb_filename, 2); sl@0: sl@0: if (!_dbus_string_append (&bb_filename, ".bb")) sl@0: die ("no memory\n"); sl@0: sl@0: if (!_dbus_file_get_contents (&contents, &bb_filename, sl@0: &error)) sl@0: { sl@0: fprintf (stderr, "Could not open file: %s\n", sl@0: error.message); sl@0: exit (1); sl@0: } sl@0: sl@0: get_lines_from_bb_file (&contents, f); sl@0: sl@0: _dbus_string_free (&contents); sl@0: _dbus_string_free (&bb_filename); sl@0: } sl@0: sl@0: static int sl@0: count_lines_in_string (const DBusString *str) sl@0: { sl@0: int n_lines; sl@0: const char *p; sl@0: const char *prev; sl@0: const char *end; sl@0: const char *last_line_end; sl@0: sl@0: #if 0 sl@0: printf ("Counting lines in source file\n"); sl@0: #endif sl@0: sl@0: n_lines = 0; sl@0: prev = NULL; sl@0: p = _dbus_string_get_const_data (str); sl@0: end = p + _dbus_string_get_length (str); sl@0: last_line_end = p; sl@0: while (p != end) sl@0: { sl@0: /* too lazy to handle \r\n as one linebreak */ sl@0: if (*p == '\n' || *p == '\r') sl@0: { sl@0: ++n_lines; sl@0: last_line_end = p + 1; sl@0: } sl@0: sl@0: prev = p; sl@0: ++p; sl@0: } sl@0: sl@0: if (last_line_end != p) sl@0: ++n_lines; sl@0: sl@0: return n_lines; sl@0: } sl@0: sl@0: static void sl@0: fill_line_content (const DBusString *str, sl@0: Line *lines) sl@0: { sl@0: int n_lines; sl@0: const char *p; sl@0: const char *prev; sl@0: const char *end; sl@0: const char *last_line_end; sl@0: sl@0: #if 0 sl@0: printf ("Saving contents of each line in source file\n"); sl@0: #endif sl@0: sl@0: n_lines = 0; sl@0: prev = NULL; sl@0: p = _dbus_string_get_const_data (str); sl@0: end = p + _dbus_string_get_length (str); sl@0: last_line_end = p; sl@0: while (p != end) sl@0: { sl@0: if (*p == '\n' || *p == '\r') sl@0: { sl@0: lines[n_lines].text = dbus_malloc0 (p - last_line_end + 1); sl@0: if (lines[n_lines].text == NULL) sl@0: die ("no memory\n"); sl@0: sl@0: memcpy (lines[n_lines].text, last_line_end, p - last_line_end); sl@0: lines[n_lines].number = n_lines + 1; sl@0: sl@0: ++n_lines; sl@0: sl@0: last_line_end = p + 1; sl@0: } sl@0: sl@0: prev = p; sl@0: ++p; sl@0: } sl@0: sl@0: if (p != last_line_end) sl@0: { sl@0: memcpy (lines[n_lines].text, last_line_end, p - last_line_end); sl@0: ++n_lines; sl@0: } sl@0: } sl@0: sl@0: static void sl@0: mark_inside_dbus_build_tests (File *f) sl@0: { sl@0: int i; sl@0: DBusList *link; sl@0: int inside_depth; sl@0: sl@0: inside_depth = 0; sl@0: i = 0; sl@0: while (i < f->n_lines) sl@0: { sl@0: Line *l = &f->lines[i]; sl@0: dbus_bool_t is_verbose; sl@0: sl@0: is_verbose = strstr (l->text, "_dbus_verbose") != NULL; sl@0: sl@0: if (inside_depth == 0) sl@0: { sl@0: const char *a, *b; sl@0: sl@0: a = strstr (l->text, "#if"); sl@0: b = strstr (l->text, "DBUS_BUILD_TESTS"); sl@0: if (a && b && (a < b)) sl@0: inside_depth += 1; sl@0: } sl@0: else sl@0: { sl@0: if (strstr (l->text, "#if") != NULL) sl@0: inside_depth += 1; sl@0: else if (strstr (l->text, "#endif") != NULL) sl@0: inside_depth -= 1; sl@0: } sl@0: sl@0: if (inside_depth > 0 || is_verbose) sl@0: { sl@0: /* Mark the line and its blocks */ sl@0: DBusList *blink; sl@0: sl@0: l->inside_dbus_build_tests = TRUE; sl@0: sl@0: blink = _dbus_list_get_first_link (&l->blocks); sl@0: while (blink != NULL) sl@0: { sl@0: Block *b = blink->data; sl@0: sl@0: b->inside_dbus_build_tests = TRUE; sl@0: sl@0: blink = _dbus_list_get_next_link (&l->blocks, blink); sl@0: } sl@0: } sl@0: sl@0: ++i; sl@0: } sl@0: sl@0: /* Now mark functions where for all blocks that are associated sl@0: * with a source line, the block is inside_dbus_build_tests. sl@0: */ sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: /* The issue is that some blocks aren't associated with a source line. sl@0: * Assume they are inside/outside tests according to the source sl@0: * line of the preceding block. For the first block, make it sl@0: * match the first following block with a line associated. sl@0: */ sl@0: if (func->block_graph[0].lines == NULL) sl@0: { sl@0: /* find first following line */ sl@0: i = 1; sl@0: while (i < func->n_blocks) sl@0: { sl@0: if (func->block_graph[i].lines != NULL) sl@0: { sl@0: func->block_graph[0].inside_dbus_build_tests = sl@0: func->block_graph[i].inside_dbus_build_tests; sl@0: break; sl@0: } sl@0: sl@0: ++i; sl@0: } sl@0: } sl@0: sl@0: /* Now mark all blocks but the first */ sl@0: i = 1; sl@0: while (i < func->n_blocks) sl@0: { sl@0: if (func->block_graph[i].lines == NULL) sl@0: { sl@0: func->block_graph[i].inside_dbus_build_tests = sl@0: func->block_graph[i-1].inside_dbus_build_tests; sl@0: } sl@0: sl@0: ++i; sl@0: } sl@0: sl@0: i = 0; sl@0: while (i < func->n_blocks) sl@0: { sl@0: /* Break as soon as any block is not a test block */ sl@0: if (func->block_graph[i].lines != NULL && sl@0: !func->block_graph[i].inside_dbus_build_tests) sl@0: break; sl@0: sl@0: ++i; sl@0: } sl@0: sl@0: if (i == func->n_blocks) sl@0: func->inside_dbus_build_tests = TRUE; sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: } sl@0: sl@0: static void sl@0: mark_coverage (File *f) sl@0: { sl@0: int i; sl@0: DBusList *link; sl@0: sl@0: i = 0; sl@0: while (i < f->n_lines) sl@0: { sl@0: Line *l = &f->lines[i]; sl@0: DBusList *blink; sl@0: int n_blocks; sl@0: int n_blocks_executed; sl@0: sl@0: n_blocks = 0; sl@0: n_blocks_executed = 0; sl@0: blink = _dbus_list_get_first_link (&l->blocks); sl@0: while (blink != NULL) sl@0: { sl@0: Block *b = blink->data; sl@0: sl@0: if (b->exec_count > 0) sl@0: n_blocks_executed += 1; sl@0: sl@0: n_blocks += 1; sl@0: sl@0: blink = _dbus_list_get_next_link (&l->blocks, blink); sl@0: } sl@0: sl@0: if (n_blocks_executed > 0 && sl@0: n_blocks_executed < n_blocks) sl@0: l->partial = TRUE; sl@0: sl@0: ++i; sl@0: } sl@0: sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: int i; sl@0: int n_test_blocks; sl@0: int n_test_blocks_executed; sl@0: int n_nontest_blocks; sl@0: int n_nontest_blocks_executed; sl@0: sl@0: n_test_blocks = 0; sl@0: n_test_blocks_executed = 0; sl@0: n_nontest_blocks = 0; sl@0: n_nontest_blocks_executed = 0; sl@0: sl@0: i = 0; sl@0: while (i < func->n_blocks) sl@0: { sl@0: if (!func->block_graph[i].inside_dbus_build_tests) sl@0: { sl@0: n_nontest_blocks += 1; sl@0: sl@0: if (func->block_graph[i].exec_count > 0) sl@0: n_nontest_blocks_executed += 1; sl@0: } sl@0: else sl@0: { sl@0: n_test_blocks += 1; sl@0: sl@0: if (func->block_graph[i].exec_count > 0) sl@0: n_test_blocks_executed += 1; sl@0: } sl@0: sl@0: ++i; sl@0: } sl@0: sl@0: if (n_nontest_blocks_executed > 0 && sl@0: n_nontest_blocks_executed < n_nontest_blocks) sl@0: func->partial = TRUE; sl@0: sl@0: if (n_nontest_blocks_executed == 0 && sl@0: n_nontest_blocks > 0) sl@0: func->unused = TRUE; sl@0: sl@0: func->n_test_blocks = n_test_blocks; sl@0: func->n_test_blocks_executed = n_test_blocks_executed; sl@0: func->n_nontest_blocks = n_nontest_blocks; sl@0: func->n_nontest_blocks_executed = n_nontest_blocks_executed; sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: } sl@0: sl@0: static File* sl@0: load_c_file (const DBusString *filename) sl@0: { sl@0: DBusString contents; sl@0: DBusError error; sl@0: File *f; sl@0: sl@0: f = dbus_new0 (File, 1); sl@0: if (f == NULL) sl@0: die ("no memory\n"); sl@0: sl@0: if (!_dbus_string_copy_data (filename, &f->name)) sl@0: die ("no memory\n"); sl@0: sl@0: if (!_dbus_string_init (&contents)) sl@0: die ("no memory\n"); sl@0: sl@0: dbus_error_init (&error); sl@0: sl@0: if (!_dbus_file_get_contents (&contents, filename, sl@0: &error)) sl@0: { sl@0: fprintf (stderr, "Could not open file: %s\n", sl@0: error.message); sl@0: dbus_error_free (&error); sl@0: exit (1); sl@0: } sl@0: sl@0: load_functions_for_c_file (filename, &f->functions); sl@0: sl@0: f->n_lines = count_lines_in_string (&contents); sl@0: f->lines = dbus_new0 (Line, f->n_lines); sl@0: if (f->lines == NULL) sl@0: die ("no memory\n"); sl@0: sl@0: fill_line_content (&contents, f->lines); sl@0: sl@0: _dbus_string_free (&contents); sl@0: sl@0: load_block_line_associations (filename, f); sl@0: sl@0: mark_inside_dbus_build_tests (f); sl@0: mark_coverage (f); sl@0: sl@0: return f; sl@0: } sl@0: sl@0: typedef struct Stats Stats; sl@0: sl@0: struct Stats sl@0: { sl@0: int n_blocks; sl@0: int n_blocks_executed; sl@0: int n_blocks_inside_dbus_build_tests; sl@0: sl@0: int n_lines; /* lines that have blocks on them */ sl@0: int n_lines_executed; sl@0: int n_lines_partial; sl@0: int n_lines_inside_dbus_build_tests; sl@0: sl@0: int n_functions; sl@0: int n_functions_executed; sl@0: int n_functions_partial; sl@0: int n_functions_inside_dbus_build_tests; sl@0: }; sl@0: sl@0: static dbus_bool_t sl@0: line_was_executed (Line *l) sl@0: { sl@0: DBusList *link; sl@0: sl@0: link = _dbus_list_get_first_link (&l->blocks); sl@0: while (link != NULL) sl@0: { sl@0: Block *b = link->data; sl@0: sl@0: if (b->exec_count > 0) sl@0: return TRUE; sl@0: sl@0: link = _dbus_list_get_next_link (&l->blocks, link); sl@0: } sl@0: sl@0: return FALSE; sl@0: } sl@0: sl@0: sl@0: static int sl@0: line_exec_count (Line *l) sl@0: { sl@0: DBusList *link; sl@0: dbus_int64_t total; sl@0: sl@0: total = 0; sl@0: link = _dbus_list_get_first_link (&l->blocks); sl@0: while (link != NULL) sl@0: { sl@0: Block *b = link->data; sl@0: sl@0: total += b->exec_count; sl@0: sl@0: link = _dbus_list_get_next_link (&l->blocks, link); sl@0: } sl@0: sl@0: return total; sl@0: } sl@0: sl@0: static void sl@0: merge_stats_for_file (Stats *stats, sl@0: File *f) sl@0: { sl@0: int i; sl@0: DBusList *link; sl@0: sl@0: for (i = 0; i < f->n_lines; ++i) sl@0: { sl@0: Line *l = &f->lines[i]; sl@0: sl@0: if (l->inside_dbus_build_tests) sl@0: { sl@0: stats->n_lines_inside_dbus_build_tests += 1; sl@0: continue; sl@0: } sl@0: sl@0: if (line_was_executed (l)) sl@0: stats->n_lines_executed += 1; sl@0: sl@0: if (l->blocks != NULL) sl@0: stats->n_lines += 1; sl@0: sl@0: if (l->partial) sl@0: stats->n_lines_partial += 1; sl@0: } sl@0: sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: if (func->inside_dbus_build_tests) sl@0: stats->n_functions_inside_dbus_build_tests += 1; sl@0: else sl@0: { sl@0: stats->n_functions += 1; sl@0: sl@0: if (!func->unused) sl@0: stats->n_functions_executed += 1; sl@0: sl@0: if (func->partial) sl@0: stats->n_functions_partial += 1; sl@0: } sl@0: sl@0: stats->n_blocks_inside_dbus_build_tests += sl@0: func->n_test_blocks; sl@0: sl@0: stats->n_blocks_executed += sl@0: func->n_nontest_blocks_executed; sl@0: sl@0: stats->n_blocks += sl@0: func->n_nontest_blocks; sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: } sl@0: sl@0: /* The output of this matches gcov exactly ("diff" shows no difference) */ sl@0: static void sl@0: print_annotated_source_gcov_format (File *f) sl@0: { sl@0: int i; sl@0: sl@0: i = 0; sl@0: while (i < f->n_lines) sl@0: { sl@0: Line *l = &f->lines[i]; sl@0: sl@0: if (l->blocks != NULL) sl@0: { sl@0: int exec_count; sl@0: sl@0: exec_count = line_exec_count (l); sl@0: sl@0: if (exec_count > 0) sl@0: printf ("%12d %s\n", sl@0: exec_count, l->text); sl@0: else sl@0: printf (" ###### %s\n", l->text); sl@0: } sl@0: else sl@0: { sl@0: printf ("\t\t%s\n", l->text); sl@0: } sl@0: sl@0: ++i; sl@0: } sl@0: } sl@0: sl@0: static void sl@0: print_annotated_source (File *f) sl@0: { sl@0: int i; sl@0: sl@0: i = 0; sl@0: while (i < f->n_lines) sl@0: { sl@0: Line *l = &f->lines[i]; sl@0: sl@0: if (l->inside_dbus_build_tests) sl@0: printf ("*"); sl@0: else sl@0: printf (" "); sl@0: sl@0: if (l->blocks != NULL) sl@0: { sl@0: int exec_count; sl@0: sl@0: exec_count = line_exec_count (l); sl@0: sl@0: if (exec_count > 0) sl@0: printf ("%12d %s\n", sl@0: exec_count, l->text); sl@0: else sl@0: printf (" ###### %s\n", l->text); sl@0: } sl@0: else sl@0: { sl@0: printf ("\t\t%s\n", l->text); sl@0: } sl@0: sl@0: ++i; sl@0: } sl@0: } sl@0: sl@0: static void sl@0: print_block_superdetails (File *f) sl@0: { sl@0: DBusList *link; sl@0: int i; sl@0: sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: printf ("=== %s():\n", func->name); sl@0: sl@0: i = 0; sl@0: while (i < func->n_blocks) sl@0: { sl@0: Block *b = &func->block_graph[i]; sl@0: DBusList *l; sl@0: sl@0: printf (" %5d executed %d times%s\n", i, sl@0: (int) b->exec_count, sl@0: b->inside_dbus_build_tests ? sl@0: " [inside DBUS_BUILD_TESTS]" : ""); sl@0: sl@0: l = _dbus_list_get_first_link (&b->lines); sl@0: while (l != NULL) sl@0: { sl@0: Line *line = l->data; sl@0: sl@0: printf ("4%d\t%s\n", line->number, line->text); sl@0: sl@0: l = _dbus_list_get_next_link (&b->lines, l); sl@0: } sl@0: sl@0: ++i; sl@0: } sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: } sl@0: sl@0: static void sl@0: print_one_file (const DBusString *filename) sl@0: { sl@0: if (_dbus_string_ends_with_c_str (filename, ".bb")) sl@0: { sl@0: DBusString contents; sl@0: DBusError error; sl@0: sl@0: if (!_dbus_string_init (&contents)) sl@0: die ("no memory\n"); sl@0: sl@0: dbus_error_init (&error); sl@0: sl@0: if (!_dbus_file_get_contents (&contents, filename, sl@0: &error)) sl@0: { sl@0: fprintf (stderr, "Could not open file: %s\n", sl@0: error.message); sl@0: dbus_error_free (&error); sl@0: exit (1); sl@0: } sl@0: sl@0: dump_bb_file (&contents); sl@0: sl@0: _dbus_string_free (&contents); sl@0: } sl@0: else if (_dbus_string_ends_with_c_str (filename, ".bbg")) sl@0: { sl@0: DBusString contents; sl@0: DBusError error; sl@0: sl@0: if (!_dbus_string_init (&contents)) sl@0: die ("no memory\n"); sl@0: sl@0: dbus_error_init (&error); sl@0: sl@0: if (!_dbus_file_get_contents (&contents, filename, sl@0: &error)) sl@0: { sl@0: fprintf (stderr, "Could not open file: %s\n", sl@0: error.message); sl@0: dbus_error_free (&error); sl@0: exit (1); sl@0: } sl@0: sl@0: dump_bbg_file (&contents); sl@0: sl@0: _dbus_string_free (&contents); sl@0: } sl@0: else if (_dbus_string_ends_with_c_str (filename, ".da")) sl@0: { sl@0: DBusString contents; sl@0: DBusError error; sl@0: sl@0: if (!_dbus_string_init (&contents)) sl@0: die ("no memory\n"); sl@0: sl@0: dbus_error_init (&error); sl@0: sl@0: if (!_dbus_file_get_contents (&contents, filename, sl@0: &error)) sl@0: { sl@0: fprintf (stderr, "Could not open file: %s\n", sl@0: error.message); sl@0: dbus_error_free (&error); sl@0: exit (1); sl@0: } sl@0: sl@0: dump_da_file (&contents); sl@0: sl@0: _dbus_string_free (&contents); sl@0: } sl@0: else if (_dbus_string_ends_with_c_str (filename, ".c")) sl@0: { sl@0: File *f; sl@0: sl@0: f = load_c_file (filename); sl@0: sl@0: print_annotated_source (f); sl@0: } sl@0: else sl@0: { sl@0: fprintf (stderr, "Unknown file type %s\n", sl@0: _dbus_string_get_const_data (filename)); sl@0: exit (1); sl@0: } sl@0: } sl@0: sl@0: static void sl@0: print_untested_functions (File *f) sl@0: { sl@0: DBusList *link; sl@0: dbus_bool_t found; sl@0: sl@0: found = FALSE; sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: if (func->unused && sl@0: !func->inside_dbus_build_tests) sl@0: found = TRUE; sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: sl@0: if (!found) sl@0: return; sl@0: sl@0: printf ("Untested functions in %s\n", f->name); sl@0: printf ("=======\n"); sl@0: sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: if (func->unused && sl@0: !func->inside_dbus_build_tests) sl@0: printf (" %s\n", func->name); sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: sl@0: printf ("\n"); sl@0: } sl@0: sl@0: static void sl@0: print_poorly_tested_functions (File *f, sl@0: Stats *stats) sl@0: { sl@0: DBusList *link; sl@0: dbus_bool_t found; sl@0: sl@0: #define TEST_FRACTION(function) ((function)->n_nontest_blocks_executed / (double) (function)->n_nontest_blocks) sl@0: sl@0: #define AVERAGE_COVERAGE ((stats)->n_blocks_executed / (double) (stats)->n_blocks) sl@0: sl@0: #define POORLY_TESTED(function) (!(function)->unused && \ sl@0: (function)->n_nontest_blocks > 0 && \ sl@0: TEST_FRACTION (function) < AVERAGE_COVERAGE) sl@0: sl@0: found = FALSE; sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: if (POORLY_TESTED (func)) sl@0: found = TRUE; sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: sl@0: if (!found) sl@0: return; sl@0: sl@0: printf ("Below average functions in %s\n", f->name); sl@0: printf ("=======\n"); sl@0: sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: if (POORLY_TESTED (func)) sl@0: printf (" %s (%d%%)\n", func->name, sl@0: (int) (TEST_FRACTION (func) * 100)); sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: sl@0: printf ("\n"); sl@0: } sl@0: sl@0: static int sl@0: func_cmp (const void *a, sl@0: const void *b) sl@0: { sl@0: Function *af = *(Function**) a; sl@0: Function *bf = *(Function**) b; sl@0: int a_untested = af->n_nontest_blocks - af->n_nontest_blocks_executed; sl@0: int b_untested = bf->n_nontest_blocks - bf->n_nontest_blocks_executed; sl@0: sl@0: /* Sort by number of untested blocks */ sl@0: return b_untested - a_untested; sl@0: } sl@0: sl@0: static void sl@0: print_n_untested_blocks_by_function (File *f, sl@0: Stats *stats) sl@0: { sl@0: DBusList *link; sl@0: Function **funcs; sl@0: int n_found; sl@0: int i; sl@0: sl@0: n_found = 0; sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: if (func->n_nontest_blocks_executed < sl@0: func->n_nontest_blocks) sl@0: n_found += 1; sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: sl@0: if (n_found == 0) sl@0: return; sl@0: sl@0: /* make an array so we can use qsort */ sl@0: sl@0: funcs = dbus_new (Function*, n_found); sl@0: if (funcs == NULL) sl@0: return; sl@0: sl@0: i = 0; sl@0: link = _dbus_list_get_first_link (&f->functions); sl@0: while (link != NULL) sl@0: { sl@0: Function *func = link->data; sl@0: sl@0: if (func->n_nontest_blocks_executed < sl@0: func->n_nontest_blocks) sl@0: { sl@0: funcs[i] = func; sl@0: ++i; sl@0: } sl@0: sl@0: link = _dbus_list_get_next_link (&f->functions, link); sl@0: } sl@0: sl@0: _dbus_assert (i == n_found); sl@0: sl@0: qsort (funcs, n_found, sizeof (Function*), sl@0: func_cmp); sl@0: sl@0: printf ("Incomplete functions in %s\n", f->name); sl@0: printf ("=======\n"); sl@0: sl@0: i = 0; sl@0: while (i < n_found) sl@0: { sl@0: Function *func = funcs[i]; sl@0: sl@0: printf (" %s (%d/%d untested blocks)\n", sl@0: func->name, sl@0: func->n_nontest_blocks - func->n_nontest_blocks_executed, sl@0: func->n_nontest_blocks); sl@0: sl@0: ++i; sl@0: } sl@0: sl@0: dbus_free (funcs); sl@0: sl@0: printf ("\n"); sl@0: } sl@0: sl@0: static void sl@0: print_stats (Stats *stats, sl@0: const char *of_what) sl@0: { sl@0: int completely; sl@0: sl@0: printf ("Summary (%s)\n", of_what); sl@0: printf ("=======\n"); sl@0: printf (" %g%% blocks executed (%d of %d)\n", sl@0: (stats->n_blocks_executed / (double) stats->n_blocks) * 100.0, sl@0: stats->n_blocks_executed, sl@0: stats->n_blocks); sl@0: sl@0: printf (" (ignored %d blocks of test-only/debug-only code)\n", sl@0: stats->n_blocks_inside_dbus_build_tests); sl@0: sl@0: printf (" %g%% functions executed (%d of %d)\n", sl@0: (stats->n_functions_executed / (double) stats->n_functions) * 100.0, sl@0: stats->n_functions_executed, sl@0: stats->n_functions); sl@0: sl@0: completely = stats->n_functions_executed - stats->n_functions_partial; sl@0: printf (" %g%% functions completely executed (%d of %d)\n", sl@0: (completely / (double) stats->n_functions) * 100.0, sl@0: completely, sl@0: stats->n_functions); sl@0: sl@0: printf (" (ignored %d functions of test-only/debug-only code)\n", sl@0: stats->n_functions_inside_dbus_build_tests); sl@0: sl@0: printf (" %g%% lines executed (%d of %d)\n", sl@0: (stats->n_lines_executed / (double) stats->n_lines) * 100.0, sl@0: stats->n_lines_executed, sl@0: stats->n_lines); sl@0: sl@0: completely = stats->n_lines_executed - stats->n_lines_partial; sl@0: printf (" %g%% lines completely executed (%d of %d)\n", sl@0: (completely / (double) stats->n_lines) * 100.0, sl@0: completely, sl@0: stats->n_lines); sl@0: sl@0: printf (" (ignored %d lines of test-only/debug-only code)\n", sl@0: stats->n_lines_inside_dbus_build_tests); sl@0: sl@0: printf ("\n"); sl@0: } sl@0: sl@0: typedef enum sl@0: { sl@0: MODE_PRINT, sl@0: MODE_REPORT, sl@0: MODE_BLOCKS, sl@0: MODE_GCOV sl@0: } Mode; sl@0: sl@0: int sl@0: main (int argc, char **argv) sl@0: { sl@0: DBusString filename; sl@0: int i; sl@0: Mode m; sl@0: sl@0: if (argc < 2) sl@0: { sl@0: fprintf (stderr, "Must specify files on command line\n"); sl@0: return 1; sl@0: } sl@0: sl@0: m = MODE_PRINT; sl@0: i = 1; sl@0: sl@0: if (strcmp (argv[i], "--report") == 0) sl@0: { sl@0: m = MODE_REPORT; sl@0: ++i; sl@0: } sl@0: else if (strcmp (argv[i], "--blocks") == 0) sl@0: { sl@0: m = MODE_BLOCKS; sl@0: ++i; sl@0: } sl@0: else if (strcmp (argv[i], "--gcov") == 0) sl@0: { sl@0: m = MODE_GCOV; sl@0: ++i; sl@0: } sl@0: sl@0: sl@0: if (i == argc) sl@0: { sl@0: fprintf (stderr, "Must specify files on command line\n"); sl@0: return 1; sl@0: } sl@0: sl@0: if (m == MODE_PRINT) sl@0: { sl@0: while (i < argc) sl@0: { sl@0: _dbus_string_init_const (&filename, argv[i]); sl@0: sl@0: print_one_file (&filename); sl@0: sl@0: ++i; sl@0: } sl@0: } sl@0: else if (m == MODE_BLOCKS || m == MODE_GCOV) sl@0: { sl@0: while (i < argc) sl@0: { sl@0: File *f; sl@0: sl@0: _dbus_string_init_const (&filename, argv[i]); sl@0: sl@0: f = load_c_file (&filename); sl@0: sl@0: if (m == MODE_BLOCKS) sl@0: print_block_superdetails (f); sl@0: else if (m == MODE_GCOV) sl@0: print_annotated_source_gcov_format (f); sl@0: sl@0: ++i; sl@0: } sl@0: } sl@0: else if (m == MODE_REPORT) sl@0: { sl@0: Stats stats = { 0, }; sl@0: DBusList *files; sl@0: DBusList *link; sl@0: DBusHashTable *stats_by_dir; sl@0: DBusHashIter iter; sl@0: sl@0: files = NULL; sl@0: while (i < argc) sl@0: { sl@0: _dbus_string_init_const (&filename, argv[i]); sl@0: sl@0: if (_dbus_string_ends_with_c_str (&filename, ".c")) sl@0: { sl@0: File *f; sl@0: sl@0: f = load_c_file (&filename); sl@0: sl@0: if (!_dbus_list_append (&files, f)) sl@0: die ("no memory\n"); sl@0: } sl@0: else sl@0: { sl@0: fprintf (stderr, "Unknown file type %s\n", sl@0: _dbus_string_get_const_data (&filename)); sl@0: exit (1); sl@0: } sl@0: sl@0: ++i; sl@0: } sl@0: sl@0: link = _dbus_list_get_first_link (&files); sl@0: while (link != NULL) sl@0: { sl@0: File *f = link->data; sl@0: sl@0: merge_stats_for_file (&stats, f); sl@0: sl@0: link = _dbus_list_get_next_link (&files, link); sl@0: } sl@0: sl@0: print_stats (&stats, "all files"); sl@0: sl@0: stats_by_dir = _dbus_hash_table_new (DBUS_HASH_STRING, sl@0: dbus_free, dbus_free); sl@0: sl@0: link = _dbus_list_get_first_link (&files); sl@0: while (link != NULL) sl@0: { sl@0: File *f = link->data; sl@0: DBusString dirname; sl@0: char *dirname_c; sl@0: Stats *dir_stats; sl@0: sl@0: _dbus_string_init_const (&filename, f->name); sl@0: sl@0: if (!_dbus_string_init (&dirname)) sl@0: die ("no memory\n"); sl@0: sl@0: if (!_dbus_string_get_dirname (&filename, &dirname) || sl@0: !_dbus_string_copy_data (&dirname, &dirname_c)) sl@0: die ("no memory\n"); sl@0: sl@0: dir_stats = _dbus_hash_table_lookup_string (stats_by_dir, sl@0: dirname_c); sl@0: sl@0: if (dir_stats == NULL) sl@0: { sl@0: dir_stats = dbus_new0 (Stats, 1); sl@0: if (!_dbus_hash_table_insert_string (stats_by_dir, dirname_c, sl@0: dir_stats)) sl@0: die ("no memory\n"); sl@0: } sl@0: else sl@0: dbus_free (dirname_c); sl@0: sl@0: merge_stats_for_file (dir_stats, f); sl@0: sl@0: link = _dbus_list_get_next_link (&files, link); sl@0: } sl@0: sl@0: _dbus_hash_iter_init (stats_by_dir, &iter); sl@0: while (_dbus_hash_iter_next (&iter)) sl@0: { sl@0: const char *dirname = _dbus_hash_iter_get_string_key (&iter); sl@0: Stats *dir_stats = _dbus_hash_iter_get_value (&iter); sl@0: sl@0: print_stats (dir_stats, dirname); sl@0: } sl@0: sl@0: _dbus_hash_table_unref (stats_by_dir); sl@0: sl@0: link = _dbus_list_get_first_link (&files); sl@0: while (link != NULL) sl@0: { sl@0: File *f = link->data; sl@0: sl@0: print_untested_functions (f); sl@0: sl@0: link = _dbus_list_get_next_link (&files, link); sl@0: } sl@0: sl@0: link = _dbus_list_get_first_link (&files); sl@0: while (link != NULL) sl@0: { sl@0: File *f = link->data; sl@0: sl@0: print_poorly_tested_functions (f, &stats); sl@0: sl@0: link = _dbus_list_get_next_link (&files, link); sl@0: } sl@0: sl@0: link = _dbus_list_get_first_link (&files); sl@0: while (link != NULL) sl@0: { sl@0: File *f = link->data; sl@0: sl@0: print_n_untested_blocks_by_function (f, &stats); sl@0: sl@0: link = _dbus_list_get_next_link (&files, link); sl@0: } sl@0: } sl@0: sl@0: return 0; sl@0: }