]>
Commit | Line | Data |
---|---|---|
ca29da43 NS |
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 Free Software Foundation, Inc. | |
4 | Contributed by James E. Wilson, UC Berkeley/Cygnus Support; | |
5 | based on some ideas from Dain Samples of UC Berkeley. | |
6 | Further mangling by Bob Manson, Cygnus Support. | |
7 | Further mangled by Nathan Sidwell, CodeSourcery | |
8 | ||
9 | This file is part of GCC. | |
10 | ||
11 | GCC is free software; you can redistribute it and/or modify it under | |
12 | the terms of the GNU General Public License as published by the Free | |
13 | Software Foundation; either version 2, or (at your option) any later | |
14 | version. | |
15 | ||
16 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
17 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
18 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
19 | for more details. | |
20 | ||
21 | You should have received a copy of the GNU General Public License | |
22 | along with GCC; see the file COPYING. If not, write to the Free | |
23 | Software Foundation, 59 Temple Place - Suite 330, Boston, MA | |
24 | 02111-1307, USA. */ | |
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 "ggc.h" | |
42 | #include "target.h" | |
43 | #include "coverage.h" | |
44 | #include "libfuncs.h" | |
45 | #include "langhooks.h" | |
46 | #include "hashtab.h" | |
47 | ||
48 | #include "gcov-io.c" | |
49 | ||
50 | struct function_list | |
51 | { | |
159b3be1 AJ |
52 | struct function_list *next; /* next function */ |
53 | unsigned ident; /* function ident */ | |
cdb23767 NS |
54 | unsigned checksum; /* function checksum */ |
55 | unsigned n_ctrs[GCOV_COUNTERS];/* number of counters. */ | |
ca29da43 NS |
56 | }; |
57 | ||
58 | /* Counts information for a function. */ | |
59 | typedef struct counts_entry | |
60 | { | |
61 | /* We hash by */ | |
796621e8 | 62 | unsigned ident; |
cdb23767 | 63 | unsigned ctr; |
159b3be1 | 64 | |
ca29da43 NS |
65 | /* Store */ |
66 | unsigned checksum; | |
ca29da43 | 67 | gcov_type *counts; |
cdb23767 | 68 | struct gcov_ctr_summary summary; |
ca29da43 NS |
69 | |
70 | /* Workspace */ | |
71 | struct counts_entry *chain; | |
159b3be1 | 72 | |
ca29da43 NS |
73 | } counts_entry_t; |
74 | ||
75 | static struct function_list *functions_head = 0; | |
76 | static struct function_list **functions_tail = &functions_head; | |
6d70e6be | 77 | static unsigned no_coverage = 0; |
ca29da43 | 78 | |
cdb23767 NS |
79 | /* Cumulative counter information for whole program. */ |
80 | static unsigned prg_ctr_mask; /* Mask of counter types generated. */ | |
6d70e6be | 81 | static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated. */ |
ca29da43 | 82 | |
cdb23767 | 83 | /* Counter information for current function. */ |
71c0e7fc | 84 | static unsigned fn_ctr_mask; /* Mask of counters used. */ |
6d70e6be NS |
85 | static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */ |
86 | static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */ | |
ca29da43 NS |
87 | |
88 | /* Name of the output file for coverage output file. */ | |
89 | static char *bbg_file_name; | |
90 | static unsigned bbg_file_opened; | |
91 | static int bbg_function_announced; | |
92 | ||
93 | /* Name of the count data file. */ | |
94 | static char *da_file_name; | |
95 | ||
96 | /* Hash table of count data. */ | |
97 | static htab_t counts_hash = NULL; | |
98 | ||
cdb23767 NS |
99 | /* The names of the counter tables. */ |
100 | static GTY(()) rtx ctr_labels[GCOV_COUNTERS]; | |
ca29da43 | 101 | |
09780dfb | 102 | /* The names of merge functions for counters. */ |
9b514d25 NS |
103 | static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS; |
104 | static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES; | |
09780dfb | 105 | |
ca29da43 | 106 | /* Forward declarations. */ |
159b3be1 AJ |
107 | static hashval_t htab_counts_entry_hash (const void *); |
108 | static int htab_counts_entry_eq (const void *, const void *); | |
109 | static void htab_counts_entry_del (void *); | |
110 | static void read_counts_file (void); | |
111 | static unsigned compute_checksum (void); | |
112 | static unsigned checksum_string (unsigned, const char *); | |
113 | static tree build_fn_info_type (unsigned); | |
114 | static tree build_fn_info_value (const struct function_list *, tree); | |
115 | static tree build_ctr_info_type (void); | |
116 | static tree build_ctr_info_value (unsigned, tree); | |
117 | static tree build_gcov_info (void); | |
118 | static void create_coverage (void); | |
ca29da43 NS |
119 | |
120 | \f | |
121 | static hashval_t | |
159b3be1 | 122 | htab_counts_entry_hash (const void *of) |
ca29da43 NS |
123 | { |
124 | const counts_entry_t *entry = of; | |
125 | ||
796621e8 | 126 | return entry->ident * GCOV_COUNTERS + entry->ctr; |
ca29da43 NS |
127 | } |
128 | ||
129 | static int | |
159b3be1 | 130 | htab_counts_entry_eq (const void *of1, const void *of2) |
ca29da43 NS |
131 | { |
132 | const counts_entry_t *entry1 = of1; | |
133 | const counts_entry_t *entry2 = of2; | |
134 | ||
796621e8 | 135 | return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr; |
ca29da43 NS |
136 | } |
137 | ||
138 | static void | |
159b3be1 | 139 | htab_counts_entry_del (void *of) |
ca29da43 NS |
140 | { |
141 | counts_entry_t *entry = of; | |
142 | ||
ca29da43 NS |
143 | free (entry->counts); |
144 | free (entry); | |
145 | } | |
146 | ||
147 | /* Read in the counts file, if available. */ | |
148 | ||
149 | static void | |
159b3be1 | 150 | read_counts_file (void) |
ca29da43 | 151 | { |
9b514d25 NS |
152 | gcov_unsigned_t fn_ident = 0; |
153 | gcov_unsigned_t version, checksum = -1; | |
154 | unsigned ix; | |
ca29da43 NS |
155 | counts_entry_t *summaried = NULL; |
156 | unsigned seen_summary = 0; | |
7d63a2fa NS |
157 | gcov_unsigned_t tag; |
158 | int error = 0; | |
159 | ||
ca29da43 NS |
160 | if (!gcov_open (da_file_name, 1)) |
161 | return; | |
159b3be1 | 162 | |
ca29da43 NS |
163 | if (gcov_read_unsigned () != GCOV_DATA_MAGIC) |
164 | { | |
165 | warning ("`%s' is not a gcov data file", da_file_name); | |
166 | gcov_close (); | |
167 | return; | |
168 | } | |
169 | else if ((version = gcov_read_unsigned ()) != GCOV_VERSION) | |
170 | { | |
171 | char v[4], e[4]; | |
9b514d25 | 172 | gcov_unsigned_t required = GCOV_VERSION; |
159b3be1 | 173 | |
ca29da43 NS |
174 | for (ix = 4; ix--; required >>= 8, version >>= 8) |
175 | { | |
176 | v[ix] = version; | |
177 | e[ix] = required; | |
178 | } | |
179 | warning ("`%s' is version `%.4s', expected version `%.4s'", | |
180 | da_file_name, v, e); | |
181 | gcov_close (); | |
182 | return; | |
183 | } | |
159b3be1 | 184 | |
dd486eb2 NS |
185 | /* Read and discard the stamp. */ |
186 | gcov_read_unsigned (); | |
187 | ||
ca29da43 NS |
188 | counts_hash = htab_create (10, |
189 | htab_counts_entry_hash, htab_counts_entry_eq, | |
190 | htab_counts_entry_del); | |
7d63a2fa | 191 | while ((tag = gcov_read_unsigned ())) |
ca29da43 | 192 | { |
7d63a2fa | 193 | gcov_unsigned_t length; |
9b514d25 | 194 | gcov_position_t offset; |
159b3be1 | 195 | |
ca29da43 NS |
196 | length = gcov_read_unsigned (); |
197 | offset = gcov_position (); | |
198 | if (tag == GCOV_TAG_FUNCTION) | |
199 | { | |
796621e8 | 200 | fn_ident = gcov_read_unsigned (); |
ca29da43 NS |
201 | checksum = gcov_read_unsigned (); |
202 | if (seen_summary) | |
203 | { | |
204 | /* We have already seen a summary, this means that this | |
205 | new function begins a new set of program runs. We | |
206 | must unlink the summaried chain. */ | |
207 | counts_entry_t *entry, *chain; | |
159b3be1 | 208 | |
ca29da43 NS |
209 | for (entry = summaried; entry; entry = chain) |
210 | { | |
211 | chain = entry->chain; | |
ca29da43 NS |
212 | entry->chain = NULL; |
213 | } | |
214 | summaried = NULL; | |
215 | seen_summary = 0; | |
216 | } | |
217 | } | |
218 | else if (tag == GCOV_TAG_PROGRAM_SUMMARY) | |
219 | { | |
220 | counts_entry_t *entry; | |
221 | struct gcov_summary summary; | |
159b3be1 | 222 | |
ca29da43 NS |
223 | gcov_read_summary (&summary); |
224 | seen_summary = 1; | |
225 | for (entry = summaried; entry; entry = entry->chain) | |
226 | { | |
cdb23767 | 227 | struct gcov_ctr_summary *csum = &summary.ctrs[entry->ctr]; |
159b3be1 | 228 | |
cdb23767 NS |
229 | entry->summary.runs += csum->runs; |
230 | entry->summary.sum_all += csum->sum_all; | |
231 | if (entry->summary.run_max < csum->run_max) | |
232 | entry->summary.run_max = csum->run_max; | |
233 | entry->summary.sum_max += csum->sum_max; | |
ca29da43 NS |
234 | } |
235 | } | |
796621e8 | 236 | else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident) |
ca29da43 NS |
237 | { |
238 | counts_entry_t **slot, *entry, elt; | |
239 | unsigned n_counts = length / 8; | |
240 | unsigned ix; | |
241 | ||
796621e8 | 242 | elt.ident = fn_ident; |
cdb23767 | 243 | elt.ctr = GCOV_COUNTER_FOR_TAG (tag); |
ca29da43 NS |
244 | |
245 | slot = (counts_entry_t **) htab_find_slot | |
246 | (counts_hash, &elt, INSERT); | |
247 | entry = *slot; | |
248 | if (!entry) | |
249 | { | |
cdb23767 | 250 | *slot = entry = xcalloc (1, sizeof (counts_entry_t)); |
796621e8 | 251 | entry->ident = elt.ident; |
cdb23767 | 252 | entry->ctr = elt.ctr; |
ca29da43 | 253 | entry->checksum = checksum; |
cdb23767 | 254 | entry->summary.num = n_counts; |
ca29da43 NS |
255 | entry->counts = xcalloc (n_counts, sizeof (gcov_type)); |
256 | } | |
cdb23767 NS |
257 | else if (entry->checksum != checksum |
258 | || entry->summary.num != n_counts) | |
ca29da43 | 259 | { |
796621e8 | 260 | warning ("coverage mismatch for function %u", fn_ident); |
ca29da43 NS |
261 | htab_delete (counts_hash); |
262 | break; | |
263 | } | |
9b514d25 NS |
264 | else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE) |
265 | { | |
266 | warning ("cannot merge separate %s counters for function %u", | |
267 | ctr_names[elt.ctr], fn_ident); | |
268 | goto skip_merge; | |
269 | } | |
270 | ||
271 | if (elt.ctr < GCOV_COUNTERS_SUMMABLE | |
272 | /* This should always be true for a just allocated entry, | |
159b3be1 AJ |
273 | and always false for an existing one. Check this way, in |
274 | case the gcov file is corrupt. */ | |
9b514d25 | 275 | && (!entry->chain || summaried != entry)) |
ca29da43 NS |
276 | { |
277 | entry->chain = summaried; | |
278 | summaried = entry; | |
279 | } | |
280 | for (ix = 0; ix != n_counts; ix++) | |
281 | entry->counts[ix] += gcov_read_counter (); | |
9b514d25 | 282 | skip_merge:; |
ca29da43 | 283 | } |
474f141e | 284 | gcov_sync (offset, length); |
ca29da43 | 285 | if ((error = gcov_is_error ())) |
7d63a2fa | 286 | break; |
ca29da43 NS |
287 | } |
288 | ||
7d63a2fa NS |
289 | if (!gcov_is_eof ()) |
290 | { | |
291 | warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted", | |
292 | da_file_name); | |
293 | htab_delete (counts_hash); | |
294 | } | |
159b3be1 | 295 | |
ca29da43 NS |
296 | gcov_close (); |
297 | } | |
298 | ||
299 | /* Returns the counters for a particular tag. */ | |
300 | ||
301 | gcov_type * | |
cdb23767 NS |
302 | get_coverage_counts (unsigned counter, unsigned expected, |
303 | const struct gcov_ctr_summary **summary) | |
ca29da43 NS |
304 | { |
305 | counts_entry_t *entry, elt; | |
306 | ||
71c0e7fc | 307 | /* No hash table, no counts. */ |
ca29da43 NS |
308 | if (!counts_hash) |
309 | { | |
310 | static int warned = 0; | |
311 | ||
312 | if (!warned++) | |
313 | warning ("file %s not found, execution counts assumed to be zero", | |
314 | da_file_name); | |
315 | return NULL; | |
316 | } | |
317 | ||
6d70e6be | 318 | elt.ident = current_function_funcdef_no + 1; |
cdb23767 | 319 | elt.ctr = counter; |
ca29da43 NS |
320 | entry = htab_find (counts_hash, &elt); |
321 | if (!entry) | |
322 | { | |
796621e8 NS |
323 | warning ("no coverage for function '%s' found.", IDENTIFIER_POINTER |
324 | (DECL_ASSEMBLER_NAME (current_function_decl))); | |
ca29da43 NS |
325 | return 0; |
326 | } | |
159b3be1 | 327 | |
cdb23767 | 328 | if (expected != entry->summary.num |
ca29da43 NS |
329 | || compute_checksum () != entry->checksum) |
330 | { | |
796621e8 NS |
331 | warning ("coverage mismatch for `%s'", IDENTIFIER_POINTER |
332 | (DECL_ASSEMBLER_NAME (current_function_decl))); | |
ca29da43 NS |
333 | return NULL; |
334 | } | |
159b3be1 | 335 | |
cdb23767 NS |
336 | if (summary) |
337 | *summary = &entry->summary; | |
ca29da43 NS |
338 | |
339 | return entry->counts; | |
340 | } | |
cdb23767 | 341 | |
6356f892 | 342 | /* Allocate NUM counters of type COUNTER. Returns nonzero if the |
6d70e6be | 343 | allocation succeeded. */ |
cdb23767 | 344 | |
6d70e6be NS |
345 | int |
346 | coverage_counter_alloc (unsigned counter, unsigned num) | |
cdb23767 | 347 | { |
6d70e6be NS |
348 | if (no_coverage) |
349 | return 0; | |
159b3be1 | 350 | |
6d70e6be NS |
351 | if (!num) |
352 | return 1; | |
159b3be1 | 353 | |
cdb23767 NS |
354 | if (!ctr_labels[counter]) |
355 | { | |
356 | /* Generate and save a copy of this so it can be shared. */ | |
357 | char buf[20]; | |
159b3be1 | 358 | |
cdb23767 NS |
359 | ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1); |
360 | ctr_labels[counter] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); | |
361 | } | |
6d70e6be NS |
362 | fn_b_ctrs[counter] = fn_n_ctrs[counter]; |
363 | fn_n_ctrs[counter] += num; | |
364 | fn_ctr_mask |= 1 << counter; | |
365 | return 1; | |
366 | } | |
cdb23767 | 367 | |
6d70e6be NS |
368 | /* Generate a MEM rtl to access COUNTER NO. */ |
369 | ||
370 | rtx | |
371 | coverage_counter_ref (unsigned counter, unsigned no) | |
372 | { | |
373 | unsigned gcov_size = tree_low_cst (TYPE_SIZE (GCOV_TYPE_NODE), 1); | |
374 | enum machine_mode mode = mode_for_size (gcov_size, MODE_INT, 0); | |
375 | rtx ref; | |
376 | ||
377 | if (no >= fn_n_ctrs[counter] - fn_b_ctrs[counter]) | |
378 | abort (); | |
379 | no += prg_n_ctrs[counter] + fn_b_ctrs[counter]; | |
9b514d25 | 380 | ref = plus_constant (ctr_labels[counter], gcov_size / BITS_PER_UNIT * no); |
cdb23767 NS |
381 | ref = gen_rtx_MEM (mode, ref); |
382 | set_mem_alias_set (ref, new_alias_set ()); | |
383 | ||
384 | return ref; | |
385 | } | |
ca29da43 NS |
386 | \f |
387 | /* Generate a checksum for a string. CHKSUM is the current | |
71c0e7fc | 388 | checksum. */ |
ca29da43 NS |
389 | |
390 | static unsigned | |
391 | checksum_string (unsigned chksum, const char *string) | |
392 | { | |
393 | do | |
394 | { | |
395 | unsigned value = *string << 24; | |
396 | unsigned ix; | |
397 | ||
398 | for (ix = 8; ix--; value <<= 1) | |
399 | { | |
400 | unsigned feedback; | |
159b3be1 | 401 | |
ca29da43 NS |
402 | feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0; |
403 | chksum <<= 1; | |
404 | chksum ^= feedback; | |
405 | } | |
406 | } | |
407 | while (*string++); | |
159b3be1 | 408 | |
ca29da43 NS |
409 | return chksum; |
410 | } | |
411 | ||
412 | /* Compute checksum for the current function. We generate a CRC32. */ | |
413 | ||
414 | static unsigned | |
159b3be1 | 415 | compute_checksum (void) |
ca29da43 NS |
416 | { |
417 | unsigned chksum = DECL_SOURCE_LINE (current_function_decl); | |
418 | ||
419 | chksum = checksum_string (chksum, DECL_SOURCE_FILE (current_function_decl)); | |
420 | chksum = checksum_string | |
421 | (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl))); | |
422 | ||
423 | return chksum; | |
424 | } | |
425 | \f | |
426 | /* Begin output to the graph file for the current function. | |
427 | Opens the output file, if not already done. Writes the | |
6356f892 | 428 | function header, if not already done. Returns nonzero if data |
ca29da43 NS |
429 | should be output. */ |
430 | ||
431 | int | |
159b3be1 | 432 | coverage_begin_output (void) |
ca29da43 | 433 | { |
6d70e6be NS |
434 | if (no_coverage) |
435 | return 0; | |
159b3be1 | 436 | |
ca29da43 NS |
437 | if (!bbg_function_announced) |
438 | { | |
439 | const char *file = DECL_SOURCE_FILE (current_function_decl); | |
440 | unsigned line = DECL_SOURCE_LINE (current_function_decl); | |
441 | unsigned long offset; | |
159b3be1 | 442 | |
ca29da43 NS |
443 | if (!bbg_file_opened) |
444 | { | |
445 | if (!gcov_open (bbg_file_name, -1)) | |
446 | error ("cannot open %s", bbg_file_name); | |
447 | else | |
448 | { | |
449 | gcov_write_unsigned (GCOV_GRAPH_MAGIC); | |
450 | gcov_write_unsigned (GCOV_VERSION); | |
dd486eb2 | 451 | gcov_write_unsigned (local_tick); |
ca29da43 NS |
452 | } |
453 | bbg_file_opened = 1; | |
454 | } | |
159b3be1 | 455 | |
ca29da43 NS |
456 | /* Announce function */ |
457 | offset = gcov_write_tag (GCOV_TAG_FUNCTION); | |
6d70e6be | 458 | gcov_write_unsigned (current_function_funcdef_no + 1); |
796621e8 | 459 | gcov_write_unsigned (compute_checksum ()); |
ca29da43 NS |
460 | gcov_write_string (IDENTIFIER_POINTER |
461 | (DECL_ASSEMBLER_NAME (current_function_decl))); | |
ca29da43 NS |
462 | gcov_write_string (file); |
463 | gcov_write_unsigned (line); | |
464 | gcov_write_length (offset); | |
465 | ||
466 | bbg_function_announced = 1; | |
467 | } | |
468 | return !gcov_is_error (); | |
469 | } | |
470 | ||
471 | /* Finish coverage data for the current function. Verify no output | |
472 | error has occurred. Save function coverage counts. */ | |
473 | ||
474 | void | |
159b3be1 | 475 | coverage_end_function (void) |
ca29da43 NS |
476 | { |
477 | unsigned i; | |
159b3be1 | 478 | |
ca29da43 | 479 | if (bbg_file_opened > 1 && gcov_is_error ()) |
159b3be1 | 480 | { |
ca29da43 NS |
481 | warning ("error writing `%s'", bbg_file_name); |
482 | bbg_file_opened = -1; | |
483 | } | |
cdb23767 NS |
484 | |
485 | if (fn_ctr_mask) | |
486 | { | |
487 | struct function_list *item; | |
159b3be1 | 488 | |
cdb23767 | 489 | item = xmalloc (sizeof (struct function_list)); |
159b3be1 | 490 | |
cdb23767 NS |
491 | *functions_tail = item; |
492 | functions_tail = &item->next; | |
159b3be1 | 493 | |
cdb23767 | 494 | item->next = 0; |
6d70e6be | 495 | item->ident = current_function_funcdef_no + 1; |
cdb23767 NS |
496 | item->checksum = compute_checksum (); |
497 | for (i = 0; i != GCOV_COUNTERS; i++) | |
498 | { | |
499 | item->n_ctrs[i] = fn_n_ctrs[i]; | |
500 | prg_n_ctrs[i] += fn_n_ctrs[i]; | |
6d70e6be | 501 | fn_n_ctrs[i] = fn_b_ctrs[i] = 0; |
cdb23767 NS |
502 | } |
503 | prg_ctr_mask |= fn_ctr_mask; | |
504 | fn_ctr_mask = 0; | |
505 | } | |
ca29da43 NS |
506 | bbg_function_announced = 0; |
507 | } | |
508 | ||
cdb23767 | 509 | /* Creates the gcov_fn_info RECORD_TYPE. */ |
ca29da43 | 510 | |
ca29da43 | 511 | static tree |
159b3be1 | 512 | build_fn_info_type (unsigned int counters) |
ca29da43 | 513 | { |
cdb23767 | 514 | tree type = (*lang_hooks.types.make_type) (RECORD_TYPE); |
ca29da43 | 515 | tree field, fields; |
cdb23767 | 516 | tree array_type; |
159b3be1 | 517 | |
796621e8 | 518 | /* ident */ |
9b514d25 | 519 | fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node); |
ca29da43 | 520 | |
cdb23767 | 521 | /* checksum */ |
9b514d25 | 522 | field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node); |
ca29da43 NS |
523 | TREE_CHAIN (field) = fields; |
524 | fields = field; | |
525 | ||
cdb23767 NS |
526 | array_type = build_index_type (build_int_2 (counters - 1, 0)); |
527 | array_type = build_array_type (unsigned_type_node, array_type); | |
159b3be1 | 528 | |
ca29da43 | 529 | /* counters */ |
cdb23767 | 530 | field = build_decl (FIELD_DECL, NULL_TREE, array_type); |
ca29da43 NS |
531 | TREE_CHAIN (field) = fields; |
532 | fields = field; | |
533 | ||
cdb23767 NS |
534 | finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE); |
535 | ||
536 | return type; | |
ca29da43 NS |
537 | } |
538 | ||
cdb23767 NS |
539 | /* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is |
540 | the function being processed and TYPE is the gcov_fn_info | |
541 | RECORD_TYPE. */ | |
542 | ||
ca29da43 | 543 | static tree |
159b3be1 | 544 | build_fn_info_value (const struct function_list *function, tree type) |
ca29da43 | 545 | { |
cdb23767 NS |
546 | tree value = NULL_TREE; |
547 | tree fields = TYPE_FIELDS (type); | |
cdb23767 NS |
548 | unsigned ix; |
549 | tree array_value = NULL_TREE; | |
159b3be1 | 550 | |
796621e8 | 551 | /* ident */ |
cdb23767 | 552 | value = tree_cons (fields, |
9b514d25 | 553 | convert (unsigned_intSI_type_node, |
796621e8 | 554 | build_int_2 (function->ident, 0)), |
ca29da43 | 555 | value); |
cdb23767 | 556 | fields = TREE_CHAIN (fields); |
159b3be1 | 557 | |
cdb23767 NS |
558 | /* checksum */ |
559 | value = tree_cons (fields, | |
9b514d25 | 560 | convert (unsigned_intSI_type_node, |
cdb23767 | 561 | build_int_2 (function->checksum, 0)), |
ca29da43 | 562 | value); |
cdb23767 | 563 | fields = TREE_CHAIN (fields); |
159b3be1 | 564 | |
ca29da43 | 565 | /* counters */ |
cdb23767 NS |
566 | for (ix = 0; ix != GCOV_COUNTERS; ix++) |
567 | if (prg_ctr_mask & (1 << ix)) | |
568 | { | |
569 | tree counters = convert (unsigned_type_node, | |
570 | build_int_2 (function->n_ctrs[ix], 0)); | |
159b3be1 | 571 | |
cdb23767 NS |
572 | array_value = tree_cons (NULL_TREE, counters, array_value); |
573 | } | |
159b3be1 | 574 | |
cdb23767 NS |
575 | array_value = build_constructor (TREE_TYPE (fields), nreverse (array_value)); |
576 | value = tree_cons (fields, array_value, value); | |
ca29da43 | 577 | |
cdb23767 | 578 | value = build_constructor (type, nreverse (value)); |
159b3be1 | 579 | |
ca29da43 NS |
580 | return value; |
581 | } | |
582 | ||
cdb23767 NS |
583 | /* Creates the gcov_ctr_info RECORD_TYPE. */ |
584 | ||
ca29da43 | 585 | static tree |
159b3be1 | 586 | build_ctr_info_type (void) |
ca29da43 | 587 | { |
cdb23767 NS |
588 | tree type = (*lang_hooks.types.make_type) (RECORD_TYPE); |
589 | tree field, fields = NULL_TREE; | |
9b514d25 | 590 | tree gcov_ptr_type = build_pointer_type (GCOV_TYPE_NODE); |
09780dfb | 591 | tree gcov_merge_fn_type; |
9b514d25 | 592 | |
cdb23767 | 593 | /* counters */ |
9b514d25 | 594 | field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node); |
ca29da43 NS |
595 | TREE_CHAIN (field) = fields; |
596 | fields = field; | |
597 | ||
cdb23767 | 598 | /* values */ |
9b514d25 | 599 | field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type); |
ca29da43 NS |
600 | TREE_CHAIN (field) = fields; |
601 | fields = field; | |
602 | ||
09780dfb ZD |
603 | /* merge */ |
604 | gcov_merge_fn_type = | |
9b514d25 NS |
605 | build_function_type_list (void_type_node, |
606 | gcov_ptr_type, unsigned_type_node, | |
607 | NULL_TREE); | |
09780dfb ZD |
608 | field = build_decl (FIELD_DECL, NULL_TREE, |
609 | build_pointer_type (gcov_merge_fn_type)); | |
610 | TREE_CHAIN (field) = fields; | |
611 | fields = field; | |
612 | ||
cdb23767 | 613 | finish_builtin_struct (type, "__gcov_ctr_info", fields, NULL_TREE); |
ca29da43 | 614 | |
cdb23767 | 615 | return type; |
ca29da43 NS |
616 | } |
617 | ||
cdb23767 NS |
618 | /* Creates a CONSTRUCTOR for a gcov_ctr_info. COUNTER is |
619 | the counter being processed and TYPE is the gcov_ctr_info | |
620 | RECORD_TYPE. */ | |
621 | ||
ca29da43 | 622 | static tree |
159b3be1 | 623 | build_ctr_info_value (unsigned int counter, tree type) |
ca29da43 NS |
624 | { |
625 | tree value = NULL_TREE; | |
cdb23767 | 626 | tree fields = TYPE_FIELDS (type); |
09780dfb | 627 | tree fn; |
ca29da43 | 628 | |
cdb23767 NS |
629 | /* counters */ |
630 | value = tree_cons (fields, | |
9b514d25 | 631 | convert (unsigned_intSI_type_node, |
cdb23767 | 632 | build_int_2 (prg_n_ctrs[counter], 0)), |
ca29da43 | 633 | value); |
cdb23767 | 634 | fields = TREE_CHAIN (fields); |
ca29da43 | 635 | |
cdb23767 | 636 | if (prg_n_ctrs[counter]) |
ca29da43 | 637 | { |
cdb23767 | 638 | tree array_type, array; |
159b3be1 | 639 | |
cdb23767 NS |
640 | array_type = build_index_type (build_int_2 (prg_n_ctrs[counter] - 1, 0)); |
641 | array_type = build_array_type (TREE_TYPE (TREE_TYPE (fields)), | |
642 | array_type); | |
159b3be1 | 643 | |
cdb23767 NS |
644 | array = build (VAR_DECL, array_type, NULL_TREE, NULL_TREE); |
645 | TREE_STATIC (array) = 1; | |
646 | DECL_NAME (array) = get_identifier (XSTR (ctr_labels[counter], 0)); | |
647 | assemble_variable (array, 0, 0, 0); | |
159b3be1 | 648 | |
cdb23767 NS |
649 | value = tree_cons (fields, |
650 | build1 (ADDR_EXPR, TREE_TYPE (fields), array), | |
651 | value); | |
ca29da43 NS |
652 | } |
653 | else | |
cdb23767 | 654 | value = tree_cons (fields, null_pointer_node, value); |
09780dfb ZD |
655 | fields = TREE_CHAIN (fields); |
656 | ||
657 | fn = build_decl (FUNCTION_DECL, | |
658 | get_identifier (ctr_merge_functions[counter]), | |
659 | TREE_TYPE (TREE_TYPE (fields))); | |
660 | DECL_EXTERNAL (fn) = 1; | |
661 | TREE_PUBLIC (fn) = 1; | |
662 | DECL_ARTIFICIAL (fn) = 1; | |
663 | TREE_NOTHROW (fn) = 1; | |
664 | value = tree_cons (fields, | |
9b514d25 | 665 | build1 (ADDR_EXPR, TREE_TYPE (fields), fn), |
09780dfb | 666 | value); |
ca29da43 | 667 | |
cdb23767 | 668 | value = build_constructor (type, nreverse (value)); |
159b3be1 | 669 | |
ca29da43 NS |
670 | return value; |
671 | } | |
672 | ||
cdb23767 NS |
673 | /* Creates the gcov_info RECORD_TYPE and initializer for it. Returns a |
674 | CONSTRUCTOR. */ | |
675 | ||
ca29da43 | 676 | static tree |
159b3be1 | 677 | build_gcov_info (void) |
ca29da43 | 678 | { |
cdb23767 NS |
679 | unsigned n_ctr_types, ix; |
680 | tree type, const_type; | |
681 | tree fn_info_type, fn_info_value = NULL_TREE; | |
682 | tree fn_info_ptr_type; | |
683 | tree ctr_info_type, ctr_info_ary_type, ctr_info_value = NULL_TREE; | |
684 | tree field, fields = NULL_TREE; | |
685 | tree value = NULL_TREE; | |
686 | tree filename_string; | |
ca29da43 NS |
687 | char *filename; |
688 | int filename_len; | |
cdb23767 NS |
689 | unsigned n_fns; |
690 | const struct function_list *fn; | |
691 | tree string_type; | |
159b3be1 | 692 | |
cdb23767 NS |
693 | /* Count the number of active counters. */ |
694 | for (n_ctr_types = 0, ix = 0; ix != GCOV_COUNTERS; ix++) | |
695 | if (prg_ctr_mask & (1 << ix)) | |
696 | n_ctr_types++; | |
159b3be1 | 697 | |
cdb23767 NS |
698 | type = (*lang_hooks.types.make_type) (RECORD_TYPE); |
699 | const_type = build_qualified_type (type, TYPE_QUAL_CONST); | |
159b3be1 | 700 | |
cdb23767 | 701 | /* Version ident */ |
9b514d25 | 702 | field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node); |
ca29da43 NS |
703 | TREE_CHAIN (field) = fields; |
704 | fields = field; | |
9b514d25 | 705 | value = tree_cons (field, convert (unsigned_intSI_type_node, |
cdb23767 | 706 | build_int_2 (GCOV_VERSION, 0)), |
ca29da43 | 707 | value); |
159b3be1 | 708 | |
ca29da43 | 709 | /* next -- NULL */ |
cdb23767 NS |
710 | field = build_decl (FIELD_DECL, NULL_TREE, build_pointer_type (const_type)); |
711 | TREE_CHAIN (field) = fields; | |
712 | fields = field; | |
713 | value = tree_cons (field, null_pointer_node, value); | |
159b3be1 | 714 | |
dd486eb2 NS |
715 | /* stamp */ |
716 | field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node); | |
717 | TREE_CHAIN (field) = fields; | |
718 | fields = field; | |
719 | value = tree_cons (field, convert (unsigned_intSI_type_node, | |
720 | build_int_2 (local_tick, 0)), | |
721 | value); | |
722 | ||
ca29da43 | 723 | /* Filename */ |
cdb23767 NS |
724 | string_type = build_pointer_type (build_qualified_type (char_type_node, |
725 | TYPE_QUAL_CONST)); | |
726 | field = build_decl (FIELD_DECL, NULL_TREE, string_type); | |
727 | TREE_CHAIN (field) = fields; | |
728 | fields = field; | |
ca29da43 NS |
729 | filename = getpwd (); |
730 | filename = (filename && da_file_name[0] != '/' | |
731 | ? concat (filename, "/", da_file_name, NULL) | |
732 | : da_file_name); | |
733 | filename_len = strlen (filename); | |
734 | filename_string = build_string (filename_len + 1, filename); | |
735 | if (filename != da_file_name) | |
736 | free (filename); | |
737 | TREE_TYPE (filename_string) = | |
738 | build_array_type (char_type_node, | |
739 | build_index_type (build_int_2 (filename_len, 0))); | |
cdb23767 | 740 | value = tree_cons (field, build1 (ADDR_EXPR, string_type, filename_string), |
ca29da43 | 741 | value); |
159b3be1 | 742 | |
cdb23767 NS |
743 | /* Build the fn_info type and initializer. */ |
744 | fn_info_type = build_fn_info_type (n_ctr_types); | |
745 | fn_info_ptr_type = build_pointer_type (build_qualified_type | |
746 | (fn_info_type, TYPE_QUAL_CONST)); | |
747 | for (fn = functions_head, n_fns = 0; fn; fn = fn->next, n_fns++) | |
748 | fn_info_value = tree_cons (NULL_TREE, | |
749 | build_fn_info_value (fn, fn_info_type), | |
750 | fn_info_value); | |
751 | if (n_fns) | |
ca29da43 NS |
752 | { |
753 | tree array_type; | |
754 | ||
cdb23767 NS |
755 | array_type = build_index_type (build_int_2 (n_fns - 1, 0)); |
756 | array_type = build_array_type (fn_info_type, array_type); | |
159b3be1 | 757 | |
cdb23767 NS |
758 | fn_info_value = build_constructor (array_type, nreverse (fn_info_value)); |
759 | fn_info_value = build1 (ADDR_EXPR, fn_info_ptr_type, fn_info_value); | |
ca29da43 NS |
760 | } |
761 | else | |
cdb23767 | 762 | fn_info_value = null_pointer_node; |
159b3be1 | 763 | |
cdb23767 NS |
764 | /* number of functions */ |
765 | field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); | |
766 | TREE_CHAIN (field) = fields; | |
767 | fields = field; | |
768 | value = tree_cons (field, | |
769 | convert (unsigned_type_node, build_int_2 (n_fns, 0)), | |
770 | value); | |
159b3be1 | 771 | |
cdb23767 NS |
772 | /* fn_info table */ |
773 | field = build_decl (FIELD_DECL, NULL_TREE, fn_info_ptr_type); | |
774 | TREE_CHAIN (field) = fields; | |
775 | fields = field; | |
776 | value = tree_cons (field, fn_info_value, value); | |
ca29da43 | 777 | |
cdb23767 NS |
778 | /* counter_mask */ |
779 | field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node); | |
780 | TREE_CHAIN (field) = fields; | |
781 | fields = field; | |
782 | value = tree_cons (field, | |
ca29da43 | 783 | convert (unsigned_type_node, |
cdb23767 | 784 | build_int_2 (prg_ctr_mask, 0)), |
ca29da43 | 785 | value); |
159b3be1 | 786 | |
cdb23767 NS |
787 | /* counters */ |
788 | ctr_info_type = build_ctr_info_type (); | |
789 | ctr_info_ary_type = build_index_type (build_int_2 (n_ctr_types, 0)); | |
790 | ctr_info_ary_type = build_array_type (ctr_info_type, ctr_info_ary_type); | |
791 | for (ix = 0; ix != GCOV_COUNTERS; ix++) | |
792 | if (prg_ctr_mask & (1 << ix)) | |
793 | ctr_info_value = tree_cons (NULL_TREE, | |
794 | build_ctr_info_value (ix, ctr_info_type), | |
795 | ctr_info_value); | |
796 | ctr_info_value = build_constructor (ctr_info_ary_type, | |
797 | nreverse (ctr_info_value)); | |
798 | ||
799 | field = build_decl (FIELD_DECL, NULL_TREE, ctr_info_ary_type); | |
800 | TREE_CHAIN (field) = fields; | |
801 | fields = field; | |
802 | value = tree_cons (field, ctr_info_value, value); | |
159b3be1 | 803 | |
cdb23767 | 804 | finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE); |
ca29da43 | 805 | |
cdb23767 | 806 | value = build_constructor (type, nreverse (value)); |
159b3be1 | 807 | |
ca29da43 NS |
808 | return value; |
809 | } | |
810 | ||
cdb23767 | 811 | /* Write out the structure which libgcov uses to locate all the |
ca29da43 NS |
812 | counters. The structures used here must match those defined in |
813 | gcov-io.h. Write out the constructor to call __gcov_init. */ | |
814 | ||
815 | static void | |
159b3be1 | 816 | create_coverage (void) |
ca29da43 | 817 | { |
cdb23767 | 818 | tree gcov_info, gcov_info_value; |
ca29da43 NS |
819 | char name[20]; |
820 | char *ctor_name; | |
821 | tree ctor; | |
822 | rtx gcov_info_address; | |
ca29da43 | 823 | |
6d70e6be | 824 | no_coverage = 1; /* Disable any further coverage. */ |
159b3be1 | 825 | |
cdb23767 | 826 | if (!prg_ctr_mask) |
ca29da43 | 827 | return; |
159b3be1 | 828 | |
cdb23767 | 829 | gcov_info_value = build_gcov_info (); |
ca29da43 | 830 | |
cdb23767 NS |
831 | gcov_info = build (VAR_DECL, TREE_TYPE (gcov_info_value), |
832 | NULL_TREE, NULL_TREE); | |
833 | DECL_INITIAL (gcov_info) = gcov_info_value; | |
ca29da43 NS |
834 | |
835 | TREE_STATIC (gcov_info) = 1; | |
836 | ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0); | |
837 | DECL_NAME (gcov_info) = get_identifier (name); | |
159b3be1 | 838 | |
ca29da43 NS |
839 | /* Build structure. */ |
840 | assemble_variable (gcov_info, 0, 0, 0); | |
841 | ||
842 | /* Build the constructor function to invoke __gcov_init. */ | |
843 | ctor_name = concat (IDENTIFIER_POINTER (get_file_function_name ('I')), | |
844 | "_GCOV", NULL); | |
845 | ctor = build_decl (FUNCTION_DECL, get_identifier (ctor_name), | |
846 | build_function_type (void_type_node, NULL_TREE)); | |
847 | free (ctor_name); | |
848 | DECL_EXTERNAL (ctor) = 0; | |
849 | ||
850 | /* It can be a static function as long as collect2 does not have | |
851 | to scan the object file to find its ctor/dtor routine. */ | |
852 | TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors; | |
853 | TREE_USED (ctor) = 1; | |
854 | DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node); | |
6d70e6be | 855 | DECL_UNINLINABLE (ctor) = 1; |
ca29da43 NS |
856 | |
857 | ctor = (*lang_hooks.decls.pushdecl) (ctor); | |
858 | rest_of_decl_compilation (ctor, 0, 1, 0); | |
859 | announce_function (ctor); | |
860 | current_function_decl = ctor; | |
861 | DECL_INITIAL (ctor) = error_mark_node; | |
862 | make_decl_rtl (ctor, NULL); | |
ee6b0296 | 863 | init_function_start (ctor); |
ca29da43 NS |
864 | (*lang_hooks.decls.pushlevel) (0); |
865 | expand_function_start (ctor, 0); | |
ca29da43 NS |
866 | |
867 | /* Actually generate the code to call __gcov_init. */ | |
868 | gcov_info_address = force_reg (Pmode, XEXP (DECL_RTL (gcov_info), 0)); | |
869 | emit_library_call (gcov_init_libfunc, LCT_NORMAL, VOIDmode, 1, | |
870 | gcov_info_address, Pmode); | |
871 | ||
1f9cc6db | 872 | expand_function_end (); |
ca29da43 NS |
873 | (*lang_hooks.decls.poplevel) (1, 0, 1); |
874 | ||
ca29da43 NS |
875 | rest_of_compilation (ctor); |
876 | ||
ca29da43 NS |
877 | if (! quiet_flag) |
878 | fflush (asm_out_file); | |
879 | current_function_decl = NULL_TREE; | |
880 | ||
881 | if (targetm.have_ctors_dtors) | |
882 | (* targetm.asm_out.constructor) (XEXP (DECL_RTL (ctor), 0), | |
883 | DEFAULT_INIT_PRIORITY); | |
884 | } | |
ca29da43 NS |
885 | \f |
886 | /* Perform file-level initialization. Read in data file, generate name | |
71c0e7fc | 887 | of graph file. */ |
ca29da43 NS |
888 | |
889 | void | |
159b3be1 | 890 | coverage_init (const char *filename) |
ca29da43 NS |
891 | { |
892 | int len = strlen (filename); | |
893 | ||
796621e8 | 894 | /* Name of da file. */ |
ca29da43 NS |
895 | da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1); |
896 | strcpy (da_file_name, filename); | |
897 | strcat (da_file_name, GCOV_DATA_SUFFIX); | |
159b3be1 | 898 | |
796621e8 | 899 | /* Name of bbg file. */ |
ca29da43 NS |
900 | bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1); |
901 | strcpy (bbg_file_name, filename); | |
902 | strcat (bbg_file_name, GCOV_GRAPH_SUFFIX); | |
796621e8 NS |
903 | |
904 | read_counts_file (); | |
ca29da43 NS |
905 | } |
906 | ||
907 | /* Performs file-level cleanup. Close graph file, generate coverage | |
908 | variables and constructor. */ | |
909 | ||
910 | void | |
159b3be1 | 911 | coverage_finish (void) |
ca29da43 NS |
912 | { |
913 | create_coverage (); | |
914 | if (bbg_file_opened) | |
915 | { | |
916 | int error = gcov_close (); | |
159b3be1 | 917 | |
ca29da43 NS |
918 | if (error) |
919 | unlink (bbg_file_name); | |
dd486eb2 NS |
920 | if (!local_tick) |
921 | /* Only remove the da file, if we cannot stamp it. If we can | |
922 | stamp it, libgcov will DTRT. */ | |
ca29da43 NS |
923 | unlink (da_file_name); |
924 | } | |
925 | } | |
926 | ||
ca29da43 | 927 | #include "gt-coverage.h" |