]>
Commit | Line | Data |
---|---|---|
ca29da43 NS |
1 | /* File format for coverage information |
2 | Copyright (C) 1996, 1997, 1998, 2000, 2002, | |
3 | 2003 Free Software Foundation, Inc. | |
4 | Contributed by Bob Manson <manson@cygnus.com>. | |
5 | Completely remangled by Nathan Sidwell <nathan@codesourcery.com>. | |
6 | ||
7 | This file is part of GCC. | |
8 | ||
9 | GCC is free software; you can redistribute it and/or modify it under | |
10 | the terms of the GNU General Public License as published by the Free | |
11 | Software Foundation; either version 2, or (at your option) any later | |
12 | version. | |
13 | ||
14 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
15 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 | for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with GCC; see the file COPYING. If not, write to the Free | |
21 | Software Foundation, 59 Temple Place - Suite 330, Boston, MA | |
22 | 02111-1307, USA. */ | |
23 | ||
24 | /* Routines declared in gcov-io.h. This file should be #included by | |
25 | another source file, after having #included gcov-io.h. */ | |
26 | ||
27 | /* Open a gcov file. NAME is the name of the file to open and MODE | |
28 | indicates whether a new file should be created, or an existing file | |
29 | opened for modification. If MODE is >= 0 an existing file will be | |
30 | opened, if possible, and if MODE is <= 0, a new file will be | |
31 | created. Use MODE=0 to attempt to reopen an existing file and then | |
32 | fall back on creating a new one. Return zero on failure, >0 on | |
33 | opening an existing file and <0 on creating a new one. */ | |
34 | ||
35 | GCOV_LINKAGE int | |
36 | gcov_open (const char *name, int mode) | |
37 | { | |
38 | int result = 1; | |
39 | size_t alloc = 1024; | |
40 | #if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV | |
41 | struct flock s_flock; | |
42 | ||
43 | s_flock.l_type = F_WRLCK; | |
44 | s_flock.l_whence = SEEK_SET; | |
45 | s_flock.l_start = 0; | |
46 | s_flock.l_len = 0; /* Until EOF. */ | |
47 | s_flock.l_pid = getpid (); | |
48 | #endif | |
49 | ||
50 | if (gcov_var.file) | |
51 | abort (); | |
52 | gcov_var.position = gcov_var.length = 0; | |
53 | gcov_var.error = gcov_var.modified = 0; | |
54 | if (mode >= 0) | |
55 | gcov_var.file = fopen (name, "r+b"); | |
56 | if (!gcov_var.file && mode <= 0) | |
57 | { | |
58 | result = -1; | |
59 | gcov_var.file = fopen (name, "w+b"); | |
60 | } | |
61 | if (!gcov_var.file) | |
62 | return 0; | |
63 | ||
64 | #if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV | |
65 | while (fcntl (fileno (gcov_var.file), F_SETLKW, &s_flock) | |
66 | && errno == EINTR) | |
67 | continue; | |
68 | #endif | |
69 | ||
70 | if (result >= 0) | |
71 | { | |
72 | if (fseek (gcov_var.file, 0, SEEK_END)) | |
73 | { | |
74 | fclose (gcov_var.file); | |
75 | gcov_var.file = 0; | |
76 | return 0; | |
77 | } | |
78 | gcov_var.length = ftell (gcov_var.file); | |
79 | fseek (gcov_var.file, 0, SEEK_SET); | |
80 | alloc += gcov_var.length; | |
81 | } | |
82 | if (alloc > gcov_var.alloc) | |
83 | { | |
84 | if (gcov_var.buffer) | |
85 | free (gcov_var.buffer); | |
86 | gcov_var.alloc = alloc; | |
87 | #if IN_LIBGCOV | |
88 | gcov_var.buffer = malloc (gcov_var.alloc); | |
89 | if (!gcov_var.buffer) | |
90 | { | |
91 | fclose (gcov_var.file); | |
92 | gcov_var.file = 0; | |
93 | gcov_var.length = 0; | |
94 | gcov_var.alloc = 0; | |
95 | return 0; | |
96 | } | |
97 | #else | |
98 | gcov_var.buffer = xmalloc (gcov_var.alloc); | |
99 | #endif | |
100 | } | |
101 | if (result >= 0 | |
102 | && fread (gcov_var.buffer, gcov_var.length, 1, gcov_var.file) != 1) | |
103 | { | |
104 | fclose (gcov_var.file); | |
105 | gcov_var.file = 0; | |
106 | gcov_var.length = 0; | |
107 | return 0; | |
108 | } | |
109 | return result; | |
110 | } | |
111 | ||
112 | /* Close the current gcov file. Flushes data to disk. Returns nonzero | |
113 | on failure or error flag set. */ | |
114 | ||
115 | GCOV_LINKAGE int | |
116 | gcov_close () | |
117 | { | |
118 | int result = 0; | |
119 | ||
120 | if (gcov_var.file) | |
121 | { | |
122 | if (gcov_var.modified | |
123 | && (fseek (gcov_var.file, 0, SEEK_SET) | |
124 | || fwrite (gcov_var.buffer, gcov_var.length, | |
125 | 1, gcov_var.file) != 1)) | |
126 | result = 1; | |
127 | fclose (gcov_var.file); | |
128 | gcov_var.file = 0; | |
129 | gcov_var.length = 0; | |
130 | } | |
131 | #if !IN_LIBGCOV | |
132 | free (gcov_var.buffer); | |
133 | gcov_var.alloc = 0; | |
134 | gcov_var.buffer = 0; | |
135 | #endif | |
136 | return result ? 1 : gcov_var.error; | |
137 | } | |
138 | ||
139 | #if !IN_GCOV | |
140 | /* Allocate space to write BYTES bytes to the gcov file. Return a | |
141 | pointer to those bytes, or NULL on failure. */ | |
142 | ||
143 | GCOV_LINKAGE unsigned char * | |
144 | gcov_write_bytes (unsigned bytes) | |
145 | { | |
146 | char unsigned *result; | |
147 | ||
148 | if (gcov_var.position + bytes > gcov_var.alloc) | |
149 | { | |
150 | size_t new_size = (gcov_var.alloc + bytes) * 3 / 2; | |
151 | ||
152 | if (!gcov_var.buffer) | |
153 | return 0; | |
154 | #if IN_LIBGCOV | |
155 | result = realloc (gcov_var.buffer, new_size); | |
156 | if (!result) | |
157 | { | |
158 | free (gcov_var.buffer); | |
159 | gcov_var.buffer = 0; | |
160 | gcov_var.alloc = 0; | |
161 | gcov_var.position = gcov_var.length = 0; | |
162 | gcov_var.error = 1; | |
163 | return 0; | |
164 | } | |
165 | #else | |
166 | result = xrealloc (gcov_var.buffer, new_size); | |
167 | #endif | |
168 | gcov_var.alloc = new_size; | |
169 | gcov_var.buffer = result; | |
170 | } | |
171 | ||
172 | result = &gcov_var.buffer[gcov_var.position]; | |
173 | gcov_var.position += bytes; | |
174 | gcov_var.modified = 1; | |
175 | if (gcov_var.position > gcov_var.length) | |
176 | gcov_var.length = gcov_var.position; | |
177 | return result; | |
178 | } | |
179 | ||
180 | /* Write unsigned VALUE to coverage file. Sets error flag | |
181 | appropriately. */ | |
182 | ||
183 | GCOV_LINKAGE void | |
184 | gcov_write_unsigned (unsigned value) | |
185 | { | |
186 | unsigned char *buffer = gcov_write_bytes (4); | |
187 | unsigned ix; | |
188 | ||
189 | if (!buffer) | |
190 | return; | |
191 | for (ix = 4; ix--; ) | |
192 | { | |
193 | buffer[ix] = value; | |
194 | value >>= 8; | |
195 | } | |
196 | if (sizeof (value) > 4 && value) | |
197 | gcov_var.error = -1; | |
198 | ||
199 | return; | |
200 | } | |
201 | ||
202 | /* Write counter VALUE to coverage file. Sets error flag | |
203 | appropriately. */ | |
204 | ||
205 | #if IN_LIBGCOV | |
206 | GCOV_LINKAGE void | |
207 | gcov_write_counter (gcov_type value) | |
208 | { | |
209 | unsigned char *buffer = gcov_write_bytes (8); | |
210 | unsigned ix; | |
211 | ||
212 | if (!buffer) | |
213 | return; | |
214 | for (ix = 8; ix--; ) | |
215 | { | |
216 | buffer[ix] = value; | |
217 | value >>= 8; | |
218 | } | |
219 | if ((sizeof (value) > 8 && value) || value < 0) | |
220 | gcov_var.error = -1; | |
221 | return; | |
222 | } | |
223 | #endif /* IN_LIBGCOV */ | |
224 | ||
796621e8 | 225 | #if !IN_LIBGCOV |
ca29da43 NS |
226 | /* Write STRING to coverage file. Sets error flag on file |
227 | error, overflow flag on overflow */ | |
228 | ||
229 | GCOV_LINKAGE void | |
230 | gcov_write_string (const char *string) | |
231 | { | |
232 | unsigned length = 0; | |
233 | unsigned pad = 0; | |
234 | unsigned rem = 0; | |
235 | unsigned char *buffer; | |
236 | ||
237 | if (string) | |
238 | { | |
239 | length = strlen (string); | |
240 | rem = 4 - (length & 3); | |
241 | } | |
242 | ||
243 | buffer = gcov_write_bytes (4 + length + rem); | |
244 | if (buffer) | |
245 | { | |
246 | unsigned ix; | |
247 | unsigned value = length; | |
248 | ||
249 | for (ix = 4; ix--; ) | |
250 | { | |
251 | buffer[ix] = value; | |
252 | value >>= 8; | |
253 | } | |
254 | memcpy (buffer + 4, string, length); | |
255 | memcpy (buffer + 4 + length, &pad, rem); | |
256 | } | |
257 | } | |
796621e8 | 258 | #endif |
ca29da43 NS |
259 | |
260 | /* Write a tag TAG and reserve space for the record length. Return a | |
261 | value to be used for gcov_write_length. */ | |
262 | ||
263 | GCOV_LINKAGE unsigned long | |
264 | gcov_write_tag (unsigned tag) | |
265 | { | |
266 | unsigned long result = gcov_var.position; | |
267 | unsigned char *buffer = gcov_write_bytes (8); | |
268 | unsigned ix; | |
269 | ||
270 | if (!buffer) | |
271 | return 0; | |
272 | for (ix = 4; ix--; ) | |
273 | { | |
274 | buffer[ix] = tag; | |
275 | tag >>= 8; | |
276 | } | |
277 | memset (buffer + 4, 0, 4); | |
278 | return result; | |
279 | } | |
280 | ||
281 | /* Write a record length using POSITION, which was returned by | |
282 | gcov_write_tag. The current file position is the end of the | |
283 | record, and is restored before returning. Returns nonzero on | |
284 | overflow. */ | |
285 | ||
286 | GCOV_LINKAGE void | |
287 | gcov_write_length (unsigned long position) | |
288 | { | |
289 | if (position) | |
290 | { | |
291 | unsigned length = gcov_var.position - position - 8; | |
292 | unsigned char *buffer = &gcov_var.buffer[position + 4]; | |
293 | unsigned ix; | |
294 | ||
295 | for (ix = 4; ix--; ) | |
296 | { | |
297 | buffer[ix] = length; | |
298 | length >>= 8; | |
299 | } | |
300 | } | |
301 | } | |
302 | ||
303 | #if IN_LIBGCOV | |
304 | /* Write a summary structure to the gcov file. Return non-zero on | |
305 | overflow. */ | |
306 | ||
307 | GCOV_LINKAGE void | |
308 | gcov_write_summary (unsigned tag, const struct gcov_summary *summary) | |
309 | { | |
cdb23767 NS |
310 | unsigned ix; |
311 | const struct gcov_ctr_summary *csum; | |
ca29da43 NS |
312 | unsigned long base; |
313 | ||
314 | base = gcov_write_tag (tag); | |
315 | gcov_write_unsigned (summary->checksum); | |
cdb23767 NS |
316 | for (csum = summary->ctrs, ix = GCOV_COUNTERS; ix--; csum++) |
317 | { | |
318 | gcov_write_unsigned (csum->num); | |
319 | gcov_write_unsigned (csum->runs); | |
320 | gcov_write_counter (csum->sum_all); | |
321 | gcov_write_counter (csum->run_max); | |
322 | gcov_write_counter (csum->sum_max); | |
323 | } | |
ca29da43 NS |
324 | gcov_write_length (base); |
325 | } | |
326 | #endif /* IN_LIBGCOV */ | |
327 | ||
328 | #endif /*!IN_GCOV */ | |
329 | ||
330 | /* Return a pointer to read BYTES bytes from the gcov file. Returns | |
331 | NULL on failure (read past EOF). */ | |
332 | ||
333 | GCOV_LINKAGE const unsigned char * | |
334 | gcov_read_bytes (unsigned bytes) | |
335 | { | |
336 | const unsigned char *result; | |
337 | ||
338 | if (gcov_var.position + bytes > gcov_var.length) | |
339 | { | |
340 | gcov_var.error = 1; | |
341 | return 0; | |
342 | } | |
343 | ||
344 | result = &gcov_var.buffer[gcov_var.position]; | |
345 | gcov_var.position += bytes; | |
346 | return result; | |
347 | } | |
348 | ||
349 | /* Read unsigned value from a coverage file. Sets error flag on file | |
350 | error, overflow flag on overflow */ | |
351 | ||
352 | GCOV_LINKAGE unsigned | |
353 | gcov_read_unsigned () | |
354 | { | |
355 | unsigned value = 0; | |
356 | unsigned ix; | |
357 | const unsigned char *buffer = gcov_read_bytes (4); | |
358 | ||
359 | if (!buffer) | |
360 | return 0; | |
361 | for (ix = sizeof (value); ix < 4; ix++) | |
362 | if (buffer[ix]) | |
363 | gcov_var.error = -1; | |
364 | for (ix = 0; ix != 4; ix++) | |
365 | { | |
366 | value <<= 8; | |
367 | value |= buffer[ix]; | |
368 | } | |
369 | return value; | |
370 | } | |
371 | ||
372 | /* Read counter value from a coverage file. Sets error flag on file | |
373 | error, overflow flag on overflow */ | |
374 | ||
375 | GCOV_LINKAGE gcov_type | |
376 | gcov_read_counter () | |
377 | { | |
378 | gcov_type value = 0; | |
379 | unsigned ix; | |
380 | const unsigned char *buffer = gcov_read_bytes (8); | |
381 | ||
382 | if (!buffer) | |
383 | return 0; | |
384 | for (ix = sizeof (value); ix < 8; ix++) | |
385 | if (buffer[ix]) | |
386 | gcov_var.error = -1; | |
387 | for (ix = 0; ix != 8; ix++) | |
388 | { | |
389 | value <<= 8; | |
390 | value |= buffer[ix]; | |
391 | } | |
392 | if (value < 0) | |
393 | gcov_var.error = -1; | |
394 | return value; | |
395 | } | |
396 | ||
397 | /* Read string from coverage file. Returns a pointer to a static | |
398 | buffer, or NULL on empty string. You must copy the string before | |
399 | calling another gcov function. */ | |
400 | ||
796621e8 | 401 | #if !IN_LIBGCOV |
ca29da43 NS |
402 | GCOV_LINKAGE const char * |
403 | gcov_read_string () | |
404 | { | |
405 | unsigned length = gcov_read_unsigned (); | |
406 | ||
407 | if (!length) | |
408 | return 0; | |
409 | ||
410 | length += 4 - (length & 3); | |
411 | return (const char *) gcov_read_bytes (length); | |
412 | } | |
796621e8 | 413 | #endif |
ca29da43 NS |
414 | |
415 | GCOV_LINKAGE void | |
416 | gcov_read_summary (struct gcov_summary *summary) | |
417 | { | |
cdb23767 NS |
418 | unsigned ix; |
419 | struct gcov_ctr_summary *csum; | |
420 | ||
ca29da43 | 421 | summary->checksum = gcov_read_unsigned (); |
cdb23767 NS |
422 | for (csum = summary->ctrs, ix = GCOV_COUNTERS; ix--; csum++) |
423 | { | |
424 | csum->num = gcov_read_unsigned (); | |
425 | csum->runs = gcov_read_unsigned (); | |
426 | csum->sum_all = gcov_read_counter (); | |
427 | csum->run_max = gcov_read_counter (); | |
428 | csum->sum_max = gcov_read_counter (); | |
429 | } | |
ca29da43 NS |
430 | } |
431 | ||
ca29da43 NS |
432 | #if IN_GCOV > 0 |
433 | /* Return the modification time of the current gcov file. */ | |
434 | ||
435 | GCOV_LINKAGE time_t | |
436 | gcov_time () | |
437 | { | |
438 | struct stat status; | |
439 | ||
440 | if (fstat (fileno (gcov_var.file), &status)) | |
441 | return 0; | |
442 | else | |
443 | return status.st_mtime; | |
444 | } | |
445 | #endif /* IN_GCOV */ |