Commit ca29da43 by Nathan Sidwell

Break out coverage routines to new file.

        * Makefile.in (COVERAGE_H): New variable
        (C_OBJS): Add coverage.o
        (coverage.o): New target.
        (profile.o, loop-init.o, sched-ebb.o, predict.o, tracer.o): Adjust
        dependencies.
        (GTFILES): Adjust.
        (gt-coverage.h): New target.
        (gt-profile.h): Remove.
        * profile.h: Remove. Move to ...
        * coverage.h: ... here. New. #include gcov-io.h.
        * gcov-io.h: Move function definitions to ...
        * gcov-io.c: ... here. New.
        * profile.c: Move coverage routines to coverage.c.
        (instrument_edges, get_exec_counts, branch_prob, init_branch_prob,
        end_branch_prob): Adjust.
        * coverage.c: New. Coverage routines from profile.c
        (coverage_counter_ref, coverage_init, coverage_finish,
        coverage_end_function, coverage_begin_output,
        coverage_counter_ref, get_coverage_counts): Define.
        * gcov-dump.c, gcov.c: #include gcov-io.c.
        * libgcov.c: Likewise. Adjust.
        * loop-init.c: Don't #include profile.h
        * tracer.c, predict.c, sched-ebb.c: Adjust #includes.
        * rtl.h: Add coverage prototypes.
        * toplev.c (compile_file): Init coverage, not branch_prob.
        Always call coverage_finish.
        (rest_of_compilation): Call coverage_end_function.

