]> git.llucax.com Git - software/druntime.git/blob - src/compiler/dmd/cover.d
7d880e1c059efabb474545bc209196a8bfef1b7f
[software/druntime.git] / src / compiler / dmd / cover.d
1 /**
2  * Code coverage analyzer.
3  *
4  * Bugs:
5  *      $(UL
6  *          $(LI the execution counters are 32 bits in size, and can overflow)
7  *          $(LI inline asm statements are not counted)
8  *      )
9  *
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
13  */
14
15 module rt. cover;
16
17 private
18 {
19     version( Windows )
20         import sys.windows.windows;
21     else version( Posix )
22         import stdc.posix.unistd;
23     import bitmanip;
24     import stdc.stdio;
25     import util.utf;
26
27     struct BitArray
28     {
29         size_t  len;
30         uint*   ptr;
31
32         bool opIndex( size_t i )
33         in
34         {
35             assert( i < len );
36         }
37         body
38         {
39             return cast(bool) bt( ptr, i );
40         }
41     }
42
43     struct Cover
44     {
45         char[]      filename;
46         BitArray    valid;
47         uint[]      data;
48     }
49
50     Cover[] gdata;
51     char[]  srcpath;
52     char[]  dstpath;
53     bool    merge;
54 }
55
56
57 /**
58  * Set path to where source files are located.
59  *
60  * Params:
61  *  pathname = The new path name.
62  */
63 extern (C) void dmd_coverSourcePath( char[] pathname )
64 {
65     srcpath = pathname;
66 }
67
68
69 /**
70  * Set path to where listing files are to be written.
71  *
72  * Params:
73  *  pathname = The new path name.
74  */
75 extern (C) void dmd_coverDestPath( char[] pathname )
76 {
77     dstpath = pathname;
78 }
79
80
81 /**
82  * Set merge mode.
83  *
84  * Params:
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.
87  */
88 extern (C) void dmd_coverSetMerge( bool flag )
89 {
90     merge = flag;
91 }
92
93
94 /**
95  * The coverage callback.
96  *
97  * Params:
98  *  filename = The name of the coverage file.
99  *  valid    = ???
100  *  data     = ???
101  */
102 extern (C) void _d_cover_register( char[] filename, BitArray valid, uint[] data )
103 {
104     Cover c;
105
106     c.filename  = filename;
107     c.valid     = valid;
108     c.data      = data;
109     gdata      ~= c;
110 }
111
112
113 static ~this()
114 {
115     const NUMLINES = 16384 - 1;
116     const NUMCHARS = 16384 * 16 - 1;
117
118     char[]      srcbuf      = new char[NUMCHARS];
119     char[][]    srclines    = new char[][NUMLINES];
120     char[]      lstbuf      = new char[NUMCHARS];
121     char[][]    lstlines    = new char[][NUMLINES];
122
123     foreach( Cover c; gdata )
124     {
125         if( !readFile( appendFN( srcpath, c.filename ), srcbuf ) )
126             continue;
127         splitLines( srcbuf, srclines );
128
129         if( merge )
130         {
131             if( !readFile( c.filename ~ ".lst", lstbuf ) )
132                 break;
133             splitLines( lstbuf, lstlines );
134
135             for( size_t i = 0; i < lstlines.length; ++i )
136             {
137                 if( i >= c.data.length )
138                     break;
139
140                 int count = 0;
141
142                 foreach( char c2; lstlines[i] )
143                 {
144                     switch( c2 )
145                     {
146                     case ' ':
147                         continue;
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';
151                         continue;
152                     default:
153                         break;
154                     }
155                 }
156                 c.data[i] += count;
157             }
158         }
159
160         FILE* flst = fopen( (c.filename ~ ".lst").ptr, "wb" );
161
162         if( !flst )
163             continue; //throw new Exception( "Error opening file for write: " ~ lstfn );
164
165         uint nno;
166         uint nyes;
167
168         for( int i = 0; i < c.data.length; i++ )
169         {
170             if( i < srclines.length )
171             {
172                 uint    n    = c.data[i];
173                 char[]  line = srclines[i];
174
175                 line = expandTabs( line );
176
177                 if( n == 0 )
178                 {
179                     if( c.valid[i] )
180                     {
181                         nno++;
182                         fprintf( flst, "0000000|%.*s\n", line );
183                     }
184                     else
185                     {
186                         fprintf( flst, "       |%.*s\n", line );
187                     }
188                 }
189                 else
190                 {
191                     nyes++;
192                     fprintf( flst, "%7u|%.*s\n", n, line );
193                 }
194             }
195         }
196         if( nyes + nno ) // no divide by 0 bugs
197         {
198             fprintf( flst, "%.*s is %d%% covered\n", c.filename, ( nyes * 100 ) / ( nyes + nno ) );
199         }
200         fclose( flst );
201     }
202 }
203
204
205 char[] appendFN( char[] path, char[] name )
206 {
207     version( Windows )
208         const char sep = '\\';
209     else
210         const char sep = '/';
211
212     char[] dest = path;
213
214     if( dest && dest[$ - 1] != sep )
215         dest ~= sep;
216     dest ~= name;
217     return dest;
218 }
219
220
221 bool readFile( char[] name, inout char[] buf )
222 {
223     version( Windows )
224     {
225         auto    wnamez  = toUTF16z( name );
226         HANDLE  file    = CreateFileW( wnamez,
227                                        GENERIC_READ,
228                                        FILE_SHARE_READ,
229                                        null,
230                                        OPEN_EXISTING,
231                                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
232                                        cast(HANDLE) null );
233
234         delete wnamez;
235         if( file == INVALID_HANDLE_VALUE )
236             return false;
237         scope( exit ) CloseHandle( file );
238
239         DWORD   num = 0;
240         DWORD   pos = 0;
241
242         buf.length = 4096;
243         while( true )
244         {
245             if( !ReadFile( file, &buf[pos], cast(DWORD)( buf.length - pos ), &num, null ) )
246                 return false;
247             if( !num )
248                 break;
249             pos += num;
250             buf.length = pos * 2;
251         }
252         buf.length = pos;
253         return true;
254     }
255     else version( linux )
256     {
257         char[]  namez = new char[name.length + 1];
258                         namez[0 .. name.length] = name;
259                         namez[$ - 1] = 0;
260         int     file = open( namez.ptr, O_RDONLY );
261
262         delete namez;
263         if( file == -1 )
264             return false;
265         scope( exit ) close( file );
266
267         int     num = 0;
268         uint    pos = 0;
269
270         buf.length = 4096;
271         while( true )
272         {
273             num = read( file, &buf[pos], cast(uint)( buf.length - pos ) );
274             if( num == -1 )
275                 return false;
276             if( !num )
277                 break;
278             pos += num;
279             buf.length = pos * 2;
280         }
281         buf.length = pos;
282         return true;
283     }
284 }
285
286
287 void splitLines( char[] buf, inout char[][] lines )
288 {
289     size_t  beg = 0,
290             pos = 0;
291
292     lines.length = 0;
293     for( ; pos < buf.length; ++pos )
294     {
295         char c = buf[pos];
296
297         switch( buf[pos] )
298         {
299         case '\r':
300         case '\n':
301             lines ~= buf[beg .. pos];
302             beg = pos + 1;
303             if( buf[pos] == '\r' && pos < buf.length - 1 && buf[pos + 1] == '\n' )
304                 ++pos, ++beg;
305         default:
306             continue;
307         }
308     }
309     if( beg != pos )
310     {
311         lines ~= buf[beg .. pos];
312     }
313 }
314
315
316 char[] expandTabs( char[] string, int tabsize = 8 )
317 {
318     const dchar LS = '\u2028'; // UTF line separator
319     const dchar PS = '\u2029'; // UTF paragraph separator
320
321     bool changes = false;
322     char[] result = string;
323     int column;
324     int nspaces;
325
326     foreach( size_t i, dchar c; string )
327     {
328         switch( c )
329         {
330             case '\t':
331                 nspaces = tabsize - (column % tabsize);
332                 if( !changes )
333                 {
334                     changes = true;
335                     result = null;
336                     result.length = string.length + nspaces - 1;
337                     result.length = i + nspaces;
338                     result[0 .. i] = string[0 .. i];
339                     result[i .. i + nspaces] = ' ';
340                 }
341                 else
342                 {   int j = result.length;
343                     result.length = j + nspaces;
344                     result[j .. j + nspaces] = ' ';
345                 }
346                 column += nspaces;
347                 break;
348
349             case '\r':
350             case '\n':
351             case PS:
352             case LS:
353                 column = 0;
354                 goto L1;
355
356             default:
357                 column++;
358             L1:
359                 if (changes)
360                 {
361                     if (c <= 0x7F)
362                         result ~= c;
363                     else
364                         encode(result, c);
365                 }
366                 break;
367         }
368     }
369     return result;
370 }