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