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