diff druntime/src/compiler/dmd/cover.d @ 1458:e0b2d67cfe7c

Added druntime (this should be removed once it works).
author Robert Clipsham <robert@octarineparrot.com>
date Tue, 02 Jun 2009 17:43:06 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/druntime/src/compiler/dmd/cover.d	Tue Jun 02 17:43:06 2009 +0100
@@ -0,0 +1,463 @@
+/**
+ * Implementation of code coverage analyzer.
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2009.
+ * License:   <a href="http://www.boost.org/LICENSE_1_0.txt>Boost License 1.0</a>.
+ * Authors:   Walter Bright, Sean Kelly
+ *
+ *          Copyright Digital Mars 2000 - 2009.
+ * Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ */
+module rt.cover;
+
+private
+{
+    version( Windows )
+        import core.sys.windows.windows;
+    else version( Posix )
+    {
+        import core.sys.posix.fcntl;
+        import core.sys.posix.unistd;
+    }
+    import core.bitop;
+    import core.stdc.stdio;
+    import rt.util.utf;
+
+    struct BitArray
+    {
+        size_t  len;
+        uint*   ptr;
+
+        bool opIndex( size_t i )
+        in
+        {
+            assert( i < len );
+        }
+        body
+        {
+            return cast(bool) bt( ptr, i );
+        }
+    }
+
+    struct Cover
+    {
+        string      filename;
+        BitArray    valid;
+        uint[]      data;
+    }
+
+    __gshared
+    {
+	Cover[] gdata;
+	string  srcpath;
+	string  dstpath;
+	bool    merge;
+    }
+}
+
+
+/**
+ * Set path to where source files are located.
+ *
+ * Params:
+ *  pathname = The new path name.
+ */
+extern (C) void dmd_coverSourcePath( string pathname )
+{
+    srcpath = pathname;
+}
+
+
+/**
+ * Set path to where listing files are to be written.
+ *
+ * Params:
+ *  pathname = The new path name.
+ */
+extern (C) void dmd_coverDestPath( string pathname )
+{
+    dstpath = pathname;
+}
+
+
+/**
+ * Set merge mode.
+ *
+ * Params:
+ *      flag = true means new data is summed with existing data in the listing
+ *         file; false means a new listing file is always created.
+ */
+extern (C) void dmd_coverSetMerge( bool flag )
+{
+    merge = flag;
+}
+
+
+/**
+ * The coverage callback.
+ *
+ * Params:
+ *  filename = The name of the coverage file.
+ *  valid    = ???
+ *  data     = ???
+ */
+extern (C) void _d_cover_register( string filename, BitArray valid, uint[] data )
+{
+    Cover c;
+
+    c.filename  = filename;
+    c.valid     = valid;
+    c.data      = data;
+    gdata      ~= c;
+}
+
+
+static ~this()
+{
+    const NUMLINES = 16384 - 1;
+    const NUMCHARS = 16384 * 16 - 1;
+
+    char[]      srcbuf      = new char[NUMCHARS];
+    char[][]    srclines    = new char[][NUMLINES];
+    char[]      lstbuf      = new char[NUMCHARS];
+    char[][]    lstlines    = new char[][NUMLINES];
+
+    foreach( Cover c; gdata )
+    {
+        if( !readFile( appendFN( srcpath, c.filename ), srcbuf ) )
+            continue;
+        splitLines( srcbuf, srclines );
+
+        if( merge )
+        {
+            if( !readFile( addExt( baseName( c.filename ), "lst" ), lstbuf ) )
+                break;
+            splitLines( lstbuf, lstlines );
+
+            for( size_t i = 0; i < lstlines.length; ++i )
+            {
+                if( i >= c.data.length )
+                    break;
+
+                int count = 0;
+
+                foreach( char c2; lstlines[i] )
+                {
+                    switch( c2 )
+                    {
+                    case ' ':
+                        continue;
+                    case '0': case '1': case '2': case '3': case '4':
+                    case '5': case '6': case '7': case '8': case '9':
+                        count = count * 10 + c2 - '0';
+                        continue;
+                    default:
+                        break;
+                    }
+                }
+                c.data[i] += count;
+            }
+        }
+
+        FILE* flst = fopen( (addExt( baseName( c.filename ), "lst\0" )).ptr, "wb" );
+
+        if( !flst )
+            continue; //throw new Exception( "Error opening file for write: " ~ lstfn );
+
+        uint nno;
+        uint nyes;
+
+        for( int i = 0; i < c.data.length; i++ )
+        {
+            if( i < srclines.length )
+            {
+                uint    n    = c.data[i];
+                char[]  line = srclines[i];
+
+                line = expandTabs( line );
+
+                if( n == 0 )
+                {
+                    if( c.valid[i] )
+                    {
+                        nno++;
+                        fprintf( flst, "0000000|%.*s\n", line );
+                    }
+                    else
+                    {
+                        fprintf( flst, "       |%.*s\n", line );
+                    }
+                }
+                else
+                {
+                    nyes++;
+                    fprintf( flst, "%7u|%.*s\n", n, line );
+                }
+            }
+        }
+        if( nyes + nno ) // no divide by 0 bugs
+        {
+            fprintf( flst, "%.*s is %d%% covered\n", c.filename, ( nyes * 100 ) / ( nyes + nno ) );
+        }
+        fclose( flst );
+    }
+}
+
+
+string appendFN( string path, string name )
+{
+    version( Windows )
+        const char sep = '\\';
+    else
+        const char sep = '/';
+
+    auto dest = path;
+
+    if( dest && dest[$ - 1] != sep )
+        dest ~= sep;
+    dest ~= name;
+    return dest;
+}
+
+
+string baseName( string name, string ext = null )
+{
+    auto i = name.length;
+    for( ; i > 0; --i )
+    {
+        version( Windows )
+        {
+            if( name[i - 1] == ':' || name[i - 1] == '\\' )
+                break;
+        }
+        else version( Posix )
+        {
+            if( name[i - 1] == '/' )
+                break;
+        }
+    }
+    return chomp( name[i .. $], ext ? ext : "" );
+}
+
+
+string getExt( string name )
+{
+    auto i = name.length;
+
+    while( i > 0 )
+    {
+        if( name[i - 1] == '.' )
+            return name[i .. $];
+        --i;
+        version( Windows )
+        {
+            if( name[i] == ':' || name[i] == '\\' )
+                break;
+        }
+        else version( Posix )
+        {
+            if( name[i] == '/' )
+                break;
+        }
+    }
+    return null;
+}
+
+
+string addExt( string name, string ext )
+{
+    auto  existing = getExt( name );
+
+    if( existing.length == 0 )
+    {
+        if( name.length && name[$ - 1] == '.' )
+            name ~= ext;
+        else
+            name = name ~ "." ~ ext;
+    }
+    else
+    {
+        name = name[0 .. $ - existing.length] ~ ext;
+    }
+    return name;
+}
+
+
+string chomp( string str, string delim = null )
+{
+    if( delim is null )
+    {
+        auto len = str.length;
+
+        if( len )
+        {
+            auto c = str[len - 1];
+
+            if( c == '\r' )
+                --len;
+            else if( c == '\n' && str[--len - 1] == '\r' )
+                --len;
+        }
+        return str[0 .. len];
+    }
+    else if( str.length >= delim.length )
+    {
+        if( str[$ - delim.length .. $] == delim )
+            return str[0 .. $ - delim.length];
+    }
+    return str;
+}
+
+
+bool readFile( string name, inout char[] buf )
+{
+    version( Windows )
+    {
+        auto    wnamez  = toUTF16z( name );
+        HANDLE  file    = CreateFileW( wnamez,
+                                       GENERIC_READ,
+                                       FILE_SHARE_READ,
+                                       null,
+                                       OPEN_EXISTING,
+                                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
+                                       cast(HANDLE) null );
+
+        delete wnamez;
+        if( file == INVALID_HANDLE_VALUE )
+            return false;
+        scope( exit ) CloseHandle( file );
+
+        DWORD   num = 0;
+        DWORD   pos = 0;
+
+        buf.length = 4096;
+        while( true )
+        {
+            if( !ReadFile( file, &buf[pos], cast(DWORD)( buf.length - pos ), &num, null ) )
+                return false;
+            if( !num )
+                break;
+            pos += num;
+            buf.length = pos * 2;
+        }
+        buf.length = pos;
+        return true;
+    }
+    else version( Posix )
+    {
+        char[]  namez = new char[name.length + 1];
+                        namez[0 .. name.length] = name;
+                        namez[$ - 1] = 0;
+        int     file = open( namez.ptr, O_RDONLY );
+
+        delete namez;
+        if( file == -1 )
+            return false;
+        scope( exit ) close( file );
+
+        int     num = 0;
+        uint    pos = 0;
+
+        buf.length = 4096;
+        while( true )
+        {
+            num = read( file, &buf[pos], cast(uint)( buf.length - pos ) );
+            if( num == -1 )
+                return false;
+            if( !num )
+                break;
+            pos += num;
+            buf.length = pos * 2;
+        }
+        buf.length = pos;
+        return true;
+    }
+}
+
+
+void splitLines( char[] buf, inout char[][] lines )
+{
+    size_t  beg = 0,
+            pos = 0;
+
+    lines.length = 0;
+    for( ; pos < buf.length; ++pos )
+    {
+        char c = buf[pos];
+
+        switch( buf[pos] )
+        {
+        case '\r':
+        case '\n':
+            lines ~= buf[beg .. pos];
+            beg = pos + 1;
+            if( buf[pos] == '\r' && pos < buf.length - 1 && buf[pos + 1] == '\n' )
+                ++pos, ++beg;
+        default:
+            continue;
+        }
+    }
+    if( beg != pos )
+    {
+        lines ~= buf[beg .. pos];
+    }
+}
+
+
+char[] expandTabs( char[] str, int tabsize = 8 )
+{
+    const dchar LS = '\u2028'; // UTF line separator
+    const dchar PS = '\u2029'; // UTF paragraph separator
+
+    bool changes = false;
+    char[] result = str;
+    int column;
+    int nspaces;
+
+    foreach( size_t i, dchar c; str )
+    {
+        switch( c )
+        {
+            case '\t':
+                nspaces = tabsize - (column % tabsize);
+                if( !changes )
+                {
+                    changes = true;
+                    result = null;
+                    result.length = str.length + nspaces - 1;
+                    result.length = i + nspaces;
+                    result[0 .. i] = str[0 .. i];
+                    result[i .. i + nspaces] = ' ';
+                }
+                else
+                {   int j = result.length;
+                    result.length = j + nspaces;
+                    result[j .. j + nspaces] = ' ';
+                }
+                column += nspaces;
+                break;
+
+            case '\r':
+            case '\n':
+            case PS:
+            case LS:
+                column = 0;
+                goto L1;
+
+            default:
+                column++;
+            L1:
+                if (changes)
+                {
+                    if (c <= 0x7F)
+                        result ~= c;
+                    else
+                        encode(result, c);
+                }
+                break;
+        }
+    }
+    return result;
+}