comparison druntime/src/compiler/ldc/cover.d @ 1458:e0b2d67cfe7c

Added druntime (this should be removed once it works).
author Robert Clipsham <robert@octarineparrot.com>
date Tue, 02 Jun 2009 17:43:06 +0100
parents
children
comparison
equal deleted inserted replaced
1456:7b218ec1044f 1458:e0b2d67cfe7c
1 /**
2 * Implementation of code coverage analyzer.
3 *
4 * Copyright: Copyright Digital Mars 2000 - 2009.
5 * License: <a href="http://www.boost.org/LICENSE_1_0.txt>Boost License 1.0</a>.
6 * Authors: Walter Bright, Sean Kelly
7 *
8 * Copyright Digital Mars 2000 - 2009.
9 * Distributed under the Boost Software License, Version 1.0.
10 * (See accompanying file LICENSE_1_0.txt or copy at
11 * http://www.boost.org/LICENSE_1_0.txt)
12 */
13 module rt.cover;
14
15 private
16 {
17 version( Windows )
18 import core.sys.windows.windows;
19 else version( Posix )
20 {
21 import core.sys.posix.fcntl;
22 import core.sys.posix.unistd;
23 }
24 import core.bitop;
25 import core.stdc.stdio;
26 import rt.util.utf;
27
28 struct BitArray
29 {
30 size_t len;
31 uint* ptr;
32
33 bool opIndex( size_t i )
34 in
35 {
36 assert( i < len );
37 }
38 body
39 {
40 return cast(bool) bt( ptr, i );
41 }
42 }
43
44 struct Cover
45 {
46 string filename;
47 BitArray valid;
48 uint[] data;
49 }
50
51 __gshared
52 {
53 Cover[] gdata;
54 string srcpath;
55 string dstpath;
56 bool merge;
57 }
58 }
59
60
61 /**
62 * Set path to where source files are located.
63 *
64 * Params:
65 * pathname = The new path name.
66 */
67 extern (C) void dmd_coverSourcePath( string pathname )
68 {
69 srcpath = pathname;
70 }
71
72
73 /**
74 * Set path to where listing files are to be written.
75 *
76 * Params:
77 * pathname = The new path name.
78 */
79 extern (C) void dmd_coverDestPath( string pathname )
80 {
81 dstpath = pathname;
82 }
83
84
85 /**
86 * Set merge mode.
87 *
88 * Params:
89 * flag = true means new data is summed with existing data in the listing
90 * file; false means a new listing file is always created.
91 */
92 extern (C) void dmd_coverSetMerge( bool flag )
93 {
94 merge = flag;
95 }
96
97
98 /**
99 * The coverage callback.
100 *
101 * Params:
102 * filename = The name of the coverage file.
103 * valid = ???
104 * data = ???
105 */
106 extern (C) void _d_cover_register( string filename, BitArray valid, uint[] data )
107 {
108 Cover c;
109
110 c.filename = filename;
111 c.valid = valid;
112 c.data = data;
113 gdata ~= c;
114 }
115
116
117 static ~this()
118 {
119 const NUMLINES = 16384 - 1;
120 const NUMCHARS = 16384 * 16 - 1;
121
122 char[] srcbuf = new char[NUMCHARS];
123 char[][] srclines = new char[][NUMLINES];
124 char[] lstbuf = new char[NUMCHARS];
125 char[][] lstlines = new char[][NUMLINES];
126
127 foreach( Cover c; gdata )
128 {
129 if( !readFile( appendFN( srcpath, c.filename ), srcbuf ) )
130 continue;
131 splitLines( srcbuf, srclines );
132
133 if( merge )
134 {
135 if( !readFile( addExt( baseName( c.filename ), "lst" ), lstbuf ) )
136 break;
137 splitLines( lstbuf, lstlines );
138
139 for( size_t i = 0; i < lstlines.length; ++i )
140 {
141 if( i >= c.data.length )
142 break;
143
144 int count = 0;
145
146 foreach( char c2; lstlines[i] )
147 {
148 switch( c2 )
149 {
150 case ' ':
151 continue;
152 case '0': case '1': case '2': case '3': case '4':
153 case '5': case '6': case '7': case '8': case '9':
154 count = count * 10 + c2 - '0';
155 continue;
156 default:
157 break;
158 }
159 }
160 c.data[i] += count;
161 }
162 }
163
164 FILE* flst = fopen( (addExt( baseName( c.filename ), "lst\0" )).ptr, "wb" );
165
166 if( !flst )
167 continue; //throw new Exception( "Error opening file for write: " ~ lstfn );
168
169 uint nno;
170 uint nyes;
171
172 for( int i = 0; i < c.data.length; i++ )
173 {
174 if( i < srclines.length )
175 {
176 uint n = c.data[i];
177 char[] line = srclines[i];
178
179 line = expandTabs( line );
180
181 if( n == 0 )
182 {
183 if( c.valid[i] )
184 {
185 nno++;
186 fprintf( flst, "0000000|%.*s\n", line );
187 }
188 else
189 {
190 fprintf( flst, " |%.*s\n", line );
191 }
192 }
193 else
194 {
195 nyes++;
196 fprintf( flst, "%7u|%.*s\n", n, line );
197 }
198 }
199 }
200 if( nyes + nno ) // no divide by 0 bugs
201 {
202 fprintf( flst, "%.*s is %d%% covered\n", c.filename, ( nyes * 100 ) / ( nyes + nno ) );
203 }
204 fclose( flst );
205 }
206 }
207
208
209 string appendFN( string path, string name )
210 {
211 version( Windows )
212 const char sep = '\\';
213 else
214 const char sep = '/';
215
216 auto dest = path;
217
218 if( dest && dest[$ - 1] != sep )
219 dest ~= sep;
220 dest ~= name;
221 return dest;
222 }
223
224
225 string baseName( string name, string ext = null )
226 {
227 auto i = name.length;
228 for( ; i > 0; --i )
229 {
230 version( Windows )
231 {
232 if( name[i - 1] == ':' || name[i - 1] == '\\' )
233 break;
234 }
235 else version( Posix )
236 {
237 if( name[i - 1] == '/' )
238 break;
239 }
240 }
241 return chomp( name[i .. $], ext ? ext : "" );
242 }
243
244
245 string getExt( string name )
246 {
247 auto i = name.length;
248
249 while( i > 0 )
250 {
251 if( name[i - 1] == '.' )
252 return name[i .. $];
253 --i;
254 version( Windows )
255 {
256 if( name[i] == ':' || name[i] == '\\' )
257 break;
258 }
259 else version( Posix )
260 {
261 if( name[i] == '/' )
262 break;
263 }
264 }
265 return null;
266 }
267
268
269 string addExt( string name, string ext )
270 {
271 auto existing = getExt( name );
272
273 if( existing.length == 0 )
274 {
275 if( name.length && name[$ - 1] == '.' )
276 name ~= ext;
277 else
278 name = name ~ "." ~ ext;
279 }
280 else
281 {
282 name = name[0 .. $ - existing.length] ~ ext;
283 }
284 return name;
285 }
286
287
288 string chomp( string str, string delim = null )
289 {
290 if( delim is null )
291 {
292 auto len = str.length;
293
294 if( len )
295 {
296 auto c = str[len - 1];
297
298 if( c == '\r' )
299 --len;
300 else if( c == '\n' && str[--len - 1] == '\r' )
301 --len;
302 }
303 return str[0 .. len];
304 }
305 else if( str.length >= delim.length )
306 {
307 if( str[$ - delim.length .. $] == delim )
308 return str[0 .. $ - delim.length];
309 }
310 return str;
311 }
312
313
314 bool readFile( string name, inout char[] buf )
315 {
316 version( Windows )
317 {
318 auto wnamez = toUTF16z( name );
319 HANDLE file = CreateFileW( wnamez,
320 GENERIC_READ,
321 FILE_SHARE_READ,
322 null,
323 OPEN_EXISTING,
324 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
325 cast(HANDLE) null );
326
327 delete wnamez;
328 if( file == INVALID_HANDLE_VALUE )
329 return false;
330 scope( exit ) CloseHandle( file );
331
332 DWORD num = 0;
333 DWORD pos = 0;
334
335 buf.length = 4096;
336 while( true )
337 {
338 if( !ReadFile( file, &buf[pos], cast(DWORD)( buf.length - pos ), &num, null ) )
339 return false;
340 if( !num )
341 break;
342 pos += num;
343 buf.length = pos * 2;
344 }
345 buf.length = pos;
346 return true;
347 }
348 else version( Posix )
349 {
350 char[] namez = new char[name.length + 1];
351 namez[0 .. name.length] = name;
352 namez[$ - 1] = 0;
353 int file = open( namez.ptr, O_RDONLY );
354
355 delete namez;
356 if( file == -1 )
357 return false;
358 scope( exit ) close( file );
359
360 int num = 0;
361 uint pos = 0;
362
363 buf.length = 4096;
364 while( true )
365 {
366 num = read( file, &buf[pos], cast(uint)( buf.length - pos ) );
367 if( num == -1 )
368 return false;
369 if( !num )
370 break;
371 pos += num;
372 buf.length = pos * 2;
373 }
374 buf.length = pos;
375 return true;
376 }
377 }
378
379
380 void splitLines( char[] buf, inout char[][] lines )
381 {
382 size_t beg = 0,
383 pos = 0;
384
385 lines.length = 0;
386 for( ; pos < buf.length; ++pos )
387 {
388 char c = buf[pos];
389
390 switch( buf[pos] )
391 {
392 case '\r':
393 case '\n':
394 lines ~= buf[beg .. pos];
395 beg = pos + 1;
396 if( buf[pos] == '\r' && pos < buf.length - 1 && buf[pos + 1] == '\n' )
397 ++pos, ++beg;
398 default:
399 continue;
400 }
401 }
402 if( beg != pos )
403 {
404 lines ~= buf[beg .. pos];
405 }
406 }
407
408
409 char[] expandTabs( char[] str, int tabsize = 8 )
410 {
411 const dchar LS = '\u2028'; // UTF line separator
412 const dchar PS = '\u2029'; // UTF paragraph separator
413
414 bool changes = false;
415 char[] result = str;
416 int column;
417 int nspaces;
418
419 foreach( size_t i, dchar c; str )
420 {
421 switch( c )
422 {
423 case '\t':
424 nspaces = tabsize - (column % tabsize);
425 if( !changes )
426 {
427 changes = true;
428 result = null;
429 result.length = str.length + nspaces - 1;
430 result.length = i + nspaces;
431 result[0 .. i] = str[0 .. i];
432 result[i .. i + nspaces] = ' ';
433 }
434 else
435 { int j = result.length;
436 result.length = j + nspaces;
437 result[j .. j + nspaces] = ' ';
438 }
439 column += nspaces;
440 break;
441
442 case '\r':
443 case '\n':
444 case PS:
445 case LS:
446 column = 0;
447 goto L1;
448
449 default:
450 column++;
451 L1:
452 if (changes)
453 {
454 if (c <= 0x7F)
455 result ~= c;
456 else
457 encode(result, c);
458 }
459 break;
460 }
461 }
462 return result;
463 }