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