coverage.c revision 169689
1132718Skan/* Read and write coverage files, and associated functionality. 2132718Skan Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 3169689Skan 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. 4132718Skan Contributed by James E. Wilson, UC Berkeley/Cygnus Support; 5132718Skan based on some ideas from Dain Samples of UC Berkeley. 6132718Skan Further mangling by Bob Manson, Cygnus Support. 7132718Skan Further mangled by Nathan Sidwell, CodeSourcery 8132718Skan 9132718SkanThis file is part of GCC. 10132718Skan 11132718SkanGCC is free software; you can redistribute it and/or modify it under 12132718Skanthe terms of the GNU General Public License as published by the Free 13132718SkanSoftware Foundation; either version 2, or (at your option) any later 14132718Skanversion. 15132718Skan 16132718SkanGCC is distributed in the hope that it will be useful, but WITHOUT ANY 17132718SkanWARRANTY; without even the implied warranty of MERCHANTABILITY or 18132718SkanFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 19132718Skanfor more details. 20132718Skan 21132718SkanYou should have received a copy of the GNU General Public License 22132718Skanalong with GCC; see the file COPYING. If not, write to the Free 23169689SkanSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 24169689Skan02110-1301, USA. */ 25132718Skan 26132718Skan 27132718Skan#define GCOV_LINKAGE 28132718Skan 29132718Skan#include "config.h" 30132718Skan#include "system.h" 31132718Skan#include "coretypes.h" 32132718Skan#include "tm.h" 33132718Skan#include "rtl.h" 34132718Skan#include "tree.h" 35132718Skan#include "flags.h" 36132718Skan#include "output.h" 37132718Skan#include "regs.h" 38132718Skan#include "expr.h" 39132718Skan#include "function.h" 40132718Skan#include "toplev.h" 41132718Skan#include "ggc.h" 42132718Skan#include "coverage.h" 43132718Skan#include "langhooks.h" 44132718Skan#include "hashtab.h" 45169689Skan#include "tree-iterator.h" 46169689Skan#include "cgraph.h" 47132718Skan 48132718Skan#include "gcov-io.c" 49132718Skan 50132718Skanstruct function_list 51132718Skan{ 52132718Skan struct function_list *next; /* next function */ 53132718Skan unsigned ident; /* function ident */ 54132718Skan unsigned checksum; /* function checksum */ 55132718Skan unsigned n_ctrs[GCOV_COUNTERS];/* number of counters. */ 56132718Skan}; 57132718Skan 58132718Skan/* Counts information for a function. */ 59132718Skantypedef struct counts_entry 60132718Skan{ 61132718Skan /* We hash by */ 62132718Skan unsigned ident; 63132718Skan unsigned ctr; 64132718Skan 65132718Skan /* Store */ 66132718Skan unsigned checksum; 67132718Skan gcov_type *counts; 68132718Skan struct gcov_ctr_summary summary; 69132718Skan 70132718Skan /* Workspace */ 71132718Skan struct counts_entry *chain; 72132718Skan 73132718Skan} counts_entry_t; 74132718Skan 75132718Skanstatic struct function_list *functions_head = 0; 76132718Skanstatic struct function_list **functions_tail = &functions_head; 77132718Skanstatic unsigned no_coverage = 0; 78132718Skan 79132718Skan/* Cumulative counter information for whole program. */ 80132718Skanstatic unsigned prg_ctr_mask; /* Mask of counter types generated. */ 81132718Skanstatic unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated. */ 82132718Skan 83132718Skan/* Counter information for current function. */ 84132718Skanstatic unsigned fn_ctr_mask; /* Mask of counters used. */ 85132718Skanstatic unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */ 86132718Skanstatic unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */ 87132718Skan 88132718Skan/* Name of the output file for coverage output file. */ 89132718Skanstatic char *bbg_file_name; 90132718Skanstatic unsigned bbg_file_opened; 91132718Skanstatic int bbg_function_announced; 92132718Skan 93132718Skan/* Name of the count data file. */ 94132718Skanstatic char *da_file_name; 95132718Skan 96132718Skan/* Hash table of count data. */ 97132718Skanstatic htab_t counts_hash = NULL; 98132718Skan 99169689Skan/* Trees representing the counter table arrays. */ 100169689Skanstatic GTY(()) tree tree_ctr_tables[GCOV_COUNTERS]; 101169689Skan 102169689Skan/* The names of the counter tables. Not used if we're 103169689Skan generating counters at tree level. */ 104132718Skanstatic GTY(()) rtx ctr_labels[GCOV_COUNTERS]; 105132718Skan 106132718Skan/* The names of merge functions for counters. */ 107132718Skanstatic const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS; 108132718Skanstatic const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES; 109132718Skan 110132718Skan/* Forward declarations. */ 111132718Skanstatic hashval_t htab_counts_entry_hash (const void *); 112132718Skanstatic int htab_counts_entry_eq (const void *, const void *); 113132718Skanstatic void htab_counts_entry_del (void *); 114132718Skanstatic void read_counts_file (void); 115132718Skanstatic unsigned compute_checksum (void); 116132718Skanstatic unsigned coverage_checksum_string (unsigned, const char *); 117132718Skanstatic tree build_fn_info_type (unsigned); 118132718Skanstatic tree build_fn_info_value (const struct function_list *, tree); 119132718Skanstatic tree build_ctr_info_type (void); 120132718Skanstatic tree build_ctr_info_value (unsigned, tree); 121132718Skanstatic tree build_gcov_info (void); 122132718Skanstatic void create_coverage (void); 123169689Skan 124169689Skan/* Return the type node for gcov_type. */ 125132718Skan 126169689Skantree 127169689Skanget_gcov_type (void) 128169689Skan{ 129169689Skan return lang_hooks.types.type_for_size (GCOV_TYPE_SIZE, false); 130169689Skan} 131169689Skan 132169689Skan/* Return the type node for gcov_unsigned_t. */ 133169689Skan 134169689Skanstatic tree 135169689Skanget_gcov_unsigned_t (void) 136169689Skan{ 137169689Skan return lang_hooks.types.type_for_size (32, true); 138169689Skan} 139132718Skan 140132718Skanstatic hashval_t 141132718Skanhtab_counts_entry_hash (const void *of) 142132718Skan{ 143132718Skan const counts_entry_t *entry = of; 144132718Skan 145132718Skan return entry->ident * GCOV_COUNTERS + entry->ctr; 146132718Skan} 147132718Skan 148132718Skanstatic int 149132718Skanhtab_counts_entry_eq (const void *of1, const void *of2) 150132718Skan{ 151132718Skan const counts_entry_t *entry1 = of1; 152132718Skan const counts_entry_t *entry2 = of2; 153132718Skan 154132718Skan return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr; 155132718Skan} 156132718Skan 157132718Skanstatic void 158132718Skanhtab_counts_entry_del (void *of) 159132718Skan{ 160132718Skan counts_entry_t *entry = of; 161132718Skan 162132718Skan free (entry->counts); 163132718Skan free (entry); 164132718Skan} 165132718Skan 166132718Skan/* Read in the counts file, if available. */ 167132718Skan 168132718Skanstatic void 169132718Skanread_counts_file (void) 170132718Skan{ 171132718Skan gcov_unsigned_t fn_ident = 0; 172132718Skan gcov_unsigned_t checksum = -1; 173132718Skan counts_entry_t *summaried = NULL; 174132718Skan unsigned seen_summary = 0; 175132718Skan gcov_unsigned_t tag; 176132718Skan int is_error = 0; 177132718Skan 178132718Skan if (!gcov_open (da_file_name, 1)) 179132718Skan return; 180132718Skan 181132718Skan if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) 182132718Skan { 183169689Skan warning (0, "%qs is not a gcov data file", da_file_name); 184132718Skan gcov_close (); 185132718Skan return; 186132718Skan } 187132718Skan else if ((tag = gcov_read_unsigned ()) != GCOV_VERSION) 188132718Skan { 189132718Skan char v[4], e[4]; 190132718Skan 191132718Skan GCOV_UNSIGNED2STRING (v, tag); 192132718Skan GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 193132718Skan 194169689Skan warning (0, "%qs is version %q.*s, expected version %q.*s", 195161651Skan da_file_name, 4, v, 4, e); 196132718Skan gcov_close (); 197132718Skan return; 198132718Skan } 199132718Skan 200132718Skan /* Read and discard the stamp. */ 201132718Skan gcov_read_unsigned (); 202132718Skan 203132718Skan counts_hash = htab_create (10, 204132718Skan htab_counts_entry_hash, htab_counts_entry_eq, 205132718Skan htab_counts_entry_del); 206132718Skan while ((tag = gcov_read_unsigned ())) 207132718Skan { 208132718Skan gcov_unsigned_t length; 209132718Skan gcov_position_t offset; 210132718Skan 211132718Skan length = gcov_read_unsigned (); 212132718Skan offset = gcov_position (); 213132718Skan if (tag == GCOV_TAG_FUNCTION) 214132718Skan { 215132718Skan fn_ident = gcov_read_unsigned (); 216132718Skan checksum = gcov_read_unsigned (); 217132718Skan if (seen_summary) 218132718Skan { 219132718Skan /* We have already seen a summary, this means that this 220132718Skan new function begins a new set of program runs. We 221132718Skan must unlink the summaried chain. */ 222132718Skan counts_entry_t *entry, *chain; 223132718Skan 224132718Skan for (entry = summaried; entry; entry = chain) 225132718Skan { 226132718Skan chain = entry->chain; 227132718Skan entry->chain = NULL; 228132718Skan } 229132718Skan summaried = NULL; 230132718Skan seen_summary = 0; 231132718Skan } 232132718Skan } 233132718Skan else if (tag == GCOV_TAG_PROGRAM_SUMMARY) 234132718Skan { 235132718Skan counts_entry_t *entry; 236132718Skan struct gcov_summary summary; 237132718Skan 238132718Skan gcov_read_summary (&summary); 239132718Skan seen_summary = 1; 240132718Skan for (entry = summaried; entry; entry = entry->chain) 241132718Skan { 242132718Skan struct gcov_ctr_summary *csum = &summary.ctrs[entry->ctr]; 243132718Skan 244132718Skan entry->summary.runs += csum->runs; 245132718Skan entry->summary.sum_all += csum->sum_all; 246132718Skan if (entry->summary.run_max < csum->run_max) 247132718Skan entry->summary.run_max = csum->run_max; 248132718Skan entry->summary.sum_max += csum->sum_max; 249132718Skan } 250132718Skan } 251132718Skan else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident) 252132718Skan { 253132718Skan counts_entry_t **slot, *entry, elt; 254132718Skan unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); 255132718Skan unsigned ix; 256132718Skan 257132718Skan elt.ident = fn_ident; 258132718Skan elt.ctr = GCOV_COUNTER_FOR_TAG (tag); 259132718Skan 260132718Skan slot = (counts_entry_t **) htab_find_slot 261132718Skan (counts_hash, &elt, INSERT); 262132718Skan entry = *slot; 263132718Skan if (!entry) 264132718Skan { 265169689Skan *slot = entry = XCNEW (counts_entry_t); 266132718Skan entry->ident = elt.ident; 267132718Skan entry->ctr = elt.ctr; 268132718Skan entry->checksum = checksum; 269132718Skan entry->summary.num = n_counts; 270169689Skan entry->counts = XCNEWVEC (gcov_type, n_counts); 271132718Skan } 272132718Skan else if (entry->checksum != checksum) 273132718Skan { 274169689Skan error ("coverage mismatch for function %u while reading execution counters", 275132718Skan fn_ident); 276132718Skan error ("checksum is %x instead of %x", entry->checksum, checksum); 277132718Skan htab_delete (counts_hash); 278132718Skan break; 279132718Skan } 280132718Skan else if (entry->summary.num != n_counts) 281132718Skan { 282169689Skan error ("coverage mismatch for function %u while reading execution counters", 283132718Skan fn_ident); 284132718Skan error ("number of counters is %d instead of %d", entry->summary.num, n_counts); 285132718Skan htab_delete (counts_hash); 286132718Skan break; 287132718Skan } 288132718Skan else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE) 289132718Skan { 290132718Skan error ("cannot merge separate %s counters for function %u", 291132718Skan ctr_names[elt.ctr], fn_ident); 292132718Skan goto skip_merge; 293132718Skan } 294132718Skan 295132718Skan if (elt.ctr < GCOV_COUNTERS_SUMMABLE 296132718Skan /* This should always be true for a just allocated entry, 297132718Skan and always false for an existing one. Check this way, in 298132718Skan case the gcov file is corrupt. */ 299132718Skan && (!entry->chain || summaried != entry)) 300132718Skan { 301132718Skan entry->chain = summaried; 302132718Skan summaried = entry; 303132718Skan } 304132718Skan for (ix = 0; ix != n_counts; ix++) 305132718Skan entry->counts[ix] += gcov_read_counter (); 306132718Skan skip_merge:; 307132718Skan } 308132718Skan gcov_sync (offset, length); 309132718Skan if ((is_error = gcov_is_error ())) 310169689Skan { 311169689Skan error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted", 312169689Skan da_file_name); 313169689Skan htab_delete (counts_hash); 314169689Skan break; 315169689Skan } 316132718Skan } 317132718Skan 318132718Skan gcov_close (); 319132718Skan} 320132718Skan 321132718Skan/* Returns the counters for a particular tag. */ 322132718Skan 323132718Skangcov_type * 324132718Skanget_coverage_counts (unsigned counter, unsigned expected, 325132718Skan const struct gcov_ctr_summary **summary) 326132718Skan{ 327132718Skan counts_entry_t *entry, elt; 328132718Skan gcov_unsigned_t checksum = -1; 329132718Skan 330132718Skan /* No hash table, no counts. */ 331132718Skan if (!counts_hash) 332132718Skan { 333132718Skan static int warned = 0; 334132718Skan 335132718Skan if (!warned++) 336169689Skan inform ((flag_guess_branch_prob 337169689Skan ? "file %s not found, execution counts estimated" 338169689Skan : "file %s not found, execution counts assumed to be zero"), 339132718Skan da_file_name); 340132718Skan return NULL; 341132718Skan } 342132718Skan 343132718Skan elt.ident = current_function_funcdef_no + 1; 344132718Skan elt.ctr = counter; 345132718Skan entry = htab_find (counts_hash, &elt); 346132718Skan if (!entry) 347132718Skan { 348169689Skan warning (0, "no coverage for function %qs found", IDENTIFIER_POINTER 349132718Skan (DECL_ASSEMBLER_NAME (current_function_decl))); 350132718Skan return 0; 351132718Skan } 352132718Skan 353132718Skan checksum = compute_checksum (); 354132718Skan if (entry->checksum != checksum) 355132718Skan { 356169689Skan error ("coverage mismatch for function %qs while reading counter %qs", 357132718Skan IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)), 358132718Skan ctr_names[counter]); 359132718Skan error ("checksum is %x instead of %x", entry->checksum, checksum); 360132718Skan return 0; 361132718Skan } 362132718Skan else if (entry->summary.num != expected) 363132718Skan { 364169689Skan error ("coverage mismatch for function %qs while reading counter %qs", 365132718Skan IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)), 366132718Skan ctr_names[counter]); 367132718Skan error ("number of counters is %d instead of %d", entry->summary.num, expected); 368132718Skan return 0; 369132718Skan } 370132718Skan 371132718Skan if (summary) 372132718Skan *summary = &entry->summary; 373132718Skan 374132718Skan return entry->counts; 375132718Skan} 376132718Skan 377132718Skan/* Allocate NUM counters of type COUNTER. Returns nonzero if the 378132718Skan allocation succeeded. */ 379132718Skan 380132718Skanint 381132718Skancoverage_counter_alloc (unsigned counter, unsigned num) 382132718Skan{ 383132718Skan if (no_coverage) 384132718Skan return 0; 385132718Skan 386132718Skan if (!num) 387132718Skan return 1; 388132718Skan 389169689Skan if (!tree_ctr_tables[counter]) 390132718Skan { 391169689Skan /* Generate and save a copy of this so it can be shared. Leave 392169689Skan the index type unspecified for now; it will be set after all 393169689Skan functions have been compiled. */ 394132718Skan char buf[20]; 395169689Skan tree gcov_type_node = get_gcov_type (); 396169689Skan tree gcov_type_array_type 397169689Skan = build_array_type (gcov_type_node, NULL_TREE); 398169689Skan tree_ctr_tables[counter] 399169689Skan = build_decl (VAR_DECL, NULL_TREE, gcov_type_array_type); 400169689Skan TREE_STATIC (tree_ctr_tables[counter]) = 1; 401132718Skan ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1); 402169689Skan DECL_NAME (tree_ctr_tables[counter]) = get_identifier (buf); 403169689Skan DECL_ALIGN (tree_ctr_tables[counter]) = TYPE_ALIGN (gcov_type_node); 404132718Skan } 405132718Skan fn_b_ctrs[counter] = fn_n_ctrs[counter]; 406132718Skan fn_n_ctrs[counter] += num; 407132718Skan fn_ctr_mask |= 1 << counter; 408132718Skan return 1; 409132718Skan} 410132718Skan 411169689Skan/* Generate a tree to access COUNTER NO. */ 412132718Skan 413169689Skantree 414169689Skantree_coverage_counter_ref (unsigned counter, unsigned no) 415132718Skan{ 416169689Skan tree gcov_type_node = get_gcov_type (); 417132718Skan 418169689Skan gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]); 419132718Skan no += prg_n_ctrs[counter] + fn_b_ctrs[counter]; 420132718Skan 421169689Skan /* "no" here is an array index, scaled to bytes later. */ 422169689Skan return build4 (ARRAY_REF, gcov_type_node, tree_ctr_tables[counter], 423169689Skan build_int_cst (NULL_TREE, no), NULL, NULL); 424132718Skan} 425132718Skan 426132718Skan/* Generate a checksum for a string. CHKSUM is the current 427132718Skan checksum. */ 428132718Skan 429132718Skanstatic unsigned 430132718Skancoverage_checksum_string (unsigned chksum, const char *string) 431132718Skan{ 432132718Skan int i; 433132718Skan char *dup = NULL; 434132718Skan 435132718Skan /* Look for everything that looks if it were produced by 436132718Skan get_file_function_name_long and zero out the second part 437132718Skan that may result from flag_random_seed. This is not critical 438132718Skan as the checksums are used only for sanity checking. */ 439132718Skan for (i = 0; string[i]; i++) 440132718Skan { 441169689Skan int offset = 0; 442169689Skan if (!strncmp (string + i, "_GLOBAL__N_", 11)) 443169689Skan offset = 11; 444132718Skan if (!strncmp (string + i, "_GLOBAL__", 9)) 445169689Skan offset = 9; 446132718Skan 447169689Skan /* C++ namespaces do have scheme: 448169689Skan _GLOBAL__N_<filename>_<wrongmagicnumber>_<magicnumber>functionname 449169689Skan since filename might contain extra underscores there seems 450169689Skan to be no better chance then walk all possible offsets looking 451169689Skan for magicnuber. */ 452169689Skan if (offset) 453169689Skan { 454169689Skan for (i = i + offset; string[i]; i++) 455169689Skan if (string[i]=='_') 456169689Skan { 457169689Skan int y; 458169689Skan 459169689Skan for (y = 1; y < 9; y++) 460169689Skan if (!(string[i + y] >= '0' && string[i + y] <= '9') 461169689Skan && !(string[i + y] >= 'A' && string[i + y] <= 'F')) 462169689Skan break; 463169689Skan if (y != 9 || string[i + 9] != '_') 464169689Skan continue; 465169689Skan for (y = 10; y < 18; y++) 466169689Skan if (!(string[i + y] >= '0' && string[i + y] <= '9') 467169689Skan && !(string[i + y] >= 'A' && string[i + y] <= 'F')) 468169689Skan break; 469169689Skan if (y != 18) 470169689Skan continue; 471169689Skan if (!dup) 472169689Skan string = dup = xstrdup (string); 473169689Skan for (y = 10; y < 18; y++) 474169689Skan dup[i + y] = '0'; 475169689Skan } 476169689Skan break; 477169689Skan } 478132718Skan } 479132718Skan 480132718Skan chksum = crc32_string (chksum, string); 481132718Skan if (dup) 482132718Skan free (dup); 483132718Skan 484132718Skan return chksum; 485132718Skan} 486132718Skan 487132718Skan/* Compute checksum for the current function. We generate a CRC32. */ 488132718Skan 489132718Skanstatic unsigned 490132718Skancompute_checksum (void) 491132718Skan{ 492169689Skan expanded_location xloc 493169689Skan = expand_location (DECL_SOURCE_LOCATION (current_function_decl)); 494169689Skan unsigned chksum = xloc.line; 495132718Skan 496169689Skan chksum = coverage_checksum_string (chksum, xloc.file); 497132718Skan chksum = coverage_checksum_string 498132718Skan (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl))); 499132718Skan 500132718Skan return chksum; 501132718Skan} 502132718Skan 503132718Skan/* Begin output to the graph file for the current function. 504132718Skan Opens the output file, if not already done. Writes the 505132718Skan function header, if not already done. Returns nonzero if data 506132718Skan should be output. */ 507132718Skan 508132718Skanint 509132718Skancoverage_begin_output (void) 510132718Skan{ 511132718Skan if (no_coverage) 512132718Skan return 0; 513132718Skan 514132718Skan if (!bbg_function_announced) 515132718Skan { 516169689Skan expanded_location xloc 517169689Skan = expand_location (DECL_SOURCE_LOCATION (current_function_decl)); 518132718Skan unsigned long offset; 519132718Skan 520132718Skan if (!bbg_file_opened) 521132718Skan { 522132718Skan if (!gcov_open (bbg_file_name, -1)) 523132718Skan error ("cannot open %s", bbg_file_name); 524132718Skan else 525132718Skan { 526132718Skan gcov_write_unsigned (GCOV_NOTE_MAGIC); 527132718Skan gcov_write_unsigned (GCOV_VERSION); 528132718Skan gcov_write_unsigned (local_tick); 529132718Skan } 530132718Skan bbg_file_opened = 1; 531132718Skan } 532132718Skan 533132718Skan /* Announce function */ 534132718Skan offset = gcov_write_tag (GCOV_TAG_FUNCTION); 535132718Skan gcov_write_unsigned (current_function_funcdef_no + 1); 536132718Skan gcov_write_unsigned (compute_checksum ()); 537132718Skan gcov_write_string (IDENTIFIER_POINTER 538132718Skan (DECL_ASSEMBLER_NAME (current_function_decl))); 539169689Skan gcov_write_string (xloc.file); 540169689Skan gcov_write_unsigned (xloc.line); 541132718Skan gcov_write_length (offset); 542132718Skan 543132718Skan bbg_function_announced = 1; 544132718Skan } 545132718Skan return !gcov_is_error (); 546132718Skan} 547132718Skan 548132718Skan/* Finish coverage data for the current function. Verify no output 549132718Skan error has occurred. Save function coverage counts. */ 550132718Skan 551132718Skanvoid 552132718Skancoverage_end_function (void) 553132718Skan{ 554132718Skan unsigned i; 555132718Skan 556132718Skan if (bbg_file_opened > 1 && gcov_is_error ()) 557132718Skan { 558169689Skan warning (0, "error writing %qs", bbg_file_name); 559132718Skan bbg_file_opened = -1; 560132718Skan } 561132718Skan 562132718Skan if (fn_ctr_mask) 563132718Skan { 564132718Skan struct function_list *item; 565132718Skan 566169689Skan item = XNEW (struct function_list); 567132718Skan 568132718Skan *functions_tail = item; 569132718Skan functions_tail = &item->next; 570132718Skan 571132718Skan item->next = 0; 572132718Skan item->ident = current_function_funcdef_no + 1; 573132718Skan item->checksum = compute_checksum (); 574132718Skan for (i = 0; i != GCOV_COUNTERS; i++) 575132718Skan { 576132718Skan item->n_ctrs[i] = fn_n_ctrs[i]; 577132718Skan prg_n_ctrs[i] += fn_n_ctrs[i]; 578132718Skan fn_n_ctrs[i] = fn_b_ctrs[i] = 0; 579132718Skan } 580132718Skan prg_ctr_mask |= fn_ctr_mask; 581132718Skan fn_ctr_mask = 0; 582132718Skan } 583132718Skan bbg_function_announced = 0; 584132718Skan} 585132718Skan 586132718Skan/* Creates the gcov_fn_info RECORD_TYPE. */ 587132718Skan 588132718Skanstatic tree 589132718Skanbuild_fn_info_type (unsigned int counters) 590132718Skan{ 591169689Skan tree type = lang_hooks.types.make_type (RECORD_TYPE); 592132718Skan tree field, fields; 593132718Skan tree array_type; 594132718Skan 595132718Skan /* ident */ 596169689Skan fields = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); 597132718Skan 598132718Skan /* checksum */ 599169689Skan field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); 600132718Skan TREE_CHAIN (field) = fields; 601132718Skan fields = field; 602132718Skan 603169689Skan array_type = build_int_cst (NULL_TREE, counters - 1); 604169689Skan array_type = build_index_type (array_type); 605169689Skan array_type = build_array_type (get_gcov_unsigned_t (), array_type); 606132718Skan 607132718Skan /* counters */ 608132718Skan field = build_decl (FIELD_DECL, NULL_TREE, array_type); 609132718Skan TREE_CHAIN (field) = fields; 610132718Skan fields = field; 611132718Skan 612132718Skan finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE); 613132718Skan 614132718Skan return type; 615132718Skan} 616132718Skan 617132718Skan/* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is 618132718Skan the function being processed and TYPE is the gcov_fn_info 619132718Skan RECORD_TYPE. */ 620132718Skan 621132718Skanstatic tree 622132718Skanbuild_fn_info_value (const struct function_list *function, tree type) 623132718Skan{ 624132718Skan tree value = NULL_TREE; 625132718Skan tree fields = TYPE_FIELDS (type); 626132718Skan unsigned ix; 627132718Skan tree array_value = NULL_TREE; 628132718Skan 629132718Skan /* ident */ 630169689Skan value = tree_cons (fields, build_int_cstu (get_gcov_unsigned_t (), 631169689Skan function->ident), value); 632132718Skan fields = TREE_CHAIN (fields); 633132718Skan 634132718Skan /* checksum */ 635169689Skan value = tree_cons (fields, build_int_cstu (get_gcov_unsigned_t (), 636169689Skan function->checksum), value); 637132718Skan fields = TREE_CHAIN (fields); 638132718Skan 639132718Skan /* counters */ 640132718Skan for (ix = 0; ix != GCOV_COUNTERS; ix++) 641132718Skan if (prg_ctr_mask & (1 << ix)) 642132718Skan { 643169689Skan tree counters = build_int_cstu (get_gcov_unsigned_t (), 644169689Skan function->n_ctrs[ix]); 645132718Skan 646132718Skan array_value = tree_cons (NULL_TREE, counters, array_value); 647132718Skan } 648132718Skan 649169689Skan /* FIXME: use build_constructor directly. */ 650169689Skan array_value = build_constructor_from_list (TREE_TYPE (fields), 651169689Skan nreverse (array_value)); 652132718Skan value = tree_cons (fields, array_value, value); 653132718Skan 654169689Skan /* FIXME: use build_constructor directly. */ 655169689Skan value = build_constructor_from_list (type, nreverse (value)); 656132718Skan 657132718Skan return value; 658132718Skan} 659132718Skan 660132718Skan/* Creates the gcov_ctr_info RECORD_TYPE. */ 661132718Skan 662132718Skanstatic tree 663132718Skanbuild_ctr_info_type (void) 664132718Skan{ 665169689Skan tree type = lang_hooks.types.make_type (RECORD_TYPE); 666132718Skan tree field, fields = NULL_TREE; 667169689Skan tree gcov_ptr_type = build_pointer_type (get_gcov_type ()); 668132718Skan tree gcov_merge_fn_type; 669132718Skan 670132718Skan /* counters */ 671169689Skan field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); 672132718Skan TREE_CHAIN (field) = fields; 673132718Skan fields = field; 674132718Skan 675132718Skan /* values */ 676132718Skan field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type); 677132718Skan TREE_CHAIN (field) = fields; 678132718Skan fields = field; 679132718Skan 680132718Skan /* merge */ 681132718Skan gcov_merge_fn_type = 682132718Skan build_function_type_list (void_type_node, 683169689Skan gcov_ptr_type, get_gcov_unsigned_t (), 684132718Skan NULL_TREE); 685132718Skan field = build_decl (FIELD_DECL, NULL_TREE, 686132718Skan build_pointer_type (gcov_merge_fn_type)); 687132718Skan TREE_CHAIN (field) = fields; 688132718Skan fields = field; 689132718Skan 690132718Skan finish_builtin_struct (type, "__gcov_ctr_info", fields, NULL_TREE); 691132718Skan 692132718Skan return type; 693132718Skan} 694132718Skan 695132718Skan/* Creates a CONSTRUCTOR for a gcov_ctr_info. COUNTER is 696132718Skan the counter being processed and TYPE is the gcov_ctr_info 697132718Skan RECORD_TYPE. */ 698132718Skan 699132718Skanstatic tree 700132718Skanbuild_ctr_info_value (unsigned int counter, tree type) 701132718Skan{ 702132718Skan tree value = NULL_TREE; 703132718Skan tree fields = TYPE_FIELDS (type); 704132718Skan tree fn; 705132718Skan 706132718Skan /* counters */ 707132718Skan value = tree_cons (fields, 708169689Skan build_int_cstu (get_gcov_unsigned_t (), 709169689Skan prg_n_ctrs[counter]), 710132718Skan value); 711132718Skan fields = TREE_CHAIN (fields); 712132718Skan 713132718Skan if (prg_n_ctrs[counter]) 714132718Skan { 715169689Skan tree array_type; 716132718Skan 717169689Skan array_type = build_int_cstu (get_gcov_unsigned_t (), 718169689Skan prg_n_ctrs[counter] - 1); 719169689Skan array_type = build_index_type (array_type); 720132718Skan array_type = build_array_type (TREE_TYPE (TREE_TYPE (fields)), 721132718Skan array_type); 722132718Skan 723169689Skan TREE_TYPE (tree_ctr_tables[counter]) = array_type; 724169689Skan DECL_SIZE (tree_ctr_tables[counter]) = TYPE_SIZE (array_type); 725169689Skan DECL_SIZE_UNIT (tree_ctr_tables[counter]) = TYPE_SIZE_UNIT (array_type); 726169689Skan assemble_variable (tree_ctr_tables[counter], 0, 0, 0); 727132718Skan 728132718Skan value = tree_cons (fields, 729169689Skan build1 (ADDR_EXPR, TREE_TYPE (fields), 730169689Skan tree_ctr_tables[counter]), 731132718Skan value); 732132718Skan } 733132718Skan else 734132718Skan value = tree_cons (fields, null_pointer_node, value); 735132718Skan fields = TREE_CHAIN (fields); 736132718Skan 737132718Skan fn = build_decl (FUNCTION_DECL, 738132718Skan get_identifier (ctr_merge_functions[counter]), 739132718Skan TREE_TYPE (TREE_TYPE (fields))); 740132718Skan DECL_EXTERNAL (fn) = 1; 741132718Skan TREE_PUBLIC (fn) = 1; 742132718Skan DECL_ARTIFICIAL (fn) = 1; 743132718Skan TREE_NOTHROW (fn) = 1; 744132718Skan value = tree_cons (fields, 745132718Skan build1 (ADDR_EXPR, TREE_TYPE (fields), fn), 746132718Skan value); 747132718Skan 748169689Skan /* FIXME: use build_constructor directly. */ 749169689Skan value = build_constructor_from_list (type, nreverse (value)); 750132718Skan 751132718Skan return value; 752132718Skan} 753132718Skan 754132718Skan/* Creates the gcov_info RECORD_TYPE and initializer for it. Returns a 755132718Skan CONSTRUCTOR. */ 756132718Skan 757132718Skanstatic tree 758132718Skanbuild_gcov_info (void) 759132718Skan{ 760132718Skan unsigned n_ctr_types, ix; 761132718Skan tree type, const_type; 762132718Skan tree fn_info_type, fn_info_value = NULL_TREE; 763132718Skan tree fn_info_ptr_type; 764132718Skan tree ctr_info_type, ctr_info_ary_type, ctr_info_value = NULL_TREE; 765132718Skan tree field, fields = NULL_TREE; 766132718Skan tree value = NULL_TREE; 767132718Skan tree filename_string; 768132718Skan char *filename; 769132718Skan int filename_len; 770132718Skan unsigned n_fns; 771132718Skan const struct function_list *fn; 772132718Skan tree string_type; 773132718Skan 774132718Skan /* Count the number of active counters. */ 775132718Skan for (n_ctr_types = 0, ix = 0; ix != GCOV_COUNTERS; ix++) 776132718Skan if (prg_ctr_mask & (1 << ix)) 777132718Skan n_ctr_types++; 778132718Skan 779169689Skan type = lang_hooks.types.make_type (RECORD_TYPE); 780132718Skan const_type = build_qualified_type (type, TYPE_QUAL_CONST); 781132718Skan 782132718Skan /* Version ident */ 783169689Skan field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); 784132718Skan TREE_CHAIN (field) = fields; 785132718Skan fields = field; 786169689Skan value = tree_cons (field, build_int_cstu (TREE_TYPE (field), GCOV_VERSION), 787132718Skan value); 788132718Skan 789132718Skan /* next -- NULL */ 790132718Skan field = build_decl (FIELD_DECL, NULL_TREE, build_pointer_type (const_type)); 791132718Skan TREE_CHAIN (field) = fields; 792132718Skan fields = field; 793132718Skan value = tree_cons (field, null_pointer_node, value); 794132718Skan 795132718Skan /* stamp */ 796169689Skan field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); 797132718Skan TREE_CHAIN (field) = fields; 798132718Skan fields = field; 799169689Skan value = tree_cons (field, build_int_cstu (TREE_TYPE (field), local_tick), 800132718Skan value); 801132718Skan 802132718Skan /* Filename */ 803132718Skan string_type = build_pointer_type (build_qualified_type (char_type_node, 804132718Skan TYPE_QUAL_CONST)); 805132718Skan field = build_decl (FIELD_DECL, NULL_TREE, string_type); 806132718Skan TREE_CHAIN (field) = fields; 807132718Skan fields = field; 808132718Skan filename = getpwd (); 809132718Skan filename = (filename && da_file_name[0] != '/' 810132718Skan ? concat (filename, "/", da_file_name, NULL) 811132718Skan : da_file_name); 812132718Skan filename_len = strlen (filename); 813132718Skan filename_string = build_string (filename_len + 1, filename); 814132718Skan if (filename != da_file_name) 815132718Skan free (filename); 816169689Skan TREE_TYPE (filename_string) = build_array_type 817169689Skan (char_type_node, build_index_type 818169689Skan (build_int_cst (NULL_TREE, filename_len))); 819132718Skan value = tree_cons (field, build1 (ADDR_EXPR, string_type, filename_string), 820132718Skan value); 821132718Skan 822132718Skan /* Build the fn_info type and initializer. */ 823132718Skan fn_info_type = build_fn_info_type (n_ctr_types); 824132718Skan fn_info_ptr_type = build_pointer_type (build_qualified_type 825132718Skan (fn_info_type, TYPE_QUAL_CONST)); 826132718Skan for (fn = functions_head, n_fns = 0; fn; fn = fn->next, n_fns++) 827132718Skan fn_info_value = tree_cons (NULL_TREE, 828132718Skan build_fn_info_value (fn, fn_info_type), 829132718Skan fn_info_value); 830132718Skan if (n_fns) 831132718Skan { 832132718Skan tree array_type; 833132718Skan 834169689Skan array_type = build_index_type (build_int_cst (NULL_TREE, n_fns - 1)); 835132718Skan array_type = build_array_type (fn_info_type, array_type); 836132718Skan 837169689Skan /* FIXME: use build_constructor directly. */ 838169689Skan fn_info_value = build_constructor_from_list (array_type, 839169689Skan nreverse (fn_info_value)); 840132718Skan fn_info_value = build1 (ADDR_EXPR, fn_info_ptr_type, fn_info_value); 841132718Skan } 842132718Skan else 843132718Skan fn_info_value = null_pointer_node; 844132718Skan 845132718Skan /* number of functions */ 846169689Skan field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); 847132718Skan TREE_CHAIN (field) = fields; 848132718Skan fields = field; 849132718Skan value = tree_cons (field, 850169689Skan build_int_cstu (get_gcov_unsigned_t (), n_fns), 851132718Skan value); 852132718Skan 853132718Skan /* fn_info table */ 854132718Skan field = build_decl (FIELD_DECL, NULL_TREE, fn_info_ptr_type); 855132718Skan TREE_CHAIN (field) = fields; 856132718Skan fields = field; 857132718Skan value = tree_cons (field, fn_info_value, value); 858132718Skan 859132718Skan /* counter_mask */ 860169689Skan field = build_decl (FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); 861132718Skan TREE_CHAIN (field) = fields; 862132718Skan fields = field; 863132718Skan value = tree_cons (field, 864169689Skan build_int_cstu (get_gcov_unsigned_t (), prg_ctr_mask), 865132718Skan value); 866132718Skan 867132718Skan /* counters */ 868132718Skan ctr_info_type = build_ctr_info_type (); 869169689Skan ctr_info_ary_type = build_index_type (build_int_cst (NULL_TREE, 870169689Skan n_ctr_types)); 871132718Skan ctr_info_ary_type = build_array_type (ctr_info_type, ctr_info_ary_type); 872132718Skan for (ix = 0; ix != GCOV_COUNTERS; ix++) 873132718Skan if (prg_ctr_mask & (1 << ix)) 874132718Skan ctr_info_value = tree_cons (NULL_TREE, 875132718Skan build_ctr_info_value (ix, ctr_info_type), 876132718Skan ctr_info_value); 877169689Skan /* FIXME: use build_constructor directly. */ 878169689Skan ctr_info_value = build_constructor_from_list (ctr_info_ary_type, 879169689Skan nreverse (ctr_info_value)); 880132718Skan 881132718Skan field = build_decl (FIELD_DECL, NULL_TREE, ctr_info_ary_type); 882132718Skan TREE_CHAIN (field) = fields; 883132718Skan fields = field; 884132718Skan value = tree_cons (field, ctr_info_value, value); 885132718Skan 886132718Skan finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE); 887132718Skan 888169689Skan /* FIXME: use build_constructor directly. */ 889169689Skan value = build_constructor_from_list (type, nreverse (value)); 890132718Skan 891132718Skan return value; 892132718Skan} 893132718Skan 894132718Skan/* Write out the structure which libgcov uses to locate all the 895132718Skan counters. The structures used here must match those defined in 896132718Skan gcov-io.h. Write out the constructor to call __gcov_init. */ 897132718Skan 898132718Skanstatic void 899132718Skancreate_coverage (void) 900132718Skan{ 901169689Skan tree gcov_info, gcov_init, body, t; 902169689Skan char name_buf[32]; 903132718Skan 904132718Skan no_coverage = 1; /* Disable any further coverage. */ 905132718Skan 906132718Skan if (!prg_ctr_mask) 907132718Skan return; 908132718Skan 909169689Skan t = build_gcov_info (); 910132718Skan 911169689Skan gcov_info = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (t)); 912132718Skan TREE_STATIC (gcov_info) = 1; 913169689Skan ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0); 914169689Skan DECL_NAME (gcov_info) = get_identifier (name_buf); 915169689Skan DECL_INITIAL (gcov_info) = t; 916132718Skan 917132718Skan /* Build structure. */ 918132718Skan assemble_variable (gcov_info, 0, 0, 0); 919132718Skan 920169689Skan /* Build a decl for __gcov_init. */ 921169689Skan t = build_pointer_type (TREE_TYPE (gcov_info)); 922169689Skan t = build_function_type_list (void_type_node, t, NULL); 923169689Skan t = build_decl (FUNCTION_DECL, get_identifier ("__gcov_init"), t); 924169689Skan TREE_PUBLIC (t) = 1; 925169689Skan DECL_EXTERNAL (t) = 1; 926169689Skan gcov_init = t; 927132718Skan 928169689Skan /* Generate a call to __gcov_init(&gcov_info). */ 929169689Skan body = NULL; 930169689Skan t = build_fold_addr_expr (gcov_info); 931169689Skan t = tree_cons (NULL, t, NULL); 932169689Skan t = build_function_call_expr (gcov_init, t); 933169689Skan append_to_statement_list (t, &body); 934132718Skan 935169689Skan /* Generate a constructor to run it. */ 936169689Skan cgraph_build_static_cdtor ('I', body, DEFAULT_INIT_PRIORITY); 937132718Skan} 938132718Skan 939132718Skan/* Perform file-level initialization. Read in data file, generate name 940132718Skan of graph file. */ 941132718Skan 942132718Skanvoid 943132718Skancoverage_init (const char *filename) 944132718Skan{ 945132718Skan int len = strlen (filename); 946132718Skan 947132718Skan /* Name of da file. */ 948169689Skan da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX) + 1); 949132718Skan strcpy (da_file_name, filename); 950132718Skan strcat (da_file_name, GCOV_DATA_SUFFIX); 951132718Skan 952132718Skan /* Name of bbg file. */ 953169689Skan bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1); 954132718Skan strcpy (bbg_file_name, filename); 955132718Skan strcat (bbg_file_name, GCOV_NOTE_SUFFIX); 956132718Skan 957132718Skan read_counts_file (); 958132718Skan} 959132718Skan 960132718Skan/* Performs file-level cleanup. Close graph file, generate coverage 961132718Skan variables and constructor. */ 962132718Skan 963132718Skanvoid 964132718Skancoverage_finish (void) 965132718Skan{ 966132718Skan create_coverage (); 967132718Skan if (bbg_file_opened) 968132718Skan { 969132718Skan int error = gcov_close (); 970132718Skan 971132718Skan if (error) 972132718Skan unlink (bbg_file_name); 973132718Skan if (!local_tick) 974132718Skan /* Only remove the da file, if we cannot stamp it. If we can 975132718Skan stamp it, libgcov will DTRT. */ 976132718Skan unlink (da_file_name); 977132718Skan } 978132718Skan} 979132718Skan 980132718Skan#include "gt-coverage.h" 981