Mercurial > projects > ldc
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 } |