Author: njn
Date: 2008-02-03 22:59:08 +0000 (Sun, 03 Feb 2008)
New Revision: 7370

Log:
Added code for VCov.  One minor change to the core: I added some code to
iterate through debuginfo Locs and inspect some of their attributes.

Added:
   branches/VCOV/exp-vcov/
   branches/VCOV/exp-vcov/Makefile.am
   branches/VCOV/exp-vcov/docs/
   branches/VCOV/exp-vcov/docs/Makefile.am
   branches/VCOV/exp-vcov/docs/vc-manual.xml
   branches/VCOV/exp-vcov/tests/
   branches/VCOV/exp-vcov/tests/Makefile.am
   branches/VCOV/exp-vcov/tests/filter_stderr
   branches/VCOV/exp-vcov/tests/true.stderr.exp
   branches/VCOV/exp-vcov/tests/true.stderr.out
   branches/VCOV/exp-vcov/tests/true.vgtest
   branches/VCOV/exp-vcov/vc_annotate
   branches/VCOV/exp-vcov/vc_main.c
Modified:
   branches/VCOV/Makefile.am
   branches/VCOV/configure.in
   branches/VCOV/coregrind/m_debuginfo/debuginfo.c
   branches/VCOV/include/pub_tool_debuginfo.h


Modified: branches/VCOV/Makefile.am
===================================================================
--- branches/VCOV/Makefile.am   2008-02-03 22:35:58 UTC (rev 7369)
+++ branches/VCOV/Makefile.am   2008-02-03 22:59:08 UTC (rev 7370)
@@ -12,7 +12,8 @@
                helgrind
 
 EXP_TOOLS =    exp-omega \
-               exp-drd
+               exp-drd \
+               exp-vcov
 
 # Put docs last because building the HTML is slow and we want to get
 # everything else working before we try it.

Modified: branches/VCOV/configure.in
===================================================================
--- branches/VCOV/configure.in  2008-02-03 22:35:58 UTC (rev 7369)
+++ branches/VCOV/configure.in  2008-02-03 22:59:08 UTC (rev 7370)
@@ -1027,6 +1027,9 @@
    exp-drd/Makefile
    exp-drd/docs/Makefile
    exp-drd/tests/Makefile
+   exp-vcov/Makefile
+   exp-vcov/tests/Makefile
+   exp-vcov/docs/Makefile
 ) 
 
 cat<<EOF

Modified: branches/VCOV/coregrind/m_debuginfo/debuginfo.c
===================================================================
--- branches/VCOV/coregrind/m_debuginfo/debuginfo.c     2008-02-03 22:35:58 UTC 
(rev 7369)
+++ branches/VCOV/coregrind/m_debuginfo/debuginfo.c     2008-02-03 22:59:08 UTC 
(rev 7370)
@@ -478,6 +478,66 @@
 /*------------------------------------------------------------*/
 
 /*------------------------------------------------------------*/
+/*--- Iterating through SegInfos                           ---*/
+/*------------------------------------------------------------*/
+
+UInt VG_(seginfo_num_locs) ( const SegInfo *si )
+{
+   return si->loctab_used;
+}
+
+Bool VG_(seginfo_locN_addr)( const SegInfo *si, UInt n, Addr* addr )
+{
+   if (n < 0 || n > si->loctab_used)
+      return False;
+   else {
+      *addr = si->loctab[n].addr;
+      return True;
+   }
+}
+
+Bool VG_(seginfo_locN_size)( const SegInfo *si, UInt n, UInt* size)
+{
+   if (n < 0 || n > si->loctab_used)
+      return False;
+   else {
+      *size = si->loctab[n].size;
+      return True;
+   }
+}
+
+Bool VG_(seginfo_locN_line)( const SegInfo *si, UInt n, UInt* line)
+{
+   if (n < 0 || n > si->loctab_used)
+      return False;
+   else {
+      *line = si->loctab[n].lineno;
+      return True;
+   }
+}
+
+Bool VG_(seginfo_locN_filename)( const SegInfo *si, UInt n, Char** filename)
+{
+   if (n < 0 || n > si->loctab_used)
+      return False;
+   else {
+      *filename = si->loctab[n].filename;
+      return True;
+   }
+}
+
+Bool VG_(seginfo_locN_dirname)( const SegInfo *si, UInt n, Char** dirname)
+{
+   if (n < 0 || n > si->loctab_used)
+      return False;
+   else {
+      *dirname = si->loctab[n].dirname;
+      return True;
+   }
+}
+
+
+/*------------------------------------------------------------*/
 /*--- Use of symbol table & location info to create        ---*/
 /*--- plausible-looking stack dumps.                       ---*/
 /*------------------------------------------------------------*/

