Mercurial > projects > ldc
comparison druntime/src/compiler/dmd/cover.d @ 759:d3eb054172f9
Added copy of druntime from DMD 2.020 modified for LDC.
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Tue, 11 Nov 2008 01:52:37 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
758:f04dde6e882c | 759:d3eb054172f9 |
---|---|
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 core.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 } |