From-SVN: r65897
parent 1774b213
2003-04-21 Nathan Sidwell <nathan@codesourcery.com>
Break out coverage routines to new file.
* Makefile.in (COVERAGE_H): New variable
(C_OBJS): Add coverage.o
(coverage.o): New target.
(profile.o, loop-init.o, sched-ebb.o, predict.o, tracer.o): Adjust
dependencies.
(GTFILES): Adjust.
(gt-coverage.h): New target.
(gt-profile.h): Remove.
* profile.h: Remove. Move to ...
* coverage.h: ... here. New. #include gcov-io.h.
* gcov-io.h: Move function definitions to ...
* gcov-io.c: ... here. New.
* profile.c: Move coverage routines to coverage.c.
(instrument_edges, get_exec_counts, branch_prob, init_branch_prob,
end_branch_prob): Adjust.
* coverage.c: New. Coverage routines from profile.c
(coverage_counter_ref, coverage_init, coverage_finish,
coverage_end_function, coverage_begin_output,
coverage_counter_ref, get_coverage_counts): Define.
* gcov-dump.c, gcov.c: #include gcov-io.c.
* libgcov.c: Likewise. Adjust.
* loop-init.c: Don't #include profile.h
* tracer.c, predict.c, sched-ebb.c: Adjust #includes.
* rtl.h: Add coverage prototypes.
* toplev.c (compile_file): Init coverage, not branch_prob.
Always call coverage_finish.
(rest_of_compilation): Call coverage_end_function.
2003-04-21 Matt Kraai <kraai@alumni.cmu.edu> 2003-04-21 Matt Kraai <kraai@alumni.cmu.edu>
* config/rs6000/rs6000.md (*movsf_softfloat): Add "h" <- "0" case. * config/rs6000/rs6000.md (*movsf_softfloat): Add "h" <- "0" case.
...@@ -417,12 +448,14 @@ Sat Apr 19 14:56:17 CEST 2003 Jan Hubicka <jh@suse.cz> ...@@ -417,12 +448,14 @@ Sat Apr 19 14:56:17 CEST 2003 Jan Hubicka <jh@suse.cz>
Fri Apr 18 01:28:51 CEST 2003 Jan Hubicka <jh@suse.cz> Fri Apr 18 01:28:51 CEST 2003 Jan Hubicka <jh@suse.cz>
* gcov-dump.c (tag_summary): Remove max_sum * gcov-dump.c (tag_summary): Remove max_sum
* gcov-io.h (gcov_summary, gcov_write_summary, gcov_read_summary): Kill * gcov-io.h (gcov_summary, gcov_write_summary,
max_sum. gcov_read_summary): Kill max_sum.
* libgcov.c (gcov_exit): Do one pass over the data. Make error message * libgcov.c (gcov_exit): Do one pass over the data. Make error
more verbose. message more verbose.
* emit-rtl.c (subreg_hard_regno): Check that register is representable. * emit-rtl.c (subreg_hard_regno): Check that register is
representable.
* reload.c (reload_inner_reg_of_subreg): When register is not * reload.c (reload_inner_reg_of_subreg): When register is not
representable, reload the whole thing. representable, reload the whole thing.
(find_reloads): Likewsie. (find_reloads): Likewsie.
...@@ -430,7 +463,8 @@ Fri Apr 18 01:28:51 CEST 2003 Jan Hubicka <jh@suse.cz> ...@@ -430,7 +463,8 @@ Fri Apr 18 01:28:51 CEST 2003 Jan Hubicka <jh@suse.cz>
* profile.c (compute_branch_probabilities): Cleanup sanity checking; * profile.c (compute_branch_probabilities): Cleanup sanity checking;
allow negative probabilities for edges from the call to exit. allow negative probabilities for edges from the call to exit.
(branch_prob): Do not add fake edges for functions that may return twice (branch_prob): Do not add fake edges for functions that may return
twice.
2003-04-17 DJ Delorie <dj@redhat.com> 2003-04-17 DJ Delorie <dj@redhat.com>
......
...@@ -636,6 +636,7 @@ TREE_H = tree.h tree.def $(MACHMODE_H) tree-check.h version.h builtins.def \ ...@@ -636,6 +636,7 @@ TREE_H = tree.h tree.def $(MACHMODE_H) tree-check.h version.h builtins.def \
location.h location.h
BASIC_BLOCK_H = basic-block.h bitmap.h sbitmap.h varray.h $(PARTITION_H) \ BASIC_BLOCK_H = basic-block.h bitmap.h sbitmap.h varray.h $(PARTITION_H) \
hard-reg-set.h hard-reg-set.h
COVERAGE_H = coverage.h gcov-io.h gcov-iov.h
DEMANGLE_H = $(srcdir)/../include/demangle.h DEMANGLE_H = $(srcdir)/../include/demangle.h
RECOG_H = recog.h RECOG_H = recog.h
EXPR_H = expr.h EXPR_H = expr.h
...@@ -803,8 +804,8 @@ C_OBJS = c-parse.o c-lang.o c-pretty-print.o $(C_AND_OBJC_OBJS) ...@@ -803,8 +804,8 @@ C_OBJS = c-parse.o c-lang.o c-pretty-print.o $(C_AND_OBJC_OBJS)
OBJS = alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o \ OBJS = alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o \
cfg.o cfganal.o cfgbuild.o cfgcleanup.o cfglayout.o cfgloop.o \ cfg.o cfganal.o cfgbuild.o cfgcleanup.o cfglayout.o cfgloop.o \
cfgloopanal.o cfgloopmanip.o loop-init.o loop-unswitch.o loop-unroll.o \ cfgloopanal.o cfgloopmanip.o loop-init.o loop-unswitch.o loop-unroll.o \
cfgrtl.o combine.o conflict.o convert.o cse.o cselib.o dbxout.o \ cfgrtl.o combine.o conflict.o convert.o coverage.o cse.o cselib.o \
debug.o df.o diagnostic.o dojump.o doloop.o dominance.o \ dbxout.o debug.o df.o diagnostic.o dojump.o doloop.o dominance.o \
dwarf2asm.o dwarf2out.o dwarfout.o emit-rtl.o except.o explow.o \ dwarf2asm.o dwarf2out.o dwarfout.o emit-rtl.o except.o explow.o \
expmed.o expr.o final.o flow.o fold-const.o function.o gcse.o \ expmed.o expr.o final.o flow.o fold-const.o function.o gcse.o \
genrtl.o ggc-common.o global.o graph.o gtype-desc.o \ genrtl.o ggc-common.o global.o graph.o gtype-desc.o \
...@@ -1584,6 +1585,10 @@ cgraph.o : cgraph.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ ...@@ -1584,6 +1585,10 @@ cgraph.o : cgraph.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
langhooks.h tree-inline.h toplev.h flags.h ggc.h $(TARGET_H) cgraph.h gt-cgraph.h langhooks.h tree-inline.h toplev.h flags.h ggc.h $(TARGET_H) cgraph.h gt-cgraph.h
cgraphunit.o : cgraphunit.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ cgraphunit.o : cgraphunit.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
langhooks.h tree-inline.h toplev.h flags.h ggc.h $(TARGET_H) cgraph.h langhooks.h tree-inline.h toplev.h flags.h ggc.h $(TARGET_H) cgraph.h
coverage.o : coverage.c gcov-io.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TM_H) $(RTL_H) $(TREE_H) flags.h output.h $(REGS_H) $(EXPR_H) function.h \
toplev.h $(GGC_H) $(TARGET_H) langhooks.h $(COVERAGE_H) libfuncs.h \
gt-coverage.h $(HASHTAB_H)
cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \ cselib.o : cselib.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(REGS_H) \
hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \ hard-reg-set.h flags.h real.h insn-config.h $(RECOG_H) $(EXPR_H) toplev.h \
output.h function.h cselib.h $(GGC_H) $(TM_P_H) gt-cselib.h output.h function.h cselib.h $(GGC_H) $(TM_P_H) gt-cselib.h
...@@ -1615,10 +1620,9 @@ df.o : df.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ ...@@ -1615,10 +1620,9 @@ df.o : df.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(BASIC_BLOCK_H) df.h $(FIBHEAP_H) $(BASIC_BLOCK_H) df.h $(FIBHEAP_H)
conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(OBSTACK_H) \ conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(OBSTACK_H) \
$(HASHTAB_H) $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H) $(HASHTAB_H) $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H)
profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
flags.h insn-config.h output.h $(REGS_H) $(EXPR_H) function.h \ $(TREE_H) flags.h output.h $(REGS_H) $(EXPR_H) function.h \
gcov-io.h gcov-iov.h toplev.h $(GGC_H) hard-reg-set.h $(BASIC_BLOCK_H) \ toplev.h $(BASIC_BLOCK_H) $(COVERAGE_H)
$(TARGET_H) langhooks.h profile.h libfuncs.h gt-profile.h $(HASHTAB_H)
loop.o : loop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) flags.h $(LOOP_H) \ loop.o : loop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) flags.h $(LOOP_H) \
insn-config.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) \ insn-config.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) \
real.h $(PREDICT_H) $(BASIC_BLOCK_H) function.h cfgloop.h \ real.h $(PREDICT_H) $(BASIC_BLOCK_H) function.h cfgloop.h \
...@@ -1656,7 +1660,7 @@ cfgloopanal.o : cfgloopanal.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \ ...@@ -1656,7 +1660,7 @@ cfgloopanal.o : cfgloopanal.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \
cfgloopmanip.o : cfgloopmanip.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \ cfgloopmanip.o : cfgloopmanip.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \
$(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h output.h coretypes.h $(TM_H) $(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h output.h coretypes.h $(TM_H)
loop-init.o : loop-init.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \ loop-init.o : loop-init.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \
$(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h profile.h \ $(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h \
coretypes.h $(TM_H) coretypes.h $(TM_H)
loop-unswitch.o : loop-unswitch.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_H) \ loop-unswitch.o : loop-unswitch.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_H) \
$(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h params.h \ $(BASIC_BLOCK_H) hard-reg-set.h cfgloop.h cfglayout.h params.h \
...@@ -1728,7 +1732,7 @@ sched-rgn.o : sched-rgn.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ ...@@ -1728,7 +1732,7 @@ sched-rgn.o : sched-rgn.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(TARGET_H) $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(TARGET_H)
sched-ebb.o : sched-ebb.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ sched-ebb.o : sched-ebb.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
sched-int.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \ sched-int.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \
$(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(PARAMS_H) profile.h $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h $(TM_P_H) $(PARAMS_H)
sched-vis.o : sched-vis.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ sched-vis.o : sched-vis.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
sched-int.h hard-reg-set.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(REGS_H) $(TM_P_H) \ sched-int.h hard-reg-set.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(REGS_H) $(TM_P_H) \
$(TARGET_H) real.h $(TARGET_H) real.h
...@@ -1747,14 +1751,14 @@ sreal.o: sreal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) sreal.h ...@@ -1747,14 +1751,14 @@ sreal.o: sreal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) sreal.h
predict.o: predict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ predict.o: predict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
flags.h insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h \ flags.h insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h \
$(RECOG_H) function.h except.h $(EXPR_H) $(TM_P_H) $(PREDICT_H) sreal.h \ $(RECOG_H) function.h except.h $(EXPR_H) $(TM_P_H) $(PREDICT_H) sreal.h \
$(PARAMS_H) $(TARGET_H) cfgloop.h $(PARAMS_H) $(TARGET_H) cfgloop.h $(COVERAGE_H)
lists.o: lists.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) toplev.h $(RTL_H) $(GGC_H) lists.o: lists.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) toplev.h $(RTL_H) $(GGC_H)
bb-reorder.o : bb-reorder.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ bb-reorder.o : bb-reorder.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_H) $(BASIC_BLOCK_H) flags.h output.h cfglayout.h $(FIBHEAP_H) \ $(RTL_H) $(BASIC_BLOCK_H) flags.h output.h cfglayout.h $(FIBHEAP_H) \
$(TARGET_H) $(TARGET_H)
tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
$(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h flags.h \ $(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h flags.h \
$(PARAMS_H) profile.h $(PARAMS_H) $(COVERAGE_H)
cfglayout.o : cfglayout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ cfglayout.o : cfglayout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_H) $(TREE_H) insn-config.h $(BASIC_BLOCK_H) hard-reg-set.h output.h \ $(RTL_H) $(TREE_H) insn-config.h $(BASIC_BLOCK_H) hard-reg-set.h output.h \
function.h cfglayout.h cfgloop.h $(TARGET_H) function.h cfglayout.h cfgloop.h $(TARGET_H)
...@@ -1966,10 +1970,10 @@ s-preds: genpreds$(build_exeext) $(srcdir)/move-if-change ...@@ -1966,10 +1970,10 @@ s-preds: genpreds$(build_exeext) $(srcdir)/move-if-change
GTFILES = $(srcdir)/location.h $(srcdir)/coretypes.h $(srcdir)/cpplib.h \ GTFILES = $(srcdir)/location.h $(srcdir)/coretypes.h $(srcdir)/cpplib.h \
$(host_xm_file_list) $(tm_file_list) $(HASHTAB_H) $(SPLAY_TREE_H) \ $(host_xm_file_list) $(tm_file_list) $(HASHTAB_H) $(SPLAY_TREE_H) \
$(srcdir)/bitmap.h $(srcdir)/function.h $(srcdir)/rtl.h $(srcdir)/optabs.h \ $(srcdir)/bitmap.h $(srcdir)/coverage.c $(srcdir)/function.h $(srcdir)/rtl.h \
$(srcdir)/tree.h $(srcdir)/libfuncs.h $(srcdir)/hashtable.h $(srcdir)/real.h \ $(srcdir)/optabs.h $(srcdir)/tree.h $(srcdir)/libfuncs.h $(srcdir)/hashtable.h \
$(srcdir)/varray.h $(srcdir)/ssa.h $(srcdir)/insn-addr.h $(srcdir)/cselib.h \ $(srcdir)/real.h $(srcdir)/varray.h $(srcdir)/ssa.h $(srcdir)/insn-addr.h \
$(srcdir)/basic-block.h $(srcdir)/location.h \ $(srcdir)/cselib.h $(srcdir)/basic-block.h $(srcdir)/location.h \
$(srcdir)/c-common.h $(srcdir)/c-tree.h \ $(srcdir)/c-common.h $(srcdir)/c-tree.h \
$(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \ $(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \
$(srcdir)/dbxout.c $(srcdir)/dwarf2out.c $(srcdir)/dwarf2asm.c \ $(srcdir)/dbxout.c $(srcdir)/dwarf2out.c $(srcdir)/dwarf2asm.c \
...@@ -1988,14 +1992,15 @@ GTFILES_FILES_FILES = @all_gtfiles_files_files@ ...@@ -1988,14 +1992,15 @@ GTFILES_FILES_FILES = @all_gtfiles_files_files@
GTFILES_LANG_DIR_NAMES = @subdirs@ GTFILES_LANG_DIR_NAMES = @subdirs@
GTFILES_SRCDIR = @srcdir@ GTFILES_SRCDIR = @srcdir@
gt-cgraph.h gtype-desc.h gtype-desc.c gt-except.h gt-function.h : s-gtype; @true gt-cgraph.h gt-coverage.h gtype-desc.h gtype-desc.c gt-except.h \
gt-integrate.h gt-stmt.h gt-tree.h gt-varasm.h gt-emit-rtl.h : s-gtype; @true gt-function.h gt-integrate.h gt-stmt.h gt-tree.h gt-varasm.h \
gt-explow.h gt-stor-layout.h gt-regclass.h gt-lists.h : s-gtype; @true gt-emit-rtl.h gt-explow.h gt-stor-layout.h gt-regclass.h \
gt-alias.h gt-cselib.h gt-fold-const.h gt-gcse.h gt-profile.h : s-gtype; @true gt-lists.h gt-alias.h gt-cselib.h gt-fold-const.h gt-gcse.h \
gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h gt-dwarf2out.h : s-gtype ; @true gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h \
gt-ra-build.h gt-reg-stack.h gt-dwarf2asm.h gt-dbxout.h : s-gtype ; @true gt-dwarf2out.h gt-ra-build.h gt-reg-stack.h gt-dwarf2asm.h \
gt-c-common.h gt-c-decl.h gt-c-parse.h gt-c-pragma.h : s-gtype; @true gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parse.h \
gt-c-objc-common.h gtype-c.h gt-location.h gt-stringpool.h : s-gtype ; @true gt-c-pragma.h gt-c-objc-common.h gtype-c.h gt-location.h \
gt-stringpool.h : s-gtype ; @true
gtyp-gen.h: Makefile gtyp-gen.h: Makefile
echo "/* This file is machine generated. Do not edit. */" > tmp-gtyp.h echo "/* This file is machine generated. Do not edit. */" > tmp-gtyp.h
......
/* Read and write coverage files, and associated functionality.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
2000, 2001, 2003 Free Software Foundation, Inc.
Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
based on some ideas from Dain Samples of UC Berkeley.
Further mangling by Bob Manson, Cygnus Support.
Further mangled by Nathan Sidwell, CodeSourcery
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.
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. */
#define GCOV_LINKAGE
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "output.h"
#include "regs.h"
#include "expr.h"
#include "function.h"
#include "toplev.h"
#include "ggc.h"
#include "target.h"
#include "coverage.h"
#include "libfuncs.h"
#include "langhooks.h"
#include "hashtab.h"
#include "gcov-io.c"
struct function_list
{
struct function_list *next; /* next function */
const char *name; /* function name */
unsigned cfg_checksum; /* function checksum */
unsigned n_counter_sections; /* number of counter sections */
struct gcov_counter_section counter_sections[MAX_COUNTER_SECTIONS];
/* the sections */
};
/* Counts information for a function. */
typedef struct counts_entry
{
/* We hash by */
char *function_name;
unsigned section;
/* Store */
unsigned checksum;
unsigned n_counts;
gcov_type *counts;
unsigned merged;
gcov_type max_counter;
gcov_type max_counter_sum;
/* Workspace */
struct counts_entry *chain;
} counts_entry_t;
static struct function_list *functions_head = 0;
static struct function_list **functions_tail = &functions_head;
/* Instantiate the profile info structure. */
struct profile_info profile_info;
/* Name of the output file for coverage output file. */
static char *bbg_file_name;
static unsigned bbg_file_opened;
static int bbg_function_announced;
/* Name of the count data file. */
static char *da_file_name;
/* Hash table of count data. */
static htab_t counts_hash = NULL;
/* The name of the count table. Used by the edge profiling code. */
static GTY(()) rtx profiler_label;
/* Forward declarations. */
static hashval_t htab_counts_entry_hash PARAMS ((const void *));
static int htab_counts_entry_eq PARAMS ((const void *, const void *));
static void htab_counts_entry_del PARAMS ((void *));
static void read_counts_file PARAMS ((void));
static unsigned compute_checksum PARAMS ((void));
static unsigned checksum_string PARAMS ((unsigned, const char *));
static void set_purpose PARAMS ((tree, tree));
static rtx label_for_tag PARAMS ((unsigned));
static tree build_counter_section_fields PARAMS ((void));
static tree build_counter_section_value PARAMS ((unsigned, unsigned));
static tree build_counter_section_data_fields PARAMS ((void));
static tree build_counter_section_data_value PARAMS ((unsigned, unsigned));
static tree build_function_info_fields PARAMS ((void));
static tree build_function_info_value PARAMS ((struct function_list *));
static tree build_gcov_info_fields PARAMS ((tree));
static tree build_gcov_info_value PARAMS ((void));
static void create_coverage PARAMS ((void));
static hashval_t
htab_counts_entry_hash (of)
const void *of;
{
const counts_entry_t *entry = of;
return htab_hash_string (entry->function_name) ^ entry->section;
}
static int
htab_counts_entry_eq (of1, of2)
const void *of1;
const void *of2;
{
const counts_entry_t *entry1 = of1;
const counts_entry_t *entry2 = of2;
return !strcmp (entry1->function_name, entry2->function_name)
&& entry1->section == entry2->section;
}
static void
htab_counts_entry_del (of)
void *of;
{
counts_entry_t *entry = of;
free (entry->function_name);
free (entry->counts);
free (entry);
}
/* Read in the counts file, if available. */
static void
read_counts_file ()
{
char *function_name_buffer = NULL;
unsigned version, ix, checksum = -1;
counts_entry_t *summaried = NULL;
unsigned seen_summary = 0;
if (!gcov_open (da_file_name, 1))
return;
if (gcov_read_unsigned () != GCOV_DATA_MAGIC)
{
warning ("`%s' is not a gcov data file", da_file_name);
gcov_close ();
return;
}
else if ((version = gcov_read_unsigned ()) != GCOV_VERSION)
{
char v[4], e[4];
unsigned required = GCOV_VERSION;
for (ix = 4; ix--; required >>= 8, version >>= 8)
{
v[ix] = version;
e[ix] = required;
}
warning ("`%s' is version `%.4s', expected version `%.4s'",
da_file_name, v, e);
gcov_close ();
return;
}
counts_hash = htab_create (10,
htab_counts_entry_hash, htab_counts_entry_eq,
htab_counts_entry_del);
while (!gcov_is_eof ())
{
unsigned tag, length;
unsigned long offset;
int error;
tag = gcov_read_unsigned ();
length = gcov_read_unsigned ();
offset = gcov_position ();
if (tag == GCOV_TAG_FUNCTION)
{
const char *string = gcov_read_string ();
free (function_name_buffer);
function_name_buffer = string ? xstrdup (string) : NULL;
checksum = gcov_read_unsigned ();
if (seen_summary)
{
/* We have already seen a summary, this means that this
new function begins a new set of program runs. We
must unlink the summaried chain. */
counts_entry_t *entry, *chain;
for (entry = summaried; entry; entry = chain)
{
chain = entry->chain;
entry->max_counter_sum += entry->max_counter;
entry->chain = NULL;
}
summaried = NULL;
seen_summary = 0;
}
}
else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
{
counts_entry_t *entry;
struct gcov_summary summary;
gcov_read_summary (&summary);
seen_summary = 1;
for (entry = summaried; entry; entry = entry->chain)
{
entry->merged += summary.runs;
if (entry->max_counter < summary.arc_sum_max)
entry->max_counter = summary.arc_sum_max;
}
}
else if (GCOV_TAG_IS_SUBTAG (GCOV_TAG_FUNCTION, tag)
&& function_name_buffer)
{
counts_entry_t **slot, *entry, elt;
unsigned n_counts = length / 8;
unsigned ix;
elt.function_name = function_name_buffer;
elt.section = tag;
slot = (counts_entry_t **) htab_find_slot
(counts_hash, &elt, INSERT);
entry = *slot;
if (!entry)
{
*slot = entry = xmalloc (sizeof (counts_entry_t));
entry->function_name = xstrdup (function_name_buffer);
entry->section = tag;
entry->checksum = checksum;
entry->n_counts = n_counts;
entry->counts = xcalloc (n_counts, sizeof (gcov_type));
}
else if (entry->checksum != checksum || entry->n_counts != n_counts)
{
warning ("profile mismatch for `%s'", function_name_buffer);
htab_delete (counts_hash);
break;
}
/* This should always be true for a just allocated entry,
and always false for an existing one. Check this way, in
case the gcov file is corrupt. */
if (!entry->chain || summaried != entry)
{
entry->chain = summaried;
summaried = entry;
}
for (ix = 0; ix != n_counts; ix++)
entry->counts[ix] += gcov_read_counter ();
}
gcov_seek (offset, length);
if ((error = gcov_is_error ()))
{
warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
da_file_name);
htab_delete (counts_hash);
break;
}
}
free (function_name_buffer);
gcov_close ();
}
/* Returns the counters for a particular tag. */
gcov_type *
get_coverage_counts (unsigned tag, unsigned expected)
{
counts_entry_t *entry, elt;
profile_info.max_counter_in_program = 0;
profile_info.count_profiles_merged = 0;
/* No hash table, no counts. */
if (!counts_hash)
{
static int warned = 0;
if (!warned++)
warning ("file %s not found, execution counts assumed to be zero",
da_file_name);
return NULL;
}
elt.function_name
= (char *) IDENTIFIER_POINTER
(DECL_ASSEMBLER_NAME (current_function_decl));
elt.section = tag;
entry = htab_find (counts_hash, &elt);
if (!entry)
{
warning ("No profile for function '%s' found.", elt.function_name);
return 0;
}
if (expected != entry->n_counts
|| compute_checksum () != entry->checksum)
{
warning ("profile mismatch for `%s'", elt.function_name);
return NULL;
}
profile_info.count_profiles_merged = entry->merged;
profile_info.max_counter_in_program = entry->max_counter_sum;
return entry->counts;
}
/* Generate a checksum for a string. CHKSUM is the current
checksum. */
static unsigned
checksum_string (unsigned chksum, const char *string)
{
do
{
unsigned value = *string << 24;
unsigned ix;
for (ix = 8; ix--; value <<= 1)
{
unsigned feedback;
feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0;
chksum <<= 1;
chksum ^= feedback;
}
}
while (*string++);
return chksum;
}
/* Compute checksum for the current function. We generate a CRC32. */
static unsigned
compute_checksum ()
{
unsigned chksum = DECL_SOURCE_LINE (current_function_decl);
chksum = checksum_string (chksum, DECL_SOURCE_FILE (current_function_decl));
chksum = checksum_string
(chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)));
return chksum;
}
/* Begin output to the graph file for the current function.
Opens the output file, if not already done. Writes the
function header, if not already done. Returns non-zero if data
should be output. */
int
coverage_begin_output ()
{
if (!bbg_function_announced)
{
const char *file = DECL_SOURCE_FILE (current_function_decl);
unsigned line = DECL_SOURCE_LINE (current_function_decl);
unsigned long offset;
if (!bbg_file_opened)
{
if (!gcov_open (bbg_file_name, -1))
error ("cannot open %s", bbg_file_name);
else
{
gcov_write_unsigned (GCOV_GRAPH_MAGIC);
gcov_write_unsigned (GCOV_VERSION);
}
bbg_file_opened = 1;
}
/* Announce function */
offset = gcov_write_tag (GCOV_TAG_FUNCTION);
gcov_write_string (IDENTIFIER_POINTER
(DECL_ASSEMBLER_NAME (current_function_decl)));
gcov_write_unsigned (compute_checksum ());
gcov_write_string (file);
gcov_write_unsigned (line);
gcov_write_length (offset);
bbg_function_announced = 1;
}
return !gcov_is_error ();
}
/* Finish coverage data for the current function. Verify no output
error has occurred. Save function coverage counts. */
void
coverage_end_function ()
{
unsigned i;
if (bbg_file_opened > 1 && gcov_is_error ())
{
warning ("error writing `%s'", bbg_file_name);
bbg_file_opened = -1;
}
for (i = 0; i != profile_info.n_sections; i++)
if (profile_info.section_info[i].n_counters_now)
{
struct function_list *item;
/* ??? Probably should re-use the existing struct function. */
item = xmalloc (sizeof (struct function_list));
*functions_tail = item;
functions_tail = &item->next;
item->next = 0;
item->name = xstrdup (IDENTIFIER_POINTER
(DECL_ASSEMBLER_NAME (current_function_decl)));
item->cfg_checksum = compute_checksum ();
item->n_counter_sections = 0;
for (i = 0; i < profile_info.n_sections; i++)
if (profile_info.section_info[i].n_counters_now)
{
item->counter_sections[item->n_counter_sections].tag =
profile_info.section_info[i].tag;
item->counter_sections[item->n_counter_sections].n_counters =
profile_info.section_info[i].n_counters_now;
item->n_counter_sections++;
profile_info.section_info[i].n_counters
+= profile_info.section_info[i].n_counters_now;
profile_info.section_info[i].n_counters_now = 0;
}
break;
}
bbg_function_announced = 0;
}
/* Set FIELDS as purpose to VALUE. */
static void
set_purpose (value, fields)
tree value;
tree fields;
{
tree act_field, act_value;
for (act_field = fields, act_value = value;
act_field;
act_field = TREE_CHAIN (act_field), act_value = TREE_CHAIN (act_value))
TREE_PURPOSE (act_value) = act_field;
}
/* Returns label for base of counters inside TAG section. */
static rtx
label_for_tag (tag)
unsigned tag;
{
switch (tag)
{
case GCOV_TAG_ARC_COUNTS:
return profiler_label;
default:
abort ();
}
}
/* Creates fields of struct counter_section (in gcov-io.h). */
static tree
build_counter_section_fields ()
{
tree field, fields;
/* tag */
fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
/* n_counters */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
return fields;
}
/* Creates value of struct counter_section (in gcov-io.h). */
static tree
build_counter_section_value (tag, n_counters)
unsigned tag;
unsigned n_counters;
{
tree value = NULL_TREE;
/* tag */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (tag, 0)),
value);
/* n_counters */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (n_counters, 0)),
value);
return value;
}
/* Creates fields of struct counter_section_data (in gcov-io.h). */
static tree
build_counter_section_data_fields ()
{
tree field, fields, gcov_type, gcov_ptr_type;
gcov_type = make_signed_type (GCOV_TYPE_SIZE);
gcov_ptr_type =
build_pointer_type (build_qualified_type (gcov_type,
TYPE_QUAL_CONST));
/* tag */
fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
/* n_counters */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* counters */
field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
TREE_CHAIN (field) = fields;
fields = field;
return fields;
}
/* Creates value of struct counter_section_data (in gcov-io.h). */
static tree
build_counter_section_data_value (tag, n_counters)
unsigned tag;
unsigned n_counters;
{
tree value = NULL_TREE, counts_table, gcov_type, gcov_ptr_type;
gcov_type = make_signed_type (GCOV_TYPE_SIZE);
gcov_ptr_type
= build_pointer_type (build_qualified_type
(gcov_type, TYPE_QUAL_CONST));
/* tag */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (tag, 0)),
value);
/* n_counters */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (n_counters, 0)),
value);
/* counters */
if (n_counters)
{
tree gcov_type_array_type =
build_array_type (gcov_type,
build_index_type (build_int_2 (n_counters - 1,
0)));
counts_table =
build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE);
TREE_STATIC (counts_table) = 1;
DECL_NAME (counts_table) = get_identifier (XSTR (label_for_tag (tag), 0));
assemble_variable (counts_table, 0, 0, 0);
counts_table = build1 (ADDR_EXPR, gcov_ptr_type, counts_table);
}
else
counts_table = null_pointer_node;
value = tree_cons (NULL_TREE, counts_table, value);
return value;
}
/* Creates fields for struct function_info type (in gcov-io.h). */
static tree
build_function_info_fields ()
{
tree field, fields, counter_section_fields, counter_section_type;
tree counter_sections_ptr_type;
tree string_type =
build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
/* name */
fields = build_decl (FIELD_DECL, NULL_TREE, string_type);
/* checksum */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* n_counter_sections */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* counter_sections */
counter_section_fields = build_counter_section_fields ();
counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
finish_builtin_struct (counter_section_type, "__counter_section",
counter_section_fields, NULL_TREE);
counter_sections_ptr_type =
build_pointer_type
(build_qualified_type (counter_section_type,
TYPE_QUAL_CONST));
field = build_decl (FIELD_DECL, NULL_TREE, counter_sections_ptr_type);
TREE_CHAIN (field) = fields;
fields = field;
return fields;
}
/* Creates value for struct function_info (in gcov-io.h). */
static tree
build_function_info_value (function)
struct function_list *function;
{
tree value = NULL_TREE;
size_t name_len = strlen (function->name);
tree fname = build_string (name_len + 1, function->name);
tree string_type =
build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
tree counter_section_fields, counter_section_type, counter_sections_value;
tree counter_sections_ptr_type, counter_sections_array_type;
unsigned i;
/* name */
TREE_TYPE (fname) =
build_array_type (char_type_node,
build_index_type (build_int_2 (name_len, 0)));
value = tree_cons (NULL_TREE,
build1 (ADDR_EXPR,
string_type,
fname),
value);
/* checksum */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (function->cfg_checksum, 0)),
value);
/* n_counter_sections */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (function->n_counter_sections, 0)),
value);
/* counter_sections */
counter_section_fields = build_counter_section_fields ();
counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
counter_sections_ptr_type =
build_pointer_type
(build_qualified_type (counter_section_type,
TYPE_QUAL_CONST));
counter_sections_array_type =
build_array_type (counter_section_type,
build_index_type (
build_int_2 (function->n_counter_sections - 1,
0)));
counter_sections_value = NULL_TREE;
for (i = 0; i < function->n_counter_sections; i++)
{
tree counter_section_value
= build_counter_section_value (function->counter_sections[i].tag,
function->counter_sections[i].n_counters);
set_purpose (counter_section_value, counter_section_fields);
counter_sections_value =
tree_cons (NULL_TREE,
build_constructor (counter_section_type,
nreverse (counter_section_value)),
counter_sections_value);
}
finish_builtin_struct (counter_section_type, "__counter_section",
counter_section_fields, NULL_TREE);
if (function->n_counter_sections)
{
counter_sections_value =
build_constructor (counter_sections_array_type,
nreverse (counter_sections_value)),
counter_sections_value = build1 (ADDR_EXPR,
counter_sections_ptr_type,
counter_sections_value);
}
else
counter_sections_value = null_pointer_node;
value = tree_cons (NULL_TREE, counter_sections_value, value);
return value;
}
/* Creates fields of struct gcov_info type (in gcov-io.h). */
static tree
build_gcov_info_fields (gcov_info_type)
tree gcov_info_type;
{
tree field, fields;
char *filename;
int filename_len;
tree string_type =
build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
tree function_info_fields, function_info_type, function_info_ptr_type;
tree counter_section_data_fields, counter_section_data_type;
tree counter_section_data_ptr_type;
/* Version ident */
fields = build_decl (FIELD_DECL, NULL_TREE, long_unsigned_type_node);
/* next -- NULL */
field = build_decl (FIELD_DECL, NULL_TREE,
build_pointer_type
(build_qualified_type
(gcov_info_type, TYPE_QUAL_CONST)));
TREE_CHAIN (field) = fields;
fields = field;
/* Filename */
filename = getpwd ();
filename = (filename && da_file_name[0] != '/'
? concat (filename, "/", da_file_name, NULL)
: da_file_name);
filename_len = strlen (filename);
if (filename != da_file_name)
free (filename);
field = build_decl (FIELD_DECL, NULL_TREE, string_type);
TREE_CHAIN (field) = fields;
fields = field;
/* Workspace */
field = build_decl (FIELD_DECL, NULL_TREE, long_integer_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* number of functions */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* function_info table */
function_info_fields = build_function_info_fields ();
function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
finish_builtin_struct (function_info_type, "__function_info",
function_info_fields, NULL_TREE);
function_info_ptr_type =
build_pointer_type
(build_qualified_type (function_info_type,
TYPE_QUAL_CONST));
field = build_decl (FIELD_DECL, NULL_TREE, function_info_ptr_type);
TREE_CHAIN (field) = fields;
fields = field;
/* n_counter_sections */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* counter sections */
counter_section_data_fields = build_counter_section_data_fields ();
counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
finish_builtin_struct (counter_section_data_type, "__counter_section_data",
counter_section_data_fields, NULL_TREE);
counter_section_data_ptr_type =
build_pointer_type
(build_qualified_type (counter_section_data_type,
TYPE_QUAL_CONST));
field = build_decl (FIELD_DECL, NULL_TREE, counter_section_data_ptr_type);
TREE_CHAIN (field) = fields;
fields = field;
return fields;
}
/* Creates struct gcov_info value (in gcov-io.h). */
static tree
build_gcov_info_value ()
{
tree value = NULL_TREE;
tree filename_string;
char *filename;
int filename_len;
unsigned n_functions, i;
struct function_list *item;
tree string_type =
build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
tree function_info_fields, function_info_type, function_info_ptr_type;
tree functions;
tree counter_section_data_fields, counter_section_data_type;
tree counter_section_data_ptr_type, counter_sections;
/* Version ident */
value = tree_cons (NULL_TREE,
convert (long_unsigned_type_node,
build_int_2 (GCOV_VERSION, 0)),
value);
/* next -- NULL */
value = tree_cons (NULL_TREE, null_pointer_node, value);
/* Filename */
filename = getpwd ();
filename = (filename && da_file_name[0] != '/'
? concat (filename, "/", da_file_name, NULL)
: da_file_name);
filename_len = strlen (filename);
filename_string = build_string (filename_len + 1, filename);
if (filename != da_file_name)
free (filename);
TREE_TYPE (filename_string) =
build_array_type (char_type_node,
build_index_type (build_int_2 (filename_len, 0)));
value = tree_cons (NULL_TREE,
build1 (ADDR_EXPR,
string_type,
filename_string),
value);
/* Workspace */
value = tree_cons (NULL_TREE,
convert (long_integer_type_node, integer_zero_node),
value);
/* number of functions */
n_functions = 0;
for (item = functions_head; item != 0; item = item->next, n_functions++)
continue;
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (n_functions, 0)),
value);
/* function_info table */
function_info_fields = build_function_info_fields ();
function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
function_info_ptr_type =
build_pointer_type (
build_qualified_type (function_info_type,
TYPE_QUAL_CONST));
functions = NULL_TREE;
for (item = functions_head; item != 0; item = item->next)
{
tree function_info_value = build_function_info_value (item);
set_purpose (function_info_value, function_info_fields);
functions
= tree_cons (NULL_TREE,
build_constructor (function_info_type,
nreverse (function_info_value)),
functions);
}
finish_builtin_struct (function_info_type, "__function_info",
function_info_fields, NULL_TREE);
/* Create constructor for array. */
if (n_functions)
{
tree array_type;
array_type = build_array_type (
function_info_type,
build_index_type (build_int_2 (n_functions - 1, 0)));
functions = build_constructor (array_type, nreverse (functions));
functions = build1 (ADDR_EXPR,
function_info_ptr_type,
functions);
}
else
functions = null_pointer_node;
value = tree_cons (NULL_TREE, functions, value);
/* n_counter_sections */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (profile_info.n_sections, 0)),
value);
/* counter sections */
counter_section_data_fields = build_counter_section_data_fields ();
counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
counter_sections = NULL_TREE;
for (i = 0; i < profile_info.n_sections; i++)
{
tree counter_sections_value =
build_counter_section_data_value (
profile_info.section_info[i].tag,
profile_info.section_info[i].n_counters);
set_purpose (counter_sections_value, counter_section_data_fields);
counter_sections =
tree_cons (NULL_TREE,
build_constructor (counter_section_data_type,
nreverse (counter_sections_value)),
counter_sections);
}
finish_builtin_struct (counter_section_data_type, "__counter_section_data",
counter_section_data_fields, NULL_TREE);
counter_section_data_ptr_type =
build_pointer_type
(build_qualified_type (counter_section_data_type,
TYPE_QUAL_CONST));
if (profile_info.n_sections)
{
tree cst_type = build_index_type (build_int_2 (profile_info.n_sections-1,
0));
cst_type = build_array_type (counter_section_data_type, cst_type);
counter_sections = build_constructor (cst_type,
nreverse (counter_sections));
counter_sections = build1 (ADDR_EXPR,
counter_section_data_ptr_type,
counter_sections);
}
else
counter_sections = null_pointer_node;
value = tree_cons (NULL_TREE, counter_sections, value);
return value;
}
/* Write out the structure which libgcc uses to locate all the arc
counters. The structures used here must match those defined in
gcov-io.h. Write out the constructor to call __gcov_init. */
static void
create_coverage ()
{
tree gcov_info_fields, gcov_info_type, gcov_info_value, gcov_info;
char name[20];
char *ctor_name;
tree ctor;
rtx gcov_info_address;
int save_flag_inline_functions = flag_inline_functions;
unsigned i;
for (i = 0; i < profile_info.n_sections; i++)
if (profile_info.section_info[i].n_counters)
break;
if (i == profile_info.n_sections)
return;
gcov_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
gcov_info_fields = build_gcov_info_fields (gcov_info_type);
gcov_info_value = build_gcov_info_value ();
set_purpose (gcov_info_value, gcov_info_fields);
finish_builtin_struct (gcov_info_type, "__gcov_info",
gcov_info_fields, NULL_TREE);
gcov_info = build (VAR_DECL, gcov_info_type, NULL_TREE, NULL_TREE);
DECL_INITIAL (gcov_info) =
build_constructor (gcov_info_type, nreverse (gcov_info_value));
TREE_STATIC (gcov_info) = 1;
ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0);
DECL_NAME (gcov_info) = get_identifier (name);
/* Build structure. */
assemble_variable (gcov_info, 0, 0, 0);
/* Build the constructor function to invoke __gcov_init. */
ctor_name = concat (IDENTIFIER_POINTER (get_file_function_name ('I')),
"_GCOV", NULL);
ctor = build_decl (FUNCTION_DECL, get_identifier (ctor_name),
build_function_type (void_type_node, NULL_TREE));
free (ctor_name);
DECL_EXTERNAL (ctor) = 0;
/* It can be a static function as long as collect2 does not have
to scan the object file to find its ctor/dtor routine. */
TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors;
TREE_USED (ctor) = 1;
DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
ctor = (*lang_hooks.decls.pushdecl) (ctor);
rest_of_decl_compilation (ctor, 0, 1, 0);
announce_function (ctor);
current_function_decl = ctor;
DECL_INITIAL (ctor) = error_mark_node;
make_decl_rtl (ctor, NULL);
init_function_start (ctor, input_filename, lineno);
(*lang_hooks.decls.pushlevel) (0);
expand_function_start (ctor, 0);
cfun->arc_profile = 0;
/* Actually generate the code to call __gcov_init. */
gcov_info_address = force_reg (Pmode, XEXP (DECL_RTL (gcov_info), 0));
emit_library_call (gcov_init_libfunc, LCT_NORMAL, VOIDmode, 1,
gcov_info_address, Pmode);
expand_function_end (input_filename, lineno, 0);
(*lang_hooks.decls.poplevel) (1, 0, 1);
/* Since ctor isn't in the list of globals, it would never be emitted
when it's considered to be 'safe' for inlining, so turn off
flag_inline_functions. */
flag_inline_functions = 0;
rest_of_compilation (ctor);
/* Reset flag_inline_functions to its original value. */
flag_inline_functions = save_flag_inline_functions;
if (! quiet_flag)
fflush (asm_out_file);
current_function_decl = NULL_TREE;
if (targetm.have_ctors_dtors)
(* targetm.asm_out.constructor) (XEXP (DECL_RTL (ctor), 0),
DEFAULT_INIT_PRIORITY);
}
/* Find (and create if not present) a section with TAG for the current
function. */
struct section_info *
find_counters_section (tag)
unsigned tag;
{
unsigned i;
for (i = 0; i < profile_info.n_sections; i++)
if (profile_info.section_info[i].tag == tag)
return profile_info.section_info + i;
if (i == MAX_COUNTER_SECTIONS)
abort ();
profile_info.section_info[i].tag = tag;
profile_info.section_info[i].present = 0;
profile_info.section_info[i].n_counters = 0;
profile_info.section_info[i].n_counters_now = 0;
profile_info.n_sections++;
return profile_info.section_info + i;
}
/* Generate a MEM rtl to access counter NO in counter section TAG. */
rtx
coverage_counter_ref (unsigned tag, unsigned no)
{
enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0);
struct section_info *sect = find_counters_section (tag);
rtx ref;
if (!profiler_label)
{
/* Generate and save a copy of this so it can be shared. */
char buf[20];
ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 2);
profiler_label = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
}
if (no + 1 > (unsigned) sect->n_counters_now)
sect->n_counters_now = no + 1;
no += sect->n_counters;
ref = plus_constant (profiler_label, GCOV_TYPE_SIZE / BITS_PER_UNIT * no);
ref = gen_rtx_MEM (mode, ref);
set_mem_alias_set (ref, new_alias_set ());
return ref;
}
/* Perform file-level initialization. Read in data file, generate name
of graph file. */
void
coverage_init (filename)
const char *filename;
{
int len = strlen (filename);
da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
strcpy (da_file_name, filename);
strcat (da_file_name, GCOV_DATA_SUFFIX);
read_counts_file ();
/* Open the bbg output file. */
bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1);
strcpy (bbg_file_name, filename);
strcat (bbg_file_name, GCOV_GRAPH_SUFFIX);
}
/* Performs file-level cleanup. Close graph file, generate coverage
variables and constructor. */
void
coverage_finish ()
{
create_coverage ();
if (bbg_file_opened)
{
int error = gcov_close ();
if (error)
unlink (bbg_file_name);
#if SELF_COVERAGE
/* If the compiler is instrumented, we should not
unconditionally remove the counts file, because we might be
recompiling ourselves. The .da files are all removed during
copying the stage1 files. */
if (error)
#endif
unlink (da_file_name);
}
}
#include "gt-coverage.h"
/* profile.h - Defines data exported from profile.c to other passes. /* coverage.h - Defines data exported from coverage.c
Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
This file is part of GCC. This file is part of GCC.
...@@ -18,8 +18,10 @@ along with GCC; see the file COPYING. If not, write to the Free ...@@ -18,8 +18,10 @@ along with GCC; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */ 02111-1307, USA. */
#ifndef GCC_PROFILE_H #ifndef GCC_COVERAGE_H
#define GCC_PROFILE_H #define GCC_COVERAGE_H
#include "gcov-io.h"
/* The number of different counter sections. */ /* The number of different counter sections. */
#define MAX_COUNTER_SECTIONS 1 #define MAX_COUNTER_SECTIONS 1
...@@ -57,6 +59,14 @@ struct profile_info ...@@ -57,6 +59,14 @@ struct profile_info
extern struct profile_info profile_info; extern struct profile_info profile_info;
extern void coverage_init (const char *);
extern void coverage_finish (void);
extern void coverage_end_function (void);
extern int coverage_begin_output (void);
extern rtx coverage_counter_ref (unsigned /*tag*/, unsigned/*num*/);
gcov_type *get_coverage_counts (unsigned /*tag*/, unsigned /*expected*/);
struct section_info *find_counters_section PARAMS ((unsigned)); struct section_info *find_counters_section PARAMS ((unsigned));
#endif #endif
...@@ -25,6 +25,7 @@ Boston, MA 02111-1307, USA. */ ...@@ -25,6 +25,7 @@ Boston, MA 02111-1307, USA. */
#include <getopt.h> #include <getopt.h>
#define IN_GCOV (-1) #define IN_GCOV (-1)
#include "gcov-io.h" #include "gcov-io.h"
#include "gcov-io.c"
static void dump_file PARAMS ((const char *)); static void dump_file PARAMS ((const char *));
static void print_prefix PARAMS ((const char *, unsigned)); static void print_prefix PARAMS ((const char *, unsigned));
......
/* File format for coverage information
Copyright (C) 1996, 1997, 1998, 2000, 2002,
2003 Free Software Foundation, Inc.
Contributed by Bob Manson <manson@cygnus.com>.
Completely remangled by Nathan Sidwell <nathan@codesourcery.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 2, 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 COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
/* Routines declared in gcov-io.h. This file should be #included by
another source file, after having #included gcov-io.h. */
/* Open a gcov file. NAME is the name of the file to open and MODE
indicates whether a new file should be created, or an existing file
opened for modification. If MODE is >= 0 an existing file will be
opened, if possible, and if MODE is <= 0, a new file will be
created. Use MODE=0 to attempt to reopen an existing file and then
fall back on creating a new one. Return zero on failure, >0 on
opening an existing file and <0 on creating a new one. */
GCOV_LINKAGE int
gcov_open (const char *name, int mode)
{
int result = 1;
size_t alloc = 1024;
#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV
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
if (gcov_var.file)
abort ();
gcov_var.position = gcov_var.length = 0;
gcov_var.error = gcov_var.modified = 0;
if (mode >= 0)
gcov_var.file = fopen (name, "r+b");
if (!gcov_var.file && mode <= 0)
{
result = -1;
gcov_var.file = fopen (name, "w+b");
}
if (!gcov_var.file)
return 0;
#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV
while (fcntl (fileno (gcov_var.file), F_SETLKW, &s_flock)
&& errno == EINTR)
continue;
#endif
if (result >= 0)
{
if (fseek (gcov_var.file, 0, SEEK_END))
{
fclose (gcov_var.file);
gcov_var.file = 0;
return 0;
}
gcov_var.length = ftell (gcov_var.file);
fseek (gcov_var.file, 0, SEEK_SET);
alloc += gcov_var.length;
}
if (alloc > gcov_var.alloc)
{
if (gcov_var.buffer)
free (gcov_var.buffer);
gcov_var.alloc = alloc;
#if IN_LIBGCOV
gcov_var.buffer = malloc (gcov_var.alloc);
if (!gcov_var.buffer)
{
fclose (gcov_var.file);
gcov_var.file = 0;
gcov_var.length = 0;
gcov_var.alloc = 0;
return 0;
}
#else
gcov_var.buffer = xmalloc (gcov_var.alloc);
#endif
}
if (result >= 0
&& fread (gcov_var.buffer, gcov_var.length, 1, gcov_var.file) != 1)
{
fclose (gcov_var.file);
gcov_var.file = 0;
gcov_var.length = 0;
return 0;
}
return result;
}
/* Close the current gcov file. Flushes data to disk. Returns nonzero
on failure or error flag set. */
GCOV_LINKAGE int
gcov_close ()
{
int result = 0;
if (gcov_var.file)
{
if (gcov_var.modified
&& (fseek (gcov_var.file, 0, SEEK_SET)
|| fwrite (gcov_var.buffer, gcov_var.length,
1, gcov_var.file) != 1))
result = 1;
fclose (gcov_var.file);
gcov_var.file = 0;
gcov_var.length = 0;
}
#if !IN_LIBGCOV
free (gcov_var.buffer);
gcov_var.alloc = 0;
gcov_var.buffer = 0;
#endif
return result ? 1 : gcov_var.error;
}
#if !IN_GCOV
/* Allocate space to write BYTES bytes to the gcov file. Return a
pointer to those bytes, or NULL on failure. */
GCOV_LINKAGE unsigned char *
gcov_write_bytes (unsigned bytes)
{
char unsigned *result;
if (gcov_var.position + bytes > gcov_var.alloc)
{
size_t new_size = (gcov_var.alloc + bytes) * 3 / 2;
if (!gcov_var.buffer)
return 0;
#if IN_LIBGCOV
result = realloc (gcov_var.buffer, new_size);
if (!result)
{
free (gcov_var.buffer);
gcov_var.buffer = 0;
gcov_var.alloc = 0;
gcov_var.position = gcov_var.length = 0;
gcov_var.error = 1;
return 0;
}
#else
result = xrealloc (gcov_var.buffer, new_size);
#endif
gcov_var.alloc = new_size;
gcov_var.buffer = result;
}
result = &gcov_var.buffer[gcov_var.position];
gcov_var.position += bytes;
gcov_var.modified = 1;
if (gcov_var.position > gcov_var.length)
gcov_var.length = gcov_var.position;
return result;
}
/* Write unsigned VALUE to coverage file. Sets error flag
appropriately. */
GCOV_LINKAGE void
gcov_write_unsigned (unsigned value)
{
unsigned char *buffer = gcov_write_bytes (4);
unsigned ix;
if (!buffer)
return;
for (ix = 4; ix--; )
{
buffer[ix] = value;
value >>= 8;
}
if (sizeof (value) > 4 && value)
gcov_var.error = -1;
return;
}
/* Write counter VALUE to coverage file. Sets error flag
appropriately. */
#if IN_LIBGCOV
GCOV_LINKAGE void
gcov_write_counter (gcov_type value)
{
unsigned char *buffer = gcov_write_bytes (8);
unsigned ix;
if (!buffer)
return;
for (ix = 8; ix--; )
{
buffer[ix] = value;
value >>= 8;
}
if ((sizeof (value) > 8 && value) || value < 0)
gcov_var.error = -1;
return;
}
#endif /* IN_LIBGCOV */
/* Write STRING to coverage file. Sets error flag on file
error, overflow flag on overflow */
GCOV_LINKAGE void
gcov_write_string (const char *string)
{
unsigned length = 0;
unsigned pad = 0;
unsigned rem = 0;
unsigned char *buffer;
if (string)
{
length = strlen (string);
rem = 4 - (length & 3);
}
buffer = gcov_write_bytes (4 + length + rem);
if (buffer)
{
unsigned ix;
unsigned value = length;
for (ix = 4; ix--; )
{
buffer[ix] = value;
value >>= 8;
}
memcpy (buffer + 4, string, length);
memcpy (buffer + 4 + length, &pad, rem);
}
}
/* Write a tag TAG and reserve space for the record length. Return a
value to be used for gcov_write_length. */
GCOV_LINKAGE unsigned long
gcov_write_tag (unsigned tag)
{
unsigned long result = gcov_var.position;
unsigned char *buffer = gcov_write_bytes (8);
unsigned ix;
if (!buffer)
return 0;
for (ix = 4; ix--; )
{
buffer[ix] = tag;
tag >>= 8;
}
memset (buffer + 4, 0, 4);
return result;
}
/* Write a record length using POSITION, which was returned by
gcov_write_tag. The current file position is the end of the
record, and is restored before returning. Returns nonzero on
overflow. */
GCOV_LINKAGE void
gcov_write_length (unsigned long position)
{
if (position)
{
unsigned length = gcov_var.position - position - 8;
unsigned char *buffer = &gcov_var.buffer[position + 4];
unsigned ix;
for (ix = 4; ix--; )
{
buffer[ix] = length;
length >>= 8;
}
}
}
#if IN_LIBGCOV
/* Write a summary structure to the gcov file. Return non-zero on
overflow. */
GCOV_LINKAGE void
gcov_write_summary (unsigned tag, const struct gcov_summary *summary)
{
unsigned long base;
base = gcov_write_tag (tag);
gcov_write_unsigned (summary->checksum);
gcov_write_unsigned (summary->runs);
gcov_write_unsigned (summary->arcs);
gcov_write_counter (summary->arc_sum);
gcov_write_counter (summary->arc_max_one);
gcov_write_counter (summary->arc_sum_max);
gcov_write_length (base);
}
#endif /* IN_LIBGCOV */
#endif /*!IN_GCOV */
/* Return a pointer to read BYTES bytes from the gcov file. Returns
NULL on failure (read past EOF). */
GCOV_LINKAGE const unsigned char *
gcov_read_bytes (unsigned bytes)
{
const unsigned char *result;
if (gcov_var.position + bytes > gcov_var.length)
{
gcov_var.error = 1;
return 0;
}
result = &gcov_var.buffer[gcov_var.position];
gcov_var.position += bytes;
return result;
}
/* Read unsigned value from a coverage file. Sets error flag on file
error, overflow flag on overflow */
GCOV_LINKAGE unsigned
gcov_read_unsigned ()
{
unsigned value = 0;
unsigned ix;
const unsigned char *buffer = gcov_read_bytes (4);
if (!buffer)
return 0;
for (ix = sizeof (value); ix < 4; ix++)
if (buffer[ix])
gcov_var.error = -1;
for (ix = 0; ix != 4; ix++)
{
value <<= 8;
value |= buffer[ix];
}
return value;
}
/* Read counter value from a coverage file. Sets error flag on file
error, overflow flag on overflow */
GCOV_LINKAGE gcov_type
gcov_read_counter ()
{
gcov_type value = 0;
unsigned ix;
const unsigned char *buffer = gcov_read_bytes (8);
if (!buffer)
return 0;
for (ix = sizeof (value); ix < 8; ix++)
if (buffer[ix])
gcov_var.error = -1;
for (ix = 0; ix != 8; ix++)
{
value <<= 8;
value |= buffer[ix];
}
if (value < 0)
gcov_var.error = -1;
return value;
}
/* 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. */
GCOV_LINKAGE const char *
gcov_read_string ()
{
unsigned length = gcov_read_unsigned ();
if (!length)
return 0;
length += 4 - (length & 3);
return (const char *) gcov_read_bytes (length);
}
GCOV_LINKAGE void
gcov_read_summary (struct gcov_summary *summary)
{
summary->checksum = gcov_read_unsigned ();
summary->runs = gcov_read_unsigned ();
summary->arcs = gcov_read_unsigned ();
summary->arc_sum = gcov_read_counter ();
summary->arc_max_one = gcov_read_counter ();
summary->arc_sum_max = gcov_read_counter ();
}
#if IN_GCOV > 0
/* Return the modification time of the current gcov file. */
GCOV_LINKAGE time_t
gcov_time ()
{
struct stat status;
if (fstat (fileno (gcov_var.file), &status))
return 0;
else
return status.st_mtime;
}
#endif /* IN_GCOV */
/* File format for coverage information /* File format for coverage information
Copyright (C) 1996, 1997, 1998, 2000, 2002 Free Software Foundation, Inc. Copyright (C) 1996, 1997, 1998, 2000, 2002,
2003 Free Software Foundation, Inc.
Contributed by Bob Manson <manson@cygnus.com>. Contributed by Bob Manson <manson@cygnus.com>.
Completely remangled by Nathan Sidwell <nathan@codesourcery.com>. Completely remangled by Nathan Sidwell <nathan@codesourcery.com>.
...@@ -160,6 +161,17 @@ typedef HOST_WIDEST_INT gcov_type; ...@@ -160,6 +161,17 @@ typedef HOST_WIDEST_INT gcov_type;
#endif #endif
#endif #endif
/* In lib gcov we want function linkage to be static, so we do not
polute the global namespace. In the compiler we want it extern, so
that they can be accessed from elsewhere. */
#if IN_LIBGCOV || IN_GCOV
#define GCOV_LINKAGE static
#else
#ifndef GCOV_LINKAGE
#define GCOV_LINKAGE extern
#endif
#endif
/* File suffixes. */ /* File suffixes. */
#define GCOV_DATA_SUFFIX ".da" #define GCOV_DATA_SUFFIX ".da"
#define GCOV_GRAPH_SUFFIX ".bbg" #define GCOV_GRAPH_SUFFIX ".bbg"
...@@ -227,7 +239,7 @@ struct gcov_summary ...@@ -227,7 +239,7 @@ struct gcov_summary
by write_profile must match these. */ by write_profile must match these. */
/* Information about section of counters for a function. */ /* Information about section of counters for a function. */
struct counter_section struct gcov_counter_section
{ {
unsigned tag; /* Tag of the section. */ unsigned tag; /* Tag of the section. */
unsigned n_counters; /* Number of counters in the section. */ unsigned n_counters; /* Number of counters in the section. */
...@@ -235,7 +247,7 @@ struct counter_section ...@@ -235,7 +247,7 @@ struct counter_section
#if IN_LIBGCOV #if IN_LIBGCOV
/* Information about section of counters for an object file. */ /* Information about section of counters for an object file. */
struct counter_section_data struct gcov_counter_section_data
{ {
unsigned tag; /* Tag of the section. */ unsigned tag; /* Tag of the section. */
unsigned n_counters; /* Number of counters in the section. */ unsigned n_counters; /* Number of counters in the section. */
...@@ -243,12 +255,12 @@ struct counter_section_data ...@@ -243,12 +255,12 @@ struct counter_section_data
}; };
/* Information about a single function. */ /* Information about a single function. */
struct function_info struct gcov_function_info
{ {
const char *name; /* (mangled) name of function */ const char *name; /* (mangled) name of function */
unsigned checksum; /* function checksum */ unsigned checksum; /* function checksum */
unsigned n_counter_sections; /* Number of types of counters */ unsigned n_counter_sections; /* Number of types of counters */
const struct counter_section *counter_sections; const struct gcov_counter_section *counter_sections;
/* The section descriptions */ /* The section descriptions */
}; };
...@@ -262,10 +274,10 @@ struct gcov_info ...@@ -262,10 +274,10 @@ struct gcov_info
long wkspc; /* libgcc workspace */ long wkspc; /* libgcc workspace */
unsigned n_functions; /* number of functions */ unsigned n_functions; /* number of functions */
const struct function_info *functions; /* table of functions */ const struct gcov_function_info *functions; /* table of functions */
unsigned n_counter_sections; /* Number of types of counters */ unsigned n_counter_sections; /* Number of types of counters */
const struct counter_section_data *counter_sections; const struct gcov_counter_section_data *counter_sections;
/* The data to be put into the sections. */ /* The data to be put into the sections. */
}; };
...@@ -275,18 +287,12 @@ extern void __gcov_init (struct gcov_info *); ...@@ -275,18 +287,12 @@ extern void __gcov_init (struct gcov_info *);
/* Called before fork, to avoid double counting. */ /* Called before fork, to avoid double counting. */
extern void __gcov_flush (void); extern void __gcov_flush (void);
/* Since this file is used in both host and target files, and we don't
include ansidecl.h in target files, provide some necessary macros. */
#ifndef ATTRIBUTE_UNUSED
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
#endif
#endif /* IN_LIBGCOV */ #endif /* IN_LIBGCOV */
/* Because small reads and writes, interspersed with seeks cause lots /* Because small reads and writes, interspersed with seeks cause lots
of disk activity, we buffer the entire count files. */ of disk activity, we buffer the entire count files. */
static struct gcov_var GCOV_LINKAGE struct gcov_var
{ {
FILE *file; FILE *file;
size_t position; size_t position;
...@@ -298,26 +304,26 @@ static struct gcov_var ...@@ -298,26 +304,26 @@ static struct gcov_var
} gcov_var; } gcov_var;
/* Functions for reading and writing gcov files. */ /* Functions for reading and writing gcov files. */
static int gcov_open (const char */*name*/, int /*truncate*/); GCOV_LINKAGE int gcov_open (const char */*name*/, int /*truncate*/);
static int gcov_close (void); GCOV_LINKAGE int gcov_close (void);
#if !IN_GCOV #if !IN_GCOV
static unsigned char *gcov_write_bytes (unsigned); GCOV_LINKAGE unsigned char *gcov_write_bytes (unsigned);
static void gcov_write_unsigned (unsigned); GCOV_LINKAGE void gcov_write_unsigned (unsigned);
#if IN_LIBGCOV #if IN_LIBGCOV
static void gcov_write_counter (gcov_type); GCOV_LINKAGE void gcov_write_counter (gcov_type);
#endif #endif
static void gcov_write_string (const char *); GCOV_LINKAGE void gcov_write_string (const char *);
static unsigned long gcov_write_tag (unsigned); GCOV_LINKAGE unsigned long gcov_write_tag (unsigned);
static void gcov_write_length (unsigned long /*position*/); GCOV_LINKAGE void gcov_write_length (unsigned long /*position*/);
#if IN_LIBGCOV #if IN_LIBGCOV
static void gcov_write_summary (unsigned, const struct gcov_summary *); GCOV_LINKAGE void gcov_write_summary (unsigned, const struct gcov_summary *);
#endif #endif
#endif /* !IN_GCOV */ #endif /* !IN_GCOV */
static const unsigned char *gcov_read_bytes (unsigned); GCOV_LINKAGE const unsigned char *gcov_read_bytes (unsigned);
static unsigned gcov_read_unsigned (void); GCOV_LINKAGE unsigned gcov_read_unsigned (void);
static gcov_type gcov_read_counter (void); GCOV_LINKAGE gcov_type gcov_read_counter (void);
static const char *gcov_read_string (void); GCOV_LINKAGE const char *gcov_read_string (void);
static void gcov_read_summary (struct gcov_summary *); GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *);
static unsigned long gcov_position (void); static unsigned long gcov_position (void);
static void gcov_seek (unsigned long /*base*/, unsigned /*length */); static void gcov_seek (unsigned long /*base*/, unsigned /*length */);
...@@ -325,399 +331,8 @@ static unsigned long gcov_seek_end (void); ...@@ -325,399 +331,8 @@ static unsigned long gcov_seek_end (void);
static int gcov_is_eof (void); static int gcov_is_eof (void);
static int gcov_is_error (void); static int gcov_is_error (void);
#if IN_GCOV > 0 #if IN_GCOV > 0
static time_t gcov_time (void); GCOV_LINKAGE time_t gcov_time (void);
#endif
/* Open a gcov file. NAME is the name of the file to open and MODE
indicates whether a new file should be created, or an existing file
opened for modification. If MODE is >= 0 an existing file will be
opened, if possible, and if MODE is <= 0, a new file will be
created. Use MODE=0 to attempt to reopen an existing file and then
fall back on creating a new one. Return zero on failure, >0 on
opening an existing file and <0 on creating a new one. */
static int
gcov_open (const char *name, int mode)
{
int result = 1;
size_t alloc = 1024;
#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV
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
if (gcov_var.file)
abort ();
gcov_var.position = gcov_var.length = 0;
gcov_var.error = gcov_var.modified = 0;
if (mode >= 0)
gcov_var.file = fopen (name, "r+b");
if (!gcov_var.file && mode <= 0)
{
result = -1;
gcov_var.file = fopen (name, "w+b");
}
if (!gcov_var.file)
return 0;
#if defined (TARGET_HAS_F_SETLKW) && IN_LIBGCOV
while (fcntl (fileno (gcov_var.file), F_SETLKW, &s_flock)
&& errno == EINTR)
continue;
#endif
if (result >= 0)
{
if (fseek (gcov_var.file, 0, SEEK_END))
{
fclose (gcov_var.file);
gcov_var.file = 0;
return 0;
}
gcov_var.length = ftell (gcov_var.file);
fseek (gcov_var.file, 0, SEEK_SET);
alloc += gcov_var.length;
}
if (alloc > gcov_var.alloc)
{
if (gcov_var.buffer)
free (gcov_var.buffer);
gcov_var.alloc = alloc;
#if IN_LIBGCOV
gcov_var.buffer = malloc (gcov_var.alloc);
if (!gcov_var.buffer)
{
fclose (gcov_var.file);
gcov_var.file = 0;
gcov_var.length = 0;
gcov_var.alloc = 0;
return 0;
}
#else
gcov_var.buffer = xmalloc (gcov_var.alloc);
#endif
}
if (result >= 0
&& fread (gcov_var.buffer, gcov_var.length, 1, gcov_var.file) != 1)
{
fclose (gcov_var.file);
gcov_var.file = 0;
gcov_var.length = 0;
return 0;
}
return result;
}
/* Close the current gcov file. Flushes data to disk. Returns nonzero
on failure or error flag set. */
static int
gcov_close ()
{
int result = 0;
if (gcov_var.file)
{
if (gcov_var.modified
&& (fseek (gcov_var.file, 0, SEEK_SET)
|| fwrite (gcov_var.buffer, gcov_var.length,
1, gcov_var.file) != 1))
result = 1;
fclose (gcov_var.file);
gcov_var.file = 0;
gcov_var.length = 0;
}
#if !IN_LIBGCOV
free (gcov_var.buffer);
gcov_var.alloc = 0;
gcov_var.buffer = 0;
#endif #endif
return result ? 1 : gcov_var.error;
}
#if !IN_GCOV
/* Allocate space to write BYTES bytes to the gcov file. Return a
pointer to those bytes, or NULL on failure. */
static unsigned char *
gcov_write_bytes (unsigned bytes)
{
char unsigned *result;
if (gcov_var.position + bytes > gcov_var.alloc)
{
size_t new_size = (gcov_var.alloc + bytes) * 3 / 2;
if (!gcov_var.buffer)
return 0;
#if IN_LIBGCOV
result = realloc (gcov_var.buffer, new_size);
if (!result)
{
free (gcov_var.buffer);
gcov_var.buffer = 0;
gcov_var.alloc = 0;
gcov_var.position = gcov_var.length = 0;
gcov_var.error = 1;
return 0;
}
#else
result = xrealloc (gcov_var.buffer, new_size);
#endif
gcov_var.alloc = new_size;
gcov_var.buffer = result;
}
result = &gcov_var.buffer[gcov_var.position];
gcov_var.position += bytes;
gcov_var.modified = 1;
if (gcov_var.position > gcov_var.length)
gcov_var.length = gcov_var.position;
return result;
}
/* Write unsigned VALUE to coverage file. Sets error flag
appropriately. */
static void
gcov_write_unsigned (unsigned value)
{
unsigned char *buffer = gcov_write_bytes (4);
unsigned ix;
if (!buffer)
return;
for (ix = 4; ix--; )
{
buffer[ix] = value;
value >>= 8;
}
if (sizeof (value) > 4 && value)
gcov_var.error = -1;
return;
}
/* Write counter VALUE to coverage file. Sets error flag
appropriately. */
#if IN_LIBGCOV
static void
gcov_write_counter (gcov_type value)
{
unsigned char *buffer = gcov_write_bytes (8);
unsigned ix;
if (!buffer)
return;
for (ix = 8; ix--; )
{
buffer[ix] = value;
value >>= 8;
}
if ((sizeof (value) > 8 && value) || value < 0)
gcov_var.error = -1;
return;
}
#endif /* IN_LIBGCOV */
/* Write STRING to coverage file. Sets error flag on file
error, overflow flag on overflow */
static void
gcov_write_string (const char *string)
{
unsigned length = 0;
unsigned pad = 0;
unsigned rem = 0;
unsigned char *buffer;
if (string)
{
length = strlen (string);
rem = 4 - (length & 3);
}
buffer = gcov_write_bytes (4 + length + rem);
if (buffer)
{
unsigned ix;
unsigned value = length;
for (ix = 4; ix--; )
{
buffer[ix] = value;
value >>= 8;
}
memcpy (buffer + 4, string, length);
memcpy (buffer + 4 + length, &pad, rem);
}
}
/* Write a tag TAG and reserve space for the record length. Return a
value to be used for gcov_write_length. */
static unsigned long
gcov_write_tag (unsigned tag)
{
unsigned long result = gcov_var.position;
unsigned char *buffer = gcov_write_bytes (8);
unsigned ix;
if (!buffer)
return 0;
for (ix = 4; ix--; )
{
buffer[ix] = tag;
tag >>= 8;
}
memset (buffer + 4, 0, 4);
return result;
}
/* Write a record length using POSITION, which was returned by
gcov_write_tag. The current file position is the end of the
record, and is restored before returning. Returns nonzero on
overflow. */
static void
gcov_write_length (unsigned long position)
{
if (position)
{
unsigned length = gcov_var.position - position - 8;
unsigned char *buffer = &gcov_var.buffer[position + 4];
unsigned ix;
for (ix = 4; ix--; )
{
buffer[ix] = length;
length >>= 8;
}
}
}
#if IN_LIBGCOV
/* Write a summary structure to the gcov file. Return non-zero on
overflow. */
static void
gcov_write_summary (unsigned tag, const struct gcov_summary *summary)
{
unsigned long base;
base = gcov_write_tag (tag);
gcov_write_unsigned (summary->checksum);
gcov_write_unsigned (summary->runs);
gcov_write_unsigned (summary->arcs);
gcov_write_counter (summary->arc_sum);
gcov_write_counter (summary->arc_max_one);
gcov_write_counter (summary->arc_sum_max);
gcov_write_length (base);
}
#endif /* IN_LIBGCOV */
#endif /*!IN_GCOV */
/* Return a pointer to read BYTES bytes from the gcov file. Returns
NULL on failure (read past EOF). */
static const unsigned char *
gcov_read_bytes (unsigned bytes)
{
const unsigned char *result;
if (gcov_var.position + bytes > gcov_var.length)
{
gcov_var.error = 1;
return 0;
}
result = &gcov_var.buffer[gcov_var.position];
gcov_var.position += bytes;
return result;
}
/* Read unsigned value from a coverage file. Sets error flag on file
error, overflow flag on overflow */
static unsigned
gcov_read_unsigned ()
{
unsigned value = 0;
unsigned ix;
const unsigned char *buffer = gcov_read_bytes (4);
if (!buffer)
return 0;
for (ix = sizeof (value); ix < 4; ix++)
if (buffer[ix])
gcov_var.error = -1;
for (ix = 0; ix != 4; ix++)
{
value <<= 8;
value |= buffer[ix];
}
return value;
}
/* Read counter value from a coverage file. Sets error flag on file
error, overflow flag on overflow */
static gcov_type
gcov_read_counter ()
{
gcov_type value = 0;
unsigned ix;
const unsigned char *buffer = gcov_read_bytes (8);
if (!buffer)
return 0;
for (ix = sizeof (value); ix < 8; ix++)
if (buffer[ix])
gcov_var.error = -1;
for (ix = 0; ix != 8; ix++)
{
value <<= 8;
value |= buffer[ix];
}
if (value < 0)
gcov_var.error = -1;
return value;
}
/* 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. */
static const char *
gcov_read_string ()
{
unsigned length = gcov_read_unsigned ();
if (!length)
return 0;
length += 4 - (length & 3);
return (const char *) gcov_read_bytes (length);
}
#define GCOV_SUMMARY_LENGTH 44
static void
gcov_read_summary (struct gcov_summary *summary)
{
summary->checksum = gcov_read_unsigned ();
summary->runs = gcov_read_unsigned ();
summary->arcs = gcov_read_unsigned ();
summary->arc_sum = gcov_read_counter ();
summary->arc_max_one = gcov_read_counter ();
summary->arc_sum_max = gcov_read_counter ();
}
/* Save the current position in the gcov file. */ /* Save the current position in the gcov file. */
...@@ -770,18 +385,4 @@ gcov_is_error () ...@@ -770,18 +385,4 @@ gcov_is_error ()
return gcov_var.file ? gcov_var.error : 1; return gcov_var.file ? gcov_var.error : 1;
} }
#if IN_GCOV > 0
/* Return the modification time of the current gcov file. */
static time_t
gcov_time ()
{
struct stat status;
if (fstat (fileno (gcov_var.file), &status))
return 0;
else
return status.st_mtime;
}
#endif /* IN_GCOV */
#endif /* GCC_GCOV_IO_H */ #endif /* GCC_GCOV_IO_H */
...@@ -59,6 +59,7 @@ void __gcov_flush (void) { } ...@@ -59,6 +59,7 @@ void __gcov_flush (void) { }
#endif #endif
#define IN_LIBGCOV 1 #define IN_LIBGCOV 1
#include "gcov-io.h" #include "gcov-io.h"
#include "gcov-io.c"
/* Chain of per-object gcov structures. */ /* Chain of per-object gcov structures. */
static struct gcov_info *gcov_list; static struct gcov_info *gcov_list;
...@@ -136,7 +137,7 @@ gcov_exit (void) ...@@ -136,7 +137,7 @@ gcov_exit (void)
int error; int error;
int merging; int merging;
unsigned long base; unsigned long base;
const struct function_info *fn_info; const struct gcov_function_info *fn_info;
gcov_type **counters; gcov_type **counters;
gcov_type *count_ptr; gcov_type *count_ptr;
gcov_type object_max_one = 0; gcov_type object_max_one = 0;
......
...@@ -27,7 +27,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ...@@ -27,7 +27,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "basic-block.h" #include "basic-block.h"
#include "cfgloop.h" #include "cfgloop.h"
#include "cfglayout.h" #include "cfglayout.h"
#include "profile.h"
/* Initialize loop optimizer. */ /* Initialize loop optimizer. */
......
/* Calculate branch probabilities, and basic block execution counts. /* Calculate branch probabilities, and basic block execution counts.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
2000, 2001 Free Software Foundation, Inc. 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
Contributed by James E. Wilson, UC Berkeley/Cygnus Support; Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
based on some ideas from Dain Samples of UC Berkeley. based on some ideas from Dain Samples of UC Berkeley.
Further mangling by Bob Manson, Cygnus Support. Further mangling by Bob Manson, Cygnus Support.
...@@ -53,23 +53,13 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ...@@ -53,23 +53,13 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "coretypes.h" #include "coretypes.h"
#include "tm.h" #include "tm.h"
#include "rtl.h" #include "rtl.h"
#include "tree.h"
#include "flags.h" #include "flags.h"
#include "insn-config.h"
#include "output.h" #include "output.h"
#include "regs.h" #include "regs.h"
#include "expr.h" #include "expr.h"
#include "function.h" #include "function.h"
#include "toplev.h" #include "toplev.h"
#include "ggc.h" #include "coverage.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "gcov-io.h"
#include "target.h"
#include "profile.h"
#include "libfuncs.h"
#include "langhooks.h"
#include "hashtab.h"
/* Additional information about the edges we need. */ /* Additional information about the edges we need. */
struct edge_info { struct edge_info {
...@@ -91,17 +81,6 @@ struct bb_info { ...@@ -91,17 +81,6 @@ struct bb_info {
gcov_type pred_count; gcov_type pred_count;
}; };
struct function_list
{
struct function_list *next; /* next function */
const char *name; /* function name */
unsigned cfg_checksum; /* function checksum */
unsigned n_counter_sections; /* number of counter sections */
struct counter_section counter_sections[MAX_COUNTER_SECTIONS];
/* the sections */
};
/* Counts information for a function. */ /* Counts information for a function. */
typedef struct counts_entry typedef struct counts_entry
{ {
...@@ -122,9 +101,6 @@ typedef struct counts_entry ...@@ -122,9 +101,6 @@ typedef struct counts_entry
} counts_entry_t; } counts_entry_t;
static struct function_list *functions_head = 0;
static struct function_list **functions_tail = &functions_head;
#define EDGE_INFO(e) ((struct edge_info *) (e)->aux) #define EDGE_INFO(e) ((struct edge_info *) (e)->aux)
#define BB_INFO(b) ((struct bb_info *) (b)->aux) #define BB_INFO(b) ((struct bb_info *) (b)->aux)
...@@ -134,21 +110,6 @@ static struct function_list **functions_tail = &functions_head; ...@@ -134,21 +110,6 @@ static struct function_list **functions_tail = &functions_head;
: ((bb) == EXIT_BLOCK_PTR \ : ((bb) == EXIT_BLOCK_PTR \
? last_basic_block + 1 : (bb)->index + 1)) ? last_basic_block + 1 : (bb)->index + 1))
/* Instantiate the profile info structure. */
struct profile_info profile_info;
/* Name and file pointer of the output file for the basic block graph. */
static char *bbg_file_name;
/* Name and file pointer of the input file for the arc count data. */
static char *da_file_name;
/* The name of the count table. Used by the edge profiling code. */
static GTY(()) rtx profiler_label;
/* Collect statistics on the performance of this pass for the entire source /* Collect statistics on the performance of this pass for the entire source
file. */ file. */
...@@ -168,24 +129,9 @@ static void find_spanning_tree PARAMS ((struct edge_list *)); ...@@ -168,24 +129,9 @@ static void find_spanning_tree PARAMS ((struct edge_list *));
static rtx gen_edge_profiler PARAMS ((int)); static rtx gen_edge_profiler PARAMS ((int));
static void instrument_edges PARAMS ((struct edge_list *)); static void instrument_edges PARAMS ((struct edge_list *));
static void compute_branch_probabilities PARAMS ((void)); static void compute_branch_probabilities PARAMS ((void));
static hashval_t htab_counts_entry_hash PARAMS ((const void *));
static int htab_counts_entry_eq PARAMS ((const void *, const void *));
static void htab_counts_entry_del PARAMS ((void *));
static void read_counts_file PARAMS ((const char *));
static gcov_type * get_exec_counts PARAMS ((void)); static gcov_type * get_exec_counts PARAMS ((void));
static unsigned compute_checksum PARAMS ((void));
static basic_block find_group PARAMS ((basic_block)); static basic_block find_group PARAMS ((basic_block));
static void union_groups PARAMS ((basic_block, basic_block)); static void union_groups PARAMS ((basic_block, basic_block));
static void set_purpose PARAMS ((tree, tree));
static rtx label_for_tag PARAMS ((unsigned));
static tree build_counter_section_fields PARAMS ((void));
static tree build_counter_section_value PARAMS ((unsigned, unsigned));
static tree build_counter_section_data_fields PARAMS ((void));
static tree build_counter_section_data_value PARAMS ((unsigned, unsigned));
static tree build_function_info_fields PARAMS ((void));
static tree build_function_info_value PARAMS ((struct function_list *));
static tree build_gcov_info_fields PARAMS ((tree));
static tree build_gcov_info_value PARAMS ((void));
/* Add edge instrumentation code to the entire insn chain. /* Add edge instrumentation code to the entire insn chain.
...@@ -200,7 +146,6 @@ instrument_edges (el) ...@@ -200,7 +146,6 @@ instrument_edges (el)
int num_instr_edges = 0; int num_instr_edges = 0;
int num_edges = NUM_EDGES (el); int num_edges = NUM_EDGES (el);
basic_block bb; basic_block bb;
struct section_info *section_info;
remove_fake_edges (); remove_fake_edges ();
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
...@@ -218,196 +163,18 @@ instrument_edges (el) ...@@ -218,196 +163,18 @@ instrument_edges (el)
e->src->index, e->dest->index, e->src->index, e->dest->index,
EDGE_CRITICAL_P (e) ? " (and split)" : ""); EDGE_CRITICAL_P (e) ? " (and split)" : "");
insert_insn_on_edge ( insert_insn_on_edge (
gen_edge_profiler (total_num_edges_instrumented gen_edge_profiler (num_instr_edges++), e);
+ num_instr_edges++), e);
rebuild_jump_labels (e->insns); rebuild_jump_labels (e->insns);
} }
e = e->succ_next; e = e->succ_next;
} }
} }
section_info = find_counters_section (GCOV_TAG_ARC_COUNTS);
section_info->n_counters_now = num_instr_edges;
total_num_edges_instrumented += num_instr_edges;
section_info->n_counters = total_num_edges_instrumented;
total_num_blocks_created += num_edges; total_num_blocks_created += num_edges;
if (rtl_dump_file) if (rtl_dump_file)
fprintf (rtl_dump_file, "%d edges instrumented\n", num_instr_edges); fprintf (rtl_dump_file, "%d edges instrumented\n", num_instr_edges);
} }
static hashval_t
htab_counts_entry_hash (of)
const void *of;
{
const counts_entry_t *entry = of;
return htab_hash_string (entry->function_name) ^ entry->section;
}
static int
htab_counts_entry_eq (of1, of2)
const void *of1;
const void *of2;
{
const counts_entry_t *entry1 = of1;
const counts_entry_t *entry2 = of2;
return !strcmp (entry1->function_name, entry2->function_name)
&& entry1->section == entry2->section;
}
static void
htab_counts_entry_del (of)
void *of;
{
counts_entry_t *entry = of;
free (entry->function_name);
free (entry->counts);
free (entry);
}
static htab_t counts_hash = NULL;
static void
read_counts_file (const char *name)
{
char *function_name_buffer = NULL;
unsigned version, ix, checksum = -1;
counts_entry_t *summaried = NULL;
unsigned seen_summary = 0;
if (!gcov_open (name, 1))
{
warning ("file %s not found, execution counts assumed to be zero", name);
return;
}
if (gcov_read_unsigned () != GCOV_DATA_MAGIC)
{
warning ("`%s' is not a gcov data file", name);
gcov_close ();
return;
}
else if ((version = gcov_read_unsigned ()) != GCOV_VERSION)
{
char v[4], e[4];
unsigned required = GCOV_VERSION;
for (ix = 4; ix--; required >>= 8, version >>= 8)
{
v[ix] = version;
e[ix] = required;
}
warning ("`%s' is version `%.4s', expected version `%.4s'", name, v, e);
gcov_close ();
return;
}
counts_hash = htab_create (10,
htab_counts_entry_hash, htab_counts_entry_eq,
htab_counts_entry_del);
while (!gcov_is_eof ())
{
unsigned tag, length;
unsigned long offset;
int error;
tag = gcov_read_unsigned ();
length = gcov_read_unsigned ();
offset = gcov_position ();
if (tag == GCOV_TAG_FUNCTION)
{
const char *string = gcov_read_string ();
free (function_name_buffer);
function_name_buffer = string ? xstrdup (string) : NULL;
checksum = gcov_read_unsigned ();
if (seen_summary)
{
/* We have already seen a summary, this means that this
new function begins a new set of program runs. We
must unlink the summaried chain. */
counts_entry_t *entry, *chain;
for (entry = summaried; entry; entry = chain)
{
chain = entry->chain;
entry->max_counter_sum += entry->max_counter;
entry->chain = NULL;
}
summaried = NULL;
seen_summary = 0;
}
}
else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
{
counts_entry_t *entry;
struct gcov_summary summary;
gcov_read_summary (&summary);
seen_summary = 1;
for (entry = summaried; entry; entry = entry->chain)
{
entry->merged += summary.runs;
if (entry->max_counter < summary.arc_sum_max)
entry->max_counter = summary.arc_sum_max;
}
}
else if (GCOV_TAG_IS_SUBTAG (GCOV_TAG_FUNCTION, tag)
&& function_name_buffer)
{
counts_entry_t **slot, *entry, elt;
unsigned n_counts = length / 8;
unsigned ix;
elt.function_name = function_name_buffer;
elt.section = tag;
slot = (counts_entry_t **) htab_find_slot
(counts_hash, &elt, INSERT);
entry = *slot;
if (!entry)
{
*slot = entry = xmalloc (sizeof (counts_entry_t));
entry->function_name = xstrdup (function_name_buffer);
entry->section = tag;
entry->checksum = checksum;
entry->n_counts = n_counts;
entry->counts = xcalloc (n_counts, sizeof (gcov_type));
}
else if (entry->checksum != checksum || entry->n_counts != n_counts)
{
warning ("profile mismatch for `%s'", function_name_buffer);
htab_delete (counts_hash);
break;
}
/* This should always be true for a just allocated entry,
and always false for an existing one. Check this way, in
case the gcov file is corrupt. */
if (!entry->chain || summaried != entry)
{
entry->chain = summaried;
summaried = entry;
}
for (ix = 0; ix != n_counts; ix++)
entry->counts[ix] += gcov_read_counter ();
}
gcov_seek (offset, length);
if ((error = gcov_is_error ()))
{
warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
name);
htab_delete (counts_hash);
break;
}
}
free (function_name_buffer);
gcov_close ();
}
/* Computes hybrid profile for all matching entries in da_file. /* Computes hybrid profile for all matching entries in da_file.
Sets max_counter_in_program as a side effect. */ Sets max_counter_in_program as a side effect. */
...@@ -417,16 +184,8 @@ get_exec_counts () ...@@ -417,16 +184,8 @@ get_exec_counts ()
{ {
unsigned num_edges = 0; unsigned num_edges = 0;
basic_block bb; basic_block bb;
const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)); gcov_type *counts;
counts_entry_t *entry, elt;
profile_info.max_counter_in_program = 0;
profile_info.count_profiles_merged = 0;
/* No hash table, no counts. */
if (!counts_hash)
return NULL;
/* Count the edges to be (possibly) instrumented. */ /* Count the edges to be (possibly) instrumented. */
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb) FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
{ {
...@@ -436,24 +195,9 @@ get_exec_counts () ...@@ -436,24 +195,9 @@ get_exec_counts ()
num_edges++; num_edges++;
} }
elt.function_name = (char *) name; counts = get_coverage_counts (GCOV_TAG_ARC_COUNTS, num_edges);
elt.section = GCOV_TAG_ARC_COUNTS; if (!counts)
entry = htab_find (counts_hash, &elt); return NULL;
if (!entry)
{
warning ("No profile for function '%s' found.", name);
return NULL;
}
if (entry->checksum != profile_info.current_function_cfg_checksum
|| num_edges != entry->n_counts)
{
warning ("profile mismatch for `%s'", current_function_name);
return NULL;
}
profile_info.count_profiles_merged = entry->merged;
profile_info.max_counter_in_program = entry->max_counter_sum;
if (rtl_dump_file) if (rtl_dump_file)
{ {
...@@ -462,7 +206,7 @@ get_exec_counts () ...@@ -462,7 +206,7 @@ get_exec_counts ()
(int)profile_info.max_counter_in_program); (int)profile_info.max_counter_in_program);
} }
return entry->counts; return counts;
} }
...@@ -806,45 +550,6 @@ compute_branch_probabilities () ...@@ -806,45 +550,6 @@ compute_branch_probabilities ()
find_counters_section (GCOV_TAG_ARC_COUNTS)->present = 1; find_counters_section (GCOV_TAG_ARC_COUNTS)->present = 1;
} }
/* Compute checksum for the current function. We generate a CRC32. */
static unsigned
compute_checksum ()
{
unsigned chksum = 0;
basic_block bb;
FOR_EACH_BB (bb)
{
edge e = NULL;
do
{
unsigned value = BB_TO_GCOV_INDEX (e ? e->dest : bb);
unsigned ix;
/* No need to use all bits in value identically, nearly all
functions have less than 256 blocks. */
value ^= value << 16;
value ^= value << 8;
for (ix = 8; ix--; value <<= 1)
{
unsigned feedback;
feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0;
chksum <<= 1;
chksum ^= feedback;
}
e = e ? e->succ_next : bb->succ;
}
while (e);
}
return chksum;
}
/* Instrument and/or analyze program behavior based on program flow graph. /* Instrument and/or analyze program behavior based on program flow graph.
In either case, this function builds a flow graph for the function being In either case, this function builds a flow graph for the function being
compiled. The flow graph is stored in BB_GRAPH. compiled. The flow graph is stored in BB_GRAPH.
...@@ -868,19 +573,6 @@ branch_prob () ...@@ -868,19 +573,6 @@ branch_prob ()
unsigned i; unsigned i;
unsigned num_edges, ignored_edges; unsigned num_edges, ignored_edges;
struct edge_list *el; struct edge_list *el;
const char *name = IDENTIFIER_POINTER
(DECL_ASSEMBLER_NAME (current_function_decl));
profile_info.current_function_cfg_checksum = compute_checksum ();
for (i = 0; i < profile_info.n_sections; i++)
{
profile_info.section_info[i].n_counters_now = 0;
profile_info.section_info[i].present = 0;
}
if (rtl_dump_file)
fprintf (rtl_dump_file, "CFG checksum is %u\n",
profile_info.current_function_cfg_checksum);
total_num_times_called++; total_num_times_called++;
...@@ -998,25 +690,15 @@ branch_prob () ...@@ -998,25 +690,15 @@ branch_prob ()
if (rtl_dump_file) if (rtl_dump_file)
fprintf (rtl_dump_file, "%d ignored edges\n", ignored_edges); fprintf (rtl_dump_file, "%d ignored edges\n", ignored_edges);
/* Create a .bbg file from which gcov can reconstruct the basic block /* Write the .bbg data from which gcov can reconstruct the basic block
graph. First output the number of basic blocks, and then for every graph. First output the number of basic blocks, and then for every
edge output the source and target basic block numbers. edge output the source and target basic block numbers.
NOTE: The format of this file must be compatible with gcov. */ NOTE: The format of this file must be compatible with gcov. */
if (!gcov_is_error ()) if (coverage_begin_output ())
{ {
long offset; long offset;
const char *file = DECL_SOURCE_FILE (current_function_decl);
unsigned line = DECL_SOURCE_LINE (current_function_decl);
/* Announce function */
offset = gcov_write_tag (GCOV_TAG_FUNCTION);
gcov_write_string (name);
gcov_write_unsigned (profile_info.current_function_cfg_checksum);
gcov_write_string (file);
gcov_write_unsigned (line);
gcov_write_length (offset);
/* Basic block flags */ /* Basic block flags */
offset = gcov_write_tag (GCOV_TAG_BLOCKS); offset = gcov_write_tag (GCOV_TAG_BLOCKS);
for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++) for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++)
...@@ -1052,77 +734,77 @@ branch_prob () ...@@ -1052,77 +734,77 @@ branch_prob ()
gcov_write_length (offset); gcov_write_length (offset);
} }
}
/* Output line number information about each basic block for
GCOV utility. */ /* Output line number information about each basic block for GCOV
{ utility. */
char const *prev_file_name = NULL; if (coverage_begin_output ())
{
FOR_EACH_BB (bb) char const *prev_file_name = NULL;
{ long offset;
rtx insn = bb->head;
int ignore_next_note = 0; FOR_EACH_BB (bb)
{
offset = 0; rtx insn = bb->head;
int ignore_next_note = 0;
/* We are looking for line number notes. Search backward
before basic block to find correct ones. */ offset = 0;
insn = prev_nonnote_insn (insn);
if (!insn) /* We are looking for line number notes. Search backward
insn = get_insns (); before basic block to find correct ones. */
else insn = prev_nonnote_insn (insn);
if (!insn)
insn = get_insns ();
else
insn = NEXT_INSN (insn);
while (insn != bb->end)
{
if (GET_CODE (insn) == NOTE)
{
/* Must ignore the line number notes that
immediately follow the end of an inline function
to avoid counting it twice. There is a note
before the call, and one after the call. */
if (NOTE_LINE_NUMBER (insn)
== NOTE_INSN_REPEATED_LINE_NUMBER)
ignore_next_note = 1;
else if (NOTE_LINE_NUMBER (insn) <= 0)
/*NOP*/;
else if (ignore_next_note)
ignore_next_note = 0;
else
{
if (!offset)
{
offset = gcov_write_tag (GCOV_TAG_LINES);
gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
}
/* If this is a new source file, then output the
file's name to the .bb file. */
if (!prev_file_name
|| strcmp (NOTE_SOURCE_FILE (insn),
prev_file_name))
{
prev_file_name = NOTE_SOURCE_FILE (insn);
gcov_write_unsigned (0);
gcov_write_string (prev_file_name);
}
gcov_write_unsigned (NOTE_LINE_NUMBER (insn));
}
}
insn = NEXT_INSN (insn); insn = NEXT_INSN (insn);
}
while (insn != bb->end)
{ if (offset)
if (GET_CODE (insn) == NOTE) {
{ /* A file of NULL indicates the end of run. */
/* Must ignore the line number notes that immediately gcov_write_unsigned (0);
follow the end of an inline function to avoid counting gcov_write_string (NULL);
it twice. There is a note before the call, and one gcov_write_length (offset);
after the call. */ }
if (NOTE_LINE_NUMBER (insn) }
== NOTE_INSN_REPEATED_LINE_NUMBER)
ignore_next_note = 1;
else if (NOTE_LINE_NUMBER (insn) <= 0)
/*NOP*/;
else if (ignore_next_note)
ignore_next_note = 0;
else
{
if (!offset)
{
offset = gcov_write_tag (GCOV_TAG_LINES);
gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
}
/* If this is a new source file, then output
the file's name to the .bb file. */
if (!prev_file_name
|| strcmp (NOTE_SOURCE_FILE (insn),
prev_file_name))
{
prev_file_name = NOTE_SOURCE_FILE (insn);
gcov_write_unsigned (0);
gcov_write_string (prev_file_name);
}
gcov_write_unsigned (NOTE_LINE_NUMBER (insn));
}
}
insn = NEXT_INSN (insn);
}
if (offset)
{
/* A file of NULL indicates the end of run. */
gcov_write_unsigned (0);
gcov_write_string (NULL);
gcov_write_length (offset);
}
if (gcov_is_error ())
warning ("error writing `%s'", bbg_file_name);
}
}
} }
if (flag_branch_probabilities) if (flag_branch_probabilities)
...@@ -1132,33 +814,11 @@ branch_prob () ...@@ -1132,33 +814,11 @@ branch_prob ()
if (cfun->arc_profile && profile_arc_flag) if (cfun->arc_profile && profile_arc_flag)
{ {
struct function_list *item;
instrument_edges (el); instrument_edges (el);
/* Commit changes done by instrumentation. */ /* Commit changes done by instrumentation. */
commit_edge_insertions_watch_calls (); commit_edge_insertions_watch_calls ();
allocate_reg_info (max_reg_num (), FALSE, FALSE); allocate_reg_info (max_reg_num (), FALSE, FALSE);
/* ??? Probably should re-use the existing struct function. */
item = xmalloc (sizeof (struct function_list));
*functions_tail = item;
functions_tail = &item->next;
item->next = 0;
item->name = xstrdup (name);
item->cfg_checksum = profile_info.current_function_cfg_checksum;
item->n_counter_sections = 0;
for (i = 0; i < profile_info.n_sections; i++)
if (profile_info.section_info[i].n_counters_now)
{
item->counter_sections[item->n_counter_sections].tag =
profile_info.section_info[i].tag;
item->counter_sections[item->n_counter_sections].n_counters =
profile_info.section_info[i].n_counters_now;
item->n_counter_sections++;
}
} }
remove_fake_edges (); remove_fake_edges ();
...@@ -1289,40 +949,10 @@ find_spanning_tree (el) ...@@ -1289,40 +949,10 @@ find_spanning_tree (el)
/* Perform file-level initialization for branch-prob processing. */ /* Perform file-level initialization for branch-prob processing. */
void void
init_branch_prob (filename) init_branch_prob ()
const char *filename;
{ {
int len = strlen (filename);
int i; int i;
da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
strcpy (da_file_name, filename);
strcat (da_file_name, GCOV_DATA_SUFFIX);
if (flag_branch_probabilities)
read_counts_file (da_file_name);
if (flag_test_coverage)
{
/* Open the bbg output file. */
bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1);
strcpy (bbg_file_name, filename);
strcat (bbg_file_name, GCOV_GRAPH_SUFFIX);
if (!gcov_open (bbg_file_name, -1))
error ("cannot open %s", bbg_file_name);
gcov_write_unsigned (GCOV_GRAPH_MAGIC);
gcov_write_unsigned (GCOV_VERSION);
}
if (profile_arc_flag)
{
/* Generate and save a copy of this so it can be shared. */
char buf[20];
ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 2);
profiler_label = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
}
total_num_blocks = 0; total_num_blocks = 0;
total_num_edges = 0; total_num_edges = 0;
total_num_edges_ignored = 0; total_num_edges_ignored = 0;
...@@ -1342,22 +972,6 @@ init_branch_prob (filename) ...@@ -1342,22 +972,6 @@ init_branch_prob (filename)
void void
end_branch_prob () end_branch_prob ()
{ {
if (flag_test_coverage)
{
int error = gcov_close ();
if (error)
unlink (bbg_file_name);
#if SELF_COVERAGE
/* If the compiler is instrumented, we should not
unconditionally remove the counts file, because we might be
recompiling ourselves. The .da files are all removed during
copying the stage1 files. */
if (error)
#endif
unlink (da_file_name);
}
if (rtl_dump_file) if (rtl_dump_file)
{ {
fprintf (rtl_dump_file, "\n"); fprintf (rtl_dump_file, "\n");
...@@ -1392,610 +1006,6 @@ end_branch_prob () ...@@ -1392,610 +1006,6 @@ end_branch_prob ()
} }
} }
/* Find (and create if not present) a section with TAG. */
struct section_info *
find_counters_section (tag)
unsigned tag;
{
unsigned i;
for (i = 0; i < profile_info.n_sections; i++)
if (profile_info.section_info[i].tag == tag)
return profile_info.section_info + i;
if (i == MAX_COUNTER_SECTIONS)
abort ();
profile_info.section_info[i].tag = tag;
profile_info.section_info[i].present = 0;
profile_info.section_info[i].n_counters = 0;
profile_info.section_info[i].n_counters_now = 0;
profile_info.n_sections++;
return profile_info.section_info + i;
}
/* Set FIELDS as purpose to VALUE. */
static void
set_purpose (value, fields)
tree value;
tree fields;
{
tree act_field, act_value;
for (act_field = fields, act_value = value;
act_field;
act_field = TREE_CHAIN (act_field), act_value = TREE_CHAIN (act_value))
TREE_PURPOSE (act_value) = act_field;
}
/* Returns label for base of counters inside TAG section. */
static rtx
label_for_tag (tag)
unsigned tag;
{
switch (tag)
{
case GCOV_TAG_ARC_COUNTS:
return profiler_label;
default:
abort ();
}
}
/* Creates fields of struct counter_section (in gcov-io.h). */
static tree
build_counter_section_fields ()
{
tree field, fields;
/* tag */
fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
/* n_counters */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
return fields;
}
/* Creates value of struct counter_section (in gcov-io.h). */
static tree
build_counter_section_value (tag, n_counters)
unsigned tag;
unsigned n_counters;
{
tree value = NULL_TREE;
/* tag */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (tag, 0)),
value);
/* n_counters */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (n_counters, 0)),
value);
return value;
}
/* Creates fields of struct counter_section_data (in gcov-io.h). */
static tree
build_counter_section_data_fields ()
{
tree field, fields, gcov_type, gcov_ptr_type;
gcov_type = make_signed_type (GCOV_TYPE_SIZE);
gcov_ptr_type =
build_pointer_type (build_qualified_type (gcov_type,
TYPE_QUAL_CONST));
/* tag */
fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
/* n_counters */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* counters */
field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
TREE_CHAIN (field) = fields;
fields = field;
return fields;
}
/* Creates value of struct counter_section_data (in gcov-io.h). */
static tree
build_counter_section_data_value (tag, n_counters)
unsigned tag;
unsigned n_counters;
{
tree value = NULL_TREE, counts_table, gcov_type, gcov_ptr_type;
gcov_type = make_signed_type (GCOV_TYPE_SIZE);
gcov_ptr_type
= build_pointer_type (build_qualified_type
(gcov_type, TYPE_QUAL_CONST));
/* tag */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (tag, 0)),
value);
/* n_counters */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (n_counters, 0)),
value);
/* counters */
if (n_counters)
{
tree gcov_type_array_type =
build_array_type (gcov_type,
build_index_type (build_int_2 (n_counters - 1,
0)));
counts_table =
build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE);
TREE_STATIC (counts_table) = 1;
DECL_NAME (counts_table) = get_identifier (XSTR (label_for_tag (tag), 0));
assemble_variable (counts_table, 0, 0, 0);
counts_table = build1 (ADDR_EXPR, gcov_ptr_type, counts_table);
}
else
counts_table = null_pointer_node;
value = tree_cons (NULL_TREE, counts_table, value);
return value;
}
/* Creates fields for struct function_info type (in gcov-io.h). */
static tree
build_function_info_fields ()
{
tree field, fields, counter_section_fields, counter_section_type;
tree counter_sections_ptr_type;
tree string_type =
build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
/* name */
fields = build_decl (FIELD_DECL, NULL_TREE, string_type);
/* checksum */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* n_counter_sections */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* counter_sections */
counter_section_fields = build_counter_section_fields ();
counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
finish_builtin_struct (counter_section_type, "__counter_section",
counter_section_fields, NULL_TREE);
counter_sections_ptr_type =
build_pointer_type
(build_qualified_type (counter_section_type,
TYPE_QUAL_CONST));
field = build_decl (FIELD_DECL, NULL_TREE, counter_sections_ptr_type);
TREE_CHAIN (field) = fields;
fields = field;
return fields;
}
/* Creates value for struct function_info (in gcov-io.h). */
static tree
build_function_info_value (function)
struct function_list *function;
{
tree value = NULL_TREE;
size_t name_len = strlen (function->name);
tree fname = build_string (name_len + 1, function->name);
tree string_type =
build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
tree counter_section_fields, counter_section_type, counter_sections_value;
tree counter_sections_ptr_type, counter_sections_array_type;
unsigned i;
/* name */
TREE_TYPE (fname) =
build_array_type (char_type_node,
build_index_type (build_int_2 (name_len, 0)));
value = tree_cons (NULL_TREE,
build1 (ADDR_EXPR,
string_type,
fname),
value);
/* checksum */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (function->cfg_checksum, 0)),
value);
/* n_counter_sections */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (function->n_counter_sections, 0)),
value);
/* counter_sections */
counter_section_fields = build_counter_section_fields ();
counter_section_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
counter_sections_ptr_type =
build_pointer_type
(build_qualified_type (counter_section_type,
TYPE_QUAL_CONST));
counter_sections_array_type =
build_array_type (counter_section_type,
build_index_type (
build_int_2 (function->n_counter_sections - 1,
0)));
counter_sections_value = NULL_TREE;
for (i = 0; i < function->n_counter_sections; i++)
{
tree counter_section_value =
build_counter_section_value (function->counter_sections[i].tag,
function->counter_sections[i].n_counters);
set_purpose (counter_section_value, counter_section_fields);
counter_sections_value =
tree_cons (NULL_TREE,
build_constructor (counter_section_type,
nreverse (counter_section_value)),
counter_sections_value);
}
finish_builtin_struct (counter_section_type, "__counter_section",
counter_section_fields, NULL_TREE);
if (function->n_counter_sections)
{
counter_sections_value =
build_constructor (counter_sections_array_type,
nreverse (counter_sections_value)),
counter_sections_value = build1 (ADDR_EXPR,
counter_sections_ptr_type,
counter_sections_value);
}
else
counter_sections_value = null_pointer_node;
value = tree_cons (NULL_TREE, counter_sections_value, value);
return value;
}
/* Creates fields of struct gcov_info type (in gcov-io.h). */
static tree
build_gcov_info_fields (gcov_info_type)
tree gcov_info_type;
{
tree field, fields;
char *filename;
int filename_len;
tree string_type =
build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
tree function_info_fields, function_info_type, function_info_ptr_type;
tree counter_section_data_fields, counter_section_data_type;
tree counter_section_data_ptr_type;
/* Version ident */
fields = build_decl (FIELD_DECL, NULL_TREE, long_unsigned_type_node);
/* next -- NULL */
field = build_decl (FIELD_DECL, NULL_TREE,
build_pointer_type (build_qualified_type (gcov_info_type,
TYPE_QUAL_CONST)));
TREE_CHAIN (field) = fields;
fields = field;
/* Filename */
filename = getpwd ();
filename = (filename && da_file_name[0] != '/'
? concat (filename, "/", da_file_name, NULL)
: da_file_name);
filename_len = strlen (filename);
if (filename != da_file_name)
free (filename);
field = build_decl (FIELD_DECL, NULL_TREE, string_type);
TREE_CHAIN (field) = fields;
fields = field;
/* Workspace */
field = build_decl (FIELD_DECL, NULL_TREE, long_integer_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* number of functions */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* function_info table */
function_info_fields = build_function_info_fields ();
function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
finish_builtin_struct (function_info_type, "__function_info",
function_info_fields, NULL_TREE);
function_info_ptr_type =
build_pointer_type
(build_qualified_type (function_info_type,
TYPE_QUAL_CONST));
field = build_decl (FIELD_DECL, NULL_TREE, function_info_ptr_type);
TREE_CHAIN (field) = fields;
fields = field;
/* n_counter_sections */
field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
TREE_CHAIN (field) = fields;
fields = field;
/* counter sections */
counter_section_data_fields = build_counter_section_data_fields ();
counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
finish_builtin_struct (counter_section_data_type, "__counter_section_data",
counter_section_data_fields, NULL_TREE);
counter_section_data_ptr_type =
build_pointer_type
(build_qualified_type (counter_section_data_type,
TYPE_QUAL_CONST));
field = build_decl (FIELD_DECL, NULL_TREE, counter_section_data_ptr_type);
TREE_CHAIN (field) = fields;
fields = field;
return fields;
}
/* Creates struct gcov_info value (in gcov-io.h). */
static tree
build_gcov_info_value ()
{
tree value = NULL_TREE;
tree filename_string;
char *filename;
int filename_len;
unsigned n_functions, i;
struct function_list *item;
tree string_type =
build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
tree function_info_fields, function_info_type, function_info_ptr_type;
tree functions;
tree counter_section_data_fields, counter_section_data_type;
tree counter_section_data_ptr_type, counter_sections;
/* Version ident */
value = tree_cons (NULL_TREE,
convert (long_unsigned_type_node,
build_int_2 (GCOV_VERSION, 0)),
value);
/* next -- NULL */
value = tree_cons (NULL_TREE, null_pointer_node, value);
/* Filename */
filename = getpwd ();
filename = (filename && da_file_name[0] != '/'
? concat (filename, "/", da_file_name, NULL)
: da_file_name);
filename_len = strlen (filename);
filename_string = build_string (filename_len + 1, filename);
if (filename != da_file_name)
free (filename);
TREE_TYPE (filename_string) =
build_array_type (char_type_node,
build_index_type (build_int_2 (filename_len, 0)));
value = tree_cons (NULL_TREE,
build1 (ADDR_EXPR,
string_type,
filename_string),
value);
/* Workspace */
value = tree_cons (NULL_TREE,
convert (long_integer_type_node, integer_zero_node),
value);
/* number of functions */
n_functions = 0;
for (item = functions_head; item != 0; item = item->next, n_functions++)
continue;
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (n_functions, 0)),
value);
/* function_info table */
function_info_fields = build_function_info_fields ();
function_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
function_info_ptr_type =
build_pointer_type (
build_qualified_type (function_info_type,
TYPE_QUAL_CONST));
functions = NULL_TREE;
for (item = functions_head; item != 0; item = item->next)
{
tree function_info_value = build_function_info_value (item);
set_purpose (function_info_value, function_info_fields);
functions = tree_cons (NULL_TREE,
build_constructor (function_info_type,
nreverse (function_info_value)),
functions);
}
finish_builtin_struct (function_info_type, "__function_info",
function_info_fields, NULL_TREE);
/* Create constructor for array. */
if (n_functions)
{
tree array_type;
array_type = build_array_type (
function_info_type,
build_index_type (build_int_2 (n_functions - 1, 0)));
functions = build_constructor (array_type, nreverse (functions));
functions = build1 (ADDR_EXPR,
function_info_ptr_type,
functions);
}
else
functions = null_pointer_node;
value = tree_cons (NULL_TREE, functions, value);
/* n_counter_sections */
value = tree_cons (NULL_TREE,
convert (unsigned_type_node,
build_int_2 (profile_info.n_sections, 0)),
value);
/* counter sections */
counter_section_data_fields = build_counter_section_data_fields ();
counter_section_data_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
counter_sections = NULL_TREE;
for (i = 0; i < profile_info.n_sections; i++)
{
tree counter_sections_value =
build_counter_section_data_value (
profile_info.section_info[i].tag,
profile_info.section_info[i].n_counters);
set_purpose (counter_sections_value, counter_section_data_fields);
counter_sections =
tree_cons (NULL_TREE,
build_constructor (counter_section_data_type,
nreverse (counter_sections_value)),
counter_sections);
}
finish_builtin_struct (counter_section_data_type, "__counter_section_data",
counter_section_data_fields, NULL_TREE);
counter_section_data_ptr_type =
build_pointer_type
(build_qualified_type (counter_section_data_type,
TYPE_QUAL_CONST));
if (profile_info.n_sections)
{
tree cst_type = build_index_type (build_int_2 (profile_info.n_sections-1,
0));
cst_type = build_array_type (counter_section_data_type, cst_type);
counter_sections = build_constructor (cst_type,
nreverse (counter_sections));
counter_sections = build1 (ADDR_EXPR,
counter_section_data_ptr_type,
counter_sections);
}
else
counter_sections = null_pointer_node;
value = tree_cons (NULL_TREE, counter_sections, value);
return value;
}
/* Write out the structure which libgcc uses to locate all the arc
counters. The structures used here must match those defined in
gcov-io.h. Write out the constructor to call __gcov_init. */
void
create_profiler ()
{
tree gcov_info_fields, gcov_info_type, gcov_info_value, gcov_info;
char name[20];
char *ctor_name;
tree ctor;
rtx gcov_info_address;
int save_flag_inline_functions = flag_inline_functions;
unsigned i;
for (i = 0; i < profile_info.n_sections; i++)
if (profile_info.section_info[i].n_counters_now)
break;
if (i == profile_info.n_sections)
return;
gcov_info_type = (*lang_hooks.types.make_type) (RECORD_TYPE);
gcov_info_fields = build_gcov_info_fields (gcov_info_type);
gcov_info_value = build_gcov_info_value ();
set_purpose (gcov_info_value, gcov_info_fields);
finish_builtin_struct (gcov_info_type, "__gcov_info",
gcov_info_fields, NULL_TREE);
gcov_info = build (VAR_DECL, gcov_info_type, NULL_TREE, NULL_TREE);
DECL_INITIAL (gcov_info) =
build_constructor (gcov_info_type, nreverse (gcov_info_value));
TREE_STATIC (gcov_info) = 1;
ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0);
DECL_NAME (gcov_info) = get_identifier (name);
/* Build structure. */
assemble_variable (gcov_info, 0, 0, 0);
/* Build the constructor function to invoke __gcov_init. */
ctor_name = concat (IDENTIFIER_POINTER (get_file_function_name ('I')),
"_GCOV", NULL);
ctor = build_decl (FUNCTION_DECL, get_identifier (ctor_name),
build_function_type (void_type_node, NULL_TREE));
free (ctor_name);
DECL_EXTERNAL (ctor) = 0;
/* It can be a static function as long as collect2 does not have
to scan the object file to find its ctor/dtor routine. */
TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors;
TREE_USED (ctor) = 1;
DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
ctor = (*lang_hooks.decls.pushdecl) (ctor);
rest_of_decl_compilation (ctor, 0, 1, 0);
announce_function (ctor);
current_function_decl = ctor;
DECL_INITIAL (ctor) = error_mark_node;
make_decl_rtl (ctor, NULL);
init_function_start (ctor, input_filename, lineno);
(*lang_hooks.decls.pushlevel) (0);
expand_function_start (ctor, 0);
cfun->arc_profile = 0;
/* Actually generate the code to call __gcov_init. */
gcov_info_address = force_reg (Pmode, XEXP (DECL_RTL (gcov_info), 0));
emit_library_call (gcov_init_libfunc, LCT_NORMAL, VOIDmode, 1,
gcov_info_address, Pmode);
expand_function_end (input_filename, lineno, 0);
(*lang_hooks.decls.poplevel) (1, 0, 1);
/* Since ctor isn't in the list of globals, it would never be emitted
when it's considered to be 'safe' for inlining, so turn off
flag_inline_functions. */
flag_inline_functions = 0;
rest_of_compilation (ctor);
/* Reset flag_inline_functions to its original value. */
flag_inline_functions = save_flag_inline_functions;
if (! quiet_flag)
fflush (asm_out_file);
current_function_decl = NULL_TREE;
if (targetm.have_ctors_dtors)
(* targetm.asm_out.constructor) (XEXP (DECL_RTL (ctor), 0),
DEFAULT_INIT_PRIORITY);
}
/* Output instructions as RTL to increment the edge execution count. */ /* Output instructions as RTL to increment the edge execution count. */
...@@ -2003,27 +1013,21 @@ static rtx ...@@ -2003,27 +1013,21 @@ static rtx
gen_edge_profiler (edgeno) gen_edge_profiler (edgeno)
int edgeno; int edgeno;
{ {
enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0); rtx ref = coverage_counter_ref (GCOV_TAG_ARC_COUNTS, edgeno);
rtx mem_ref, tmp; rtx tmp;
enum machine_mode mode = GET_MODE (ref);
rtx sequence; rtx sequence;
start_sequence (); start_sequence ();
ref = validize_mem (ref);
tmp = force_reg (Pmode, profiler_label); tmp = expand_simple_binop (mode, PLUS, ref, const1_rtx,
tmp = plus_constant (tmp, GCOV_TYPE_SIZE / BITS_PER_UNIT * edgeno); ref, 0, OPTAB_WIDEN);
mem_ref = validize_mem (gen_rtx_MEM (mode, tmp));
set_mem_alias_set (mem_ref, new_alias_set ());
tmp = expand_simple_binop (mode, PLUS, mem_ref, const1_rtx, if (tmp != ref)
mem_ref, 0, OPTAB_WIDEN); emit_move_insn (copy_rtx (ref), tmp);
if (tmp != mem_ref)
emit_move_insn (copy_rtx (mem_ref), tmp);
sequence = get_insns (); sequence = get_insns ();
end_sequence (); end_sequence ();
return sequence; return sequence;
} }
#include "gt-profile.h"
...@@ -2210,11 +2210,15 @@ extern void dump_local_alloc PARAMS ((FILE *)); ...@@ -2210,11 +2210,15 @@ extern void dump_local_alloc PARAMS ((FILE *));
extern int local_alloc PARAMS ((void)); extern int local_alloc PARAMS ((void));
extern int function_invariant_p PARAMS ((rtx)); extern int function_invariant_p PARAMS ((rtx));
/* In coverage.c */
extern void coverage_init (const char *);
extern void coverage_finish (void);
extern void coverage_end_function (void);
/* In profile.c */ /* In profile.c */
extern void init_branch_prob PARAMS ((const char *)); extern void init_branch_prob PARAMS ((void));
extern void branch_prob PARAMS ((void)); extern void branch_prob PARAMS ((void));
extern void end_branch_prob PARAMS ((void)); extern void end_branch_prob PARAMS ((void));
extern void create_profiler PARAMS ((void));
/* In reg-stack.c */ /* In reg-stack.c */
#ifdef BUFSIZ #ifdef BUFSIZ
......
...@@ -2195,7 +2195,7 @@ compile_file () ...@@ -2195,7 +2195,7 @@ compile_file ()
/* Initialize yet another pass. */ /* Initialize yet another pass. */
init_final (main_input_filename); init_final (main_input_filename);
init_branch_prob (aux_base_name); coverage_init (aux_base_name);
timevar_push (TV_PARSE); timevar_push (TV_PARSE);
...@@ -2216,11 +2216,10 @@ compile_file () ...@@ -2216,11 +2216,10 @@ compile_file ()
(*lang_hooks.decls.final_write_globals)(); (*lang_hooks.decls.final_write_globals)();
if (profile_arc_flag) /* This must occur after the loop to output deferred functions.
/* This must occur after the loop to output deferred functions. Else the coverage initializer would not be emitted if all the
Else the profiler initializer would not be emitted if all the functions in this compilation unit were deferred. */
functions in this compilation unit were deferred. */ coverage_finish ();
create_profiler ();
/* Write out any pending weak symbol declarations. */ /* Write out any pending weak symbol declarations. */
...@@ -3765,6 +3764,8 @@ rest_of_compilation (decl) ...@@ -3765,6 +3764,8 @@ rest_of_compilation (decl)
exit_rest_of_compilation: exit_rest_of_compilation:
coverage_end_function ();
/* In case the function was not output, /* In case the function was not output,
don't leave any temporary anonymous types don't leave any temporary anonymous types
queued up for sdb output. */ queued up for sdb output. */
......
/* The tracer pass for the GNU compiler. /* The tracer pass for the GNU compiler.
Contributed by Jan Hubicka, SuSE Labs. Contributed by Jan Hubicka, SuSE Labs.
Copyright (C) 2001, 2002 Free Software Foundation, Inc. Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC. This file is part of GCC.
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
#include "fibheap.h" #include "fibheap.h"
#include "flags.h" #include "flags.h"
#include "params.h" #include "params.h"
#include "profile.h" #include "coverage.h"
static int count_insns PARAMS ((basic_block)); static int count_insns PARAMS ((basic_block));
static bool ignore_bb_p PARAMS ((basic_block)); static bool ignore_bb_p PARAMS ((basic_block));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment