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