From 38a8aecfb882072900434499696b5c32a2274515 Mon Sep 17 00:00:00 2001 From: Rong Xu Date: Mon, 21 Jul 2014 16:47:22 -0700 Subject: [4.9] Switch gcc-4.9 to use google/gcc-4_9 branch. This source drop uses svn version r212828 of google/gcc-4.9 branch. We also cherry-picked r213062, r213063 and r213064 to fix windows build issues. All gcc-4.9 patches before July 3rd are ported to google/gcc-4.9. The following prior commits has not been merged to google branch yet. (They are included in this commit). e7af147f979e657fe2df00808e5b4319b0e088c6, baf87df3cb2683649ba7e9872362a7e721117c23, and c231900e5dcc14d8296bd9f62b45997a49d4d5e7. Change-Id: I4bea3ea470387ff751c2be4cb0d4a12059b9299b --- gcc-4.9/libgcc/ChangeLog | 88 + gcc-4.9/libgcc/Makefile.in | 71 +- gcc-4.9/libgcc/config.host | 11 +- gcc-4.9/libgcc/config/arm/bpabi-lib.h | 4 + gcc-4.9/libgcc/config/arm/lib1funcs.S | 4 + gcc-4.9/libgcc/config/arm/sfp-machine.h | 8 +- gcc-4.9/libgcc/config/mips/linux-unwind.h | 2 + gcc-4.9/libgcc/config/msp430/t-msp430 | 2 +- gcc-4.9/libgcc/config/t-slibgcc-sld | 4 + gcc-4.9/libgcc/config/t-static-no-vis-hide | 2 + gcc-4.9/libgcc/configure | 634 ++++--- gcc-4.9/libgcc/configure.ac | 8 + gcc-4.9/libgcc/dyn-ipa.c | 2540 ++++++++++++++++++++++++++++ gcc-4.9/libgcc/libgcov-driver-system.c | 91 +- gcc-4.9/libgcc/libgcov-driver.c | 276 ++- gcc-4.9/libgcc/libgcov-interface.c | 53 + gcc-4.9/libgcc/libgcov-merge.c | 145 +- gcc-4.9/libgcc/libgcov-profiler.c | 219 +++ gcc-4.9/libgcc/libgcov-util.c | 1162 +++++++++++++ gcc-4.9/libgcc/libgcov.h | 205 ++- gcc-4.9/libgcc/pmu-profile.c | 1552 +++++++++++++++++ 21 files changed, 6688 insertions(+), 393 deletions(-) create mode 100644 gcc-4.9/libgcc/config/t-static-no-vis-hide create mode 100644 gcc-4.9/libgcc/dyn-ipa.c create mode 100644 gcc-4.9/libgcc/libgcov-util.c create mode 100644 gcc-4.9/libgcc/pmu-profile.c (limited to 'gcc-4.9/libgcc') diff --git a/gcc-4.9/libgcc/ChangeLog b/gcc-4.9/libgcc/ChangeLog index 57a83718a..9f17a7d14 100644 --- a/gcc-4.9/libgcc/ChangeLog +++ b/gcc-4.9/libgcc/ChangeLog @@ -1,3 +1,91 @@ +2014-07-14 Richard Biener + Backport r212520 from trunk. + * libgcov.h (struct gcov_fn_info): Make ctrs size 1. + +2014-07-11 Rong Xu + + Backport r212463 from trunk. + * libgcov-util.c (gcov_max_filename): Fix declartion. + +2014-07-10 Rong Xu + + Backport r212448 from trunk. + + Add gcov-tool: an offline gcda profile processing tool + Support. + * libgcov-driver.c (gcov_max_filename): Make available + to gcov-tool. + * libgcov-merge.c (__gcov_merge_add): Replace + gcov_read_counter() with a Macro. + (__gcov_merge_ior): Ditto. + (__gcov_merge_time_profile): Ditto. + (__gcov_merge_single): Ditto. + (__gcov_merge_delta): Ditto. + * libgcov-util.c (void gcov_set_verbose): Set the verbose flag + in the utility functions. + (set_fn_ctrs): Utility function for reading gcda files to in-memory + gcov_list object link lists. + (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_read_counter_mem): Ditto. + (gcov_get_merge_weight): Ditto. + (merge_wrapper): A wrapper function that calls merging handler. + (gcov_merge): Merge two gcov_info objects with weights. + (find_match_gcov_info): Find the matched gcov_info in the list. + (gcov_profile_merge): Merge two gcov_info object lists. + (__gcov_add_counter_op): Process edge profile counter values. + (__gcov_ior_counter_op): Process IOR profile counter values. + (__gcov_delta_counter_op): Process delta profile counter values. + (__gcov_single_counter_op): Process single profile counter values. + (fp_scale): Callback function for float-point scaling. + (int_scale): Callback function for integer fraction scaling. + (gcov_profile_scale): Scaling profile counters. + (gcov_profile_normalize): Normalize profile counters. + * libgcov.h: Add headers and functions for gcov-tool use. + (gcov_get_counter): New. + (gcov_get_counter_target): Ditto. + (struct gcov_info): Make the functions field mutable in gcov-tool + compilation. + +2014-05-27 Georg-Johann Lay + + Backport from 2014-05-27 mainline r210322. + + PR libgcc/61152 + * config/arm/bpabi-lib.h (License): Add GCC Runtime Library Exception. + +2014-05-22 Nick Clifton + + * config/msp430/t-msp430 (HOST_LIBGCC2_CFLAGS): Add + -mhwmult=none. + +2014-05-21 Maciej W. Rozycki + + Backport from mainline + 2014-05-21 Maciej W. Rozycki + + PR libgcc/60166 + * config/arm/sfp-machine.h (_FP_NANFRAC_H, _FP_NANFRAC_S) + (_FP_NANFRAC_D, _FP_NANSIGN_Q): Set the quiet bit. + +2014-05-15 Rainer Orth + + Backport from mainline + 2014-05-08 Rainer Orth + + PR libgcc/61097 + * config/t-slibgcc-sld: Only build and install libgcc-unwind.map + if --enable-shared. + 2014-04-30 Bernd Edlinger Work around for current cygwin32 build problems. diff --git a/gcc-4.9/libgcc/Makefile.in b/gcc-4.9/libgcc/Makefile.in index 06b3c884b..1e941f29f 100644 --- a/gcc-4.9/libgcc/Makefile.in +++ b/gcc-4.9/libgcc/Makefile.in @@ -46,9 +46,16 @@ fixed_point = @fixed_point@ host_noncanonical = @host_noncanonical@ target_noncanonical = @target_noncanonical@ +is_android = @is_android@ + # List of extra object files that should be compiled for this target machine. # The rules for compiling them should be in the t-* file for the machine. +ifeq ($(enable_vtable_verify),yes) EXTRA_PARTS = @extra_parts@ +else +EXTRA_PARTS = @extra_parts@ @vtv_extra_parts@ +endif + extra-parts = libgcc-extra-parts @@ -224,15 +231,17 @@ DECNUMINC = endif # Options to use when compiling libgcc2.a. -# Adding -funwind-tables to debug idiv0 cases for Android # LIBGCC2_DEBUG_CFLAGS = -g LIBGCC2_CFLAGS = -O2 $(LIBGCC2_INCLUDES) $(GCC_CFLAGS) $(HOST_LIBGCC2_CFLAGS) \ $(LIBGCC2_DEBUG_CFLAGS) -DIN_LIBGCC2 \ -fbuilding-libgcc -fno-stack-protector \ - -funwind-tables \ $(INHIBIT_LIBC_CFLAGS) +ifeq ($(is_android),yes) +LIBGCC2_CFLAGS += -funwind-tables +endif + # Additional options to use when compiling libgcc2.a. # Some targets override this to -isystem include LIBGCC2_INCLUDES = @@ -340,6 +349,24 @@ LIBUNWIND = SHLIBUNWIND_LINK = SHLIBUNWIND_INSTALL = +ifeq ($(is_android),yes) +ifneq ($(enable_shared),yes) +# Some prebuilt libraries for Android link libc.so before libgcc.a, and they +# rely on libgcc.a to provide those symbols with default visibility to resolve +# them eventually. The linker order has been fixed in JB maintain releases but +# may take a while to trickle down to partners to refresh their prebuilt +# libraries. Therefore we need to maintain the same visibility as older GCC +# for now. +vis_hide = +endif +else +# For -fvisibility=hidden. We need both a -fvisibility=hidden on +# the command line, and a #define to prevent libgcc2.h etc from +# overriding that with #pragmas. This is set before including $(tmake_file) +# so it can be overridden on a host-specific basis. +vis_hide = @vis_hide@ +endif + tmake_file = @tmake_file@ include $(srcdir)/empty.mk $(tmake_file) @@ -370,21 +397,6 @@ ifeq ($(enable_shared),yes) endif endif -ifneq ($(enable_shared),yes) -# Some prebuilt libraries for Android link libc.so before libgcc.a, and they -# rely on libgcc.a to provide those symbols with default visibility to resolve -# them eventually. The linker order has been fixed in JB maintain releases but -# may take a while to trickle down to partners to refresh their prebuilt -# libraries. Therefore we need to maintain the same visibility as older GCC -# for now. -vis_hide = -else -# For -fvisibility=hidden. We need both a -fvisibility=hidden on -# the command line, and a #define to prevent libgcc2.h etc from -# overriding that with #pragmas. -vis_hide = @vis_hide@ -endif - ifneq (,$(vis_hide)) # If we have -fvisibility=hidden, then we need to generate hide @@ -865,12 +877,14 @@ include $(iterator) # Build libgcov components. LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta _gcov_merge_ior \ - _gcov_merge_time_profile + _gcov_merge_dc _gcov_merge_icall_topn _gcov_merge_time_profile LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \ _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \ - _gcov_indirect_call_profiler_v2 _gcov_time_profiler + _gcov_indirect_call_profiler_v2 _gcov_direct_call_profiler \ + _gcov_indirect_call_topn_profiler _gcov_time_profiler LIBGCOV_INTERFACE = _gcov_flush _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \ - _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump + _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump _gcov_sampling \ + _gcov_prefix LIBGCOV_DRIVER = _gcov libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE)) @@ -880,6 +894,8 @@ libgcov-driver-objects = $(patsubst %,%$(objext),$(LIBGCOV_DRIVER)) libgcov-objects = $(libgcov-merge-objects) $(libgcov-profiler-objects) \ $(libgcov-interface-objects) $(libgcov-driver-objects) +dyn-ipa.o: %$(objext): $(srcdir)/dyn-ipa.c libgcc_tm.h + $(gcc_compile) -c $(srcdir)/dyn-ipa.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 $(srcdir)/libgcov.h @@ -890,10 +906,9 @@ $(libgcov-driver-objects): %$(objext): $(srcdir)/libgcov-driver.c \ $(srcdir)/libgcov-driver-system.c $(srcdir)/libgcov.h $(gcc_compile) -DL$* -c $(srcdir)/libgcov-driver.c - # Static libraries. libgcc.a: $(libgcc-objects) -libgcov.a: $(libgcov-objects) +libgcov.a: $(libgcov-objects) dyn-ipa$(objext) libunwind.a: $(libunwind-objects) libgcc_eh.a: $(libgcc-eh-objects) @@ -1126,6 +1141,18 @@ install-leaf: $(install-shared) $(install-libunwind) esac; \ done + if [ "$(MULTIDIR)" == "." ]; then \ + gcov_src_dest="$(DESTDIR)$(inst_libdir)/gcov-src"; \ + $(mkinstalldirs) $$gcov_src_dest; \ + cp ../../gcc/gcov-iov.h $$gcov_src_dest; \ + cp $(srcdir)/../gcc/gcov-io.h $$gcov_src_dest; \ + cp $(srcdir)/../gcc/gcov-io.c $$gcov_src_dest; \ + cp $(srcdir)/libgcov-driver.c $$gcov_src_dest; \ + chmod 644 $$gcov_src_dest/gcov-iov.h \ + $$gcov_src_dest/gcov-io.h $$gcov_src_dest/gcov-io.c \ + $$gcov_src_dest/libgcov-driver.c; \ + fi + install: install-leaf install-unwind_h @: $(MAKE) ; $(MULTIDO) $(FLAGS_TO_PASS) multi-do DO=install diff --git a/gcc-4.9/libgcc/config.host b/gcc-4.9/libgcc/config.host index 2a9ee9a8d..b089fb918 100644 --- a/gcc-4.9/libgcc/config.host +++ b/gcc-4.9/libgcc/config.host @@ -229,9 +229,7 @@ case ${host} in *-*-linux* | frv-*-*linux* | *-*-kfreebsd*-gnu | *-*-knetbsd*-gnu | *-*-gnu* | *-*-kopensolaris*-gnu) tmake_file="$tmake_file t-crtstuff-pic t-libgcc-pic t-eh-dw2-dip t-slibgcc t-slibgcc-gld t-slibgcc-elf-ver t-linux" extra_parts="crtbegin.o crtbeginS.o crtbeginT.o crtend.o crtendS.o" - if test x$enable_vtable_verify = xyes; then - extra_parts="$extra_parts vtv_start.o vtv_end.o vtv_start_preinit.o vtv_end_preinit.o" - fi + vtv_extra_parts="vtv_start.o vtv_end.o vtv_start_preinit.o vtv_end_preinit.o" ;; *-*-lynxos*) tmake_file="$tmake_file t-lynx $cpu_type/t-crtstuff t-crtstuff-pic t-libgcc-pic" @@ -1283,3 +1281,10 @@ i[34567]86-*-linux* | x86_64-*-linux*) tm_file="${tm_file} i386/value-unwind.h" ;; esac + +case ${host} in +*-grtev3-* | *-grtev4-* ) + # Don't hide symbols in static libraries. + tmake_file="${tmake_file} t-static-no-vis-hide" + ;; +esac diff --git a/gcc-4.9/libgcc/config/arm/bpabi-lib.h b/gcc-4.9/libgcc/config/arm/bpabi-lib.h index 193cc5604..d6e779690 100644 --- a/gcc-4.9/libgcc/config/arm/bpabi-lib.h +++ b/gcc-4.9/libgcc/config/arm/bpabi-lib.h @@ -14,6 +14,10 @@ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + You should have received a copy of the GNU General Public License and a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see diff --git a/gcc-4.9/libgcc/config/arm/lib1funcs.S b/gcc-4.9/libgcc/config/arm/lib1funcs.S index 762b0ec4f..de7b40252 100644 --- a/gcc-4.9/libgcc/config/arm/lib1funcs.S +++ b/gcc-4.9/libgcc/config/arm/lib1funcs.S @@ -1326,16 +1326,20 @@ LSYM(Lover12): ARM_FUNC_START div0 #endif +#if defined (__ANDROID__) /* ANDROID LOCAL BEGIN */ /* Adding stack unwinding directives to debug divide-by-0 errors */ .fnstart .save {r1, lr} +#endif do_push {r1, lr} mov r0, #SIGFPE bl SYM(raise) __PLT__ RETLDM r1 +#if defined (__ANDROID__) .fnend /* ANDROID LOCAL END */ +#endif #ifdef __ARM_EABI__ FUNC_END aeabi_ldiv0 diff --git a/gcc-4.9/libgcc/config/arm/sfp-machine.h b/gcc-4.9/libgcc/config/arm/sfp-machine.h index 4f2b15d7e..b7b5171e7 100644 --- a/gcc-4.9/libgcc/config/arm/sfp-machine.h +++ b/gcc-4.9/libgcc/config/arm/sfp-machine.h @@ -21,10 +21,10 @@ typedef int __gcc_CMPtype __attribute__ ((mode (__libgcc_cmp_return__))); /* According to RTABI, QNAN is only with the most significant bit of the significand set, and all other significand bits zero. */ -#define _FP_NANFRAC_H 0 -#define _FP_NANFRAC_S 0 -#define _FP_NANFRAC_D 0, 0 -#define _FP_NANFRAC_Q 0, 0, 0, 0 +#define _FP_NANFRAC_H _FP_QNANBIT_H +#define _FP_NANFRAC_S _FP_QNANBIT_S +#define _FP_NANFRAC_D _FP_QNANBIT_D, 0 +#define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0, 0, 0 #define _FP_NANSIGN_H 0 #define _FP_NANSIGN_S 0 #define _FP_NANSIGN_D 0 diff --git a/gcc-4.9/libgcc/config/mips/linux-unwind.h b/gcc-4.9/libgcc/config/mips/linux-unwind.h index 33507de09..61110fa8c 100644 --- a/gcc-4.9/libgcc/config/mips/linux-unwind.h +++ b/gcc-4.9/libgcc/config/mips/linux-unwind.h @@ -27,7 +27,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see state data appropriately. See unwind-dw2.c for the structs. */ #include +#if defined (__ANDROID__) #include +#endif #include /* The third parameter to the signal handler points to something with diff --git a/gcc-4.9/libgcc/config/msp430/t-msp430 b/gcc-4.9/libgcc/config/msp430/t-msp430 index 7a7b68007..f82c123b5 100644 --- a/gcc-4.9/libgcc/config/msp430/t-msp430 +++ b/gcc-4.9/libgcc/config/msp430/t-msp430 @@ -42,7 +42,7 @@ LIB2ADD = \ $(srcdir)/config/msp430/floathisf.c \ $(srcdir)/config/msp430/cmpd.c -HOST_LIBGCC2_CFLAGS += -Os -ffunction-sections -fdata-sections +HOST_LIBGCC2_CFLAGS += -Os -ffunction-sections -fdata-sections -mhwmult=none # Local Variables: # mode: Makefile diff --git a/gcc-4.9/libgcc/config/t-slibgcc-sld b/gcc-4.9/libgcc/config/t-slibgcc-sld index ec6e5db0a..0b9539114 100644 --- a/gcc-4.9/libgcc/config/t-slibgcc-sld +++ b/gcc-4.9/libgcc/config/t-slibgcc-sld @@ -4,6 +4,8 @@ SHLIB_LDFLAGS = -Wl,-h,$(SHLIB_SONAME) -Wl,-z,text -Wl,-z,defs \ -Wl,-M,$(SHLIB_MAP) +ifeq ($(enable_shared),yes) + # Linker mapfile to enforce direct binding to libgcc_s unwinder # (PR target/59788). libgcc-unwind.map: libgcc-std.ver @@ -26,3 +28,5 @@ install-libgcc-unwind-map: libgcc-unwind.map $(INSTALL_DATA) $< $(DESTDIR)$(slibdir) install: install-libgcc-unwind-map + +endif diff --git a/gcc-4.9/libgcc/config/t-static-no-vis-hide b/gcc-4.9/libgcc/config/t-static-no-vis-hide new file mode 100644 index 000000000..955ff355e --- /dev/null +++ b/gcc-4.9/libgcc/config/t-static-no-vis-hide @@ -0,0 +1,2 @@ +# Don't hide symbols in static libraries. +vis_hide = diff --git a/gcc-4.9/libgcc/configure b/gcc-4.9/libgcc/configure index 35896deb7..8db50b9c7 100644 --- a/gcc-4.9/libgcc/configure +++ b/gcc-4.9/libgcc/configure @@ -1,11 +1,13 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.64 for GNU C Runtime Library 1.0. +# Generated by GNU Autoconf 2.68 for GNU C Runtime Library 1.0. +# # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software # Foundation, Inc. # +# # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## @@ -87,6 +89,7 @@ fi IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. +as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -212,11 +215,18 @@ IFS=$as_save_IFS # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV export CONFIG_SHELL - exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : @@ -314,7 +324,7 @@ $as_echo X"$as_dir" | test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -354,19 +364,19 @@ else fi # as_fn_arith -# as_fn_error ERROR [LINENO LOG_FD] -# --------------------------------- +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with status $?, using 1 if that was 0. +# script with STATUS, using 1 if that was 0. as_fn_error () { - as_status=$?; test $as_status -eq 0 && as_status=1 - if test "$3"; then - as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi - $as_echo "$as_me: error: $1" >&2 + $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error @@ -524,10 +534,11 @@ as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" -exec 7<&0 &1 +test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. -# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` @@ -556,9 +567,11 @@ ac_includes_default='/* none */' ac_subst_vars='LTLIBOBJS LIBOBJS asm_hidden_op +vtv_extra_parts extra_parts cpu_type thread_header +is_android tm_defines tm_file tmake_file @@ -737,8 +750,9 @@ do fi case $ac_option in - *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *) ac_optarg=yes ;; + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. @@ -783,7 +797,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error "invalid feature name: $ac_useropt" + as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -809,7 +823,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error "invalid feature name: $ac_useropt" + as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1013,7 +1027,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error "invalid package name: $ac_useropt" + as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1029,7 +1043,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error "invalid package name: $ac_useropt" + as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1059,8 +1073,8 @@ do | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) as_fn_error "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information." + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" ;; *=*) @@ -1068,7 +1082,7 @@ Try \`$0 --help' for more information." # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error "invalid variable name: \`$ac_envvar'" ;; + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; @@ -1078,7 +1092,7 @@ Try \`$0 --help' for more information." $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac @@ -1086,13 +1100,13 @@ done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error "missing argument to $ac_option" + as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; - fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi @@ -1115,7 +1129,7 @@ do [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac - as_fn_error "expected an absolute directory name for --$ac_var: $ac_val" + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' @@ -1129,8 +1143,8 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used." >&2 + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1145,9 +1159,9 @@ test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error "working directory cannot be determined" + as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error "pwd does not report name of working directory" + as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. @@ -1186,11 +1200,11 @@ else fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error "cannot find sources ($ac_unique_file) in $srcdir" + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg" + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then @@ -1230,7 +1244,7 @@ Configuration: --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking...' messages + -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files @@ -1319,7 +1333,7 @@ Some influential environment variables: LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor @@ -1392,9 +1406,9 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF GNU C Runtime Library configure 1.0 -generated by GNU Autoconf 2.64 +generated by GNU Autoconf 2.68 -Copyright (C) 2009 Free Software Foundation, Inc. +Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1438,8 +1452,8 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} - return $ac_retval + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval } # ac_fn_c_try_compile @@ -1464,7 +1478,7 @@ $as_echo "$ac_try_echo"; } >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } >/dev/null && { + test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : @@ -1475,8 +1489,8 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} - return $ac_retval + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval } # ac_fn_c_try_cpp @@ -1517,8 +1531,8 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} - return $ac_retval + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval } # ac_fn_c_try_run @@ -1695,8 +1709,8 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ rm -f conftest.val fi - eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} - return $ac_retval + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval } # ac_fn_c_compute_int cat >config.log <<_ACEOF @@ -1704,7 +1718,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by GNU C Runtime Library $as_me 1.0, which was -generated by GNU Autoconf 2.64. Invocation command line was +generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -1814,11 +1828,9 @@ trap 'exit_status=$? { echo - cat <<\_ASBOX -## ---------------- ## + $as_echo "## ---------------- ## ## Cache variables. ## -## ---------------- ## -_ASBOX +## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( @@ -1852,11 +1864,9 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; ) echo - cat <<\_ASBOX -## ----------------- ## + $as_echo "## ----------------- ## ## Output variables. ## -## ----------------- ## -_ASBOX +## ----------------- ##" echo for ac_var in $ac_subst_vars do @@ -1869,11 +1879,9 @@ _ASBOX echo if test -n "$ac_subst_files"; then - cat <<\_ASBOX -## ------------------- ## + $as_echo "## ------------------- ## ## File substitutions. ## -## ------------------- ## -_ASBOX +## ------------------- ##" echo for ac_var in $ac_subst_files do @@ -1887,11 +1895,9 @@ _ASBOX fi if test -s confdefs.h; then - cat <<\_ASBOX -## ----------- ## + $as_echo "## ----------- ## ## confdefs.h. ## -## ----------- ## -_ASBOX +## ----------- ##" echo cat confdefs.h echo @@ -1946,7 +1952,12 @@ _ACEOF ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then - ac_site_file1=$CONFIG_SITE + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site @@ -1957,18 +1968,22 @@ fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue - if test -r "$ac_site_file"; then + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special - # files actually), so we avoid doing that. - if test -f "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in @@ -2037,7 +2052,7 @@ if $ac_cache_corrupted; then $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## @@ -2096,16 +2111,22 @@ fi ac_aux_dir= for ac_dir in $libgcc_topdir "$srcdir"/$libgcc_topdir; do - for ac_t in install-sh install.sh shtool; do - if test -f "$ac_dir/$ac_t"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/$ac_t -c" - break 2 - fi - done + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi done if test -z "$ac_aux_dir"; then - as_fn_error "cannot find install-sh, install.sh, or shtool in $libgcc_topdir \"$srcdir\"/$libgcc_topdir" "$LINENO" 5 + as_fn_error $? "cannot find install-sh, install.sh, or shtool in $libgcc_topdir \"$srcdir\"/$libgcc_topdir" "$LINENO" 5 fi # These three variables are undocumented and unsupported, @@ -2158,27 +2179,27 @@ fi # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } -if test "${ac_cv_build+set}" = set; then : +if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && - as_fn_error "cannot guess build type; you must specify one" "$LINENO" 5 + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; -*) as_fn_error "invalid value of canonical build" "$LINENO" 5;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' @@ -2196,14 +2217,14 @@ case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } -if test "${ac_cv_host+set}" = set; then : +if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi @@ -2211,7 +2232,7 @@ fi $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; -*) as_fn_error "invalid value of canonical host" "$LINENO" 5;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' @@ -2311,7 +2332,7 @@ if test "${enable_version_specific_runtime_libs+set}" = set; then : enableval=$enable_version_specific_runtime_libs; case "$enableval" in yes) version_specific_libs=yes ;; no) version_specific_libs=no ;; - *) as_fn_error "Unknown argument to enable/disable version-specific libs" "$LINENO" 5;; + *) as_fn_error $? "Unknown argument to enable/disable version-specific libs" "$LINENO" 5;; esac else version_specific_libs=no @@ -2343,7 +2364,7 @@ if test "${enable_maintainer_mode+set}" = set; then : enableval=$enable_maintainer_mode; case ${enable_maintainer_mode} in yes) MAINT='' ;; no) MAINT='#' ;; - *) as_fn_error "--enable-maintainer-mode must be yes or no" "$LINENO" 5 ;; + *) as_fn_error $? "--enable-maintainer-mode must be yes or no" "$LINENO" 5 ;; esac maintainer_mode=${enableval} else @@ -2368,7 +2389,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then -if test "${ac_cv_path_install+set}" = set; then : +if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -2451,7 +2472,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_AWK+set}" = set; then : +if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then @@ -2489,7 +2510,7 @@ done # We need awk; bail out if it's missing. case ${AWK} in - "") as_fn_error "can't build without awk, bailing out" "$LINENO" 5 ;; + "") as_fn_error $? "can't build without awk, bailing out" "$LINENO" 5 ;; esac @@ -2578,7 +2599,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_AR+set}" = set; then : +if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then @@ -2618,7 +2639,7 @@ if test -z "$ac_cv_prog_AR"; then set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_AR+set}" = set; then : +if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then @@ -2670,7 +2691,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_LIPO+set}" = set; then : +if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then @@ -2710,7 +2731,7 @@ if test -z "$ac_cv_prog_LIPO"; then set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_LIPO+set}" = set; then : +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then @@ -2762,7 +2783,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}nm; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_NM+set}" = set; then : +if ${ac_cv_prog_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then @@ -2802,7 +2823,7 @@ if test -z "$ac_cv_prog_NM"; then set dummy nm; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_NM+set}" = set; then : +if ${ac_cv_prog_ac_ct_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NM"; then @@ -2854,7 +2875,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_RANLIB+set}" = set; then : +if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then @@ -2894,7 +2915,7 @@ if test -z "$ac_cv_prog_RANLIB"; then set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then : +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then @@ -2946,7 +2967,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_STRIP+set}" = set; then : +if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then @@ -2986,7 +3007,7 @@ if test -z "$ac_cv_prog_STRIP"; then set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then @@ -3056,7 +3077,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_CC+set}" = set; then : +if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -3096,7 +3117,7 @@ if test -z "$ac_cv_prog_CC"; then set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : +if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then @@ -3149,7 +3170,7 @@ if test -z "$CC"; then set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_CC+set}" = set; then : +if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -3189,7 +3210,7 @@ if test -z "$CC"; then set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_CC+set}" = set; then : +if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -3248,7 +3269,7 @@ if test -z "$CC"; then set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_CC+set}" = set; then : +if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -3292,7 +3313,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : +if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then @@ -3346,8 +3367,8 @@ fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "no acceptable C compiler found in \$PATH -See \`config.log' for more details." "$LINENO" 5; } +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 @@ -3368,8 +3389,8 @@ $as_echo "$ac_try_echo"; } >&5 ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 - rm -f conftest.er1 conftest.err fi + rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done @@ -3414,12 +3435,12 @@ main () } _ACEOF ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out conftest.out" +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: @@ -3481,62 +3502,28 @@ test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } if test -z "$ac_file"; then : - $as_echo "$as_me: failed program was:" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "C compiler cannot create executables -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -# If not cross compiling, check that we can run a simple program. -if test "$cross_compiling" != yes; then - if { ac_try='./$ac_file' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out conftest.out +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" @@ -3566,20 +3553,79 @@ done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." "$LINENO" 5; } +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } fi -rm -f conftest$ac_cv_exeext +rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } -if test "${ac_cv_objext+set}" = set; then : +if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -3619,8 +3665,8 @@ sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "cannot compute suffix of object files: cannot compile -See \`config.log' for more details." "$LINENO" 5; } +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi @@ -3630,7 +3676,7 @@ OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if test "${ac_cv_c_compiler_gnu+set}" = set; then : +if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -3667,7 +3713,7 @@ ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } -if test "${ac_cv_prog_cc_g+set}" = set; then : +if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag @@ -3745,7 +3791,7 @@ else fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if test "${ac_cv_prog_cc_c89+set}" = set; then : +if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no @@ -3852,7 +3898,7 @@ if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then - if test "${ac_cv_prog_CPP+set}" = set; then : + if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded @@ -3882,7 +3928,7 @@ else # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -3898,11 +3944,11 @@ else ac_preproc_ok=: break fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext +rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi @@ -3941,7 +3987,7 @@ else # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -3957,18 +4003,18 @@ else ac_preproc_ok=: break fi -rm -f conftest.err conftest.$ac_ext +rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext +rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." "$LINENO" 5; } +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c @@ -3988,7 +4034,7 @@ ac_c_preproc_warn_flag=yes # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of double" >&5 $as_echo_n "checking size of double... " >&6; } -if test "${ac_cv_sizeof_double+set}" = set; then : +if ${ac_cv_sizeof_double+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (double))" "ac_cv_sizeof_double" "$ac_includes_default"; then : @@ -3997,9 +4043,8 @@ else if test "$ac_cv_type_double" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (double) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (double) +See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_double=0 fi @@ -4022,7 +4067,7 @@ _ACEOF # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long double" >&5 $as_echo_n "checking size of long double... " >&6; } -if test "${ac_cv_sizeof_long_double+set}" = set; then : +if ${ac_cv_sizeof_long_double+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long double))" "ac_cv_sizeof_long_double" "$ac_includes_default"; then : @@ -4031,9 +4076,8 @@ else if test "$ac_cv_type_long_double" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -{ as_fn_set_status 77 -as_fn_error "cannot compute sizeof (long double) -See \`config.log' for more details." "$LINENO" 5; }; } +as_fn_error 77 "cannot compute sizeof (long double) +See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long_double=0 fi @@ -4058,7 +4102,7 @@ as_fn_arith $ac_cv_sizeof_long_double \* 8 && long_double_type_size=$as_val # Check for decimal float support. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether decimal floating point is supported" >&5 $as_echo_n "checking whether decimal floating point is supported... " >&6; } -if test "${libgcc_cv_dfp+set}" = set; then : +if ${libgcc_cv_dfp+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4096,7 +4140,7 @@ if test "${enable_decimal_float+set}" = set; then : enableval=$enable_decimal_float; case $enable_decimal_float in yes | no | bid | dpd) default_decimal_float=$enable_decimal_float ;; - *) as_fn_error "'$enable_decimal_float' is an invalid value for --enable-decimal-float. + *) as_fn_error $? "'$enable_decimal_float' is an invalid value for --enable-decimal-float. Valid choices are 'yes', 'bid', 'dpd', and 'no'." "$LINENO" 5 ;; esac @@ -4145,7 +4189,7 @@ esac # Check for fixed-point support. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether fixed-point is supported" >&5 $as_echo_n "checking whether fixed-point is supported... " >&6; } -if test "${libgcc_cv_fixed_point+set}" = set; then : +if ${libgcc_cv_fixed_point+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4212,7 +4256,7 @@ $as_echo "#define HAVE_GETIPINFO 1" >>confdefs.h if test "${enable_sjlj_exceptions+set}" = set; then : enableval=$enable_sjlj_exceptions; case "$enableval" in yes|no|auto) ;; - *) as_fn_error "unknown argument to --enable-sjlj-exceptions" "$LINENO" 5 ;; + *) as_fn_error $? "unknown argument to --enable-sjlj-exceptions" "$LINENO" 5 ;; esac else enable_sjlj_exceptions=auto @@ -4221,7 +4265,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use setjmp/longjmp exceptions" >&5 $as_echo_n "checking whether to use setjmp/longjmp exceptions... " >&6; } -if test "${libgcc_cv_lib_sjlj_exceptions+set}" = set; then : +if ${libgcc_cv_lib_sjlj_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4266,13 +4310,13 @@ $as_echo "#define LIBGCC_SJLJ_EXCEPTIONS 1" >>confdefs.h no) ;; *) - as_fn_error "unable to detect exception model" "$LINENO" 5 + as_fn_error $? "unable to detect exception model" "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } -if test "${acl_cv_prog_gnu_ld+set}" = set; then : +if ${acl_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU ld's only accept -v. @@ -4296,7 +4340,7 @@ $as_echo "$target_thread_file" >&6; } # Check for assembler CFI support. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether assembler supports CFI directives" >&5 $as_echo_n "checking whether assembler supports CFI directives... " >&6; } -if test "${libgcc_cv_cfi+set}" = set; then : +if ${libgcc_cv_cfi+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4384,7 +4428,7 @@ esac # we can check for asm_hidden_op. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __attribute__((visibility(\"hidden\")))" >&5 $as_echo_n "checking for __attribute__((visibility(\"hidden\")))... " >&6; } -if test "${libgcc_cv_hidden_visibility_attribute+set}" = set; then : +if ${libgcc_cv_hidden_visibility_attribute+:} false; then : $as_echo_n "(cached) " >&6 else @@ -4423,7 +4467,7 @@ if test "${enable_tls+set}" = set; then : enableval=$enable_tls; case "$enableval" in yes|no) ;; - *) as_fn_error "Argument to enable/disable tls must be yes or no" "$LINENO" 5 ;; + *) as_fn_error $? "Argument to enable/disable tls must be yes or no" "$LINENO" 5 ;; esac else @@ -4433,7 +4477,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the target assembler supports thread-local storage" >&5 $as_echo_n "checking whether the target assembler supports thread-local storage... " >&6; } -if test "${gcc_cv_have_cc_tls+set}" = set; then : +if ${gcc_cv_have_cc_tls+:} false; then : $as_echo_n "(cached) " >&6 else @@ -4461,7 +4505,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the thread-local storage support is from emutls" >&5 $as_echo_n "checking whether the thread-local storage support is from emutls... " >&6; } -if test "${gcc_cv_use_emutls+set}" = set; then : +if ${gcc_cv_use_emutls+:} false; then : $as_echo_n "(cached) " >&6 else @@ -4495,7 +4539,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for init priority support" >&5 $as_echo_n "checking for init priority support... " >&6; } -if test "${libgcc_cv_init_priority+set}" = set; then : +if ${libgcc_cv_init_priority+:} false; then : $as_echo_n "(cached) " >&6 else @@ -4556,7 +4600,10 @@ do done tm_file="${tm_file_}" - +case "$target" in + *android*) is_android="yes" ;; + *) is_android="no" +esac # Map from thread model to thread header. @@ -4579,6 +4626,7 @@ esac + ac_config_links="$ac_config_links enable-execute-stack.c:$enable_execute_stack" ac_config_links="$ac_config_links unwind.h:$unwind_header" @@ -4659,10 +4707,21 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then - test "x$cache_file" != "x/dev/null" && + if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} - cat confcache >$cache_file + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} @@ -4678,6 +4737,7 @@ DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= +U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' @@ -4693,7 +4753,7 @@ LTLIBOBJS=$ac_ltlibobjs -: ${CONFIG_STATUS=./config.status} +: "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" @@ -4794,6 +4854,7 @@ fi IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. +as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -4839,19 +4900,19 @@ export LANGUAGE (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# as_fn_error ERROR [LINENO LOG_FD] -# --------------------------------- +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with status $?, using 1 if that was 0. +# script with STATUS, using 1 if that was 0. as_fn_error () { - as_status=$?; test $as_status -eq 0 && as_status=1 - if test "$3"; then - as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi - $as_echo "$as_me: error: $1" >&2 + $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error @@ -5047,7 +5108,7 @@ $as_echo X"$as_dir" | test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -5101,7 +5162,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # values after options handling. ac_log=" This file was extended by GNU C Runtime Library $as_me 1.0, which was -generated by GNU Autoconf 2.64. Invocation command line was +generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -5142,6 +5203,7 @@ Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit + --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files @@ -5169,12 +5231,13 @@ General help using GNU software: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ GNU C Runtime Library config.status 1.0 -configured by $0, generated by GNU Autoconf 2.64, - with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" +configured by $0, generated by GNU Autoconf 2.68, + with options \\"\$ac_cs_config\\" -Copyright (C) 2009 Free Software Foundation, Inc. +Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -5191,11 +5254,16 @@ ac_need_defaults=: while test $# != 0 do case $1 in - --*=*) + --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; *) ac_option=$1 ac_optarg=$2 @@ -5209,12 +5277,15 @@ do ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; @@ -5227,7 +5298,7 @@ do ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - as_fn_error "ambiguous option: \`$1' + as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; @@ -5236,7 +5307,7 @@ Try \`$0 --help' for more information.";; ac_cs_silent=: ;; # This is an error. - -*) as_fn_error "unrecognized option: \`$1' + -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" @@ -5307,7 +5378,7 @@ do "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "default") CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;; - *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done @@ -5331,9 +5402,10 @@ fi # after its creation but before its name has been assigned to `$tmp'. $debug || { - tmp= + tmp= ac_tmp= trap 'exit_status=$? - { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } @@ -5341,12 +5413,13 @@ $debug || { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -n "$tmp" && test -d "$tmp" + test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") -} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5 +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. @@ -5363,12 +5436,12 @@ if test "x$ac_cr" = x; then fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\r' + ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi -echo 'BEGIN {' >"$tmp/subs1.awk" && +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF @@ -5377,18 +5450,18 @@ _ACEOF echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || - as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || - as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then - as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -5396,7 +5469,7 @@ done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h @@ -5410,7 +5483,7 @@ s/'"$ac_delim"'$// t delim :nl h -s/\(.\{148\}\).*/\1/ +s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p @@ -5424,7 +5497,7 @@ s/.\{148\}// t nl :delim h -s/\(.\{148\}\).*/\1/ +s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p @@ -5444,7 +5517,7 @@ t delim rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK -cat >>"\$tmp/subs1.awk" <<_ACAWK && +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" @@ -5476,21 +5549,29 @@ if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat -fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ - || as_fn_error "could not setup config files machinery" "$LINENO" 5 +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF -# VPATH may cause trouble with some makes, so we remove $(srcdir), -# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=/{ -s/:*\$(srcdir):*/:/ -s/:*\${srcdir}:*/:/ -s/:*@srcdir@:*/:/ -s/^\([^=]*=[ ]*\):*/\1/ + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// s/^[^=]*=[ ]*$// }' fi @@ -5502,7 +5583,7 @@ fi # test -n "$CONFIG_FILES" # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then -cat >"$tmp/defines.awk" <<\_ACAWK || +cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF @@ -5514,11 +5595,11 @@ _ACEOF # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do - ac_t=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_t"; then + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then break elif $ac_last_try; then - as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5 + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -5603,7 +5684,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error "could not setup config headers machinery" "$LINENO" 5 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" @@ -5616,7 +5697,7 @@ do esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -5635,7 +5716,7 @@ do for ac_f do case $ac_f in - -) ac_f="$tmp/stdin";; + -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. @@ -5644,7 +5725,7 @@ do [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;; + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" @@ -5670,8 +5751,8 @@ $as_echo "$as_me: creating $ac_file" >&6;} esac case $ac_tag in - *:-:* | *:-) cat >"$tmp/stdin" \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac @@ -5801,23 +5882,24 @@ s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined." >&5 +which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined." >&2;} +which seems to be undefined. Please make sure it is defined" >&2;} - rm -f "$tmp/stdin" + rm -f "$ac_tmp/stdin" case $ac_file in - -) cat "$tmp/out" && rm -f "$tmp/out";; - *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # @@ -5826,21 +5908,21 @@ which seems to be undefined. Please make sure it is defined." >&2;} if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" - } >"$tmp/config.h" \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" - mv "$tmp/config.h" "$ac_file" \ - || as_fn_error "could not create $ac_file" "$LINENO" 5 + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error "could not create -" "$LINENO" 5 + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; :L) @@ -5860,19 +5942,19 @@ $as_echo "$as_me: $ac_file is unchanged" >&6;} $as_echo "$as_me: linking $ac_source to $ac_file" >&6;} if test ! -r "$ac_source"; then - as_fn_error "$ac_source: file not found" "$LINENO" 5 + as_fn_error $? "$ac_source: file not found" "$LINENO" 5 fi rm -f "$ac_file" # Try a relative symlink, then a hard link, then a copy. - case $srcdir in + case $ac_source in [\\/$]* | ?:[\\/]* ) ac_rel_source=$ac_source ;; *) ac_rel_source=$ac_top_build_prefix$ac_source ;; esac ln -s "$ac_rel_source" "$ac_file" 2>/dev/null || ln "$ac_source" "$ac_file" 2>/dev/null || cp -p "$ac_source" "$ac_file" || - as_fn_error "cannot link or copy $ac_source to $ac_file" "$LINENO" 5 + as_fn_error $? "cannot link or copy $ac_source to $ac_file" "$LINENO" 5 fi ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 @@ -5898,7 +5980,7 @@ _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || - as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5 + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. @@ -5919,7 +6001,7 @@ if test "$no_create" != yes; then exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit $? + $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 diff --git a/gcc-4.9/libgcc/configure.ac b/gcc-4.9/libgcc/configure.ac index d877d21c0..1f0558592 100644 --- a/gcc-4.9/libgcc/configure.ac +++ b/gcc-4.9/libgcc/configure.ac @@ -407,12 +407,20 @@ tm_file="${tm_file_}" AC_SUBST(tm_file) AC_SUBST(tm_defines) + +case "$target" in + *android*) is_android="yes" ;; + *) is_android="no" +esac +AC_SUBST(is_android) + # Map from thread model to thread header. GCC_AC_THREAD_HEADER([$target_thread_file]) # Substitute configuration variables AC_SUBST(cpu_type) AC_SUBST(extra_parts) +AC_SUBST(vtv_extra_parts) AC_SUBST(asm_hidden_op) AC_CONFIG_LINKS([enable-execute-stack.c:$enable_execute_stack]) AC_CONFIG_LINKS([unwind.h:$unwind_header]) diff --git a/gcc-4.9/libgcc/dyn-ipa.c b/gcc-4.9/libgcc/dyn-ipa.c new file mode 100644 index 000000000..eb5ae9cc9 --- /dev/null +++ b/gcc-4.9/libgcc/dyn-ipa.c @@ -0,0 +1,2540 @@ +/* Compile this one with gcc. */ +/* Copyright (C) 2009. Free Software Foundation, Inc. + Contributed by Xinliang David Li (davidxl@google.com) and + Raksit Ashok (raksit@google.com) + +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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "libgcov.h" + +struct dyn_pointer_set; + +#ifndef IN_GCOV_TOOL +#define XNEWVEC(type,ne) (type *)malloc(sizeof(type) * (ne)) +#define XCNEWVEC(type,ne) (type *)calloc(1, sizeof(type) * (ne)) +#define XNEW(type) (type *)malloc(sizeof(type)) +#define XDELETEVEC(p) free(p) +#define XDELETE(p) free(p) +#endif + +struct dyn_cgraph_node +{ + struct dyn_cgraph_edge *callees; + struct dyn_cgraph_edge *callers; + struct dyn_pointer_set *imported_modules; + + gcov_type guid; + gcov_type sum_in_count; + gcov_unsigned_t visited; +}; + +struct dyn_cgraph_edge +{ + struct dyn_cgraph_node *caller; + struct dyn_cgraph_node *callee; + struct dyn_cgraph_edge *next_caller; + struct dyn_cgraph_edge *next_callee; + gcov_type count; +}; + +struct dyn_module_info +{ + struct dyn_pointer_set *imported_modules; + gcov_unsigned_t max_func_ident; + + /* Used by new algorithm. This dyn_pointer_set only + stored the gcov_info pointer, keyed by + module ident. */ + struct dyn_pointer_set *exported_to; + gcov_unsigned_t group_ggc_mem; +}; + +struct dyn_cgraph +{ + struct dyn_pointer_set **call_graph_nodes; + struct gcov_info **modules; + /* supplement module information */ + struct dyn_module_info *sup_modules; + unsigned num_modules; + unsigned num_nodes_executed; + /* used by new algorithm */ + struct modu_node *modu_nodes; +}; + +/* Module info is stored in dyn_caph->sup_modules + which is indexed by m_ix. */ +struct modu_node +{ + struct gcov_info *module; + struct modu_edge *callees; + struct modu_edge *callers; +}; + +struct modu_edge +{ + struct modu_node *caller; + struct modu_node *callee; + struct modu_edge *next_caller; + struct modu_edge *next_callee; + unsigned n_edges; /* used when combining edges */ + gcov_type sum_count; + unsigned char visited; +}; + +struct dyn_pointer_set +{ + size_t log_slots; + size_t n_slots; /* n_slots = 2^log_slots */ + size_t n_elements; + + void **slots; + unsigned (*get_key) (const void *); +}; + +typedef long dyn_fibheapkey_t; + +typedef struct dyn_fibheap +{ + size_t nodes; + struct fibnode *min; + struct fibnode *root; +} *dyn_fibheap_t; + +typedef struct fibnode +{ + struct fibnode *parent; + struct fibnode *child; + struct fibnode *left; + struct fibnode *right; + dyn_fibheapkey_t key; + void *data; + unsigned int degree : 31; + unsigned int mark : 1; +} *fibnode_t; + +static dyn_fibheap_t dyn_fibheap_new (void); +static fibnode_t dyn_fibheap_insert (dyn_fibheap_t, dyn_fibheapkey_t, void *); +static void *dyn_fibheap_extract_min (dyn_fibheap_t); + +extern gcov_unsigned_t __gcov_lipo_cutoff; +extern gcov_unsigned_t __gcov_lipo_random_seed; +extern gcov_unsigned_t __gcov_lipo_random_group_size; +extern gcov_unsigned_t __gcov_lipo_propagate_scale; +extern gcov_unsigned_t __gcov_lipo_dump_cgraph; +extern gcov_unsigned_t __gcov_lipo_max_mem; +extern gcov_unsigned_t __gcov_lipo_grouping_algorithm; +extern gcov_unsigned_t __gcov_lipo_merge_modu_edges; +extern gcov_unsigned_t __gcov_lipo_weak_inclusion; + +#if defined(inhibit_libc) +void __gcov_build_callgraph (void) {} +#else + +void __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN; +void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN; +static void gcov_dump_callgraph (gcov_type); +static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node); +static void gcov_dump_cgraph_node (struct dyn_cgraph_node *node, + unsigned m, unsigned f); +static int do_cgraph_dump (void); + +static void +gcov_dump_cgraph_node_dot (struct dyn_cgraph_node *node, + unsigned m, unsigned f, + gcov_type cutoff_count); +static void +pointer_set_destroy (struct dyn_pointer_set *pset); +static void +pointer_set_destroy_not_free_value_pointer (struct dyn_pointer_set *); +static void ** +pointer_set_find_or_insert (struct dyn_pointer_set *pset, unsigned key); +static struct dyn_pointer_set * +pointer_set_create (unsigned (*get_key) (const void *)); + +static struct dyn_cgraph the_dyn_call_graph; +static int total_zero_count = 0; +static int total_insane_count = 0; + +enum GROUPING_ALGORITHM +{ + EAGER_PROPAGATION_ALGORITHM=0, + INCLUSION_BASED_PRIORITY_ALGORITHM +}; +static int flag_alg_mode; +static int flag_modu_merge_edges; +static int flag_weak_inclusion; +static int flag_use_existing_grouping; +static gcov_unsigned_t mem_threshold; + +/* Returns 0 if no dump is enabled. Returns 1 if text form graph + dump is enabled. Returns 2 if .dot form dump is enabled. */ + +static int +do_cgraph_dump (void) +{ + const char *dyn_cgraph_dump = 0; + + if (__gcov_lipo_dump_cgraph) + return __gcov_lipo_dump_cgraph; + + dyn_cgraph_dump = getenv ("GCOV_DYN_CGRAPH_DUMP"); + + if (!dyn_cgraph_dump || !strlen (dyn_cgraph_dump)) + return 0; + + if (dyn_cgraph_dump[0] == '1') + return 1; + if (dyn_cgraph_dump[0] == '2') + return 2; + + return 0; +} + +static void +init_dyn_cgraph_node (struct dyn_cgraph_node *node, gcov_type guid) +{ + node->callees = 0; + node->callers = 0; + node->imported_modules = 0; + node->guid = guid; + node->visited = 0; +} + +/* Return module_id. FUNC_GUID is the global unique id. + This id is 1 based. 0 is the invalid id. */ + +static inline gcov_unsigned_t +get_module_ident_from_func_glob_uid (gcov_type func_guid) +{ + return EXTRACT_MODULE_ID_FROM_GLOBAL_ID (func_guid); +} + +/* Return module_id for MODULE_INFO. */ + +static inline gcov_unsigned_t +get_module_ident (const struct gcov_info *module_info) +{ + return module_info->mod_info->ident; +} + +/* Return intra-module function id given function global unique id + FUNC_GUID. */ + +static inline gcov_unsigned_t +get_intra_module_func_id (gcov_type func_guid) +{ + return EXTRACT_FUNC_ID_FROM_GLOBAL_ID (func_guid); +} + +/* Return the pointer to the dynamic call graph node for FUNC_GUID. */ + +static inline struct dyn_cgraph_node * +get_cgraph_node (gcov_type func_guid) +{ + gcov_unsigned_t mod_idx, func_id; + + mod_idx = get_module_ident_from_func_glob_uid (func_guid) - 1; + + /* This is to workaround: calls in __static_initialization_and_destruction + should not be instrumented as the module id context for the callees have + not setup yet -- this leads to mod_idx == (unsigned) (0 - 1). Multithreaded + programs may also produce insane func_guid in the profile counter. */ + if (mod_idx >= the_dyn_call_graph.num_modules) + return 0; + + func_id = get_intra_module_func_id (func_guid); + if (func_id > the_dyn_call_graph.sup_modules[mod_idx].max_func_ident) + return 0; + + return (struct dyn_cgraph_node *) *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[mod_idx], func_id)); +} + +static inline unsigned +imp_mod_get_key (const void *p) +{ + return ((const struct dyn_imp_mod *) p)->imp_mod->mod_info->ident; +} + +static int +imp_mod_set_insert (struct dyn_pointer_set *p, const struct gcov_info *imp_mod, + double wt) +{ + struct dyn_imp_mod **m = (struct dyn_imp_mod **) + pointer_set_find_or_insert (p, get_module_ident (imp_mod)); + if (*m) + { + (*m)->weight += wt; + return 1; + } + else + { + *m = XNEW (struct dyn_imp_mod); + (*m)->imp_mod = imp_mod; + (*m)->weight = wt; + p->n_elements++; + return 0; + } +} + +/* Return the gcov_info pointer for module with id MODULE_ID. */ + +static inline struct gcov_info * +get_module_info (gcov_unsigned_t module_id) +{ + return the_dyn_call_graph.modules[module_id - 1]; +} + +struct gcov_info *__gcov_list ATTRIBUTE_HIDDEN; + +static inline unsigned +cgraph_node_get_key (const void *p) +{ + return get_intra_module_func_id (((const struct dyn_cgraph_node *) p)->guid); +} + +static inline unsigned +gcov_info_get_key (const void *p) +{ + return get_module_ident ((const struct gcov_info *)p); +} + +static struct dyn_pointer_set * +get_exported_to (unsigned module_ident) +{ + gcc_assert (module_ident != 0); + return the_dyn_call_graph.sup_modules[module_ident - 1].exported_to; +} + +static struct dyn_pointer_set * +create_exported_to (unsigned module_ident) +{ + struct dyn_pointer_set *p; + + gcc_assert (module_ident != 0); + p = pointer_set_create (gcov_info_get_key); + the_dyn_call_graph.sup_modules[module_ident - 1].exported_to = p; + return p; +} + +static struct dyn_pointer_set * +get_imported_modus (unsigned module_ident) +{ + struct dyn_pointer_set *p; + struct gcov_info *gi_ptr; + + gcc_assert (module_ident != 0); + p = the_dyn_call_graph.sup_modules[module_ident - 1].imported_modules; + + if (p) + return p; + + the_dyn_call_graph.sup_modules[module_ident - 1].imported_modules = p + = pointer_set_create (imp_mod_get_key); + + gi_ptr = the_dyn_call_graph.modules[module_ident - 1]; + /* make the modules an auxiliay module to itself. */ + imp_mod_set_insert (p, gi_ptr, 0); + + return p; +} + +/* Initialize dynamic call graph. */ + +static void +init_dyn_call_graph (void) +{ + unsigned num_modules = 0; + unsigned max_module_id = 0; + struct gcov_info *gi_ptr; + const char *env_str; + int do_dump = (do_cgraph_dump () != 0); + + the_dyn_call_graph.call_graph_nodes = 0; + the_dyn_call_graph.modules = 0; + the_dyn_call_graph.num_nodes_executed = 0; + + flag_alg_mode = __gcov_lipo_grouping_algorithm; + flag_modu_merge_edges = __gcov_lipo_merge_modu_edges; + flag_weak_inclusion = __gcov_lipo_weak_inclusion; + mem_threshold = __gcov_lipo_max_mem * 1.25; + + gi_ptr = __gcov_list; + + for (; gi_ptr; gi_ptr = gi_ptr->next) + { + unsigned mod_id = get_module_ident (gi_ptr); + num_modules++; + if (max_module_id < mod_id) + max_module_id = mod_id; + } + + if (num_modules < max_module_id) + num_modules = max_module_id; + + the_dyn_call_graph.num_modules = num_modules; + + the_dyn_call_graph.modules + = XNEWVEC (struct gcov_info *, num_modules); + memset (the_dyn_call_graph.modules, 0, + num_modules * sizeof (struct gcov_info*)); + + the_dyn_call_graph.sup_modules + = XNEWVEC (struct dyn_module_info, num_modules); + memset (the_dyn_call_graph.sup_modules, 0, + num_modules * sizeof (struct dyn_module_info)); + + the_dyn_call_graph.call_graph_nodes + = XNEWVEC (struct dyn_pointer_set *, num_modules); + + gi_ptr = __gcov_list; + + if ((env_str = getenv ("GCOV_DYN_ALG"))) + { + flag_alg_mode = atoi (env_str); + + if ((env_str = getenv ("GCOV_DYN_MERGE_EDGES"))) + flag_modu_merge_edges = atoi (env_str); + + if ((env_str = getenv ("GCOV_DYN_WEAK_INCLUSION"))) + flag_weak_inclusion = atoi (env_str); + + if (do_dump) + fprintf (stderr, + "!!!! Using ALG=%d merge_edges=%d weak_inclusion=%d. \n", + flag_alg_mode, flag_modu_merge_edges, flag_weak_inclusion); + } + + if (do_dump) + fprintf (stderr, "Group mem limit: %u KB \n", + __gcov_lipo_max_mem); + + for (; gi_ptr; gi_ptr = gi_ptr->next) + { + /* mod_idx is module_ident - 1. */ + unsigned j, mod_id, mod_idx, max_func_ident = 0; + struct dyn_cgraph_node *node; + + /* initialize flags field. */ + gi_ptr->mod_info->flags = 0; + + mod_id = get_module_ident (gi_ptr); + if (do_dump) + fprintf (stderr, "Module %s %d uses %u KB memory in parsing\n", + gi_ptr->mod_info->source_filename, mod_id, + gi_ptr->mod_info->ggc_memory); + + if (mod_id == 0) + { + fprintf (stderr, "Bad module_ident of 0. Skipping.\n"); + continue; + } + mod_idx = mod_id - 1; + + the_dyn_call_graph.modules[mod_idx] = gi_ptr; + + the_dyn_call_graph.call_graph_nodes[mod_idx] + = pointer_set_create (cgraph_node_get_key); + + for (j = 0; j < gi_ptr->n_functions; j++) + { + const struct gcov_fn_info *fi_ptr = gi_ptr->functions[j]; + *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[mod_idx], fi_ptr->ident)) + = node = XNEW (struct dyn_cgraph_node); + the_dyn_call_graph.call_graph_nodes[mod_idx]->n_elements++; + init_dyn_cgraph_node (node, GEN_FUNC_GLOBAL_ID (gi_ptr->mod_info->ident, + fi_ptr->ident)); + if (fi_ptr->ident > max_func_ident) + max_func_ident = fi_ptr->ident; + } + the_dyn_call_graph.sup_modules[mod_idx].max_func_ident = max_func_ident; + if (flag_alg_mode == INCLUSION_BASED_PRIORITY_ALGORITHM) + { + struct dyn_module_info *sup_module = + &(the_dyn_call_graph.sup_modules[mod_idx]); + + sup_module->group_ggc_mem = gi_ptr->mod_info->ggc_memory; + sup_module->imported_modules = 0; + sup_module->exported_to = 0; + } + } +} + +/* Free up memory allocated for dynamic call graph. */ + +void +__gcov_finalize_dyn_callgraph (void) +{ + unsigned i; + + for (i = 0; i < the_dyn_call_graph.num_modules; i++) + { + struct gcov_info *gi_ptr = the_dyn_call_graph.modules[i]; + const struct gcov_fn_info *fi_ptr; + unsigned f_ix; + + if (gi_ptr == NULL) + continue; + + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + struct dyn_cgraph_node *node; + struct dyn_cgraph_edge *callees, *next_callee; + fi_ptr = gi_ptr->functions[f_ix]; + node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[i], fi_ptr->ident)); + gcc_assert (node); + callees = node->callees; + + if (!callees) + continue; + while (callees != 0) + { + next_callee = callees->next_callee; + XDELETE (callees); + callees = next_callee; + } + if (node->imported_modules) + pointer_set_destroy (node->imported_modules); + } + if (the_dyn_call_graph.call_graph_nodes[i]) + pointer_set_destroy (the_dyn_call_graph.call_graph_nodes[i]); + /* Now delete sup modules */ + if (the_dyn_call_graph.sup_modules[i].imported_modules) + pointer_set_destroy (the_dyn_call_graph.sup_modules[i].imported_modules); + if (flag_alg_mode == INCLUSION_BASED_PRIORITY_ALGORITHM + && the_dyn_call_graph.sup_modules[i].exported_to) + pointer_set_destroy_not_free_value_pointer + (the_dyn_call_graph.sup_modules[i].exported_to); + } + XDELETEVEC (the_dyn_call_graph.call_graph_nodes); + XDELETEVEC (the_dyn_call_graph.sup_modules); + XDELETEVEC (the_dyn_call_graph.modules); +} + +/* Add outgoing edge OUT_EDGE for caller node CALLER. */ + +static void +gcov_add_out_edge (struct dyn_cgraph_node *caller, + struct dyn_cgraph_edge *out_edge) +{ + if (!caller->callees) + caller->callees = out_edge; + else + { + out_edge->next_callee = caller->callees; + caller->callees = out_edge; + } +} + +/* Add incoming edge IN_EDGE for callee node CALLEE. */ + +static void +gcov_add_in_edge (struct dyn_cgraph_node *callee, + struct dyn_cgraph_edge *in_edge) +{ + if (!callee->callers) + callee->callers = in_edge; + else + { + in_edge->next_caller = callee->callers; + callee->callers = in_edge; + } +} + +/* Add a call graph edge between caller CALLER and callee CALLEE. + The edge count is COUNT. */ + +static void +gcov_add_cgraph_edge (struct dyn_cgraph_node *caller, + struct dyn_cgraph_node *callee, + gcov_type count) +{ + struct dyn_cgraph_edge *new_edge = XNEW (struct dyn_cgraph_edge); + new_edge->caller = caller; + new_edge->callee = callee; + new_edge->count = count; + new_edge->next_caller = 0; + new_edge->next_callee = 0; + + gcov_add_out_edge (caller, new_edge); + gcov_add_in_edge (callee, new_edge); +} + +/* Add call graph edges from direct calls for caller CALLER. DIR_CALL_COUNTERS + is the array of call counters. N_COUNTS is the number of counters. */ + +static void +gcov_build_callgraph_dc_fn (struct dyn_cgraph_node *caller, + gcov_type *dir_call_counters, + unsigned n_counts) +{ + unsigned i; + + for (i = 0; i < n_counts; i += 2) + { + struct dyn_cgraph_node *callee; + gcov_type count; + gcov_type callee_guid = dir_call_counters[i]; + + count = dir_call_counters[i + 1]; + if (count == 0) + { + total_zero_count++; + continue; + } + callee = get_cgraph_node (callee_guid); + if (!callee) + { + total_insane_count++; + continue; + } + gcov_add_cgraph_edge (caller, callee, count); + } +} + +/* Add call graph edges from indirect calls for caller CALLER. ICALL_COUNTERS + is the array of icall counters. N_COUNTS is the number of counters. */ + +static void +gcov_build_callgraph_ic_fn (struct dyn_cgraph_node *caller, + gcov_type *icall_counters, + unsigned n_counts) +{ + unsigned i, j; + + for (i = 0; i < n_counts; i += GCOV_ICALL_TOPN_NCOUNTS) + { + gcov_type *value_array = &icall_counters[i + 1]; + for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2) + { + struct dyn_cgraph_node *callee; + gcov_type count; + gcov_type callee_guid = value_array[j]; + + count = value_array[j + 1]; + /* Do not update zero count edge count + * as it means there is no target in this entry. */ + if (count == 0) + continue; + callee = get_cgraph_node (callee_guid); + if (!callee) + { + total_insane_count++; + continue; + } + gcov_add_cgraph_edge (caller, callee, count); + } + } +} + +/* Build the dynamic call graph. */ + +static void +gcov_build_callgraph (void) +{ + struct gcov_info *gi_ptr; + unsigned m_ix; + + init_dyn_call_graph (); + + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + const struct gcov_fn_info *fi_ptr; + unsigned f_ix, i; + + gi_ptr = the_dyn_call_graph.modules[m_ix]; + if (gi_ptr == NULL) + continue; + + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + struct dyn_cgraph_node *caller; + const struct gcov_ctr_info *ci_ptr = 0; + + fi_ptr = gi_ptr->functions[f_ix]; + ci_ptr = fi_ptr->ctrs; + + caller = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[m_ix], + fi_ptr->ident)); + gcc_assert (caller); + + for (i = 0; i < GCOV_COUNTERS; i++) + { + if (!gi_ptr->merge[i]) + continue; + if (i == GCOV_COUNTER_DIRECT_CALL) + gcov_build_callgraph_dc_fn (caller, ci_ptr->values, ci_ptr->num); + + if (i == GCOV_COUNTER_ICALL_TOPNV) + gcov_build_callgraph_ic_fn (caller, ci_ptr->values, ci_ptr->num); + + if (i == GCOV_COUNTER_ARCS && 0) + { + gcov_type total_arc_count = 0; + unsigned arc; + for (arc = 0; arc < ci_ptr->num; arc++) + total_arc_count += ci_ptr->values[arc]; + if (total_arc_count != 0) + the_dyn_call_graph.num_nodes_executed++; + } + ci_ptr++; + } + } + } +} + +static inline size_t +hash1 (unsigned p, unsigned long max, unsigned long logmax) +{ + const unsigned long long A = 0x9e3779b97f4a7c16ull; + const unsigned long long shift = 64 - logmax; + + return ((A * (unsigned long) p) >> shift) & (max - 1); +} + +/* Allocate an empty imported-modules set. */ + +static struct dyn_pointer_set * +pointer_set_create (unsigned (*get_key) (const void *)) +{ + struct dyn_pointer_set *result = XNEW (struct dyn_pointer_set); + + result->n_elements = 0; + result->log_slots = 8; + result->n_slots = (size_t) 1 << result->log_slots; + + result->slots = XNEWVEC (void *, result->n_slots); + memset (result->slots, 0, sizeof (void *) * result->n_slots); + result->get_key = get_key; + + return result; +} + +/* Reclaim all memory associated with PSET. */ + +static void +pointer_set_destroy (struct dyn_pointer_set *pset) +{ + size_t i; + for (i = 0; i < pset->n_slots; i++) + if (pset->slots[i]) + XDELETE (pset->slots[i]); + XDELETEVEC (pset->slots); + XDELETE (pset); +} + +/* Reclaim the memory of PSET but not the value pointer. */ +static void +pointer_set_destroy_not_free_value_pointer (struct dyn_pointer_set *pset) +{ + XDELETEVEC (pset->slots); + XDELETE (pset); +} + +/* Subroutine of pointer_set_find_or_insert. Return the insertion slot for KEY + into an empty element of SLOTS, an array of length N_SLOTS. */ +static inline size_t +insert_aux (unsigned key, void **slots, + size_t n_slots, size_t log_slots, + unsigned (*get_key) (const void *)) +{ + size_t n = hash1 (key, n_slots, log_slots); + while (1) + { + if (slots[n] == 0 || get_key (slots[n]) == key) + return n; + else + { + ++n; + if (n == n_slots) + n = 0; + } + } +} + +/* Find slot for KEY. KEY must be nonnull. */ + +static void ** +pointer_set_find_or_insert (struct dyn_pointer_set *pset, unsigned key) +{ + size_t n; + + /* For simplicity, expand the set even if KEY is already there. This can be + superfluous but can happen at most once. */ + if (pset->n_elements > pset->n_slots / 4) + { + size_t new_log_slots = pset->log_slots + 1; + size_t new_n_slots = pset->n_slots * 2; + void **new_slots = XNEWVEC (void *, new_n_slots); + memset (new_slots, 0, sizeof (void *) * new_n_slots); + size_t i; + + for (i = 0; i < pset->n_slots; ++i) + { + void *value = pset->slots[i]; + if (!value) + continue; + n = insert_aux (pset->get_key (value), new_slots, new_n_slots, + new_log_slots, pset->get_key); + new_slots[n] = value; + } + + XDELETEVEC (pset->slots); + pset->n_slots = new_n_slots; + pset->log_slots = new_log_slots; + pset->slots = new_slots; + } + + n = insert_aux (key, pset->slots, pset->n_slots, pset->log_slots, + pset->get_key); + return &pset->slots[n]; +} + + +/* Pass each pointer in PSET to the function in FN, together with the fixed + parameters DATA1, DATA2, DATA3. If FN returns false, the iteration stops. */ + +static void +pointer_set_traverse (const struct dyn_pointer_set *pset, + int (*fn) (const void *, void *, void *, void *), + void *data1, void *data2, void *data3) +{ + size_t i; + for (i = 0; i < pset->n_slots; ++i) + if (pset->slots[i] && !fn (pset->slots[i], data1, data2, data3)) + break; +} + + +/* Returns nonzero if PSET contains an entry with KEY as the key value. + Collisions are resolved by linear probing. */ + +static int +pointer_set_contains (const struct dyn_pointer_set *pset, unsigned key) +{ + size_t n = hash1 (key, pset->n_slots, pset->log_slots); + + while (1) + { + if (pset->slots[n] == 0) + return 0; + else if (pset->get_key (pset->slots[n]) == key) + return 1; + else + { + ++n; + if (n == pset->n_slots) + n = 0; + } + } +} + +/* Callback function to propagate import module (VALUE) from callee to + caller's imported-module-set (DATA1). + The weight is scaled by the scaling-factor (DATA2) before propagation, + and accumulated into DATA3. */ + +static int +gcov_propagate_imp_modules (const void *value, void *data1, void *data2, + void *data3) +{ + const struct dyn_imp_mod *m = (const struct dyn_imp_mod *) value; + struct dyn_pointer_set *receiving_set = (struct dyn_pointer_set *) data1; + double *scale = (double *) data2; + double *sum = (double *) data3; + double wt = m->weight; + if (scale) + wt *= *scale; + if (sum) + (*sum) += wt; + imp_mod_set_insert (receiving_set, m->imp_mod, wt); + return 1; +} + +static int +sort_by_count (const void *pa, const void *pb) +{ + const struct dyn_cgraph_edge *edge_a = *(struct dyn_cgraph_edge * const *)pa; + const struct dyn_cgraph_edge *edge_b = *(struct dyn_cgraph_edge * const *)pb; + + /* This can overvlow. */ + /* return edge_b->count - edge_a->count; */ + if (edge_b->count > edge_a->count) + return 1; + else if (edge_b->count == edge_a->count) + return 0; + else + return -1; +} + +/* Compute the hot callgraph edge threhold. */ + +static gcov_type +gcov_compute_cutoff_count (void) +{ + unsigned m_ix, capacity, i; + unsigned num_edges = 0; + gcov_type cutoff_count = 0; + double total, cum, cum_cutoff; + struct dyn_cgraph_edge **edges; + struct gcov_info *gi_ptr; + char *cutoff_str; + char *num_perc_str; + unsigned cutoff_perc; + unsigned num_perc; + int do_dump; + + capacity = 100; + /* allocate an edge array */ + edges = XNEWVEC (struct dyn_cgraph_edge*, capacity); + /* First count the number of edges. */ + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + const struct gcov_fn_info *fi_ptr; + unsigned f_ix; + + gi_ptr = the_dyn_call_graph.modules[m_ix]; + if (gi_ptr == NULL) + continue; + + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + struct dyn_cgraph_node *node; + struct dyn_cgraph_edge *callees; + + fi_ptr = gi_ptr->functions[f_ix]; + + node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident)); + gcc_assert (node); + + callees = node->callees; + while (callees != 0) + { + num_edges++; + if (num_edges < capacity) + edges[num_edges - 1] = callees; + else + { + capacity = capacity + (capacity >> 1); + edges = (struct dyn_cgraph_edge **)xrealloc (edges, sizeof (void*) * capacity); + edges[num_edges - 1] = callees; + } + callees = callees->next_callee; + } + } + } + + /* Now sort */ + qsort (edges, num_edges, sizeof (void *), sort_by_count); +#define CUM_CUTOFF_PERCENT 80 +#define MIN_NUM_EDGE_PERCENT 0 + + /* The default parameter value is 100 which is a reserved special value. When + the cutoff parameter is 100, use the environment variable setting if it + exists, otherwise, use the default value 80. */ + if (__gcov_lipo_cutoff != 100) + { + cutoff_perc = __gcov_lipo_cutoff; + num_perc = MIN_NUM_EDGE_PERCENT; + } + else + { + cutoff_str = getenv ("GCOV_DYN_CGRAPH_CUTOFF"); + if (cutoff_str && strlen (cutoff_str)) + { + if ((num_perc_str = strchr (cutoff_str, ':'))) + { + *num_perc_str = '\0'; + num_perc_str++; + } + cutoff_perc = atoi (cutoff_str); + if (num_perc_str) + num_perc = atoi (num_perc_str); + else + num_perc = MIN_NUM_EDGE_PERCENT; + } + else + { + cutoff_perc = CUM_CUTOFF_PERCENT; + num_perc = MIN_NUM_EDGE_PERCENT; + } + } + + total = 0; + cum = 0; + for (i = 0; i < num_edges; i++) + total += edges[i]->count; + + cum_cutoff = (total * cutoff_perc)/100; + do_dump = (do_cgraph_dump () != 0); + for (i = 0; i < num_edges; i++) + { + cum += edges[i]->count; + if (do_dump) + fprintf (stderr, "// edge[%d] count = %.0f [%llx --> %llx]\n", + i, (double) edges[i]->count, + (long long) edges[i]->caller->guid, + (long long) edges[i]->callee->guid); + if (cum >= cum_cutoff && (i * 100 >= num_edges * num_perc)) + { + cutoff_count = edges[i]->count; + break; + } + } + + if (do_dump) + fprintf (stderr,"cum count cutoff = %d%%, minimal num edge cutoff = %d%%\n", + cutoff_perc, num_perc); + + if (do_dump) + fprintf (stderr, "// total = %.0f cum = %.0f cum/total = %.0f%%" + " cutoff_count = %lld [total edges: %d hot edges: %d perc: %d%%]\n" + " total_zero_count_edges = %d total_insane_count_edgess = %d\n" + " total_nodes_executed = %d\n", + total, cum, (cum * 100)/total, (long long) cutoff_count, + num_edges, i, (i * 100)/num_edges, total_zero_count, + total_insane_count, the_dyn_call_graph.num_nodes_executed); + + XDELETEVEC (edges); + return cutoff_count; +} + +/* Return the imported module set for NODE. */ + +static struct dyn_pointer_set * +gcov_get_imp_module_set (struct dyn_cgraph_node *node) +{ + if (!node->imported_modules) + node->imported_modules = pointer_set_create (imp_mod_get_key); + + return node->imported_modules; +} + +/* Return the imported module set for MODULE MI. */ + +static struct dyn_pointer_set * +gcov_get_module_imp_module_set (struct dyn_module_info *mi) +{ + if (!mi->imported_modules) + mi->imported_modules = pointer_set_create (imp_mod_get_key); + + return mi->imported_modules; +} + +/* Callback function to mark if a module needs to be exported. */ + +static int +gcov_mark_export_modules (const void *value, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED, + void *data3 ATTRIBUTE_UNUSED) +{ + const struct gcov_info *module_info + = ((const struct dyn_imp_mod *) value)->imp_mod; + + SET_MODULE_EXPORTED (module_info->mod_info); + return 1; +} + +struct gcov_import_mod_array +{ + const struct dyn_imp_mod **imported_modules; + struct gcov_info *importing_module; + unsigned len; +}; + +/* Callback function to compute pointer set size. */ + +static int +gcov_compute_mset_size (const void *value ATTRIBUTE_UNUSED, + void *data1, + void *data2 ATTRIBUTE_UNUSED, + void *data3 ATTRIBUTE_UNUSED) +{ + unsigned *len = (unsigned *) data1; + (*len)++; + return 1; +} + +/* Callback function to collect imported modules. */ + +static int +gcov_collect_imported_modules (const void *value, + void *data1, + void *data2 ATTRIBUTE_UNUSED, + void *data3 ATTRIBUTE_UNUSED) +{ + struct gcov_import_mod_array *out_array; + const struct dyn_imp_mod *m + = (const struct dyn_imp_mod *) value; + + out_array = (struct gcov_import_mod_array *) data1; + + if (m->imp_mod != out_array->importing_module) + out_array->imported_modules[out_array->len++] = m; + + return 1; +} + +/* Comparator for sorting imported modules using weights. */ + +static int +sort_by_module_wt (const void *pa, const void *pb) +{ + const struct dyn_imp_mod *m_a = *((const struct dyn_imp_mod * const *) pa); + const struct dyn_imp_mod *m_b = *((const struct dyn_imp_mod * const *) pb); + + /* We want to sort in descending order of weights. */ + if (m_a->weight < m_b->weight) + return +1; + if (m_a->weight > m_b->weight) + return -1; + return get_module_ident (m_a->imp_mod) - get_module_ident (m_b->imp_mod); +} + +/* Return a dynamic array of imported modules that is sorted for + the importing module MOD_INFO. The length of the array is returned + in *LEN. */ + +const struct dyn_imp_mod ** +gcov_get_sorted_import_module_array (struct gcov_info *mod_info, + unsigned *len) +{ + unsigned mod_idx; + struct dyn_module_info *sup_mod_info; + unsigned array_len = 0; + struct gcov_import_mod_array imp_array; + + mod_idx = get_module_ident (mod_info) - 1; + sup_mod_info = &the_dyn_call_graph.sup_modules[mod_idx]; + + if (sup_mod_info->imported_modules == 0) + return 0; + + pointer_set_traverse (sup_mod_info->imported_modules, + gcov_compute_mset_size, &array_len, 0, 0); + imp_array.imported_modules = XNEWVEC (const struct dyn_imp_mod *, array_len); + imp_array.len = 0; + imp_array.importing_module = mod_info; + pointer_set_traverse (sup_mod_info->imported_modules, + gcov_collect_imported_modules, &imp_array, 0, 0); + *len = imp_array.len; + qsort (imp_array.imported_modules, imp_array.len, + sizeof (void *), sort_by_module_wt); + return imp_array.imported_modules; +} + +/* Compute modules that are needed for NODE (for cross module inlining). + CUTTOFF_COUNT is the call graph edge count cutoff value. + IMPORT_SCALE is the scaling-factor (percent) by which to scale the + weights of imported modules of a callee before propagating them to + the caller, if the callee and caller are in different modules. + + Each imported module is assigned a weight that corresponds to the + expected benefit due to cross-module inlining. When the imported modules + are written out, they are sorted with highest weight first. + + The following example illustrates how the weight is computed: + + Suppose we are processing call-graph node A. It calls function B 50 times, + which calls function C 1000 times, and function E 800 times. Lets say B has + another in-edge from function D, with edge-count of 50. Say all the + functions are in separate modules (modules a, b, c, d, e, respectively): + + D + | + | 50 + | + 50 v 1000 + A --------> B ----------> C + | + | 800 + | + v + E + + Nodes are processed in depth-first order, so when processing A, we first + process B. For node B, we are going to add module c to the imported-module + set, with weight 1000 (edge-count), and module e with weight 800. + Coming back to A, we are going to add the imported-module-set of B to A, + after doing some scaling. + The first scaling factor comes from the fact that A calls B 50 times, but B + has in-edge-count total of 100. So this scaling factor is 50/100 = 0.5 + The second scaling factor is that since B is in a different module than A, + we want to slightly downgrade imported modules of B, before adding to the + imported-modules set of A. This scaling factor has a default value of 50% + (can be set via env variable GCOV_DYN_IMPORT_SCALE). + So we end up adding modules c and e to the imported-set of A, with weights + 0.5*0.5*1000=250 and 0.5*0.5*800=200, respectively. + + Next, we have to add module b itself to A. The weight is computed as the + edge-count plus the sum of scaled-weights of all modules in the + imported-module set of B, i.e., 50 + 250 + 200 = 500. + + In computing the weight of module b, we add the sum of scaled-weights of + imported modules of b, because it doesn't make sense to import c, e in + module a, until module b is imported. */ + +static void +gcov_process_cgraph_node (struct dyn_cgraph_node *node, + gcov_type cutoff_count, + unsigned import_scale) +{ + unsigned mod_id; + struct dyn_cgraph_edge *callees; + struct dyn_cgraph_edge *callers; + node->visited = 1; + node->sum_in_count = 0; + + callers = node->callers; + while (callers) + { + node->sum_in_count += callers->count; + callers = callers->next_caller; + } + + callees = node->callees; + mod_id = get_module_ident_from_func_glob_uid (node->guid); + + while (callees) + { + if (!callees->callee->visited) + gcov_process_cgraph_node (callees->callee, + cutoff_count, + import_scale); + callees = callees->next_callee; + } + + callees = node->callees; + while (callees) + { + if (callees->count >= cutoff_count) + { + unsigned callee_mod_id; + struct dyn_pointer_set *imp_modules + = gcov_get_imp_module_set (node); + + callee_mod_id + = get_module_ident_from_func_glob_uid (callees->callee->guid); + + double callee_mod_wt = (double) callees->count; + if (callees->callee->imported_modules) + { + double scale = ((double) callees->count) / + ((double) callees->callee->sum_in_count); + /* Reduce weight if callee is in different module. */ + if (mod_id != callee_mod_id) + scale = (scale * import_scale) / 100.0; + pointer_set_traverse (callees->callee->imported_modules, + gcov_propagate_imp_modules, + imp_modules, &scale, &callee_mod_wt); + } + if (mod_id != callee_mod_id) + { + struct gcov_info *callee_mod_info + = get_module_info (callee_mod_id); + if (callee_mod_info) + imp_mod_set_insert (imp_modules, callee_mod_info, callee_mod_wt); + } + } + + callees = callees->next_callee; + } +} + +static void gcov_compute_module_groups_eager_propagation (gcov_type); +static void gcov_compute_module_groups_inclusion_based_with_priority + (gcov_type); + +/* dyn_fibheap */ +static void dyn_fibheap_ins_root (dyn_fibheap_t, fibnode_t); +static void dyn_fibheap_rem_root (dyn_fibheap_t, fibnode_t); +static void dyn_fibheap_consolidate (dyn_fibheap_t); +static void dyn_fibheap_link (dyn_fibheap_t, fibnode_t, fibnode_t); +static fibnode_t dyn_fibheap_extr_min_node (dyn_fibheap_t); +static int dyn_fibheap_compare (dyn_fibheap_t, fibnode_t, fibnode_t); +static int dyn_fibheap_comp_data (dyn_fibheap_t, dyn_fibheapkey_t, + void *, fibnode_t); +static fibnode_t fibnode_new (void); +static void fibnode_insert_after (fibnode_t, fibnode_t); +#define fibnode_insert_before(a, b) fibnode_insert_after (a->left, b) +static fibnode_t fibnode_remove (fibnode_t); + +/* Create a new fibonacci heap. */ +static dyn_fibheap_t +dyn_fibheap_new (void) +{ + return (dyn_fibheap_t) xcalloc (1, sizeof (struct dyn_fibheap)); +} + +/* Create a new fibonacci heap node. */ +static fibnode_t +fibnode_new (void) +{ + fibnode_t node; + + node = (fibnode_t) xcalloc (1, sizeof *node); + node->left = node; + node->right = node; + + return node; +} + +static inline int +dyn_fibheap_compare (dyn_fibheap_t heap ATTRIBUTE_UNUSED, fibnode_t a, + fibnode_t b) +{ + if (a->key < b->key) + return -1; + if (a->key > b->key) + return 1; + return 0; +} + +static inline int +dyn_fibheap_comp_data (dyn_fibheap_t heap, dyn_fibheapkey_t key, + void *data, fibnode_t b) +{ + struct fibnode a; + + a.key = key; + a.data = data; + + return dyn_fibheap_compare (heap, &a, b); +} + +/* Insert DATA, with priority KEY, into HEAP. */ +static fibnode_t +dyn_fibheap_insert (dyn_fibheap_t heap, dyn_fibheapkey_t key, void *data) +{ + fibnode_t node; + + /* Create the new node. */ + node = fibnode_new (); + + /* Set the node's data. */ + node->data = data; + node->key = key; + + /* Insert it into the root list. */ + dyn_fibheap_ins_root (heap, node); + + /* If their was no minimum, or this key is less than the min, + it's the new min. */ + if (heap->min == 0 || node->key < heap->min->key) + heap->min = node; + + heap->nodes++; + + return node; +} + +/* Extract the data of the minimum node from HEAP. */ +static void * +dyn_fibheap_extract_min (dyn_fibheap_t heap) +{ + fibnode_t z; + void *ret = 0; + + /* If we don't have a min set, it means we have no nodes. */ + if (heap->min != 0) + { + /* Otherwise, extract the min node, free the node, and return the + node's data. */ + z = dyn_fibheap_extr_min_node (heap); + ret = z->data; + free (z); + } + + return ret; +} + +/* Delete HEAP. */ +static void +dyn_fibheap_delete (dyn_fibheap_t heap) +{ + while (heap->min != 0) + free (dyn_fibheap_extr_min_node (heap)); + + free (heap); +} + +/* Extract the minimum node of the heap. */ +static fibnode_t +dyn_fibheap_extr_min_node (dyn_fibheap_t heap) +{ + fibnode_t ret = heap->min; + fibnode_t x, y, orig; + + /* Attach the child list of the minimum node to the root list of the heap. + If there is no child list, we don't do squat. */ + for (x = ret->child, orig = 0; x != orig && x != 0; x = y) + { + if (orig == 0) + orig = x; + y = x->right; + x->parent = 0; + dyn_fibheap_ins_root (heap, x); + } + + /* Remove the old root. */ + dyn_fibheap_rem_root (heap, ret); + heap->nodes--; + + /* If we are left with no nodes, then the min is 0. */ + if (heap->nodes == 0) + heap->min = 0; + else + { + /* Otherwise, consolidate to find new minimum, as well as do the reorg + work that needs to be done. */ + heap->min = ret->right; + dyn_fibheap_consolidate (heap); + } + + return ret; +} + +/* Insert NODE into the root list of HEAP. */ +static void +dyn_fibheap_ins_root (dyn_fibheap_t heap, fibnode_t node) +{ + /* If the heap is currently empty, the new node becomes the singleton + circular root list. */ + if (heap->root == 0) + { + heap->root = node; + node->left = node; + node->right = node; + return; + } + + /* Otherwise, insert it in the circular root list between the root + and it's right node. */ + fibnode_insert_after (heap->root, node); +} + +/* Remove NODE from the rootlist of HEAP. */ +static void +dyn_fibheap_rem_root (dyn_fibheap_t heap, fibnode_t node) +{ + if (node->left == node) + heap->root = 0; + else + heap->root = fibnode_remove (node); +} + +/* Consolidate the heap. */ +static void +dyn_fibheap_consolidate (dyn_fibheap_t heap) +{ + fibnode_t a[1 + 8 * sizeof (long)]; + fibnode_t w; + fibnode_t y; + fibnode_t x; + int i; + int d; + int D; + + D = 1 + 8 * sizeof (long); + + memset (a, 0, sizeof (fibnode_t) * D); + + while ((w = heap->root) != 0) + { + x = w; + dyn_fibheap_rem_root (heap, w); + d = x->degree; + while (a[d] != 0) + { + y = a[d]; + if (dyn_fibheap_compare (heap, x, y) > 0) + { + fibnode_t temp; + temp = x; + x = y; + y = temp; + } + dyn_fibheap_link (heap, y, x); + a[d] = 0; + d++; + } + a[d] = x; + } + heap->min = 0; + for (i = 0; i < D; i++) + if (a[i] != 0) + { + dyn_fibheap_ins_root (heap, a[i]); + if (heap->min == 0 || dyn_fibheap_compare (heap, a[i], heap->min) < 0) + heap->min = a[i]; + } +} + +/* Make NODE a child of PARENT. */ +static void +dyn_fibheap_link (dyn_fibheap_t heap ATTRIBUTE_UNUSED, + fibnode_t node, fibnode_t parent) +{ + if (parent->child == 0) + parent->child = node; + else + fibnode_insert_before (parent->child, node); + node->parent = parent; + parent->degree++; + node->mark = 0; +} + +static void +fibnode_insert_after (fibnode_t a, fibnode_t b) +{ + if (a == a->right) + { + a->right = b; + a->left = b; + b->right = a; + b->left = a; + } + else + { + b->right = a->right; + a->right->left = b; + a->right = b; + b->left = a; + } +} + +static fibnode_t +fibnode_remove (fibnode_t node) +{ + fibnode_t ret; + + if (node == node->left) + ret = 0; + else + ret = node->left; + + if (node->parent != 0 && node->parent->child == node) + node->parent->child = ret; + + node->right->left = node->left; + node->left->right = node->right; + + node->parent = 0; + node->left = node; + node->right = node; + + return ret; +} +/* end of dyn_fibheap */ + +/* Compute module grouping using CUTOFF_COUNT as the hot edge + threshold. */ + +static void +gcov_compute_module_groups (gcov_type cutoff_count) +{ + switch (flag_alg_mode) + { + case INCLUSION_BASED_PRIORITY_ALGORITHM: + return gcov_compute_module_groups_inclusion_based_with_priority + (cutoff_count); + case EAGER_PROPAGATION_ALGORITHM: + default: + return gcov_compute_module_groups_eager_propagation (cutoff_count); + } +} + +static void +modu_graph_add_edge (unsigned m_id, unsigned callee_m_id, gcov_type count) +{ + struct modu_node *mnode; + struct modu_node *callee_mnode; + struct modu_edge *e; + + if (m_id == 0 || callee_m_id == 0) + return; + + mnode = &the_dyn_call_graph.modu_nodes[m_id - 1]; + callee_mnode = &the_dyn_call_graph.modu_nodes[callee_m_id - 1]; + + if (flag_modu_merge_edges) + { + struct modu_edge *callees = mnode->callees; + while (callees) + { + if (callees->callee == callee_mnode) + { + callees->n_edges += 1; + callees->sum_count += count; + return; + } + callees = callees->next_callee; + } + } + e = XNEW (struct modu_edge); + e->caller = mnode; + e->callee = callee_mnode; + e->n_edges = 1; + e->sum_count = count; + e->next_callee = mnode->callees; + e->next_caller = callee_mnode->callers; + mnode->callees = e; + callee_mnode->callers = e; + e->visited = 0; +} + +static void +modu_graph_process_dyn_cgraph_node (struct dyn_cgraph_node *node, + gcov_type cutoff_count) +{ + unsigned m_id = get_module_ident_from_func_glob_uid (node->guid); + struct dyn_cgraph_edge *callees; + struct dyn_cgraph_node *callee; + + callees = node->callees; + while (callees != 0) + { + callee = callees->callee; + unsigned callee_m_id = + get_module_ident_from_func_glob_uid (callee->guid); + if (callee_m_id != m_id) + { + if (callees->count >= cutoff_count) + modu_graph_add_edge (m_id, callee_m_id, callees->count); + } + callees = callees->next_callee; + } +} + +static void +build_modu_graph (gcov_type cutoff_count) +{ + unsigned m_ix; + struct gcov_info *gi_ptr; + unsigned n_modules = the_dyn_call_graph.num_modules; + struct modu_node *modu_nodes; + + /* Create modu graph nodes/edges. */ + modu_nodes = XCNEWVEC (struct modu_node, n_modules); + the_dyn_call_graph.modu_nodes = modu_nodes; + for (m_ix = 0; m_ix < n_modules; m_ix++) + { + const struct gcov_fn_info *fi_ptr; + unsigned f_ix; + + gi_ptr = the_dyn_call_graph.modules[m_ix]; + if (gi_ptr == NULL) + continue; + modu_nodes[m_ix].module = gi_ptr; + + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + struct dyn_cgraph_node *node; + + fi_ptr = gi_ptr->functions[f_ix]; + node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident)); + if (!node) + { + fprintf (stderr, "Cannot find module_node (ix = %u)./n", m_ix); + continue; + } + modu_graph_process_dyn_cgraph_node (node, cutoff_count); + } + } +} + +/* Collect ggc_mem_size for the impored_module in VALUE + if DATA1 (a pointer_set) is provided, only count these not in DATA1. + Result is stored in DATA2. */ + +static int +collect_ggc_mem_size (const void *value, + void *data1, + void *data2, + void *data3 ATTRIBUTE_UNUSED) +{ + const struct dyn_imp_mod *g = (const struct dyn_imp_mod *) value; + struct dyn_pointer_set *s = (struct dyn_pointer_set *) data1; + unsigned mod_id = get_module_ident (g->imp_mod); + gcov_unsigned_t *size = (gcov_unsigned_t *) data2; + + if (s && pointer_set_contains (s, mod_id)) + return 1; + + (*size) += g->imp_mod->mod_info->ggc_memory; + + return 1; + +} + +/* Get the group ggc_memory size of a imported list. */ + +static gcov_unsigned_t +get_group_ggc_mem (struct dyn_pointer_set *s) +{ + gcov_unsigned_t ggc_size = 0; + + pointer_set_traverse (s, collect_ggc_mem_size, 0, &ggc_size, 0); + return ggc_size; +} + +/* Get the group ggc_memory size of the unioned imported lists. */ + +static gcov_unsigned_t +modu_union_ggc_size (unsigned t_mid, unsigned s_mid) +{ + struct dyn_pointer_set *t_imported_mods = get_imported_modus (t_mid); + struct dyn_pointer_set *s_imported_mods = get_imported_modus (s_mid); + gcov_unsigned_t size = 0; + + pointer_set_traverse (s_imported_mods, collect_ggc_mem_size, + t_imported_mods, &size, 0); + + size += get_group_ggc_mem (t_imported_mods); + + return size; +} + +/* Insert one module (VALUE) to the target module (DATA1) */ + +static int +modu_add_auxiliary_1 (const void *value, + void *data1, + void *data2, + void *data3 ATTRIBUTE_UNUSED) +{ + const struct dyn_imp_mod *src = (const struct dyn_imp_mod *) value; + const struct gcov_info *src_modu = src->imp_mod; + unsigned t_m_id = *(unsigned *) data1; + struct dyn_pointer_set *t_imported_mods = get_imported_modus (t_m_id); + double wt = (double) *(gcov_type*)data2; + unsigned s_m_id = get_module_ident (src_modu); + struct gcov_info **gp; + struct dyn_pointer_set *s_exported_to; + int already_have = 0; + + if (pointer_set_contains (t_imported_mods, s_m_id)) + already_have = 1; + + /* Insert even it's already there. This is to update the wt. */ + imp_mod_set_insert (t_imported_mods, src_modu, wt); + + if (already_have) + return 1; + + /* add module t_m_id to s_m_id's exported list. */ + s_exported_to = get_exported_to (s_m_id); + if (!s_exported_to) + s_exported_to = create_exported_to (s_m_id); + gp = (struct gcov_info **) pointer_set_find_or_insert + (s_exported_to, t_m_id); + *gp = the_dyn_call_graph.modules[t_m_id - 1]; + s_exported_to->n_elements++; + + return 1; +} + +/* Insert module S_MID and it's imported modules to + imported list of module T_MID. */ + +static void +modu_add_auxiliary (unsigned t_mid, unsigned s_mid, gcov_type count) +{ + struct dyn_pointer_set *s_imported_mods = get_imported_modus (s_mid); + + pointer_set_traverse (s_imported_mods, modu_add_auxiliary_1, + &t_mid, &count, 0); + + /* Recompute the gcc_memory for the group. */ + the_dyn_call_graph.sup_modules[t_mid - 1].group_ggc_mem = + get_group_ggc_mem (get_imported_modus (t_mid)); +} + +/* Check if inserting the module specified by DATA1 (including + it's imported list to grouping VALUE, makes the ggc_memory + size exceed the memory threshold. + Return 0 if size is great than the thereshold and 0 otherwise. */ + +static int +ps_check_ggc_mem (const void *value, + void *data1, + void *data2, + void *data3 ATTRIBUTE_UNUSED) +{ + const struct gcov_info *modu = (const struct gcov_info *) value; + unsigned s_m_id = *(unsigned *) data1; + unsigned *fail = (unsigned *) data2; + unsigned m_id = get_module_ident (modu); + gcov_unsigned_t new_ggc_size; + + new_ggc_size = modu_union_ggc_size (m_id, s_m_id); + if (new_ggc_size > mem_threshold) + { + (*fail) = 1; + return 0; + } + + return 1; +} + +/* Add module specified by DATA1 and it's imported list to + the grouping specified by VALUE. */ + +static int +ps_add_auxiliary (const void *value, + void *data1, + void *data2, + void *data3) +{ + const struct gcov_info *modu = (const struct gcov_info *) value; + unsigned s_m_id = *(unsigned *) data1; + unsigned m_id = get_module_ident (modu); + int not_safe_to_insert = *(int *) data3; + gcov_unsigned_t new_ggc_size; + + /* For strict inclusion, we know it's safe to insert. */ + if (!not_safe_to_insert) + { + modu_add_auxiliary (m_id, s_m_id, *(gcov_type*)data2); + return 1; + } + + /* Check if we can do a partial insertion. */ + new_ggc_size = modu_union_ggc_size (m_id, s_m_id); + if (new_ggc_size > mem_threshold) + return 1; + + modu_add_auxiliary (m_id, s_m_id, *(gcov_type*)data2); + return 1; +} + +/* Return 1 if insertion happened, otherwise 0. */ + +static int +modu_edge_add_auxiliary (struct modu_edge *edge) +{ + struct modu_node *node; + struct modu_node *callee; + struct gcov_info *node_modu; + struct gcov_info *callee_modu; + gcov_unsigned_t group_ggc_mem; + gcov_unsigned_t new_ggc_size; + struct dyn_pointer_set *node_imported_mods; + struct dyn_pointer_set *node_exported_to; + unsigned m_id, callee_m_id; + int fail = 0; + + node = edge->caller; + callee = edge->callee; + node_modu = node->module; + callee_modu = callee->module; + m_id = get_module_ident (node_modu); + + if (m_id == 0) + return 0; + + group_ggc_mem = the_dyn_call_graph.sup_modules[m_id - 1].group_ggc_mem; + + if (group_ggc_mem >= mem_threshold) + return 0; + + node_imported_mods = get_imported_modus (m_id); + + /* Check if the callee is already included. */ + callee_m_id = get_module_ident (callee_modu); + if (pointer_set_contains (node_imported_mods, callee_m_id)) + return 0; + + new_ggc_size = modu_union_ggc_size (m_id, callee_m_id); + if (new_ggc_size > mem_threshold) + return 0; + + /* check the size for the grouping that includes this node. */ + node_exported_to = get_exported_to (m_id); + if (node_exported_to) + { + pointer_set_traverse (node_exported_to, ps_check_ggc_mem, + &callee_m_id, &fail, 0); + if (fail && !flag_weak_inclusion) + return 0; + } + + /* Perform the insertion: first insert to node + and then to all the exported_to nodes. */ + modu_add_auxiliary (m_id, callee_m_id, edge->sum_count); + + if (node_exported_to) + pointer_set_traverse (node_exported_to, ps_add_auxiliary, + &callee_m_id, &(edge->sum_count), &fail); + return 1; +} + +static void +compute_module_groups_inclusion_impl (void) +{ + dyn_fibheap_t heap; + unsigned i; + unsigned n_modules = the_dyn_call_graph.num_modules; + + /* insert all the edges to the heap. */ + heap = dyn_fibheap_new (); + for (i = 0; i < n_modules; i++) + { + struct modu_edge * callees; + struct modu_node *node = &the_dyn_call_graph.modu_nodes[i]; + + callees = node->callees; + while (callees != 0) + { + dyn_fibheap_insert (heap, -1 * callees->sum_count, callees); + callees = callees->next_callee; + } + } + + while (1) + { + struct modu_edge *curr + = (struct modu_edge *) dyn_fibheap_extract_min (heap); + + if (!curr) + break; + if (curr->visited) + continue; + curr->visited = 1; + + modu_edge_add_auxiliary (curr); + } + + dyn_fibheap_delete (heap); + + /* Now compute the export attribute */ + for (i = 0; i < n_modules; i++) + { + struct dyn_module_info *mi + = &the_dyn_call_graph.sup_modules[i]; + if (mi->exported_to) + SET_MODULE_EXPORTED (the_dyn_call_graph.modules[i]->mod_info); + } +} + +static void +gcov_compute_module_groups_inclusion_based_with_priority + (gcov_type cutoff_count) +{ + build_modu_graph (cutoff_count); + compute_module_groups_inclusion_impl (); +} + +static void +gcov_compute_module_groups_eager_propagation (gcov_type cutoff_count) +{ + unsigned m_ix; + struct gcov_info *gi_ptr; + const char *import_scale_str; + unsigned import_scale = __gcov_lipo_propagate_scale; + + /* Different from __gcov_lipo_cutoff handling, the + environment variable here takes precedance */ + import_scale_str = getenv ("GCOV_DYN_IMPORT_SCALE"); + if (import_scale_str && strlen (import_scale_str)) + import_scale = atoi (import_scale_str); + + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + const struct gcov_fn_info *fi_ptr; + unsigned f_ix; + + gi_ptr = the_dyn_call_graph.modules[m_ix]; + if (gi_ptr == NULL) + continue; + + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + struct dyn_cgraph_node *node; + + fi_ptr = gi_ptr->functions[f_ix]; + node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident)); + gcc_assert (node); + if (node->visited) + continue; + + gcov_process_cgraph_node (node, cutoff_count, import_scale); + } + } + + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + const struct gcov_fn_info *fi_ptr; + unsigned f_ix; + + gi_ptr = the_dyn_call_graph.modules[m_ix]; + if (gi_ptr == NULL) + continue; + + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + struct dyn_cgraph_node *node; + unsigned mod_id; + struct dyn_pointer_set *imp_modules; + + fi_ptr = gi_ptr->functions[f_ix]; + node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident)); + gcc_assert (node); + + if (!node->imported_modules) + continue; + + mod_id = get_module_ident_from_func_glob_uid (node->guid); + gcc_assert (mod_id == (m_ix + 1)); + + imp_modules + = gcov_get_module_imp_module_set ( + &the_dyn_call_graph.sup_modules[mod_id - 1]); + + pointer_set_traverse (node->imported_modules, + gcov_propagate_imp_modules, + imp_modules, 0, 0); + } + } + + /* Now compute the export attribute */ + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + struct dyn_module_info *mi + = &the_dyn_call_graph.sup_modules[m_ix]; + + if (mi->imported_modules) + pointer_set_traverse (mi->imported_modules, + gcov_mark_export_modules, 0, 0, 0); + } +} + +/* For each module, compute at random, the group of imported modules, + that is of size at most MAX_GROUP_SIZE. */ + +static void +gcov_compute_random_module_groups (unsigned max_group_size) +{ + unsigned m_ix; + + if (max_group_size > the_dyn_call_graph.num_modules) + max_group_size = the_dyn_call_graph.num_modules; + + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + struct dyn_pointer_set *imp_modules = + gcov_get_module_imp_module_set (&the_dyn_call_graph.sup_modules[m_ix]); + int cur_group_size = rand () % max_group_size; + int i = 0; + while (i < cur_group_size) + { + struct gcov_info *imp_mod_info; + unsigned mod_idx = rand () % the_dyn_call_graph.num_modules; + if (mod_idx == m_ix) + continue; + imp_mod_info = get_module_info (mod_idx + 1); + if (imp_mod_info && + !imp_mod_set_insert (imp_modules, imp_mod_info, 1.0)) + i++; + } + } + + /* Now compute the export attribute */ + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + struct dyn_module_info *mi + = &the_dyn_call_graph.sup_modules[m_ix]; + if (mi->imported_modules) + pointer_set_traverse (mi->imported_modules, + gcov_mark_export_modules, 0, 0, 0); + } +} + +/* Write out MOD_INFO into the gcda file. IS_PRIMARY is a flag + indicating if the module is the primary module in the group. */ + +static void +gcov_write_module_info (const struct gcov_info *mod_info, + unsigned is_primary) +{ + gcov_unsigned_t len = 0, filename_len = 0, src_filename_len = 0, i, j; + gcov_unsigned_t num_strings; + gcov_unsigned_t *aligned_fname; + struct gcov_module_info *module_info = mod_info->mod_info; + filename_len = (strlen (module_info->da_filename) + + sizeof (gcov_unsigned_t)) / sizeof (gcov_unsigned_t); + src_filename_len = (strlen (module_info->source_filename) + + sizeof (gcov_unsigned_t)) / sizeof (gcov_unsigned_t); + len = filename_len + src_filename_len; + len += 2; /* each name string is led by a length. */ + + num_strings = module_info->num_quote_paths + module_info->num_bracket_paths + + module_info->num_system_paths + + module_info->num_cpp_defines + module_info->num_cpp_includes + + module_info->num_cl_args; + for (i = 0; i < num_strings; i++) + { + gcov_unsigned_t string_len + = (strlen (module_info->string_array[i]) + sizeof (gcov_unsigned_t)) + / sizeof (gcov_unsigned_t); + len += string_len; + len += 1; /* Each string is lead by a length. */ + } + + len += 11; /* 11 more fields */ + + gcov_write_tag_length (GCOV_TAG_MODULE_INFO, len); + gcov_write_unsigned (module_info->ident); + gcov_write_unsigned (is_primary); + if (flag_alg_mode == INCLUSION_BASED_PRIORITY_ALGORITHM && is_primary) + SET_MODULE_INCLUDE_ALL_AUX (module_info); + gcov_write_unsigned (module_info->flags); + gcov_write_unsigned (module_info->lang); + gcov_write_unsigned (module_info->ggc_memory); + gcov_write_unsigned (module_info->num_quote_paths); + gcov_write_unsigned (module_info->num_bracket_paths); + gcov_write_unsigned (module_info->num_system_paths); + gcov_write_unsigned (module_info->num_cpp_defines); + gcov_write_unsigned (module_info->num_cpp_includes); + gcov_write_unsigned (module_info->num_cl_args); + + /* Now write the filenames */ + aligned_fname = (gcov_unsigned_t *) alloca ((filename_len + src_filename_len + 2) * + sizeof (gcov_unsigned_t)); + memset (aligned_fname, 0, + (filename_len + src_filename_len + 2) * sizeof (gcov_unsigned_t)); + aligned_fname[0] = filename_len; + strcpy ((char*) (aligned_fname + 1), module_info->da_filename); + aligned_fname[filename_len + 1] = src_filename_len; + strcpy ((char*) (aligned_fname + filename_len + 2), module_info->source_filename); + + for (i = 0; i < (filename_len + src_filename_len + 2); i++) + gcov_write_unsigned (aligned_fname[i]); + + /* Now write the string array. */ + for (j = 0; j < num_strings; j++) + { + gcov_unsigned_t *aligned_string; + gcov_unsigned_t string_len = + (strlen (module_info->string_array[j]) + sizeof (gcov_unsigned_t)) / + sizeof (gcov_unsigned_t); + aligned_string = (gcov_unsigned_t *) + alloca ((string_len + 1) * sizeof (gcov_unsigned_t)); + memset (aligned_string, 0, (string_len + 1) * sizeof (gcov_unsigned_t)); + aligned_string[0] = string_len; + strcpy ((char*) (aligned_string + 1), module_info->string_array[j]); + for (i = 0; i < (string_len + 1); i++) + gcov_write_unsigned (aligned_string[i]); + } +} + +/* Write out MOD_INFO and its imported modules into gcda file. */ + +void +gcov_write_module_infos (struct gcov_info *mod_info) +{ + unsigned imp_len = 0; + const struct dyn_imp_mod **imp_mods; + + gcov_write_module_info (mod_info, 1); + + imp_mods = gcov_get_sorted_import_module_array (mod_info, &imp_len); + if (imp_mods) + { + unsigned i; + + for (i = 0; i < imp_len; i++) + { + const struct gcov_info *imp_mod = imp_mods[i]->imp_mod; + if (imp_mod != mod_info) + gcov_write_module_info (imp_mod, 0); + } + free (imp_mods); + } +} + +/* Set to use module grouping from existing imports files in + the profile directory. */ +void set_use_existing_grouping (void); + +void +set_use_existing_grouping (void) +{ + flag_use_existing_grouping = 1; +} + +#ifdef IN_GCOV_TOOL +extern const char *get_source_profile_dir (void); + +/* find and open the imports files based on da_filename + in GI_PTR. */ + +static FILE * +open_imports_file (struct gcov_info *gi_ptr) +{ + const char *gcda_name; + char *imports_name; + const char *source_dir = ""; + + if (gi_ptr == NULL || gi_ptr->mod_info == NULL) + return NULL; + + gcda_name = gi_ptr->mod_info->da_filename; + gcc_assert (gcda_name); + + source_dir = get_source_profile_dir (); + gcc_assert (source_dir); + imports_name = (char *) alloca (strlen (gcda_name) + strlen (source_dir) + + strlen (".gcda.imports") + 2); + strcpy (imports_name, source_dir); + strcat (imports_name, "/"); + strcat (imports_name, gcda_name); + strcat (imports_name, ".gcda.imports"); + return fopen (imports_name, "r"); +} + +extern int get_module_id_from_name (const char *); + +#endif /* IN_GCOV_TOOL */ + +/* Use the module grouping from existing imports files in + the profile directory. */ + +static void +read_modu_groups_from_imports_files (void) +{ +#ifdef IN_GCOV_TOOL + unsigned m_ix; + const int max_line_size = (1 << 12); + char line[max_line_size]; + + init_dyn_call_graph (); + + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + struct gcov_info *gi_ptr = the_dyn_call_graph.modules[m_ix]; + FILE *fd; + struct dyn_pointer_set *imp_modules; + char buf[8192]; + + if (gi_ptr == NULL) + continue; + + imp_modules = gcov_get_module_imp_module_set + (&the_dyn_call_graph.sup_modules[m_ix]); + + if ((fd = open_imports_file (gi_ptr)) != NULL) + { +#define MAX_MODU_SIZE 200000 + int w = MAX_MODU_SIZE; + int i = 0; + + while (fgets (line, max_line_size, fd) != NULL) + { + unsigned mod_id = 0; + char *name = strtok (line, " \t\n"); + + if (name && (mod_id = get_module_id_from_name (name))) + { + struct gcov_info *imp_mod_info; + unsigned mod_idx = mod_id - 1; + if (mod_idx == m_ix) + continue; + imp_mod_info = get_module_info (mod_idx + 1); + i++; + imp_mod_set_insert (imp_modules, imp_mod_info, w - i); + } + } + fclose (fd); + } + } + + /* Now compute the export attribute */ + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + struct dyn_module_info *mi + = &the_dyn_call_graph.sup_modules[m_ix]; + if (mi->imported_modules) + pointer_set_traverse (mi->imported_modules, + gcov_mark_export_modules, 0, 0, 0); + } +#else /* !IN_GCOV_TOOL */ + gcc_assert (0); +#endif /* IN_GCOV_TOOL */ +} + +/* Compute module groups needed for L-IPO compilation. */ + +void +__gcov_compute_module_groups (void) +{ + gcov_type cut_off_count; + char *seed = getenv ("LIPO_RANDOM_GROUPING"); + char *max_group_size = seed ? strchr (seed, ':') : 0; + + /* The random group is set via compile time parameter. */ + if (__gcov_lipo_random_group_size != 0) + { + srand (__gcov_lipo_random_seed); + init_dyn_call_graph (); + gcov_compute_random_module_groups (__gcov_lipo_random_group_size); + if (do_cgraph_dump () != 0) + { + fprintf (stderr, " Creating random grouping with %u:%u\n", + __gcov_lipo_random_seed, __gcov_lipo_random_group_size); + } + return; + } + else if (seed && max_group_size) + { + *max_group_size = '\0'; + max_group_size++; + srand (atoi (seed)); + init_dyn_call_graph (); + gcov_compute_random_module_groups (atoi (max_group_size)); + if (do_cgraph_dump () != 0) + { + fprintf (stderr, " Creating random grouping with %s:%s\n", + seed, max_group_size); + } + return; + } + + if (flag_use_existing_grouping) + { + read_modu_groups_from_imports_files (); + return; + } + + /* First compute dynamic call graph. */ + gcov_build_callgraph (); + + cut_off_count = gcov_compute_cutoff_count (); + + gcov_compute_module_groups (cut_off_count); + + gcov_dump_callgraph (cut_off_count); + +} + +/* Dumper function for NODE. */ +static void +gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node) +{ + unsigned mod_id, func_id; + struct gcov_info *mod_info; + mod_id = get_module_ident_from_func_glob_uid (node->guid); + func_id = get_intra_module_func_id (node->guid); + + mod_info = the_dyn_call_graph.modules[mod_id - 1]; + + fprintf (stderr, "NODE(%llx) module(%s) func(%u)", + (long long)node->guid, + mod_info->mod_info->source_filename, func_id); +} + +/* Dumper function for NODE. M is the module id and F is the function id. */ + +static void +gcov_dump_cgraph_node (struct dyn_cgraph_node *node, unsigned m, unsigned f) +{ + unsigned mod_id, func_id; + struct gcov_info *mod_info; + struct dyn_cgraph_edge *callers; + struct dyn_cgraph_edge *callees; + + mod_id = get_module_ident_from_func_glob_uid (node->guid); + func_id = get_intra_module_func_id (node->guid); + gcc_assert (mod_id == (m + 1) && func_id == f); + + mod_info = the_dyn_call_graph.modules[mod_id - 1]; + + fprintf (stderr, "NODE(%llx) module(%s) func(%x)\n", + (long long) node->guid, + mod_info->mod_info->source_filename, f); + + /* Now dump callers. */ + callers = node->callers; + fprintf (stderr, "\t[CALLERS]\n"); + while (callers != 0) + { + fprintf (stderr,"\t\t[count=%ld] ", (long) callers->count); + gcov_dump_cgraph_node_short (callers->caller); + fprintf (stderr,"\n"); + callers = callers->next_caller; + } + + callees = node->callees; + fprintf (stderr, "\t[CALLEES]\n"); + while (callees != 0) + { + fprintf (stderr,"\t\t[count=%ld] ", (long) callees->count); + gcov_dump_cgraph_node_short (callees->callee); + fprintf (stderr,"\n"); + callees = callees->next_callee; + } +} + +/* Dumper function for NODE. M is the module_ident -1 + and F is the function id. */ + +static void +gcov_dump_cgraph_node_dot (struct dyn_cgraph_node *node, + unsigned m, unsigned f, + gcov_type cutoff_count) +{ + unsigned mod_id, func_id, imp_len = 0, i; + struct gcov_info *mod_info; + const struct dyn_imp_mod **imp_mods; + struct dyn_cgraph_edge *callees; + + mod_id = get_module_ident_from_func_glob_uid (node->guid); + func_id = get_intra_module_func_id (node->guid); + gcc_assert (mod_id == (m + 1) && func_id == f); + + mod_info = the_dyn_call_graph.modules[mod_id - 1]; + + fprintf (stderr, "NODE_%llx[label=\"MODULE\\n(%s)\\n FUNC(%x)\\n", + (long long) node->guid, mod_info->mod_info->source_filename, f); + + imp_mods = gcov_get_sorted_import_module_array (mod_info, &imp_len); + fprintf (stderr, "IMPORTS:\\n"); + if (imp_mods) + { + for (i = 0; i < imp_len; i++) + fprintf (stderr, "%s\\n", imp_mods[i]->imp_mod->mod_info->source_filename); + fprintf (stderr, "\"]\n"); + free (imp_mods); + } + else + fprintf (stderr, "\"]\n"); + + callees = node->callees; + while (callees != 0) + { + if (callees->count >= cutoff_count) + fprintf (stderr, "NODE_%llx -> NODE_%llx[label=%lld color=red]\n", + (long long) node->guid, (long long) callees->callee->guid, + (long long) callees->count); + else + fprintf (stderr, "NODE_%llx -> NODE_%llx[label=%lld color=blue]\n", + (long long) node->guid, (long long) callees->callee->guid, + (long long) callees->count); + callees = callees->next_callee; + } +} + +/* Dump dynamic call graph. CUTOFF_COUNT is the computed hot edge threshold. */ + +static void +gcov_dump_callgraph (gcov_type cutoff_count) +{ + struct gcov_info *gi_ptr; + unsigned m_ix; + int do_dump; + + do_dump = do_cgraph_dump (); + + if (do_dump == 0) + return; + + fprintf (stderr,"digraph dyn_call_graph {\n"); + fprintf (stderr,"node[shape=box]\nsize=\"11,8.5\"\n"); + + for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++) + { + const struct gcov_fn_info *fi_ptr; + unsigned f_ix; + + gi_ptr = the_dyn_call_graph.modules[m_ix]; + if (gi_ptr == NULL) + continue; + + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + struct dyn_cgraph_node *node; + + fi_ptr = gi_ptr->functions[f_ix]; + node = (struct dyn_cgraph_node *) *(pointer_set_find_or_insert + (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident)); + gcc_assert (node); + + /* skip dead functions */ + if (!node->callees && !node->callers) + continue; + + if (do_dump == 1) + gcov_dump_cgraph_node (node, m_ix, fi_ptr->ident); + else + gcov_dump_cgraph_node_dot (node, m_ix, fi_ptr->ident, + cutoff_count); + } + } + fprintf (stderr,"}\n"); +} + +static int +dump_imported_modules_1 (const void *value, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED, + void *data3 ATTRIBUTE_UNUSED) +{ + const struct dyn_imp_mod *d = (const struct dyn_imp_mod*) value; + fprintf (stderr, "%d ", get_module_ident (d->imp_mod)); + return 1; +} + +static int +dump_exported_to_1 (const void *value, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED, + void *data3 ATTRIBUTE_UNUSED) +{ + const struct gcov_info *modu = (const struct gcov_info *) value; + fprintf (stderr, "%d ", get_module_ident (modu)); + return 1; +} + +static void ATTRIBUTE_UNUSED +debug_dump_imported_modules (const struct dyn_pointer_set *p) +{ + fprintf (stderr, "imported: "); + pointer_set_traverse (p, dump_imported_modules_1, 0, 0, 0); + fprintf (stderr, "\n"); +} + +static void ATTRIBUTE_UNUSED +debug_dump_exported_to (const struct dyn_pointer_set *p) +{ + fprintf (stderr, "exported: "); + pointer_set_traverse (p, dump_exported_to_1, 0, 0, 0); + fprintf (stderr, "\n"); +} +#endif diff --git a/gcc-4.9/libgcc/libgcov-driver-system.c b/gcc-4.9/libgcc/libgcov-driver-system.c index 1bb740281..d0bed4975 100644 --- a/gcc-4.9/libgcc/libgcov-driver-system.c +++ b/gcc-4.9/libgcc/libgcov-driver-system.c @@ -134,70 +134,87 @@ allocate_filename_struct (struct gcov_filename_aux *gf) gf->gcov_prefix_strip = gcov_prefix_strip; } -/* Open a gcda file specified by GI_FILENAME. - Return -1 on error. Return 0 on success. */ - static int -gcov_exit_open_gcda_file (struct gcov_info *gi_ptr, struct gcov_filename_aux *gf) +gcov_open_by_filename (char *gi_filename) { - int gcov_prefix_strip; - size_t prefix_length; - char *gi_filename_up; - const char *fname, *s; + 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); + return -1; + } + if (!gcov_open (gi_filename)) + { + fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename); + return -1; + } + } + return 0; +} - gcov_prefix_strip = gf->gcov_prefix_strip; - gi_filename_up = gf->gi_filename_up; - prefix_length = gf->prefix_length; - fname = gi_ptr->filename; +#define GCOV_GET_FILENAME gcov_strip_leading_dirs + +/* Strip GCOV_PREFIX_STRIP levels of leading '/' from FILENAME and + put the result into GI_FILENAME_UP. */ + +static void +gcov_strip_leading_dirs (int prefix_length, int gcov_prefix_strip, + const char *filename, char *gi_filename_up) +{ /* Avoid to add multiple drive letters into combined path. */ - if (prefix_length != 0 && HAS_DRIVE_SPEC(fname)) - fname += 2; + if (prefix_length != 0 && HAS_DRIVE_SPEC(filename)) + filename += 2; /* Build relocated filename, stripping off leading directories from the initial filename if requested. */ if (gcov_prefix_strip > 0) { int level = 0; - - s = fname; + const char *s = filename; if (IS_DIR_SEPARATOR(*s)) - ++s; + ++s; /* Skip selected directory levels. */ for (; (*s != '\0') && (level < gcov_prefix_strip); s++) if (IS_DIR_SEPARATOR(*s)) { - fname = s; + filename = s; level++; } } /* Update complete filename with stripped original. */ - if (prefix_length != 0 && !IS_DIR_SEPARATOR (*fname)) + if (prefix_length != 0 && !IS_DIR_SEPARATOR (*filename)) { /* If prefix is given, add directory separator. */ strcpy (gi_filename_up, "/"); - strcpy (gi_filename_up + 1, fname); + strcpy (gi_filename_up + 1, filename); } else - strcpy (gi_filename_up, fname); + strcpy (gi_filename_up, filename); +} - 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); - return -1; - } - if (!gcov_open (gi_filename)) - { - fprintf (stderr, "profiling:%s:Cannot open\n", gi_filename); - return -1; - } - } - return 0; +/* Open a gcda file specified by GI_FILENAME. + Return -1 on error. Return 0 on success. */ + +static int +gcov_exit_open_gcda_file (struct gcov_info *gi_ptr, struct gcov_filename_aux *gf) +{ + int gcov_prefix_strip; + size_t prefix_length; + char *gi_filename_up; + + gcov_prefix_strip = gf->gcov_prefix_strip; + gi_filename_up = gf->gi_filename_up; + prefix_length = gf->prefix_length; + + GCOV_GET_FILENAME (prefix_length, gcov_prefix_strip, gi_ptr->filename, + gi_filename_up); + + return gcov_open_by_filename (gi_filename); } diff --git a/gcc-4.9/libgcc/libgcov-driver.c b/gcc-4.9/libgcc/libgcov-driver.c index f8abdb1e8..e829fe588 100644 --- a/gcc-4.9/libgcc/libgcov-driver.c +++ b/gcc-4.9/libgcc/libgcov-driver.c @@ -44,6 +44,20 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {} #ifdef L_gcov #include "gcov-io.c" +#ifndef IN_GCOV_TOOL +extern gcov_unsigned_t __gcov_sampling_period; +extern gcov_unsigned_t __gcov_has_sampling; +static int gcov_sampling_period_initialized = 0; +#endif + +/* Unique identifier assigned to each module (object file). */ +static gcov_unsigned_t gcov_cur_module_id = 0; + + +/* Dynamic call graph build and form module groups. */ +void __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN; +void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN; + /* The following functions can be called from outside of this file. */ extern void gcov_clear (void) ATTRIBUTE_HIDDEN; extern void gcov_exit (void) ATTRIBUTE_HIDDEN; @@ -51,6 +65,45 @@ extern void set_gcov_dump_complete (void) ATTRIBUTE_HIDDEN; extern void reset_gcov_dump_complete (void) ATTRIBUTE_HIDDEN; extern int get_gcov_dump_complete (void) ATTRIBUTE_HIDDEN; extern void set_gcov_list (struct gcov_info *) ATTRIBUTE_HIDDEN; +__attribute__((weak)) void __coverage_callback (gcov_type, int); + +#ifndef IN_GCOV_TOOL +/* Create a strong reference to these symbols so that they are + unconditionally pulled into the instrumented binary, even when + the only reference is a weak reference. This is necessary because + we are using weak references to enable references from code that + may not be linked with libgcov. These are the only symbols that + should be accessed via link references from application code! + + A subtlety of the linker is that it will only resolve weak references + defined within archive libraries when there is a strong reference to + something else defined within the same object file. Since these functions + are defined within their own object files, they would not automatically + get resolved. Since there are symbols within the main L_gcov + section that are strongly referenced during -fprofile-generate and + -ftest-coverage builds, these dummy symbols will always need to be + resolved. */ +void (*__gcov_dummy_ref1)(void) = &__gcov_reset; +void (*__gcov_dummy_ref2)(void) = &__gcov_dump; +extern char *__gcov_get_profile_prefix (void); +char *(*__gcov_dummy_ref3)(void) = &__gcov_get_profile_prefix; +extern void __gcov_set_sampling_period (unsigned int period); +char *(*__gcov_dummy_ref4)(void) = &__gcov_set_sampling_period; +extern unsigned int __gcov_sampling_enabled (void); +char *(*__gcov_dummy_ref5)(void) = &__gcov_sampling_enabled; +extern void __gcov_flush (void); +char *(*__gcov_dummy_ref6)(void) = &__gcov_flush; +extern unsigned int __gcov_profiling_for_test_coverage (void); +char *(*__gcov_dummy_ref7)(void) = &__gcov_profiling_for_test_coverage; +#endif + +/* Default callback function for profile instrumentation callback. */ +__attribute__((weak)) void +__coverage_callback (gcov_type funcdef_no __attribute__ ((unused)), + int edge_no __attribute__ ((unused))) +{ + /* nothing */ +} struct gcov_fn_buffer { @@ -67,17 +120,21 @@ struct gcov_summary_buffer }; /* Chain of per-object gcov structures. */ -static struct gcov_info *gcov_list; +extern struct gcov_info *__gcov_list; /* Set the head of gcov_list. */ void set_gcov_list (struct gcov_info *head) { - gcov_list = head; + __gcov_list = head; } /* Size of the longest file name. */ -static size_t gcov_max_filename = 0; +/* We need to expose this static variable when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static +#endif +size_t gcov_max_filename = 0; /* Flag when the profile has already been dumped via __gcov_dump(). */ static int gcov_dump_complete; @@ -191,6 +248,14 @@ fail: return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix); } +/* Determine whether a counter is active. */ + +static inline int +gcov_counter_active (const struct gcov_info *info, unsigned int type) +{ + return (info->merge[type] != 0); +} + /* Add an unsigned value to the current crc */ static gcov_unsigned_t @@ -226,8 +291,12 @@ gcov_version (struct gcov_info *ptr, gcov_unsigned_t version, GCOV_UNSIGNED2STRING (v, version); GCOV_UNSIGNED2STRING (e, GCOV_VERSION); - gcov_error ("profiling:%s:Version mismatch - expected %.4s got %.4s\n", - filename? filename : ptr->filename, e, v); + if (filename) + gcov_error ("profiling:%s:Version mismatch - expected %.4s got %.4s\n", + filename? filename : ptr->filename, e, v); + else + gcov_error ("profiling:Version mismatch - expected %.4s got %.4s\n", e, v); + return 0; } return 1; @@ -276,7 +345,7 @@ gcov_compute_histogram (struct gcov_summary *sum) /* Walk through all the per-object structures and record each of the count values in histogram. */ - for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) + for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next) { if (!gi_ptr->merge[t_ix]) continue; @@ -331,7 +400,7 @@ gcov_exit_compute_summary (struct gcov_summary *this_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) + 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); @@ -518,7 +587,7 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr, goto read_error; } - if (tag) + if (tag && tag != GCOV_TAG_MODULE_INFO) { read_mismatch:; gcov_error ("profiling:%s:Merge mismatch for %s %u\n", @@ -539,7 +608,7 @@ read_error: We will write the file starting from SUMMAY_POS. */ static void -gcov_exit_write_gcda (const struct gcov_info *gi_ptr, +gcov_exit_write_gcda (struct gcov_info *gi_ptr, const struct gcov_summary *prg_p, const gcov_position_t eof_pos, const gcov_position_t summary_pos) @@ -627,6 +696,7 @@ gcov_exit_write_gcda (const struct gcov_info *gi_ptr, fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); } + gi_ptr->eof_pos = gcov_position (); gcov_write_unsigned (0); } @@ -709,6 +779,77 @@ gcov_exit_merge_summary (const struct gcov_info *gi_ptr, struct gcov_summary *pr return 0; } +/* Sort N entries in VALUE_ARRAY in descending order. + Each entry in VALUE_ARRAY has two values. The sorting + is based on the second value. */ + +GCOV_LINKAGE void +gcov_sort_n_vals (gcov_type *value_array, int n) +{ + int j, k; + for (j = 2; j < n; j += 2) + { + gcov_type cur_ent[2]; + cur_ent[0] = value_array[j]; + cur_ent[1] = value_array[j + 1]; + k = j - 2; + while (k >= 0 && value_array[k + 1] < cur_ent[1]) + { + value_array[k + 2] = value_array[k]; + value_array[k + 3] = value_array[k+1]; + k -= 2; + } + value_array[k + 2] = cur_ent[0]; + value_array[k + 3] = cur_ent[1]; + } +} + +/* Sort the profile counters for all indirect call sites. Counters + for each call site are allocated in array COUNTERS. */ + +static void +gcov_sort_icall_topn_counter (const struct gcov_ctr_info *counters) +{ + int i; + gcov_type *values; + int n = counters->num; + gcc_assert (!(n % GCOV_ICALL_TOPN_NCOUNTS)); + + values = counters->values; + + for (i = 0; i < n; i += GCOV_ICALL_TOPN_NCOUNTS) + { + gcov_type *value_array = &values[i + 1]; + gcov_sort_n_vals (value_array, GCOV_ICALL_TOPN_NCOUNTS - 1); + } +} + +static void +gcov_sort_topn_counter_arrays (const struct gcov_info *gi_ptr) +{ + unsigned int i; + int f_ix; + const struct gcov_fn_info *gfi_ptr; + const struct gcov_ctr_info *ci_ptr; + + for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++) + { + gfi_ptr = gi_ptr->functions[f_ix]; + ci_ptr = gfi_ptr->ctrs; + for (i = 0; i < GCOV_COUNTERS; i++) + { + if (!gcov_counter_active (gi_ptr, i)) + continue; + if (i == GCOV_COUNTER_ICALL_TOPNV) + { + gcov_sort_icall_topn_counter (ci_ptr); + break; + } + ci_ptr++; + } + } +} + /* Dump the coverage counts for one gcov_info object. We merge with existing counts when possible, to avoid growing the .da files ad infinitum. We use this program's checksum to make sure we only accumulate whole program @@ -730,6 +871,8 @@ gcov_exit_dump_gcov (struct gcov_info *gi_ptr, struct gcov_filename_aux *gf, fn_buffer = 0; sum_buffer = 0; + gcov_sort_topn_counter_arrays (gi_ptr); + error = gcov_exit_open_gcda_file (gi_ptr, gf); if (error == -1) return; @@ -775,6 +918,79 @@ read_fatal:; gi_filename); } +/* Write imported files (auxiliary modules) for primary module GI_PTR + into file GI_FILENAME. */ + +static void +gcov_write_import_file (char *gi_filename, struct gcov_info *gi_ptr) +{ + char *gi_imports_filename; + const char *gcov_suffix; + FILE *imports_file; + size_t prefix_length, suffix_length; + + gcov_suffix = getenv ("GCOV_IMPORTS_SUFFIX"); + if (!gcov_suffix || !strlen (gcov_suffix)) + gcov_suffix = ".imports"; + suffix_length = strlen (gcov_suffix); + prefix_length = strlen (gi_filename); + gi_imports_filename = (char *) alloca (prefix_length + suffix_length + 1); + memset (gi_imports_filename, 0, prefix_length + suffix_length + 1); + memcpy (gi_imports_filename, gi_filename, prefix_length); + memcpy (gi_imports_filename + prefix_length, gcov_suffix, suffix_length); + imports_file = fopen (gi_imports_filename, "w"); + if (imports_file) + { + const struct dyn_imp_mod **imp_mods; + unsigned i, imp_len; + imp_mods = gcov_get_sorted_import_module_array (gi_ptr, &imp_len); + if (imp_mods) + { + for (i = 0; i < imp_len; i++) + { + fprintf (imports_file, "%s\n", + imp_mods[i]->imp_mod->mod_info->source_filename); + fprintf (imports_file, "%s%s\n", + imp_mods[i]->imp_mod->mod_info->da_filename, GCOV_DATA_SUFFIX); + } + free (imp_mods); + } + fclose (imports_file); + } +} + +static void +gcov_dump_module_info (struct gcov_filename_aux *gf) +{ + struct gcov_info *gi_ptr; + + __gcov_compute_module_groups (); + + /* Now write out module group info. */ + for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next) + { + int error; + + if (gcov_exit_open_gcda_file (gi_ptr, gf) == -1) + continue; + + /* Overwrite the zero word at the of the file. */ + gcov_rewrite (); + gcov_seek (gi_ptr->eof_pos); + + gcov_write_module_infos (gi_ptr); + /* Write the end marker */ + gcov_write_unsigned (0); + gcov_truncate (); + + if ((error = gcov_close ())) + gcov_error (error < 0 ? "profiling:%s:Overflow writing\n" : + "profiling:%s:Error writing\n", + gi_filename); + gcov_write_import_file (gi_filename, gi_ptr); + } + __gcov_finalize_dyn_callgraph (); +} /* Dump all the coverage counts for the program. It first computes program summary and then traverses gcov_list list and dumps the gcov_info @@ -786,6 +1002,7 @@ gcov_exit (void) struct gcov_info *gi_ptr; struct gcov_filename_aux gf; gcov_unsigned_t crc32; + int dump_module_info = 0; struct gcov_summary all_prg; struct gcov_summary this_prg; @@ -802,10 +1019,19 @@ gcov_exit (void) #endif /* Now merge each file. */ - for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) - gcov_exit_dump_gcov (gi_ptr, &gf, crc32, &all_prg, &this_prg); + for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next) + { + gcov_exit_dump_gcov (gi_ptr, &gf, crc32, &all_prg, &this_prg); + + /* The IS_PRIMARY field is overloaded to indicate if this module + is FDO/LIPO. */ + dump_module_info |= gi_ptr->mod_info->is_primary; + } run_accounted = 1; + if (dump_module_info) + gcov_dump_module_info (&gf); + if (gi_filename) free (gi_filename); } @@ -817,7 +1043,7 @@ gcov_clear (void) { const struct gcov_info *gi_ptr; - for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) + for (gi_ptr = __gcov_list; gi_ptr; gi_ptr = gi_ptr->next) { unsigned f_ix; @@ -847,6 +1073,20 @@ gcov_clear (void) void __gcov_init (struct gcov_info *info) { +#ifndef IN_GCOV_TOOL + if (!gcov_sampling_period_initialized) + { + const char* env_value_str = getenv ("GCOV_SAMPLING_PERIOD"); + if (env_value_str) + { + int env_value_int = atoi(env_value_str); + if (env_value_int >= 1) + __gcov_sampling_period = env_value_int; + } + gcov_sampling_period_initialized = 1; + } +#endif + if (!info->version || !info->n_functions) return; if (gcov_version (info, info->version, 0)) @@ -857,11 +1097,17 @@ __gcov_init (struct gcov_info *info) if (filename_length > gcov_max_filename) gcov_max_filename = filename_length; - if (!gcov_list) + /* Assign the module ID (starting at 1). */ + info->mod_info->ident = (++gcov_cur_module_id); + gcc_assert (EXTRACT_MODULE_ID_FROM_GLOBAL_ID (GEN_FUNC_GLOBAL_ID ( + info->mod_info->ident, 0)) + == info->mod_info->ident); + + if (!__gcov_list) atexit (gcov_exit); - info->next = gcov_list; - gcov_list = info; + info->next = __gcov_list; + __gcov_list = info; } info->version = 0; } diff --git a/gcc-4.9/libgcc/libgcov-interface.c b/gcc-4.9/libgcc/libgcov-interface.c index 7f831f2ef..77d839512 100644 --- a/gcc-4.9/libgcc/libgcov-interface.c +++ b/gcc-4.9/libgcc/libgcov-interface.c @@ -115,8 +115,61 @@ __gcov_dump (void) set_gcov_dump_complete (); } +/* Emitted in coverage.c. */ +extern gcov_unsigned_t __gcov_test_coverage; + +unsigned int __gcov_profiling_for_test_coverage (void); + +/* Function that can be called from application to distinguish binaries + instrumented for coverage from those instrumented for profile + optimization (e.g. -fprofile-generate). */ + +unsigned int __gcov_profiling_for_test_coverage (void) +{ + return __gcov_test_coverage; +} + #endif /* L_gcov_dump */ +#ifdef L_gcov_sampling + +/* Emitted in coverage.c. */ + +/* Sampling period. */ +extern gcov_unsigned_t __gcov_sampling_period; +extern gcov_unsigned_t __gcov_has_sampling; +void __gcov_set_sampling_period (unsigned int period); +unsigned int __gcov_sampling_enabled (); +/* Per thread sample counter. */ +__thread gcov_unsigned_t __gcov_sample_counter = 0; + +/* Set sampling period to PERIOD. */ + +void __gcov_set_sampling_period (unsigned int period) +{ + gcc_assert (__gcov_has_sampling); + __gcov_sampling_period = period; +} + +unsigned int __gcov_sampling_enabled () +{ + return __gcov_has_sampling; +} + +#endif + +#ifdef L_gcov_prefix + +/* Profile directory prefix specified to -fprofile-generate=. */ +extern char * __gcov_profile_prefix; + +char *__gcov_get_profile_prefix () +{ + return __gcov_profile_prefix; +} + +#endif + #ifdef L_gcov_fork /* A wrapper for the fork function. Flushes the accumulated profiling data, so diff --git a/gcc-4.9/libgcc/libgcov-merge.c b/gcc-4.9/libgcc/libgcov-merge.c index 488d5426c..09d5769ff 100644 --- a/gcc-4.9/libgcc/libgcov-merge.c +++ b/gcc-4.9/libgcc/libgcov-merge.c @@ -53,7 +53,7 @@ void __gcov_merge_add (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters += gcov_read_counter (); + *counters += gcov_get_counter (); } #endif /* L_gcov_merge_add */ @@ -65,10 +65,135 @@ void __gcov_merge_ior (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters |= gcov_read_counter (); + *counters |= gcov_get_counter_target (); } #endif + +#ifdef L_gcov_merge_dc + +/* Returns 1 if the function global id GID is not valid. */ + +static int +__gcov_is_gid_insane (gcov_type gid) +{ + if (EXTRACT_MODULE_ID_FROM_GLOBAL_ID (gid) == 0 + || EXTRACT_FUNC_ID_FROM_GLOBAL_ID (gid) == 0) + return 1; + return 0; +} + +/* The profile merging function used for merging direct call counts + This function is given array COUNTERS of N_COUNTERS old counters and it + reads the same number of counters from the gcov file. */ + +void +__gcov_merge_dc (gcov_type *counters, unsigned n_counters) +{ + unsigned i; + + gcc_assert (!(n_counters % 2)); + for (i = 0; i < n_counters; i += 2) + { + gcov_type global_id = gcov_read_counter (); + gcov_type call_count = gcov_read_counter (); + + /* Note that global id counter may never have been set if no calls were + made from this call-site. */ + if (counters[i] && global_id) + { + /* TODO race condition requires us do the following correction. */ + if (__gcov_is_gid_insane (counters[i])) + counters[i] = global_id; + else if (__gcov_is_gid_insane (global_id)) + global_id = counters[i]; + + gcc_assert (counters[i] == global_id); + } + else if (global_id) + counters[i] = global_id; + + counters[i + 1] += call_count; + + /* Reset. */ + if (__gcov_is_gid_insane (counters[i])) + counters[i] = counters[i + 1] = 0; + + /* Assert that the invariant (global_id == 0) <==> (call_count == 0) + holds true after merging. */ + if (counters[i] == 0) + counters[i+1] = 0; + if (counters[i + 1] == 0) + counters[i] = 0; + } +} +#endif + + +#ifdef L_gcov_merge_icall_topn +/* The profile merging function used for merging indirect call counts + This function is given array COUNTERS of N_COUNTERS old counters and it + reads the same number of counters from the gcov file. */ + +void +__gcov_merge_icall_topn (gcov_type *counters, unsigned n_counters) +{ + unsigned i, j, k, m; + + gcc_assert (!(n_counters % GCOV_ICALL_TOPN_NCOUNTS)); + for (i = 0; i < n_counters; i += GCOV_ICALL_TOPN_NCOUNTS) + { + gcov_type *value_array = &counters[i + 1]; + unsigned tmp_size = 2 * (GCOV_ICALL_TOPN_NCOUNTS - 1); + gcov_type *tmp_array + = (gcov_type *) alloca (tmp_size * sizeof (gcov_type)); + + for (j = 0; j < tmp_size; j++) + tmp_array[j] = 0; + + for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2) + { + tmp_array[j] = value_array[j]; + tmp_array[j + 1] = value_array [j + 1]; + } + + /* Skip the number_of_eviction entry. */ + gcov_read_counter (); + for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2) + { + int found = 0; + gcov_type global_id = gcov_read_counter (); + gcov_type call_count = gcov_read_counter (); + for (m = 0; m < j; m += 2) + { + if (tmp_array[m] == global_id) + { + found = 1; + tmp_array[m + 1] += call_count; + break; + } + } + if (!found) + { + tmp_array[j] = global_id; + tmp_array[j + 1] = call_count; + j += 2; + } + } + /* Now sort the temp array */ + gcov_sort_n_vals (tmp_array, j); + + /* Now copy back the top half of the temp array */ + for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2) + { + value_array[k] = tmp_array[k]; + value_array[k + 1] = tmp_array[k + 1]; + } + } +} +#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 @@ -81,7 +206,7 @@ __gcov_merge_time_profile (gcov_type *counters, unsigned n_counters) for (i = 0; i < n_counters; i++) { - value = gcov_read_counter (); + value = gcov_get_counter_target (); if (value && (!counters[i] || value < counters[i])) counters[i] = value; @@ -109,9 +234,9 @@ __gcov_merge_single (gcov_type *counters, unsigned n_counters) n_measures = n_counters / 3; for (i = 0; i < n_measures; i++, counters += 3) { - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + value = gcov_get_counter_target (); + counter = gcov_get_counter (); + all = gcov_get_counter (); if (counters[0] == value) counters[1] += counter; @@ -148,10 +273,10 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_counters) n_measures = n_counters / 4; for (i = 0; i < n_measures; i++, counters += 4) { - /* last = */ gcov_read_counter (); - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + /* last = */ gcov_get_counter (); + value = gcov_get_counter_target (); + counter = gcov_get_counter (); + all = gcov_get_counter (); if (counters[1] == value) counters[2] += counter; diff --git a/gcc-4.9/libgcc/libgcov-profiler.c b/gcc-4.9/libgcc/libgcov-profiler.c index 3290bf62c..3057b6157 100644 --- a/gcc-4.9/libgcc/libgcov-profiler.c +++ b/gcc-4.9/libgcc/libgcov-profiler.c @@ -85,12 +85,37 @@ __gcov_one_value_profiler_body (gcov_type *counters, gcov_type value) counters[2]++; } +/* Atomic update version of __gcov_one_value_profile_body(). */ +static inline void +__gcov_one_value_profiler_body_atomic (gcov_type *counters, gcov_type value) +{ + if (value == counters[0]) + GCOV_TYPE_ATOMIC_FETCH_ADD_FN (&counters[1], 1, MEMMODEL_RELAXED); + else if (counters[1] == 0) + { + counters[1] = 1; + counters[0] = value; + } + else + GCOV_TYPE_ATOMIC_FETCH_ADD_FN (&counters[1], -1, MEMMODEL_RELAXED); + GCOV_TYPE_ATOMIC_FETCH_ADD_FN (&counters[2], 1, MEMMODEL_RELAXED); +} + + #ifdef L_gcov_one_value_profiler void __gcov_one_value_profiler (gcov_type *counters, gcov_type value) { __gcov_one_value_profiler_body (counters, value); } + +void +__gcov_one_value_profiler_atomic (gcov_type *counters, gcov_type value) +{ + __gcov_one_value_profiler_body_atomic (counters, value); +} + + #endif #ifdef L_gcov_indirect_call_profiler @@ -128,6 +153,19 @@ __gcov_indirect_call_profiler (gcov_type* counter, gcov_type value, __gcov_one_value_profiler_body (counter, value); } + +/* Atomic update version of __gcov_indirect_call_profiler(). */ +void +__gcov_indirect_call_profiler_atomic (gcov_type* counter, gcov_type value, + void* cur_func, void* callee_func) +{ + if (cur_func == callee_func + || (VTABLE_USES_DESCRIPTORS && callee_func + && *(void **) cur_func == *(void **) callee_func)) + __gcov_one_value_profiler_body_atomic (counter, value); +} + + #endif #ifdef L_gcov_indirect_call_profiler_v2 @@ -174,8 +212,189 @@ __gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func) && *(void **) cur_func == *(void **) __gcov_indirect_call_callee)) __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value); } + +void +__gcov_indirect_call_profiler_atomic_v2 (gcov_type value, void* cur_func) +{ + /* If the C++ virtual tables contain function descriptors then one + function may have multiple descriptors and we need to dereference + the descriptors to see if they point to the same function. */ + if (cur_func == __gcov_indirect_call_callee + || (VTABLE_USES_DESCRIPTORS && __gcov_indirect_call_callee + && *(void **) cur_func == *(void **) __gcov_indirect_call_callee)) + __gcov_one_value_profiler_body_atomic (__gcov_indirect_call_counters, value); +} + #endif +#ifdef L_gcov_indirect_call_topn_profiler +/* Tries to keep track the most frequent N values in the counters where + N is specified by parameter TOPN_VAL. To track top N values, 2*N counter + entries are used. + counter[0] --- the accumative count of the number of times one entry in + in the counters gets evicted/replaced due to limited capacity. + When this value reaches a threshold, the bottom N values are + cleared. + counter[1] through counter[2*N] records the top 2*N values collected so far. + Each value is represented by two entries: count[2*i+1] is the ith value, and + count[2*i+2] is the number of times the value is seen. */ + +static void +__gcov_topn_value_profiler_body (gcov_type *counters, gcov_type value, + gcov_unsigned_t topn_val) +{ + unsigned i, found = 0, have_zero_count = 0; + + gcov_type *entry; + gcov_type *lfu_entry = &counters[1]; + gcov_type *value_array = &counters[1]; + gcov_type *num_eviction = &counters[0]; + + /* There are 2*topn_val values tracked, each value takes two slots in the + counter array */ + for ( i = 0; i < (topn_val << 2); i += 2) + { + entry = &value_array[i]; + if ( entry[0] == value) + { + entry[1]++ ; + found = 1; + break; + } + else if (entry[1] == 0) + { + lfu_entry = entry; + have_zero_count = 1; + } + else if (entry[1] < lfu_entry[1]) + lfu_entry = entry; + } + + if (found) + return; + + /* lfu_entry is either an empty entry or an entry + with lowest count, which will be evicted. */ + lfu_entry[0] = value; + lfu_entry[1] = 1; + +#define GCOV_ICALL_COUNTER_CLEAR_THRESHOLD 3000 + + /* Too many evictions -- time to clear bottom entries to + avoid hot values bumping each other out. */ + if ( !have_zero_count + && ++*num_eviction >= GCOV_ICALL_COUNTER_CLEAR_THRESHOLD) + { + unsigned i, j; + gcov_type *p, minv; + gcov_type* tmp_cnts + = (gcov_type *)alloca (topn_val * sizeof(gcov_type)); + + *num_eviction = 0; + + for ( i = 0; i < topn_val; i++ ) + tmp_cnts[i] = 0; + + /* Find the largest topn_val values from the group of + 2*topn_val values and put them into tmp_cnts. */ + + for ( i = 0; i < 2 * topn_val; i += 2 ) + { + p = 0; + for ( j = 0; j < topn_val; j++ ) + { + if ( !p || tmp_cnts[j] < *p ) + p = &tmp_cnts[j]; + } + if ( value_array[i + 1] > *p ) + *p = value_array[i + 1]; + } + + minv = tmp_cnts[0]; + for ( j = 1; j < topn_val; j++ ) + { + if (tmp_cnts[j] < minv) + minv = tmp_cnts[j]; + } + /* Zero out low value entries */ + for ( i = 0; i < 2 * topn_val; i += 2 ) + { + if (value_array[i + 1] < minv) + { + value_array[i] = 0; + value_array[i + 1] = 0; + } + } + } +} + +#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) +__thread +#endif +gcov_type *__gcov_indirect_call_topn_counters ATTRIBUTE_HIDDEN; + +#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) +__thread +#endif +void *__gcov_indirect_call_topn_callee ATTRIBUTE_HIDDEN; + +#ifdef TARGET_VTABLE_USES_DESCRIPTORS +#define VTABLE_USES_DESCRIPTORS 1 +#else +#define VTABLE_USES_DESCRIPTORS 0 +#endif +void +__gcov_indirect_call_topn_profiler (void *cur_func, + void *cur_module_gcov_info, + gcov_unsigned_t cur_func_id) +{ + void *callee_func = __gcov_indirect_call_topn_callee; + gcov_type *counter = __gcov_indirect_call_topn_counters; + /* If the C++ virtual tables contain function descriptors then one + function may have multiple descriptors and we need to dereference + the descriptors to see if they point to the same function. */ + if (cur_func == callee_func + || (VTABLE_USES_DESCRIPTORS && callee_func + && *(void **) cur_func == *(void **) callee_func)) + { + gcov_type global_id + = ((struct gcov_info *) cur_module_gcov_info)->mod_info->ident; + global_id = GEN_FUNC_GLOBAL_ID (global_id, cur_func_id); + __gcov_topn_value_profiler_body (counter, global_id, GCOV_ICALL_TOPN_VAL); + __gcov_indirect_call_topn_callee = 0; + } +} + +#endif + +#ifdef L_gcov_direct_call_profiler +#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) +__thread +#endif +gcov_type *__gcov_direct_call_counters ATTRIBUTE_HIDDEN; +#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS) +__thread +#endif +void *__gcov_direct_call_callee ATTRIBUTE_HIDDEN; +/* Direct call profiler. */ +void +__gcov_direct_call_profiler (void *cur_func, + void *cur_module_gcov_info, + gcov_unsigned_t cur_func_id) +{ + if (cur_func == __gcov_direct_call_callee) + { + gcov_type global_id + = ((struct gcov_info *) cur_module_gcov_info)->mod_info->ident; + global_id = GEN_FUNC_GLOBAL_ID (global_id, cur_func_id); + __gcov_direct_call_counters[0] = global_id; + __gcov_direct_call_counters[1]++; + __gcov_direct_call_callee = 0; + } +} +#endif + + #ifdef L_gcov_time_profiler /* Counter for first visit of each function. */ diff --git a/gcc-4.9/libgcc/libgcov-util.c b/gcc-4.9/libgcc/libgcov-util.c new file mode 100644 index 000000000..d1401b54d --- /dev/null +++ b/gcc-4.9/libgcc/libgcov-util.c @@ -0,0 +1,1162 @@ +/* Utility functions for reading gcda files into in-memory + gcov_info structures and offline profile processing. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu . + +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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#define IN_GCOV_TOOL 1 + +#include "libgcov.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "demangle.h" + +/* Borrowed from basic-block.h. */ +#define RDIV(X,Y) (((X) + (Y) / 2) / (Y)) + +extern gcov_position_t gcov_position(); +extern int gcov_is_error(); +extern size_t gcov_max_filename; + +/* Verbose mode for debug. */ +static int verbose; + +/* Set verbose flag. */ +void gcov_set_verbose (void) +{ + verbose = 1; +} + +/* The following part is to read Gcda and reconstruct GCOV_INFO. */ + +#include "obstack.h" +#include +#if !defined (_WIN32) +#include +#else +#include +#endif + +static void tag_function (unsigned, unsigned); +static void tag_blocks (unsigned, unsigned); +static void tag_arcs (unsigned, unsigned); +static void tag_lines (unsigned, unsigned); +static void tag_counters (unsigned, unsigned); +static void tag_summary (unsigned, unsigned); +static void tag_module_info (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 points to the module being processed. */ +static struct gcov_module_info *curr_module_info; +/* 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. */ +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) __gcov_merge ## FN_TYPE, +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; +#undef DEF_GCOV_COUNTER + +/* Set the ctrs field in gcov_fn_info object 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); +} + +/* For each tag in gcda file, we have an entry here. + TAG is the tag value; NAME is the tag name; and + PROC is the handler function. */ + +typedef struct tag_format +{ + unsigned tag; + char const *name; + void (*proc) (unsigned, unsigned); +} tag_format_t; + +/* Handler table for various Tags. */ + +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}, + {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info}, + {0, NULL, NULL} +}; + +/* Handler for reading function tag. */ + +static void +tag_function (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_ptr_grow (&fn_info, curr_fn_info); + } + + /* 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) + fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident); +} + +/* Handler for reading block tag. */ + +static void +tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading flow arc tag. */ + +static void +tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading line tag. */ + +static void +tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading counters array tag with value as TAG and length of LENGTH. */ + +static void +tag_counters (unsigned tag, unsigned length) +{ + 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 (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + struct gcov_summary summary; + + gcov_read_summary (&summary); +} + +/* This function is called at the end of reading a gcda file. + It flushes the contents in curr_fn_info to gcov_info object OBJ_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]; + } + + obj_info->mod_info = curr_module_info; +} + +extern void gcov_read_module_info (struct gcov_module_info *mod_info, + gcov_unsigned_t len); + +/* Substitute string is of this format: + old_sub1:new_sub1[,old_sub2:new_sub2] + Note that we only apply the substutution ONE time, for the first match. */ + +static const char *substitute_string; + +/* A global function to set the substitute string. */ + +void +lipo_set_substitute_string (const char *str) +{ + char *sub_dup = xstrdup (str); + char *cur_sub = sub_dup; + + /* First check if the str is in the right form. + Dup the string and split it into tokens with + ',' and ':' as the delimiters. */ + do + { + char *new_str; + char *next = strchr (cur_sub, ','); + if (next) + *next++ = '\0'; + new_str = strchr (cur_sub, ':'); + if (!new_str) + { + fprintf (stderr, "Warning: Skip invalid substibution string:%s\n", + str); + free (sub_dup); + return; + } + *new_str++ = '\0'; + cur_sub = next; + } while (cur_sub); + + free (sub_dup); + substitute_string = str; +} + +/* Replace the first occurance of CUT_STR to NEW_STR in INPUT_STR. */ + +static char * +lipo_process_substitute_string_1 (char *input_str, + const char *cur_str, + const char *new_str) +{ + char *p; + + if (!input_str || !cur_str || !new_str) + return input_str; + + if ((p = strstr (input_str, cur_str)) != NULL) + { + char *t; + + if (verbose) + printf ("Substitute: %s \n", input_str); + t = (char*) xmalloc (strlen (input_str) + 1 + + strlen (new_str) - strlen (cur_str)); + *p = 0; + + strcpy (t, input_str); + strcat (t, new_str); + strcat (t, p + strlen (cur_str)); + if (verbose) + printf (" --> %s\n", t); + return t; + } + + return input_str; +} + +/* Parse the substitute string and apply to the INPUT_STR. */ + +static char * +lipo_process_substitute_string (char *input_str) +{ + char *sub_dup, *cur_sub, *ret; + + if (substitute_string == NULL) + return input_str; + + sub_dup = xstrdup (substitute_string); + cur_sub = sub_dup; + ret = input_str; + + /* Dup the string and split it into tokens with + ',' and ':' as the delimiters. */ + do + { + char *new_str, *new_input; + char *next = strchr (cur_sub, ','); + if (next) + *next++ = '\0'; + new_str = strchr (cur_sub, ':'); + gcc_assert (new_str); + *new_str++ = '\0'; + new_input = ret; + ret = lipo_process_substitute_string_1 (new_input, cur_sub, new_str); + if (ret != new_input) + free (new_input); + cur_sub = next; + } while (cur_sub); + + free (sub_dup); + return ret; +} + +/* This function reads module_info from a gcda file. */ + +static void +tag_module_info (unsigned tag ATTRIBUTE_UNUSED, unsigned length) +{ + struct gcov_module_info* mod_info; + + mod_info = (struct gcov_module_info *) + xmalloc ((length + 2) * sizeof (gcov_unsigned_t)); + + gcov_read_module_info (mod_info, length); + + if (mod_info->is_primary) + { + mod_info->da_filename = + lipo_process_substitute_string (mod_info->da_filename); + curr_module_info = mod_info; + } + else + free (mod_info); +} + +/* Read the content of a gcda file FILENAME, 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, len; + char *str_dup; + + for (i=0; i< GCOV_COUNTERS; i++) + k_ctrs_mask[i] = 0; + k_ctrs_types = 0; + + if (!gcov_open (filename)) + { + fnotice (stderr, "%s:cannot open\n", filename); + return NULL; + } + + /* Read magic. */ + magic = gcov_read_unsigned (); + if (magic != GCOV_DATA_MAGIC) + { + fnotice (stderr, "%s:not a gcov data file\n", filename); + gcov_close (); + return NULL; + } + + /* Read version. */ + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + fnotice (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; + curr_module_info = 0; + + str_dup = lipo_process_substitute_string (xstrdup (filename)); + obj_info->filename = str_dup; + + if ((len = strlen (str_dup)) > 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)) + { + warning (0, "%s:tag `%x' 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)) + warning (0, "%s:tag `%x' is incorrectly nested\n", + filename, tag); + } + depth = tag_depth; + tags[depth - 1] = tag; + } + + if (format->proc) + { + unsigned long actual_length; + + (*format->proc) (tag, length); + + actual_length = gcov_position () - base; + if (actual_length > length) + warning (0, "%s:record size mismatch %lu bytes overread\n", + filename, actual_length - length); + else if (length > actual_length) + warning (0, "%s:record size mismatch %lu bytes unread\n", + filename, length - actual_length); + } + + gcov_sync (base, length); + if ((error = gcov_is_error ())) + { + warning (0, error < 0 ? "%s:counter overflow at %lu\n" : + "%s:read error at %lu\n", filename, + (long unsigned) gcov_position ()); + break; + } + } + + read_gcda_finalize (obj_info); + gcov_close (); + + return obj_info; +} + +extern int is_module_available (const char *, unsigned *, int); + +/* If only use the modules in the modu_list. */ + +static int flag_use_modu_list; + +/* Set to use only the modules in the modu_list file. */ + +void +set_use_modu_list (void) +{ + flag_use_modu_list = 1; +} + +/* Handler to open and read a gcda file FILENAME. */ + +static int +read_file_handler (const char *filename) +{ + int filename_len; + int suffix_len; + struct gcov_info *obj_info; + + filename_len = strlen (filename); + suffix_len = strlen (GCOV_DATA_SUFFIX); + + if (filename_len <= suffix_len) + return 0; + + if (strcmp(filename + filename_len - suffix_len, GCOV_DATA_SUFFIX)) + return 0; + + if (verbose) + fnotice (stderr, "reading file: %s\n", filename); + + obj_info = read_gcda_file (filename); + if (!obj_info) + return 0; + + if (obj_info->mod_info) + { + unsigned mod_id = obj_info->mod_info->ident; + int create = (flag_use_modu_list ? 0 : 1); + + if (!is_module_available (obj_info->mod_info->source_filename, + &mod_id, create)) + { + if (verbose) + fprintf (stderr, "warning: module %s (%d) is not avail\n", + obj_info->mod_info->source_filename, mod_id); + return 0; + } + } + + obj_info->next = gcov_info_head; + gcov_info_head = obj_info; + + return 0; +} + + +#if !defined(_WIN32) +/* This will be called by ftw(). It opens and reads a gcda file FILENAME. + 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) +{ + /* Only read regular files. */ + if (type != FTW_F) + return 0; + + return read_file_handler (filename); +} + +#else /* _WIN32 */ + +/* Funtion to find all the gcda files recursively in DIR. */ +static void +myftw (char *dir, char* pattern, int (*handler)(const char *)) +{ + char buffer[MAX_PATH]; + WIN32_FIND_DATA filedata; + HANDLE ret; + + /* Process the subdirectories. */ + sprintf (buffer, "%s\\*", dir); + ret = FindFirstFile (buffer, &filedata); + if(ret != INVALID_HANDLE_VALUE) + { + do + { + if(filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (filedata.cFileName[0] == '.') + continue; + sprintf (buffer, "%s\\%s", dir, filedata.cFileName); + myftw (buffer, pattern, handler); + } + } while(FindNextFile (ret, &filedata)); + FindClose(ret); + } + + /* Find the matching files. */ + sprintf (buffer, "%s\\%s", dir, pattern); + ret = FindFirstFile (buffer, &filedata); + if(ret != INVALID_HANDLE_VALUE) + { + do + { + if(!(filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + /* Apply action. */ + (*handler) (buffer); + } + } while(FindNextFile (ret, &filedata)); + FindClose (ret); + } +} +#endif + +/* Source profile directory name. */ + +static const char *source_profile_dir; + +/* Return Source profile directory name. */ + +const char * +get_source_profile_dir (void) +{ + return source_profile_dir; +} + +/* Initializer for reading a profile dir. */ + +static inline void +read_profile_dir_init (void) +{ + gcov_info_head = 0; +} + +/* Driver for read a profile directory and convert into gcov_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) + { + fnotice (stderr, "cannot access directory %s\n", dir_name); + return NULL; + } + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (dir_name); + if (ret !=0) + { + fnotice (stderr, "%s is not a directory\n", dir_name); + return NULL; + } + source_profile_dir = getcwd (NULL, 0); + +#if !defined(_WIN32) + ftw (".", ftw_read_file, 50); +#else + myftw (".", "*.gcda",read_file_handler); +#endif + ret = chdir (pwd); + free (pwd); + + + /* gcov_max_filename is defined in libgcov.c that records the + 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;; +} + +/* This part of the code is to merge profile counters. These + variables are set in merge_wrapper and to be used by + global function gcov_read_counter_mem() and gcov_get_merge_weight. */ + +/* We save the counter value address to this variable. */ +static gcov_type *gcov_value_buf; + +/* The number of counter values to be read by current merging. */ +static gcov_unsigned_t gcov_value_buf_size; + +/* The index of counter values being read. */ +static gcov_unsigned_t gcov_value_buf_pos; + +/* The weight of current merging. */ +static unsigned gcov_merge_weight; + +/* Read a counter value from gcov_value_buf array. */ + +gcov_type +gcov_read_counter_mem (void) +{ + gcov_type ret; + gcc_assert (gcov_value_buf_pos < gcov_value_buf_size); + ret = *(gcov_value_buf + gcov_value_buf_pos); + ++gcov_value_buf_pos; + return ret; +} + +/* Return the recorded merge weight. */ + +unsigned +gcov_get_merge_weight (void) +{ + return gcov_merge_weight; +} + +/* A wrapper function for merge functions. It sets up the + value buffer and weights and then calls the merge function. */ + +static void +merge_wrapper (gcov_merge_fn f, gcov_type *v1, gcov_unsigned_t n, + gcov_type *v2, unsigned w) +{ + gcov_value_buf = v2; + gcov_value_buf_pos = 0; + gcov_value_buf_size = n; + gcov_merge_weight = w; + (*f) (v1, n); +} + +/* 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 multiply by + 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 by 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) + { + fnotice (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); + merge_wrapper (merge1, ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w); + ci_ptr1++; + ci_ptr2++; + } + } + + return has_mismatch; +} + +/* Find and return the match gcov_info object for INFO from ARRAY. + SIZE is the length of ARRAY. + Return NULL if there is no match. */ + +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; + /* For LIPO, it's easy as we can just match the module_id. */ + if (gi_ptr->mod_info && info->mod_info) + { + if (gi_ptr->mod_info->ident == info->mod_info->ident) + { + ret = gi_ptr; + array[i] = 0; + break; + } + } + else /* For FDO, we have to match the name. This can be expensive. + Maybe we should use hash here. */ + if (!strcmp (gi_ptr->filename, info->filename)) + { + ret = gi_ptr; + array[i] = 0; + break; + } + } + + if (ret && ret->n_functions != info->n_functions) + { + fnotice (stderr, "mismatched profiles in %s (%d functions" + " vs %d functions)\n", + ret->filename, + ret->n_functions, + info->n_functions); + ret = NULL; + } + return ret; +} + +/* Merge the list of gcov_info objects from SRC_PROFILE to TGT_PROFILE. + 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); + } + + /* 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; + } + + return 0; +} + +typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*); + +/* Performing FN upon arc counters. */ + +static void +__gcov_add_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = fn(val, data1, data2); + } +} + +/* Performing FN upon ior counters. */ + +static void +__gcov_ior_counter_op (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + counter_op_fn fn ATTRIBUTE_UNUSED, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +/* Performing FN upon time-profile counters. */ + +static void +__gcov_time_profile_counter_op (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + counter_op_fn fn ATTRIBUTE_UNUSED, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +/* Performaing FN upon delta counters. */ + +static void +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + 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] = fn (counters[2], data1, data2); + counters[3] = fn (counters[3], data1, data2); + } +} + +/* Performing FN upon single counters. */ + +static void +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + 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] = fn (counters[1], data1, data2); + counters[2] = fn (counters[2], data1, data2); + } +} + +/* Performing FN upon indirect-call profile counters. */ + +static void +__gcov_icall_topn_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i; + + gcc_assert (!(n_counters % GCOV_ICALL_TOPN_NCOUNTS)); + for (i = 0; i < n_counters; i += GCOV_ICALL_TOPN_NCOUNTS) + { + unsigned j; + gcov_type *value_array = &counters[i + 1]; + + for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2) + value_array[j + 1] = fn (value_array[j + 1], data1, data2); + } +} + +/* Performing FN upon direct-call profile counters. */ + +static void +__gcov_dc_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i; + + gcc_assert (!(n_counters % 2)); + for (i = 0; i < n_counters; i += 2) + counters[i + 1] = fn (counters[i + 1], data1, data2); +} + + +/* Scaling the counter value V by multiplying *(float*) DATA1. */ + +static gcov_type +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED) +{ + float f = *(float *) data1; + return (gcov_type) (v * f); +} + +/* Scaling the counter value V by multiplying DATA2/DATA1. */ + +static gcov_type +int_scale (gcov_type v, void *data1, void *data2) +{ + int n = *(int *) data1; + int d = *(int *) data2; + return (gcov_type) ( RDIV (v,d) * n); +} + +/* Type of function used to process counters. */ +typedef void (*gcov_counter_fn) (gcov_type *, gcov_unsigned_t, + counter_op_fn, void *, void *); + +/* Function array to process profile counters. */ +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) \ + __gcov ## FN_TYPE ## _counter_op, +static gcov_counter_fn ctr_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; +#undef DEF_GCOV_COUNTER + +/* Driver for scaling profile counters. */ + +int +gcov_profile_scale (struct gcov_info *profile, float scale_factor, int n, int d) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d); + + /* Scaling 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; + if (d == 0) + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + fp_scale, &scale_factor, NULL); + else + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + int_scale, &n, &d); + ci_ptr++; + } + } + + return 0; +} + +/* Driver to 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; + + /* Find the largest 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 !defined (_WIN32) + if (verbose) + fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val); +#endif + + return gcov_profile_scale (profile, scale_factor, 0, 0); +} diff --git a/gcc-4.9/libgcc/libgcov.h b/gcc-4.9/libgcc/libgcov.h index 1e831de69..25534acd5 100644 --- a/gcc-4.9/libgcc/libgcov.h +++ b/gcc-4.9/libgcc/libgcov.h @@ -32,6 +32,13 @@ #ifndef xcalloc #define xcalloc calloc #endif +#ifndef xrealloc +#define xrealloc realloc +#endif + +#ifndef IN_GCOV_TOOL +/* About the target. */ +/* This path will be used by libgcov runtime. */ #include "tconfig.h" #include "tsystem.h" @@ -39,38 +46,61 @@ #include "tm.h" #include "libgcc_tm.h" +#undef FUNC_ID_WIDTH +#undef FUNC_ID_MASK + #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))); +#define FUNC_ID_WIDTH 32 +#define FUNC_ID_MASK ((1ll << FUNC_ID_WIDTH) - 1) #else typedef signed gcov_type __attribute__ ((mode (SI))); typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); +#define FUNC_ID_WIDTH 16 +#define FUNC_ID_MASK ((1 << FUNC_ID_WIDTH) - 1) #endif -#else +#else /* BITS_PER_UNIT != 8 */ #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))); +#define FUNC_ID_WIDTH 32 +#define FUNC_ID_MASK ((1ll << FUNC_ID_WIDTH) - 1) #else typedef signed gcov_type __attribute__ ((mode (HI))); typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); +#define FUNC_ID_WIDTH 16 +#define FUNC_ID_MASK ((1 << FUNC_ID_WIDTH) - 1) #endif -#else +#else /* BITS_PER_UNIT != 16 */ 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))); +#define FUNC_ID_WIDTH 32 +#define FUNC_ID_MASK ((1ll << FUNC_ID_WIDTH) - 1) #else typedef signed gcov_type __attribute__ ((mode (QI))); typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI))); +#define FUNC_ID_WIDTH 16 +#define FUNC_ID_MASK ((1 << FUNC_ID_WIDTH) - 1) #endif -#endif +#endif /* BITS_PER_UNIT == 16 */ +#endif /* BITS_PER_UNIT == 8 */ + +#if LONG_LONG_TYPE_SIZE > 32 +#define GCOV_TYPE_ATOMIC_FETCH_ADD_FN __atomic_fetch_add_8 +#define GCOV_TYPE_ATOMIC_FETCH_ADD BUILT_IN_ATOMIC_FETCH_ADD_8 +#else +#define GCOV_TYPE_ATOMIC_FETCH_ADD_FN __atomic_fetch_add_4 +#define GCOV_TYPE_ATOMIC_FETCH_ADD BUILT_IN_ATOMIC_FETCH_ADD_4 #endif #if defined (TARGET_POSIX_IO) @@ -79,6 +109,58 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI))); #define GCOV_LOCKED 0 #endif +#else /* IN_GCOV_TOOL */ +/* About the host. */ +/* This path will be compiled for the host and linked into + gcov-tool binary. */ + +#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 + +/* xur??? */ +#define FUNC_ID_WIDTH 32 +#define FUNC_ID_MASK ((1ll << FUNC_ID_WIDTH) - 1) + +/* Some Macros specific to gcov-tool. */ + +#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 +#define L_gcov_merge_icall_topn 1 +#define L_gcov_merge_dc 1 + +/* Make certian internal functions/variables in libgcov available for + gcov-tool access. */ +#define GCOV_TOOL_LINKAGE + +extern gcov_type gcov_read_counter_mem (); +extern unsigned gcov_get_merge_weight (); + +#endif /* !IN_GCOV_TOOL */ + +#undef EXTRACT_MODULE_ID_FROM_GLOBAL_ID +#undef EXTRACT_FUNC_ID_FROM_GLOBAL_ID +#undef GEN_FUNC_GLOBAL_ID +#define EXTRACT_MODULE_ID_FROM_GLOBAL_ID(gid) \ + (gcov_unsigned_t)(((gid) >> FUNC_ID_WIDTH) & FUNC_ID_MASK) +#define EXTRACT_FUNC_ID_FROM_GLOBAL_ID(gid) \ + (gcov_unsigned_t)((gid) & FUNC_ID_MASK) +#define GEN_FUNC_GLOBAL_ID(m,f) ((((gcov_type) (m)) << FUNC_ID_WIDTH) | (f)) + #if defined(inhibit_libc) #define IN_LIBGCOV (-1) #else @@ -98,13 +180,17 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI))); #define gcov_position __gcov_position #define gcov_seek __gcov_seek #define gcov_rewrite __gcov_rewrite +#define gcov_truncate __gcov_truncate #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_write_module_info __gcov_write_module_info #define gcov_read_unsigned __gcov_read_unsigned #define gcov_read_counter __gcov_read_counter #define gcov_read_summary __gcov_read_summary +#define gcov_read_module_info __gcov_read_module_info +#define gcov_sort_n_vals __gcov_sort_n_vals /* Poison these, so they don't accidentally slip in. */ #pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length @@ -120,12 +206,11 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI))); /* 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. */ + gcov_unsigned_t num; /* number of counters. */ + gcov_type *values; /* their values. */ }; /* Information about a single function. This uses the trailing array @@ -136,11 +221,11 @@ struct gcov_ctr_info 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 */ + 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[1]; /* instrumented counters */ }; /* Type of function used to merge counters. */ @@ -149,38 +234,50 @@ 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_unsigned_t version; /* expected version number */ + struct gcov_module_info *mod_info; /* addtional module info. */ + struct gcov_info *next; /* link to next, used by libgcov */ + gcov_unsigned_t stamp; /* uniquifying time stamp */ + const char *filename; /* output file name */ + gcov_unsigned_t eof_pos; /* end position of profile data */ gcov_merge_fn merge[GCOV_COUNTERS]; /* merge functions (null for - unused) */ - - unsigned n_functions; /* number of functions */ + unused) */ + + unsigned n_functions; /* number of functions */ + +#ifndef IN_GCOV_TOOL const struct gcov_fn_info *const *functions; /* pointer to pointers - to function information */ + to function information */ +#else + const struct gcov_fn_info **functions; +#endif /* !IN_GCOV_TOOL */ +}; + +/* Information about a single imported module. */ +struct dyn_imp_mod +{ + const struct gcov_info *imp_mod; + double weight; }; /* Register a new object file module. */ extern void __gcov_init (struct gcov_info *) ATTRIBUTE_HIDDEN; +/* Set sampling rate to RATE. */ +extern void __gcov_set_sampling_rate (unsigned int rate); + /* 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 select the minimum valid counter value. */ -extern void __gcov_merge_time_profile (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; - /* The merge function to choose the most common value. */ extern void __gcov_merge_single (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; @@ -191,6 +288,14 @@ 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; +/* The merge function used for direct call counters. */ +extern void __gcov_merge_dc (gcov_type *, unsigned) ATTRIBUTE_HIDDEN; + +/* The merge function used for indirect call counters. */ +extern void __gcov_merge_icall_topn (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); @@ -198,9 +303,12 @@ 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_time_profiler (gcov_type *); +extern void __gcov_indirect_call_topn_profiler (void *, void *, gcov_unsigned_t) ATTRIBUTE_HIDDEN; +extern void __gcov_direct_call_profiler (void *, void *, gcov_unsigned_t) ATTRIBUTE_HIDDEN; extern void __gcov_average_profiler (gcov_type *, gcov_type); extern void __gcov_ior_profiler (gcov_type *, gcov_type); +extern void __gcov_sort_n_vals (gcov_type *value_array, int n); +extern void __gcov_time_profiler (gcov_type *); #ifndef inhibit_libc /* The wrappers around some library functions.. */ @@ -213,6 +321,7 @@ 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; @@ -222,8 +331,54 @@ 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; +GCOV_LINKAGE void gcov_truncate (void) ATTRIBUTE_HIDDEN; +GCOV_LINKAGE void gcov_write_module_infos (struct gcov_info *mod_info) + ATTRIBUTE_HIDDEN; +GCOV_LINKAGE const struct dyn_imp_mod ** +gcov_get_sorted_import_module_array (struct gcov_info *mod_info, unsigned *len) + ATTRIBUTE_HIDDEN; GCOV_LINKAGE inline void gcov_rewrite (void); +/* "Counts" stored in gcda files can be a real counter value, or + an target address. When differentiate these two types because + when manipulating counts, we should only change real counter values, + rather target addresses. */ + +static inline gcov_type +gcov_get_counter (void) +{ +#ifndef IN_GCOV_TOOL + /* This version is for reading count values in libgcov runtime: + we read from gcda files. */ + + return gcov_read_counter (); +#else + /* This version is for gcov-tool. We read the value from memory and + multiply it by the merge weight. */ + + return gcov_read_counter_mem () * gcov_get_merge_weight (); +#endif +} + +/* Similar function as gcov_get_counter(), but handles target address + counters. */ + +static inline gcov_type +gcov_get_counter_target (void) +{ +#ifndef IN_GCOV_TOOL + /* This version is for reading count target values in libgcov runtime: + we read from gcda files. */ + + return gcov_read_counter (); +#else + /* This version is for gcov-tool. We read the value from memory and we do NOT + multiply it by the merge weight. */ + + return gcov_read_counter_mem (); +#endif +} + #endif /* !inhibit_libc */ #endif /* GCC_LIBGCOV_H */ diff --git a/gcc-4.9/libgcc/pmu-profile.c b/gcc-4.9/libgcc/pmu-profile.c new file mode 100644 index 000000000..14a5132e4 --- /dev/null +++ b/gcc-4.9/libgcc/pmu-profile.c @@ -0,0 +1,1552 @@ +/* Performance monitoring unit (PMU) profiler. If available, use an + external tool to collect hardware performance counter data and + write it in the .gcda files. + + Copyright (C) 2011. Free Software Foundation, Inc. + Contributed by Sharad Singhai . + +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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "coretypes.h" +#include "tm.h" +#if (defined (__x86_64__) || defined (__i386__)) +#include "cpuid.h" +#endif + +#if defined(inhibit_libc) +#define IN_LIBGCOV (-1) +#else +#include +#include +#define IN_LIBGCOV 1 + #if defined(L_gcov) + #define GCOV_LINKAGE /* nothing */ + #endif +#endif +#include "gcov-io.h" +#ifdef TARGET_POSIX_IO + #include + #include + #include + #include +#endif + +#if defined(inhibit_libc) +#else +#include +#include +#include +#include + +#include +#include + +#define XNEWVEC(type,ne) (type *)calloc((ne),sizeof(type)) +#define XNEW(type) (type *)malloc(sizeof(type)) +#define XDELETEVEC(p) free(p) +#define XDELETE(p) free(p) + +#define PFMON_CMD "/usr/bin/pfmon" +#define ADDR2LINE_CMD "/usr/bin/addr2line" +#define PMU_TOOL_MAX_ARGS (20) +static char default_addr2line[] = "??:0"; +static const char pfmon_ll_header[] = "# counts %self %cum " + "<10 <32 <64 <256 <1024 >=1024 %wself " + "code addr symbol\n"; +static const char pfmon_bm_header[] = + "# counts %self %cum code addr symbol\n"; + +const char *pfmon_intel_ll_args[PMU_TOOL_MAX_ARGS] = { + PFMON_CMD, + "--aggregate-results", + "--follow-all", + "--with-header", + "--smpl-module=pebs-ll", + "--ld-lat-threshold=4", + "--pebs-ll-dcmiss-code", + "--resolve-addresses", + "-emem_inst_retired:LATENCY_ABOVE_THRESHOLD", + "--long-smpl-periods=10000", + 0 /* terminating NULL must be present */ +}; + +const char *pfmon_amd_ll_args[PMU_TOOL_MAX_ARGS] = { + PFMON_CMD, + "--aggregate-results", + "--follow-all", + "-uk", + "--with-header", + "--smpl-module=ibs", + "--resolve-addresses", + "-eibsop_event:uops", + "--ibs-dcmiss-code", + "--long-smpl-periods=0xffff0", + 0 /* terminating NULL must be present */ +}; + +const char *pfmon_intel_brm_args[PMU_TOOL_MAX_ARGS] = { + PFMON_CMD, + "--aggregate-results", + "--follow-all", + "--with-header", + "--resolve-addresses", + "-eMISPREDICTED_BRANCH_RETIRED", + "--long-smpl-periods=10000", + 0 /* terminating NULL must be present */ +}; + +const char *pfmon_amd_brm_args[PMU_TOOL_MAX_ARGS] = { + PFMON_CMD, + "--aggregate-results", + "--follow-all", + "--with-header", + "--resolve-addresses", + "-eRETIRED_MISPREDICTED_BRANCH_INSTRUCTIONS", + "--long-smpl-periods=10000", + 0 /* terminating NULL must be present */ +}; + +const char *addr2line_args[PMU_TOOL_MAX_ARGS] = { + ADDR2LINE_CMD, + "-e", + 0 /* terminating NULL must be present */ +}; + + +enum pmu_tool_type +{ + PTT_PFMON, + PTT_LAST +}; + +enum pmu_event_type +{ + PET_INTEL_LOAD_LATENCY, + PET_AMD_LOAD_LATENCY, + PET_INTEL_BRANCH_MISPREDICT, + PET_AMD_BRANCH_MISPREDICT, + PET_LAST +}; + +typedef struct pmu_tool_fns { + const char *name; /* name of the pmu tool */ + /* pmu tool commandline argument. */ + const char **arg_array; + /* Initialize pmu module. */ + void *(*init_pmu_module) (void); + /* Start profililing. */ + void (*start_pmu_module) (pid_t ppid, char *tmpfile, const char **args); + /* Stop profililing. */ + void (*stop_pmu_module) (void); + /* How to parse the output generated by the PMU tool. */ + int (*parse_pmu_output) (char *filename, void *pmu_data); + /* How to write parsed pmu data into gcda file. */ + void (*gcov_write_pmu_data) (void *data); + /* How to cleanup any data structure created during parsing. */ + void (*cleanup_pmu_data) (void *data); + /* How to initialize symbolizer for the PPID. */ + int (*start_symbolizer) (pid_t ppid); + void (*end_symbolizer) (void); + char *(*symbolize) (void *addr); +} pmu_tool_fns; + +enum pmu_state +{ + PMU_NONE, /* Not configurated at all. */ + PMU_INITIALIZED, /* Configured and initialized. */ + PMU_ERROR, /* Configuration error. Cannot recover. */ + PMU_ON, /* Currently profiling. */ + PMU_OFF /* Currently stopped, but can be restarted. */ +}; + +enum cpu_vendor_signature +{ + CPU_VENDOR_UKNOWN = 0, + CPU_VENDOR_INTEL = 0x756e6547, /* Genu */ + CPU_VENDOR_AMD = 0x68747541 /* Auth */ +}; + +/* Info about pmu tool during the run time. */ +struct pmu_tool_info +{ + /* Current pmu tool. */ + enum pmu_tool_type tool; + /* Current event. */ + enum pmu_event_type event; + /* filename for storing the pmu profile. */ + char *pmu_profile_filename; + /* Intermediate file where the tool stores the PMU data. */ + char *raw_pmu_profile_filename; + /* Where PMU tool's stderr should be stored. */ + char *tool_stderr_filename; + enum pmu_state pmu_profiling_state; + enum cpu_vendor_signature cpu_vendor; /* as discovered by cpuid */ + pid_t pmu_tool_pid; /* process id of the pmu tool */ + pid_t symbolizer_pid; /* process id of the symbolizer */ + int symbolizer_to_pipefd[2]; /* pipe for writing to the symbolizer */ + int symbolizer_from_pipefd[2]; /* pipe for reading from the symbolizer */ + void *pmu_data; /* an opaque pointer for the tool to store pmu data */ + int verbose; /* turn on additional debugging */ + unsigned top_n_address; /* how many addresses to symbolize */ + pmu_tool_fns *tool_details; /* list of functions how to start/stop/parse */ +}; + +/* Global struct for recordkeeping. */ +static struct pmu_tool_info *the_pmu_tool_info; + +/* Additional info is printed if these are non-zero. */ +static int tool_debug = 0; +static int sym_debug = 0; + +static int parse_load_latency_line (char *line, gcov_pmu_ll_info_t *ll_info); +static int parse_branch_mispredict_line (char *line, + gcov_pmu_brm_info_t *brm_info); +static unsigned convert_pct_to_unsigned (float pct); +static void start_pfmon_module (pid_t ppid, char *tmpfile, const char **pfmon_args); +static void *init_pmu_load_latency (void); +static void *init_pmu_branch_mispredict (void); +static void destroy_load_latency_infos (void *info); +static void destroy_branch_mispredict_infos (void *info); +static int parse_pfmon_load_latency (char *filename, void *pmu_data); +static int parse_pfmon_branch_mispredicts (char *filename, void *pmu_data); +static gcov_unsigned_t gcov_tag_pmu_tool_header_length (gcov_pmu_tool_header_t + *header); +static void gcov_write_tool_header (gcov_pmu_tool_header_t *header); +static void gcov_write_load_latency_infos (void *info); +static void gcov_write_branch_mispredict_infos (void *info); +static void gcov_write_ll_line (const gcov_pmu_ll_info_t *ll_info); +static void gcov_write_branch_mispredict_line (const gcov_pmu_brm_info_t + *brm_info); +static int start_addr2line_symbolizer (pid_t pid); +static void end_addr2line_symbolizer (void); +static char *symbolize_addr2line (void *p); +static void reset_symbolizer_parent_pipes (void); +static void reset_symbolizer_child_pipes (void); +/* parse and cache relevant tool info. */ +static int parse_pmu_profile_options (const char *options); +static gcov_pmu_tool_header_t *parse_pfmon_tool_header (FILE *fp, + const char *end_header); + + +/* How to access the necessary functions for the PMU tools. */ +pmu_tool_fns all_pmu_tool_fns[PTT_LAST][PET_LAST] = { + { + { + "intel-load-latency", /* name */ + pfmon_intel_ll_args, /* tool args */ + init_pmu_load_latency, /* initialization */ + start_pfmon_module, /* start */ + 0, /* stop */ + parse_pfmon_load_latency, /* parse */ + gcov_write_load_latency_infos, /* write */ + destroy_load_latency_infos, /* cleanup */ + start_addr2line_symbolizer, /* start symbolizer */ + end_addr2line_symbolizer, /* end symbolizer */ + symbolize_addr2line, /* symbolize */ + }, + { + "amd-load-latency", /* name */ + pfmon_amd_ll_args, /* tool args */ + init_pmu_load_latency, /* initialization */ + start_pfmon_module, /* start */ + 0, /* stop */ + parse_pfmon_load_latency, /* parse */ + gcov_write_load_latency_infos, /* write */ + destroy_load_latency_infos, /* cleanup */ + start_addr2line_symbolizer, /* start symbolizer */ + end_addr2line_symbolizer, /* end symbolizer */ + symbolize_addr2line, /* symbolize */ + }, + { + "intel-branch-mispredict", /* name */ + pfmon_intel_brm_args, /* tool args */ + init_pmu_branch_mispredict, /* initialization */ + start_pfmon_module, /* start */ + 0, /* stop */ + parse_pfmon_branch_mispredicts, /* parse */ + gcov_write_branch_mispredict_infos,/* write */ + destroy_branch_mispredict_infos, /* cleanup */ + start_addr2line_symbolizer, /* start symbolizer */ + end_addr2line_symbolizer, /* end symbolizer */ + symbolize_addr2line, /* symbolize */ + }, + { + "amd-branch-mispredict", /* name */ + pfmon_amd_brm_args, /* tool args */ + init_pmu_branch_mispredict, /* initialization */ + start_pfmon_module, /* start */ + 0, /* stop */ + parse_pfmon_branch_mispredicts, /* parse */ + gcov_write_branch_mispredict_infos,/* write */ + destroy_branch_mispredict_infos, /* cleanup */ + start_addr2line_symbolizer, /* start symbolizer */ + end_addr2line_symbolizer, /* end symbolizer */ + symbolize_addr2line, /* symbolize */ + } + } +}; + +/* Determine the CPU vendor. Currently only distinguishes x86 based + cpus where the vendor is either Intel or AMD. Returns one of the + enum cpu_vendor_signatures. */ + +static unsigned int +get_x86cpu_vendor (void) +{ + unsigned int vendor = CPU_VENDOR_UKNOWN; + +#if (defined (__x86_64__) || defined (__i386__)) + if (__get_cpuid_max (0, &vendor) < 1) + return CPU_VENDOR_UKNOWN; /* Cannot determine cpu type. */ +#endif + + if (vendor == CPU_VENDOR_INTEL || vendor == CPU_VENDOR_AMD) + return vendor; + else + return CPU_VENDOR_UKNOWN; +} + + +/* Parse PMU tool option string provided on the command line and store + information in global structure. Return 0 on success, otherwise + return 1. Any changes to this should be synced with + check_pmu_profile_options() which does compile time check. */ + +static int +parse_pmu_profile_options (const char *options) +{ + enum pmu_tool_type ptt = the_pmu_tool_info->tool; + enum pmu_event_type pet = PET_LAST; + const char *pmutool_path; + the_pmu_tool_info->cpu_vendor = get_x86cpu_vendor (); + /* Determine the platform we are running on. */ + if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_UKNOWN) + { + /* Cpuid failed or uknown vendor. */ + the_pmu_tool_info->pmu_profiling_state = PMU_ERROR; + return 1; + } + + /* Validate the options. */ + if (strcmp(options, "load-latency") && + strcmp(options, "load-latency-verbose") && + strcmp(options, "branch-mispredict") && + strcmp(options, "branch-mispredict-verbose")) + return 1; + + /* Check if are aksed to collect load latency PMU data. */ + if (!strcmp(options, "load-latency") || + !strcmp(options, "load-latency-verbose")) + { + if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_INTEL) + pet = PET_INTEL_LOAD_LATENCY; + else + pet = PET_AMD_LOAD_LATENCY; + if (!strcmp(options, "load-latency-verbose")) + the_pmu_tool_info->verbose = 1; + } + + /* Check if are aksed to collect branch mispredict PMU data. */ + if (!strcmp(options, "branch-mispredict") || + !strcmp(options, "branch-mispredict-verbose")) + { + if (the_pmu_tool_info->cpu_vendor == CPU_VENDOR_INTEL) + pet = PET_INTEL_BRANCH_MISPREDICT; + else + pet = PET_AMD_BRANCH_MISPREDICT; + if (!strcmp(options, "branch-mispredict-verbose")) + the_pmu_tool_info->verbose = 1; + } + + the_pmu_tool_info->tool_details = &all_pmu_tool_fns[ptt][pet]; + the_pmu_tool_info->event = pet; + + /* Allow users to override the default tool path. */ + pmutool_path = getenv ("GCOV_PMUTOOL_PATH"); + if (pmutool_path && strlen (pmutool_path)) + the_pmu_tool_info->tool_details->arg_array[0] = pmutool_path; + + return 0; +} + +/* Do the initialization of addr2line symbolizer for the process id + given by TASK_PID. It forks an addr2line process and creates two + pipes where addresses can be written and source_filename:line_num + entries can be read. Returns 0 on success, non-zero otherwise. */ + +static int +start_addr2line_symbolizer (pid_t task_pid) +{ + pid_t pid; + char *addr2line_path; + + /* Allow users to override the default addr2line path. */ + addr2line_path = getenv ("GCOV_ADDR2LINE_PATH"); + if (addr2line_path && strlen (addr2line_path)) + addr2line_args[0] = addr2line_path; + + if (pipe (the_pmu_tool_info->symbolizer_from_pipefd) == -1) + { + fprintf (stderr, "Cannot create symbolizer write pipe.\n"); + return 1; + } + if (pipe (the_pmu_tool_info->symbolizer_to_pipefd) == -1) + { + fprintf (stderr, "Cannot create symbolizer read pipe.\n"); + return 1; + } + + pid = fork (); + if (pid == -1) + { + /* error condition */ + fprintf (stderr, "Cannot create symbolizer process.\n"); + reset_symbolizer_parent_pipes (); + reset_symbolizer_child_pipes (); + return 1; + } + + if (pid == 0) + { + /* child does an exec and then connects to/from the pipe */ + unsigned n_args = 0; + char proc_exe_buf[128]; + int new_write_fd, new_read_fd; + int i; + + /* Go over the current addr2line args. */ + for (i = 0; i < PMU_TOOL_MAX_ARGS && addr2line_args[i]; ++i) + n_args++; + + /* We are going to add one more arg for the /proc/pid/exe */ + if (n_args >= (PMU_TOOL_MAX_ARGS - 1)) + { + fprintf (stderr, "too many addr2line args: %d\n", n_args); + _exit (0); + } + snprintf (proc_exe_buf, sizeof (proc_exe_buf), "/proc/%d/exe", + task_pid); + + /* Add the extra arg for the process id. */ + addr2line_args[n_args] = proc_exe_buf; + n_args++; + + addr2line_args[n_args] = (const char *)NULL; /* terminating NULL */ + + if (sym_debug) + { + fprintf (stderr, "addr2line args:"); + for (i = 0; i < PMU_TOOL_MAX_ARGS && addr2line_args[i]; ++i) + fprintf (stderr, " %s", addr2line_args[i]); + fprintf (stderr, "\n"); + } + + /* Close unused ends of the two pipes. */ + reset_symbolizer_child_pipes (); + + /* Connect the pipes to stdin/stdout of the child process. */ + new_read_fd = dup2 (the_pmu_tool_info->symbolizer_to_pipefd[0], 0); + new_write_fd = dup2 (the_pmu_tool_info->symbolizer_from_pipefd[1], 1); + if (new_read_fd == -1 || new_write_fd == -1) + { + fprintf (stderr, "could not dup symbolizer fds\n"); + reset_symbolizer_parent_pipes (); + reset_symbolizer_child_pipes (); + _exit (0); + } + the_pmu_tool_info->symbolizer_to_pipefd[0] = new_read_fd; + the_pmu_tool_info->symbolizer_from_pipefd[1] = new_write_fd; + + /* Do execve with NULL env. */ + execve (addr2line_args[0], (char * const*)addr2line_args, + (char * const*)NULL); + /* exec returned, an error condition. */ + fprintf (stderr, "could not create symbolizer process: %s\n", + addr2line_args[0]); + reset_symbolizer_parent_pipes (); + reset_symbolizer_child_pipes (); + _exit (0); + } + else + { + /* parent */ + the_pmu_tool_info->symbolizer_pid = pid; + /* Close unused ends of the two pipes. */ + reset_symbolizer_parent_pipes (); + return 0; + } + return 0; +} + +/* Close unused write end of the from-pipe and read end of the + to-pipe. */ + +static void +reset_symbolizer_parent_pipes (void) +{ + if (the_pmu_tool_info->symbolizer_from_pipefd[1] != -1) + { + close (the_pmu_tool_info->symbolizer_from_pipefd[1]); + the_pmu_tool_info->symbolizer_from_pipefd[1] = -1; + } + if (the_pmu_tool_info->symbolizer_to_pipefd[0] != -1) + { + close (the_pmu_tool_info->symbolizer_to_pipefd[0]); + the_pmu_tool_info->symbolizer_to_pipefd[0] = -1; + } +} + +/* Close unused write end of the to-pipe and read end of the + from-pipe. */ + +static void +reset_symbolizer_child_pipes (void) +{ + if (the_pmu_tool_info->symbolizer_to_pipefd[1] != -1) + { + close (the_pmu_tool_info->symbolizer_to_pipefd[1]); + the_pmu_tool_info->symbolizer_to_pipefd[1] = -1; + } + if (the_pmu_tool_info->symbolizer_from_pipefd[0] != -1) + { + close (the_pmu_tool_info->symbolizer_from_pipefd[0]); + the_pmu_tool_info->symbolizer_from_pipefd[0] = -1; + } +} + + +/* Perform cleanup for the symbolizer process. */ + +static void +end_addr2line_symbolizer (void) +{ + int pid_status; + int wait_status; + pid_t pid = the_pmu_tool_info->symbolizer_pid; + + /* Symbolizer was not running. */ + if (!pid) + return; + + reset_symbolizer_parent_pipes (); + reset_symbolizer_child_pipes (); + kill (pid, SIGTERM); + wait_status = waitpid (pid, &pid_status, 0); + if (sym_debug) + { + if (wait_status == pid) + fprintf (stderr, "Normal exit. symbolizer terminated.\n"); + else + fprintf (stderr, "Abnormal exit. symbolizer status, %d.\n", pid_status); + } + the_pmu_tool_info->symbolizer_pid = 0; /* Symoblizer no longer running. */ +} + + +/* Given an address ADDR, return a string containing + source_filename:line_num entries. */ + +static char * +symbolize_addr2line (void *addr) +{ + char buf[32]; /* holds the ascii version of address */ + int write_count; + int read_count; + char *srcfile_linenum; + size_t max_length = 1024; + + if (!the_pmu_tool_info->symbolizer_pid) + return default_addr2line; /* symbolizer is not running */ + + write_count = snprintf (buf, sizeof (buf), "%p\n", addr); + + /* Write the address into the pipe. */ + if (write (the_pmu_tool_info->symbolizer_to_pipefd[1], buf, write_count) + < write_count) + { + if (sym_debug) + fprintf (stderr, "Cannot write symbolizer pipe.\n"); + return default_addr2line; + } + + srcfile_linenum = XNEWVEC (char, max_length); + read_count = read (the_pmu_tool_info->symbolizer_from_pipefd[0], + srcfile_linenum, max_length); + if (read_count == -1) + { + if (sym_debug) + fprintf (stderr, "Cannot read symbolizer pipe.\n"); + XDELETEVEC (srcfile_linenum); + return default_addr2line; + } + + srcfile_linenum[read_count] = 0; + if (sym_debug) + fprintf (stderr, "symbolizer: for address %p, read_count %d, got %s\n", + addr, read_count, srcfile_linenum); + return srcfile_linenum; +} + +/* Start monitoring PPID process via pfmon tool using TMPFILE as a + file to store the raw data and using PFMON_ARGS as the command line + arguments. */ + +static void +start_pfmon_module (pid_t ppid, char *tmpfile, const char **pfmon_args) +{ + int i; + unsigned int n_args = 0; + unsigned n_chars; + char pid_buf[64]; + char filename_buf[1024]; + char top_n_buf[24]; + unsigned extra_args; + + /* Go over the current pfmon args */ + for (i = 0; i < PMU_TOOL_MAX_ARGS && pfmon_args[i]; ++i) + n_args++; + + if (the_pmu_tool_info->verbose) + extra_args = 4; /* account for additional --verbose */ + else + extra_args = 3; + + /* We are going to add args. */ + if (n_args >= (PMU_TOOL_MAX_ARGS - extra_args)) + { + fprintf (stderr, "too many pfmon args: %d\n", n_args); + _exit (0); + } + + n_chars = snprintf (pid_buf, sizeof (pid_buf), "--attach-task=%ld", + (long)ppid); + if (n_chars >= sizeof (pid_buf)) + { + fprintf (stderr, "pfmon task id too long: %s\n", pid_buf); + return; + } + pfmon_args[n_args] = pid_buf; + n_args++; + + n_chars = snprintf (filename_buf, sizeof (filename_buf), "--smpl-outfile=%s", + tmpfile); + if (n_chars >= sizeof (filename_buf)) + { + fprintf (stderr, "pfmon filename too long: %s\n", filename_buf); + return; + } + pfmon_args[n_args] = filename_buf; + n_args++; + + n_chars = snprintf (top_n_buf, sizeof (top_n_buf), "--smpl-show-top=%d", + the_pmu_tool_info->top_n_address); + if (n_chars >= sizeof (top_n_buf)) + { + fprintf (stderr, "pfmon option too long: %s\n", top_n_buf); + return; + } + pfmon_args[n_args] = top_n_buf; + n_args++; + + if (the_pmu_tool_info->verbose) { + /* Add --verbose as well. */ + pfmon_args[n_args] = "--verbose"; + n_args++; + } + pfmon_args[n_args] = (char *)NULL; + + if (tool_debug) + { + fprintf (stderr, "pfmon args:"); + for (i = 0; i < PMU_TOOL_MAX_ARGS && pfmon_args[i]; ++i) + fprintf (stderr, " %s", pfmon_args[i]); + fprintf (stderr, "\n"); + } + /* Do execve with NULL env. */ + execve (pfmon_args[0], (char *const *)pfmon_args, (char * const*)NULL); + /* does not return */ +} + +/* Convert a fractional PCT to an unsigned integer after + muliplying by 100. */ + +static unsigned +convert_pct_to_unsigned (float pct) +{ + return (unsigned)(pct * 100.0f); +} + +/* Parse the load latency info pointed by LINE and save it into + LL_INFO. Returns 0 if the line was parsed successfully, non-zero + otherwise. + + An example header+line look like these: + "counts %self %cum <10 <32 <64 <256 <1024 >=1024 + %wself code addr symbol" + "218 24.06% 24.06% 100.00% 0.00% 0.00% 0.00% 0.00% 0.00% 22.70% + 0x0000000000413e75 CalcSSIM(...)+965" +*/ + +static int +parse_load_latency_line (char *line, gcov_pmu_ll_info_t *ll_info) +{ + unsigned counts; + /* These are percentages parsed as floats, but then converted to + integers after multiplying by 100. */ + float self, cum, lt_10, lt_32, lt_64, lt_256, lt_1024, gt_1024, wself; + long unsigned int p; + int n_values; + pmu_tool_fns *tool_details = the_pmu_tool_info->tool_details; + + n_values = sscanf (line, "%u%f%%%f%%%f%%%f%%%f%%%f%%%f%%%f%%%f%%%lx", + &counts, &self, &cum, <_10, <_32, <_64, <_256, + <_1024, >_1024, &wself, &p); + if (n_values != 11) + return 1; + + /* Values read successfully. Do the assignment after converting + * percentages into ints. */ + ll_info->counts = counts; + ll_info->self = convert_pct_to_unsigned (self); + ll_info->cum = convert_pct_to_unsigned (cum); + ll_info->lt_10 = convert_pct_to_unsigned (lt_10); + ll_info->lt_32 = convert_pct_to_unsigned (lt_32); + ll_info->lt_64 = convert_pct_to_unsigned (lt_64); + ll_info->lt_256 = convert_pct_to_unsigned (lt_256); + ll_info->lt_1024 = convert_pct_to_unsigned (lt_1024); + ll_info->gt_1024 = convert_pct_to_unsigned (gt_1024); + ll_info->wself = convert_pct_to_unsigned (wself); + ll_info->code_addr = p; + + /* Run the raw address through the symbolizer. */ + if (tool_details->symbolize) + { + char *sym_info = tool_details->symbolize ((void *)p); + /* sym_info is of the form src_filename:linenum. Descriminator is + currently not supported by addr2line. */ + char *sep = strchr (sym_info, ':'); + if (!sep) + { + /* Assume entire string is srcfile. */ + ll_info->filename = (char *)sym_info; + ll_info->line = 0; + } + else + { + /* Terminate the filename string at the separator. */ + *sep = 0; + ll_info->filename = (char *)sym_info; + /* Convert rest of the sym info to a line number. */ + ll_info->line = atol (sep+1); + } + ll_info->discriminator = 0; + } + else + { + /* No symbolizer available. */ + ll_info->filename = NULL; + ll_info->line = 0; + ll_info->discriminator = 0; + } + return 0; +} + +/* Parse the branch mispredict info pointed by LINE and save it into + BRM_INFO. Returns 0 if the line was parsed successfully, non-zero + otherwise. + + An example header+line look like these: + "counts %self %cum code addr symbol" + "6869 37.67% 37.67% 0x00000000004007e5 sum(std::vector > const&)+51" +*/ + +static int +parse_branch_mispredict_line (char *line, gcov_pmu_brm_info_t *brm_info) +{ + unsigned counts; + /* These are percentages parsed as floats, but then converted to + ints after multiplying by 100. */ + float self, cum; + long unsigned int p; + int n_values; + pmu_tool_fns *tool_details = the_pmu_tool_info->tool_details; + + n_values = sscanf (line, "%u%f%%%f%%%lx", + &counts, &self, &cum, &p); + if (n_values != 4) + return 1; + + /* Values read successfully. Do the assignment after converting + * percentages into ints. */ + brm_info->counts = counts; + brm_info->self = convert_pct_to_unsigned (self); + brm_info->cum = convert_pct_to_unsigned (cum); + brm_info->code_addr = p; + + /* Run the raw address through the symbolizer. */ + if (tool_details->symbolize) + { + char *sym_info = tool_details->symbolize ((void *)p); + /* sym_info is of the form src_filename:linenum. Descriminator is + currently not supported by addr2line. */ + char *sep = strchr (sym_info, ':'); + if (!sep) + { + /* Assume entire string is srcfile. */ + brm_info->filename = sym_info; + brm_info->line = 0; + } + else + { + /* Terminate the filename string at the separator. */ + *sep = 0; + brm_info->filename = sym_info; + /* Convert rest of the sym info to a line number. */ + brm_info->line = atol (sep+1); + } + brm_info->discriminator = 0; + } + else + { + /* No symbolizer available. */ + brm_info->filename = NULL; + brm_info->line = 0; + brm_info->discriminator = 0; + } + return 0; +} + +/* Delete load latency info structures INFO. */ + +static void +destroy_load_latency_infos (void *info) +{ + unsigned i; + ll_infos_t* ll_infos = (ll_infos_t *)info; + + /* delete each element */ + for (i = 0; i < ll_infos->ll_count; ++i) + XDELETE (ll_infos->ll_array[i]); + /* delete the array itself */ + XDELETE (ll_infos->ll_array); + __destroy_pmu_tool_header (ll_infos->pmu_tool_header); + free (ll_infos->pmu_tool_header); + ll_infos->ll_array = 0; + ll_infos->ll_count = 0; +} + +/* Delete branch mispredict structure INFO. */ + +static void +destroy_branch_mispredict_infos (void *info) +{ + unsigned i; + brm_infos_t* brm_infos = (brm_infos_t *)info; + + /* delete each element */ + for (i = 0; i < brm_infos->brm_count; ++i) + XDELETE (brm_infos->brm_array[i]); + /* delete the array itself */ + XDELETE (brm_infos->brm_array); + __destroy_pmu_tool_header (brm_infos->pmu_tool_header); + free (brm_infos->pmu_tool_header); + brm_infos->brm_array = 0; + brm_infos->brm_count = 0; +} + +/* Parse FILENAME for load latency lines into a structure + PMU_DATA. Returns 0 on on success. Returns non-zero on + failure. */ + +static int +parse_pfmon_load_latency (char *filename, void *pmu_data) +{ + FILE *fp; + size_t buflen = 2*1024; + char *buf; + ll_infos_t *load_latency_infos = (ll_infos_t *)pmu_data; + gcov_pmu_tool_header_t *tool_header = 0; + + if ((fp = fopen (filename, "r")) == NULL) + { + fprintf (stderr, "cannot open pmu data file: %s\n", filename); + return 1; + } + + if (!(tool_header = parse_pfmon_tool_header (fp, pfmon_ll_header))) + { + fprintf (stderr, "cannot parse pmu data file header: %s\n", filename); + return 1; + } + + buf = XNEWVEC (char, buflen); + while (fgets (buf, buflen, fp)) + { + gcov_pmu_ll_info_t *ll_info = XNEW (gcov_pmu_ll_info_t); + if (!parse_load_latency_line (buf, ll_info)) + { + /* valid line, add to the array */ + load_latency_infos->ll_count++; + if (load_latency_infos->ll_count >= + load_latency_infos->alloc_ll_count) + { + /* need to realloc */ + load_latency_infos->ll_array = + realloc (load_latency_infos->ll_array, + 2 * load_latency_infos->alloc_ll_count); + if (load_latency_infos->ll_array == NULL) + { + fprintf (stderr, "Cannot allocate load latency memory.\n"); + __destroy_pmu_tool_header (tool_header); + free (buf); + fclose (fp); + return 1; + } + } + load_latency_infos->ll_array[load_latency_infos->ll_count - 1] = + ll_info; + } + else + /* Delete invalid line. */ + XDELETE (ll_info); + } + free (buf); + fclose (fp); + load_latency_infos->pmu_tool_header = tool_header; + return 0; +} + +/* Parse open file FP until END_HEADER is seen. The data matching + gcov_pmu_tool_header_t fields is saved and returned in a new + struct. In case of failure, it returns NULL. */ + +static gcov_pmu_tool_header_t * +parse_pfmon_tool_header (FILE *fp, const char *end_header) +{ + static const char tag_hostname[] = "# hostname: "; + static const char tag_kversion[] = "# kernel version: "; + static const char tag_hostcpu[] = "# host CPUs: "; + static const char tag_column_desc_start[] = "# description of columns:"; + static const char tag_column_desc_end[] = + "# other columns are self-explanatory"; + size_t buflen = 4*1024; + char *buf, *buf_start, *buf_end; + gcov_pmu_tool_header_t *tool_header = XNEWVEC (gcov_pmu_tool_header_t, 1); + char *hostname = 0; + char *kversion = 0; + char *hostcpu = 0; + char *column_description = 0; + char *column_desc_start = 0; + char *column_desc_end = 0; + const char *column_header = 0; + int got_hostname = 0; + int got_kversion = 0 ; + int got_hostcpu = 0; + int got_end_header = 0; + int got_column_description = 0; + + buf = XNEWVEC (char, buflen); + buf_start = buf; + buf_end = buf + buflen; + while (buf < (buf_end - 1) && fgets (buf, buf_end - buf, fp)) + { + if (strncmp (end_header, buf, buf_end - buf) == 0) + { + got_end_header = 1; + break; + } + if (!got_hostname && + strncmp (buf, tag_hostname, strlen (tag_hostname)) == 0) + { + size_t len = strlen (buf) - strlen (tag_hostname); + hostname = XNEWVEC (char, len); + memcpy (hostname, buf + strlen (tag_hostname), len); + hostname[len - 1] = 0; + tool_header->hostname = hostname; + got_hostname = 1; + } + + if (!got_kversion && + strncmp (buf, tag_kversion, strlen (tag_kversion)) == 0) + { + size_t len = strlen (buf) - strlen (tag_kversion); + kversion = XNEWVEC (char, len); + memcpy (kversion, buf + strlen (tag_kversion), len); + kversion[len - 1] = 0; + tool_header->kernel_version = kversion; + got_kversion = 1; + } + + if (!got_hostcpu && + strncmp (buf, tag_hostcpu, strlen (tag_hostcpu)) == 0) + { + size_t len = strlen (buf) - strlen (tag_hostcpu); + hostcpu = XNEWVEC (char, len); + memcpy (hostcpu, buf + strlen (tag_hostcpu), len); + hostcpu[len - 1] = 0; + tool_header->host_cpu = hostcpu; + got_hostcpu = 1; + } + if (!got_column_description && + strncmp (buf, tag_column_desc_start, strlen (tag_column_desc_start)) + == 0) + { + column_desc_start = buf; + column_desc_end = 0; + /* Continue reading until end of the column descriptor. */ + while (buf < (buf_end - 1) && fgets (buf, buf_end - buf, fp)) + { + if (strncmp (buf, tag_column_desc_end, + strlen (tag_column_desc_end)) == 0) + { + column_desc_end = buf + strlen (tag_column_desc_end); + break; + } + buf += strlen (buf); + } + if (column_desc_end) + { + /* Found the end, copy it into a new string. */ + column_description = XNEWVEC (char, column_desc_end - + column_desc_start + 1); + got_column_description = 1; + strcpy (column_description, column_desc_start); + tool_header->column_description = column_description; + } + } + buf += strlen (buf); + } + + /* If we are missing any of the fields, return NULL. */ + if (!got_end_header || !got_hostname || !got_kversion || !got_hostcpu + || !got_column_description) + { + free (hostname); + free (kversion); + free (hostcpu); + free (column_description); + free (buf_start); + free (tool_header); + return NULL; + } + + switch (the_pmu_tool_info->event) + { + case PET_INTEL_LOAD_LATENCY: + case PET_AMD_LOAD_LATENCY: + column_header = pfmon_ll_header; + break; + case PET_INTEL_BRANCH_MISPREDICT: + case PET_AMD_BRANCH_MISPREDICT: + column_header = pfmon_bm_header; + break; + default: + break; + } + tool_header->column_header = strdup (column_header); + tool_header->full_header = buf_start; + return tool_header; +} + + +/* Parse FILENAME for branch mispredict lines into a structure + PMU_DATA. Returns 0 on on success. Returns non-zero on + failure. */ + +static int +parse_pfmon_branch_mispredicts (char *filename, void *pmu_data) +{ + FILE *fp; + size_t buflen = 2*1024; + char *buf; + brm_infos_t *brm_infos = (brm_infos_t *)pmu_data; + gcov_pmu_tool_header_t *tool_header = 0; + + if ((fp = fopen (filename, "r")) == NULL) + { + fprintf (stderr, "cannot open pmu data file: %s\n", filename); + return 1; + } + + if (!(tool_header = parse_pfmon_tool_header (fp, pfmon_bm_header))) + { + fprintf (stderr, "cannot parse pmu data file header: %s\n", filename); + return 1; + } + + buf = XNEWVEC (char, buflen); + while (fgets (buf, buflen, fp)) + { + gcov_pmu_brm_info_t *brm = XNEW (gcov_pmu_brm_info_t); + if (!parse_branch_mispredict_line (buf, brm)) + { + /* Valid line, add to the array. */ + brm_infos->brm_count++; + if (brm_infos->brm_count >= brm_infos->alloc_brm_count) + { + /* Do we need to realloc? */ + brm_infos->brm_array = + realloc (brm_infos->brm_array, + 2 * brm_infos->alloc_brm_count); + if (brm_infos->brm_array == NULL) { + fprintf (stderr, + "Cannot allocate memory for br mispredicts.\n"); + __destroy_pmu_tool_header (tool_header); + free (buf); + fclose (fp); + return 1; + } + } + brm_infos->brm_array[brm_infos->brm_count - 1] = brm; + } + else + /* Delete invalid line. */ + XDELETE (brm); + } + free (buf); + fclose (fp); + brm_infos->pmu_tool_header = tool_header; + return 0; +} + +/* Start the monitoring process using pmu tool. Return 0 on success, + non-zero otherwise. */ + +static int +pmu_start (void) +{ + pid_t pid; + + /* no start function */ + if (!the_pmu_tool_info->tool_details->start_pmu_module) + return 1; + + pid = fork (); + if (pid == -1) + { + /* error condition */ + fprintf (stderr, "Cannot create PMU profiling process, exiting.\n"); + return 1; + } + else if (pid == 0) + { + /* child */ + pid_t ppid = getppid(); + char *tmpfile = the_pmu_tool_info->raw_pmu_profile_filename; + const char **pfmon_args = the_pmu_tool_info->tool_details->arg_array; + int new_stderr_fd; + + /* Redirect stderr from the child process into a separate file. */ + new_stderr_fd = creat (the_pmu_tool_info->tool_stderr_filename, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (new_stderr_fd != -1) + dup2 (new_stderr_fd, 2); + /* The following does an exec and thus is not expected to return. */ + the_pmu_tool_info->tool_details->start_pmu_module(ppid, tmpfile, + pfmon_args); + /* exec returned, an error condition. */ + fprintf (stderr, "could not create profiling process: %s\n", + the_pmu_tool_info->tool_details->arg_array[0]); + _exit (0); + } + else + { + /* parent */ + the_pmu_tool_info->pmu_tool_pid = pid; + return 0; + } +} + +/* Allocate and initialize pmu load latency structure. */ + +static void * +init_pmu_load_latency (void) +{ + ll_infos_t *load_latency = XNEWVEC (ll_infos_t, 1); + load_latency->ll_count = 0; + load_latency->alloc_ll_count = 64; + load_latency->ll_array = XNEWVEC (gcov_pmu_ll_info_t *, + load_latency->alloc_ll_count); + return (void *)load_latency; +} + +/* Allocate and initialize pmu branch mispredict structure. */ + +static void * +init_pmu_branch_mispredict (void) +{ + brm_infos_t *brm_info = XNEWVEC (brm_infos_t, 1); + brm_info->brm_count = 0; + brm_info->alloc_brm_count = 64; + brm_info->brm_array = XNEWVEC (gcov_pmu_brm_info_t *, + brm_info->alloc_brm_count); + return (void *)brm_info; +} + +/* Initialize pmu tool based upon PMU_INFO. Sets the appropriate tool + type in the global the_pmu_tool_info. */ + +static int +init_pmu_tool (struct gcov_pmu_info *pmu_info) +{ + the_pmu_tool_info->pmu_profiling_state = PMU_NONE; + the_pmu_tool_info->verbose = 0; + the_pmu_tool_info->tool = PTT_PFMON; /* we support only pfmon */ + the_pmu_tool_info->pmu_tool_pid = 0; + the_pmu_tool_info->top_n_address = pmu_info->pmu_top_n_address; + the_pmu_tool_info->symbolizer_pid = 0; + the_pmu_tool_info->symbolizer_to_pipefd[0] = -1; + the_pmu_tool_info->symbolizer_to_pipefd[1] = -1; + the_pmu_tool_info->symbolizer_from_pipefd[0] = -1; + the_pmu_tool_info->symbolizer_from_pipefd[1] = -1; + + if (parse_pmu_profile_options (pmu_info->pmu_tool)) + return 1; + + if (the_pmu_tool_info->pmu_profiling_state == PMU_ERROR) + { + fprintf (stderr, "Unsupported PMU module: %s, disabling PMU profiling.\n", + pmu_info->pmu_tool); + return 1; + } + + if (the_pmu_tool_info->tool_details->init_pmu_module) + /* initialize module */ + the_pmu_tool_info->pmu_data = + the_pmu_tool_info->tool_details->init_pmu_module(); + return 0; +} + +/* Initialize PMU profiling based upon the information passed in + PMU_INFO and use pmu_profile_filename as the file to store the PMU + profile. This is called multiple times from libgcov, once per + object file. We need to make sure to do the necessary + initialization only the first time. For subsequent invocations it + behaves as a NOOP. */ + +void +__gcov_init_pmu_profiler (struct gcov_pmu_info *pmu_info) +{ + char *raw_pmu_profile_filename; + char *tool_stderr_filename; + if (!pmu_info || !pmu_info->pmu_profile_filename || !pmu_info->pmu_tool) + return; + + /* Allocate the global structure on first invocation. */ + if (!the_pmu_tool_info) + { + the_pmu_tool_info = XNEWVEC (struct pmu_tool_info, 1); + if (!the_pmu_tool_info) + { + fprintf (stderr, "Error allocating memory for PMU tool\n"); + return; + } + if (init_pmu_tool (pmu_info)) + { + /* Initialization error. */ + XDELETE (the_pmu_tool_info); + the_pmu_tool_info = 0; + return; + } + } + + switch (the_pmu_tool_info->pmu_profiling_state) + { + case PMU_NONE: + the_pmu_tool_info->pmu_profile_filename = + strdup (pmu_info->pmu_profile_filename); + /* Construct an intermediate filename by substituting trailing + '.gcda' with '.pmud'. */ + raw_pmu_profile_filename = strdup (pmu_info->pmu_profile_filename); + if (raw_pmu_profile_filename == NULL) + { + fprintf (stderr, "Cannot allocate memory\n"); + exit (1); + } + strcpy (raw_pmu_profile_filename + strlen (raw_pmu_profile_filename) - 4, + "pmud"); + + /* Construct a filename for collecting PMU tool's stderr by + substituting trailing '.gcda' with '.stderr'. */ + tool_stderr_filename = + XNEWVEC (char, strlen (pmu_info->pmu_profile_filename) + 1 + 2); + strcpy (tool_stderr_filename, pmu_info->pmu_profile_filename); + strcpy (tool_stderr_filename + strlen (tool_stderr_filename) - 4, + "stderr"); + the_pmu_tool_info->raw_pmu_profile_filename = raw_pmu_profile_filename; + the_pmu_tool_info->tool_stderr_filename = tool_stderr_filename; + the_pmu_tool_info->pmu_profiling_state = PMU_INITIALIZED; + break; + + case PMU_INITIALIZED: + case PMU_OFF: + case PMU_ON: + case PMU_ERROR: + break; + default: + break; + } +} + +/* Start PMU profiling. It updates the current state. */ + +void +__gcov_start_pmu_profiler (void) +{ + if (!the_pmu_tool_info) + return; + + switch (the_pmu_tool_info->pmu_profiling_state) + { + case PMU_INITIALIZED: + if (!pmu_start ()) + the_pmu_tool_info->pmu_profiling_state = PMU_ON; + else + the_pmu_tool_info->pmu_profiling_state = PMU_ERROR; + break; + + case PMU_NONE: + /* PMU was not properly initialized, don't attempt start it. */ + the_pmu_tool_info->pmu_profiling_state = PMU_ERROR; + break; + + case PMU_OFF: + /* Restarting PMU is not yet supported. */ + case PMU_ON: + /* Do nothing. */ + case PMU_ERROR: + break; + + default: + break; + } +} + +/* Stop PMU profiling. Currently it doesn't do anything except + bookkeeping. */ + +void +__gcov_stop_pmu_profiler (void) +{ + if (!the_pmu_tool_info) + return; + + if (the_pmu_tool_info->tool_details->stop_pmu_module) + the_pmu_tool_info->tool_details->stop_pmu_module(); + if (the_pmu_tool_info->pmu_profiling_state == PMU_ON) + the_pmu_tool_info->pmu_profiling_state = PMU_OFF; +} + +/* Write the load latency information LL_INFO into the gcda file. */ + +static void +gcov_write_ll_line (const gcov_pmu_ll_info_t *ll_info) +{ + gcov_unsigned_t len = GCOV_TAG_PMU_LOAD_LATENCY_LENGTH (ll_info->filename); + gcov_write_tag_length (GCOV_TAG_PMU_LOAD_LATENCY_INFO, len); + gcov_write_unsigned (ll_info->counts); + gcov_write_unsigned (ll_info->self); + gcov_write_unsigned (ll_info->cum); + gcov_write_unsigned (ll_info->lt_10); + gcov_write_unsigned (ll_info->lt_32); + gcov_write_unsigned (ll_info->lt_64); + gcov_write_unsigned (ll_info->lt_256); + gcov_write_unsigned (ll_info->lt_1024); + gcov_write_unsigned (ll_info->gt_1024); + gcov_write_unsigned (ll_info->wself); + gcov_write_counter (ll_info->code_addr); + gcov_write_unsigned (ll_info->line); + gcov_write_unsigned (ll_info->discriminator); + gcov_write_string (ll_info->filename); +} + + +/* Write the branch mispredict information BRM_INFO into the gcda file. */ + +static void +gcov_write_branch_mispredict_line (const gcov_pmu_brm_info_t *brm_info) +{ + gcov_unsigned_t len = GCOV_TAG_PMU_BRANCH_MISPREDICT_LENGTH ( + brm_info->filename); + gcov_write_tag_length (GCOV_TAG_PMU_BRANCH_MISPREDICT_INFO, len); + gcov_write_unsigned (brm_info->counts); + gcov_write_unsigned (brm_info->self); + gcov_write_unsigned (brm_info->cum); + gcov_write_counter (brm_info->code_addr); + gcov_write_unsigned (brm_info->line); + gcov_write_unsigned (brm_info->discriminator); + gcov_write_string (brm_info->filename); +} + +/* Write load latency information INFO into the gcda file. The gcda + file has already been opened and is available for writing. */ + +static void +gcov_write_load_latency_infos (void *info) +{ + unsigned i; + const ll_infos_t *ll_infos = (const ll_infos_t *)info; + gcov_unsigned_t stamp = 0; /* Don't use stamp as we don't support merge. */ + /* We don't support merge, and instead always rewrite the file. But + to rewrite a gcov file we must first read it, however the read + value is ignored. */ + gcov_read_unsigned (); + gcov_rewrite (); + gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION); + gcov_write_unsigned (stamp); + if (ll_infos->pmu_tool_header) + gcov_write_tool_header (ll_infos->pmu_tool_header); + for (i = 0; i < ll_infos->ll_count; ++i) + { + /* Write each line. */ + gcov_write_ll_line (ll_infos->ll_array[i]); + } + gcov_truncate (); +} + +/* Write branch mispredict information INFO into the gcda file. The + gcda file has already been opened and is available for writing. */ + +static void +gcov_write_branch_mispredict_infos (void *info) +{ + unsigned i; + const brm_infos_t *brm_infos = (const brm_infos_t *)info; + gcov_unsigned_t stamp = 0; /* Don't use stamp as we don't support merge. */ + /* We don't support merge, and instead always rewrite the file. */ + gcov_rewrite (); + gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION); + gcov_write_unsigned (stamp); + if (brm_infos->pmu_tool_header) + gcov_write_tool_header (brm_infos->pmu_tool_header); + for (i = 0; i < brm_infos->brm_count; ++i) + { + /* Write each line. */ + gcov_write_branch_mispredict_line (brm_infos->brm_array[i]); + } + gcov_truncate (); +} + +/* Compute TOOL_HEADER length for writing into the gcov file. */ + +static gcov_unsigned_t +gcov_tag_pmu_tool_header_length (gcov_pmu_tool_header_t *header) +{ + gcov_unsigned_t len = 0; + if (header) + { + len += gcov_string_length (header->host_cpu); + len += gcov_string_length (header->hostname); + len += gcov_string_length (header->kernel_version); + len += gcov_string_length (header->column_header); + len += gcov_string_length (header->column_description); + len += gcov_string_length (header->full_header); + } + return len; +} + +/* Write tool header into the gcda file. It assumes that the gcda file + has already been opened and is available for writing. */ + +static void +gcov_write_tool_header (gcov_pmu_tool_header_t *header) +{ + gcov_unsigned_t len = gcov_tag_pmu_tool_header_length (header); + gcov_write_tag_length (GCOV_TAG_PMU_TOOL_HEADER, len); + gcov_write_string (header->host_cpu); + gcov_write_string (header->hostname); + gcov_write_string (header->kernel_version); + gcov_write_string (header->column_header); + gcov_write_string (header->column_description); + gcov_write_string (header->full_header); +} + + +/* End PMU profiling. If GCDA_ERROR is non-zero then write profiling data into + already open gcda file */ + +void +__gcov_end_pmu_profiler (int gcda_error) +{ + int pid_status; + int wait_status; + pid_t pid; + pmu_tool_fns *tool_details; + + if (!the_pmu_tool_info) + return; + + tool_details = the_pmu_tool_info->tool_details; + pid = the_pmu_tool_info->pmu_tool_pid; + if (pid) + { + if (tool_debug) + fprintf (stderr, "terminating PMU profiling process %ld\n", (long)pid); + kill (pid, SIGTERM); + if (tool_debug) + fprintf (stderr, "parent: waiting for pmu process to end\n"); + wait_status = waitpid (pid, &pid_status, 0); + if (tool_debug) { + if (wait_status == pid) + fprintf (stderr, "Normal exit. Child terminated.\n"); + else + fprintf (stderr, "Abnormal exit. child status, %d.\n", pid_status); + } + } + + if (the_pmu_tool_info->pmu_profiling_state != PMU_OFF) + { + /* nothing to do */ + fprintf (stderr, + "__gcov_dump_pmu_profile: incorrect pmu state: %d, pid: %ld\n", + the_pmu_tool_info->pmu_profiling_state, + (unsigned long)pid); + return; + } + + if (!tool_details->parse_pmu_output) + return; + + /* Since we are going to parse the output, we also need symbolizer. */ + if (tool_details->start_symbolizer) + tool_details->start_symbolizer (getpid ()); + + if (!tool_details->parse_pmu_output + (the_pmu_tool_info->raw_pmu_profile_filename, + the_pmu_tool_info->pmu_data)) + { + if (!gcda_error && tool_details->gcov_write_pmu_data) + /* Write tool output into the gcda file. */ + tool_details->gcov_write_pmu_data (the_pmu_tool_info->pmu_data); + } + + if (tool_details->end_symbolizer) + tool_details->end_symbolizer (); + + if (tool_details->cleanup_pmu_data) + tool_details->cleanup_pmu_data (the_pmu_tool_info->pmu_data); +} + +#endif -- cgit v1.2.3