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