Added: branches/VCOV/exp-vcov/Makefile.am
===================================================================
--- branches/VCOV/exp-vcov/Makefile.am                          (rev 0)
+++ branches/VCOV/exp-vcov/Makefile.am  2008-02-03 22:59:08 UTC (rev 7370)
@@ -0,0 +1,68 @@
+include $(top_srcdir)/Makefile.tool.am
+
+bin_SCRIPTS = vc_annotate
+
+noinst_PROGRAMS = 
+if VGP_X86_LINUX
+noinst_PROGRAMS += exp-vcov-x86-linux
+endif
+if VGP_AMD64_LINUX
+noinst_PROGRAMS += exp-vcov-amd64-linux
+endif
+if VGP_PPC32_LINUX
+noinst_PROGRAMS += exp-vcov-ppc32-linux
+endif
+if VGP_PPC64_LINUX
+noinst_PROGRAMS += exp-vcov-ppc64-linux
+endif
+if VGP_PPC32_AIX5
+noinst_PROGRAMS += exp-vcov-ppc32-aix5
+endif
+if VGP_PPC64_AIX5
+noinst_PROGRAMS += exp-vcov-ppc64-aix5
+endif
+
+VCOV_SOURCES_COMMON = vc_main.c
+
+exp_vcov_x86_linux_SOURCES      = $(VCOV_SOURCES_COMMON)
+exp_vcov_x86_linux_CPPFLAGS     = $(AM_CPPFLAGS_X86_LINUX)
+exp_vcov_x86_linux_CFLAGS       = $(AM_CFLAGS_X86_LINUX)
+exp_vcov_x86_linux_DEPENDENCIES = $(COREGRIND_LIBS_X86_LINUX)
+exp_vcov_x86_linux_LDADD        = $(TOOL_LDADD_X86_LINUX)
+exp_vcov_x86_linux_LDFLAGS      = $(TOOL_LDFLAGS_X86_LINUX)
+
+exp_vcov_amd64_linux_SOURCES      = $(VCOV_SOURCES_COMMON)
+exp_vcov_amd64_linux_CPPFLAGS     = $(AM_CPPFLAGS_AMD64_LINUX)
+exp_vcov_amd64_linux_CFLAGS       = $(AM_CFLAGS_AMD64_LINUX)
+exp_vcov_amd64_linux_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_LINUX)
+exp_vcov_amd64_linux_LDADD        = $(TOOL_LDADD_AMD64_LINUX)
+exp_vcov_amd64_linux_LDFLAGS      = $(TOOL_LDFLAGS_AMD64_LINUX)
+
+exp_vcov_ppc32_linux_SOURCES      = $(VCOV_SOURCES_COMMON)
+exp_vcov_ppc32_linux_CPPFLAGS     = $(AM_CPPFLAGS_PPC32_LINUX)
+exp_vcov_ppc32_linux_CFLAGS       = $(AM_CFLAGS_PPC32_LINUX)
+exp_vcov_ppc32_linux_DEPENDENCIES = $(COREGRIND_LIBS_PPC32_LINUX)
+exp_vcov_ppc32_linux_LDADD        = $(TOOL_LDADD_PPC32_LINUX)
+exp_vcov_ppc32_linux_LDFLAGS      = $(TOOL_LDFLAGS_PPC32_LINUX)
+
+exp_vcov_ppc64_linux_SOURCES      = $(VCOV_SOURCES_COMMON)
+exp_vcov_ppc64_linux_CPPFLAGS     = $(AM_CPPFLAGS_PPC64_LINUX)
+exp_vcov_ppc64_linux_CFLAGS       = $(AM_CFLAGS_PPC64_LINUX)
+exp_vcov_ppc64_linux_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_LINUX)
+exp_vcov_ppc64_linux_LDADD        = $(TOOL_LDADD_PPC64_LINUX)
+exp_vcov_ppc64_linux_LDFLAGS      = $(TOOL_LDFLAGS_PPC64_LINUX)
+
+exp_vcov_ppc32_aix5_SOURCES      = $(VCOV_SOURCES_COMMON)
+exp_vcov_ppc32_aix5_CPPFLAGS     = $(AM_CPPFLAGS_PPC32_AIX5)
+exp_vcov_ppc32_aix5_CFLAGS       = $(AM_CFLAGS_PPC32_AIX5)
+exp_vcov_ppc32_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC32_AIX5)
+exp_vcov_ppc32_aix5_LDADD        = $(TOOL_LDADD_PPC32_AIX5)
+exp_vcov_ppc32_aix5_LDFLAGS      = $(TOOL_LDFLAGS_PPC32_AIX5)
+
+exp_vcov_ppc64_aix5_SOURCES      = $(VCOV_SOURCES_COMMON)
+exp_vcov_ppc64_aix5_CPPFLAGS     = $(AM_CPPFLAGS_PPC64_AIX5)
+exp_vcov_ppc64_aix5_CFLAGS       = $(AM_CFLAGS_PPC64_AIX5)
+exp_vcov_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5)
+exp_vcov_ppc64_aix5_LDADD        = $(TOOL_LDADD_PPC64_AIX5)
+exp_vcov_ppc64_aix5_LDFLAGS      = $(TOOL_LDFLAGS_PPC64_AIX5)
+

Added: branches/VCOV/exp-vcov/docs/Makefile.am
===================================================================
--- branches/VCOV/exp-vcov/docs/Makefile.am                             (rev 0)
+++ branches/VCOV/exp-vcov/docs/Makefile.am     2008-02-03 22:59:08 UTC (rev 
7370)
@@ -0,0 +1 @@
+EXTRA_DIST = vc-manual.xml

