static int merge_one_data (const char *filename, struct gcov_info *gi_ptr, struct gcov_summary *prg_p, struct gcov_summary *this_prg, gcov_position_t *summary_pos_p, gcov_position_t *eof_pos_p, gcov_unsigned_t crc32) { gcov_unsigned_t tag, length; unsigned t_ix; int f_ix; int error = 0; struct gcov_fn_buffer **fn_tail = &fn_buffer; struct gcov_summary_buffer **sum_tail = &sum_buffer; length = gcov_read_unsigned (); if (!gcov_version (gi_ptr, length, filename)) return -1; length = gcov_read_unsigned (); if (length != gi_ptr->stamp) /* Read from a different compilation. Overwrite the file. */ return 0; /* Look for program summary. */ for (f_ix = 0;;) { struct gcov_summary tmp; *eof_pos_p = gcov_position (); tag = gcov_read_unsigned (); if (tag != GCOV_TAG_PROGRAM_SUMMARY) break; f_ix--; length = gcov_read_unsigned (); gcov_read_summary (&tmp); if ((error = gcov_is_error ())) goto read_error; if (*summary_pos_p) { /* Save all summaries after the one that will be merged into below. These will need to be rewritten as histogram merging may change the number of non-zero histogram entries that will be emitted, and thus the size of the merged summary. */ (*sum_tail) = (struct gcov_summary_buffer *) xmalloc (sizeof(struct gcov_summary_buffer)); (*sum_tail)->summary = tmp; (*sum_tail)->next = 0; sum_tail = &((*sum_tail)->next); goto next_summary; } if (tmp.checksum != crc32) goto next_summary; for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++) if (tmp.ctrs[t_ix].num != this_prg->ctrs[t_ix].num) goto next_summary; *prg_p = tmp; *summary_pos_p = *eof_pos_p; next_summary:; } /* Merge execution counts for each function. */ for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++, tag = gcov_read_unsigned ()) { const struct gcov_ctr_info *ci_ptr; const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; if (tag != GCOV_TAG_FUNCTION) goto read_mismatch; length = gcov_read_unsigned (); if (!length) /* This function did not appear in the other program. We have nothing to merge. */ continue; if (length != GCOV_TAG_FUNCTION_LENGTH) goto read_mismatch; if (!gfi_ptr || gfi_ptr->key != gi_ptr) { /* This function appears in the other program. We need to buffer the information in order to write it back out -- we'll be inserting data before this point, so cannot simply keep the data in the file. */ fn_tail = buffer_fn_data (filename, gi_ptr, fn_tail, f_ix); if (!fn_tail) goto read_mismatch; continue; } length = gcov_read_unsigned (); if (length != gfi_ptr->ident) goto read_mismatch; length = gcov_read_unsigned (); if (length != gfi_ptr->lineno_checksum) goto read_mismatch; length = gcov_read_unsigned (); if (length != gfi_ptr->cfg_checksum) goto read_mismatch; ci_ptr = gfi_ptr->ctrs; for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) { gcov_merge_fn merge = gi_ptr->merge[t_ix]; if (!merge) continue; tag = gcov_read_unsigned (); length = gcov_read_unsigned (); if (tag != GCOV_TAG_FOR_COUNTER (t_ix) || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)) goto read_mismatch; (*merge) (ci_ptr->values, ci_ptr->num); ci_ptr++; } if ((error = gcov_is_error ())) goto read_error; } if (tag) { read_mismatch:; gcov_error ("profiling:%s:Merge mismatch for %s %u\n", filename, f_ix >= 0 ? "function" : "summary", f_ix < 0 ? -1 - f_ix : f_ix); return -1; } return 0; read_error: gcov_error ("profiling:%s:%s merging\n", filename, error < 0 ? "Overflow": "Error"); return -1; }
static void gcov_exit (void) { struct gcov_info *gi_ptr; const struct gcov_fn_info *gfi_ptr; struct gcov_summary this_prg; /* summary for program. */ struct gcov_summary all_prg; /* summary for all instances of program. */ struct gcov_ctr_summary *cs_ptr; const struct gcov_ctr_info *ci_ptr; unsigned t_ix; int f_ix; gcov_unsigned_t c_num; const char *gcov_prefix; int gcov_prefix_strip = 0; size_t prefix_length; char *gi_filename, *gi_filename_up; gcov_unsigned_t crc32 = 0; memset (&all_prg, 0, sizeof (all_prg)); /* Find the totals for this execution. */ memset (&this_prg, 0, sizeof (this_prg)); for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) { crc32 = crc32_unsigned (crc32, gi_ptr->stamp); crc32 = crc32_unsigned (crc32, gi_ptr->n_functions); for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++) { gfi_ptr = gi_ptr->functions[f_ix]; if (gfi_ptr && gfi_ptr->key != gi_ptr) gfi_ptr = 0; crc32 = crc32_unsigned (crc32, gfi_ptr ? gfi_ptr->cfg_checksum : 0); crc32 = crc32_unsigned (crc32, gfi_ptr ? gfi_ptr->lineno_checksum : 0); if (!gfi_ptr) continue; ci_ptr = gfi_ptr->ctrs; for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++) { if (!gi_ptr->merge[t_ix]) continue; cs_ptr = &this_prg.ctrs[t_ix]; cs_ptr->num += ci_ptr->num; crc32 = crc32_unsigned (crc32, ci_ptr->num); for (c_num = 0; c_num < ci_ptr->num; c_num++) { cs_ptr->sum_all += ci_ptr->values[c_num]; if (cs_ptr->run_max < ci_ptr->values[c_num]) cs_ptr->run_max = ci_ptr->values[c_num]; } ci_ptr++; } } } { /* Check if the level of dirs to strip off specified. */ char *tmp = getenv("GCOV_PREFIX_STRIP"); if (tmp) { gcov_prefix_strip = atoi (tmp); /* Do not consider negative values. */ if (gcov_prefix_strip < 0) gcov_prefix_strip = 0; } } /* Get file name relocation prefix. Non-absolute values are ignored. */ gcov_prefix = getenv("GCOV_PREFIX"); if (gcov_prefix) { prefix_length = strlen(gcov_prefix); /* Remove an unnecessary trailing '/' */ if (IS_DIR_SEPARATOR (gcov_prefix[prefix_length - 1])) prefix_length--; } else prefix_length = 0; /* If no prefix was specified and a prefix stip, then we assume relative. */ if (gcov_prefix_strip != 0 && prefix_length == 0) { gcov_prefix = "."; prefix_length = 1; } /* Allocate and initialize the filename scratch space plus one. */ gi_filename = (char *) alloca (prefix_length + gcov_max_filename + 2 + GCOV_TARGET_SUFFIX_LENGTH); if (prefix_length) memcpy (gi_filename, gcov_prefix, prefix_length); gi_filename_up = gi_filename + prefix_length; /* Now merge each file. */ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) { unsigned n_counts; struct gcov_summary prg; /* summary for this object over all program. */ struct gcov_ctr_summary *cs_prg, *cs_tprg, *cs_all; int error = 0; gcov_unsigned_t tag, length; gcov_position_t summary_pos = 0; gcov_position_t eof_pos = 0; const char *fname, *s; struct gcov_fn_buffer *fn_buffer = 0; struct gcov_fn_buffer **fn_tail = &fn_buffer; fname = gi_ptr->filename; /* Avoid to add multiple drive letters into combined path. */ if (prefix_length != 0 && HAS_DRIVE_SPEC(fname)) fname += 2; /* Build relocated filename, stripping off leading directories from the initial filename if requested. */ if (gcov_prefix_strip > 0) { int level = 0; s = fname; if (IS_DIR_SEPARATOR(*s)) ++s; /* Skip selected directory levels. */ for (; (*s != '\0') && (level < gcov_prefix_strip); s++) if (IS_DIR_SEPARATOR(*s)) { fname = s; level++; } } /* Update complete filename with stripped original. */ if (prefix_length != 0 && !IS_DIR_SEPARATOR (*fname)) { /* If prefix is given, add directory separator. */ strcpy (gi_filename_up, "/"); strcpy (gi_filename_up + 1, fname); } else strcpy (gi_filename_up, fname); #ifdef ADD_GCOV_TARGET_SUFFIX /* Give a chance to target to change the name. */ ADD_GCOV_TARGET_SUFFIX (gi_filename_up); #endif if (!gcov_open (gi_filename)) { /* Open failed likely due to missed directory. Create directory and retry to open file. */ if (create_file_directory (gi_filename)) { fprintf (stderr, "profiling:%s:Skip\n", gi_filename); continue; } if (!gcov_open (gi_filename)) { fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename); continue; } } tag = gcov_read_unsigned (); if (tag) { /* Merge data from file. */ if (tag != GCOV_DATA_MAGIC) { fprintf (stderr, "profiling:%s:Not a gcov data file\n", gi_filename); goto read_fatal; } length = gcov_read_unsigned (); if (!gcov_version (gi_ptr, length, gi_filename)) goto read_fatal; length = gcov_read_unsigned (); if (length != gi_ptr->stamp) /* Read from a different compilation. Overwrite the file. */ goto rewrite; /* Look for program summary. */ for (f_ix = 0;;) { struct gcov_summary tmp; eof_pos = gcov_position (); tag = gcov_read_unsigned (); if (tag != GCOV_TAG_PROGRAM_SUMMARY) break; f_ix--; length = gcov_read_unsigned (); if (length != GCOV_TAG_SUMMARY_LENGTH) goto read_mismatch; gcov_read_summary (&tmp); if ((error = gcov_is_error ())) goto read_error; if (summary_pos || tmp.checksum != crc32) goto next_summary; for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++) if (tmp.ctrs[t_ix].num != this_prg.ctrs[t_ix].num) goto next_summary; prg = tmp; summary_pos = eof_pos; next_summary:; } /* Merge execution counts for each function. */ for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++, tag = gcov_read_unsigned ()) { gfi_ptr = gi_ptr->functions[f_ix]; if (tag != GCOV_TAG_FUNCTION) goto read_mismatch; length = gcov_read_unsigned (); if (!length) /* This function did not appear in the other program. We have nothing to merge. */ continue; if (length != GCOV_TAG_FUNCTION_LENGTH) goto read_mismatch; if (!gfi_ptr || gfi_ptr->key != gi_ptr) { /* This function appears in the other program. We need to buffer the information in order to write it back out -- we'll be inserting data before this point, so cannot simply keep the data in the file. */ fn_tail = buffer_fn_data (gi_filename, gi_ptr, fn_tail, f_ix); if (!fn_tail) goto read_mismatch; continue; } length = gcov_read_unsigned (); if (length != gfi_ptr->ident) goto read_mismatch; length = gcov_read_unsigned (); if (length != gfi_ptr->lineno_checksum) goto read_mismatch; length = gcov_read_unsigned (); if (length != gfi_ptr->cfg_checksum) goto read_mismatch; ci_ptr = gfi_ptr->ctrs; for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) { gcov_merge_fn merge = gi_ptr->merge[t_ix]; if (!merge) continue; tag = gcov_read_unsigned (); length = gcov_read_unsigned (); if (tag != GCOV_TAG_FOR_COUNTER (t_ix) || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)) goto read_mismatch; (*merge) (ci_ptr->values, ci_ptr->num); ci_ptr++; } if ((error = gcov_is_error ())) goto read_error; } if (tag) { read_mismatch:; fprintf (stderr, "profiling:%s:Merge mismatch for %s %u\n", gi_filename, f_ix >= 0 ? "function" : "summary", f_ix < 0 ? -1 - f_ix : f_ix); goto read_fatal; } } goto rewrite; read_error:; fprintf (stderr, "profiling:%s:%s merging\n", gi_filename, error < 0 ? "Overflow": "Error"); goto read_fatal; rewrite:; gcov_rewrite (); if (!summary_pos) { memset (&prg, 0, sizeof (prg)); summary_pos = eof_pos; } /* Merge the summaries. */ for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++) { cs_prg = &prg.ctrs[t_ix]; cs_tprg = &this_prg.ctrs[t_ix]; cs_all = &all_prg.ctrs[t_ix]; if (gi_ptr->merge[t_ix]) { if (!cs_prg->runs++) cs_prg->num = cs_tprg->num; cs_prg->sum_all += cs_tprg->sum_all; if (cs_prg->run_max < cs_tprg->run_max) cs_prg->run_max = cs_tprg->run_max; cs_prg->sum_max += cs_tprg->run_max; } else if (cs_prg->runs) goto read_mismatch; if (!cs_all->runs && cs_prg->runs) memcpy (cs_all, cs_prg, sizeof (*cs_all)); else if (!all_prg.checksum && (!GCOV_LOCKED || cs_all->runs == cs_prg->runs) && memcmp (cs_all, cs_prg, sizeof (*cs_all))) { fprintf (stderr, "profiling:%s:Invocation mismatch - some data files may have been removed%s\n", gi_filename, GCOV_LOCKED ? "" : " or concurrently updated without locking support"); all_prg.checksum = ~0u; } } prg.checksum = crc32; /* Write out the data. */ if (!eof_pos) { gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION); gcov_write_unsigned (gi_ptr->stamp); } if (summary_pos) gcov_seek (summary_pos); /* Generate whole program statistics. */ gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &prg); if (summary_pos < eof_pos) gcov_seek (eof_pos); /* Write execution counts for each function. */ for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++) { unsigned buffered = 0; if (fn_buffer && fn_buffer->fn_ix == (unsigned)f_ix) { /* Buffered data from another program. */ buffered = 1; gfi_ptr = &fn_buffer->info; length = GCOV_TAG_FUNCTION_LENGTH; } else { gfi_ptr = gi_ptr->functions[f_ix]; if (gfi_ptr && gfi_ptr->key == gi_ptr) length = GCOV_TAG_FUNCTION_LENGTH; else length = 0; } gcov_write_tag_length (GCOV_TAG_FUNCTION, length); if (!length) continue; gcov_write_unsigned (gfi_ptr->ident); gcov_write_unsigned (gfi_ptr->lineno_checksum); gcov_write_unsigned (gfi_ptr->cfg_checksum); ci_ptr = gfi_ptr->ctrs; for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) { if (!gi_ptr->merge[t_ix]) continue; n_counts = ci_ptr->num; gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix), GCOV_TAG_COUNTER_LENGTH (n_counts)); gcov_type *c_ptr = ci_ptr->values; while (n_counts--) gcov_write_counter (*c_ptr++); ci_ptr++; } if (buffered) fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); } gcov_write_unsigned (0); read_fatal:; while (fn_buffer) fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); if ((error = gcov_close ())) fprintf (stderr, error < 0 ? "profiling:%s:Overflow writing\n" : "profiling:%s:Error writing\n", gi_filename); } }