2013-11-18 Rong Xu * gcc/gcov-io.c (gcov_var): Moved from gcov-io.h and make it static. (gcov_position): Move from gcov-io.h (gcov_is_error): Ditto. (gcov_rewrite): Ditto. * gcc/gcov-io.h: Re-factoring. Move gcov_var to gcov-io.h and move the libgcov only part of libgcc/libgcov.h. * libgcc/libgcov.h: New common header files for libgcov-*.h * libgcc/Makefile.in: Add dependence to libgcov.h * libgcc/libgcov-profiler.c: Use libgcov.h * libgcc/libgcov-driver.c: Ditto. * libgcc/libgcov-interface.c: Ditto. * libgcc/libgcov-driver-system.c (allocate_filename_struct): use xmalloc instread of malloc. * libgcc/libgcov-merge.c (void __gcov_merge_delta): Add more parameters to merge function. (__gcov_merge_add): Ditto. (__gcov_merge_ior): Ditto. (__gcov_merge_time_profile): Ditto. (__gcov_merge_single): Ditto. (__gcov_merge_delta): Ditto. * libgcc/libgcov-tool.c (void gcov_tool_set_verbose): New for gcov-tool support. (set_fn_ctrs): Ditto. (tag_function): Ditto. (tag_blocks): Ditto. (tag_arcs): Ditto. (tag_lines): Ditto. (tag_counters): Ditto. (tag_summary): Ditto. (read_gcda_finalize): Ditto. (read_gcda_file): Ditto. (ftw_read_file): Ditto. (read_profile_dir_init) Ditto.: (gcov_read_profile_dir): Ditto. (gcov_merge): Ditto. (find_match_gcov_inf Ditto.o): (gcov_profile_merge): Ditto. (__gcov_scale_add): Ditto. (__gcov_scale_ior): Ditto. (__gcov_scale_delta): Ditto. (__gcov_scale_single): Ditto. (gcov_profile_scale): Ditto. (gcov_profile_normalize): Ditto. (__gcov_scale2_add): Ditto. (__gcov_scale2_ior): Ditto. (__gcov_scale2_delta): Ditto. (__gcov_scale2_single): Ditto. (gcov_profile_scale2): Ditto. * gcc/gcov-tool.c (unlink_file): Gcov-tool driver support. (unlink_dir): Ditto. (profile_merge): Ditto. (print_merge_usage_message): Ditto. (merge_usage): Ditto. (do_merge): Ditto. (profile_rewrite2): Ditto. (profile_rewrite): Ditto. (print_rewrite_usage_message): Ditto. (rewrite_usage): Ditto. (do_rewrite): Ditto. (print_usage): Ditto. (print_version): Ditto. (process_args): Ditto. (main): Ditto. * gcc/Makefile.in: Build and install gcov-tool. Index: gcc/gcov-tool.c =================================================================== --- gcc/gcov-tool.c (revision 0) +++ gcc/gcov-tool.c (revision 0) @@ -0,0 +1,462 @@ +/* GCC gcov-tool support. + Contributed by Rong Xu . + Copyright (C) 2013 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "gcov-io.h" +#include +#include +#include +#include +#include + +extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int); +extern int gcov_profile_normalize (struct gcov_info*, gcov_type); +extern int gcov_profile_scale (struct gcov_info*, float); +extern int gcov_profile_scale2 (struct gcov_info*, int, int); +extern struct gcov_info* gcov_read_profile_dir (const char*, int); +extern void gcov_exit (void); +extern void set_gcov_list (struct gcov_info *); +extern void gcov_tool_set_verbose (void); + + +static int verbose; + +static int +unlink_file (const char *name, + const struct stat *status ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED, + struct FTW *ftwbuf ATTRIBUTE_UNUSED) +{ + int ret = 0; + int len = strlen (name); + int len1 = strlen (GCOV_DATA_SUFFIX); + + if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1)) + remove (name); + + if (ret) + { + fnotice (stderr, "error in removing %s\n", name); + exit (FATAL_EXIT_CODE); + } + + return ret; +} + +static int +unlink_dir (const char *path) +{ + return nftw(path, unlink_file, 64, FTW_DEPTH | FTW_PHYS); +} + +static int +profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + struct gcov_info * d2_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + if (d2) + { + d2_profile = gcov_read_profile_dir (d2, 0); + if (!d2_profile) + return 1; + + /* the actual merge: we overwrite to d1_profile. */ + ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2); + + if (ret) + return ret; + } + + /* output */ + unlink_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +static void +print_merge_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " merge [options] Merge coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output Output directory\n"); + fnotice (file, " -w, --weight Set weights (float point values)\n"); +} + +static const struct option merge_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "weight", required_argument, NULL, 'w' }, + { 0, 0, 0, 0 } +}; + +static void +merge_usage (void) +{ + fnotice (stderr, "Merge subcomand usage:"); + print_merge_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +static int +do_merge (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + int w1 = 1, w2 = 1; + + while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_tool_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'w': + sscanf (optarg, "%d,%d", &w1, &w2); + if (w1 < 0 || w2 < 0) + { + fnotice (stderr, "weights need to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + break; + default: + merge_usage (); + } + } + + if (output_dir == NULL) + output_dir = "merged_profile"; + + if (argc - optind == 2) + ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2); + else + merge_usage (); + + return ret; +} + +static int +profile_rewrite2 (const char *d1, const char *out, int n, int d) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + /* output profile */ + unlink_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + gcov_profile_scale2 (d1_profile, n, d); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +static int +profile_rewrite (const char *d1, const char *out, long long n_val, float scale) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + /* output profile */ + unlink_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + if (n_val) + gcov_profile_normalize (d1_profile, (gcov_type) n_val); + else + gcov_profile_scale (d1_profile, scale); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +static void +print_rewrite_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " rewrite [options] Rewrite coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output Output directory\n"); + fnotice (file, " -s, --scale Scale the profile counters\n"); + fnotice (file, " -n, --normalize Normalize the profile\n"); +} + +static const struct option rewrite_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "scale", required_argument, NULL, 's' }, + { "normalize", required_argument, NULL, 'n' }, + { 0, 0, 0, 0 } +}; + +static void +rewrite_usage (void) +{ + fnotice (stderr, "Rewrite subcommand usage:"); + print_rewrite_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +static int +do_rewrite (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + long long normalize_val = 0; + float scale = 1.0; + int numerator = -1; + int denominator = -1; + + while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_tool_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'n': + if (scale != 1.0) + { + fnotice (stderr, "scaling cannot co-exist with normalization\n"); + scale = 1.0; + } + normalize_val = atoll (optarg); + break; + case 's': + ret = 0; + if (strstr (optarg, "/")) + { + ret = sscanf (optarg, "%d/%d", &numerator, &denominator); + if (ret == 2) + { + gcc_assert (numerator >= 0); + gcc_assert (denominator > 0); + scale = 0.0; + } + } + if (ret != 2) + { + ret = sscanf (optarg, "%f", &scale); + gcc_assert (ret == 1); + } + + if (scale < 0.0) + { + fnotice (stderr, "scale needs to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + if (normalize_val != 0) + { + fnotice (stderr, "normalization cannot co-exist with scaling\n"); + normalize_val = 0; + } + break; + default: + rewrite_usage (); + } + } + + if (output_dir == NULL) + output_dir = "rewrite_profile"; + + if (argc - optind == 1) + { + if (denominator > 0) + ret = profile_rewrite2 (argv[optind], output_dir, numerator, denominator); + else + ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale); + } + else + rewrite_usage (); + + return ret; +} + +/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, + otherwise the output of --help. */ + +static void +print_usage (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; + + fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname); + fnotice (file, "Offline tool to handle gcda counts.\n\n"); + fnotice (file, " -h, --help Print this help, then exit\n"); + fnotice (file, " -v, --version Print version number, then exit\n"); + print_merge_usage_message (error_p); + print_rewrite_usage_message (error_p); + fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", + bug_report_url); + exit (status); +} + +/* Print version information and exit. */ + +static void +print_version (void) +{ + fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); + fprintf (stdout, "Copyright %s 2013 Free Software Foundation, Inc.\n", + _("(C)")); + fnotice (stdout, + _("This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or \n" + "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); + exit (SUCCESS_EXIT_CODE); +} + +static const struct option options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } +}; + +/* Process args, return index to first non-arg. */ + +static int +process_args (int argc, char **argv) +{ + int opt; + + while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1) + { + switch (opt) + { + case 'h': + print_usage (false); + /* print_usage will exit. */ + case 'v': + print_version (); + /* print_version will exit. */ + default: + print_usage (true); + /* print_usage will exit. */ + } + } + + return optind; +} + +int +main (int argc, char **argv) +{ + const char *p; + const char *sub_command; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) + --p; + progname = p; + + xmalloc_set_program_name (progname); + + /* Unlock the stdio streams. */ + unlock_std_streams (); + + gcc_init_libintl (); + + diagnostic_initialize (global_dc, 0); + + /* Handle response files. */ + expandargv (&argc, &argv); + + process_args (argc, argv); + if (optind >= argc) + print_usage (true); + + sub_command = argv[optind]; + + if (!strcmp (sub_command, "merge")) + return do_merge (argc - optind, argv + optind); + else if (!strcmp (sub_command, "rewrite")) + return do_rewrite (argc - optind, argv + optind); + + print_usage (true); +} Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 204895) +++ gcc/Makefile.in (working copy) @@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build # Selection of languages to be made. CONFIG_LANGUAGES = @all_selected_languages@ -LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES) +LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) gcov-tool$(exeext) \ + $(CONFIG_LANGUAGES) # Default values for variables overridden in Makefile fragments. # CFLAGS is for the user to override to, e.g., do a cross build with -O2. @@ -196,6 +197,7 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $( # flex output may yield harmless "no previous prototype" warnings build/gengtype-lex.o-warn = -Wno-error gengtype-lex.o-warn = -Wno-error +libgcov-tool.o-warn = -Wno-error # All warnings have to be shut off in stage1 if the compiler used then # isn't gcc; configure determines that. WARN_CFLAGS will be either @@ -1486,7 +1488,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \ $(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \ $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \ - lto-wrapper.o + $(GCOV_TOOL_OBJS) libgcov-tool.o lto-wrapper.o # This lists all host object files, whether they are included in this # compilation or not. @@ -1511,6 +1513,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn $(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \ gcc-ranlib$(exeext) \ gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \ + gcov-tool$(exeect) \ gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \ libcommon-target.a libcommon.a libgcc.mk @@ -2566,6 +2569,16 @@ GCOV_DUMP_OBJS = gcov-dump.o gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \ $(LIBS) -o $@ +libgcov-tool.o: $(srcdir)/../libgcc/libgcov-tool.c gcov-io.c $(GCOV_IO_H) \ + $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \ + $(srcdir)/../libgcc/libgcov-merge.c \ + $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $< + +GCOV_TOOL_OBJS = gcov-tool.o libgcov-tool.o +gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS) + +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_TOOL_OBJS) \ + $(LIBS) -o $@ # # Build the include directories. The stamp files are stmp-* rather than # s-* so that mostlyclean does not force the include directory to @@ -3189,6 +3202,13 @@ install-common: native lang.install-common install rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ fi +# Install gcov-tool if it was compiled. + -if [ -f gcov-tool$(exeext) ]; \ + then \ + rm -f $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + $(INSTALL_PROGRAM) \ + gcov-tool$(exeext) $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + fi # Install the driver program as $(target_noncanonical)-gcc, # $(target_noncanonical)-gcc-$(version), and also as gcc if native. Index: gcc/gcov-io.c =================================================================== --- gcc/gcov-io.c (revision 204895) +++ gcc/gcov-io.c (working copy) @@ -36,6 +36,37 @@ static const gcov_unsigned_t *gcov_read_words (uns static void gcov_allocate (unsigned); #endif +/* Moved for gcov-io.h and make it static. */ +static struct gcov_var gcov_var; + +/* Save the current position in the gcov file. */ +static inline gcov_position_t +gcov_position (void) +{ + gcc_assert (gcov_var.mode > 0); + return gcov_var.start + gcov_var.offset; +} + +/* Return nonzero if the error flag is set. */ +static inline int +gcov_is_error (void) +{ + return gcov_var.file ? gcov_var.error : 1; +} + +#if IN_LIBGCOV +/* Move to beginning of file and initialize for writing. */ +static inline void +gcov_rewrite (void) +{ + gcc_assert (gcov_var.mode > 0); + gcov_var.mode = -1; + gcov_var.start = 0; + gcov_var.offset = 0; + fseek (gcov_var.file, 0L, SEEK_SET); +} +#endif + static inline gcov_unsigned_t from_file (gcov_unsigned_t value) { #if !IN_LIBGCOV @@ -503,7 +534,7 @@ gcov_read_counter (void) buffer, or NULL on empty string. You must copy the string before calling another gcov function. */ -#if !IN_LIBGCOV +#if !IN_LIBGCOV || IN_GCOV_TOOL GCOV_LINKAGE const char * gcov_read_string (void) { @@ -580,7 +611,7 @@ gcov_read_summary (struct gcov_summary *summary) } } -#if !IN_LIBGCOV +#if !IN_LIBGCOV || IN_GCOV_TOOL /* Reset to a known position. BASE should have been obtained from gcov_position, LENGTH should be a record length. */ Index: gcc/gcov-io.h =================================================================== --- gcc/gcov-io.h (revision 204895) +++ gcc/gcov-io.h (working copy) @@ -164,51 +164,7 @@ see the files COPYING3 and COPYING.RUNTIME respect #ifndef GCC_GCOV_IO_H #define GCC_GCOV_IO_H -#if IN_LIBGCOV -/* About the target */ - -#if BITS_PER_UNIT == 8 -typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI))); -typedef unsigned gcov_position_t __attribute__ ((mode (SI))); -#if LONG_LONG_TYPE_SIZE > 32 -typedef signed gcov_type __attribute__ ((mode (DI))); -typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI))); -#else -typedef signed gcov_type __attribute__ ((mode (SI))); -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); -#endif -#else -#if BITS_PER_UNIT == 16 -typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI))); -typedef unsigned gcov_position_t __attribute__ ((mode (HI))); -#if LONG_LONG_TYPE_SIZE > 32 -typedef signed gcov_type __attribute__ ((mode (SI))); -typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); -#else -typedef signed gcov_type __attribute__ ((mode (HI))); -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); -#endif -#else -typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI))); -typedef unsigned gcov_position_t __attribute__ ((mode (QI))); -#if LONG_LONG_TYPE_SIZE > 32 -typedef signed gcov_type __attribute__ ((mode (HI))); -typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); -#else -typedef signed gcov_type __attribute__ ((mode (QI))); -typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI))); -#endif -#endif -#endif - - -#if defined (TARGET_POSIX_IO) -#define GCOV_LOCKED 1 -#else -#define GCOV_LOCKED 0 -#endif - -#else /* !IN_LIBGCOV */ +#ifndef IN_LIBGCOV /* About the host */ typedef unsigned gcov_unsigned_t; @@ -231,48 +187,10 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne #define GCOV_LOCKED 0 #endif -#endif /* !IN_LIBGCOV */ - -/* In gcov we want function linkage to be static. In the compiler we want - it extern, so that they can be accessed from elsewhere. In libgcov we - need these functions to be extern, so prefix them with __gcov. In - libgcov they must also be hidden so that the instance in the executable - is not also used in a DSO. */ -#if IN_LIBGCOV - -#include "tconfig.h" - -#define gcov_var __gcov_var -#define gcov_open __gcov_open -#define gcov_close __gcov_close -#define gcov_write_tag_length __gcov_write_tag_length -#define gcov_position __gcov_position -#define gcov_seek __gcov_seek -#define gcov_rewrite __gcov_rewrite -#define gcov_is_error __gcov_is_error -#define gcov_write_unsigned __gcov_write_unsigned -#define gcov_write_counter __gcov_write_counter -#define gcov_write_summary __gcov_write_summary -#define gcov_read_unsigned __gcov_read_unsigned -#define gcov_read_counter __gcov_read_counter -#define gcov_read_summary __gcov_read_summary - -/* Poison these, so they don't accidentally slip in. */ -#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length -#pragma GCC poison gcov_read_string gcov_sync gcov_time gcov_magic - -#ifdef HAVE_GAS_HIDDEN -#define ATTRIBUTE_HIDDEN __attribute__ ((__visibility__ ("hidden"))) -#else #define ATTRIBUTE_HIDDEN -#endif -#else +#endif /* !IN_LIBGOCV */ -#define ATTRIBUTE_HIDDEN - -#endif - #ifndef GCOV_LINKAGE #define GCOV_LINKAGE extern #endif @@ -342,10 +260,11 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne counter. */ #define GCOV_COUNTER_IOR 7 /* IOR of the all values passed to counter. */ -#define GCOV_TIME_PROFILER 8 /* Time profile collecting first run of a function */ +#define GCOV_TIME_PROFILER 8 /* Time profile collecting first run of a + function */ #define GCOV_LAST_VALUE_COUNTER 8 /* The last of counters used for value - profiling. */ -#define GCOV_COUNTERS 9 + profiling. */ +#define GCOV_COUNTERS 9 /* Number of counters used for value profiling. */ #define GCOV_N_VALUE_COUNTERS \ @@ -353,7 +272,8 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne /* A list of human readable names of the counters */ #define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \ - "delta", "indirect_call", "average", "ior", "time_profiler"} + "delta", "indirect_call", "average", "ior", \ + "time_profiler"} /* Names of merge functions for counters. */ #define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \ @@ -363,8 +283,8 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne "__gcov_merge_delta", \ "__gcov_merge_single", \ "__gcov_merge_add", \ - "__gcov_merge_ior", \ - "__gcov_merge_time_profile" } + "__gcov_merge_ior", \ + "__gcov_merge_time_profile"} /* Convert a counter index to a tag. */ #define GCOV_TAG_FOR_COUNTER(COUNT) \ @@ -442,110 +362,12 @@ struct gcov_summary struct gcov_ctr_summary ctrs[GCOV_COUNTERS_SUMMABLE]; }; -/* Structures embedded in coveraged program. The structures generated - by write_profile must match these. */ +#if !defined(inhibit_libc) -#if IN_LIBGCOV -/* Information about counters for a single function. */ -struct gcov_ctr_info -{ - gcov_unsigned_t num; /* number of counters. */ - gcov_type *values; /* their values. */ -}; - -/* Information about a single function. This uses the trailing array - idiom. The number of counters is determined from the merge pointer - array in gcov_info. The key is used to detect which of a set of - comdat functions was selected -- it points to the gcov_info object - of the object file containing the selected comdat function. */ - -struct gcov_fn_info -{ - const struct gcov_info *key; /* comdat key */ - gcov_unsigned_t ident; /* unique ident of function */ - gcov_unsigned_t lineno_checksum; /* function lineo_checksum */ - gcov_unsigned_t cfg_checksum; /* function cfg checksum */ - struct gcov_ctr_info ctrs[0]; /* instrumented counters */ -}; - -/* Type of function used to merge counters. */ -typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t); - -/* Information about a single object file. */ -struct gcov_info -{ - gcov_unsigned_t version; /* expected version number */ - struct gcov_info *next; /* link to next, used by libgcov */ - - gcov_unsigned_t stamp; /* uniquifying time stamp */ - const char *filename; /* output file name */ - - gcov_merge_fn merge[GCOV_COUNTERS]; /* merge functions (null for - unused) */ - - unsigned n_functions; /* number of functions */ - const struct gcov_fn_info *const *functions; /* pointer to pointers - to function information */ -}; - -/* Register a new object file module. */ -extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN; - -/* Called before fork, to avoid double counting. */ -extern void __gcov_flush (void) ATTRIBUTE_HIDDEN; - -/* Function to reset all counters to 0. */ -extern void __gcov_reset (void); - -/* Function to enable early write of profile information so far. */ -extern void __gcov_dump (void); - -/* The merge function that just sums the counters. */ -extern void __gcov_merge_add (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; - -/* The merge function to choose the most common value. */ -extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; - -/* The merge function to choose the most common difference between - consecutive values. */ -extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; - -/* The merge function that just ors the counters together. */ -extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; - -extern void __gcov_merge_time_profile (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; - -/* The profiler functions. */ -extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned); -extern void __gcov_pow2_profiler (gcov_type *, gcov_type); -extern void __gcov_one_value_profiler (gcov_type *, gcov_type); -extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type, - void*, void*); -extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *); -extern void __gcov_average_profiler (gcov_type *, gcov_type); -extern void __gcov_ior_profiler (gcov_type *, gcov_type); -extern void __gcov_time_profiler (gcov_type *); - -#ifndef inhibit_libc -/* The wrappers around some library functions.. */ -extern pid_t __gcov_fork (void) ATTRIBUTE_HIDDEN; -extern int __gcov_execl (const char *, char *, ...) ATTRIBUTE_HIDDEN; -extern int __gcov_execlp (const char *, char *, ...) ATTRIBUTE_HIDDEN; -extern int __gcov_execle (const char *, char *, ...) ATTRIBUTE_HIDDEN; -extern int __gcov_execv (const char *, char *const []) ATTRIBUTE_HIDDEN; -extern int __gcov_execvp (const char *, char *const []) ATTRIBUTE_HIDDEN; -extern int __gcov_execve (const char *, char *const [], char *const []) - ATTRIBUTE_HIDDEN; -#endif - -#endif /* IN_LIBGCOV */ - -#if IN_LIBGCOV >= 0 - /* Optimum number of gcov_unsigned_t's read from or written to disk. */ #define GCOV_BLOCK_SIZE (1 << 10) -GCOV_LINKAGE struct gcov_var +struct gcov_var { FILE *file; gcov_position_t start; /* Position of first byte of block */ @@ -567,7 +389,7 @@ struct gcov_summary size_t alloc; gcov_unsigned_t *buffer; #endif -} gcov_var ATTRIBUTE_HIDDEN; +}; /* Functions for reading and writing gcov files. In libgcov you can open the file for reading then writing. Elsewhere you can open the @@ -578,52 +400,33 @@ struct gcov_summary you use the functions for reading, then gcov_rewrite then the functions for writing. Your file may become corrupted if you break these invariants. */ -#if IN_LIBGCOV -GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN; -#else -GCOV_LINKAGE int gcov_open (const char */*name*/, int /*direction*/); -GCOV_LINKAGE int gcov_magic (gcov_unsigned_t, gcov_unsigned_t); -#endif -GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN; /* Available everywhere. */ -static gcov_position_t gcov_position (void); -static int gcov_is_error (void); - +GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN; GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN; GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN; GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN; - -#if IN_LIBGCOV -/* Available only in libgcov */ -GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN; -GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t) - ATTRIBUTE_HIDDEN; -GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/, - const struct gcov_summary *) - ATTRIBUTE_HIDDEN; -static void gcov_rewrite (void); -GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN; -#else -/* Available outside libgcov */ GCOV_LINKAGE const char *gcov_read_string (void); GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/, gcov_unsigned_t /*length */); -#endif -#if !IN_GCOV -/* Available outside gcov */ -GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN; -#endif +#if !IN_LIBGCOV +GCOV_LINKAGE int gcov_open (const char */*name*/, int /*direction*/); +GCOV_LINKAGE int gcov_magic (gcov_unsigned_t, gcov_unsigned_t); +#endif -#if !IN_GCOV && !IN_LIBGCOV -/* Available only in compiler */ +#if !IN_LIBGCOV && !IN_GCOV +/* in compiler only */ GCOV_LINKAGE unsigned gcov_histo_index (gcov_type value); GCOV_LINKAGE void gcov_write_string (const char *); GCOV_LINKAGE gcov_position_t gcov_write_tag (gcov_unsigned_t); GCOV_LINKAGE void gcov_write_length (gcov_position_t /*position*/); #endif +#if !IN_GCOV +GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN; +#endif + #if IN_GCOV <= 0 && !IN_LIBGCOV /* Available in gcov-dump and the compiler. */ @@ -646,42 +449,11 @@ GCOV_LINKAGE void compute_working_sets (const stru gcov_working_set_t *gcov_working_sets); #endif -#if IN_GCOV > 0 -/* Available in gcov */ +#if IN_GCOV>0 +/* Available only in gcov */ GCOV_LINKAGE time_t gcov_time (void); #endif -/* Save the current position in the gcov file. */ +#endif /* !inhibit_libc */ -static inline gcov_position_t -gcov_position (void) -{ - gcc_assert (gcov_var.mode > 0); - return gcov_var.start + gcov_var.offset; -} - -/* Return nonzero if the error flag is set. */ - -static inline int -gcov_is_error (void) -{ - return gcov_var.file ? gcov_var.error : 1; -} - -#if IN_LIBGCOV -/* Move to beginning of file and initialize for writing. */ - -static inline void -gcov_rewrite (void) -{ - gcc_assert (gcov_var.mode > 0); - gcov_var.mode = -1; - gcov_var.start = 0; - gcov_var.offset = 0; - fseek (gcov_var.file, 0L, SEEK_SET); -} -#endif - -#endif /* IN_LIBGCOV >= 0 */ - #endif /* GCC_GCOV_IO_H */ Index: libgcc/libgcov-profiler.c =================================================================== --- libgcc/libgcov-profiler.c (revision 204895) +++ libgcc/libgcov-profiler.c (working copy) @@ -23,15 +23,8 @@ a copy of the GCC Runtime Library Exception along see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ -#include "tconfig.h" -#include "tsystem.h" -#include "coretypes.h" -#include "tm.h" -#include "libgcc_tm.h" - +#include "libgcov.h" #if !defined(inhibit_libc) -#define IN_LIBGCOV 1 -#include "gcov-io.h" #ifdef L_gcov_interval_profiler /* If VALUE is in interval , then increases the Index: libgcc/libgcov-driver.c =================================================================== --- libgcc/libgcov-driver.c (revision 204895) +++ libgcc/libgcov-driver.c (working copy) @@ -23,23 +23,9 @@ a copy of the GCC Runtime Library Exception along see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ -#include "tconfig.h" -#include "tsystem.h" -#include "coretypes.h" -#include "tm.h" -#include "libgcc_tm.h" +#include "libgcov.h" #if defined(inhibit_libc) -#define IN_LIBGCOV (-1) -#else -#define IN_LIBGCOV 1 -#if defined(L_gcov) -#define GCOV_LINKAGE /* nothing */ -#endif -#endif -#include "gcov-io.h" - -#if defined(inhibit_libc) /* If libc and its header files are not available, provide dummy functions. */ #if defined(L_gcov) @@ -156,7 +142,7 @@ buffer_fn_data (const char *filename, const struct n_ctrs++; len = sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs; - fn_buffer = (struct gcov_fn_buffer *)malloc (len); + fn_buffer = (struct gcov_fn_buffer *) xmalloc (len); if (!fn_buffer) goto fail; @@ -183,7 +169,7 @@ buffer_fn_data (const char *filename, const struct length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ()); len = length * sizeof (gcov_type); - values = (gcov_type *)malloc (len); + values = (gcov_type *) xmalloc (len); if (!values) goto fail; @@ -325,6 +311,9 @@ static struct gcov_summary all_prg; #endif /* crc32 for this program. */ static gcov_unsigned_t crc32; +/* Use this summary checksum rather the computed one if the value is + * non-zero. */ +static gcov_unsigned_t saved_summary_checksum; /* gcda filename. */ static char *gi_filename; /* buffer for the fn_data from another program. */ @@ -453,7 +442,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr, histogram entries that will be emitted, and thus the size of the merged summary. */ (*sum_tail) = (struct gcov_summary_buffer *) - malloc (sizeof(struct gcov_summary_buffer)); + xmalloc (sizeof(struct gcov_summary_buffer)); (*sum_tail)->summary = tmp; (*sum_tail)->next = 0; sum_tail = &((*sum_tail)->next); @@ -529,7 +518,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr, 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); + (*merge) (ci_ptr->values, ci_ptr->num, NULL, 1); ci_ptr++; } if ((error = gcov_is_error ())) @@ -715,9 +704,12 @@ gcov_exit_merge_summary (const struct gcov_info *g } #endif } + + if (saved_summary_checksum) + prg->checksum = saved_summary_checksum; + else + prg->checksum = crc32; - prg->checksum = crc32; - return 0; } Index: libgcc/libgcov-interface.c =================================================================== --- libgcc/libgcov-interface.c (revision 204895) +++ libgcc/libgcov-interface.c (working copy) @@ -23,22 +23,11 @@ a copy of the GCC Runtime Library Exception along see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ -#include "tconfig.h" -#include "tsystem.h" -#include "coretypes.h" -#include "tm.h" -#include "libgcc_tm.h" +#include "libgcov.h" #include "gthr.h" #if defined(inhibit_libc) -#define IN_LIBGCOV (-1) -#else -#define IN_LIBGCOV 1 -#endif -#include "gcov-io.h" -#if defined(inhibit_libc) - #ifdef L_gcov_flush void __gcov_flush (void) {} #endif Index: libgcc/libgcov.h =================================================================== --- libgcc/libgcov.h (revision 0) +++ libgcc/libgcov.h (revision 0) @@ -0,0 +1,252 @@ +/* Header file for libgcov-*.c. + Contributed by Rong Xu . + Copyright (C) 2013 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3, or (at your option) any later + version. + + GCC is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + . */ + +#ifndef GCC_LIBGCOV_H +#define GCC_LIBGCOV_H + +/* work around the poisoned malloc/calloc in system.h. */ +#ifndef xmalloc +#define xmalloc malloc +#endif +#ifndef xcalloc +#define xcalloc calloc +#endif + +#ifndef LIBGCOV_FOR_HOST +/* About the target */ + +#include "tconfig.h" +#include "tsystem.h" +#include "coretypes.h" +#include "tm.h" +#include "libgcc_tm.h" + +#if BITS_PER_UNIT == 8 +typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI))); +typedef unsigned gcov_position_t __attribute__ ((mode (SI))); +#if LONG_LONG_TYPE_SIZE > 32 +typedef signed gcov_type __attribute__ ((mode (DI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI))); +#else +typedef signed gcov_type __attribute__ ((mode (SI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); +#endif +#else +#if BITS_PER_UNIT == 16 +typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI))); +typedef unsigned gcov_position_t __attribute__ ((mode (HI))); +#if LONG_LONG_TYPE_SIZE > 32 +typedef signed gcov_type __attribute__ ((mode (SI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); +#else +typedef signed gcov_type __attribute__ ((mode (HI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); +#endif +#else +typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI))); +typedef unsigned gcov_position_t __attribute__ ((mode (QI))); +#if LONG_LONG_TYPE_SIZE > 32 +typedef signed gcov_type __attribute__ ((mode (HI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); +#else +typedef signed gcov_type __attribute__ ((mode (QI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI))); +#endif +#endif +#endif + +#if defined (TARGET_POSIX_IO) +#define GCOV_LOCKED 1 +#else +#define GCOV_LOCKED 0 +#endif + +#else /* ! LIBGCOV_FOR_HOST_BUILD */ +/* About the host */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" + +typedef unsigned gcov_unsigned_t; +typedef unsigned gcov_position_t; +/* gcov_type is typedef'd elsewhere for the compiler */ +#if defined (HOST_HAS_F_SETLKW) +#define GCOV_LOCKED 1 +#else +#define GCOV_LOCKED 0 +#endif + +#endif /* ! LIBGCOV_FOR_HOST_BUILD */ + +#if defined(inhibit_libc) +#define IN_LIBGCOV (-1) +#else +#define IN_LIBGCOV 1 +#if defined(L_gcov) +#define GCOV_LINKAGE /* nothing */ +#endif +#endif + +/* In libgcov we need these functions to be extern, so prefix them with + __gcov. In libgcov they must also be hidden so that the instance in + the executable is not also used in a DSO. */ +#define gcov_var __gcov_var +#define gcov_open __gcov_open +#define gcov_close __gcov_close +#define gcov_write_tag_length __gcov_write_tag_length +#define gcov_position __gcov_position +#define gcov_seek __gcov_seek +#define gcov_rewrite __gcov_rewrite +#define gcov_is_error __gcov_is_error +#define gcov_write_unsigned __gcov_write_unsigned +#define gcov_write_counter __gcov_write_counter +#define gcov_write_summary __gcov_write_summary +#define gcov_read_unsigned __gcov_read_unsigned +#define gcov_read_counter __gcov_read_counter +#define gcov_read_summary __gcov_read_summary + +/* Poison these, so they don't accidentally slip in. */ +#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length +#pragma GCC poison gcov_time gcov_magic + +#ifdef HAVE_GAS_HIDDEN +#define ATTRIBUTE_HIDDEN __attribute__ ((__visibility__ ("hidden"))) +#else +#define ATTRIBUTE_HIDDEN +#endif + +#include "gcov-io.h" + +/* Structures embedded in coveraged program. The structures generated + by write_profile must match these. */ + +/* Information about counters for a single function. */ +struct gcov_ctr_info +{ + gcov_unsigned_t num; /* number of counters. */ + gcov_type *values; /* their values. */ +}; + +/* Information about a single function. This uses the trailing array + idiom. The number of counters is determined from the merge pointer + array in gcov_info. The key is used to detect which of a set of + comdat functions was selected -- it points to the gcov_info object + of the object file containing the selected comdat function. */ + +struct gcov_fn_info +{ + const struct gcov_info *key; /* comdat key */ + gcov_unsigned_t ident; /* unique ident of function */ + gcov_unsigned_t lineno_checksum; /* function lineo_checksum */ + gcov_unsigned_t cfg_checksum; /* function cfg checksum */ + struct gcov_ctr_info ctrs[0]; /* instrumented counters */ +}; + +/* Type of function used to merge counters. */ +typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t, + gcov_type *, unsigned); + +/* Information about a single object file. */ +struct gcov_info +{ + gcov_unsigned_t version; /* expected version number */ + struct gcov_info *next; /* link to next, used by libgcov */ + + gcov_unsigned_t stamp; /* uniquifying time stamp */ + const char *filename; /* output file name */ + + gcov_merge_fn merge[GCOV_COUNTERS]; /* merge functions (null for + unused) */ + + unsigned n_functions; /* number of functions */ +#if !LIBGCOV_FOR_HOST + const struct gcov_fn_info *const *functions; /* pointer to pointers + to function information */ +#else + const struct gcov_fn_info **functions; +#endif /* !!LIBGCOV_FOR_HOST */ +}; + +/* Register a new object file module. */ +extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN; + +/* Called before fork, to avoid double counting. */ +extern void __gcov_flush (void) ATTRIBUTE_HIDDEN; + +/* Function to reset all counters to 0. */ +extern void __gcov_reset (void); + +/* Function to enable early write of profile information so far. */ +extern void __gcov_dump (void); + +/* The merge function that just sums the counters. */ +extern void __gcov_merge_add (gcov_type *, unsigned, gcov_type *, + unsigned) ATTRIBUTE_HIDDEN; + +/* The merge function to choose the most common value. */ +extern void __gcov_merge_single (gcov_type *, unsigned, gcov_type *, + unsigned) ATTRIBUTE_HIDDEN; + +/* The merge function to choose the most common difference between + consecutive values. */ +extern void __gcov_merge_delta (gcov_type *, unsigned, gcov_type *, + unsigned) ATTRIBUTE_HIDDEN; + +/* The merge function that just ors the counters together. */ +extern void __gcov_merge_ior (gcov_type *, unsigned, gcov_type *, + unsigned) ATTRIBUTE_HIDDEN; + +/* The profiler functions. */ +extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned); +extern void __gcov_pow2_profiler (gcov_type *, gcov_type); +extern void __gcov_one_value_profiler (gcov_type *, gcov_type); +extern void __gcov_indirect_call_profiler (gcov_type*, gcov_type, + void*, void*); +extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *); +extern void __gcov_average_profiler (gcov_type *, gcov_type); +extern void __gcov_ior_profiler (gcov_type *, gcov_type); + +#ifndef inhibit_libc +/* The wrappers around some library functions.. */ +extern pid_t __gcov_fork (void) ATTRIBUTE_HIDDEN; +extern int __gcov_execl (const char *, char *, ...) ATTRIBUTE_HIDDEN; +extern int __gcov_execlp (const char *, char *, ...) ATTRIBUTE_HIDDEN; +extern int __gcov_execle (const char *, char *, ...) ATTRIBUTE_HIDDEN; +extern int __gcov_execv (const char *, char *const []) ATTRIBUTE_HIDDEN; +extern int __gcov_execvp (const char *, char *const []) ATTRIBUTE_HIDDEN; +extern int __gcov_execve (const char *, char *const [], char *const []) + ATTRIBUTE_HIDDEN; + +/* Functions that only available in libgcov. */ +GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN; +GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN; +GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t) + ATTRIBUTE_HIDDEN; +GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/, + const struct gcov_summary *) + ATTRIBUTE_HIDDEN; +GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN; +static inline void gcov_rewrite (void); + +#endif /* !inhibit_libc */ + +#endif /* GCC_LIBGCOV_H */ Index: libgcc/libgcov-driver-system.c =================================================================== --- libgcc/libgcov-driver-system.c (revision 204895) +++ libgcc/libgcov-driver-system.c (working copy) @@ -124,7 +124,7 @@ allocate_filename_struct (struct gcov_filename_aux prefix_length = 1; } /* Allocate and initialize the filename scratch space plus one. */ - gi_filename = (char *) malloc (prefix_length + gcov_max_filename + 2); + gi_filename = (char *) xmalloc (prefix_length + gcov_max_filename + 2); if (prefix_length) memcpy (gi_filename, gcov_prefix, prefix_length); gi_filename_up = gi_filename + prefix_length; Index: libgcc/libgcov-merge.c =================================================================== --- libgcc/libgcov-merge.c (revision 204895) +++ libgcc/libgcov-merge.c (working copy) @@ -23,107 +23,150 @@ a copy of the GCC Runtime Library Exception along see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ -#include "tconfig.h" -#include "tsystem.h" -#include "coretypes.h" -#include "tm.h" -#include "libgcc_tm.h" +#include "libgcov.h" -#if defined(inhibit_libc) -#define IN_LIBGCOV (-1) -#else -#define IN_LIBGCOV 1 +#include "gcov-io-libgcov.h" #endif -#include "gcov-io.h" - #if defined(inhibit_libc) /* If libc and its header files are not available, provide dummy functions. */ #ifdef L_gcov_merge_add void __gcov_merge_add (gcov_type *counters __attribute__ ((unused)), - unsigned n_counters __attribute__ ((unused))) {} + unsigned n_counters __attribute__ ((unused)), + unsigned gcov_type *src __attribute__ ((unused)), + unsigned w __attribute__ ((unused))) {} #endif #ifdef L_gcov_merge_single void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)), - unsigned n_counters __attribute__ ((unused))) {} + unsigned n_counters __attribute__ ((unused)), + unsigned gcov_type *src __attribute__ ((unused)), + unsigned w __attribute__ ((unused))) {} #endif #ifdef L_gcov_merge_delta void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)), - unsigned n_counters __attribute__ ((unused))) {} + unsigned n_counters __attribute__ ((unused)), + unsigned gcov_type *src __attribute__ ((unused)), + unsigned w __attribute__ ((unused))) {} #endif #else #ifdef L_gcov_merge_add /* The profile merging function that just adds the counters. It is given - an array COUNTERS of N_COUNTERS old counters and it reads the same number - of counters from the gcov file. */ + an array COUNTERS of N_COUNTERS old counters. + When SRC==NULL, it reads the same number of counters from the gcov file. + Otherwise, it reads from SRC array. These read values will be multiplied + by weight W before adding to the old counters. */ void -__gcov_merge_add (gcov_type *counters, unsigned n_counters) +__gcov_merge_add (gcov_type *counters, unsigned n_counters, + gcov_type *src, unsigned w) { + int in_mem = (src != 0); + for (; n_counters; counters++, n_counters--) - *counters += gcov_read_counter (); + { + gcov_type value; + + if (in_mem) + value = *(src++); + else + value = gcov_read_counter (); + + *counters += w * value; + } } #endif /* L_gcov_merge_add */ #ifdef L_gcov_merge_ior /* The profile merging function that just adds the counters. It is given - an array COUNTERS of N_COUNTERS old counters and it reads the same number - of counters from the gcov file. */ + an array COUNTERS of N_COUNTERS old counters. + When SRC==NULL, it reads the same number of counters from the gcov file. + Otherwise, it reads from SRC array. */ void -__gcov_merge_ior (gcov_type *counters, unsigned n_counters) +__gcov_merge_ior (gcov_type *counters, unsigned n_counters, + gcov_type *src, unsigned w __attribute__ ((unused))) { + int in_mem = (src != 0); + for (; n_counters; counters++, n_counters--) - *counters |= gcov_read_counter (); + { + gcov_type value; + + if (in_mem) + value = *(src++); + else + value = gcov_read_counter (); + + *counters |= value; + } } #endif #ifdef L_gcov_merge_time_profile /* Time profiles are merged so that minimum from all valid (greater than zero) is stored. There could be a fork that creates new counters. To have - the profile stable, we chosen to pick the smallest function visit time. */ + the profile stable, we chosen to pick the smallest function visit time. */ void -__gcov_merge_time_profile (gcov_type *counters, unsigned n_counters) +__gcov_merge_time_profile (gcov_type *counters, unsigned n_counters, + gcov_type *src, unsigned w __attribute__ ((unused))) { unsigned int i; - gcov_type value; + int in_mem = (src != 0); for (i = 0; i < n_counters; i++) - { - value = gcov_read_counter (); + { + gcov_type value; + if (in_mem) + value = *(src++); + else + value = gcov_read_counter (); + if (value && (!counters[i] || value < counters[i])) counters[i] = value; - } + } } #endif /* L_gcov_merge_time_profile */ #ifdef L_gcov_merge_single /* The profile merging function for choosing the most common value. - It is given an array COUNTERS of N_COUNTERS old counters and it - reads the same number of counters from the gcov file. The counters - are split into 3-tuples where the members of the tuple have - meanings: + It is given an array COUNTERS of N_COUNTERS old counters. + When SRC==NULL, it reads the same number of counters from the gcov file. + Otherwise, it reads from SRC array. These read values will be multiplied + by weight W before mergeing. The counters are split into 3-tuples where + the members of the tuple have meanings: -- the stored candidate on the most common value of the measured entity -- counter -- total number of evaluations of the value */ void -__gcov_merge_single (gcov_type *counters, unsigned n_counters) +__gcov_merge_single (gcov_type *counters, unsigned n_counters, + gcov_type *src, unsigned w) { unsigned i, n_measures; gcov_type value, counter, all; + int in_mem = (src != 0); gcc_assert (!(n_counters % 3)); n_measures = n_counters / 3; - for (i = 0; i < n_measures; i++, counters += 3) + for (i = 0; i < n_measures; i++, counters += 3, src += 3) { - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + if (in_mem) + { + value = src[0]; + counter = src[1]; + all = src[2]; + } + else + { + value = gcov_read_counter (); + counter = gcov_read_counter (); + all = gcov_read_counter (); + } + counter *= w; if (counters[0] == value) counters[1] += counter; @@ -141,9 +184,10 @@ void #ifdef L_gcov_merge_delta /* The profile merging function for choosing the most common - difference between two consecutive evaluations of the value. It is - given an array COUNTERS of N_COUNTERS old counters and it reads the - same number of counters from the gcov file. The counters are split + It is given an array COUNTERS of N_COUNTERS old counters. + When SRC==NULL, it reads the same number of counters from the gcov file. + Otherwise, it reads from SRC array. These read values will be multiplied + by weight W before mergeing. The counters are split into 4-tuples where the members of the tuple have meanings: -- the last value of the measured entity @@ -151,19 +195,31 @@ void -- counter -- total number of evaluations of the value */ void -__gcov_merge_delta (gcov_type *counters, unsigned n_counters) +__gcov_merge_delta (gcov_type *counters, unsigned n_counters, + gcov_type *src, unsigned w) { unsigned i, n_measures; gcov_type value, counter, all; + int in_mem = (src != 0); gcc_assert (!(n_counters % 4)); n_measures = n_counters / 4; - for (i = 0; i < n_measures; i++, counters += 4) + for (i = 0; i < n_measures; i++, counters += 4, src += 4) { - /* last = */ gcov_read_counter (); - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + if (in_mem) + { + value = src[1]; + counter = src[2]; + all = src[3]; + } + else + { + /* last = */ gcov_read_counter (); + value = gcov_read_counter (); + counter = gcov_read_counter (); + all = gcov_read_counter (); + } + counter *= w; if (counters[1] == value) counters[2] += counter; Index: libgcc/libgcov-tool.c =================================================================== --- libgcc/libgcov-tool.c (revision 0) +++ libgcc/libgcov-tool.c (revision 0) @@ -0,0 +1,906 @@ +/* GCC gcov-tool support. + Contributed by Rong Xu . + Copyright (C) 2013 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#define LIBGCOV_FOR_HOST 1 +#define IN_GCOV_TOOL 1 +#define L_gcov 1 +#define L_gcov_merge_add 1 +#define L_gcov_merge_single 1 +#define L_gcov_merge_delta 1 +#define L_gcov_merge_ior 1 +#define L_gcov_merge_time_profile 1 + +#include "libgcov.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "demangle.h" + +/* We need the dumping and merge part of code in libgcov. */ +#include "libgcov-driver.c" +#include "libgcov-merge.c" + +/* Verbose mode for debug. */ +static int verbose; + +/* Set verbose flag. */ +void gcov_tool_set_verbose (void) +{ + verbose = 1; +} + +/* -------- Read Gcda and Reconstruct GCOV_INFO ----------- */ + +#include "obstack.h" +#include +#include + +static void tag_function (const char *, unsigned, unsigned); +static void tag_blocks (const char *, unsigned, unsigned); +static void tag_arcs (const char *, unsigned, unsigned); +static void tag_lines (const char *, unsigned, unsigned); +static void tag_counters (const char *, unsigned, unsigned); +static void tag_summary (const char *, unsigned, unsigned); + +/* The gcov_info for the first module. */ +static struct gcov_info *curr_gcov_info; +/* The gcov_info being processed. */ +static struct gcov_info *gcov_info_head; +/* This variable contains all the functions in current module. */ +static struct obstack fn_info; +/* The function being processed. */ +static struct gcov_fn_info *curr_fn_info; +/* The number of functions seen so far. */ +static unsigned num_fn_info; +/* This variable contains all the counters for current module. */ +static int k_ctrs_mask[GCOV_COUNTERS]; +/* The kind of counters that have been seen. */ +static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS]; +/* Number of kind of counters that have been seen. */ +static int k_ctrs_types; +/* The longest length of all the filenames. */ +static int max_filename_len; + +/* Merge functions for counters. */ +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { + __gcov_merge_add, + __gcov_merge_add, + __gcov_merge_add, + __gcov_merge_single, + __gcov_merge_delta, + __gcov_merge_single, + __gcov_merge_add, + __gcov_merge_ior, + __gcov_merge_time_profile, +}; + +/* Set the fn_ctrs structure in fn_info. */ + +static void +set_fn_ctrs (struct gcov_fn_info *fn_info) +{ + int j = 0, i; + + for (i = 0; i < GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i] == 0) + continue; + fn_info->ctrs[j].num = k_ctrs[i].num; + fn_info->ctrs[j].values = k_ctrs[i].values; + j++; + } + if (k_ctrs_types == 0) + k_ctrs_types = j; + else + gcc_assert (j == k_ctrs_types); +} + +typedef struct tag_format +{ + unsigned tag; + char const *name; + void (*proc) (const char *, unsigned, unsigned); +} tag_format_t; + +static const tag_format_t tag_table[] = +{ + {0, "NOP", NULL}, + {0, "UNKNOWN", NULL}, + {0, "COUNTERS", tag_counters}, + {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, + {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, + {GCOV_TAG_ARCS, "ARCS", tag_arcs}, + {GCOV_TAG_LINES, "LINES", tag_lines}, + {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, + {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, + {0, NULL, NULL} +}; + +/* Handler for reading funtion tag. */ + +static void +tag_function (const char *filename ATTRIBUTE_UNUSED, + unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + int i; + + /* write out previous fn_info. */ + if (num_fn_info) + { + set_fn_ctrs (curr_fn_info); + /* obstack_grow (&fn_info, &curr_fn_info, sizeof (struct gcov_fn_info*)); */ + obstack_ptr_grow (&fn_info, curr_fn_info); + } + /* first time. */ + { + /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active + counter types. */ + curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info) + + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1); + } + + for (i = 0; i < GCOV_COUNTERS; i++) + k_ctrs[i].num = 0; + k_ctrs_types = 0; + + curr_fn_info->key = curr_gcov_info; + curr_fn_info->ident = gcov_read_unsigned (); + curr_fn_info->lineno_checksum = gcov_read_unsigned (); + curr_fn_info->cfg_checksum = gcov_read_unsigned (); + num_fn_info++; + + if (verbose) + fprintf (stdout, "tag one function id=%d\n", curr_fn_info->ident); +} + +/* Handler for reading block tag. */ + +static void +tag_blocks (const char *filename ATTRIBUTE_UNUSED, + unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading flow arc tag. */ + +static void +tag_arcs (const char *filename ATTRIBUTE_UNUSED, + unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading line tag. */ + +static void +tag_lines (const char *filename ATTRIBUTE_UNUSED, + unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading counters array tag. */ + +static void +tag_counters (const char *filename ATTRIBUTE_UNUSED, + unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); + gcov_type *values; + unsigned ix; + unsigned tag_ix; + + tag_ix = GCOV_COUNTER_FOR_TAG (tag); + gcc_assert (tag_ix < GCOV_COUNTERS); + k_ctrs_mask [tag_ix] = 1; + gcc_assert (k_ctrs[tag_ix].num == 0); + k_ctrs[tag_ix].num = n_counts; + + k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type)); + gcc_assert (values); + + for (ix = 0; ix != n_counts; ix++) + values[ix] = gcov_read_counter (); +} + +/* Handler for reading summary tag. */ + +static void +tag_summary (const char *filename ATTRIBUTE_UNUSED, + unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + struct gcov_summary summary; + + gcov_read_summary (&summary); + if (!saved_summary_checksum) + saved_summary_checksum = summary.checksum; +} + +/* This function is called at the end of reading a gcda file. + It flushes the contents in curr_fn_info to the gcov_info. */ + +static void +read_gcda_finalize (struct gcov_info *obj_info) +{ + int i; + + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + + /* We set the following fields: merge, n_functions, and functions. */ + obj_info->n_functions = num_fn_info; + obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info); + + /* wrap all the counter array. */ + for (i=0; i< GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i]) + obj_info->merge[i] = ctr_merge_functions[i]; + } +} + +/* Read the content of a gcda file, and return a gcov_info data structure. + Program level summary CURRENT_SUMMARY will also be updated. */ + +static struct gcov_info * +read_gcda_file (const char *filename) +{ + unsigned tags[4]; + unsigned depth = 0; + unsigned magic, version; + struct gcov_info *obj_info; + int i; + + for (i=0; i< GCOV_COUNTERS; i++) + k_ctrs_mask[i] = 0; + k_ctrs_types = 0; + + if (!gcov_open (filename)) + { + fprintf (stderr, "%s:cannot open\n", filename); + return NULL; + } + + /* read magic. */ + magic = gcov_read_unsigned (); + if (magic != GCOV_DATA_MAGIC) + { + fprintf (stderr, "%s:not a gcov data file\n", filename); + gcov_close (); + return NULL; + } + + /* read version. */ + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + fprintf (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION); + gcov_close (); + return NULL; + } + + /* Instantiate a gcov_info object. */ + curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) + + sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1); + + obj_info->version = version; + obstack_init (&fn_info); + num_fn_info = 0; + curr_fn_info = 0; + { + char *str_dup = (char*) xmalloc (strlen (filename) + 1); + int len; + + strcpy (str_dup, filename); + obj_info->filename = str_dup; + if ((len = strlen (filename)) > max_filename_len) + max_filename_len = len; + } + + /* read stamp. */ + obj_info->stamp = gcov_read_unsigned (); + + while (1) + { + gcov_position_t base; + unsigned tag, length; + tag_format_t const *format; + unsigned tag_depth; + int error; + unsigned mask; + + tag = gcov_read_unsigned (); + if (!tag) + break; + length = gcov_read_unsigned (); + base = gcov_position (); + mask = GCOV_TAG_MASK (tag) >> 1; + for (tag_depth = 4; mask; mask >>= 8) + { + if (((mask & 0xff) != 0xff)) + { + fprintf (stderr, "warning: %s:tag `%08x' is invalid\n", filename, tag); + break; + } + tag_depth--; + } + for (format = tag_table; format->name; format++) + if (format->tag == tag) + goto found; + format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; + found:; + if (tag) + { + if (depth && depth < tag_depth) + { + if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) + fprintf (stderr, "warning: %s:tag `%08x' is incorrectly nested\n", + filename, tag); + } + depth = tag_depth; + tags[depth - 1] = tag; + } + + if (format->proc) + (*format->proc) (filename, tag, length); + + if (format->proc) + { + unsigned long actual_length = gcov_position () - base; + + if (actual_length > length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes overread\n", + filename, actual_length - length); + else if (length > actual_length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes unread\n", + filename, length - actual_length); + } + gcov_sync (base, length); + if ((error = gcov_is_error ())) + { + fprintf (stderr,error < 0 ? "warning:%s:counter overflow at %lu\n" : + "Warning:%s:read error at %lu\n", filename, + (long unsigned) gcov_position ()); + break; + } + } + + read_gcda_finalize (obj_info); + gcov_close (); + + return obj_info; +} + +#define GCOV_SUFFIX ".gcda" + +/* This will be called by ftw. Return a non-zero value + to stop the tree walk. */ + +static int +ftw_read_file ( const char *filename, + const struct stat *status ATTRIBUTE_UNUSED, + int type) +{ + int filename_len; + int suffix_len; + struct gcov_info *obj_info; + + /* Only read regular files. */ + if (type != FTW_F) + return 0; + + filename_len = strlen (filename); + suffix_len = strlen (GCOV_SUFFIX); + + if (filename_len <= suffix_len) + return 0; + + if (strcmp(filename + filename_len - suffix_len, GCOV_SUFFIX)) + return 0; + + if (verbose) + fprintf (stderr, "reading file: %s\n", filename); + + obj_info = read_gcda_file (filename); + + obj_info->next = gcov_info_head; + gcov_info_head = obj_info; + + return 0; +} + +static inline void +read_profile_dir_init (void) +{ + gcov_info_head = 0; +} + +/* Driver for read a profile directory and convert into gcvo_info list in memory. + Return NULL on error, + Return the head of gcov_info list on success. + Note the file static variable GCOV_MAX_FILENAME is also set. */ + +struct gcov_info * +gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED) +{ + char *pwd; + int ret; + + read_profile_dir_init (); + + if (access (dir_name, R_OK) != 0) + { + fprintf (stderr, "cannot access directory %s\n", dir_name); + return NULL; + } + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (dir_name); + if (ret !=0) + { + fprintf (stderr, "%s is not a directory\n", dir_name); + return NULL; + } + ftw (".", ftw_read_file, 50); + ret = chdir (pwd); + free (pwd); + + + /* gcov_max_filename is defined in libgcov.c that recored + max filename len. We need to set it here to allocate the + array for dumping. */ + gcov_max_filename = max_filename_len; + + return gcov_info_head;; +} + +/* -------------- Merge Profile Counters ------------------ */ + +/* Offline tool to manipulate profile data. + This tool targets on matched profiles. But it has some tolerance on + unmatched profiles. + When merging p1 to p2 (p2 is the dst), + * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; emit warning + * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiplying specified weight; emit warning. + * m.gcda in both p1 and p2: + ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge. + ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep p2->m.gcda->f and + drop p1->m.gcda->f. A warning is emitted. */ + +/* Add INFO2's counter to INFO1, multiplying weight W. */ + +static int +gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w) +{ + unsigned f_ix; + unsigned n_functions = info1->n_functions; + int has_mismatch = 0; + + gcc_assert (info2->n_functions == n_functions); + for (f_ix = 0; f_ix < n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix]; + const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr1, *ci_ptr2; + + if (!gfi_ptr1 || gfi_ptr1->key != info1) + continue; + if (!gfi_ptr2 || gfi_ptr2->key != info2) + continue; + + if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum) + { + fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n", + info1->filename); + has_mismatch = 1; + continue; + } + ci_ptr1 = gfi_ptr1->ctrs; + ci_ptr2 = gfi_ptr2->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge1 = info1->merge[t_ix]; + gcov_merge_fn merge2 = info2->merge[t_ix]; + + gcc_assert (merge1 == merge2); + if (!merge1) + continue; + gcc_assert (ci_ptr1->num == ci_ptr2->num); + (*merge1) (ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w); + ci_ptr1++; + ci_ptr2++; + } + } + + return has_mismatch; +} + +static struct gcov_info * +find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info) +{ + struct gcov_info *gi_ptr; + struct gcov_info *ret = NULL; + int i; + + for (i = 0; i < size; i++) + { + gi_ptr = array[i]; + if (gi_ptr == 0) + continue; + if (!strcmp (gi_ptr->filename, info->filename)) + { + ret = gi_ptr; + array[i] = 0; + break; + } + } + + if (ret && ret->n_functions != info->n_functions) + { + fprintf (stderr, "mismatched profiles in %s (%d functions" + " vs %d functions)\n", + ret->filename, + ret->n_functions, + info->n_functions); + ret = NULL; + } + return ret; +} + +/* return 0 on success: without mismatch. + reutrn 1 on error. */ + +int +gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile, + int w1, int w2) +{ + struct gcov_info *gi_ptr; + struct gcov_info **tgt_infos; + struct gcov_info *tgt_tail; + struct gcov_info **in_src_not_tgt; + unsigned tgt_cnt = 0, src_cnt = 0; + unsigned unmatch_info_cnt = 0; + unsigned int i; + + for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next) + tgt_cnt++; + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + src_cnt++; + tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * tgt_cnt); + gcc_assert (tgt_infos); + in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) * src_cnt); + gcc_assert (in_src_not_tgt); + + for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++) + tgt_infos[i] = gi_ptr; + + tgt_tail = tgt_infos[tgt_cnt - 1]; + + /* First pass on tgt_profile, we multiply w1 to all counters. */ + if (w1 > 1) + { + for (i = 0; i < tgt_cnt; i++) + gcov_merge (tgt_infos[i], tgt_infos[i], w1-1); + } + + /* Second pass, add src_profile to the tgt_profile. */ + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + { + struct gcov_info *gi_ptr1; + + gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr); + if (gi_ptr1 == NULL) + { + in_src_not_tgt[unmatch_info_cnt++] = gi_ptr; + continue; + } + gcov_merge (gi_ptr1, gi_ptr, w2); + } + +#if 0 + /* For modules left in the array. They are not in src_prfile. */ + for (i = 0; i < tgt_cnt; i++) + { + gi_ptr = tgt_infos[i]; + if (!gi_ptr) + continue; + gcov_merge (gi_ptr, gi_ptr, w2); + } + + /* For modules in src but not in tgt. We adjust the counter and append. */ + for (i = 0; i < unmatch_info_cnt; i++) + { + gi_ptr = in_src_not_tgt[i]; + gcov_merge (gi_ptr, gi_ptr, w1+w2-1); + } +#else + /* For modules in src but not in tgt. We adjust the counter and append. */ + for (i = 0; i < unmatch_info_cnt; i++) + { + gi_ptr = in_src_not_tgt[i]; + gcov_merge (gi_ptr, gi_ptr, w2 - 1); + tgt_tail->next = gi_ptr; + tgt_tail = gi_ptr; + } +#endif + + return 0; +} + +/* -------------- Scale Profile Counters ------------------ */ + +/* Type of function used to normalize counters. */ +typedef void (*gcov_scale_fn) (gcov_type *, gcov_unsigned_t, double); + +/* Scale arc counters. */ + +static void +__gcov_scale_add (gcov_type *counters, unsigned n_counters, double f) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = val * f; + } +} + +/* Scale ior counters. */ + +static void +__gcov_scale_ior (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + double f ATTRIBUTE_UNUSED) +{ + /* do nothing. */ +} + +/* Scale delta counters. */ + +static void +__gcov_scale_delta (gcov_type *counters, unsigned n_counters, double f) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 4)); + n_measures = n_counters / 4; + for (i = 0; i < n_measures; i++, counters += 4) + { + counters[2] *= f; + counters[3] *= f; + } +} + +/* Scale single counters. */ + +static void +__gcov_scale_single (gcov_type *counters, unsigned n_counters, double f) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 3)); + n_measures = n_counters / 3; + for (i = 0; i < n_measures; i++, counters += 3) + { + counters[1] *= f; + counters[2] *= f; + } +} + +/* Scaling functions for counters. */ +static gcov_scale_fn ctr_scale_functions[GCOV_COUNTERS] = { + __gcov_scale_add, + __gcov_scale_add, + __gcov_scale_add, + __gcov_scale_single, + __gcov_scale_delta, + __gcov_scale_single, + __gcov_scale_add, + __gcov_scale_ior, + __gcov_scale_ior, +}; + +/* Driver for scale profile counters. */ + +int +gcov_profile_scale (struct gcov_info *profile, float scale_factor) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fprintf (stdout, "scale_factor is %f\n", scale_factor); + + /* scale the counters. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + 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; + (*ctr_scale_functions[t_ix]) (ci_ptr->values, ci_ptr->num, scale_factor); + ci_ptr++; + } + } + + return 0; +} + +/* Driver for normalize profile counters. */ + +int +gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val) +{ + struct gcov_info *gi_ptr; + gcov_type curr_max_val = 0; + unsigned f_ix; + unsigned int i; + float scale_factor; + + /* get the larest count value. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix < 1; t_ix++) + { + for (i = 0; i < ci_ptr->num; i++) + if (ci_ptr->values[i] > curr_max_val) + curr_max_val = ci_ptr->values[i]; + ci_ptr++; + } + } + + scale_factor = (float)max_val / curr_max_val; + if (verbose) + fprintf (stdout, "max_val is %lld\n", (long long) curr_max_val); + + return gcov_profile_scale (profile, scale_factor); +} + +/* Type of function used to normalize counters. */ +typedef void (*gcov_scale2_fn) (gcov_type *, gcov_unsigned_t, int, int); + +/* Scale2 arc counters. */ + +static void +__gcov_scale2_add (gcov_type *counters, unsigned n_counters, int n, int d) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = (val / d) * n; + } +} + +/* Scale2 ior counters. */ + +static void +__gcov_scale2_ior (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + int n ATTRIBUTE_UNUSED, + int d ATTRIBUTE_UNUSED) +{ + /* do nothing. */ +} + +/* Scale2 delta counters. */ + +static void +__gcov_scale2_delta (gcov_type *counters, unsigned n_counters, int n, int d) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 4)); + n_measures = n_counters / 4; + for (i = 0; i < n_measures; i++, counters += 4) + { + counters[2] = (counters[2] / d) * n; + counters[3] = (counters[3] / d) * n; + } +} + +/* Scale2 single counters. */ + +static void +__gcov_scale2_single (gcov_type *counters, unsigned n_counters, int n, int d) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 3)); + n_measures = n_counters / 3; + for (i = 0; i < n_measures; i++, counters += 3) + { + counters[1] = (counters[2] / d) * n; + counters[2] = (counters[2] / d) * n; + } +} + +/* Scale2 functions for counters. */ +static gcov_scale2_fn ctr_scale2_functions[GCOV_COUNTERS] = { + __gcov_scale2_add, + __gcov_scale2_add, + __gcov_scale2_add, + __gcov_scale2_single, + __gcov_scale2_delta, + __gcov_scale2_single, + __gcov_scale2_add, + __gcov_scale2_ior, + __gcov_scale2_ior, +}; + +/* Driver for scale2 profile counters. */ + +int +gcov_profile_scale2 (struct gcov_info *profile, int n, int d) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fprintf (stdout, "scale_factor is %d/%d\n", n, d); + + gcc_assert (n >= 0 && d > 0); + + /* scale the counters. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + 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; + (*ctr_scale2_functions[t_ix]) (ci_ptr->values, ci_ptr->num, n, d); + ci_ptr++; + } + } + + return 0; +} + Index: libgcc/Makefile.in =================================================================== --- libgcc/Makefile.in (revision 204895) +++ libgcc/Makefile.in (working copy) @@ -868,14 +868,14 @@ libgcov-driver-objects = $(patsubst %,%$(objext),$ libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \ $(libgcov-interface-objects) $(libgcov-driver-objects) -$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c +$(libgcov-merge-objects): %$(objext): $(srcdir)/libgcov-merge.c $(srcdir)/libgcov.h $(gcc_compile) -DL$* -c $(srcdir)/libgcov-merge.c -$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c +$(libgcov-profiler-objects): %$(objext): $(srcdir)/libgcov-profiler.c $(srcdir)/libgcov.h $(gcc_compile) -DL$* -c $(srcdir)/libgcov-profiler.c -$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c +$(libgcov-interface-objects): %$(objext): $(srcdir)/libgcov-interface.c $(srcdir)/libgcov.h $(gcc_compile) -DL$* -c $(srcdir)/libgcov-interface.c $(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \ - $(srcdir)/libgcov-driver-system.c + $(srcdir)/libgcov-driver-system.c $(srcdir)/libgcov.h $(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c