0
|
1 /* Read and write coverage files, and associated functionality.
|
|
2 Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
|
|
3 2000, 2001, 2003, 2004, 2005, 2007, 2008 Free Software Foundation,
|
|
4 Inc.
|
|
5 Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
|
|
6 based on some ideas from Dain Samples of UC Berkeley.
|
|
7 Further mangling by Bob Manson, Cygnus Support.
|
|
8 Further mangled by Nathan Sidwell, CodeSourcery
|
|
9
|
|
10 This file is part of GCC.
|
|
11
|
|
12 GCC is free software; you can redistribute it and/or modify it under
|
|
13 the terms of the GNU General Public License as published by the Free
|
|
14 Software Foundation; either version 3, or (at your option) any later
|
|
15 version.
|
|
16
|
|
17 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
19 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
20 for more details.
|
|
21
|
|
22 You should have received a copy of the GNU General Public License
|
|
23 along with GCC; see the file COPYING3. If not see
|
|
24 <http://www.gnu.org/licenses/>. */
|
|
25
|
|
26
|
|
27 #define GCOV_LINKAGE
|
|
28
|
|
29 #include "config.h"
|
|
30 #include "system.h"
|
|
31 #include "coretypes.h"
|
|
32 #include "tm.h"
|
|
33 #include "rtl.h"
|
|
34 #include "tree.h"
|
|
35 #include "flags.h"
|
|
36 #include "output.h"
|
|
37 #include "regs.h"
|
|
38 #include "expr.h"
|
|
39 #include "function.h"
|
|
40 #include "toplev.h"
|
|
41 #include "tm_p.h"
|
|
42 #include "ggc.h"
|
|
43 #include "coverage.h"
|
|
44 #include "langhooks.h"
|
|
45 #include "hashtab.h"
|
|
46 #include "tree-iterator.h"
|
|
47 #include "cgraph.h"
|
|
48 #include "tree-pass.h"
|
|
49
|
|
50 #include "gcov-io.c"
|
|
51
|
|
52 struct function_list
|
|
53 {
|
|
54 struct function_list *next; /* next function */
|
|
55 unsigned ident; /* function ident */
|
|
56 unsigned checksum; /* function checksum */
|
|
57 unsigned n_ctrs[GCOV_COUNTERS];/* number of counters. */
|
|
58 };
|
|
59
|
|
60 /* Counts information for a function. */
|
|
61 typedef struct counts_entry
|
|
62 {
|
|
63 /* We hash by */
|
|
64 unsigned ident;
|
|
65 unsigned ctr;
|
|
66
|
|
67 /* Store */
|
|
68 unsigned checksum;
|
|
69 gcov_type *counts;
|
|
70 struct gcov_ctr_summary summary;
|
|
71
|
|
72 /* Workspace */
|
|
73 struct counts_entry *chain;
|
|
74
|
|
75 } counts_entry_t;
|
|
76
|
|
77 static struct function_list *functions_head = 0;
|
|
78 static struct function_list **functions_tail = &functions_head;
|
|
79 static unsigned no_coverage = 0;
|
|
80
|
|
81 /* Cumulative counter information for whole program. */
|
|
82 static unsigned prg_ctr_mask; /* Mask of counter types generated. */
|
|
83 static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated. */
|
|
84
|
|
85 /* Counter information for current function. */
|
|
86 static unsigned fn_ctr_mask; /* Mask of counters used. */
|
|
87 static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */
|
|
88 static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */
|
|
89
|
|
90 /* Name of the output file for coverage output file. */
|
|
91 static char *bbg_file_name;
|
|
92 static unsigned bbg_file_opened;
|
|
93 static int bbg_function_announced;
|
|
94
|
|
95 /* Name of the count data file. */
|
|
96 static char *da_file_name;
|
|
97
|
|
98 /* Hash table of count data. */
|
|
99 static htab_t counts_hash = NULL;
|
|
100
|
|
101 /* Trees representing the counter table arrays. */
|
|
102 static GTY(()) tree tree_ctr_tables[GCOV_COUNTERS];
|
|
103
|
|
104 /* The names of the counter tables. Not used if we're
|
|
105 generating counters at tree level. */
|
|
106 static GTY(()) rtx ctr_labels[GCOV_COUNTERS];
|
|
107
|
|
108 /* The names of merge functions for counters. */
|
|
109 static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS;
|
|
110 static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES;
|
|
111
|
|
112 /* Forward declarations. */
|
|
113 static hashval_t htab_counts_entry_hash (const void *);
|
|
114 static int htab_counts_entry_eq (const void *, const void *);
|
|
115 static void htab_counts_entry_del (void *);
|
|
116 static void read_counts_file (void);
|
|
117 static unsigned compute_checksum (void);
|
|
118 static unsigned coverage_checksum_string (unsigned, const char *);
|
|
119 static tree build_fn_info_type (unsigned);
|
|
120 static tree build_fn_info_value (const struct function_list *, tree);
|
|
121 static tree build_ctr_info_type (void);
|
|
122 static tree build_ctr_info_value (unsigned, tree);
|
|
123 static tree build_gcov_info (void);
|
|
124 static void create_coverage (void);
|
|
125
|
|
126 /* Return the type node for gcov_type. */
|
|
127
|
|
128 tree
|
|
129 get_gcov_type (void)
|
|
130 {
|
|
131 return lang_hooks.types.type_for_size (GCOV_TYPE_SIZE, false);
|
|
132 }
|
|
133
|
|
134 /* Return the type node for gcov_unsigned_t. */
|
|
135
|
|
136 static tree
|
|
137 get_gcov_unsigned_t (void)
|
|
138 {
|
|
139 return lang_hooks.types.type_for_size (32, true);
|
|
140 }
|
|
141
|
|
142 static hashval_t
|
|
143 htab_counts_entry_hash (const void *of)
|
|
144 {
|
|
145 const counts_entry_t *const entry = (const counts_entry_t *) of;
|
|
146
|
|
147 return entry->ident * GCOV_COUNTERS + entry->ctr;
|
|
148 }
|
|
149
|
|
150 static int
|
|
151 htab_counts_entry_eq (const void *of1, const void *of2)
|
|
152 {
|
|
153 const counts_entry_t *const entry1 = (const counts_entry_t *) of1;
|
|
154 const counts_entry_t *const entry2 = (const counts_entry_t *) of2;
|
|
155
|
|
156 return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
|
|
157 }
|
|
158
|
|
159 static void
|
|
160 htab_counts_entry_del (void *of)
|
|
161 {
|
|
162 counts_entry_t *const entry = (counts_entry_t *) of;
|
|
163
|
|
164 free (entry->counts);
|
|
165 free (entry);
|
|
166 }
|
|
167
|
|
168 /* Read in the counts file, if available. */
|
|
169
|
|
170 static void
|
|
171 read_counts_file (void)
|
|
172 {
|
|
173 gcov_unsigned_t fn_ident = 0;
|
|
174 gcov_unsigned_t checksum = -1;
|
|
175 counts_entry_t *summaried = NULL;
|
|
176 unsigned seen_summary = 0;
|
|
177 gcov_unsigned_t tag;
|
|
178 int is_error = 0;
|
|
179
|
|
180 if (!gcov_open (da_file_name, 1))
|
|
181 return;
|
|
182
|
|
183 if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
|
|
184 {
|
|
185 warning (0, "%qs is not a gcov data file", da_file_name);
|
|
186 gcov_close ();
|
|
187 return;
|
|
188 }
|
|
189 else if ((tag = gcov_read_unsigned ()) != GCOV_VERSION)
|
|
190 {
|
|
191 char v[4], e[4];
|
|
192
|
|
193 GCOV_UNSIGNED2STRING (v, tag);
|
|
194 GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
|
|
195
|
|
196 warning (0, "%qs is version %q.*s, expected version %q.*s",
|
|
197 da_file_name, 4, v, 4, e);
|
|
198 gcov_close ();
|
|
199 return;
|
|
200 }
|
|
201
|
|
202 /* Read and discard the stamp. */
|
|
203 gcov_read_unsigned ();
|
|
204
|
|
205 counts_hash = htab_create (10,
|
|
206 htab_counts_entry_hash, htab_counts_entry_eq,
|
|
207 htab_counts_entry_del);
|
|
208 while ((tag = gcov_read_unsigned ()))
|
|
209 {
|
|
210 gcov_unsigned_t length;
|
|
211 gcov_position_t offset;
|
|
212
|
|
213 length = gcov_read_unsigned ();
|
|
214 offset = gcov_position ();
|
|
215 if (tag == GCOV_TAG_FUNCTION)
|
|
216 {
|
|
217 fn_ident = gcov_read_unsigned ();
|
|
218 checksum = gcov_read_unsigned ();
|
|
219 if (seen_summary)
|
|
220 {
|
|
221 /* We have already seen a summary, this means that this
|
|
222 new function begins a new set of program runs. We
|
|
223 must unlink the summaried chain. */
|
|
224 counts_entry_t *entry, *chain;
|
|
225
|
|
226 for (entry = summaried; entry; entry = chain)
|
|
227 {
|
|
228 chain = entry->chain;
|
|
229 entry->chain = NULL;
|
|
230 }
|
|
231 summaried = NULL;
|
|
232 seen_summary = 0;
|
|
233 }
|
|
234 }
|
|
235 else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
|
|
236 {
|
|
237 counts_entry_t *entry;
|
|
238 struct gcov_summary summary;
|
|
239
|
|
240 gcov_read_summary (&summary);
|
|
241 seen_summary = 1;
|
|
242 for (entry = summaried; entry; entry = entry->chain)
|
|
243 {
|
|
244 struct gcov_ctr_summary *csum = &summary.ctrs[entry->ctr];
|
|
245
|
|
246 entry->summary.runs += csum->runs;
|
|
247 entry->summary.sum_all += csum->sum_all;
|
|
248 if (entry->summary.run_max < csum->run_max)
|
|
249 entry->summary.run_max = csum->run_max;
|
|
250 entry->summary.sum_max += csum->sum_max;
|
|
251 }
|
|
252 }
|
|
253 else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
|
|
254 {
|
|
255 counts_entry_t **slot, *entry, elt;
|
|
256 unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
|
|
257 unsigned ix;
|
|
258
|
|
259 elt.ident = fn_ident;
|
|
260 elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
|
|
261
|
|
262 slot = (counts_entry_t **) htab_find_slot
|
|
263 (counts_hash, &elt, INSERT);
|
|
264 entry = *slot;
|
|
265 if (!entry)
|
|
266 {
|
|
267 *slot = entry = XCNEW (counts_entry_t);
|
|
268 entry->ident = elt.ident;
|
|
269 entry->ctr = elt.ctr;
|
|
270 entry->checksum = checksum;
|
|
271 entry->summary.num = n_counts;
|
|
272 entry->counts = XCNEWVEC (gcov_type, n_counts);
|
|
273 }
|
|
274 else if (entry->checksum != checksum)
|
|
275 {
|
|
276 error ("coverage mismatch for function %u while reading execution counters",
|
|
277 fn_ident);
|
|
278 error ("checksum is %x instead of %x", entry->checksum, checksum);
|
|
279 htab_delete (counts_hash);
|
|
280 break;
|
|
281 }
|
|
282 else if (entry->summary.num != n_counts)
|
|
283 {
|
|
284 error ("coverage mismatch for function %u while reading execution counters",
|
|
285 fn_ident);
|
|
286 error ("number of counters is %d instead of %d", entry->summary.num, n_counts);
|
|
287 htab_delete (counts_hash);
|
|
288 break;
|
|
289 }
|
|
290 else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
|
|
291 {
|
|
292 error ("cannot merge separate %s counters for function %u",
|
|
293 ctr_names[elt.ctr], fn_ident);
|
|
294 goto skip_merge;
|
|
295 }
|
|
296
|
|
297 if (elt.ctr < GCOV_COUNTERS_SUMMABLE
|
|
298 /* This should always be true for a just allocated entry,
|
|
299 and always false for an existing one. Check this way, in
|
|
300 case the gcov file is corrupt. */
|
|
301 && (!entry->chain || summaried != entry))
|
|
302 {
|
|
303 entry->chain = summaried;
|
|
304 summaried = entry;
|
|
305 }
|
|
306 for (ix = 0; ix != n_counts; ix++)
|
|
307 entry->counts[ix] += gcov_read_counter ();
|
|
308 skip_merge:;
|
|
309 }
|
|
310 gcov_sync (offset, length);
|
|
311 if ((is_error = gcov_is_error ()))
|
|
312 {
|
|
313 error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted",
|
|
314 da_file_name);
|
|
315 htab_delete (counts_hash);
|
|
316 break;
|
|
317 }
|
|
318 }
|
|
319
|
|
320 gcov_close ();
|
|
321 }
|
|
322
|
|
323 /* Returns the counters for a particular tag. */
|
|
324
|
|
325 gcov_type *
|
|
326 get_coverage_counts (unsigned counter, unsigned expected,
|
|
327 const struct gcov_ctr_summary **summary)
|
|
328 {
|
|
329 counts_entry_t *entry, elt;
|
|
330 gcov_unsigned_t checksum = -1;
|
|
331
|
|
332 /* No hash table, no counts. */
|
|
333 if (!counts_hash)
|
|
334 {
|
|
335 static int warned = 0;
|
|
336
|
|
337 if (!warned++)
|
|
338 inform (input_location, (flag_guess_branch_prob
|
|
339 ? "file %s not found, execution counts estimated"
|
|
340 : "file %s not found, execution counts assumed to be zero"),
|
|
341 da_file_name);
|
|
342 return NULL;
|
|
343 }
|
|
344
|
|
345 elt.ident = current_function_funcdef_no + 1;
|
|
346 elt.ctr = counter;
|
|
347 entry = (counts_entry_t *) htab_find (counts_hash, &elt);
|
|
348 if (!entry)
|
|
349 {
|
|
350 warning (0, "no coverage for function %qs found", IDENTIFIER_POINTER
|
|
351 (DECL_ASSEMBLER_NAME (current_function_decl)));
|
|
352 return NULL;
|
|
353 }
|
|
354
|
|
355 checksum = compute_checksum ();
|
|
356 if (entry->checksum != checksum
|
|
357 || entry->summary.num != expected)
|
|
358 {
|
|
359 static int warned = 0;
|
|
360 const char *id = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME
|
|
361 (current_function_decl));
|
|
362
|
|
363 if (warn_coverage_mismatch)
|
|
364 warning (OPT_Wcoverage_mismatch, "coverage mismatch for function "
|
|
365 "%qs while reading counter %qs", id, ctr_names[counter]);
|
|
366 else
|
|
367 error ("coverage mismatch for function %qs while reading counter %qs",
|
|
368 id, ctr_names[counter]);
|
|
369
|
|
370 if (!inhibit_warnings)
|
|
371 {
|
|
372 if (entry->checksum != checksum)
|
|
373 inform (input_location, "checksum is %x instead of %x", entry->checksum, checksum);
|
|
374 else
|
|
375 inform (input_location, "number of counters is %d instead of %d",
|
|
376 entry->summary.num, expected);
|
|
377 }
|
|
378
|
|
379 if (warn_coverage_mismatch
|
|
380 && !inhibit_warnings
|
|
381 && !warned++)
|
|
382 {
|
|
383 inform (input_location, "coverage mismatch ignored due to -Wcoverage-mismatch");
|
|
384 inform (input_location, flag_guess_branch_prob
|
|
385 ? "execution counts estimated"
|
|
386 : "execution counts assumed to be zero");
|
|
387 if (!flag_guess_branch_prob)
|
|
388 inform (input_location, "this can result in poorly optimized code");
|
|
389 }
|
|
390
|
|
391 return NULL;
|
|
392 }
|
|
393
|
|
394 if (summary)
|
|
395 *summary = &entry->summary;
|
|
396
|
|
397 return entry->counts;
|
|
398 }
|
|
399
|
|
400 /* Allocate NUM counters of type COUNTER. Returns nonzero if the
|
|
401 allocation succeeded. */
|
|
402
|
|
403 int
|
|
404 coverage_counter_alloc (unsigned counter, unsigned num)
|
|
405 {
|
|
406 if (no_coverage)
|
|
407 return 0;
|
|
408
|
|
409 if (!num)
|
|
410 return 1;
|
|
411
|
|
412 if (!tree_ctr_tables[counter])
|
|
413 {
|
|
414 /* Generate and save a copy of this so it can be shared. Leave
|
|
415 the index type unspecified for now; it will be set after all
|
|
416 functions have been compiled. */
|
|
417 char buf[20];
|
|
418 tree gcov_type_node = get_gcov_type ();
|
|
419 tree gcov_type_array_type
|
|
420 = build_array_type (gcov_type_node, NULL_TREE);
|
|
421 tree_ctr_tables[counter]
|
|
422 = build_decl (VAR_DECL, NULL_TREE, gcov_type_array_type);
|
|
423 TREE_STATIC (tree_ctr_tables[counter]) = 1;
|
|
424 ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1);
|
|
425 DECL_NAME (tree_ctr_tables[counter]) = get_identifier (buf);
|
|
426 DECL_ALIGN (tree_ctr_tables[counter]) = TYPE_ALIGN (gcov_type_node);
|
|
427
|
|
428 if (dump_file)
|
|
429 fprintf (dump_file, "Using data file %s\n", da_file_name);
|
|
430 }
|
|
431 fn_b_ctrs[counter] = fn_n_ctrs[counter];
|
|
432 fn_n_ctrs[counter] += num;
|
|
433 fn_ctr_mask |= 1 << counter;
|
|
434 return 1;
|
|
435 }
|
|
436
|
|
437 /* Generate a tree to access COUNTER NO. */
|
|
438
|
|
439 tree
|
|
440 tree_coverage_counter_ref (unsigned counter, unsigned no)
|
|
441 {
|
|
442 tree gcov_type_node = get_gcov_type ();
|
|
443
|
|
444 gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
|
|
445 no += prg_n_ctrs[counter] + fn_b_ctrs[counter];
|
|
446
|
|
447 /* "no" here is an array index, scaled to bytes later. */
|
|
448 return build4 (ARRAY_REF, gcov_type_node, tree_ctr_tables[counter],
|
|
449 build_int_cst (NULL_TREE, no), NULL, NULL);
|
|
450 }
|
|
451
|
|
452 /* Generate a tree to access the address of COUNTER NO. */
|
|
453
|
|
454 tree
|
|
455 tree_coverage_counter_addr (unsigned counter, unsigned no)
|
|
456 {
|
|
457 tree gcov_type_node = get_gcov_type ();
|
|
458
|
|
459 gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
|
|
460 no += prg_n_ctrs[counter] + fn_b_ctrs[counter];
|
|
461
|
|
462 /* "no" here is an array index, scaled to bytes later. */
|
|
463 return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node,
|
|
464 tree_ctr_tables[counter],
|
|
465 build_int_cst (NULL_TREE, no),
|
|
466 NULL, NULL));
|
|
467 }
|
|
468
|
|
469 /* Generate a checksum for a string. CHKSUM is the current
|
|
470 checksum. */
|
|
471
|
|
472 static unsigned
|
|
473 coverage_checksum_string (unsigned chksum, const char *string)
|
|
474 {
|
|
475 int i;
|
|
476 char *dup = NULL;
|
|
477
|
|
478 /* Look for everything that looks if it were produced by
|
|
479 get_file_function_name and zero out the second part
|
|
480 that may result from flag_random_seed. This is not critical
|
|
481 as the checksums are used only for sanity checking. */
|
|
482 for (i = 0; string[i]; i++)
|
|
483 {
|
|
484 int offset = 0;
|
|
485 if (!strncmp (string + i, "_GLOBAL__N_", 11))
|
|
486 offset = 11;
|
|
487 if (!strncmp (string + i, "_GLOBAL__", 9))
|
|
488 offset = 9;
|
|
489
|
|
490 /* C++ namespaces do have scheme:
|
|
491 _GLOBAL__N_<filename>_<wrongmagicnumber>_<magicnumber>functionname
|
|
492 since filename might contain extra underscores there seems
|
|
493 to be no better chance then walk all possible offsets looking
|
|
494 for magicnumber. */
|
|
495 if (offset)
|
|
496 {
|
|
497 for (i = i + offset; string[i]; i++)
|
|
498 if (string[i]=='_')
|
|
499 {
|
|
500 int y;
|
|
501
|
|
502 for (y = 1; y < 9; y++)
|
|
503 if (!(string[i + y] >= '0' && string[i + y] <= '9')
|
|
504 && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
|
|
505 break;
|
|
506 if (y != 9 || string[i + 9] != '_')
|
|
507 continue;
|
|
508 for (y = 10; y < 18; y++)
|
|
509 if (!(string[i + y] >= '0' && string[i + y] <= '9')
|
|
510 && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
|
|
511 break;
|
|
512 if (y != 18)
|
|
513 continue;
|
|
514 if (!dup)
|
|
515 string = dup = xstrdup (string);
|
|
516 for (y = 10; y < 18; y++)
|
|
517 dup[i + y] = '0';
|
|
518 }
|
|
519 break;
|
|
520 }
|
|
521 }
|
|
522
|
|
523 chksum = crc32_string (chksum, string);
|
|
524 if (dup)
|
|
525 free (dup);
|
|
526
|
|
527 return chksum;
|
|
528 }
|
|
529
|
|
530 /* Compute checksum for the current function. We generate a CRC32. */
|
|
531
|
|
532 static unsigned
|
|
533 compute_checksum (void)
|
|
534 {
|
|
535 expanded_location xloc
|
|
536 = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
|
|
537 unsigned chksum = xloc.line;
|
|
538
|
|
539 chksum = coverage_checksum_string (chksum, xloc.file);
|
|
540 chksum = coverage_checksum_string
|
|
541 (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)));
|
|
542
|
|
543 return chksum;
|
|
544 }
|
|
545
|
|
546 /* Begin output to the graph file for the current function.
|
|
547 Opens the output file, if not already done. Writes the
|
|
548 function header, if not already done. Returns nonzero if data
|
|
549 should be output. */
|
|
550
|
|
551 int
|
|
552 coverage_begin_output (void)
|
|
553 {
|
|
554 /* We don't need to output .gcno file unless we're under -ftest-coverage
|
|
555 (e.g. -fprofile-arcs/generate/use don't need .gcno to work). */
|
|
556 if (no_coverage || !flag_test_coverage)
|
|
557 return 0;
|
|
558
|
|
559 if (!bbg_function_announced)
|
|
560 {
|
|
561 expanded_location xloc
|
|
562 = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
|
|
563 unsigned long offset;
|
|
564
|
|
565 if (!bbg_file_opened)
|
|
566 {
|
|
567 if (!gcov_open (bbg_file_name, -1))
|
|
568 error ("cannot open %s", bbg_file_name);
|
|
569 else
|
|
570 {
|
|
571 gcov_write_unsigned (GCOV_NOTE_MAGIC);
|
|
572 gcov_write_unsigned (GCOV_VERSION);
|
|
573 gcov_write_unsigned (local_tick);
|
|
574 }
|
|
575 bbg_file_opened = 1;
|
|
576 }
|
|
577
|
|
578 /* Announce function */
|
|
579 offset = gcov_write_tag (GCOV_TAG_FUNCTION);
|
|
580 gcov_write_unsigned (current_function_funcdef_no + 1);
|
|
581 gcov_write_unsigned (compute_checksum ());
|
|
582 gcov_write_string (IDENTIFIER_POINTER
|
|
583 (DECL_ASSEMBLER_NAME (current_function_decl)));
|
|
584 gcov_write_string (xloc.file);
|
|
585 gcov_write_unsigned (xloc.line);
|
|
586 gcov_write_length (offset);
|
|
587
|
|
588 bbg_function_announced = 1;
|
|
589 }
|
|
590 return !gcov_is_error ();
|
|
591 }
|
|
592
|
|
593 /* Finish coverage data for the current function. Verify no output
|
|
594 error has occurred. Save function coverage counts. */
|
|
595
|
|
596 void
|
|
597 coverage_end_function (void)
|
|
598 {
|
|
599 unsigned i;
|
|
600
|
|
601 if (bbg_file_opened > 1 && gcov_is_error ())
|
|
602 {
|
|
603 warning (0, "error writing %qs", bbg_file_name);
|
|
604 bbg_file_opened = -1;
|
|
605 }
|
|
606
|
|
607 if (fn_ctr_mask)
|
|
608 {
|
|
609 struct function_list *item;
|
|
610
|
|
611 item = XNEW (struct function_list);
|
|
612
|
|
613 *functions_tail = item;
|
|
614 functions_tail = &item->next;
|
|
615
|
|
616 item->next = 0;
|
|
617 item->ident = current_function_funcdef_no + 1;
|
|
618 item->checksum = compute_checksum ();
|
|
619 for (i = 0; i != GCOV_COUNTERS; i++)
|
|
620 {
|
|
621 item->n_ctrs[i] = fn_n_ctrs[i];
|
|
622 prg_n_ctrs[i] += fn_n_ctrs[i];
|
|
623 fn_n_ctrs[i] = fn_b_ctrs[i] = 0;
|
|
624 }
|
|
625 prg_ctr_mask |= fn_ctr_mask;
|
|
626 fn_ctr_mask = 0;
|
|
627 }
|
|
628 bbg_function_announced = 0;
|
|
629 }
|
|
630
|
|
631 /* Creates the gcov_fn_info RECORD_TYPE. */
|
|
632
|
|
633 static tree
|
|
634 build_fn_info_type (unsigned int counters)
|
|
635 {
|
|
636 tree type = lang_hooks.types.make_type (RECORD_TYPE);
|
|
637 tree field, fields;
|
|
638 tree array_type;
|
|
639
|
|
640 /* ident */
|
|
641 fields = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
|
|
642
|
|
643 /* checksum */
|
|
644 field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
|
|
645 TREE_CHAIN (field) = fields;
|
|
646 fields = field;
|
|
647
|
|
648 array_type = build_int_cst (NULL_TREE, counters - 1);
|
|
649 array_type = build_index_type (array_type);
|
|
650 array_type = build_array_type (get_gcov_unsigned_t (), array_type);
|
|
651
|
|
652 /* counters */
|
|
653 field = build_decl (FIELD_DECL, NULL_TREE, array_type);
|
|
654 TREE_CHAIN (field) = fields;
|
|
655 fields = field;
|
|
656
|
|
657 finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
|
|
658
|
|
659 return type;
|
|
660 }
|
|
661
|
|
662 /* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is
|
|
663 the function being processed and TYPE is the gcov_fn_info
|
|
664 RECORD_TYPE. */
|
|
665
|
|
666 static tree
|
|
667 build_fn_info_value (const struct function_list *function, tree type)
|
|
668 {
|
|
669 tree value = NULL_TREE;
|
|
670 tree fields = TYPE_FIELDS (type);
|
|
671 unsigned ix;
|
|
672 tree array_value = NULL_TREE;
|
|
673
|
|
674 /* ident */
|
|
675 value = tree_cons (fields, build_int_cstu (get_gcov_unsigned_t (),
|
|
676 function->ident), value);
|
|
677 fields = TREE_CHAIN (fields);
|
|
678
|
|
679 /* checksum */
|
|
680 value = tree_cons (fields, build_int_cstu (get_gcov_unsigned_t (),
|
|
681 function->checksum), value);
|
|
682 fields = TREE_CHAIN (fields);
|
|
683
|
|
684 /* counters */
|
|
685 for (ix = 0; ix != GCOV_COUNTERS; ix++)
|
|
686 if (prg_ctr_mask & (1 << ix))
|
|
687 {
|
|
688 tree counters = build_int_cstu (get_gcov_unsigned_t (),
|
|
689 function->n_ctrs[ix]);
|
|
690
|
|
691 array_value = tree_cons (NULL_TREE, counters, array_value);
|
|
692 }
|
|
693
|
|
694 /* FIXME: use build_constructor directly. */
|
|
695 array_value = build_constructor_from_list (TREE_TYPE (fields),
|
|
696 nreverse (array_value));
|
|
697 value = tree_cons (fields, array_value, value);
|
|
698
|
|
699 /* FIXME: use build_constructor directly. */
|
|
700 value = build_constructor_from_list (type, nreverse (value));
|
|
701
|
|
702 return value;
|
|
703 }
|
|
704
|
|
705 /* Creates the gcov_ctr_info RECORD_TYPE. */
|
|
706
|
|
707 static tree
|
|
708 build_ctr_info_type (void)
|
|
709 {
|
|
710 tree type = lang_hooks.types.make_type (RECORD_TYPE);
|
|
711 tree field, fields = NULL_TREE;
|
|
712 tree gcov_ptr_type = build_pointer_type (get_gcov_type ());
|
|
713 tree gcov_merge_fn_type;
|
|
714
|
|
715 /* counters */
|
|
716 field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
|
|
717 TREE_CHAIN (field) = fields;
|
|
718 fields = field;
|
|
719
|
|
720 /* values */
|
|
721 field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
|
|
722 TREE_CHAIN (field) = fields;
|
|
723 fields = field;
|
|
724
|
|
725 /* merge */
|
|
726 gcov_merge_fn_type =
|
|
727 build_function_type_list (void_type_node,
|
|
728 gcov_ptr_type, get_gcov_unsigned_t (),
|
|
729 NULL_TREE);
|
|
730 field = build_decl (FIELD_DECL, NULL_TREE,
|
|
731 build_pointer_type (gcov_merge_fn_type));
|
|
732 TREE_CHAIN (field) = fields;
|
|
733 fields = field;
|
|
734
|
|
735 finish_builtin_struct (type, "__gcov_ctr_info", fields, NULL_TREE);
|
|
736
|
|
737 return type;
|
|
738 }
|
|
739
|
|
740 /* Creates a CONSTRUCTOR for a gcov_ctr_info. COUNTER is
|
|
741 the counter being processed and TYPE is the gcov_ctr_info
|
|
742 RECORD_TYPE. */
|
|
743
|
|
744 static tree
|
|
745 build_ctr_info_value (unsigned int counter, tree type)
|
|
746 {
|
|
747 tree value = NULL_TREE;
|
|
748 tree fields = TYPE_FIELDS (type);
|
|
749 tree fn;
|
|
750
|
|
751 /* counters */
|
|
752 value = tree_cons (fields,
|
|
753 build_int_cstu (get_gcov_unsigned_t (),
|
|
754 prg_n_ctrs[counter]),
|
|
755 value);
|
|
756 fields = TREE_CHAIN (fields);
|
|
757
|
|
758 if (prg_n_ctrs[counter])
|
|
759 {
|
|
760 tree array_type;
|
|
761
|
|
762 array_type = build_int_cstu (get_gcov_unsigned_t (),
|
|
763 prg_n_ctrs[counter] - 1);
|
|
764 array_type = build_index_type (array_type);
|
|
765 array_type = build_array_type (TREE_TYPE (TREE_TYPE (fields)),
|
|
766 array_type);
|
|
767
|
|
768 TREE_TYPE (tree_ctr_tables[counter]) = array_type;
|
|
769 DECL_SIZE (tree_ctr_tables[counter]) = TYPE_SIZE (array_type);
|
|
770 DECL_SIZE_UNIT (tree_ctr_tables[counter]) = TYPE_SIZE_UNIT (array_type);
|
|
771 assemble_variable (tree_ctr_tables[counter], 0, 0, 0);
|
|
772
|
|
773 value = tree_cons (fields,
|
|
774 build1 (ADDR_EXPR, TREE_TYPE (fields),
|
|
775 tree_ctr_tables[counter]),
|
|
776 value);
|
|
777 }
|
|
778 else
|
|
779 value = tree_cons (fields, null_pointer_node, value);
|
|
780 fields = TREE_CHAIN (fields);
|
|
781
|
|
782 fn = build_decl (FUNCTION_DECL,
|
|
783 get_identifier (ctr_merge_functions[counter]),
|
|
784 TREE_TYPE (TREE_TYPE (fields)));
|
|
785 DECL_EXTERNAL (fn) = 1;
|
|
786 TREE_PUBLIC (fn) = 1;
|
|
787 DECL_ARTIFICIAL (fn) = 1;
|
|
788 TREE_NOTHROW (fn) = 1;
|
|
789 value = tree_cons (fields,
|
|
790 build1 (ADDR_EXPR, TREE_TYPE (fields), fn),
|
|
791 value);
|
|
792
|
|
793 /* FIXME: use build_constructor directly. */
|
|
794 value = build_constructor_from_list (type, nreverse (value));
|
|
795
|
|
796 return value;
|
|
797 }
|
|
798
|
|
799 /* Creates the gcov_info RECORD_TYPE and initializer for it. Returns a
|
|
800 CONSTRUCTOR. */
|
|
801
|
|
802 static tree
|
|
803 build_gcov_info (void)
|
|
804 {
|
|
805 unsigned n_ctr_types, ix;
|
|
806 tree type, const_type;
|
|
807 tree fn_info_type, fn_info_value = NULL_TREE;
|
|
808 tree fn_info_ptr_type;
|
|
809 tree ctr_info_type, ctr_info_ary_type, ctr_info_value = NULL_TREE;
|
|
810 tree field, fields = NULL_TREE;
|
|
811 tree value = NULL_TREE;
|
|
812 tree filename_string;
|
|
813 int da_file_name_len;
|
|
814 unsigned n_fns;
|
|
815 const struct function_list *fn;
|
|
816 tree string_type;
|
|
817
|
|
818 /* Count the number of active counters. */
|
|
819 for (n_ctr_types = 0, ix = 0; ix != GCOV_COUNTERS; ix++)
|
|
820 if (prg_ctr_mask & (1 << ix))
|
|
821 n_ctr_types++;
|
|
822
|
|
823 type = lang_hooks.types.make_type (RECORD_TYPE);
|
|
824 const_type = build_qualified_type (type, TYPE_QUAL_CONST);
|
|
825
|
|
826 /* Version ident */
|
|
827 field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
|
|
828 TREE_CHAIN (field) = fields;
|
|
829 fields = field;
|
|
830 value = tree_cons (field, build_int_cstu (TREE_TYPE (field), GCOV_VERSION),
|
|
831 value);
|
|
832
|
|
833 /* next -- NULL */
|
|
834 field = build_decl (FIELD_DECL, NULL_TREE, build_pointer_type (const_type));
|
|
835 TREE_CHAIN (field) = fields;
|
|
836 fields = field;
|
|
837 value = tree_cons (field, null_pointer_node, value);
|
|
838
|
|
839 /* stamp */
|
|
840 field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
|
|
841 TREE_CHAIN (field) = fields;
|
|
842 fields = field;
|
|
843 value = tree_cons (field, build_int_cstu (TREE_TYPE (field), local_tick),
|
|
844 value);
|
|
845
|
|
846 /* Filename */
|
|
847 string_type = build_pointer_type (build_qualified_type (char_type_node,
|
|
848 TYPE_QUAL_CONST));
|
|
849 field = build_decl (FIELD_DECL, NULL_TREE, string_type);
|
|
850 TREE_CHAIN (field) = fields;
|
|
851 fields = field;
|
|
852 da_file_name_len = strlen (da_file_name);
|
|
853 filename_string = build_string (da_file_name_len + 1, da_file_name);
|
|
854 TREE_TYPE (filename_string) = build_array_type
|
|
855 (char_type_node, build_index_type
|
|
856 (build_int_cst (NULL_TREE, da_file_name_len)));
|
|
857 value = tree_cons (field, build1 (ADDR_EXPR, string_type, filename_string),
|
|
858 value);
|
|
859
|
|
860 /* Build the fn_info type and initializer. */
|
|
861 fn_info_type = build_fn_info_type (n_ctr_types);
|
|
862 fn_info_ptr_type = build_pointer_type (build_qualified_type
|
|
863 (fn_info_type, TYPE_QUAL_CONST));
|
|
864 for (fn = functions_head, n_fns = 0; fn; fn = fn->next, n_fns++)
|
|
865 fn_info_value = tree_cons (NULL_TREE,
|
|
866 build_fn_info_value (fn, fn_info_type),
|
|
867 fn_info_value);
|
|
868 if (n_fns)
|
|
869 {
|
|
870 tree array_type;
|
|
871
|
|
872 array_type = build_index_type (build_int_cst (NULL_TREE, n_fns - 1));
|
|
873 array_type = build_array_type (fn_info_type, array_type);
|
|
874
|
|
875 /* FIXME: use build_constructor directly. */
|
|
876 fn_info_value = build_constructor_from_list (array_type,
|
|
877 nreverse (fn_info_value));
|
|
878 fn_info_value = build1 (ADDR_EXPR, fn_info_ptr_type, fn_info_value);
|
|
879 }
|
|
880 else
|
|
881 fn_info_value = null_pointer_node;
|
|
882
|
|
883 /* number of functions */
|
|
884 field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
|
|
885 TREE_CHAIN (field) = fields;
|
|
886 fields = field;
|
|
887 value = tree_cons (field,
|
|
888 build_int_cstu (get_gcov_unsigned_t (), n_fns),
|
|
889 value);
|
|
890
|
|
891 /* fn_info table */
|
|
892 field = build_decl (FIELD_DECL, NULL_TREE, fn_info_ptr_type);
|
|
893 TREE_CHAIN (field) = fields;
|
|
894 fields = field;
|
|
895 value = tree_cons (field, fn_info_value, value);
|
|
896
|
|
897 /* counter_mask */
|
|
898 field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ());
|
|
899 TREE_CHAIN (field) = fields;
|
|
900 fields = field;
|
|
901 value = tree_cons (field,
|
|
902 build_int_cstu (get_gcov_unsigned_t (), prg_ctr_mask),
|
|
903 value);
|
|
904
|
|
905 /* counters */
|
|
906 ctr_info_type = build_ctr_info_type ();
|
|
907 ctr_info_ary_type = build_index_type (build_int_cst (NULL_TREE,
|
|
908 n_ctr_types));
|
|
909 ctr_info_ary_type = build_array_type (ctr_info_type, ctr_info_ary_type);
|
|
910 for (ix = 0; ix != GCOV_COUNTERS; ix++)
|
|
911 if (prg_ctr_mask & (1 << ix))
|
|
912 ctr_info_value = tree_cons (NULL_TREE,
|
|
913 build_ctr_info_value (ix, ctr_info_type),
|
|
914 ctr_info_value);
|
|
915 /* FIXME: use build_constructor directly. */
|
|
916 ctr_info_value = build_constructor_from_list (ctr_info_ary_type,
|
|
917 nreverse (ctr_info_value));
|
|
918
|
|
919 field = build_decl (FIELD_DECL, NULL_TREE, ctr_info_ary_type);
|
|
920 TREE_CHAIN (field) = fields;
|
|
921 fields = field;
|
|
922 value = tree_cons (field, ctr_info_value, value);
|
|
923
|
|
924 finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
|
|
925
|
|
926 /* FIXME: use build_constructor directly. */
|
|
927 value = build_constructor_from_list (type, nreverse (value));
|
|
928
|
|
929 return value;
|
|
930 }
|
|
931
|
|
932 /* Write out the structure which libgcov uses to locate all the
|
|
933 counters. The structures used here must match those defined in
|
|
934 gcov-io.h. Write out the constructor to call __gcov_init. */
|
|
935
|
|
936 static void
|
|
937 create_coverage (void)
|
|
938 {
|
|
939 tree gcov_info, gcov_init, body, t;
|
|
940 char name_buf[32];
|
|
941
|
|
942 no_coverage = 1; /* Disable any further coverage. */
|
|
943
|
|
944 if (!prg_ctr_mask)
|
|
945 return;
|
|
946
|
|
947 t = build_gcov_info ();
|
|
948
|
|
949 gcov_info = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (t));
|
|
950 TREE_STATIC (gcov_info) = 1;
|
|
951 ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
|
|
952 DECL_NAME (gcov_info) = get_identifier (name_buf);
|
|
953 DECL_INITIAL (gcov_info) = t;
|
|
954
|
|
955 /* Build structure. */
|
|
956 assemble_variable (gcov_info, 0, 0, 0);
|
|
957
|
|
958 /* Build a decl for __gcov_init. */
|
|
959 t = build_pointer_type (TREE_TYPE (gcov_info));
|
|
960 t = build_function_type_list (void_type_node, t, NULL);
|
|
961 t = build_decl (FUNCTION_DECL, get_identifier ("__gcov_init"), t);
|
|
962 TREE_PUBLIC (t) = 1;
|
|
963 DECL_EXTERNAL (t) = 1;
|
|
964 gcov_init = t;
|
|
965
|
|
966 /* Generate a call to __gcov_init(&gcov_info). */
|
|
967 body = NULL;
|
|
968 t = build_fold_addr_expr (gcov_info);
|
|
969 t = build_call_expr (gcov_init, 1, t);
|
|
970 append_to_statement_list (t, &body);
|
|
971
|
|
972 /* Generate a constructor to run it. */
|
|
973 cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY);
|
|
974 }
|
|
975
|
|
976 /* Perform file-level initialization. Read in data file, generate name
|
|
977 of graph file. */
|
|
978
|
|
979 void
|
|
980 coverage_init (const char *filename)
|
|
981 {
|
|
982 int len = strlen (filename);
|
|
983 /* + 1 for extra '/', in case prefix doesn't end with /. */
|
|
984 int prefix_len;
|
|
985
|
|
986 if (profile_data_prefix == 0 && filename[0] != '/')
|
|
987 profile_data_prefix = getpwd ();
|
|
988
|
|
989 prefix_len = (profile_data_prefix) ? strlen (profile_data_prefix) + 1 : 0;
|
|
990
|
|
991 /* Name of da file. */
|
|
992 da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
|
|
993 + prefix_len + 1);
|
|
994
|
|
995 if (profile_data_prefix)
|
|
996 {
|
|
997 strcpy (da_file_name, profile_data_prefix);
|
|
998 da_file_name[prefix_len - 1] = '/';
|
|
999 da_file_name[prefix_len] = 0;
|
|
1000 }
|
|
1001 else
|
|
1002 da_file_name[0] = 0;
|
|
1003 strcat (da_file_name, filename);
|
|
1004 strcat (da_file_name, GCOV_DATA_SUFFIX);
|
|
1005
|
|
1006 /* Name of bbg file. */
|
|
1007 bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1);
|
|
1008 strcpy (bbg_file_name, filename);
|
|
1009 strcat (bbg_file_name, GCOV_NOTE_SUFFIX);
|
|
1010
|
|
1011 if (flag_profile_use)
|
|
1012 read_counts_file ();
|
|
1013 }
|
|
1014
|
|
1015 /* Performs file-level cleanup. Close graph file, generate coverage
|
|
1016 variables and constructor. */
|
|
1017
|
|
1018 void
|
|
1019 coverage_finish (void)
|
|
1020 {
|
|
1021 create_coverage ();
|
|
1022 if (bbg_file_opened)
|
|
1023 {
|
|
1024 int error = gcov_close ();
|
|
1025
|
|
1026 if (error)
|
|
1027 unlink (bbg_file_name);
|
|
1028 if (!local_tick)
|
|
1029 /* Only remove the da file, if we cannot stamp it. If we can
|
|
1030 stamp it, libgcov will DTRT. */
|
|
1031 unlink (da_file_name);
|
|
1032 }
|
|
1033 }
|
|
1034
|
|
1035 #include "gt-coverage.h"
|