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