mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-01-11 17:45:03 +08:00
Add gcov-tool: an offline gcda profile processing tool Support.
2014-07-10 Rong Xu <xur@google.com> Add gcov-tool: an offline gcda profile processing tool Support. * gcc/gcov-io.c (gcov_position): Make avaialble to gcov-tool. (gcov_is_error): Ditto. (gcov_read_string): Ditto. (gcov_read_sync): Ditto. * gcc/gcov-io.h: Move counter defines to gcov-counter.def. * gcc/gcov-dump.c (tag_counters): Use gcov-counter.def. * gcc/coverage.c: Ditto. * gcc/gcov-tool.c: Offline gcda profile processing tool. (unlink_gcda_file): Remove one gcda file. (unlink_profile_dir): Remove gcda files from the profile path. (gcov_output_files): Output gcda files to an output dir. (profile_merge): Merge two profiles in directory. (print_merge_usage_message): Print merge usage. (merge_usage): Print merge usage and exit. (do_merge): Driver for profile merge sub-command. (profile_rewrite): Rewrite profile. (print_rewrite_usage_message): Print rewrite usage. (rewrite_usage): Print rewrite usage and exit. (do_rewrite): Driver for profile rewrite sub-command. (print_usage): Print gcov-info usage and exit. (print_version): Print gcov-info version. (process_args): Process arguments. (main): Main routine for gcov-tool. * gcc/Makefile.in: Build and install gcov-tool. * gcc/gcov-counter.def: New file split from gcov-io.h. * libgcc/libgcov-driver.c (gcov_max_filename): Make available to gcov-tool. * libgcc/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. * libgcc/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. * libgcc/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. * gcc/doc/gcc.texi: Include gcov-tool.texi. * gcc/doc/gcov-tool.texi: Document for gcov-tool. From-SVN: r212448
This commit is contained in:
parent
c14e64d4ca
commit
c77556a5d1
@ -1,3 +1,35 @@
|
||||
2014-07-10 Rong Xu <xur@google.com>
|
||||
|
||||
Add gcov-tool: an offline gcda profile processing tool
|
||||
Support.
|
||||
* gcov-io.c (gcov_position): Make avaialble to gcov-tool.
|
||||
(gcov_is_error): Ditto.
|
||||
(gcov_read_string): Ditto.
|
||||
(gcov_read_sync): Ditto.
|
||||
* gcov-io.h: Move counter defines to gcov-counter.def.
|
||||
* gcov-dump.c (tag_counters): Use gcov-counter.def.
|
||||
* coverage.c: Ditto.
|
||||
* gcov-tool.c: Offline gcda profile processing tool.
|
||||
(unlink_gcda_file): Remove one gcda file.
|
||||
(unlink_profile_dir): Remove gcda files from the profile path.
|
||||
(gcov_output_files): Output gcda files to an output dir.
|
||||
(profile_merge): Merge two profiles in directory.
|
||||
(print_merge_usage_message): Print merge usage.
|
||||
(merge_usage): Print merge usage and exit.
|
||||
(do_merge): Driver for profile merge sub-command.
|
||||
(profile_rewrite): Rewrite profile.
|
||||
(print_rewrite_usage_message): Print rewrite usage.
|
||||
(rewrite_usage): Print rewrite usage and exit.
|
||||
(do_rewrite): Driver for profile rewrite sub-command.
|
||||
(print_usage): Print gcov-info usage and exit.
|
||||
(print_version): Print gcov-info version.
|
||||
(process_args): Process arguments.
|
||||
(main): Main routine for gcov-tool.
|
||||
* Makefile.in: Build and install gcov-tool.
|
||||
* gcov-counter.def: New file split from gcov-io.h.
|
||||
* doc/gcc.texi: Include gcov-tool.texi.
|
||||
* doc/gcov-tool.texi: Document for gcov-tool.
|
||||
|
||||
2014-07-10 Richard Biener <rguenther@suse.de>
|
||||
|
||||
PR tree-optimization/61757
|
||||
|
@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build
|
||||
|
||||
# Selection of languages to be made.
|
||||
CONFIG_LANGUAGES = @all_selected_languages@
|
||||
LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES)
|
||||
LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) gcov-tool$(exeext) \
|
||||
$(CONFIG_LANGUAGES)
|
||||
|
||||
# Default values for variables overridden in Makefile fragments.
|
||||
# CFLAGS is for the user to override to, e.g., do a cross build with -O2.
|
||||
@ -196,6 +197,9 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $(NOCOMMON_FLAG) $($@-warn)
|
||||
# flex output may yield harmless "no previous prototype" warnings
|
||||
build/gengtype-lex.o-warn = -Wno-error
|
||||
gengtype-lex.o-warn = -Wno-error
|
||||
libgcov-util.o-warn = -Wno-error
|
||||
libgcov-driver-tool.o-warn = -Wno-error
|
||||
libgcov-merge-tool.o-warn = -Wno-error
|
||||
|
||||
# All warnings have to be shut off in stage1 if the compiler used then
|
||||
# isn't gcc; configure determines that. WARN_CFLAGS will be either
|
||||
@ -769,6 +773,7 @@ GCC_INSTALL_NAME := $(shell echo gcc|sed '$(program_transform_name)')
|
||||
GCC_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gcc|sed '$(program_transform_name)')
|
||||
CPP_INSTALL_NAME := $(shell echo cpp|sed '$(program_transform_name)')
|
||||
GCOV_INSTALL_NAME := $(shell echo gcov|sed '$(program_transform_name)')
|
||||
GCOV_TOOL_INSTALL_NAME := $(shell echo gcov-tool|sed '$(program_transform_name)')
|
||||
|
||||
# Setup the testing framework, if you have one
|
||||
EXPECT = `if [ -f $${rootme}/../expect/expect ] ; then \
|
||||
@ -890,7 +895,7 @@ BASIC_BLOCK_H = basic-block.h $(PREDICT_H) $(VEC_H) $(FUNCTION_H) \
|
||||
GIMPLE_H = gimple.h gimple.def gsstruct.def pointer-set.h $(VEC_H) \
|
||||
$(GGC_H) $(BASIC_BLOCK_H) $(TREE_H) tree-ssa-operands.h \
|
||||
tree-ssa-alias.h $(INTERNAL_FN_H) $(HASH_TABLE_H) is-a.h
|
||||
GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h
|
||||
GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h gcov-counter.def
|
||||
RECOG_H = recog.h
|
||||
EMIT_RTL_H = emit-rtl.h
|
||||
FLAGS_H = flags.h flag-types.h $(OPTIONS_H)
|
||||
@ -1492,7 +1497,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANGUAGES),$($(v)_OBJS))
|
||||
ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \
|
||||
$(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \
|
||||
$(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \
|
||||
lto-wrapper.o collect-utils.o
|
||||
$(GCOV_TOOL_OBJS) lto-wrapper.o collect-utils.o
|
||||
|
||||
# This lists all host object files, whether they are included in this
|
||||
# compilation or not.
|
||||
@ -1517,6 +1522,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn-codes.h \
|
||||
$(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \
|
||||
gcc-ranlib$(exeext) \
|
||||
gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \
|
||||
gcov-tool$(exeect) \
|
||||
gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \
|
||||
libcommon-target.a libcommon.a libgcc.mk
|
||||
|
||||
@ -2576,6 +2582,22 @@ GCOV_DUMP_OBJS = gcov-dump.o
|
||||
gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)
|
||||
+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \
|
||||
$(LIBS) -o $@
|
||||
|
||||
GCOV_TOOL_DEP_FILES = $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \
|
||||
$(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \
|
||||
$(srcdir)/../libgcc/libgcov-merge.c \
|
||||
$(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
|
||||
libgcov-util.o: $(srcdir)/../libgcc/libgcov-util.c $(GCOV_TOOL_DEP_FILES)
|
||||
+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $<
|
||||
libgcov-driver-tool.o: $(srcdir)/../libgcc/libgcov-driver.c $(GCOV_TOOL_DEP_FILES)
|
||||
+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
|
||||
-DIN_GCOV_TOOL=1 -o $@ $<
|
||||
libgcov-merge-tool.o: $(srcdir)/../libgcc/libgcov-merge.c $(GCOV_TOOL_DEP_FILES)
|
||||
+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
|
||||
-DIN_GCOV_TOOL=1 -o $@ $<
|
||||
GCOV_TOOL_OBJS = gcov-tool.o libgcov-util.o libgcov-driver-tool.o libgcov-merge-tool.o
|
||||
gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS)
|
||||
+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_TOOL_OBJS) $(LIBS) -o $@
|
||||
#
|
||||
# Build the include directories. The stamp files are stmp-* rather than
|
||||
# s-* so that mostlyclean does not force the include directory to
|
||||
@ -2804,7 +2826,8 @@ TEXI_GCC_FILES = gcc.texi gcc-common.texi gcc-vers.texi frontends.texi \
|
||||
contribute.texi compat.texi funding.texi gnu.texi gpl_v3.texi \
|
||||
fdl.texi contrib.texi cppenv.texi cppopts.texi avr-mmcu.texi \
|
||||
implement-c.texi implement-cxx.texi arm-neon-intrinsics.texi \
|
||||
arm-acle-intrinsics.texi aarch64-acle-intrinsics.texi
|
||||
arm-acle-intrinsics.texi aarch64-acle-intrinsics.texi \
|
||||
gcov-tool.texi
|
||||
|
||||
# we explicitly use $(srcdir)/doc/tm.texi here to avoid confusion with
|
||||
# the generated tm.texi; the latter might have a more recent timestamp,
|
||||
@ -2925,7 +2948,8 @@ $(build_htmldir)/gccinstall/index.html: $(TEXI_GCCINSTALL_FILES)
|
||||
DESTDIR=$(@D) \
|
||||
$(SHELL) $(srcdir)/doc/install.texi2html
|
||||
|
||||
MANFILES = doc/gcov.1 doc/cpp.1 doc/gcc.1 doc/gfdl.7 doc/gpl.7 doc/fsf-funding.7
|
||||
MANFILES = doc/gcov.1 doc/cpp.1 doc/gcc.1 doc/gfdl.7 doc/gpl.7 \
|
||||
doc/fsf-funding.7 doc/gcov-tool.1
|
||||
|
||||
generated-manpages: man
|
||||
|
||||
@ -3212,6 +3236,13 @@ install-common: native lang.install-common installdirs
|
||||
rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
|
||||
$(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \
|
||||
fi
|
||||
# Install gcov-tool if it was compiled.
|
||||
-if [ -f gcov-tool$(exeext) ]; \
|
||||
then \
|
||||
rm -f $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \
|
||||
$(INSTALL_PROGRAM) \
|
||||
gcov-tool$(exeext) $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \
|
||||
fi
|
||||
|
||||
# Install the driver program as $(target_noncanonical)-gcc,
|
||||
# $(target_noncanonical)-gcc-$(version), and also as gcc if native.
|
||||
@ -3317,6 +3348,11 @@ $(DESTDIR)$(man1dir)/$(GCOV_INSTALL_NAME)$(man1ext): doc/gcov.1 installdirs
|
||||
-$(INSTALL_DATA) $< $@
|
||||
-chmod a-x $@
|
||||
|
||||
$(DESTDIR)$(man1dir)/$(GCOV_TOOL_INSTALL_NAME)$(man1ext): doc/gcov-tool.1 installdirs
|
||||
-rm -f $@
|
||||
-$(INSTALL_DATA) $< $@
|
||||
-chmod a-x $@
|
||||
|
||||
# Install all the header files built in the include subdirectory.
|
||||
install-headers: $(INSTALL_HEADERS_DIR)
|
||||
# Fix symlinks to absolute paths in the installed include directory to
|
||||
|
@ -120,8 +120,19 @@ static unsigned bbg_file_stamp;
|
||||
static char *da_file_name;
|
||||
|
||||
/* The names of merge functions for counters. */
|
||||
static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS;
|
||||
static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES;
|
||||
#define STR(str) #str
|
||||
#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) STR(__gcov_merge ## FN_TYPE),
|
||||
static const char *const ctr_merge_functions[GCOV_COUNTERS] = {
|
||||
#include "gcov-counter.def"
|
||||
};
|
||||
#undef DEF_GCOV_COUNTER
|
||||
#undef STR
|
||||
|
||||
#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) NAME,
|
||||
static const char *const ctr_names[GCOV_COUNTERS] = {
|
||||
#include "gcov-counter.def"
|
||||
};
|
||||
#undef DEF_GCOV_COUNTER
|
||||
|
||||
/* Forward declarations. */
|
||||
static void read_counts_file (void);
|
||||
|
@ -66,6 +66,7 @@ Texts being (a) (see below), and with the Back-Cover Texts being (b)
|
||||
* gcc: (gcc). The GNU Compiler Collection.
|
||||
* g++: (gcc). The GNU C++ compiler.
|
||||
* gcov: (gcc) Gcov. @command{gcov}---a test coverage program.
|
||||
* gcov-tool: (gcc) Gcov-tool. @command{gcov-tool}---an offline gcda profile processing program.
|
||||
@end direntry
|
||||
This file documents the use of the GNU compilers.
|
||||
@sp 1
|
||||
@ -138,6 +139,7 @@ Introduction, gccint, GNU Compiler Collection (GCC) Internals}.
|
||||
* Objective-C:: GNU Objective-C runtime features.
|
||||
* Compatibility:: Binary Compatibility
|
||||
* Gcov:: @command{gcov}---a test coverage program.
|
||||
* Gcov-tool:: @command{gcov-tool}---an offline gcda profile processing program.
|
||||
* Trouble:: If you have trouble using GCC.
|
||||
* Bugs:: How, why and where to report bugs.
|
||||
* Service:: How To Get Help with GCC
|
||||
@ -164,6 +166,7 @@ Introduction, gccint, GNU Compiler Collection (GCC) Internals}.
|
||||
@include objc.texi
|
||||
@include compat.texi
|
||||
@include gcov.texi
|
||||
@include gcov-tool.texi
|
||||
@include trouble.texi
|
||||
@include bugreport.texi
|
||||
@include service.texi
|
||||
|
189
gcc/doc/gcov-tool.texi
Normal file
189
gcc/doc/gcov-tool.texi
Normal file
@ -0,0 +1,189 @@
|
||||
@c Copyright (C) 2014 Free Software Foundation, Inc.
|
||||
@c This is part of the GCC manual.
|
||||
@c For copying conditions, see the file gcc.texi.
|
||||
|
||||
@ignore
|
||||
@c man begin COPYRIGHT
|
||||
Copyright @copyright{} 2014 Free Software Foundation, Inc.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
any later version published by the Free Software Foundation; with the
|
||||
Invariant Sections being ``GNU General Public License'' and ``Funding
|
||||
Free Software'', the Front-Cover texts being (a) (see below), and with
|
||||
the Back-Cover Texts being (b) (see below). A copy of the license is
|
||||
included in the gfdl(7) man page.
|
||||
|
||||
(a) The FSF's Front-Cover Text is:
|
||||
|
||||
A GNU Manual
|
||||
|
||||
(b) The FSF's Back-Cover Text is:
|
||||
|
||||
You have freedom to copy and modify this GNU Manual, like GNU
|
||||
software. Copies published by the Free Software Foundation raise
|
||||
funds for GNU development.
|
||||
@c man end
|
||||
@c Set file name and title for the man page.
|
||||
@setfilename gcov-tool
|
||||
@settitle offline gcda profile processing tool
|
||||
@end ignore
|
||||
|
||||
@node Gcov-tool
|
||||
@chapter @command{gcov-tool}---an Offline Gcda Profile Processing Tool
|
||||
|
||||
@command{gcov-tool} is a tool you can use in conjunction with GCC to
|
||||
manipulate or process gcda profile files offline.
|
||||
|
||||
@menu
|
||||
* Gcov-tool Intro:: Introduction to gcov-tool.
|
||||
* Invoking Gcov-tool:: How to use gcov-tool.
|
||||
@end menu
|
||||
|
||||
@node Gcov-tool Intro
|
||||
@section Introduction to @command{gcov-tool}
|
||||
@c man begin DESCRIPTION
|
||||
|
||||
@command{gcov-tool} is an offline tool to process gcc's gcda profile files.
|
||||
|
||||
Current gcov-tool supports the following functionalities:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
merge two sets of profiles with weights.
|
||||
|
||||
@item
|
||||
read one set of profile and rewrite profile contents. One can scale or
|
||||
normalize the count values.
|
||||
@end itemize
|
||||
|
||||
Examples of the use cases for this tool are:
|
||||
@itemize @bullet
|
||||
@item
|
||||
Collect the profiles for different set of inputs, and use this tool to merge
|
||||
them. One can specify the weight to factor in the relative importance of
|
||||
each input.
|
||||
|
||||
@item
|
||||
Rewrite the profile after removing a subset of the gcda files, while maintaining
|
||||
the consistency of the summary and the histogram.
|
||||
|
||||
@item
|
||||
It can also be used to debug or libgcov code as the tools shares the majority
|
||||
code as the runtime library.
|
||||
@end itemize
|
||||
|
||||
Note that for the merging operation, this profile generated offline may
|
||||
contain slight different values from the online merged profile. Here are
|
||||
a list of typical differences:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
histogram difference: This offline tool recomputes the histogram after merging
|
||||
the counters. The resulting histogram, therefore, is precise. The online
|
||||
merging does not have this capability -- the histogram is merged from two
|
||||
histograms and the result is an approximation.
|
||||
|
||||
@item
|
||||
summary checksum difference: Summary checksum uses a CRC32 operation. The value
|
||||
depends on the link list order of gcov-info objects. This order is different in
|
||||
gcov-tool from that in the online merge. It's expected to have different
|
||||
summary checksums. It does not really matter as the compiler does not use this
|
||||
checksum anywhere.
|
||||
|
||||
@item
|
||||
value profile counter values difference: Some counter values for value profile
|
||||
are runtime dependent, like heap addresses. It's normal to see some difference
|
||||
in these kind of counters.
|
||||
@end itemize
|
||||
|
||||
@c man end
|
||||
|
||||
@node Invoking Gcov-tool
|
||||
@section Invoking @command{gcov-tool}
|
||||
|
||||
@smallexample
|
||||
gcov-tool @r{[}@var{global-options}@r{]} SUB_COMMAND
|
||||
@r{[}@var{sub_command-options}@r{]} @var{profile_dir}
|
||||
@end smallexample
|
||||
|
||||
@command{gcov-tool} accepts the following options:
|
||||
|
||||
@ignore
|
||||
@c man begin SYNOPSIS
|
||||
gcov-tool [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
|
||||
|
||||
gcov-tool merge [merge-options] @var{directory1} @var{directory2}
|
||||
[@option{-v}|@option{--verbose}]
|
||||
[@option{-o}|@option{ --output} @var{directory}]
|
||||
[@option{-w}|@option{--weight} @var{w1,w2}]
|
||||
|
||||
gcov-tool rewrite [rewrite-options] @var{directory}
|
||||
[@option{-v}|@option{--verbose}]
|
||||
[@option{-o}|@option{--output} @var{directory}]
|
||||
[@option{-s}|@option{--scale} @var{float_or_simple-frac_value}]
|
||||
[@option{-n}|@option{--normalize} @var{long_long_value}]
|
||||
@c man end
|
||||
@c man begin SEEALSO
|
||||
gpl(7), gfdl(7), fsf-funding(7), gcc(1), gcov(1) and the Info entry for
|
||||
@file{gcc}.
|
||||
@c man end
|
||||
@end ignore
|
||||
|
||||
@c man begin OPTIONS
|
||||
@table @gcctabopt
|
||||
@item -h
|
||||
@itemx --help
|
||||
Display help about using @command{gcov-tool} (on the standard output), and
|
||||
exit without doing any further processing.
|
||||
|
||||
@item -v
|
||||
@itemx --version
|
||||
Display the @command{gcov-tool} version number (on the standard output),
|
||||
and exit without doing any further processing.
|
||||
|
||||
@item merge
|
||||
Merge two profile directories.
|
||||
|
||||
@table @gcctabopt
|
||||
@item -v
|
||||
@itemx --verbose
|
||||
Set the verbose mode.
|
||||
|
||||
@item -o @var{directory}
|
||||
@itemx --output @var{directory}
|
||||
Set the output profile directory. Default output directory name is
|
||||
@var{merged_profile}.
|
||||
|
||||
@item -w @var{w1},@var{w2}
|
||||
@itemx --weight @var{w1},@var{w2}
|
||||
Set the merge weights of the @var{directory1} and @var{directory2},
|
||||
respectively. The default weights are 1 for both.
|
||||
@end table
|
||||
|
||||
@item rewrite
|
||||
Read the specified profile directory and rewrite to a new directory.
|
||||
|
||||
@table @gcctabopt
|
||||
@item -v
|
||||
@itemx --verbose
|
||||
Set the verbose mode.
|
||||
|
||||
@item -o @var{directory}
|
||||
@itemx --output @var{directory}
|
||||
Set the output profile directory. Default output name is @var{rewrite_profile}.
|
||||
|
||||
@item -s @var{float_or_simple-frac_value}
|
||||
@itemx --scale @var{float_or_simple-frac_value}
|
||||
Scale the profile counters. The specified value can be in floating point value,
|
||||
or simple fraction value form, such 1, 2, 2/3, and 5/3.
|
||||
|
||||
@item -n @var{long_long_value}
|
||||
@itemx --normalize <long_long_value>
|
||||
Normalize the profile. The specified value is the max counter value
|
||||
in the new profile.
|
||||
|
||||
@end table
|
||||
@end table
|
||||
|
||||
@c man end
|
54
gcc/gcov-counter.def
Normal file
54
gcc/gcov-counter.def
Normal file
@ -0,0 +1,54 @@
|
||||
/* Definitions for the gcov counters in the GNU compiler.
|
||||
Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Before including this file, define a macro:
|
||||
|
||||
DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE)
|
||||
|
||||
This macro will be expanded to all supported gcov counters, their
|
||||
names, or the type of handler functions. FN_TYPE will be
|
||||
expanded to a handler function, like in gcov_merge, it is
|
||||
expanded to __gcov_merge ## FN_TYPE. */
|
||||
|
||||
/* Arc transitions. */
|
||||
DEF_GCOV_COUNTER(GCOV_COUNTER_ARCS, "arcs", _add)
|
||||
|
||||
/* Histogram of value inside an interval. */
|
||||
DEF_GCOV_COUNTER(GCOV_COUNTER_V_INTERVAL, "interval", _add)
|
||||
|
||||
/* Histogram of exact power2 logarithm of a value. */
|
||||
DEF_GCOV_COUNTER(GCOV_COUNTER_V_POW2, "pow2", _add)
|
||||
|
||||
/* The most common value of expression. */
|
||||
DEF_GCOV_COUNTER(GCOV_COUNTER_V_SINGLE, "single", _single)
|
||||
|
||||
/* The most common difference between consecutive values of expression. */
|
||||
DEF_GCOV_COUNTER(GCOV_COUNTER_V_DELTA, "delta", _delta)
|
||||
|
||||
/* The most common indirect address. */
|
||||
DEF_GCOV_COUNTER(GCOV_COUNTER_V_INDIR, "indirect_call", _single)
|
||||
|
||||
/* Compute average value passed to the counter. */
|
||||
DEF_GCOV_COUNTER(GCOV_COUNTER_AVERAGE, "average", _add)
|
||||
|
||||
/* IOR of the all values passed to counter. */
|
||||
DEF_GCOV_COUNTER(GCOV_COUNTER_IOR, "ior", _ior)
|
||||
|
||||
/* Time profile collecting first run of a function */
|
||||
DEF_GCOV_COUNTER(GCOV_TIME_PROFILER, "time_profiler", _time_profile)
|
@ -422,7 +422,11 @@ static void
|
||||
tag_counters (const char *filename ATTRIBUTE_UNUSED,
|
||||
unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
|
||||
{
|
||||
static const char *const counter_names[] = GCOV_COUNTER_NAMES;
|
||||
#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME,
|
||||
static const char *const counter_names[] = {
|
||||
#include "gcov-counter.def"
|
||||
};
|
||||
#undef DEF_GCOV_COUNTER
|
||||
unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
|
||||
|
||||
printf (" %s %u counts",
|
||||
|
@ -64,7 +64,11 @@ GCOV_LINKAGE struct gcov_var
|
||||
} gcov_var;
|
||||
|
||||
/* Save the current position in the gcov file. */
|
||||
static inline gcov_position_t
|
||||
/* We need to expose this function when compiling for gcov-tool. */
|
||||
#ifndef IN_GCOV_TOOL
|
||||
static inline
|
||||
#endif
|
||||
gcov_position_t
|
||||
gcov_position (void)
|
||||
{
|
||||
gcov_nonruntime_assert (gcov_var.mode > 0);
|
||||
@ -72,7 +76,11 @@ gcov_position (void)
|
||||
}
|
||||
|
||||
/* Return nonzero if the error flag is set. */
|
||||
static inline int
|
||||
/* We need to expose this function when compiling for gcov-tool. */
|
||||
#ifndef IN_GCOV_TOOL
|
||||
static inline
|
||||
#endif
|
||||
int
|
||||
gcov_is_error (void)
|
||||
{
|
||||
return gcov_var.file ? gcov_var.error : 1;
|
||||
@ -557,11 +565,13 @@ gcov_read_counter (void)
|
||||
return value;
|
||||
}
|
||||
|
||||
/* We need to expose the below function when compiling for gcov-tool. */
|
||||
|
||||
#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
|
||||
/* Read string from coverage file. Returns a pointer to a static
|
||||
buffer, or NULL on empty string. You must copy the string before
|
||||
calling another gcov function. */
|
||||
|
||||
#if !IN_LIBGCOV
|
||||
GCOV_LINKAGE const char *
|
||||
gcov_read_string (void)
|
||||
{
|
||||
@ -642,7 +652,9 @@ gcov_read_summary (struct gcov_summary *summary)
|
||||
}
|
||||
}
|
||||
|
||||
#if !IN_LIBGCOV
|
||||
/* We need to expose the below function when compiling for gcov-tool. */
|
||||
|
||||
#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
|
||||
/* Reset to a known position. BASE should have been obtained from
|
||||
gcov_position, LENGTH should be a record length. */
|
||||
|
||||
|
@ -247,50 +247,29 @@ typedef uint64_t gcov_type_unsigned;
|
||||
|
||||
|
||||
/* Counters that are collected. */
|
||||
#define GCOV_COUNTER_ARCS 0 /* Arc transitions. */
|
||||
#define GCOV_COUNTERS_SUMMABLE 1 /* Counters which can be
|
||||
summaried. */
|
||||
#define GCOV_FIRST_VALUE_COUNTER 1 /* The first of counters used for value
|
||||
profiling. They must form a consecutive
|
||||
interval and their order must match
|
||||
the order of HIST_TYPEs in
|
||||
value-prof.h. */
|
||||
#define GCOV_COUNTER_V_INTERVAL 1 /* Histogram of value inside an interval. */
|
||||
#define GCOV_COUNTER_V_POW2 2 /* Histogram of exact power2 logarithm
|
||||
of a value. */
|
||||
#define GCOV_COUNTER_V_SINGLE 3 /* The most common value of expression. */
|
||||
#define GCOV_COUNTER_V_DELTA 4 /* The most common difference between
|
||||
consecutive values of expression. */
|
||||
|
||||
#define GCOV_COUNTER_V_INDIR 5 /* The most common indirect address */
|
||||
#define GCOV_COUNTER_AVERAGE 6 /* Compute average value passed to the
|
||||
counter. */
|
||||
#define GCOV_COUNTER_IOR 7 /* IOR of the all values passed to
|
||||
counter. */
|
||||
#define GCOV_TIME_PROFILER 8 /* Time profile collecting first run of a function */
|
||||
#define GCOV_LAST_VALUE_COUNTER 8 /* The last of counters used for value
|
||||
profiling. */
|
||||
#define GCOV_COUNTERS 9
|
||||
#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) COUNTER,
|
||||
enum {
|
||||
#include "gcov-counter.def"
|
||||
GCOV_COUNTERS
|
||||
};
|
||||
#undef DEF_GCOV_COUNTER
|
||||
|
||||
/* Counters which can be summaried. */
|
||||
#define GCOV_COUNTERS_SUMMABLE (GCOV_COUNTER_ARCS + 1)
|
||||
|
||||
/* The first of counters used for value profiling. They must form a
|
||||
consecutive interval and their order must match the order of
|
||||
HIST_TYPEs in value-prof.h. */
|
||||
#define GCOV_FIRST_VALUE_COUNTER GCOV_COUNTERS_SUMMABLE
|
||||
|
||||
/* The last of counters used for value profiling. */
|
||||
#define GCOV_LAST_VALUE_COUNTER (GCOV_COUNTERS - 1)
|
||||
|
||||
/* Number of counters used for value profiling. */
|
||||
#define GCOV_N_VALUE_COUNTERS \
|
||||
(GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1)
|
||||
|
||||
/* A list of human readable names of the counters */
|
||||
#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \
|
||||
"delta", "indirect_call", "average", "ior", "time_profiler"}
|
||||
|
||||
/* Names of merge functions for counters. */
|
||||
#define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \
|
||||
"__gcov_merge_add", \
|
||||
"__gcov_merge_add", \
|
||||
"__gcov_merge_single", \
|
||||
"__gcov_merge_delta", \
|
||||
"__gcov_merge_single", \
|
||||
"__gcov_merge_add", \
|
||||
"__gcov_merge_ior", \
|
||||
"__gcov_merge_time_profile" }
|
||||
|
||||
/* Convert a counter index to a tag. */
|
||||
#define GCOV_TAG_FOR_COUNTER(COUNT) \
|
||||
(GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17))
|
||||
|
478
gcc/gcov-tool.c
Normal file
478
gcc/gcov-tool.c
Normal file
@ -0,0 +1,478 @@
|
||||
/* Gcc offline profile processing tool support. */
|
||||
/* Copyright (C) 2014 Free Software Foundation, Inc.
|
||||
Contributed by Rong Xu <xur@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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tm.h"
|
||||
#include "intl.h"
|
||||
#include "diagnostic.h"
|
||||
#include "version.h"
|
||||
#include "gcov-io.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <ftw.h>
|
||||
#include <getopt.h>
|
||||
|
||||
extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
|
||||
extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
|
||||
extern int gcov_profile_scale (struct gcov_info*, float, int, int);
|
||||
extern struct gcov_info* gcov_read_profile_dir (const char*, int);
|
||||
extern void gcov_exit (void);
|
||||
extern void set_gcov_list (struct gcov_info *);
|
||||
extern void gcov_set_verbose (void);
|
||||
|
||||
/* Set to verbose output mode. */
|
||||
static bool verbose;
|
||||
|
||||
/* Remove file NAME if it has a gcda suffix. */
|
||||
|
||||
static int
|
||||
unlink_gcda_file (const char *name,
|
||||
const struct stat *status ATTRIBUTE_UNUSED,
|
||||
int type ATTRIBUTE_UNUSED,
|
||||
struct FTW *ftwbuf ATTRIBUTE_UNUSED)
|
||||
{
|
||||
int ret = 0;
|
||||
int len = strlen (name);
|
||||
int len1 = strlen (GCOV_DATA_SUFFIX);
|
||||
|
||||
if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1))
|
||||
ret = remove (name);
|
||||
|
||||
if (ret)
|
||||
fatal_error ("error in removing %s\n", name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Remove the gcda files in PATH recursively. */
|
||||
|
||||
static int
|
||||
unlink_profile_dir (const char *path)
|
||||
{
|
||||
return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS);
|
||||
}
|
||||
|
||||
/* Output GCOV_INFO lists PROFILE to directory OUT. Note that
|
||||
we will remove all the gcda files in OUT. */
|
||||
|
||||
static void
|
||||
gcov_output_files (const char *out, struct gcov_info *profile)
|
||||
{
|
||||
char *pwd;
|
||||
int ret;
|
||||
|
||||
/* Try to make directory if it doesn't already exist. */
|
||||
if (access (out, F_OK) == -1)
|
||||
{
|
||||
#ifdef TARGET_POSIX_IO
|
||||
if (mkdir (out, 0755) == -1 && errno != EEXIST)
|
||||
#else
|
||||
if (mkdir (out) == -1 && errno != EEXIST)
|
||||
#endif
|
||||
fatal_error ("Cannot make directory %s", out);
|
||||
} else
|
||||
unlink_profile_dir (out);
|
||||
|
||||
/* Output new profile. */
|
||||
pwd = getcwd (NULL, 0);
|
||||
|
||||
if (pwd == NULL)
|
||||
fatal_error ("Cannot get current directory name");
|
||||
|
||||
ret = chdir (out);
|
||||
if (ret)
|
||||
fatal_error ("Cannot change directory to %s", out);
|
||||
|
||||
set_gcov_list (profile);
|
||||
gcov_exit ();
|
||||
|
||||
ret = chdir (pwd);
|
||||
if (ret)
|
||||
fatal_error ("Cannot change directory to %s", pwd);
|
||||
|
||||
free (pwd);
|
||||
}
|
||||
|
||||
/* Merging profile D1 and D2 with weight as W1 and W2, respectively.
|
||||
The result profile is written to directory OUT.
|
||||
Return 0 on success. */
|
||||
|
||||
static int
|
||||
profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
|
||||
{
|
||||
struct gcov_info *d1_profile;
|
||||
struct gcov_info *d2_profile;
|
||||
int ret;
|
||||
|
||||
d1_profile = gcov_read_profile_dir (d1, 0);
|
||||
if (!d1_profile)
|
||||
return 1;
|
||||
|
||||
if (d2)
|
||||
{
|
||||
d2_profile = gcov_read_profile_dir (d2, 0);
|
||||
if (!d2_profile)
|
||||
return 1;
|
||||
|
||||
/* The actual merge: we overwrite to d1_profile. */
|
||||
ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
gcov_output_files (out, d1_profile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Usage message for profile merge. */
|
||||
|
||||
static void
|
||||
print_merge_usage_message (int error_p)
|
||||
{
|
||||
FILE *file = error_p ? stderr : stdout;
|
||||
|
||||
fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n");
|
||||
fnotice (file, " -v, --verbose Verbose mode\n");
|
||||
fnotice (file, " -o, --output <dir> Output directory\n");
|
||||
fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n");
|
||||
}
|
||||
|
||||
static const struct option merge_options[] =
|
||||
{
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "weight", required_argument, NULL, 'w' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/* Print merge usage and exit. */
|
||||
|
||||
static void
|
||||
merge_usage (void)
|
||||
{
|
||||
fnotice (stderr, "Merge subcomand usage:");
|
||||
print_merge_usage_message (true);
|
||||
exit (FATAL_EXIT_CODE);
|
||||
}
|
||||
|
||||
/* Driver for profile merge sub-command. */
|
||||
|
||||
static int
|
||||
do_merge (int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
int ret;
|
||||
const char *output_dir = 0;
|
||||
int w1 = 1, w2 = 1;
|
||||
|
||||
optind = 0;
|
||||
while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'v':
|
||||
verbose = true;
|
||||
gcov_set_verbose ();
|
||||
break;
|
||||
case 'o':
|
||||
output_dir = optarg;
|
||||
break;
|
||||
case 'w':
|
||||
sscanf (optarg, "%d,%d", &w1, &w2);
|
||||
if (w1 < 0 || w2 < 0)
|
||||
fatal_error ("weights need to be non-negative\n");
|
||||
break;
|
||||
default:
|
||||
merge_usage ();
|
||||
}
|
||||
}
|
||||
|
||||
if (output_dir == NULL)
|
||||
output_dir = "merged_profile";
|
||||
|
||||
if (argc - optind == 2)
|
||||
ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
|
||||
else
|
||||
merge_usage ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If N_VAL is no-zero, normalize the profile by setting the largest counter
|
||||
counter value to N_VAL and scale others counters proportionally.
|
||||
Otherwise, multiply the all counters by SCALE. */
|
||||
|
||||
static int
|
||||
profile_rewrite (const char *d1, const char *out, long long n_val,
|
||||
float scale, int n, int d)
|
||||
{
|
||||
struct gcov_info * d1_profile;
|
||||
|
||||
d1_profile = gcov_read_profile_dir (d1, 0);
|
||||
if (!d1_profile)
|
||||
return 1;
|
||||
|
||||
if (n_val)
|
||||
gcov_profile_normalize (d1_profile, (gcov_type) n_val);
|
||||
else
|
||||
gcov_profile_scale (d1_profile, scale, n, d);
|
||||
|
||||
gcov_output_files (out, d1_profile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Usage function for profile rewrite. */
|
||||
|
||||
static void
|
||||
print_rewrite_usage_message (int error_p)
|
||||
{
|
||||
FILE *file = error_p ? stderr : stdout;
|
||||
|
||||
fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n");
|
||||
fnotice (file, " -v, --verbose Verbose mode\n");
|
||||
fnotice (file, " -o, --output <dir> Output directory\n");
|
||||
fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n");
|
||||
fnotice (file, " -n, --normalize <long long> Normalize the profile\n");
|
||||
}
|
||||
|
||||
static const struct option rewrite_options[] =
|
||||
{
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "scale", required_argument, NULL, 's' },
|
||||
{ "normalize", required_argument, NULL, 'n' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/* Print profile rewrite usage and exit. */
|
||||
|
||||
static void
|
||||
rewrite_usage (void)
|
||||
{
|
||||
fnotice (stderr, "Rewrite subcommand usage:");
|
||||
print_rewrite_usage_message (true);
|
||||
exit (FATAL_EXIT_CODE);
|
||||
}
|
||||
|
||||
/* Driver for profile rewrite sub-command. */
|
||||
|
||||
static int
|
||||
do_rewrite (int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
int ret;
|
||||
const char *output_dir = 0;
|
||||
long long normalize_val = 0;
|
||||
float scale = 0.0;
|
||||
int numerator = 1;
|
||||
int denominator = 1;
|
||||
int do_scaling = 0;
|
||||
|
||||
optind = 0;
|
||||
while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'v':
|
||||
verbose = true;
|
||||
gcov_set_verbose ();
|
||||
break;
|
||||
case 'o':
|
||||
output_dir = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
if (!do_scaling)
|
||||
normalize_val = atoll (optarg);
|
||||
else
|
||||
fnotice (stderr, "scaling cannot co-exist with normalization,"
|
||||
" skipping\n");
|
||||
break;
|
||||
case 's':
|
||||
ret = 0;
|
||||
do_scaling = 1;
|
||||
if (strstr (optarg, "/"))
|
||||
{
|
||||
ret = sscanf (optarg, "%d/%d", &numerator, &denominator);
|
||||
if (ret == 2)
|
||||
{
|
||||
if (numerator < 0 || denominator <= 0)
|
||||
{
|
||||
fnotice (stderr, "incorrect format in scaling, using 1/1\n");
|
||||
denominator = 1;
|
||||
numerator = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret != 2)
|
||||
{
|
||||
ret = sscanf (optarg, "%f", &scale);
|
||||
if (ret != 1)
|
||||
fnotice (stderr, "incorrect format in scaling, using 1/1\n");
|
||||
else
|
||||
denominator = 0;
|
||||
}
|
||||
|
||||
if (scale < 0.0)
|
||||
fatal_error ("scale needs to be non-negative\n");
|
||||
|
||||
if (normalize_val != 0)
|
||||
{
|
||||
fnotice (stderr, "normalization cannot co-exist with scaling\n");
|
||||
normalize_val = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rewrite_usage ();
|
||||
}
|
||||
}
|
||||
|
||||
if (output_dir == NULL)
|
||||
output_dir = "rewrite_profile";
|
||||
|
||||
if (argc - optind == 1)
|
||||
{
|
||||
if (denominator > 0)
|
||||
ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator);
|
||||
else
|
||||
ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0);
|
||||
}
|
||||
else
|
||||
rewrite_usage ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
|
||||
otherwise the output of --help. */
|
||||
|
||||
static void
|
||||
print_usage (int error_p)
|
||||
{
|
||||
FILE *file = error_p ? stderr : stdout;
|
||||
int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
|
||||
|
||||
fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname);
|
||||
fnotice (file, "Offline tool to handle gcda counts\n\n");
|
||||
fnotice (file, " -h, --help Print this help, then exit\n");
|
||||
fnotice (file, " -v, --version Print version number, then exit\n");
|
||||
print_merge_usage_message (error_p);
|
||||
print_rewrite_usage_message (error_p);
|
||||
fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
|
||||
bug_report_url);
|
||||
exit (status);
|
||||
}
|
||||
|
||||
/* Print version information and exit. */
|
||||
|
||||
static void
|
||||
print_version (void)
|
||||
{
|
||||
fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string);
|
||||
fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n",
|
||||
_("(C)"));
|
||||
fnotice (stdout,
|
||||
_("This is free software; see the source for copying conditions.\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or \n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
|
||||
exit (SUCCESS_EXIT_CODE);
|
||||
}
|
||||
|
||||
static const struct option options[] =
|
||||
{
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/* Process args, return index to first non-arg. */
|
||||
|
||||
static int
|
||||
process_args (int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'h':
|
||||
print_usage (false);
|
||||
/* Print_usage will exit. */
|
||||
case 'v':
|
||||
print_version ();
|
||||
/* Print_version will exit. */
|
||||
default:
|
||||
print_usage (true);
|
||||
/* Print_usage will exit. */
|
||||
}
|
||||
}
|
||||
|
||||
return optind;
|
||||
}
|
||||
|
||||
/* Main function for gcov-tool. */
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
const char *p;
|
||||
const char *sub_command;
|
||||
|
||||
p = argv[0] + strlen (argv[0]);
|
||||
while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
|
||||
--p;
|
||||
progname = p;
|
||||
|
||||
xmalloc_set_program_name (progname);
|
||||
|
||||
/* Unlock the stdio streams. */
|
||||
unlock_std_streams ();
|
||||
|
||||
gcc_init_libintl ();
|
||||
|
||||
diagnostic_initialize (global_dc, 0);
|
||||
|
||||
/* Handle response files. */
|
||||
expandargv (&argc, &argv);
|
||||
|
||||
process_args (argc, argv);
|
||||
if (optind >= argc)
|
||||
print_usage (true);
|
||||
|
||||
sub_command = argv[optind];
|
||||
|
||||
if (!strcmp (sub_command, "merge"))
|
||||
return do_merge (argc - optind, argv + optind);
|
||||
else if (!strcmp (sub_command, "rewrite"))
|
||||
return do_rewrite (argc - optind, argv + optind);
|
||||
|
||||
print_usage (true);
|
||||
}
|
@ -1,3 +1,50 @@
|
||||
2014-07-10 Rong Xu <xur@google.com>
|
||||
|
||||
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-06-23 Kai Tietz <ktietz@redhat.com>
|
||||
|
||||
PR libgcc/61585
|
||||
|
@ -81,7 +81,11 @@ set_gcov_list (struct gcov_info *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;
|
||||
|
@ -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,7 +65,7 @@ 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
|
||||
|
||||
@ -81,7 +81,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 +109,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 +148,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;
|
||||
|
870
libgcc/libgcov-util.c
Normal file
870
libgcc/libgcov-util.c
Normal file
@ -0,0 +1,870 @@
|
||||
/* 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 <xur@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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
#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 gcov_unsigned_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 <unistd.h>
|
||||
#include <ftw.h>
|
||||
|
||||
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);
|
||||
|
||||
/* The gcov_info for the first module. */
|
||||
static struct gcov_info *curr_gcov_info;
|
||||
/* The gcov_info being processed. */
|
||||
static struct gcov_info *gcov_info_head;
|
||||
/* This variable contains all the functions in current module. */
|
||||
static struct obstack fn_info;
|
||||
/* The function being processed. */
|
||||
static struct gcov_fn_info *curr_fn_info;
|
||||
/* The number of functions seen so far. */
|
||||
static unsigned num_fn_info;
|
||||
/* This variable contains all the counters for current module. */
|
||||
static int k_ctrs_mask[GCOV_COUNTERS];
|
||||
/* The kind of counters that have been seen. */
|
||||
static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS];
|
||||
/* Number of kind of counters that have been seen. */
|
||||
static int k_ctrs_types;
|
||||
/* The longest length of all the filenames. */
|
||||
static int max_filename_len;
|
||||
|
||||
/* Merge functions for counters. */
|
||||
#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},
|
||||
{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];
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
{
|
||||
char *str_dup = (char*) xmalloc (strlen (filename) + 1);
|
||||
int len;
|
||||
|
||||
strcpy (str_dup, filename);
|
||||
obj_info->filename = str_dup;
|
||||
if ((len = strlen (filename)) > max_filename_len)
|
||||
max_filename_len = len;
|
||||
}
|
||||
|
||||
/* Read stamp. */
|
||||
obj_info->stamp = gcov_read_unsigned ();
|
||||
|
||||
while (1)
|
||||
{
|
||||
gcov_position_t base;
|
||||
unsigned tag, length;
|
||||
tag_format_t const *format;
|
||||
unsigned tag_depth;
|
||||
int error;
|
||||
unsigned mask;
|
||||
|
||||
tag = gcov_read_unsigned ();
|
||||
if (!tag)
|
||||
break;
|
||||
length = gcov_read_unsigned ();
|
||||
base = gcov_position ();
|
||||
mask = GCOV_TAG_MASK (tag) >> 1;
|
||||
for (tag_depth = 4; mask; mask >>= 8)
|
||||
{
|
||||
if (((mask & 0xff) != 0xff))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* This will be called by ftw(). It opens and read 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)
|
||||
{
|
||||
int filename_len;
|
||||
int suffix_len;
|
||||
struct gcov_info *obj_info;
|
||||
|
||||
/* Only read regular files. */
|
||||
if (type != FTW_F)
|
||||
return 0;
|
||||
|
||||
filename_len = strlen (filename);
|
||||
suffix_len = strlen (GCOV_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;
|
||||
|
||||
obj_info->next = gcov_info_head;
|
||||
gcov_info_head = obj_info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
ftw (".", ftw_read_file, 50);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (verbose)
|
||||
fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val);
|
||||
|
||||
return gcov_profile_scale (profile, scale_factor, 0, 0);
|
||||
}
|
@ -33,6 +33,10 @@
|
||||
#define xcalloc calloc
|
||||
#endif
|
||||
|
||||
#ifndef IN_GCOV_TOOL
|
||||
/* About the target. */
|
||||
/* This path will be used by libgcov runtime. */
|
||||
|
||||
#include "tconfig.h"
|
||||
#include "tsystem.h"
|
||||
#include "coretypes.h"
|
||||
@ -79,6 +83,43 @@ 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
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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 */
|
||||
|
||||
#if defined(inhibit_libc)
|
||||
#define IN_LIBGCOV (-1)
|
||||
#else
|
||||
@ -159,8 +200,13 @@ struct gcov_info
|
||||
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 */
|
||||
};
|
||||
|
||||
/* Register a new object file module. */
|
||||
@ -224,6 +270,46 @@ GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
|
||||
GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) 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 */
|
||||
|
Loading…
Reference in New Issue
Block a user