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 core.sys.windows.windows;
23 import core.sys.posix.fcntl;
24 import core.sys.posix.unistd;
27 import core.stdc.stdio;
35 bool opIndex( size_t i )
42 return cast(bool) bt( ptr, i );
61 * Set path to where source files are located.
64 * pathname = The new path name.
66 extern (C) void dmd_coverSourcePath( char[] pathname )
73 * Set path to where listing files are to be written.
76 * pathname = The new path name.
78 extern (C) void dmd_coverDestPath( char[] pathname )
88 * flag = true means new data is summed with existing data in the listing
89 * file; false means a new listing file is always created.
91 extern (C) void dmd_coverSetMerge( bool flag )
98 * The coverage callback.
101 * filename = The name of the coverage file.
105 extern (C) void _d_cover_register( char[] filename, BitArray valid, uint[] data )
109 c.filename = filename;
118 const NUMLINES = 16384 - 1;
119 const NUMCHARS = 16384 * 16 - 1;
121 char[] srcbuf = new char[NUMCHARS];
122 char[][] srclines = new char[][NUMLINES];
123 char[] lstbuf = new char[NUMCHARS];
124 char[][] lstlines = new char[][NUMLINES];
126 foreach( Cover c; gdata )
128 if( !readFile( appendFN( srcpath, c.filename ), srcbuf ) )
130 splitLines( srcbuf, srclines );
134 if( !readFile( c.filename ~ ".lst", lstbuf ) )
136 splitLines( lstbuf, lstlines );
138 for( size_t i = 0; i < lstlines.length; ++i )
140 if( i >= c.data.length )
145 foreach( char c2; lstlines[i] )
151 case '0': case '1': case '2': case '3': case '4':
152 case '5': case '6': case '7': case '8': case '9':
153 count = count * 10 + c2 - '0';
163 FILE* flst = fopen( (c.filename ~ ".lst").ptr, "wb" );
166 continue; //throw new Exception( "Error opening file for write: " ~ lstfn );
171 for( int i = 0; i < c.data.length; i++ )
173 if( i < srclines.length )
176 char[] line = srclines[i];
178 line = expandTabs( line );
185 fprintf( flst, "0000000|%.*s\n", line );
189 fprintf( flst, " |%.*s\n", line );
195 fprintf( flst, "%7u|%.*s\n", n, line );
199 if( nyes + nno ) // no divide by 0 bugs
201 fprintf( flst, "%.*s is %d%% covered\n", c.filename, ( nyes * 100 ) / ( nyes + nno ) );
208 char[] appendFN( char[] path, char[] name )
211 const char sep = '\\';
213 const char sep = '/';
217 if( dest && dest[$ - 1] != sep )
224 bool readFile( char[] name, inout char[] buf )
228 auto wnamez = toUTF16z( name );
229 HANDLE file = CreateFileW( wnamez,
234 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
238 if( file == INVALID_HANDLE_VALUE )
240 scope( exit ) CloseHandle( file );
248 if( !ReadFile( file, &buf[pos], cast(DWORD)( buf.length - pos ), &num, null ) )
253 buf.length = pos * 2;
258 else version( linux )
260 char[] namez = new char[name.length + 1];
261 namez[0 .. name.length] = name;
263 int file = open( namez.ptr, O_RDONLY );
268 scope( exit ) close( file );
276 num = read( file, &buf[pos], cast(uint)( buf.length - pos ) );
282 buf.length = pos * 2;
290 void splitLines( char[] buf, inout char[][] lines )
296 for( ; pos < buf.length; ++pos )
304 lines ~= buf[beg .. pos];
306 if( buf[pos] == '\r' && pos < buf.length - 1 && buf[pos + 1] == '\n' )
314 lines ~= buf[beg .. pos];
319 char[] expandTabs( char[] string, int tabsize = 8 )
321 const dchar LS = '\u2028'; // UTF line separator
322 const dchar PS = '\u2029'; // UTF paragraph separator
324 bool changes = false;
325 char[] result = string;
329 foreach( size_t i, dchar c; string )
334 nspaces = tabsize - (column % tabsize);
339 result.length = string.length + nspaces - 1;
340 result.length = i + nspaces;
341 result[0 .. i] = string[0 .. i];
342 result[i .. i + nspaces] = ' ';
345 { int j = result.length;
346 result.length = j + nspaces;
347 result[j .. j + nspaces] = ' ';