Added: branches/VCOV/exp-vcov/docs/vc-manual.xml
===================================================================
--- branches/VCOV/exp-vcov/docs/vc-manual.xml                           (rev 0)
+++ branches/VCOV/exp-vcov/docs/vc-manual.xml   2008-02-03 22:59:08 UTC (rev 
7370)
@@ -0,0 +1,55 @@
+<?xml version="1.0"?> <!-- -*- sgml -*- -->
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd";>
+
+<chapter id="vc-manual" xreflabel="VCov">
+
+<title>VCov: a coverage testing tool</title>
+
+<para>VCov is a coverage testing tool.  It works quite like gcov,
+but has the big advantage that you don't have to recompile your program in
+order to use it.</para>
+
+<para>Things to cover here:</para>
+
+<orderedlist>
+
+ <listitem>
+  <para>What coverage is good for</para>
+ </listitem>
+
+ <listitem>
+  <para>Different kinds: line vs branch/decision vs path.
+        "Code Coverage Analysis" by Steve Cornett is a good explanation of
+        these basic kinds and other kinds 
(http://www.bullseye.com/coverage.html).
+  </para>
+ </listitem>
+
+ <listitem>
+  <para>Stuff about debug info, compiling without optimisation</para>
+ </listitem>
+
+ <listitem>
+  <para>What coverage is good for</para>
+ </listitem>
+
+ <listitem>
+  <para>Gives instruction execution counts, not line execution counts.
+  Implications of this, inaccuracies.</para>
+ </listitem>
+
+ <listitem>
+  <para>Command line options</para>
+ </listitem>
+
+ <listitem>
+  <para>Created data files</para>
+ </listitem>
+
+ <listitem>
+  <para>using vc_annotate, .vcov files</para>
+ </listitem>
+
+</orderedlist>
+
+</chapter>

Added: branches/VCOV/exp-vcov/tests/Makefile.am
===================================================================
--- branches/VCOV/exp-vcov/tests/Makefile.am                            (rev 0)
+++ branches/VCOV/exp-vcov/tests/Makefile.am    2008-02-03 22:59:08 UTC (rev 
7370)
@@ -0,0 +1,5 @@
+noinst_SCRIPTS = filter_stderr
+
+EXTRA_DIST = $(noinst_SCRIPTS) \
+        true.stderr.exp true.vgtest
+

Added: branches/VCOV/exp-vcov/tests/filter_stderr
===================================================================
--- branches/VCOV/exp-vcov/tests/filter_stderr                          (rev 0)
+++ branches/VCOV/exp-vcov/tests/filter_stderr  2008-02-03 22:59:08 UTC (rev 
7370)
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+dir=`dirname $0`
+
+$dir/../../tests/filter_stderr_basic                |
+
+# Remove "VCov, ..." line and the following copyright line.
+sed "/^VCov, a coverage testing tool./ , /./ d"
+

Added: branches/VCOV/exp-vcov/tests/true.stderr.exp
===================================================================
--- branches/VCOV/exp-vcov/tests/true.stderr.exp                                
(rev 0)
+++ branches/VCOV/exp-vcov/tests/true.stderr.exp        2008-02-03 22:59:08 UTC 
(rev 7370)
@@ -0,0 +1,2 @@
+
+

Added: branches/VCOV/exp-vcov/tests/true.stderr.out
===================================================================
--- branches/VCOV/exp-vcov/tests/true.stderr.out                                
(rev 0)
+++ branches/VCOV/exp-vcov/tests/true.stderr.out        2008-02-03 22:59:08 UTC 
(rev 7370)
@@ -0,0 +1,15 @@
+
+0x8048350, true.c, 2
+0x8048351, true.c, 2
+0x8048353, true.c, 2
+0x8048356, true.c, 2
+0x8048359, true.c, 2
+0x804835E, true.c, 2
+0x8048361, true.c, 2
+0x8048364, true.c, 2
+0x8048367, true.c, 2
+0x804836A, true.c, 2
+0x804836C, true.c, 3
+0x8048371, true.c, 4
+0x8048372, true.c, 4
+

Added: branches/VCOV/exp-vcov/tests/true.vgtest
===================================================================
--- branches/VCOV/exp-vcov/tests/true.vgtest                            (rev 0)
+++ branches/VCOV/exp-vcov/tests/true.vgtest    2008-02-03 22:59:08 UTC (rev 
7370)
@@ -0,0 +1 @@
+prog: ../../tests/true

Added: branches/VCOV/exp-vcov/vc_annotate
===================================================================
--- branches/VCOV/exp-vcov/vc_annotate                          (rev 0)
+++ branches/VCOV/exp-vcov/vc_annotate  2008-02-03 22:59:08 UTC (rev 7370)
@@ -0,0 +1,294 @@
+#! /lusr/bin/perl -w
+
+##--------------------------------------------------------------------##
+##--- VCov: annotating source files                 vc_annotate.in ---##
+##--------------------------------------------------------------------##
+
+#  This file is part of VCov, a Valgrind tool for measuring execution
+#  coverage.
+#
+#  Copyright (C) 2005-2008 Nicholas Nethercote
+#     [EMAIL PROTECTED]
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License as
+#  published by the Free Software Foundation; either version 2 of the
+#  License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+#  02111-1307, USA.
+#
+#  The GNU General Public License is contained in the file COPYING.
+
+use strict;
+
+# XXX: this needs to be converted from 'vc_annotate' to 'vc_annotate.in'
+# before being distributed.
+
+# Version number
+my $version = "3.4.0.SVN";
+
+# Usage message.
+my $usage = <<END
+usage: vc_annotate [options] source-files
+
+  options for the user, with defaults in [ ], are:
+    -h --help             show this message
+    -v --version          show version
+
+  VCov is Copyright (C) 2005-2008 Nicholas Nethercote.
+  Both are licensed under the GNU General Public License, version 2.
+  Bug reports, feedback, admiration, abuse, etc, to: [EMAIL PROTECTED]
+                                                
+END
+;
+
+# Coverage data file.
+my $input_file = undef;
+
+# List of user-specified source files to annotate.
+my @srcfiles;
+
+# Directories in which to look for annotation files.
+my @include_dirs = ("");
+
+# Files chosen for annotation on the command line.  
+# key = basename (trimmed of any directory), value = full filename
+# XXX: needed?  see @srcfiles above
+my %user_ann_files;
+
+# CCs, organised by filename and line_num for easy annotation.
+# hash(filename => hash(line_num => insn_exec_count))
+my %all_CCs;
+
+# Used in various places.
+my $fancy = '-' x 80 . "\n";
+
+
+#-----------------------------------------------------------------------------
+sub process_cmd_line() 
+#-----------------------------------------------------------------------------
+{
+    for my $arg (@ARGV) { 
+        # Option handling
+        if ($arg =~ /^-/) {
+            # --version
+            if ($arg =~ /^-v$|^--version$/) {
+                die("vc_annotate-$version\n");
+            } else {            # -h and --help fall under this case
+                die($usage);
+            }
+
+        # Argument handling -- annotation file checking and selection.
+        # Stick filenames into a hash for quick 'n easy lookup throughout.
+        } else {
+            if (not defined $input_file) {
+                # First non-option argument is the output file.
+                $input_file = $arg;
+            } else {
+                # Subsequent non-option arguments are source files.
+                my $readable = 0;
+                foreach my $include_dir (@include_dirs) {
+                    if (-r $include_dir . $arg) {
+                        $readable = 1;
+                    }
+                }
+                $readable or die("File $arg not found in any of: 
@include_dirs\n");
+                $user_ann_files{$arg} = 1;
+            }
+        }
+    }
+
+    # Must have chosen an input file
+    if (not defined $input_file) {
+        die($usage);
+    }
+}
+
+#-----------------------------------------------------------------------------
+sub read_coverage_file() 
+#-----------------------------------------------------------------------------
+{
+    open(INPUTFILE, "< $input_file") 
+         || die "Cannot open $input_file for reading\n";
+
+    my $curr_filename = undef;
+    my $curr_file_CCs = {};     # hash(line_num => insn_exec_count)
+
+    while (<INPUTFILE>) {
+        # Execution count lines.
+        if (s/^(\d+) (\d+)$//) {
+            my $line_num = $1;
+            my $n_execs  = $2;
+            # Add line number to the file_CCs.
+            if (defined $curr_file_CCs->{$line_num}) {
+                die("$input_file:$.: line $line_num seen already"); 
+            }
+            $curr_file_CCs->{$line_num} = $n_execs;
+
+        # We test for fl= lines second, because they're much less common.
+        } elsif (/^fl=(.*)$/) {
+            my $next_filename = $1;
+            # Write-back CCs for the previous file, if there was one.
+            if (defined $curr_filename) {
+                $all_CCs{$curr_filename} = $curr_file_CCs 
+            };
+            # Setup the CCs for the next file.
+            $curr_filename = $next_filename;
+            if (defined $all_CCs{$curr_filename}) {
+                die("$input_file:$.: file $curr_filename seen already"); 
+            }
+            $curr_file_CCs = {};
+
+        } else {
+            die("$input_file:$.: parse error\n");
+        }
+    }
+    # Write-back CCs for the final file, if there was one.
+    if (defined $curr_filename) {
+        $all_CCs{$curr_filename} = $curr_file_CCs 
+    };
+    close(INPUTFILE);
+}
+
+#-----------------------------------------------------------------------------
+sub print_per_file_output()
+#-----------------------------------------------------------------------------
+{
+    # Totals for each file.  This is an array of hashrefs.
+    my @file_totals;
+    
+    my ($n_total_executed_lines, $n_total_unexecuted_lines) = (0, 0);
+
+    foreach my $src_file (keys %all_CCs) {
+        # If $src_file more recent than $infile, issue a warning.  (Field #9
+        # is the time of the last modification.)
+        if ((stat $src_file)[9] > (stat $input_file)[9]) {
+            warn("warning: Source file '$src_file'\n");
+            warn("         is more recent than data file '$input_file'.\n");
+            warn("         Its annotations may be incorrect.\n");
+        }
+        my ($n_executed_lines, $n_unexecuted_lines) = (0, 0);
+        my $file_CCs = $all_CCs{$src_file};
+        # Count the number of executed and non-executed lines.
+        foreach my $n_execs (values( %$file_CCs )) {
+            if ($n_execs > 0) {
+                $n_total_executed_lines++;
+                $n_executed_lines++;
+            } else {
+                $n_total_unexecuted_lines++;
+                $n_unexecuted_lines++;
+            }
+        }
+        # Record the counts.
+        push(@file_totals,
+            {"filename" => $src_file,
+             "n_executed_lines"   => $n_executed_lines,
+             "n_unexecuted_lines" => $n_unexecuted_lines}
+        ); 
+    }
+
+    # Sort files so that the files with the most unexecuted lines come
+    # first.  Nb: $a and $b are the args to the sort function (Perl creates
+    # them automatically).
+    @file_totals =
+        sort
+            {$b->{"n_unexecuted_lines"} <=> $a->{"n_unexecuted_lines"}}
+            @file_totals;
+
+    # Subroutine for printing the percentages.
+    sub print_percentage($$) {
+        my ($n_executed_lines, $n_unexecuted_lines) = @_;
+
+        my $n_executable_lines = $n_executed_lines + $n_unexecuted_lines;
+
+        printf("%5.1f%% (%4d of %4d lines)",
+           ( $n_executable_lines == 0
+             ? 0
+             : 100 * $n_executed_lines / $n_executable_lines ),
+           $n_executed_lines,
+           $n_executable_lines);
+    }
+    
+    # Print the total coverage.
+    my $n_total_executable_lines =
+        $n_total_executed_lines + $n_total_unexecuted_lines;
+    print($fancy);
+    print("Total coverage\n");
+    print($fancy);
+    print_percentage($n_total_executed_lines, $n_total_unexecuted_lines);
+    print("\n\n");
+
+    # Print the per-file counts.
+    print($fancy);
+    print("Per-file coverage (files with the most unexecuted lines are shown 
first)\n");
+    print($fancy);
+    foreach my $f (@file_totals) {
+        print_percentage($f->{"n_executed_lines"}, $f->{"n_unexecuted_lines"});
+        printf(": %s\n", $f->{"filename"});
+    }
+    print("\n");
+}
+
+#-----------------------------------------------------------------------------
+sub annotate_source_files()
+#-----------------------------------------------------------------------------
+{
+    # Do the annotations.
+    foreach my $src_file (keys %all_CCs) {
+        my $file_CCs = $all_CCs{$src_file};
+
+        my ($lines, $executable_lines, $executed_lines) = (0,0,0);
+
+        if (open(SRCFILE, "< $src_file")) {
+            print($fancy);
+            print("$src_file\n");
+            print($fancy);
+            while (<SRCFILE>) {
+                my $linenum = $.;
+                my $n_execs = $file_CCs->{$linenum};
+                my $ann;
+                if (defined $n_execs) {
+                    if (0 == $n_execs) {
+                        $ann = "#####";
+                    } else {
+                        $ann = sprintf("%d", $n_execs);
+                        $executed_lines++;
+                    }
+                    $executable_lines++;
+                } else {
+                    $ann = "-";
+                }
+                printf("%9s:%5d:%s", $ann, $linenum, $_);
+                $lines++;
+            }
+            close(SRCFILE);
+
+        } else {
+            print("warning: source file $src_file not found, skipping\n");
+        }
+        print("\n");
+    }
+}
+
+#----------------------------------------------------------------------------
+# "main()"
+#----------------------------------------------------------------------------
+process_cmd_line();
+
+read_coverage_file();
+
+print_per_file_output();
+
+annotate_source_files();
+
+##--------------------------------------------------------------------##
+##--- end                                                          ---##
+##--------------------------------------------------------------------##

Added: branches/VCOV/exp-vcov/vc_main.c
===================================================================
--- branches/VCOV/exp-vcov/vc_main.c                            (rev 0)
+++ branches/VCOV/exp-vcov/vc_main.c    2008-02-03 22:59:08 UTC (rev 7370)
@@ -0,0 +1,752 @@
+
+/*--------------------------------------------------------------------*/
+/*--- VCov: a testing coverage tool.                     vc_main.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of VCov, a Valgrind tool for measuring execution
+   coverage.
+
+   Copyright (C) 2002-2008 Nicholas Nethercote
+      [EMAIL PROTECTED]
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+// XXX TODO:
+// - inline CC incrementing, rather than using a C call
+// - add branch coverage, at least for encountered branches
+// - the new seginfo_* functions -- would providing an iterator be better?
+// - write tests -- work out how to make them deterministic...
+// - write docs
+
+// Overview:
+// - VCov is a coverage testing tool.  It has similarities and differences
+//   to gcov.
+//
+// - VCov counts the number of instructions executed for each source line.
+//   This is different from gcov, which counts how many times each line was
+//   executed;  VCov's counts will be strictly higher than gcov's for
+//   equivalent runs, since each executable line is compiled down to one or
+//   more instructions.
+//
+// - VCov relies entirely on debugging line information to determine which
+//   lines have been executed, and more importantly, which lines have not.
+//   Because of this, VCov works best when programs are compiled with no
+//   optimisation.  When optimisation is used, the line numbers don't match
+//   up as well and the annotated source can be confusing.
+//
+// - VCov does not use the same file format that 'gcov' uses.  gcov creates
+//   three files for each source file: .bb, .bbg and .da.  The first two are
+//   generated at compile time.  The third is generated at run-time.  This
+//   file format is not easy to use by VCov because VCov is entirely
+//   dynamic.  Therefore VCov uses its own file format.
+//
+// - Data for each run is stored in a single file, which is called
+//   "vcov.out" by default.  Subsequent runs augment the file.  A different
+//   file name can be specified.  Debugging information must be present in a
+//   file for coverage data to be collected for it.
+//
+// - The accompanying script "vc_annotate" can calculate per-file execution
+//   coverage.  It can also annotate source files.
+
+#include "pub_tool_basics.h"
+#include "pub_tool_vki.h"
+#include "pub_tool_debuginfo.h"
+#include "pub_tool_libcbase.h"
+#include "pub_tool_libcassert.h"
+#include "pub_tool_libcfile.h"
+#include "pub_tool_libcprint.h"
+#include "pub_tool_libcproc.h"
+#include "pub_tool_mallocfree.h"
+#include "pub_tool_options.h"
+#include "pub_tool_tooliface.h"
+
+/*------------------------------------------------------------*/
+/*--- Types and Data Structures                            ---*/
+/*------------------------------------------------------------*/
+
+//------------------------------------------------------------
+// Primary data structure: CC table
+// - Holds the per-source-line exec counts, grouped by file.
+// - hash_table(Char* filename, sorted_array(UInt line_num, ULong num_execs))
+// - The hash table is separately chained.
+// - Lookups are done by instruction address.
+// - Traversed for dumping stats at end in a per-file, then per-line order.
+
+#define N_FILE_ENTRIES        4999     // Must be prime.    XXX: too big?
+
+// Nb: the log function accesses the 'n_execs' field directly.
+typedef struct {
+   UInt  line_num;      // Line number.
+   ULong n_execs;       // How many times it has executed.
+} LineCC;
+
+typedef struct _FileCC FileCC;
+struct _FileCC {
+   Char*   dirname;     // Directory of this file.     
+   Char*   filename;    // Name of this file.     
+   FileCC* next;        // Next FileCC in the table.
+   UInt    n_lineCCs;   // Number of LineCCs for this file.
+   LineCC* lineCCs;     // The array of LineCCs for this file.
+};
+
+// Top level of CC table.  Auto-zeroed.
+static FileCC *CC_table[N_FILE_ENTRIES];
+
+//------------------------------------------------------------
+// Stats
+
+static Int  n_src_files          = 0;
+static Int  n_no_debugs          = 0;
+static Int  n_yes_debugs         = 0;
+static Int  n_lineCC_slots_used  = 0;
+static Int  n_lineCC_slots_total = 0;
+
+/*------------------------------------------------------------*/
+/*--- CC table operations                                  ---*/
+/*------------------------------------------------------------*/
+
+static UInt hash(Char* dirname, Char* filename, UInt table_size)
+{
+   const int hash_constant = 256;
+   int hash_value = 0;
+   for ( ; *dirname; dirname++)
+      hash_value = (hash_constant * hash_value + *dirname) % table_size;
+   for ( ; *filename; filename++)
+      hash_value = (hash_constant * hash_value + *filename) % table_size;
+   return hash_value;
+}
+
+static Int compareLineCC(void* va, void* vb)
+{
+   LineCC* a = (LineCC*)va;
+   LineCC* b = (LineCC*)vb;
+
+   // This should be safe because line numbers are fairly small (never
+   // greater than 2 billion) and positive.  And it's faster than doing one
+   // or more comparisons.
+   return ((Int)a->line_num) - ((Int)b->line_num);
+}
+
+static void sort_and_remove_dups_from_FileCC(FileCC* cc)
+{
+   Int i, src, dst;
+   Int n_line_CCs_with_dups = cc->n_lineCCs;
+
+   // First, sort the array.
+   VG_(ssort)(cc->lineCCs, cc->n_lineCCs, sizeof(LineCC), compareLineCC);
+   for (i = 0; i < (Int)cc->n_lineCCs - 1; i++) {
+      tl_assert(cc->lineCCs[i].line_num <= cc->lineCCs[i+1].line_num);
+   }
+
+   // Now remove any adjacent dups by shuffling entries down, and fill in
+   // the tail with zeroes.
+   dst = 1;
+   src = 1;
+   while (src < cc->n_lineCCs) {
+      // If we hit a new src line_num (ie. it differs from its predecessor)
+      // then we copy it to dst and increment dst.
+      if (cc->lineCCs[src].line_num != cc->lineCCs[src-1].line_num) {
+         cc->lineCCs[dst].line_num = cc->lineCCs[src].line_num;
+         dst++;
+      }
+      src++;
+   }
+   for (i = dst ; i < cc->n_lineCCs; i++) {     // Zero the tail.
+      cc->lineCCs[i].line_num = 0;
+   }
+   cc->n_lineCCs = dst;
+
+   // Update stats.
+   n_lineCC_slots_total += n_line_CCs_with_dups;
+   n_lineCC_slots_used  += cc->n_lineCCs;
+
+   // Check it's sorted and has no dups.
+   for (i = 1; i < cc->n_lineCCs; i++) {
+      tl_assert(cc->lineCCs[i-1].line_num < cc->lineCCs[i].line_num);
+   }
+}
+
+static __inline__ 
+FileCC* new_FileCC(Addr instrAddr, Char* dirname, Char* filename, FileCC* next)
+{
+   FileCC* cc;
+   const SegInfo* seg;
+   UInt  n_matches;
+   Int   i;
+   UInt  tmp_line;
+   Char* tmp_dirname;
+   Char* tmp_filename;
+
+   // Print the filename, if asked-for.
+   if (VG_(clo_verbosity) > 1) {
+      VG_(message)(Vg_DebugMsg, "vcov: first occurrence of '%s'", filename);
+   }
+      
+   // Create the FileCC.
+   cc = VG_(malloc)(sizeof(FileCC));
+   cc->dirname  = VG_(strdup)(dirname);
+   cc->filename = VG_(strdup)(filename);
+   cc->next = next;
+
+   // XXX: better: malloc space according to the number of locns.  Then copy
+   // the matching ones in, sort-and-remove-dups, then copy the remaining
+   // ones into a new, right-sized array.
+
+   // Now create the LineCCs.
+   // Count how many locs in this SegInfo match 'filename' in order to
+   // allocate the right-sized buffer.
+   seg = VG_(find_seginfo)(instrAddr);
+   tl_assert(seg);
+   n_matches = 0;
+   for (i = 0; i < VG_(seginfo_num_locs)(seg); i++) {
+      tl_assert( VG_(seginfo_locN_dirname) (seg, i, &tmp_dirname) );
+      tl_assert( VG_(seginfo_locN_filename)(seg, i, &tmp_filename) );
+//    VG_(printf)("dirnames:  %s %s\n",  dirname,  tmp_dirname);
+//    VG_(printf)("filenames: %s %s\n", filename, tmp_filename);
+      if (VG_STREQ(dirname, tmp_dirname) && VG_STREQ(filename, tmp_filename))
+         n_matches++;
+   }
+   // If there weren't any matches, something has gone wrong...
+   tl_assert(n_matches > 0);
+
+   // Allocate the lineCCs array.
+   cc->n_lineCCs = n_matches;
+   cc->lineCCs   = VG_(malloc)(n_matches * sizeof(LineCC));
+
+   // Go through locs again, this time initialising the array.
+   n_matches = 0;
+   for (i = 0; i < VG_(seginfo_num_locs)(seg); i++) {
+      tl_assert( VG_(seginfo_locN_dirname) (seg, i, &tmp_dirname) );
+      tl_assert( VG_(seginfo_locN_filename)(seg, i, &tmp_filename) );
+      tl_assert( VG_(seginfo_locN_line)    (seg, i, &tmp_line) );
+      if (VG_STREQ(dirname, tmp_dirname) && VG_STREQ(filename, tmp_filename)) {
+         cc->lineCCs[n_matches].line_num = tmp_line;
+         cc->lineCCs[n_matches].n_execs  = 0;
+         n_matches++;
+      }
+   }
+   // Make sure we get the same number of matches the second time around.
+   tl_assert(n_matches == cc->n_lineCCs); 
+
+   // Loctabs are sorted by address.  We want our LineCC entries sorted by
+   // line number.  Usually address ordering is pretty close to line
+   // ordering, but it's not exactly the same.  Also, sometimes a line can
+   // get mentioned more than once in the loctab.  So now we sort and remove
+   // any dups in the line numbers, and update n_lineCCs accordingly.  As a
+   // result, we will have over-allocated for lineCCs[], but the amount
+   // should usually be very small.
+   sort_and_remove_dups_from_FileCC(cc);
+
+   if (VG_(clo_verbosity) > 1) {
+      VG_(message)(Vg_DebugMsg, "vcov: %d debuginfo lines, %d matches, "
+                                "%d unique matches:",
+         VG_(seginfo_num_locs)(seg), n_matches, cc->n_lineCCs);
+      // Here we fake up a VG_(message)-style line annotation;  we have to
+      // use VG_(printf)() because we want to print multiple things on one
+      // line.
+      VG_(printf)("--%d-- vcov:   ", VG_(getpid)());
+      for (i = 0; i < cc->n_lineCCs; i++)
+         VG_(printf)("%d ", cc->lineCCs[i].line_num);
+      VG_(printf)("\n");
+   }
+
+   return cc;
+}
+
+// Returns a pointer to FileCC, creates a new one if necessary (unless
+// 'must_be_present' is true, in which case it aborts if the FileCC isn't
+// present).  New nodes are prepended to their chain.  
+static FileCC* get_FileCC(Addr instrAddr, Char* dirname, Char* filename,
+                          Bool must_be_present)
+{
+   FileCC *curr_fileCC;
+   UInt    file_hash;
+
+   file_hash = hash(dirname, filename, N_FILE_ENTRIES);
+   curr_fileCC = CC_table[file_hash];
+   // Look for the filename in the appropriate chain.
+   while (NULL != curr_fileCC &&
+          !VG_STREQ( dirname, curr_fileCC-> dirname) &&
+          !VG_STREQ(filename, curr_fileCC->filename))
+   {
+      curr_fileCC = curr_fileCC->next;
+   }
+   if (NULL == curr_fileCC) {
+      if (must_be_present) {
+         // XXX: don't panic, quit in a better way
+         tl_assert2(0, "file not present: %s, %s", dirname, filename);
+      }
+      // It wasn't in the chain.  Create a new FileCC.
+      CC_table[file_hash] = curr_fileCC = 
+         new_FileCC(instrAddr, dirname, filename, CC_table[file_hash]);
+      n_src_files++;
+   }
+   return curr_fileCC;
+}
+
+/*--------------------------------------------------------------------*/
+/*--- Command line processing                                      ---*/
+/*--------------------------------------------------------------------*/
+
+static Bool clo_fresh = False;
+
+static Bool vc_process_cmd_line_option(Char* arg)
+{
+   VG_BOOL_CLO(arg, "--fresh", clo_fresh)
+   else return False;
+
+   return True;
+}
+
+static void vc_print_usage(void)
+{
+   // XXX: allow people to change the name of the outfile.  The %p option
+   // isn't so useful here, but might as well allow it.
+   VG_(printf)(
+"    --fresh=no|yes            clear all previous coverage info [no]\n"
+   );
+}
+
+static void vc_print_debug_usage(void)
+{
+   VG_(printf)(
+"    (none)\n"
+   );
+}
+
+/*------------------------------------------------------------*/
+/*--- Instrumentation                                      ---*/
+/*------------------------------------------------------------*/
+
+static VG_REGPARM(1)
+void log_instr(LineCC* lineCC)
+{
+   (lineCC->n_execs)++;
+}
+
+// Instrumentation for the end of each original instruction.
+static
+void doOneInstr(IRSB* sbOut, Addr instrAddr)
+{
+   #define FILE_LEN  1024 
+
+   IRDirty* di;
+   IRExpr*  arg1;
+   LineCC*  lineCC;
+   Char     filename[FILE_LEN];
+   Char     dirname[VKI_PATH_MAX + 1];
+   Bool     dirname_available;
+   Int      line;
+   FileCC*  fileCC;
+   Int      mid, mid_line, lo, hi;
+
+   // Nb: This sets dirname to "" if it doesn't find a dirname.
+   Bool found_file_line = 
+      VG_(get_filename_linenum)(instrAddr, filename, FILE_LEN,
+                                dirname, VKI_PATH_MAX, &dirname_available,
+                                &line);
+
+   // Only bother instrumenting the instruction if it has debug info!
+   if (found_file_line) {
+      // Get CC table for the file (creating it if necessary).
+      fileCC = get_FileCC(instrAddr, dirname, filename,
+                          /*must_be_present*/False);
+
+      // Get the LineCC for the instruction.  Binary search.
+      lo = 0;
+      hi = fileCC->n_lineCCs - 1;
+
+      while (True) {
+         /* current unsearched space is from lo to hi, inclusive. */
+         if (lo > hi) tl_assert2(0, "didn't find %d in lineCCs", line);
+         mid      = (lo + hi) / 2;
+         mid_line = fileCC->lineCCs[mid].line_num;
+         if (line < mid_line) { hi = mid-1; continue; } 
+         if (line > mid_line) { lo = mid+1; continue; }
+         tl_assert(line == mid_line);
+         break;
+      }
+      lineCC = &(fileCC->lineCCs[mid]);
+
+      // Insert call to log function
+      arg1 = mkIRExpr_HWord( (HWord)lineCC );
+      di = unsafeIRDirty_0_N( 1, "log_instr", &log_instr, mkIRExprVec_1(arg1));
+      addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
+
+      n_yes_debugs++;
+
+   } else {
+      n_no_debugs++;
+   }
+}
+
+static
+IRSB* vc_instrument ( VgCallbackClosure* closure,
+                      IRSB* sbIn, 
+                      VexGuestLayout* layout, 
+                      VexGuestExtents* vge,
+                      IRType gWordTy, IRType hWordTy )
+{
+   Int      i;
+   IRSB*    sbOut;
+   IRStmt*  st;
+
+   if (gWordTy != hWordTy) {
+      /* We don't currently support this case. */
+      VG_(tool_panic)("host/guest word size mismatch");
+   }
+
+   // Set up new SB.
+   sbOut = deepCopyIRSBExceptStmts(sbIn);
+
+   for (i = 0; i < sbIn->stmts_used; i++) {
+      st = sbIn->stmts[i];
+
+      if (Ist_IMark == st->tag) {
+         // Ist.IMark.addr is an Addr64.  We convert it to an Addr, then
+         // check the conversion didn't change the value (it should never if
+         // all is right with the world).
+         Addr instrAddr = (Addr)st->Ist.IMark.addr;
+         tl_assert( ((Addr64)instrAddr) == st->Ist.IMark.addr );
+         doOneInstr(sbOut, instrAddr);
+      }
+      addStmtToIRSB( sbOut, st );
+   }
+
+   return sbOut;
+}
+
+/*------------------------------------------------------------*/
+/*--- vc_fini() and related function                       ---*/
+/*------------------------------------------------------------*/
+
+// Parse buffer.  Each filename line has the exact form:
+//
+//    fl=<name>
+//
+// Each exec_counts line has the exact form
+//
+//    integer space integer newline
+//
+// Simple, huh?  We move through lines in the file and lines in
+// fileCC->lineCCs[] in tandem.
+//
+static Bool parse_buffer(Char* outfile, struct vki_stat* outfile_statbuf, 
+                         Char* buf)
+{
+   Int   buf_i = 0, line_i = 0, curr_line = 1;
+   Char* error_msg = NULL;
+   FileCC* fileCC = NULL;
+
+   #define BOO(s) { error_msg = s; goto parse_end; }
+
+   while ('\0' != buf[buf_i]) {
+
+      if ('f' == buf[buf_i]) {
+         Int fl_start_i, buf_j;
+         Char*  dirname;
+         Char* filename;
+
+         // Check the previous fileCC (if there is one) had the right number
+         // of lines.
+         if (fileCC && line_i != fileCC->n_lineCCs) {
+            BOO("consistency error: not enough lines in existing file");
+         }
+
+         // fl=<name>
+         if (buf[buf_i+0] != 'f' ||
+             buf[buf_i+1] != 'l' ||
+             buf[buf_i+2] != '=')
+         {
+            BOO("parse error: bad 'fl=' line");
+         } 
+         buf_i += 3;
+         fl_start_i = buf_i;
+         while (buf[buf_i] != '\n') { buf_i++; }
+         // Replace '\n' with '\0'.  Ok because 'buf' is only temporary.
+         buf[buf_i] = '\0';   
+
+   // Warn if srcfile is present and newer than the existing outfile.
+   // XXX: should I warn if the srcfile isn't present?
+   {
+      struct vki_stat srcfile_statbuf;
+      Char* srcfile = &buf[fl_start_i];
+      if ( ! (VG_(stat)(srcfile, &srcfile_statbuf)).isError ) {
+         if (srcfile_statbuf.st_mtime > outfile_statbuf->st_mtime) {
+            VG_(message)(Vg_UserMsg,
+               "Warning: Source file '%s' is more recent than ", srcfile);
+            VG_(message)(Vg_UserMsg, 
+               "         old data file '%s'.", outfile);
+            VG_(message)(Vg_UserMsg,
+               "         Coverage information may be incorrect.",
+               srcfile);
+            VG_(message)(Vg_UserMsg,
+               "         Rerun with --fresh to purge old data and start 
again");
+         }
+      }
+   }
+
+
+
+         // Ok, we've isolated the function name.  Now split it into a
+         // filename and a dirname.
+         buf_j = buf_i-1;
+         while (True) {
+            if (buf_j == fl_start_i) {
+               dirname  = "";
+               filename = &buf[fl_start_i];
+               break;
+            } else if ('/' == buf[buf_j]) {
+               buf[buf_j] = '\0';      // Replace '/' with '\0'.
+               dirname  = &buf[fl_start_i];
+               filename = &buf[buf_j+1];
+               break;
+            }
+            buf_j--;
+         }
+
+         // Find the corresponding FileCC.
+         // XXX: the unused instrAddr is ugly.
+         fileCC = get_FileCC(/*instrAddr--unused*/0, dirname, filename,
+                             /*must_be_present*/True);
+
+         // Move past the newline, and reset line_i.
+         buf_i++;
+         line_i = 0;
+
+      } else if (VG_(isdigit)(buf[buf_i])) {
+         Long line_num, n_execs;
+
+         // Line number.
+         line_num = VG_(atoll)(buf+buf_i);
+         while (VG_(isdigit)(buf[buf_i])) { buf_i++; }
+
+         // Space.
+         if (' ' != buf[buf_i++]) BOO("parse error: expected ' '");
+
+         // n_execs number.
+         if (!VG_(isdigit)(buf[buf_i]))
+            BOO("parse error: expected exec count");
+         // XXX: use strtol instead?  better checking...
+         n_execs = VG_(atoll)(buf+buf_i);
+         while (VG_(isdigit)(buf[buf_i])) { buf_i++; }
+
+         // Newline.
+         if ('\n' != buf[buf_i++]) BOO("parse error: expected newline");
+
+         // Update fileCC with the data from the line.
+         // XXX: have regtests for all these cases...
+         if (!fileCC) {
+            BOO("parse error: first line is not a 'fl=' line");
+         }
+         if (line_i >= fileCC->n_lineCCs) {
+            BOO("consistency error: too many lines in existing file");
+         }
+         if (fileCC->lineCCs[line_i].line_num != line_num) {
+            BOO("consistency error: line mismatch with existing file");
+         }
+         fileCC->lineCCs[line_i].n_execs += n_execs;
+         line_i++;
+
+      } else {
+         VG_(printf)("bad char: %c\n",buf[buf_i]);
+         BOO("parse error: line doesn't start with 'fl=' or line number");
+      }
+      curr_line++;
+
+      #undef BOO
+   }
+
+  parse_end:
+   
+   if (error_msg) {
+      VG_(message)(Vg_UserMsg, "%s:%d: %s;\n", outfile, curr_line, error_msg);
+      VG_(message)(Vg_UserMsg, "    coverage data not written to file;\n");
+      VG_(message)(Vg_UserMsg, "    rerun VCov with --fresh to purge old data 
and start again");
+   }
+
+   return (NULL == error_msg);
+}
+
+// If an output file for this source file already exists, read it in and
+// update the fileCC stats with its data.  Returns 'False' if something went
+// wrong and we should not write out the new data.
+static Bool maybe_read_existing_outfile(Char* outfile)
+{
+   Int   fd;
+   Char* buf;
+   OffT  size;
+   struct vki_stat outfile_statbuf;
+   SysRes res;
+   Bool   ok;
+   
+   // If no old file exists, or we are overwriting it, our work here is done.
+   if ( clo_fresh || (VG_(stat)(outfile, &outfile_statbuf)).isError ) {
+      if (VG_(clo_verbosity) > 1)
+         VG_(message)(Vg_DebugMsg, "vcov: create new outfile:  '%s'", outfile);
+      return True;
+   }
+
+   // Right, we have to augment the old file.
+   if (VG_(clo_verbosity) > 1)
+      VG_(message)(Vg_DebugMsg, "vcov: augment old outfile: '%s'", outfile);
+
+   // Open existing data file.
+   res = VG_(open)(outfile, VKI_O_RDONLY, 0);
+   if (res.isError) {
+      VG_(message)(Vg_UserMsg, "warning: could not open existing '%s'",
+                   outfile);
+      return False;
+   }
+   fd = (Int)res.res;
+
+   // Allocate a buffer big enough to hold the entire file.  Files should be
+   // compact enough that even huge ones are only a few megabytes...
+   size = outfile_statbuf.st_size;
+   buf  = VG_(malloc)(size+1); 
+
+   // Read entire file into buffer.  
+   if (VG_(read)(fd, buf, size) != size) {
+      VG_(message)(Vg_UserMsg, "error: could not read '%s'\n", outfile);
+      VG_(free)(buf);
+      VG_(close)(fd);
+      return False;
+   }
+
+   buf[size] = '\0';      // Ensure null-termination.
+
+   // Parse the buffer.
+   ok = parse_buffer(outfile, &outfile_statbuf, buf);
+
+   // Deallocate buffer, close the file.
+   VG_(free)(buf);
+   VG_(close)(fd);
+
+   return ok;
+}
+
+static void write_outfile(Char* outfile)
+{
+   Char   buf[VKI_PATH_MAX+1];
+   Int    i, j, fd;
+   SysRes res;
+
+   res = VG_(open)(outfile, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_RDWR,
+                            VKI_S_IRUSR|VKI_S_IWUSR);
+   if (!res.isError) {
+      fd = (Int)res.res;
+
+      // For each FileCC, output the new/updated counts.
+      for (i = 0; i < N_FILE_ENTRIES; i++) {
+         FileCC* fileCC = CC_table[i];
+         while (fileCC != NULL) {
+            // Write the filename line.
+            if (! VG_STREQ(fileCC->dirname, "")) {
+               VG_(sprintf)(buf, "fl=%s/%s\n", fileCC->dirname,
+                                               fileCC->filename);
+            } else {
+               VG_(sprintf)(buf, "fl=%s\n", fileCC->filename);
+            }
+            VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
+
+            // Write the execution count lines.
+            for (j = 0; j < fileCC->n_lineCCs; j++) {
+               LineCC* lineCC = &(fileCC->lineCCs[j]);
+               VG_(sprintf)(buf, "%u %llu\n", lineCC->line_num,
+                                              lineCC->n_execs);
+               VG_(write)(fd, (void*)buf, VG_(strlen)(buf));
+            }
+
+            fileCC = fileCC->next;
+         }
+      }
+      VG_(close)(fd);
+
+   } else {
+      // If the file can't be opened for whatever reason, just skip.
+      VG_(message)(Vg_UserMsg,
+         "error: cannot open output file `%s'", outfile );
+   }
+}
+
+static void vc_fini(Int exitcode)
+{
+   Char* outfile = "vcov.out";
+   Bool  ok;
+
+   // ... read the old vcov.out first ...
+
+//   XXX: need to lock the file first.
+   
+   ok = maybe_read_existing_outfile(outfile);
+   if (ok)
+      write_outfile(outfile);
+
+//   XXX: now unlock the file
+
+   // Stats
+   if (VG_(clo_verbosity) > 1) {
+       Int n_tot_debugs = n_no_debugs + n_yes_debugs;
+
+       tl_assert(0 != n_tot_debugs);
+       if (0 == n_lineCC_slots_total) n_lineCC_slots_total = 1;
+
+       VG_(message)(Vg_DebugMsg, "vcov: number of source files: %d",
+                                 n_src_files);
+       VG_(message)(Vg_DebugMsg, "vcov: lines with debug info: %d%% (%d/%d)", 
+                    n_yes_debugs * 100 / n_tot_debugs,
+                    n_yes_debugs, n_tot_debugs);
+       VG_(message)(Vg_DebugMsg, "vcov: lineCC slot usage: %d%% (%d/%d)", 
+                    n_lineCC_slots_used * 100 / n_lineCC_slots_total,
+                    n_lineCC_slots_used, n_lineCC_slots_total);
+   }
+}
+
+/*--------------------------------------------------------------------*/
+/*--- Setup                                                        ---*/
+/*--------------------------------------------------------------------*/
+
+static void vc_post_clo_init(void)
+{
+}
+
+static void vc_pre_clo_init(void)
+{
+   VG_(details_name)            ("VCov");
+   VG_(details_version)         (NULL);
+   VG_(details_description)     ("a coverage testing tool");
+   VG_(details_copyright_author)(
+      "Copyright (C) 2002-2008, and GNU GPL'd, by Nicholas Nethercote.");
+   VG_(details_bug_reports_to)  (VG_BUGS_TO);
+
+   VG_(basic_tool_funcs)          (vc_post_clo_init,
+                                   vc_instrument,
+                                   vc_fini);
+
+   VG_(needs_command_line_options)(vc_process_cmd_line_option,
+                                   vc_print_usage,
+                                   vc_print_debug_usage);
+}
+
+VG_DETERMINE_INTERFACE_VERSION(vc_pre_clo_init)
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                          ---*/
+/*--------------------------------------------------------------------*/

Modified: branches/VCOV/include/pub_tool_debuginfo.h
===================================================================
--- branches/VCOV/include/pub_tool_debuginfo.h  2008-02-03 22:35:58 UTC (rev 
7369)
+++ branches/VCOV/include/pub_tool_debuginfo.h  2008-02-03 22:59:08 UTC (rev 
7370)
@@ -123,6 +123,17 @@
                                         /*OUT*/UInt*   size,
                                         /*OUT*/HChar** name );
 
+// These ones allow iterating through all the locs in a segment, and getting
+// attributes of each one.  The first one says how many locs are in the
+// segment.  The others get an attribute of a loc;  they return False if 'n'
+// is not a valid loc number.
+extern UInt VG_(seginfo_num_locs)      ( const SegInfo *seg );
+extern Bool VG_(seginfo_locN_addr)     ( const SegInfo *seg, UInt n, Addr* );
+extern Bool VG_(seginfo_locN_size)     ( const SegInfo *seg, UInt n, UInt* );
+extern Bool VG_(seginfo_locN_line)     ( const SegInfo *seg, UInt n, UInt* );
+extern Bool VG_(seginfo_locN_filename) ( const SegInfo *seg, UInt n, Char** );
+extern Bool VG_(seginfo_locN_dirname)  ( const SegInfo *seg, UInt n, Char** );
+
 typedef
    enum {
       Vg_SectUnknown,


-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Valgrind-developers mailing list
Valgrind-developers@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/valgrind-developers

Reply via email to