2 * Code coverage analyzer.
6 * $(LI the execution counters are 32 bits in size, and can overflow)
7 * $(LI inline asm statements are not counted)
10 * Copyright: Copyright (C) 2005-2006 Digital Mars, www.digitalmars.com. All rights reserved.
11 * License: BSD style: $(LICENSE)
12 * Authors: Walter Bright, Sean Kelly
20 import sys.windows.windows;
22 import stdc.posix.unistd;
32 bool opIndex( size_t i )
39 return cast(bool) bt( ptr, i );
58 * Set path to where source files are located.
61 * pathname = The new path name.
63 extern (C) void dmd_coverSourcePath( char[] pathname )
70 * Set path to where listing files are to be written.
73 * pathname = The new path name.
75 extern (C) void dmd_coverDestPath( char[] pathname )
85 * flag = true means new data is summed with existing data in the listing
86 * file; false means a new listing file is always created.
88 extern (C) void dmd_coverSetMerge( bool flag )
95 * The coverage callback.
98 * filename = The name of the coverage file.
102 extern (C) void _d_cover_register( char[] filename, BitArray valid, uint[] data )
106 c.filename = filename;
115 const NUMLINES = 16384 - 1;
116 const NUMCHARS = 16384 * 16 - 1;
118 char[] srcbuf = new char[NUMCHARS];
119 char[][] srclines = new char[][NUMLINES];
120 char[] lstbuf = new char[NUMCHARS];
121 char[][] lstlines = new char[][NUMLINES];
123 foreach( Cover c; gdata )
125 if( !readFile( appendFN( srcpath, c.filename ), srcbuf ) )
127 splitLines( srcbuf, srclines );
131 if( !readFile( c.filename ~ ".lst", lstbuf ) )
133 splitLines( lstbuf, lstlines );
135 for( size_t i = 0; i < lstlines.length; ++i )
137 if( i >= c.data.length )
142 foreach( char c2; lstlines[i] )
148 case '0': case '1': case '2': case '3': case '4':
149 case '5': case '6': case '7': case '8': case '9':
150 count = count * 10 + c2 - '0';
160 FILE* flst = fopen( (c.filename ~ ".lst").ptr, "wb" );
163 continue; //throw new Exception( "Error opening file for write: " ~ lstfn );
168 for( int i = 0; i < c.data.length; i++ )
170 if( i < srclines.length )
173 char[] line = srclines[i];
175 line = expandTabs( line );
182 fprintf( flst, "0000000|%.*s\n", line );
186 fprintf( flst, " |%.*s\n", line );
192 fprintf( flst, "%7u|%.*s\n", n, line );
196 if( nyes + nno ) // no divide by 0 bugs
198 fprintf( flst, "%.*s is %d%% covered\n", c.filename, ( nyes * 100 ) / ( nyes + nno ) );
205 char[] appendFN( char[] path, char[] name )
208 const char sep = '\\';
210 const char sep = '/';
214 if( dest && dest[$ - 1] != sep )
221 bool readFile( char[] name, inout char[] buf )
225 auto wnamez = toUTF16z( name );
226 HANDLE file = CreateFileW( wnamez,
231 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
235 if( file == INVALID_HANDLE_VALUE )
237 scope( exit ) CloseHandle( file );
245 if( !ReadFile( file, &buf[pos], cast(DWORD)( buf.length - pos ), &num, null ) )
250 buf.length = pos * 2;
255 else version( linux )
257 char[] namez = new char[name.length + 1];
258 namez[0 .. name.length] = name;
260 int file = open( namez.ptr, O_RDONLY );
265 scope( exit ) close( file );
273 num = read( file, &buf[pos], cast(uint)( buf.length - pos ) );
279 buf.length = pos * 2;
287 void splitLines( char[] buf, inout char[][] lines )
293 for( ; pos < buf.length; ++pos )
301 lines ~= buf[beg .. pos];
303 if( buf[pos] == '\r' && pos < buf.length - 1 && buf[pos + 1] == '\n' )
311 lines ~= buf[beg .. pos];
316 char[] expandTabs( char[] string, int tabsize = 8 )
318 const dchar LS = '\u2028'; // UTF line separator
319 const dchar PS = '\u2029'; // UTF paragraph separator
321 bool changes = false;
322 char[] result = string;
326 foreach( size_t i, dchar c; string )
331 nspaces = tabsize - (column % tabsize);
336 result.length = string.length + nspaces - 1;
337 result.length = i + nspaces;
338 result[0 .. i] = string[0 .. i];
339 result[i .. i + nspaces] = ' ';
342 { int j = result.length;
343 result.length = j + nspaces;
344 result[j .. j + nspaces] = ' ';