diff options
author | nathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4> | 2003-01-27 23:22:17 +0000 |
---|---|---|
committer | nathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4> | 2003-01-27 23:22:17 +0000 |
commit | ad8617cf3aae37463a6d61baa16b7b95a7520ead (patch) | |
tree | 2a2a67d83f3dfe20178bb96db57b38e585380170 /gcc | |
parent | 2dd7852ce343c33ec54fe545c112612f88c84751 (diff) | |
download | gcc-ad8617cf3aae37463a6d61baa16b7b95a7520ead.tar.gz |
* Makefile.in (LIB2FUNCS_ST): Remove _gcov.
(LIBGCOV): New variable.
(libgcc.mk): Add LIBGCOV.
(LIBGCC_DEPS): Add libgcov.c.
(libgcov.a): New target.
(clean): Remove libgcov.a.
(install-libgcc): Do libgcov too.
(stage1-start, stage2-start, stage3-start, stage4-start): Deal
with libgcov.a.
* libgcc2.c (L_gcov): Move into ...
* libgcov.c: ... here. New file.
* mklibgcc.in: Add libgcov rules.
* gcc.c (LINK_COMMAND_SPEC): Add -lgcov when profiling.
* doc/invoke.texi (profile-arcs, test-coverage): Update and
clarify.
* profile.c (index_counts_file): Remove duplicate check for open file.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@61905 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 21 | ||||
-rw-r--r-- | gcc/Makefile.in | 33 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 90 | ||||
-rw-r--r-- | gcc/gcc.c | 3 | ||||
-rw-r--r-- | gcc/libgcc2.c | 439 | ||||
-rw-r--r-- | gcc/libgcov.c | 473 | ||||
-rw-r--r-- | gcc/mklibgcc.in | 38 | ||||
-rw-r--r-- | gcc/profile.c | 6 |
8 files changed, 602 insertions, 501 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0d7452e481b..bb0804fbdfa 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,24 @@ +2003-01-27 Nathan Sidwell <nathan@codesourcery.com> + + * Makefile.in (LIB2FUNCS_ST): Remove _gcov. + (LIBGCOV): New variable. + (libgcc.mk): Add LIBGCOV. + (LIBGCC_DEPS): Add libgcov.c. + (libgcov.a): New target. + (clean): Remove libgcov.a. + (install-libgcc): Do libgcov too. + (stage1-start, stage2-start, stage3-start, stage4-start): Deal + with libgcov.a. + * libgcc2.c (L_gcov): Move into ... + * libgcov.c: ... here. New file. + * mklibgcc.in: Add libgcov rules. + * gcc.c (LINK_COMMAND_SPEC): Add -lgcov when profiling. + + * doc/invoke.texi (profile-arcs, test-coverage): Update and + clarify. + + * profile.c (index_counts_file): Remove duplicate check for open file. + 2003-01-27 Jerry Quinn <jlquinn@optonline.net> * gcc/doc/invoke.texi (Optimization Options): Group together diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 572378eb0fa..4cb7de27116 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -825,7 +825,10 @@ LIB2FUNCS_2 = _floatdixf _fixunsxfsi _fixtfdi _fixunstfdi _floatditf \ _addvdi3 _subvsi3 _subvdi3 _mulvsi3 _mulvdi3 _negvsi2 _negvdi2 _ctors # Defined in libgcc2.c, included only in the static library. -LIB2FUNCS_ST = _eprintf _gcov __gcc_bcmp +LIB2FUNCS_ST = _eprintf __gcc_bcmp + +# Defined in libgcov.c, included only in gcov library +LIBGCOV = _gcov FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \ _fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \ @@ -1054,6 +1057,7 @@ libgcc.mk: config.status Makefile mklibgcc $(LIB2ADD) $(LIB2ADD_ST) xgcc$(exeext LIB2FUNCS_1='$(LIB2FUNCS_1)' \ LIB2FUNCS_2='$(LIB2FUNCS_2)' \ LIB2FUNCS_ST='$(LIB2FUNCS_ST)' \ + LIBGCOV='$(LIBGCOV)' \ LIB2ADD='$(LIB2ADD)' \ LIB2ADD_ST='$(LIB2ADD_ST)' \ LIB2ADDEH='$(LIB2ADDEH)' \ @@ -1082,12 +1086,14 @@ libgcc.mk: config.status Makefile mklibgcc $(LIB2ADD) $(LIB2ADD_ST) xgcc$(exeext # All the things that might cause us to want to recompile bits of libgcc. LIBGCC_DEPS = $(GCC_PASSES) $(LANGUAGES) stmp-int-hdrs $(STMP_FIXPROTO) \ - libgcc.mk $(srcdir)/libgcc2.c $(TCONFIG_H) \ + libgcc.mk $(srcdir)/libgcc2.c $(srcdir)/libgcov.c $(TCONFIG_H) \ $(MACHMODE_H) longlong.h gbl-ctors.h config.status stmp-int-hdrs \ tsystem.h $(FPBIT) $(DPBIT) $(TPBIT) $(LIB2ADD) \ $(LIB2ADD_ST) $(LIB2ADDEH) $(LIB2ADDEHDEP) $(EXTRA_PARTS) \ $(srcdir)/config/$(LIB1ASMSRC) +libgcov.a: libgcc.a; @true + libgcc.a: $(LIBGCC_DEPS) $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \ BUILD_PREFIX="$(BUILD_PREFIX)" BUILD_PREFIX_1="$(BUILD_PREFIX_1)" \ @@ -2728,7 +2734,8 @@ mostlyclean: $(INTL_MOSTLYCLEAN) lang.mostlyclean # that don't exist in the distribution. INTL_CLEAN = intl.clean clean: mostlyclean $(INTL_CLEAN) lang.clean - -rm -f libgcc.a libgcc_eh.a libgcc_s$(SHLIB_EXT) libgcc_s$(SHLIB_EXT).1 + -rm -f libgcc.a libgcc_eh.a libgcov.a + -rm -f libgcc_s$(SHLIB_EXT) libgcc_s$(SHLIB_EXT).1 -rm -f config.h tconfig.h bconfig.h tm_p.h tm.h -rm -f cs-* -rm -rf libgcc @@ -2984,7 +2991,7 @@ install-man: installdirs $(GENERATED_MANPAGES) lang.install-man -chmod a-x $(DESTDIR)$(man7dir)/gpl$(man7ext) # Install the library. -install-libgcc: libgcc.mk libgcc.a installdirs +install-libgcc: libgcc.mk libgcc.a libgcov.a installdirs $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \ BUILD_PREFIX="$(BUILD_PREFIX)" BUILD_PREFIX_1="$(BUILD_PREFIX_1)" \ AR_FOR_TARGET="$(AR_FOR_TARGET)" \ @@ -3004,7 +3011,7 @@ install-libgcc: libgcc.mk libgcc.a installdirs slibdir="$(slibdir)" \ -f libgcc.mk install -# Install multiple versions of libgcc.a. +# Install multiple versions of libgcc.a, libgcov.a. install-multilib: stmp-multilib installdirs $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \ BUILD_PREFIX="$(BUILD_PREFIX)" BUILD_PREFIX_1="$(BUILD_PREFIX_1)" \ @@ -3611,9 +3618,11 @@ stage1-start: -if [ -f as$(exeext) ] ; then (cd stage1 && $(LN_S) ../as$(exeext) .) ; else true ; fi -if [ -f ld$(exeext) ] ; then (cd stage1 && $(LN_S) ../ld$(exeext) .) ; else true ; fi -if [ -f collect-ld$(exeext) ] ; then (cd stage1 && $(LN_S) ../collect-ld$(exeext) .) ; else true ; fi - -rm -f stage1/libgcc.a stage1/libgcc_eh.a + -rm -f stage1/libgcc.a stage1/libgcc_eh.a stage1/libgcov.a -cp libgcc.a stage1 -$(RANLIB_FOR_TARGET) stage1/libgcc.a + -cp libgcov.a stage1 + -$(RANLIB_FOR_TARGET) stage1/libgcov.a -if [ -f libgcc_eh.a ] ; then cp libgcc_eh.a stage1; \ $(RANLIB_FOR_TARGET) stage1/libgcc_eh.a; \ fi @@ -3641,9 +3650,11 @@ stage2-start: -if [ -f as$(exeext) ] ; then (cd stage2 && $(LN_S) ../as$(exeext) .) ; else true ; fi -if [ -f ld$(exeext) ] ; then (cd stage2 && $(LN_S) ../ld$(exeext) .) ; else true ; fi -if [ -f collect-ld$(exeext) ] ; then (cd stage2 && $(LN_S) ../collect-ld$(exeext) .) ; else true ; fi - -rm -f stage2/libgcc.a stage2/libgcc_eh.a + -rm -f stage2/libgcc.a stage2/libgcov.a stage2/libgcc_eh.a -cp libgcc.a stage2 -$(RANLIB_FOR_TARGET) stage2/libgcc.a + -cp libgcov.a stage2 + -$(RANLIB_FOR_TARGET) stage2/libgcov.a -if [ -f libgcc_eh.a ] ; then cp libgcc_eh.a stage2; \ $(RANLIB_FOR_TARGET) stage2/libgcc_eh.a; \ fi @@ -3667,9 +3678,11 @@ stage3-start: -if [ -f as$(exeext) ] ; then (cd stage3 && $(LN_S) ../as$(exeext) .) ; else true ; fi -if [ -f ld$(exeext) ] ; then (cd stage3 && $(LN_S) ../ld$(exeext) .) ; else true ; fi -if [ -f collect-ld$(exeext) ] ; then (cd stage3 && $(LN_S) ../collect-ld$(exeext) .) ; else true ; fi - -rm -f stage3/libgcc.a stage3/libgcc_eh.a + -rm -f stage3/libgcc.a stage3/libgcov.a stage3/libgcc_eh.a -cp libgcc.a stage3 -$(RANLIB_FOR_TARGET) stage3/libgcc.a + -cp libgcov.a stage3 + -$(RANLIB_FOR_TARGET) stage3/libgcov.a -if [ -f libgcc_eh.a ] ; then cp libgcc_eh.a stage3; \ $(RANLIB_FOR_TARGET) stage3/libgcc_eh.a; \ fi @@ -3693,9 +3706,11 @@ stage4-start: -if [ -f as$(exeext) ] ; then (cd stage4 && $(LN_S) ../as$(exeext) .) ; else true ; fi -if [ -f ld$(exeext) ] ; then (cd stage4 && $(LN_S) ../ld$(exeext) .) ; else true ; fi -if [ -f collect-ld$(exeext) ] ; then (cd stage4 && $(LN_S) ../collect-ld$(exeext) .) ; else true ; fi - -rm -f stage4/libgcc.a stage4/libgcc_eh.a + -rm -f stage4/libgcc.a stage4/libgcov.a stage4/libgcc_eh.a -cp libgcc.a stage4 -$(RANLIB_FOR_TARGET) stage4/libgcc.a + -cp libgcov.a stage4 + -$(RANLIB_FOR_TARGET) stage4/libgcov.a -if [ -f libgcc_eh.a ] ; then cp libgcc_eh.a stage4; \ $(RANLIB_FOR_TARGET) stage4/libgcc_eh.a; \ fi diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f8a03960d70..e4c39c59011 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -3035,27 +3035,46 @@ allocation when it finishes. @item -fprofile-arcs @opindex fprofile-arcs -Instrument @dfn{arcs} during compilation to generate coverage data or -for profile-directed block ordering. During execution the program -records how many times each branch is executed and how many times it is -taken. When the compiled program exits it saves this data to a file -called @file{@var{auxname}.da} for each source file. @var{auxname} is -generated from the name of the output file, if explicitly specified and -it is not the final executable, otherwise it is the basename of the -source file. In both cases any suffix is removed (e.g. @file{foo.da} -for input file @file{dir/foo.c}, or @file{dir/foo.da} for output file -specified as @option{-o dir/foo.o}). - -For profile-directed block ordering, compile the program with -@option{-fprofile-arcs} plus optimization and code generation options, -generate the arc profile information by running the program on a -selected workload, and then compile the program again with the same -optimization and code generation options plus +Add code so that program flow @dfn{arcs} are instrumented. During +execution the program records how many times each branch and call is +executed and how many times it is taken or returns. When the compiled +program exits it saves this data to a file called +@file{@var{auxname}.da} for each source file. The data may be used for +profile-directed optimizations (@option{-fbranch-probabilities}), or for +test coverage analysis (@option{-ftest-coverage}). Each object file's +@var{auxname} is generated from the name of the output file, if +explicitly specified and it is not the final executable, otherwise it is +the basename of the source file. In both cases any suffix is removed +(e.g. @file{foo.da} for input file @file{dir/foo.c}, or +@file{dir/foo.da} for output file specified as @option{-o dir/foo.o}). + +@itemize + +@item +Compile the source files with @option{-fprofile-arcs} plus optimization +and code generation options. For test coverage analysis, use the +additional @option{-ftest-coverage} option. You do not need to profile +every source file in a program. + +@item +Link your object files as normal. + +@item +Run the program on a representative workload to generate the arc profile +information. This may be repeated any number of times. + +@item +For profile-directed optimizations, compile the source files again with +the same optimization and code generation options plus @option{-fbranch-probabilities} (@pxref{Optimize Options,,Options that Control Optimization}). -The other use of @option{-fprofile-arcs} is for use with @command{gcov}, -when it is used with the @option{-ftest-coverage} option. +@item +For test coverage analysis, use @command{gcov} to produce human readable +information from the @file{.bbg} and @file{.da} files. Refer to the +@command{gcov} documentation for further information. + +@end itemize With @option{-fprofile-arcs}, for each function of your program GCC creates a program flow graph, then finds a spanning tree for the graph. @@ -3068,34 +3087,13 @@ block must be created to hold the instrumentation code. @need 2000 @item -ftest-coverage @opindex ftest-coverage -Create data files for the @command{gcov} code-coverage utility -(@pxref{Gcov,, @command{gcov}---a Test Coverage Program}). See -@option{-fprofile-arcs} option above for a description of @var{auxname}. - -@table @gcctabopt -@item @var{auxname}.bb -A mapping from basic blocks to line numbers, which @command{gcov} uses to -associate basic block execution counts with line numbers. - -@item @var{auxname}.bbg -A list of all arcs in the program flow graph. This allows @command{gcov} -to reconstruct the program flow graph, so that it can compute all basic -block and arc execution counts from the information in the -@file{@var{auxname}.da} file. -@end table - -Use @option{-ftest-coverage} with @option{-fprofile-arcs}; the latter -option adds instrumentation to the program, which then writes -execution counts to another data file: - -@table @gcctabopt -@item @var{auxname}.da -Runtime arc execution counts, used in conjunction with the arc -information in the file @file{@var{auxname}.bbg}. -@end table - -Coverage data will map better to the source files if -@option{-ftest-coverage} is used without optimization. +Produce a graph file that the @command{gcov} code-coverage utility +(@pxref{Gcov,, @command{gcov}---a Test Coverage Program}) can use to +show program coverage. Each source file's data file is called +@file{@var{auxname}.bbg}. Refer to the @option{-fprofile-arcs} option +above for a description of @var{auxname} and instructions on how to +generate test coverage data. Coverage data will match the source files +more closely, if you do not optimize. @item -d@var{letters} @opindex d diff --git a/gcc/gcc.c b/gcc/gcc.c index ca3a51337f6..3a36d3517b1 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -676,7 +676,8 @@ proper position among the other output files. */ %{!fsyntax-only:%{!c:%{!M:%{!MM:%{!E:%{!S:\ %(linker) %l %X %{o*} %{A} %{d} %{e*} %{m} %{N} %{n} %{r} %{s} %{t}\ %{u*} %{x} %{z} %{Z} %{!A:%{!nostdlib:%{!nostartfiles:%S}}}\ - %{static:} %{L*} %(link_libgcc) %o %{!nostdlib:%{!nodefaultlibs:%(link_gcc_c_sequence)}}\ + %{static:} %{L*} %(link_libgcc) %o %{fprofile-arcs:-lgcov}\ + %{!nostdlib:%{!nodefaultlibs:%(link_gcc_c_sequence)}}\ %{!A:%{!nostdlib:%{!nostartfiles:%E}}} %{T*} }}}}}}" #endif diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c index a47dfd10a32..deb772fa629 100644 --- a/gcc/libgcc2.c +++ b/gcc/libgcc2.c @@ -1235,445 +1235,6 @@ __eprintf (const char *string, const char *expression, #endif #endif -#ifdef L_gcov - -/* Gcov profile dumper. Requires atexit and stdio. */ - -#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */ -#include <stdio.h> - -#include "gcov-io.h" -#include <string.h> -#if defined (TARGET_HAS_F_SETLKW) -#include <fcntl.h> -#include <errno.h> -#endif - -/* Chain of per-object gcov structures. */ -static struct gcov_info *gcov_list; - -/* A program checksum allows us to distinguish program data for an - object file included in multiple programs. */ -static unsigned gcov_crc32; - -static void -gcov_version_mismatch (struct gcov_info *ptr, unsigned version) -{ - unsigned expected = GCOV_VERSION; - unsigned ix; - char e[4], v[4]; - - for (ix = 4; ix--; expected >>= 8, version >>= 8) - { - e[ix] = expected; - v[ix] = version; - } - - fprintf (stderr, - "profiling:%s:Version mismatch - expected %.4s got %.4s\n", - ptr->filename, e, v); -} - -/* Dump the coverage counts. 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 - statistics to the correct summary. An object file might be embedded - in two separate programs, and we must keep the two program - summaries separate. */ - -static void -gcov_exit (void) -{ - struct gcov_info *ptr; - unsigned ix, jx; - struct gcov_summary program; - gcov_type program_max_one = 0; - gcov_type program_max_sum = 0; - gcov_type program_sum = 0; - unsigned program_arcs = 0; - -#if defined (TARGET_HAS_F_SETLKW) - struct flock s_flock; - - s_flock.l_type = F_WRLCK; - s_flock.l_whence = SEEK_SET; - s_flock.l_start = 0; - s_flock.l_len = 0; /* Until EOF. */ - s_flock.l_pid = getpid (); -#endif - - memset (&program, 0, sizeof (program)); - program.checksum = gcov_crc32; - - for (ptr = gcov_list; ptr; ptr = ptr->next) - { - FILE *da_file; - struct gcov_summary object; - struct gcov_summary local_prg; - int merging = 0; - long base; - const struct function_info *fn_info; - gcov_type *count_ptr; - gcov_type object_max_one = 0; - - ptr->wkspc = 0; - if (!ptr->filename) - continue; - - for (ix = ptr->n_arc_counts, count_ptr = ptr->arc_counts; ix--;) - { - gcov_type count = *count_ptr++; - - if (count > object_max_one) - object_max_one = count; - } - if (object_max_one > program_max_one) - program_max_one = object_max_one; - - memset (&local_prg, 0, sizeof (local_prg)); - memset (&object, 0, sizeof (object)); - - /* Open for modification */ - if ((da_file = fopen (ptr->filename, "r+b"))) - merging = 1; - else if ((da_file = fopen (ptr->filename, "w+b"))) - ; - else - { - fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename); - ptr->filename = 0; - continue; - } - -#if defined (TARGET_HAS_F_SETLKW) - /* After a fork, another process might try to read and/or write - the same file simultaneously. So if we can, lock the file to - avoid race conditions. */ - while (fcntl (fileno (da_file), F_SETLKW, &s_flock) - && errno == EINTR) - continue; -#endif - if (merging) - { - /* Merge data from file. */ - unsigned tag, length; - - if (gcov_read_unsigned (da_file, &tag) || tag != GCOV_DATA_MAGIC) - { - fprintf (stderr, "profiling:%s:Not a gcov data file\n", - ptr->filename); - read_fatal:; - fclose (da_file); - ptr->filename = 0; - continue; - } - if (gcov_read_unsigned (da_file, &length) || length != GCOV_VERSION) - { - gcov_version_mismatch (ptr, length); - goto read_fatal; - } - - /* Merge execution counts for each function. */ - count_ptr = ptr->arc_counts; - for (ix = ptr->n_functions, fn_info = ptr->functions; - ix--; fn_info++) - { - if (gcov_read_unsigned (da_file, &tag) - || gcov_read_unsigned (da_file, &length)) - { - read_error:; - fprintf (stderr, "profiling:%s:Error merging\n", - ptr->filename); - goto read_fatal; - } - - /* Check function */ - if (tag != GCOV_TAG_FUNCTION) - { - read_mismatch:; - fprintf (stderr, "profiling:%s:Merge mismatch at %s\n", - ptr->filename, fn_info->name); - goto read_fatal; - } - { - unsigned flength, checksum; - - if (gcov_read_unsigned (da_file, &flength) - || gcov_skip_string (da_file, flength) - || gcov_read_unsigned (da_file, &checksum)) - goto read_error; - if (flength != strlen (fn_info->name) - || checksum != fn_info->checksum) - goto read_mismatch; - } - /* Check arc counts */ - if (gcov_read_unsigned (da_file, &tag) - || gcov_read_unsigned (da_file, &length)) - goto read_error; - if (tag != GCOV_TAG_ARC_COUNTS - || length / 8 != fn_info->n_arc_counts) - goto read_mismatch; - { - gcov_type count; - - for (jx = fn_info->n_arc_counts; jx--; count_ptr++) - if (gcov_read_counter (da_file, &count)) - goto read_error; - else - *count_ptr += count; - } - } - - /* Check object summary */ - if (gcov_read_unsigned (da_file, &tag) - || gcov_read_unsigned (da_file, &length)) - goto read_error; - if (tag != GCOV_TAG_OBJECT_SUMMARY) - goto read_mismatch; - if (gcov_read_summary (da_file, &object)) - goto read_error; - - /* Check program summary */ - while (1) - { - long base = ftell (da_file); - - if (gcov_read_unsigned (da_file, &tag) - || gcov_read_unsigned (da_file, &length)) - { - if (feof (da_file)) - break; - goto read_error; - } - if (tag != GCOV_TAG_PROGRAM_SUMMARY - && tag != GCOV_TAG_PLACEHOLDER_SUMMARY - && tag != GCOV_TAG_INCORRECT_SUMMARY) - goto read_mismatch; - if (gcov_read_summary (da_file, &local_prg)) - goto read_error; - if (local_prg.checksum != program.checksum) - continue; - if (tag == GCOV_TAG_PLACEHOLDER_SUMMARY) - { - fprintf (stderr, - "profiling:%s:Concurrent race detected\n", - ptr->filename); - goto read_fatal; - } - merging = -1; - if (tag != GCOV_TAG_PROGRAM_SUMMARY) - break; - - if (program.runs - && memcmp (&program, &local_prg, sizeof (program))) - { - fprintf (stderr, "profiling:%s:Invocation mismatch\n", - ptr->filename); - local_prg.runs = 0; - } - else - memcpy (&program, &local_prg, sizeof (program)); - ptr->wkspc = base; - break; - } - fseek (da_file, 0, SEEK_SET); - } - - object.runs++; - object.arcs = ptr->n_arc_counts; - object.arc_sum = 0; - if (object.arc_max_one < object_max_one) - object.arc_max_one = object_max_one; - object.arc_sum_max += object_max_one; - - /* Write out the data. */ - if (/* magic */ - gcov_write_unsigned (da_file, GCOV_DATA_MAGIC) - /* version number */ - || gcov_write_unsigned (da_file, GCOV_VERSION)) - { - write_error:; - fclose (da_file); - fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename); - ptr->filename = 0; - continue; - } - - /* Write execution counts for each function. */ - count_ptr = ptr->arc_counts; - for (ix = ptr->n_functions, fn_info = ptr->functions; ix--; fn_info++) - { - /* Announce function. */ - if (gcov_write_unsigned (da_file, GCOV_TAG_FUNCTION) - || !(base = gcov_reserve_length (da_file)) - /* function name */ - || gcov_write_string (da_file, fn_info->name, - strlen (fn_info->name)) - /* function checksum */ - || gcov_write_unsigned (da_file, fn_info->checksum) - || gcov_write_length (da_file, base)) - goto write_error; - - /* arc counts. */ - if (gcov_write_unsigned (da_file, GCOV_TAG_ARC_COUNTS) - || !(base = gcov_reserve_length (da_file))) - goto write_error; - - for (jx = fn_info->n_arc_counts; jx--;) - { - gcov_type count = *count_ptr++; - - object.arc_sum += count; - if (object.arc_max_sum < count) - object.arc_max_sum = count; - if (gcov_write_counter (da_file, count)) - goto write_error; /* RIP Edsger Dijkstra */ - } - if (gcov_write_length (da_file, base)) - goto write_error; - } - - /* Object file summary. */ - if (gcov_write_summary (da_file, GCOV_TAG_OBJECT_SUMMARY, &object)) - goto write_error; - - if (merging >= 0) - { - if (fseek (da_file, 0, SEEK_END)) - goto write_error; - ptr->wkspc = ftell (da_file); - if (gcov_write_summary (da_file, GCOV_TAG_PLACEHOLDER_SUMMARY, - &program)) - goto write_error; - } - else if (ptr->wkspc) - { - /* Zap trailing program summary */ - if (fseek (da_file, ptr->wkspc, SEEK_SET)) - goto write_error; - if (!local_prg.runs) - ptr->wkspc = 0; - if (gcov_write_unsigned (da_file, - local_prg.runs ? GCOV_TAG_PLACEHOLDER_SUMMARY - : GCOV_TAG_INCORRECT_SUMMARY)) - goto write_error; - } - if (fflush (da_file)) - goto write_error; - - if (fclose (da_file)) - { - fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename); - ptr->filename = 0; - } - else - { - program_arcs += ptr->n_arc_counts; - program_sum += object.arc_sum; - if (program_max_sum < object.arc_max_sum) - program_max_sum = object.arc_max_sum; - } - } - - /* Generate whole program statistics. */ - program.runs++; - program.arcs = program_arcs; - program.arc_sum = program_sum; - if (program.arc_max_one < program_max_one) - program.arc_max_one = program_max_one; - if (program.arc_max_sum < program_max_sum) - program.arc_max_sum = program_max_sum; - program.arc_sum_max += program_max_one; - - /* Upate whole program statistics. */ - for (ptr = gcov_list; ptr; ptr = ptr->next) - if (ptr->filename && ptr->wkspc) - { - FILE *da_file; - - da_file = fopen (ptr->filename, "r+b"); - if (!da_file) - { - fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename); - continue; - } - -#if defined (TARGET_HAS_F_SETLKW) - while (fcntl (fileno (da_file), F_SETLKW, &s_flock) - && errno == EINTR) - continue; -#endif - if (fseek (da_file, ptr->wkspc, SEEK_SET) - || gcov_write_summary (da_file, GCOV_TAG_PROGRAM_SUMMARY, &program) - || fflush (da_file)) - fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename); - if (fclose (da_file)) - fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename); - } -} - -/* Add a new object file onto the bb chain. Invoked automatically - when running an object file's global ctors. */ - -void -__gcov_init (struct gcov_info *info) -{ - if (!info->version) - return; - if (info->version != GCOV_VERSION) - gcov_version_mismatch (info, info->version); - else - { - const char *ptr = info->filename; - unsigned crc32 = gcov_crc32; - - do - { - unsigned ix; - unsigned value = *ptr << 24; - - for (ix = 8; ix--; value <<= 1) - { - unsigned feedback; - - feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0; - crc32 <<= 1; - crc32 ^= feedback; - } - } - while (*ptr++); - - gcov_crc32 = crc32; - - if (!gcov_list) - atexit (gcov_exit); - - info->next = gcov_list; - gcov_list = info; - } - info->version = 0; -} - -/* Called before fork or exec - write out profile information gathered so - far and reset it to zero. This avoids duplication or loss of the - profile information gathered so far. */ - -void -__gcov_flush (void) -{ - struct gcov_info *ptr; - - gcov_exit (); - for (ptr = gcov_list; ptr; ptr = ptr->next) - { - unsigned i; - - for (i = ptr->n_arc_counts; i--;) - ptr->arc_counts[i] = 0; - } -} - -#endif /* L_gcov */ #ifdef L_clear_cache /* Clear part of an instruction cache. */ diff --git a/gcc/libgcov.c b/gcc/libgcov.c new file mode 100644 index 00000000000..57bfb263265 --- /dev/null +++ b/gcc/libgcov.c @@ -0,0 +1,473 @@ +/* Routines required for instrumenting a program. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +/* It is incorrect to include config.h here, because this file is being + compiled for the target, and hence definitions concerning only the host + do not apply. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "coretypes.h" +#include "tm.h" + +#undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */ +#include <stdio.h> + +#include "gcov-io.h" +#include <string.h> +#if defined (TARGET_HAS_F_SETLKW) +#include <fcntl.h> +#include <errno.h> +#endif + +/* Chain of per-object gcov structures. */ +static struct gcov_info *gcov_list; + +/* A program checksum allows us to distinguish program data for an + object file included in multiple programs. */ +static unsigned gcov_crc32; + +static void +gcov_version_mismatch (struct gcov_info *ptr, unsigned version) +{ + unsigned expected = GCOV_VERSION; + unsigned ix; + char e[4], v[4]; + + for (ix = 4; ix--; expected >>= 8, version >>= 8) + { + e[ix] = expected; + v[ix] = version; + } + + fprintf (stderr, + "profiling:%s:Version mismatch - expected %.4s got %.4s\n", + ptr->filename, e, v); +} + +/* Dump the coverage counts. 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 + statistics to the correct summary. An object file might be embedded + in two separate programs, and we must keep the two program + summaries separate. */ + +static void +gcov_exit (void) +{ + struct gcov_info *ptr; + unsigned ix, jx; + struct gcov_summary program; + gcov_type program_max_one = 0; + gcov_type program_max_sum = 0; + gcov_type program_sum = 0; + unsigned program_arcs = 0; + +#if defined (TARGET_HAS_F_SETLKW) + struct flock s_flock; + + s_flock.l_type = F_WRLCK; + s_flock.l_whence = SEEK_SET; + s_flock.l_start = 0; + s_flock.l_len = 0; /* Until EOF. */ + s_flock.l_pid = getpid (); +#endif + + memset (&program, 0, sizeof (program)); + program.checksum = gcov_crc32; + + for (ptr = gcov_list; ptr; ptr = ptr->next) + { + FILE *da_file; + struct gcov_summary object; + struct gcov_summary local_prg; + int merging = 0; + long base; + const struct function_info *fn_info; + gcov_type *count_ptr; + gcov_type object_max_one = 0; + + ptr->wkspc = 0; + if (!ptr->filename) + continue; + + for (ix = ptr->n_arc_counts, count_ptr = ptr->arc_counts; ix--;) + { + gcov_type count = *count_ptr++; + + if (count > object_max_one) + object_max_one = count; + } + if (object_max_one > program_max_one) + program_max_one = object_max_one; + + memset (&local_prg, 0, sizeof (local_prg)); + memset (&object, 0, sizeof (object)); + + /* Open for modification */ + if ((da_file = fopen (ptr->filename, "r+b"))) + merging = 1; + else if ((da_file = fopen (ptr->filename, "w+b"))) + ; + else + { + fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename); + ptr->filename = 0; + continue; + } + +#if defined (TARGET_HAS_F_SETLKW) + /* After a fork, another process might try to read and/or write + the same file simultaneously. So if we can, lock the file to + avoid race conditions. */ + while (fcntl (fileno (da_file), F_SETLKW, &s_flock) + && errno == EINTR) + continue; +#endif + if (merging) + { + /* Merge data from file. */ + unsigned tag, length; + + if (gcov_read_unsigned (da_file, &tag) || tag != GCOV_DATA_MAGIC) + { + fprintf (stderr, "profiling:%s:Not a gcov data file\n", + ptr->filename); + read_fatal:; + fclose (da_file); + ptr->filename = 0; + continue; + } + if (gcov_read_unsigned (da_file, &length) || length != GCOV_VERSION) + { + gcov_version_mismatch (ptr, length); + goto read_fatal; + } + + /* Merge execution counts for each function. */ + count_ptr = ptr->arc_counts; + for (ix = ptr->n_functions, fn_info = ptr->functions; + ix--; fn_info++) + { + if (gcov_read_unsigned (da_file, &tag) + || gcov_read_unsigned (da_file, &length)) + { + read_error:; + fprintf (stderr, "profiling:%s:Error merging\n", + ptr->filename); + goto read_fatal; + } + + /* Check function */ + if (tag != GCOV_TAG_FUNCTION) + { + read_mismatch:; + fprintf (stderr, "profiling:%s:Merge mismatch at %s\n", + ptr->filename, fn_info->name); + goto read_fatal; + } + { + unsigned flength, checksum; + + if (gcov_read_unsigned (da_file, &flength) + || gcov_skip_string (da_file, flength) + || gcov_read_unsigned (da_file, &checksum)) + goto read_error; + if (flength != strlen (fn_info->name) + || checksum != fn_info->checksum) + goto read_mismatch; + } + /* Check arc counts */ + if (gcov_read_unsigned (da_file, &tag) + || gcov_read_unsigned (da_file, &length)) + goto read_error; + if (tag != GCOV_TAG_ARC_COUNTS + || length / 8 != fn_info->n_arc_counts) + goto read_mismatch; + { + gcov_type count; + + for (jx = fn_info->n_arc_counts; jx--; count_ptr++) + if (gcov_read_counter (da_file, &count)) + goto read_error; + else + *count_ptr += count; + } + } + + /* Check object summary */ + if (gcov_read_unsigned (da_file, &tag) + || gcov_read_unsigned (da_file, &length)) + goto read_error; + if (tag != GCOV_TAG_OBJECT_SUMMARY) + goto read_mismatch; + if (gcov_read_summary (da_file, &object)) + goto read_error; + + /* Check program summary */ + while (1) + { + long base = ftell (da_file); + + if (gcov_read_unsigned (da_file, &tag) + || gcov_read_unsigned (da_file, &length)) + { + if (feof (da_file)) + break; + goto read_error; + } + if (tag != GCOV_TAG_PROGRAM_SUMMARY + && tag != GCOV_TAG_PLACEHOLDER_SUMMARY + && tag != GCOV_TAG_INCORRECT_SUMMARY) + goto read_mismatch; + if (gcov_read_summary (da_file, &local_prg)) + goto read_error; + if (local_prg.checksum != program.checksum) + continue; + if (tag == GCOV_TAG_PLACEHOLDER_SUMMARY) + { + fprintf (stderr, + "profiling:%s:Concurrent race detected\n", + ptr->filename); + goto read_fatal; + } + merging = -1; + if (tag != GCOV_TAG_PROGRAM_SUMMARY) + break; + + if (program.runs + && memcmp (&program, &local_prg, sizeof (program))) + { + fprintf (stderr, "profiling:%s:Invocation mismatch\n", + ptr->filename); + local_prg.runs = 0; + } + else + memcpy (&program, &local_prg, sizeof (program)); + ptr->wkspc = base; + break; + } + fseek (da_file, 0, SEEK_SET); + } + + object.runs++; + object.arcs = ptr->n_arc_counts; + object.arc_sum = 0; + if (object.arc_max_one < object_max_one) + object.arc_max_one = object_max_one; + object.arc_sum_max += object_max_one; + + /* Write out the data. */ + if (/* magic */ + gcov_write_unsigned (da_file, GCOV_DATA_MAGIC) + /* version number */ + || gcov_write_unsigned (da_file, GCOV_VERSION)) + { + write_error:; + fclose (da_file); + fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename); + ptr->filename = 0; + continue; + } + + /* Write execution counts for each function. */ + count_ptr = ptr->arc_counts; + for (ix = ptr->n_functions, fn_info = ptr->functions; ix--; fn_info++) + { + /* Announce function. */ + if (gcov_write_unsigned (da_file, GCOV_TAG_FUNCTION) + || !(base = gcov_reserve_length (da_file)) + /* function name */ + || gcov_write_string (da_file, fn_info->name, + strlen (fn_info->name)) + /* function checksum */ + || gcov_write_unsigned (da_file, fn_info->checksum) + || gcov_write_length (da_file, base)) + goto write_error; + + /* arc counts. */ + if (gcov_write_unsigned (da_file, GCOV_TAG_ARC_COUNTS) + || !(base = gcov_reserve_length (da_file))) + goto write_error; + + for (jx = fn_info->n_arc_counts; jx--;) + { + gcov_type count = *count_ptr++; + + object.arc_sum += count; + if (object.arc_max_sum < count) + object.arc_max_sum = count; + if (gcov_write_counter (da_file, count)) + goto write_error; /* RIP Edsger Dijkstra */ + } + if (gcov_write_length (da_file, base)) + goto write_error; + } + + /* Object file summary. */ + if (gcov_write_summary (da_file, GCOV_TAG_OBJECT_SUMMARY, &object)) + goto write_error; + + if (merging >= 0) + { + if (fseek (da_file, 0, SEEK_END)) + goto write_error; + ptr->wkspc = ftell (da_file); + if (gcov_write_summary (da_file, GCOV_TAG_PLACEHOLDER_SUMMARY, + &program)) + goto write_error; + } + else if (ptr->wkspc) + { + /* Zap trailing program summary */ + if (fseek (da_file, ptr->wkspc, SEEK_SET)) + goto write_error; + if (!local_prg.runs) + ptr->wkspc = 0; + if (gcov_write_unsigned (da_file, + local_prg.runs ? GCOV_TAG_PLACEHOLDER_SUMMARY + : GCOV_TAG_INCORRECT_SUMMARY)) + goto write_error; + } + if (fflush (da_file)) + goto write_error; + + if (fclose (da_file)) + { + fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename); + ptr->filename = 0; + } + else + { + program_arcs += ptr->n_arc_counts; + program_sum += object.arc_sum; + if (program_max_sum < object.arc_max_sum) + program_max_sum = object.arc_max_sum; + } + } + + /* Generate whole program statistics. */ + program.runs++; + program.arcs = program_arcs; + program.arc_sum = program_sum; + if (program.arc_max_one < program_max_one) + program.arc_max_one = program_max_one; + if (program.arc_max_sum < program_max_sum) + program.arc_max_sum = program_max_sum; + program.arc_sum_max += program_max_one; + + /* Upate whole program statistics. */ + for (ptr = gcov_list; ptr; ptr = ptr->next) + if (ptr->filename && ptr->wkspc) + { + FILE *da_file; + + da_file = fopen (ptr->filename, "r+b"); + if (!da_file) + { + fprintf (stderr, "profiling:%s:Cannot open\n", ptr->filename); + continue; + } + +#if defined (TARGET_HAS_F_SETLKW) + while (fcntl (fileno (da_file), F_SETLKW, &s_flock) + && errno == EINTR) + continue; +#endif + if (fseek (da_file, ptr->wkspc, SEEK_SET) + || gcov_write_summary (da_file, GCOV_TAG_PROGRAM_SUMMARY, &program) + || fflush (da_file)) + fprintf (stderr, "profiling:%s:Error writing\n", ptr->filename); + if (fclose (da_file)) + fprintf (stderr, "profiling:%s:Error closing\n", ptr->filename); + } +} + +/* Add a new object file onto the bb chain. Invoked automatically + when running an object file's global ctors. */ + +void +__gcov_init (struct gcov_info *info) +{ + if (!info->version) + return; + if (info->version != GCOV_VERSION) + gcov_version_mismatch (info, info->version); + else + { + const char *ptr = info->filename; + unsigned crc32 = gcov_crc32; + + do + { + unsigned ix; + unsigned value = *ptr << 24; + + for (ix = 8; ix--; value <<= 1) + { + unsigned feedback; + + feedback = (value ^ crc32) & 0x80000000 ? 0x04c11db7 : 0; + crc32 <<= 1; + crc32 ^= feedback; + } + } + while (*ptr++); + + gcov_crc32 = crc32; + + if (!gcov_list) + atexit (gcov_exit); + + info->next = gcov_list; + gcov_list = info; + } + info->version = 0; +} + +/* Called before fork or exec - write out profile information gathered so + far and reset it to zero. This avoids duplication or loss of the + profile information gathered so far. */ + +void +__gcov_flush (void) +{ + struct gcov_info *ptr; + + gcov_exit (); + for (ptr = gcov_list; ptr; ptr = ptr->next) + { + unsigned i; + + for (i = ptr->n_arc_counts; i--;) + ptr->arc_counts[i] = 0; + } +} diff --git a/gcc/mklibgcc.in b/gcc/mklibgcc.in index faa5a69fb59..a18b8dd0a8b 100644 --- a/gcc/mklibgcc.in +++ b/gcc/mklibgcc.in @@ -12,6 +12,7 @@ # LIB2FUNCS_1 # LIB2FUNCS_2 # LIB2FUNCS_ST +# LIBGCOV # LIB2ADD # LIB2ADD_ST # LIB2ADDEH @@ -65,6 +66,9 @@ make_compile='$(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \ # Dependencies for libgcc2.c libgcc2_c_dep='stmp-dirs $(srcdir)/libgcc2.c $(CONFIG_H) coretypes.h $(TM_H) $(MACHMODE_H) longlong.h gbl-ctors.h config.status stmp-int-hdrs tsystem.h'" $LIB2ADDEHDEP" +# Dependencies for libgcov.c +libgcov_c_dep='stmp-dirs $(srcdir)/libgcov.c $(CONFIG_H) coretypes.h $(TM_H) $(MACHMODE_H) longlong.h gbl-ctors.h config.status stmp-int-hdrs tsystem.h' + # Dependencies for fp-bit.c fpbit_c_dep='stmp-dirs config.status tsystem.h' @@ -249,6 +253,25 @@ for file in $LIB2ADD_ST; do libgcc2_st_objs="$libgcc2_st_objs ${oname}${objext}" done +# +# build libgcov components +# + +libgcov_objs="" + +for name in $LIBGCOV; do + for ml in $MULTILIBS; do + dir=`echo ${ml} | sed -e 's/;.*$//' -e 's/=/$(EQ)/g'` + flags=`echo ${ml} | sed -e 's/^[^;]*;//' -e 's/@/ -/g'`; + out="libgcc/${dir}/${name}${objext}" + + echo $out: $libgcov_c_dep + echo " $gcc_compile" '$(MAYBE_USE_COLLECT2)' $flags -DL$name \ + -c '$(srcdir)/libgcov.c' -o $out + done + libgcov_objs="$libgcov_objs ${name}${objext}" +done + # SHLIB_MKMAP # SHLIB_MKMAP_OPTS # SHLIB_MAPFILES @@ -275,6 +298,11 @@ for ml in $MULTILIBS; do libgcc_st_objs="$libgcc_st_objs libgcc/${dir}/$o" done + libgcov_a_objs="" + for o in $libgcov_objs; do + libgcov_a_objs="$libgcov_a_objs libgcc/${dir}/$o" + done + if [ "$SHLIB_LINK" -a "$SHLIB_MKMAP" ]; then mapfile="libgcc/${dir}/libgcc.map" tmpmapfile="libgcc/${dir}/tmp-libgcc.map" @@ -314,6 +342,12 @@ for ml in $MULTILIBS; do echo ' $(AR_CREATE_FOR_TARGET)' ${dir}/libgcc.a $libgcc_a_objs echo ' $(RANLIB_FOR_TARGET)' ${dir}/libgcc.a + echo "" + echo "${dir}/libgcov.a: $libgcov_a_objs" + echo " -rm -rf ${dir}/libgcov.a" + echo ' $(AR_CREATE_FOR_TARGET)' ${dir}/libgcov.a $libgcov_a_objs + echo ' $(RANLIB_FOR_TARGET)' ${dir}/libgcov.a + if [ "$SHLIB_LINK" ]; then echo "" @@ -395,7 +429,7 @@ for ml in $MULTILIBS; do if [ $dir != . ]; then dirs="$dirs ${dir} libgcc/${dir}" fi - all="$all ${dir}/libgcc.a" + all="$all ${dir}/libgcc.a ${dir}/libgcov.a" if [ "$SHLIB_LINK" ]; then all="$all ${dir}/libgcc_eh.a" if [ -z "$SHLIB_MULTILIB" ]; then @@ -456,6 +490,8 @@ for ml in $MULTILIBS; do fi echo ' $(INSTALL_DATA)' ${dir}/libgcc.a ${ldir}/ echo ' $(RANLIB_FOR_TARGET)' ${ldir}/libgcc.a + echo ' $(INSTALL_DATA)' ${dir}/libgcov.a ${ldir}/ + echo ' $(RANLIB_FOR_TARGET)' ${ldir}/libgcov.a if [ "$SHLIB_LINK" ]; then echo ' $(INSTALL_DATA)' ${dir}/libgcc_eh.a ${ldir}/ diff --git a/gcc/profile.c b/gcc/profile.c index 5ee790a14df..de2d309b449 100644 --- a/gcc/profile.c +++ b/gcc/profile.c @@ -289,16 +289,12 @@ index_counts_file () unsigned magic, version, ix, checksum; long *summary; - if (!da_file) - return 0; - counts_file_index = htab_create (10, htab_counts_index_hash, htab_counts_index_eq, htab_counts_index_del); - /* No .da file, no data. */ if (!da_file) return 0; + counts_file_index = htab_create (10, htab_counts_index_hash, htab_counts_index_eq, htab_counts_index_del); /* Now index all profile sections. */ - rewind (da_file); summary = NULL; |