* [PATCH] offline gcda profile processing tool @ 2014-01-13 20:43 Rong Xu 2014-01-16 17:30 ` Rong Xu 0 siblings, 1 reply; 23+ messages in thread From: Rong Xu @ 2014-01-13 20:43 UTC (permalink / raw) To: GCC Patches, Jan Hubicka, Xinliang David Li, Teresa Johnson [-- Attachment #1: Type: text/plain, Size: 612 bytes --] Hi, This patch implements gcov-tool, a offline profile processing tool. This version supports merging two profiles with weights, and scaling the profile with a floating-point / fraction weight. Earlier discussion can be found http://gcc.gnu.org/ml/gcc-patches/2013-11/msg02631.html In the discussion of the earlier patch, Honza wanted a separated patch for interface change for gcov_merge_*() functions. In this patch, the changes for gcov_merge_*() are minimal by using a wrapper function. So I include everything in this patch. Tested with bootstrap profiledboostrap and SPEC2006 profiles. Thanks, -Rong [-- Attachment #2: gcov_tool_patch.txt --] [-- Type: text/plain, Size: 49896 bytes --] 2014-01-13 Rong Xu <xur@google.com> * gcc/gcov-io.c (gcov_read_string): Make this routine available to gcov-tool. (gcov_sync): Ditto. * libgcc/libgcov.h: Include the set of base-type headers for gcov-tool. (struct gcov_info): Make the functions field mutable in gcov-tool compilation. * libgcc/libgcov-merge.c (gcov_get_counter): New wrapper function to get the profile counter. (gcov_get_counter_target): New wrapper function to get the profile value that should not be scaled. (__gcov_merge_add): Replace gcov_read_counter() with the wrapper function. (__gcov_merge_ior): Ditto. (__gcov_merge_time_profile): Ditto. (__gcov_merge_single): Ditto. (__gcov_merge_delta): Ditto. * gcc/Makefile.in: Build and install gcov-tool. * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose in the utility functions. (set_fn_ctrs): Utility function for reading gcda files to in-memory gcov_list object link lists. (tag_function): Ditto. (tag_blocks): Ditto. (tag_arcs): Ditto. (tag_lines): Ditto. (tag_counters): Ditto. (tag_summary): Ditto. (read_gcda_finalize): Ditto. (read_gcda_file): Ditto. (ftw_read_file): Ditto. (read_profile_dir_init): Ditto. (gcov_read_profile_dir): Ditto. (gcov_read_counter_mem): Ditto. (gcov_get_merge_weight): Ditto. (merge_wrapper): A wrapper function that calls merging handler. (gcov_merge): Merge two gcov_info objects with weight. (find_match_gcov_info): Find the match gcov_info in the list. (gcov_profile_merge): Merge two gcov_info object lists. (__gcov_scale_add): Utility functions for scaling profile by a floating pointer weight. (__gcov_scale_ior): Ditto. (__gcov_scale_delta): Ditto. (__gcov_scale_single): Ditto. (gcov_profile_scale): Scale profile with a floating pointer weight. (gcov_profile_normalize): Normalize a profile. (__gcov_scale2_add): Utility function for scaling profile by the weight in fraction form. (__gcov_scale2_ior): Ditto. (__gcov_scale2_delta): Ditto. (__gcov_scale2_single): Ditto. (gcov_profile_scale2): Scale profile with the weight in fraction form. * gcc/gcov-tool.c (unlink_gcda_file): (unlink_profile_dir): Remove gcda files from the profile path. (profile_merge): Merge two profiles in directory. (print_merge_usage_message): Print merge usage. (merge_usage): Print merge usage and exit. (do_merge): Driver for profile merge sub-command. (profile_rewrite2): Scale profile by the weight in fraction form. (profile_rewrite): Scale profile by a floating-point weight. (print_rewrite_usage_message): Print rewrite usage. (rewrite_usage): Print rewrite usage and exit. (do_rewrite): Driver for profile rewrite sub-command. (print_usage): Print gcov-info usage and exit. (print_version): Print gcov-info version. (process_args): Process arguments. (main): Main routine for gcvo-info. Index: gcc/gcov-io.c =================================================================== --- gcc/gcov-io.c (revision 206542) +++ gcc/gcov-io.c (working copy) @@ -558,7 +558,7 @@ gcov_read_counter (void) buffer, or NULL on empty string. You must copy the string before calling another gcov function. */ -#if !IN_LIBGCOV +#if !IN_LIBGCOV || IN_GCOV_TOOL GCOV_LINKAGE const char * gcov_read_string (void) { @@ -635,7 +635,7 @@ gcov_read_summary (struct gcov_summary *summary) } } -#if !IN_LIBGCOV +#if !IN_LIBGCOV || IN_GCOV_TOOL /* Reset to a known position. BASE should have been obtained from gcov_position, LENGTH should be a record length. */ Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 206542) +++ gcc/Makefile.in (working copy) @@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build # Selection of languages to be made. CONFIG_LANGUAGES = @all_selected_languages@ -LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES) +LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) gcov-tool$(exeext) \ + $(CONFIG_LANGUAGES) # Default values for variables overridden in Makefile fragments. # CFLAGS is for the user to override to, e.g., do a cross build with -O2. @@ -196,6 +197,7 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $( # flex output may yield harmless "no previous prototype" warnings build/gengtype-lex.o-warn = -Wno-error gengtype-lex.o-warn = -Wno-error +libgcov-util.o-warn = -Wno-error # All warnings have to be shut off in stage1 if the compiler used then # isn't gcc; configure determines that. WARN_CFLAGS will be either @@ -1485,7 +1487,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \ $(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \ $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \ - lto-wrapper.o + $(GCOV_TOOL_OBJS) lto-wrapper.o # This lists all host object files, whether they are included in this # compilation or not. @@ -1510,6 +1512,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn $(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \ gcc-ranlib$(exeext) \ gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \ + gcov-tool$(exeect) \ gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \ libcommon-target.a libcommon.a libgcc.mk @@ -2566,6 +2569,16 @@ GCOV_DUMP_OBJS = gcov-dump.o gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \ $(LIBS) -o $@ + +libgcov-util.o: $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \ + $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \ + $(srcdir)/../libgcc/libgcov-merge.c \ + $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $< +GCOV_TOOL_OBJS = gcov-tool.o libgcov-util.o +gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS) + +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_TOOL_OBJS) \ + $(LIBS) -o $@ #\f # Build the include directories. The stamp files are stmp-* rather than # s-* so that mostlyclean does not force the include directory to @@ -3194,6 +3207,13 @@ install-common: native lang.install-common install rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ fi +# Install gcov-tool if it was compiled. + -if [ -f gcov-tool$(exeext) ]; \ + then \ + rm -f $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + $(INSTALL_PROGRAM) \ + gcov-tool$(exeext) $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + fi # Install the driver program as $(target_noncanonical)-gcc, # $(target_noncanonical)-gcc-$(version), and also as gcc if native. Index: libgcc/libgcov.h =================================================================== --- libgcc/libgcov.h (revision 206542) +++ libgcc/libgcov.h (working copy) @@ -33,6 +33,9 @@ #define xcalloc calloc #endif +#ifndef IN_GCOV_TOOL +/* About the target. */ + #include "tconfig.h" #include "tsystem.h" #include "coretypes.h" @@ -79,6 +82,25 @@ typedef unsigned gcov_type_unsigned __attribute__ #define GCOV_LOCKED 0 #endif +#else /* IN_GCOV_TOOL */ +/* About the host. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" + +typedef unsigned gcov_unsigned_t; +typedef unsigned gcov_position_t; +/* gcov_type is typedef'd elsewhere for the compiler */ +#if defined (HOST_HAS_F_SETLKW) +#define GCOV_LOCKED 1 +#else +#define GCOV_LOCKED 0 +#endif + +#endif /* !IN_GCOV_TOOL */ + #if defined(inhibit_libc) #define IN_LIBGCOV (-1) #else @@ -159,8 +181,13 @@ struct gcov_info unused) */ unsigned n_functions; /* number of functions */ + +#ifndef IN_GCOV_TOOL const struct gcov_fn_info *const *functions; /* pointer to pointers - to function information */ + to function information */ +#else + const struct gcov_fn_info **functions; +#endif /* !IN_GCOV_TOOL */ }; /* Register a new object file module. */ Index: libgcc/libgcov-merge.c =================================================================== --- libgcc/libgcov-merge.c (revision 206542) +++ libgcc/libgcov-merge.c (working copy) @@ -45,6 +45,26 @@ void __gcov_merge_delta (gcov_type *counters __at #else +static inline gcov_type +gcov_get_counter (void) +{ +#ifndef IN_GCOV_TOOL + return gcov_read_counter (); +#else + return gcov_read_counter_mem () * gcov_get_merge_weight (); +#endif +} + +static inline gcov_type +gcov_get_counter_target (void) +{ +#ifndef IN_GCOV_TOOL + return gcov_read_counter (); +#else + return gcov_read_counter_mem (); +#endif +} + #ifdef L_gcov_merge_add /* The profile merging function that just adds the counters. It is given an array COUNTERS of N_COUNTERS old counters and it reads the same number @@ -53,7 +73,7 @@ void __gcov_merge_add (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters += gcov_read_counter (); + *counters += gcov_get_counter (); } #endif /* L_gcov_merge_add */ @@ -65,7 +85,7 @@ void __gcov_merge_ior (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters |= gcov_read_counter (); + *counters |= gcov_get_counter (); } #endif @@ -81,7 +101,7 @@ __gcov_merge_time_profile (gcov_type *counters, un for (i = 0; i < n_counters; i++) { - value = gcov_read_counter (); + value = gcov_get_counter (); if (value && (!counters[i] || value < counters[i])) counters[i] = value; @@ -109,9 +129,9 @@ __gcov_merge_single (gcov_type *counters, unsigned n_measures = n_counters / 3; for (i = 0; i < n_measures; i++, counters += 3) { - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + value = gcov_get_counter_target (); + counter = gcov_get_counter (); + all = gcov_get_counter (); if (counters[0] == value) counters[1] += counter; @@ -148,10 +168,10 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_measures = n_counters / 4; for (i = 0; i < n_measures; i++, counters += 4) { - /* last = */ gcov_read_counter (); - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + /* last = */ gcov_get_counter (); + value = gcov_get_counter_target (); + counter = gcov_get_counter (); + all = gcov_get_counter (); if (counters[1] == value) counters[2] += counter; Index: libgcc/libgcov-util.c =================================================================== --- libgcc/libgcov-util.c (revision 0) +++ libgcc/libgcov-util.c (revision 0) @@ -0,0 +1,939 @@ +/* Utility functions for reading gcda files into in-memory + gcov_info structures and offline profile processing. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + + +#define IN_GCOV_TOOL 1 +#define L_gcov 1 +#define L_gcov_merge_add 1 +#define L_gcov_merge_single 1 +#define L_gcov_merge_delta 1 +#define L_gcov_merge_ior 1 +#define L_gcov_merge_time_profile 1 + +#include "libgcov.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "demangle.h" + +extern gcov_type gcov_read_counter_mem(); +extern unsigned gcov_get_merge_weight(); + +/* We need the dumping and merge part of code in libgcov. */ +#include "libgcov-driver.c" +#include "libgcov-merge.c" + +/* Verbose mode for debug. */ +static int verbose; + +/* Set verbose flag. */ +void gcov_set_verbose (void) +{ + verbose = 1; +} + +/* The following part is to read Gcda and reconstruct GCOV_INFO. */ + +#include "obstack.h" +#include <unistd.h> +#include <ftw.h> + +static void tag_function (unsigned, unsigned); +static void tag_blocks (unsigned, unsigned); +static void tag_arcs (unsigned, unsigned); +static void tag_lines (unsigned, unsigned); +static void tag_counters (unsigned, unsigned); +static void tag_summary (unsigned, unsigned); + +/* The gcov_info for the first module. */ +static struct gcov_info *curr_gcov_info; +/* The gcov_info being processed. */ +static struct gcov_info *gcov_info_head; +/* This variable contains all the functions in current module. */ +static struct obstack fn_info; +/* The function being processed. */ +static struct gcov_fn_info *curr_fn_info; +/* The number of functions seen so far. */ +static unsigned num_fn_info; +/* This variable contains all the counters for current module. */ +static int k_ctrs_mask[GCOV_COUNTERS]; +/* The kind of counters that have been seen. */ +static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS]; +/* Number of kind of counters that have been seen. */ +static int k_ctrs_types; +/* The longest length of all the filenames. */ +static int max_filename_len; + +/* Merge functions for counters. */ +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { + __gcov_merge_add, + __gcov_merge_add, + __gcov_merge_add, + __gcov_merge_single, + __gcov_merge_delta, + __gcov_merge_single, + __gcov_merge_add, + __gcov_merge_ior, + __gcov_merge_time_profile, +}; + +/* Set the ctrs field in gcvo_fn_info object FN_INFO. */ + +static void +set_fn_ctrs (struct gcov_fn_info *fn_info) +{ + int j = 0, i; + + for (i = 0; i < GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i] == 0) + continue; + fn_info->ctrs[j].num = k_ctrs[i].num; + fn_info->ctrs[j].values = k_ctrs[i].values; + j++; + } + if (k_ctrs_types == 0) + k_ctrs_types = j; + else + gcc_assert (j == k_ctrs_types); +} + +typedef struct tag_format +{ + unsigned tag; + char const *name; + void (*proc) (unsigned, unsigned); +} tag_format_t; + +static const tag_format_t tag_table[] = +{ + {0, "NOP", NULL}, + {0, "UNKNOWN", NULL}, + {0, "COUNTERS", tag_counters}, + {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, + {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, + {GCOV_TAG_ARCS, "ARCS", tag_arcs}, + {GCOV_TAG_LINES, "LINES", tag_lines}, + {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, + {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, + {0, NULL, NULL} +}; + +/* Handler for reading function tag. */ + +static void +tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + int i; + + /* write out previous fn_info. */ + if (num_fn_info) + { + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + } + + /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active + counter types. */ + curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info) + + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1); + + for (i = 0; i < GCOV_COUNTERS; i++) + k_ctrs[i].num = 0; + k_ctrs_types = 0; + + curr_fn_info->key = curr_gcov_info; + curr_fn_info->ident = gcov_read_unsigned (); + curr_fn_info->lineno_checksum = gcov_read_unsigned (); + curr_fn_info->cfg_checksum = gcov_read_unsigned (); + num_fn_info++; + + if (verbose) + fprintf (stdout, "tag one function id=%d\n", curr_fn_info->ident); +} + +/* Handler for reading block tag. */ + +static void +tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading flow arc tag. */ + +static void +tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading line tag. */ + +static void +tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading counters array tag with value as TAG and length of LENGTH. */ + +static void +tag_counters (unsigned tag, unsigned length) +{ + unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); + gcov_type *values; + unsigned ix; + unsigned tag_ix; + + tag_ix = GCOV_COUNTER_FOR_TAG (tag); + gcc_assert (tag_ix < GCOV_COUNTERS); + k_ctrs_mask [tag_ix] = 1; + gcc_assert (k_ctrs[tag_ix].num == 0); + k_ctrs[tag_ix].num = n_counts; + + k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type)); + gcc_assert (values); + + for (ix = 0; ix != n_counts; ix++) + values[ix] = gcov_read_counter (); +} + +/* Handler for reading summary tag. */ + +static void +tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + struct gcov_summary summary; + + gcov_read_summary (&summary); +} + +/* This function is called at the end of reading a gcda file. + It flushes the contents in curr_fn_info to gcov_info object OBJ_INFO. */ + +static void +read_gcda_finalize (struct gcov_info *obj_info) +{ + int i; + + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + + /* We set the following fields: merge, n_functions, and functions. */ + obj_info->n_functions = num_fn_info; + obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info); + + /* wrap all the counter array. */ + for (i=0; i< GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i]) + obj_info->merge[i] = ctr_merge_functions[i]; + } +} + +/* Read the content of a gcda file FILENAME, and return a gcov_info data structure. + Program level summary CURRENT_SUMMARY will also be updated. */ + +static struct gcov_info * +read_gcda_file (const char *filename) +{ + unsigned tags[4]; + unsigned depth = 0; + unsigned magic, version; + struct gcov_info *obj_info; + int i; + + for (i=0; i< GCOV_COUNTERS; i++) + k_ctrs_mask[i] = 0; + k_ctrs_types = 0; + + if (!gcov_open (filename)) + { + fprintf (stderr, "%s:cannot open\n", filename); + return NULL; + } + + /* Read magic. */ + magic = gcov_read_unsigned (); + if (magic != GCOV_DATA_MAGIC) + { + fprintf (stderr, "%s:not a gcov data file\n", filename); + gcov_close (); + return NULL; + } + + /* Read version. */ + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + fprintf (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION); + gcov_close (); + return NULL; + } + + /* Instantiate a gcov_info object. */ + curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) + + sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1); + + obj_info->version = version; + obstack_init (&fn_info); + num_fn_info = 0; + curr_fn_info = 0; + { + char *str_dup = (char*) xmalloc (strlen (filename) + 1); + int len; + + strcpy (str_dup, filename); + obj_info->filename = str_dup; + if ((len = strlen (filename)) > max_filename_len) + max_filename_len = len; + } + + /* Read stamp. */ + obj_info->stamp = gcov_read_unsigned (); + + while (1) + { + gcov_position_t base; + unsigned tag, length; + tag_format_t const *format; + unsigned tag_depth; + int error; + unsigned mask; + + tag = gcov_read_unsigned (); + if (!tag) + break; + length = gcov_read_unsigned (); + base = gcov_position (); + mask = GCOV_TAG_MASK (tag) >> 1; + for (tag_depth = 4; mask; mask >>= 8) + { + if (((mask & 0xff) != 0xff)) + { + fprintf (stderr, "warning: %s:tag `%08x' is invalid\n", filename, tag); + break; + } + tag_depth--; + } + for (format = tag_table; format->name; format++) + if (format->tag == tag) + goto found; + format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; + found:; + if (tag) + { + if (depth && depth < tag_depth) + { + if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) + fprintf (stderr, "warning: %s:tag `%08x' is incorrectly nested\n", + filename, tag); + } + depth = tag_depth; + tags[depth - 1] = tag; + } + + if (format->proc) + { + unsigned long actual_length; + + (*format->proc) (tag, length); + + actual_length = gcov_position () - base; + if (actual_length > length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes overread\n", + filename, actual_length - length); + else if (length > actual_length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes unread\n", + filename, length - actual_length); + } + + gcov_sync (base, length); + if ((error = gcov_is_error ())) + { + fprintf (stderr,error < 0 ? "warning:%s:counter overflow at %lu\n" : + "Warning:%s:read error at %lu\n", filename, + (long unsigned) gcov_position ()); + break; + } + } + + read_gcda_finalize (obj_info); + gcov_close (); + + return obj_info; +} + +/* This will be called by ftw(). It opens and read a gcda file FILENAME. + Return a non-zero value to stop the tree walk. */ + +static int +ftw_read_file (const char *filename, + const struct stat *status ATTRIBUTE_UNUSED, + int type) +{ + int filename_len; + int suffix_len; + struct gcov_info *obj_info; + + /* Only read regular files. */ + if (type != FTW_F) + return 0; + + filename_len = strlen (filename); + suffix_len = strlen (GCOV_DATA_SUFFIX); + + if (filename_len <= suffix_len) + return 0; + + if (strcmp(filename + filename_len - suffix_len, GCOV_DATA_SUFFIX)) + return 0; + + if (verbose) + fprintf (stderr, "reading file: %s\n", filename); + + obj_info = read_gcda_file (filename); + + obj_info->next = gcov_info_head; + gcov_info_head = obj_info; + + return 0; +} + +/* Initializer for reading a profile dir. */ + +static inline void +read_profile_dir_init (void) +{ + gcov_info_head = 0; +} + +/* Driver for read a profile directory and convert into gcov_info list in memory. + Return NULL on error, + Return the head of gcov_info list on success. + Note the file static variable GCOV_MAX_FILENAME is also set. */ + +struct gcov_info * +gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED) +{ + char *pwd; + int ret; + + read_profile_dir_init (); + + if (access (dir_name, R_OK) != 0) + { + fprintf (stderr, "cannot access directory %s\n", dir_name); + return NULL; + } + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (dir_name); + if (ret !=0) + { + fprintf (stderr, "%s is not a directory\n", dir_name); + return NULL; + } + ftw (".", ftw_read_file, 50); + ret = chdir (pwd); + free (pwd); + + + /* gcov_max_filename is defined in libgcov.c that records the + max filename len. We need to set it here to allocate the + array for dumping. */ + gcov_max_filename = max_filename_len; + + return gcov_info_head;; +} + +/* This part of the code is to merge profile counters. */ + +static gcov_type *gcov_value_buf; +static gcov_unsigned_t gcov_value_buf_size; +static gcov_unsigned_t gcov_value_buf_pos; +static unsigned gcov_merge_weight; + +/* Read a counter value from gcov_value_buf array. */ + +gcov_type +gcov_read_counter_mem (void) +{ + gcov_type ret; + gcc_assert (gcov_value_buf_pos < gcov_value_buf_size); + ret = *(gcov_value_buf + gcov_value_buf_pos); + ++gcov_value_buf_pos; + return ret; +} + +/* Return the recorded merge weight. */ + +unsigned +gcov_get_merge_weight (void) +{ + return gcov_merge_weight; +} + +/* A wrapper function for merge functions. It sets up the + value buffer and weights and then calls the merge function. */ + +static void +merge_wrapper (gcov_merge_fn f, gcov_type *v1, gcov_unsigned_t n, + gcov_type *v2, unsigned w) +{ + gcov_value_buf = v2; + gcov_value_buf_pos = 0; + gcov_value_buf_size = n; + gcov_merge_weight = w; + (*f) (v1, n); +} + +/* Offline tool to manipulate profile data. + This tool targets on matched profiles. But it has some tolerance on + unmatched profiles. + When merging p1 to p2 (p2 is the dst), + * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; + emit warning + * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiply by + specified weight; emit warning. + * m.gcda in both p1 and p2: + ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge. + ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep + p2->m.gcda->f and + drop p1->m.gcda->f. A warning is emitted. */ + +/* Add INFO2's counter to INFO1, multiplying by weight W. */ + +static int +gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w) +{ + unsigned f_ix; + unsigned n_functions = info1->n_functions; + int has_mismatch = 0; + + gcc_assert (info2->n_functions == n_functions); + for (f_ix = 0; f_ix < n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix]; + const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr1, *ci_ptr2; + + if (!gfi_ptr1 || gfi_ptr1->key != info1) + continue; + if (!gfi_ptr2 || gfi_ptr2->key != info2) + continue; + + if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum) + { + fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n", + info1->filename); + has_mismatch = 1; + continue; + } + ci_ptr1 = gfi_ptr1->ctrs; + ci_ptr2 = gfi_ptr2->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge1 = info1->merge[t_ix]; + gcov_merge_fn merge2 = info2->merge[t_ix]; + + gcc_assert (merge1 == merge2); + if (!merge1) + continue; + gcc_assert (ci_ptr1->num == ci_ptr2->num); + merge_wrapper (merge1, ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w); + ci_ptr1++; + ci_ptr2++; + } + } + + return has_mismatch; +} + +/* Find and return the match gcov_info object for INFO from ARRAY. + SIZE is the length of ARRAY. + Return NULL if there is no match. */ + +static struct gcov_info * +find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info) +{ + struct gcov_info *gi_ptr; + struct gcov_info *ret = NULL; + int i; + + for (i = 0; i < size; i++) + { + gi_ptr = array[i]; + if (gi_ptr == 0) + continue; + if (!strcmp (gi_ptr->filename, info->filename)) + { + ret = gi_ptr; + array[i] = 0; + break; + } + } + + if (ret && ret->n_functions != info->n_functions) + { + fprintf (stderr, "mismatched profiles in %s (%d functions" + " vs %d functions)\n", + ret->filename, + ret->n_functions, + info->n_functions); + ret = NULL; + } + return ret; +} + +/* Merge the list of gcov_info list from SRC_PROFILE to TGT_PROFILE. + Return 0 on success: without mismatch. + Reutrn 1 on error. */ + +int +gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile, + int w1, int w2) +{ + struct gcov_info *gi_ptr; + struct gcov_info **tgt_infos; + struct gcov_info *tgt_tail; + struct gcov_info **in_src_not_tgt; + unsigned tgt_cnt = 0, src_cnt = 0; + unsigned unmatch_info_cnt = 0; + unsigned int i; + + for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next) + tgt_cnt++; + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + src_cnt++; + tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * tgt_cnt); + gcc_assert (tgt_infos); + in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * src_cnt); + gcc_assert (in_src_not_tgt); + + for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++) + tgt_infos[i] = gi_ptr; + + tgt_tail = tgt_infos[tgt_cnt - 1]; + + /* First pass on tgt_profile, we multiply w1 to all counters. */ + if (w1 > 1) + { + for (i = 0; i < tgt_cnt; i++) + gcov_merge (tgt_infos[i], tgt_infos[i], w1-1); + } + + /* Second pass, add src_profile to the tgt_profile. */ + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + { + struct gcov_info *gi_ptr1; + + gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr); + if (gi_ptr1 == NULL) + { + in_src_not_tgt[unmatch_info_cnt++] = gi_ptr; + continue; + } + gcov_merge (gi_ptr1, gi_ptr, w2); + } + + /* For modules in src but not in tgt. We adjust the counter and append. */ + for (i = 0; i < unmatch_info_cnt; i++) + { + gi_ptr = in_src_not_tgt[i]; + gcov_merge (gi_ptr, gi_ptr, w2 - 1); + tgt_tail->next = gi_ptr; + tgt_tail = gi_ptr; + } + + return 0; +} + +/* This part of code is to scale profile counters. */ + +/* Type of function used to normalize counters. */ +typedef void (*gcov_scale_fn) (gcov_type *, gcov_unsigned_t, double); + +/* Scale arc counters. N_COUNTERS of counter value in COUNTERS array are + multiplied by a factor F. */ + +static void +__gcov_scale_add (gcov_type *counters, unsigned n_counters, double f) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = val * f; + } +} + +/* Scale ior counters. */ + +static void +__gcov_scale_ior (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + double f ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +/* Scale delta counters. Multiplied the counters in COUNTERS array + by a factor of F. */ + +static void +__gcov_scale_delta (gcov_type *counters, unsigned n_counters, double f) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 4)); + n_measures = n_counters / 4; + for (i = 0; i < n_measures; i++, counters += 4) + { + counters[2] *= f; + counters[3] *= f; + } +} + +/* Scale single counters. Multiplied the counters in COUNTERS array + by a factor of F. */ + +static void +__gcov_scale_single (gcov_type *counters, unsigned n_counters, double f) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 3)); + n_measures = n_counters / 3; + for (i = 0; i < n_measures; i++, counters += 3) + { + counters[1] *= f; + counters[2] *= f; + } +} + +/* Scaling functions for counters. */ +static gcov_scale_fn ctr_scale_functions[GCOV_COUNTERS] = { + __gcov_scale_add, + __gcov_scale_add, + __gcov_scale_add, + __gcov_scale_single, + __gcov_scale_delta, + __gcov_scale_single, + __gcov_scale_add, + __gcov_scale_ior, + __gcov_scale_ior, +}; + +/* Driver for scaling profile counters. */ + +int +gcov_profile_scale (struct gcov_info *profile, float scale_factor) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fprintf (stdout, "scale_factor is %f\n", scale_factor); + + /* Scaling the counters. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge = gi_ptr->merge[t_ix]; + + if (!merge) + continue; + (*ctr_scale_functions[t_ix]) (ci_ptr->values, ci_ptr->num, scale_factor); + ci_ptr++; + } + } + + return 0; +} + +/* Driver for normalize profile counters. */ + +int +gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val) +{ + struct gcov_info *gi_ptr; + gcov_type curr_max_val = 0; + unsigned f_ix; + unsigned int i; + float scale_factor; + + /* Find the larest count value. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix < 1; t_ix++) + { + for (i = 0; i < ci_ptr->num; i++) + if (ci_ptr->values[i] > curr_max_val) + curr_max_val = ci_ptr->values[i]; + ci_ptr++; + } + } + + scale_factor = (float)max_val / curr_max_val; + if (verbose) + fprintf (stdout, "max_val is %lld\n", (long long) curr_max_val); + + return gcov_profile_scale (profile, scale_factor); +} + +/* Type of function used to normalize counters. */ +typedef void (*gcov_scale2_fn) (gcov_type *, gcov_unsigned_t, int, int); + +/* Scale2 arc counters. */ + +static void +__gcov_scale2_add (gcov_type *counters, unsigned n_counters, int n, int d) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = (val / d) * n; + } +} + +/* Scale2 ior counters. */ + +static void +__gcov_scale2_ior (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + int n ATTRIBUTE_UNUSED, + int d ATTRIBUTE_UNUSED) +{ + /* do nothing. */ +} + +/* Scale2 delta counters. */ + +static void +__gcov_scale2_delta (gcov_type *counters, unsigned n_counters, int n, int d) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 4)); + n_measures = n_counters / 4; + for (i = 0; i < n_measures; i++, counters += 4) + { + counters[2] = (counters[2] / d) * n; + counters[3] = (counters[3] / d) * n; + } +} + +/* Scale2 single counters. */ + +static void +__gcov_scale2_single (gcov_type *counters, unsigned n_counters, int n, int d) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 3)); + n_measures = n_counters / 3; + for (i = 0; i < n_measures; i++, counters += 3) + { + counters[1] = (counters[2] / d) * n; + counters[2] = (counters[2] / d) * n; + } +} + +/* Scale2 functions for counters. */ +static gcov_scale2_fn ctr_scale2_functions[GCOV_COUNTERS] = { + __gcov_scale2_add, + __gcov_scale2_add, + __gcov_scale2_add, + __gcov_scale2_single, + __gcov_scale2_delta, + __gcov_scale2_single, + __gcov_scale2_add, + __gcov_scale2_ior, + __gcov_scale2_ior, +}; + +/* Driver for scale2 profile counters. */ + +int +gcov_profile_scale2 (struct gcov_info *profile, int n, int d) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fprintf (stdout, "scale_factor is %d/%d\n", n, d); + + gcc_assert (n >= 0 && d > 0); + + /* Scale the counters. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge = gi_ptr->merge[t_ix]; + + if (!merge) + continue; + (*ctr_scale2_functions[t_ix]) (ci_ptr->values, ci_ptr->num, n, d); + ci_ptr++; + } + } + + return 0; +} + Index: gcc/gcov-tool.c =================================================================== --- gcc/gcov-tool.c (revision 0) +++ gcc/gcov-tool.c (revision 0) @@ -0,0 +1,496 @@ +/* Gcc offline profile processing tool support. */ +/* Compile this one with gcc. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "gcov-io.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <ftw.h> +#include <getopt.h> + +extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int); +extern int gcov_profile_normalize (struct gcov_info*, gcov_type); +extern int gcov_profile_scale (struct gcov_info*, float); +extern int gcov_profile_scale2 (struct gcov_info*, int, int); +extern struct gcov_info* gcov_read_profile_dir (const char*, int); +extern void gcov_exit (void); +extern void set_gcov_list (struct gcov_info *); +extern void gcov_set_verbose (void); + +static int verbose; + +/* Remove file NAME if it has a gcda suffix. */ + +static int +unlink_gcda_file (const char *name, + const struct stat *status ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED, + struct FTW *ftwbuf ATTRIBUTE_UNUSED) +{ + int ret = 0; + int len = strlen (name); + int len1 = strlen (GCOV_DATA_SUFFIX); + + if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1)) + remove (name); + + if (ret) + { + fnotice (stderr, "error in removing %s\n", name); + exit (FATAL_EXIT_CODE); + } + + return ret; +} + +/* Remove the gcda files in PATH recursively. */ + +static int +unlink_profile_dir (const char *path) +{ + return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS); +} + +/* Merging profile D1 and D2 with weight as W1 and W2, respectively. + The result profile is written to directory OUT. + Return 0 on success. */ + +static int +profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + struct gcov_info * d2_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + if (d2) + { + d2_profile = gcov_read_profile_dir (d2, 0); + if (!d2_profile) + return 1; + + /* The actual merge: we overwrite to d1_profile. */ + ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2); + + if (ret) + return ret; + } + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage message for profile merge. */ + +static void +print_merge_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n"); +} + +static const struct option merge_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "weight", required_argument, NULL, 'w' }, + { 0, 0, 0, 0 } +}; + +/* Print merge usage and exit. */ + +static void +merge_usage (void) +{ + fnotice (stderr, "Merge subcomand usage:"); + print_merge_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile merge sub-command. */ + +static int +do_merge (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + int w1 = 1, w2 = 1; + + while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'w': + sscanf (optarg, "%d,%d", &w1, &w2); + if (w1 < 0 || w2 < 0) + { + fnotice (stderr, "weights need to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + break; + default: + merge_usage (); + } + } + + if (output_dir == NULL) + output_dir = "merged_profile"; + + if (argc - optind == 2) + ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2); + else + merge_usage (); + + return ret; +} + +/* Scale the counters in profile DIR by a factor of N/D. + Result is written to dir OUT. Return 0 on success. */ + +static int +profile_rewrite2 (const char *dir, const char *out, int n, int d) +{ + char *pwd; + int ret; + struct gcov_info * profile; + + + profile = gcov_read_profile_dir (dir, 0); + if (!profile) + return 1; + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + gcov_profile_scale2 (profile, n, d); + + set_gcov_list (profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* If N_VAL is no-zero, normalize the profile by setting the largest counter + counter value to N_VAL and scale others counters proportionally. + Otherwise, multiply the all counters by SCALE. */ + +static int +profile_rewrite (const char *d1, const char *out, long long n_val, float scale) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + if (n_val) + gcov_profile_normalize (d1_profile, (gcov_type) n_val); + else + gcov_profile_scale (d1_profile, scale); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage function for profile rewrite. */ + +static void +print_rewrite_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n"); + fnotice (file, " -n, --normalize <long long> Normalize the profile\n"); +} + +static const struct option rewrite_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "scale", required_argument, NULL, 's' }, + { "normalize", required_argument, NULL, 'n' }, + { 0, 0, 0, 0 } +}; + +/* Print profile rewrite usage and exit. */ + +static void +rewrite_usage (void) +{ + fnotice (stderr, "Rewrite subcommand usage:"); + print_rewrite_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile rewrite sub-command. */ + +static int +do_rewrite (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + long long normalize_val = 0; + float scale = 1.0; + int numerator = -1; + int denominator = -1; + + while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'n': + if (scale != 1.0) + { + fnotice (stderr, "scaling cannot co-exist with normalization\n"); + scale = 1.0; + } + normalize_val = atoll (optarg); + break; + case 's': + ret = 0; + if (strstr (optarg, "/")) + { + ret = sscanf (optarg, "%d/%d", &numerator, &denominator); + if (ret == 2) + { + gcc_assert (numerator >= 0); + gcc_assert (denominator > 0); + scale = 0.0; + } + } + if (ret != 2) + { + ret = sscanf (optarg, "%f", &scale); + gcc_assert (ret == 1); + } + + if (scale < 0.0) + { + fnotice (stderr, "scale needs to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + if (normalize_val != 0) + { + fnotice (stderr, "normalization cannot co-exist with scaling\n"); + normalize_val = 0; + } + break; + default: + rewrite_usage (); + } + } + + if (output_dir == NULL) + output_dir = "rewrite_profile"; + + if (argc - optind == 1) + { + if (denominator > 0) + ret = profile_rewrite2 (argv[optind], output_dir, numerator, denominator); + else + ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale); + } + else + rewrite_usage (); + + return ret; +} + +/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, + otherwise the output of --help. */ + +static void +print_usage (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; + + fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname); + fnotice (file, "Offline tool to handle gcda counts.\n\n"); + fnotice (file, " -h, --help Print this help, then exit\n"); + fnotice (file, " -v, --version Print version number, then exit\n"); + print_merge_usage_message (error_p); + print_rewrite_usage_message (error_p); + fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", + bug_report_url); + exit (status); +} + +/* Print version information and exit. */ + +static void +print_version (void) +{ + fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); + fprintf (stdout, "Copyright %s 2013 Free Software Foundation, Inc.\n", + _("(C)")); + fnotice (stdout, + _("This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or \n" + "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); + exit (SUCCESS_EXIT_CODE); +} + +static const struct option options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } +}; + +/* Process args, return index to first non-arg. */ + +static int +process_args (int argc, char **argv) +{ + int opt; + + while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1) + { + switch (opt) + { + case 'h': + print_usage (false); + /* Print_usage will exit. */ + case 'v': + print_version (); + /* Print_version will exit. */ + default: + print_usage (true); + /* Print_usage will exit. */ + } + } + + return optind; +} + +/* Main function for gcov-tool. */ + +int +main (int argc, char **argv) +{ + const char *p; + const char *sub_command; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) + --p; + progname = p; + + xmalloc_set_program_name (progname); + + /* Unlock the stdio streams. */ + unlock_std_streams (); + + gcc_init_libintl (); + + diagnostic_initialize (global_dc, 0); + + /* Handle response files. */ + expandargv (&argc, &argv); + + process_args (argc, argv); + if (optind >= argc) + print_usage (true); + + sub_command = argv[optind]; + + if (!strcmp (sub_command, "merge")) + return do_merge (argc - optind, argv + optind); + else if (!strcmp (sub_command, "rewrite")) + return do_rewrite (argc - optind, argv + optind); + + print_usage (true); +} ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-01-13 20:43 [PATCH] offline gcda profile processing tool Rong Xu @ 2014-01-16 17:30 ` Rong Xu 2014-03-03 22:02 ` Rong Xu 0 siblings, 1 reply; 23+ messages in thread From: Rong Xu @ 2014-01-16 17:30 UTC (permalink / raw) To: GCC Patches, Jan Hubicka, Xinliang David Li, Teresa Johnson Ping. -Rong On Mon, Jan 13, 2014 at 12:43 PM, Rong Xu <xur@google.com> wrote: > Hi, > > This patch implements gcov-tool, a offline profile processing tool. > This version supports merging two profiles with weights, and scaling > the profile with a floating-point / fraction weight. > > Earlier discussion can be found > http://gcc.gnu.org/ml/gcc-patches/2013-11/msg02631.html > > In the discussion of the earlier patch, Honza wanted a separated > patch for interface change for gcov_merge_*() functions. In this patch, > the changes for gcov_merge_*() are minimal by using a wrapper function. > So I include everything in this patch. > > Tested with bootstrap profiledboostrap and SPEC2006 profiles. > > Thanks, > > -Rong ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-01-16 17:30 ` Rong Xu @ 2014-03-03 22:02 ` Rong Xu 2014-04-15 22:05 ` Jan Hubicka 0 siblings, 1 reply; 23+ messages in thread From: Rong Xu @ 2014-03-03 22:02 UTC (permalink / raw) To: GCC Patches, Jan Hubicka, Xinliang David Li, Teresa Johnson [-- Attachment #1: Type: text/plain, Size: 912 bytes --] Attached is the new patch. Tested with bootstrap, profiledbootstrap and spec. Thanks, -Rong On Thu, Jan 16, 2014 at 9:30 AM, Rong Xu <xur@google.com> wrote: > Ping. > > -Rong > > On Mon, Jan 13, 2014 at 12:43 PM, Rong Xu <xur@google.com> wrote: >> Hi, >> >> This patch implements gcov-tool, a offline profile processing tool. >> This version supports merging two profiles with weights, and scaling >> the profile with a floating-point / fraction weight. >> >> Earlier discussion can be found >> http://gcc.gnu.org/ml/gcc-patches/2013-11/msg02631.html >> >> In the discussion of the earlier patch, Honza wanted a separated >> patch for interface change for gcov_merge_*() functions. In this patch, >> the changes for gcov_merge_*() are minimal by using a wrapper function. >> So I include everything in this patch. >> >> Tested with bootstrap profiledboostrap and SPEC2006 profiles. >> >> Thanks, >> >> -Rong [-- Attachment #2: gcov_tool_patch_v2.txt --] [-- Type: text/plain, Size: 47986 bytes --] 2014-03-03 Rong Xu <xur@google.com> * gcc/gcov-io.c (gcov_read_string): Make this routine available to gcov-tool. (gcov_sync): Ditto. * gcc/Makefile.in: Build and install gcov-tool. * gcc/gcov-tool.c (unlink_gcda_file): Remove one gcda file. (unlink_profile_dir): Remove gcda files from the profile path. (profile_merge): Merge two profiles in directory. (print_merge_usage_message): Print merge usage. (merge_usage): Print merge usage and exit. (do_merge): Driver for profile merge sub-command. (profile_rewrite): Rewrite profile. (print_rewrite_usage_message): Print rewrite usage. (rewrite_usage): Print rewrite usage and exit. (do_rewrite): Driver for profile rewrite sub-command. (print_usage): Print gcov-info usage and exit. (print_version): Print gcov-info version. (process_args): Process arguments. (main): Main routine for gcov-tool. * libgcc/libgcov.h : Include the set of base-type headers for gcov-tool. (struct gcov_info): Make the functions field mutable in gcov-tool compilation. * libgcc/libgcov-merge.c (gcov_get_counter): New wrapper function to get the profile counter. (gcov_get_counter_target): New wrapper function to get the profile values that should not be scaled. (__gcov_merge_add): Replace gcov_read_counter() with the wrapper functions. (__gcov_merge_ior): Ditto. (__gcov_merge_time_profile): Ditto. (__gcov_merge_single): Ditto. (__gcov_merge_delta): Ditto. * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag in the utility functions. (set_fn_ctrs): Utility function for reading gcda files to in-memory gcov_list object link lists. (tag_function): Ditto. (tag_blocks): Ditto. (tag_arcs): Ditto. (tag_lines): Ditto. (tag_counters): Ditto. (tag_summary): Ditto. (read_gcda_finalize): Ditto. (read_gcda_file): Ditto. (ftw_read_file): Ditto. (read_profile_dir_init): Ditto. (gcov_read_profile_dir): Ditto. (gcov_read_counter_mem): Ditto. (gcov_get_merge_weight): Ditto. (merge_wrapper): A wrapper function that calls merging handler. (gcov_merge): Merge two gcov_info objects with weights. (find_match_gcov_info): Find the matched gcov_info in the list. (gcov_profile_merge): Merge two gcov_info object lists. (__gcov_add_counter_op): Process edge profile counter values. (__gcov_ior_counter_op): Process IOR profile counter values. (__gcov_delta_counter_op): Process delta profile counter values. (__gcov_single_counter_op): Process single profile counter values. (fp_scale): Callback function for float-point scaling. (int_scale): Callback function for integer fraction scaling. (gcov_profile_scale): Scaling profile counters. (gcov_profile_normalize): Normalize profile counters. Index: gcc/gcov-io.c =================================================================== --- gcc/gcov-io.c (revision 208237) +++ gcc/gcov-io.c (working copy) @@ -564,7 +564,7 @@ gcov_read_counter (void) buffer, or NULL on empty string. You must copy the string before calling another gcov function. */ -#if !IN_LIBGCOV +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) GCOV_LINKAGE const char * gcov_read_string (void) { @@ -641,7 +641,7 @@ gcov_read_summary (struct gcov_summary *summary) } } -#if !IN_LIBGCOV +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) /* Reset to a known position. BASE should have been obtained from gcov_position, LENGTH should be a record length. */ Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 208237) +++ gcc/Makefile.in (working copy) @@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build # Selection of languages to be made. CONFIG_LANGUAGES = @all_selected_languages@ -LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES) +LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) gcov-tool$(exeext) \ + $(CONFIG_LANGUAGES) # Default values for variables overridden in Makefile fragments. # CFLAGS is for the user to override to, e.g., do a cross build with -O2. @@ -196,6 +197,7 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $( # flex output may yield harmless "no previous prototype" warnings build/gengtype-lex.o-warn = -Wno-error gengtype-lex.o-warn = -Wno-error +libgcov-util.o-warn = -Wno-error # All warnings have to be shut off in stage1 if the compiler used then # isn't gcc; configure determines that. WARN_CFLAGS will be either @@ -769,6 +771,7 @@ GCC_INSTALL_NAME := $(shell echo gcc|sed '$(progra GCC_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gcc|sed '$(program_transform_name)') CPP_INSTALL_NAME := $(shell echo cpp|sed '$(program_transform_name)') GCOV_INSTALL_NAME := $(shell echo gcov|sed '$(program_transform_name)') +GCOV_TOOL_INSTALL_NAME := $(shell echo gcov-tool|sed '$(program_transform_name)') # Setup the testing framework, if you have one EXPECT = `if [ -f $${rootme}/../expect/expect ] ; then \ @@ -1487,7 +1490,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \ $(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \ $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \ - lto-wrapper.o + $(GCOV_TOOL_OBJS) lto-wrapper.o # This lists all host object files, whether they are included in this # compilation or not. @@ -1512,6 +1515,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn $(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \ gcc-ranlib$(exeext) \ gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \ + gcov-tool$(exeect) \ gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \ libcommon-target.a libcommon.a libgcc.mk @@ -2570,6 +2574,16 @@ GCOV_DUMP_OBJS = gcov-dump.o gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \ $(LIBS) -o $@ + +libgcov-util.o: $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \ + $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \ + $(srcdir)/../libgcc/libgcov-merge.c \ + $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $< +GCOV_TOOL_OBJS = gcov-tool.o libgcov-util.o +gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS) + +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_TOOL_OBJS) \ + $(LIBS) -o $@ #\f # Build the include directories. The stamp files are stmp-* rather than # s-* so that mostlyclean does not force the include directory to @@ -3198,6 +3212,13 @@ install-common: native lang.install-common install rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ fi +# Install gcov-tool if it was compiled. + -if [ -f gcov-tool$(exeext) ]; \ + then \ + rm -f $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + $(INSTALL_PROGRAM) \ + gcov-tool$(exeext) $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + fi # Install the driver program as $(target_noncanonical)-gcc, # $(target_noncanonical)-gcc-$(version), and also as gcc if native. Index: gcc/gcov-tool.c =================================================================== --- gcc/gcov-tool.c (revision 0) +++ gcc/gcov-tool.c (revision 0) @@ -0,0 +1,465 @@ +/* Gcc offline profile processing tool support. */ +/* Compile this one with gcc. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "gcov-io.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <ftw.h> +#include <getopt.h> + +extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int); +extern int gcov_profile_normalize (struct gcov_info*, gcov_type); +extern int gcov_profile_scale (struct gcov_info*, float, int, int); +extern struct gcov_info* gcov_read_profile_dir (const char*, int); +extern void gcov_exit (void); +extern void set_gcov_list (struct gcov_info *); +extern void gcov_set_verbose (void); + +static int verbose; + +/* Remove file NAME if it has a gcda suffix. */ + +static int +unlink_gcda_file (const char *name, + const struct stat *status ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED, + struct FTW *ftwbuf ATTRIBUTE_UNUSED) +{ + int ret = 0; + int len = strlen (name); + int len1 = strlen (GCOV_DATA_SUFFIX); + + if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1)) + remove (name); + + if (ret) + { + fnotice (stderr, "error in removing %s\n", name); + exit (FATAL_EXIT_CODE); + } + + return ret; +} + +/* Remove the gcda files in PATH recursively. */ + +static int +unlink_profile_dir (const char *path) +{ + return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS); +} + +/* Merging profile D1 and D2 with weight as W1 and W2, respectively. + The result profile is written to directory OUT. + Return 0 on success. */ + +static int +profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + struct gcov_info * d2_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + if (d2) + { + d2_profile = gcov_read_profile_dir (d2, 0); + if (!d2_profile) + return 1; + + /* The actual merge: we overwrite to d1_profile. */ + ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2); + + if (ret) + return ret; + } + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage message for profile merge. */ + +static void +print_merge_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n"); +} + +static const struct option merge_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "weight", required_argument, NULL, 'w' }, + { 0, 0, 0, 0 } +}; + +/* Print merge usage and exit. */ + +static void +merge_usage (void) +{ + fnotice (stderr, "Merge subcomand usage:"); + print_merge_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile merge sub-command. */ + +static int +do_merge (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + int w1 = 1, w2 = 1; + + optind = 0; + while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'w': + sscanf (optarg, "%d,%d", &w1, &w2); + if (w1 < 0 || w2 < 0) + { + fnotice (stderr, "weights need to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + break; + default: + merge_usage (); + } + } + + if (output_dir == NULL) + output_dir = "merged_profile"; + + if (argc - optind == 2) + ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2); + else + merge_usage (); + + return ret; +} + +/* If N_VAL is no-zero, normalize the profile by setting the largest counter + counter value to N_VAL and scale others counters proportionally. + Otherwise, multiply the all counters by SCALE. */ + +static int +profile_rewrite (const char *d1, const char *out, long long n_val, + float scale, int n, int d) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + if (n_val) + gcov_profile_normalize (d1_profile, (gcov_type) n_val); + else + gcov_profile_scale (d1_profile, scale, n, d); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage function for profile rewrite. */ + +static void +print_rewrite_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n"); + fnotice (file, " -n, --normalize <long long> Normalize the profile\n"); +} + +static const struct option rewrite_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "scale", required_argument, NULL, 's' }, + { "normalize", required_argument, NULL, 'n' }, + { 0, 0, 0, 0 } +}; + +/* Print profile rewrite usage and exit. */ + +static void +rewrite_usage (void) +{ + fnotice (stderr, "Rewrite subcommand usage:"); + print_rewrite_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile rewrite sub-command. */ + +static int +do_rewrite (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + long long normalize_val = 0; + float scale = 1.0; + int numerator = -1; + int denominator = 0; + + optind = 0; + while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'n': + if (scale != 1.0) + { + fnotice (stderr, "scaling cannot co-exist with normalization\n"); + scale = 1.0; + } + normalize_val = atoll (optarg); + break; + case 's': + ret = 0; + if (strstr (optarg, "/")) + { + ret = sscanf (optarg, "%d/%d", &numerator, &denominator); + if (ret == 2) + { + gcc_assert (numerator >= 0); + gcc_assert (denominator > 0); + scale = 0.0; + } + } + if (ret != 2) + { + ret = sscanf (optarg, "%f", &scale); + gcc_assert (ret == 1); + } + + if (scale < 0.0) + { + fnotice (stderr, "scale needs to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + if (normalize_val != 0) + { + fnotice (stderr, "normalization cannot co-exist with scaling\n"); + normalize_val = 0; + } + break; + default: + rewrite_usage (); + } + } + + if (output_dir == NULL) + output_dir = "rewrite_profile"; + + if (argc - optind == 1) + { + if (denominator > 0) + ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator); + else + ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0); + } + else + rewrite_usage (); + + return ret; +} + +/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, + otherwise the output of --help. */ + +static void +print_usage (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; + + fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname); + fnotice (file, "Offline tool to handle gcda counts\n\n"); + fnotice (file, " -h, --help Print this help, then exit\n"); + fnotice (file, " -v, --version Print version number, then exit\n"); + print_merge_usage_message (error_p); + print_rewrite_usage_message (error_p); + fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", + bug_report_url); + exit (status); +} + +/* Print version information and exit. */ + +static void +print_version (void) +{ + fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); + fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n", + _("(C)")); + fnotice (stdout, + _("This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or \n" + "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); + exit (SUCCESS_EXIT_CODE); +} + +static const struct option options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } +}; + +/* Process args, return index to first non-arg. */ + +static int +process_args (int argc, char **argv) +{ + int opt; + + while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1) + { + switch (opt) + { + case 'h': + print_usage (false); + /* Print_usage will exit. */ + case 'v': + print_version (); + /* Print_version will exit. */ + default: + print_usage (true); + /* Print_usage will exit. */ + } + } + + return optind; +} + +/* Main function for gcov-tool. */ + +int +main (int argc, char **argv) +{ + const char *p; + const char *sub_command; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) + --p; + progname = p; + + xmalloc_set_program_name (progname); + + /* Unlock the stdio streams. */ + unlock_std_streams (); + + gcc_init_libintl (); + + diagnostic_initialize (global_dc, 0); + + /* Handle response files. */ + expandargv (&argc, &argv); + + process_args (argc, argv); + if (optind >= argc) + print_usage (true); + + sub_command = argv[optind]; + + if (!strcmp (sub_command, "merge")) + return do_merge (argc - optind, argv + optind); + else if (!strcmp (sub_command, "rewrite")) + return do_rewrite (argc - optind, argv + optind); + + print_usage (true); +} Index: libgcc/libgcov.h =================================================================== --- libgcc/libgcov.h (revision 208237) +++ libgcc/libgcov.h (working copy) @@ -33,6 +33,9 @@ #define xcalloc calloc #endif +#ifndef IN_GCOV_TOOL +/* About the target. */ + #include "tconfig.h" #include "tsystem.h" #include "coretypes.h" @@ -79,6 +82,25 @@ typedef unsigned gcov_type_unsigned __attribute__ #define GCOV_LOCKED 0 #endif +#else /* IN_GCOV_TOOL */ +/* About the host. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" + +typedef unsigned gcov_unsigned_t; +typedef unsigned gcov_position_t; +/* gcov_type is typedef'd elsewhere for the compiler */ +#if defined (HOST_HAS_F_SETLKW) +#define GCOV_LOCKED 1 +#else +#define GCOV_LOCKED 0 +#endif + +#endif /* !IN_GCOV_TOOL */ + #if defined(inhibit_libc) #define IN_LIBGCOV (-1) #else @@ -159,8 +181,13 @@ struct gcov_info unused) */ unsigned n_functions; /* number of functions */ + +#ifndef IN_GCOV_TOOL const struct gcov_fn_info *const *functions; /* pointer to pointers - to function information */ + to function information */ +#else + const struct gcov_fn_info **functions; +#endif /* !IN_GCOV_TOOL */ }; /* Register a new object file module. */ Index: libgcc/libgcov-merge.c =================================================================== --- libgcc/libgcov-merge.c (revision 208237) +++ libgcc/libgcov-merge.c (working copy) @@ -45,6 +45,26 @@ void __gcov_merge_delta (gcov_type *counters __at #else +static inline gcov_type +gcov_get_counter (void) +{ +#ifndef IN_GCOV_TOOL + return gcov_read_counter (); +#else + return gcov_read_counter_mem () * gcov_get_merge_weight (); +#endif +} + +static inline gcov_type +gcov_get_counter_target (void) +{ +#ifndef IN_GCOV_TOOL + return gcov_read_counter (); +#else + return gcov_read_counter_mem (); +#endif +} + #ifdef L_gcov_merge_add /* The profile merging function that just adds the counters. It is given an array COUNTERS of N_COUNTERS old counters and it reads the same number @@ -53,7 +73,7 @@ void __gcov_merge_add (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters += gcov_read_counter (); + *counters += gcov_get_counter (); } #endif /* L_gcov_merge_add */ @@ -65,7 +85,7 @@ void __gcov_merge_ior (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters |= gcov_read_counter (); + *counters |= gcov_get_counter (); } #endif @@ -81,7 +101,7 @@ __gcov_merge_time_profile (gcov_type *counters, un for (i = 0; i < n_counters; i++) { - value = gcov_read_counter (); + value = gcov_get_counter (); if (value && (!counters[i] || value < counters[i])) counters[i] = value; @@ -109,9 +129,9 @@ __gcov_merge_single (gcov_type *counters, unsigned n_measures = n_counters / 3; for (i = 0; i < n_measures; i++, counters += 3) { - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + value = gcov_get_counter_target (); + counter = gcov_get_counter (); + all = gcov_get_counter (); if (counters[0] == value) counters[1] += counter; @@ -148,10 +168,10 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_measures = n_counters / 4; for (i = 0; i < n_measures; i++, counters += 4) { - /* last = */ gcov_read_counter (); - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + /* last = */ gcov_get_counter (); + value = gcov_get_counter_target (); + counter = gcov_get_counter (); + all = gcov_get_counter (); if (counters[1] == value) counters[2] += counter; Index: libgcc/libgcov-util.c =================================================================== --- libgcc/libgcov-util.c (revision 0) +++ libgcc/libgcov-util.c (revision 0) @@ -0,0 +1,855 @@ +/* Utility functions for reading gcda files into in-memory + gcov_info structures and offline profile processing. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + + +#define IN_GCOV_TOOL 1 +#define L_gcov 1 +#define L_gcov_merge_add 1 +#define L_gcov_merge_single 1 +#define L_gcov_merge_delta 1 +#define L_gcov_merge_ior 1 +#define L_gcov_merge_time_profile 1 + +#include "libgcov.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "demangle.h" + +extern gcov_type gcov_read_counter_mem(); +extern unsigned gcov_get_merge_weight(); + +/* We need the dumping and merge part of code in libgcov. */ +#include "libgcov-driver.c" +#include "libgcov-merge.c" + +/* Verbose mode for debug. */ +static int verbose; + +/* Set verbose flag. */ +void gcov_set_verbose (void) +{ + verbose = 1; +} + +/* The following part is to read Gcda and reconstruct GCOV_INFO. */ + +#include "obstack.h" +#include <unistd.h> +#include <ftw.h> + +static void tag_function (unsigned, unsigned); +static void tag_blocks (unsigned, unsigned); +static void tag_arcs (unsigned, unsigned); +static void tag_lines (unsigned, unsigned); +static void tag_counters (unsigned, unsigned); +static void tag_summary (unsigned, unsigned); + +/* The gcov_info for the first module. */ +static struct gcov_info *curr_gcov_info; +/* The gcov_info being processed. */ +static struct gcov_info *gcov_info_head; +/* This variable contains all the functions in current module. */ +static struct obstack fn_info; +/* The function being processed. */ +static struct gcov_fn_info *curr_fn_info; +/* The number of functions seen so far. */ +static unsigned num_fn_info; +/* This variable contains all the counters for current module. */ +static int k_ctrs_mask[GCOV_COUNTERS]; +/* The kind of counters that have been seen. */ +static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS]; +/* Number of kind of counters that have been seen. */ +static int k_ctrs_types; +/* The longest length of all the filenames. */ +static int max_filename_len; + +/* Merge functions for counters. */ +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { + __gcov_merge_add, + __gcov_merge_add, + __gcov_merge_add, + __gcov_merge_single, + __gcov_merge_delta, + __gcov_merge_single, + __gcov_merge_add, + __gcov_merge_ior, + __gcov_merge_time_profile, +}; + +/* Set the ctrs field in gcov_fn_info object FN_INFO. */ + +static void +set_fn_ctrs (struct gcov_fn_info *fn_info) +{ + int j = 0, i; + + for (i = 0; i < GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i] == 0) + continue; + fn_info->ctrs[j].num = k_ctrs[i].num; + fn_info->ctrs[j].values = k_ctrs[i].values; + j++; + } + if (k_ctrs_types == 0) + k_ctrs_types = j; + else + gcc_assert (j == k_ctrs_types); +} + +typedef struct tag_format +{ + unsigned tag; + char const *name; + void (*proc) (unsigned, unsigned); +} tag_format_t; + +static const tag_format_t tag_table[] = +{ + {0, "NOP", NULL}, + {0, "UNKNOWN", NULL}, + {0, "COUNTERS", tag_counters}, + {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, + {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, + {GCOV_TAG_ARCS, "ARCS", tag_arcs}, + {GCOV_TAG_LINES, "LINES", tag_lines}, + {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, + {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, + {0, NULL, NULL} +}; + +/* Handler for reading function tag. */ + +static void +tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + int i; + + /* write out previous fn_info. */ + if (num_fn_info) + { + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + } + + /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active + counter types. */ + curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info) + + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1); + + for (i = 0; i < GCOV_COUNTERS; i++) + k_ctrs[i].num = 0; + k_ctrs_types = 0; + + curr_fn_info->key = curr_gcov_info; + curr_fn_info->ident = gcov_read_unsigned (); + curr_fn_info->lineno_checksum = gcov_read_unsigned (); + curr_fn_info->cfg_checksum = gcov_read_unsigned (); + num_fn_info++; + + if (verbose) + fprintf (stdout, "tag one function id=%d\n", curr_fn_info->ident); +} + +/* Handler for reading block tag. */ + +static void +tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading flow arc tag. */ + +static void +tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading line tag. */ + +static void +tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + gcc_assert (0); +} + +/* Handler for reading counters array tag with value as TAG and length of LENGTH. */ + +static void +tag_counters (unsigned tag, unsigned length) +{ + unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); + gcov_type *values; + unsigned ix; + unsigned tag_ix; + + tag_ix = GCOV_COUNTER_FOR_TAG (tag); + gcc_assert (tag_ix < GCOV_COUNTERS); + k_ctrs_mask [tag_ix] = 1; + gcc_assert (k_ctrs[tag_ix].num == 0); + k_ctrs[tag_ix].num = n_counts; + + k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type)); + gcc_assert (values); + + for (ix = 0; ix != n_counts; ix++) + values[ix] = gcov_read_counter (); +} + +/* Handler for reading summary tag. */ + +static void +tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + struct gcov_summary summary; + + gcov_read_summary (&summary); +} + +/* This function is called at the end of reading a gcda file. + It flushes the contents in curr_fn_info to gcov_info object OBJ_INFO. */ + +static void +read_gcda_finalize (struct gcov_info *obj_info) +{ + int i; + + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + + /* We set the following fields: merge, n_functions, and functions. */ + obj_info->n_functions = num_fn_info; + obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info); + + /* wrap all the counter array. */ + for (i=0; i< GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i]) + obj_info->merge[i] = ctr_merge_functions[i]; + } +} + +/* Read the content of a gcda file FILENAME, and return a gcov_info data structure. + Program level summary CURRENT_SUMMARY will also be updated. */ + +static struct gcov_info * +read_gcda_file (const char *filename) +{ + unsigned tags[4]; + unsigned depth = 0; + unsigned magic, version; + struct gcov_info *obj_info; + int i; + + for (i=0; i< GCOV_COUNTERS; i++) + k_ctrs_mask[i] = 0; + k_ctrs_types = 0; + + if (!gcov_open (filename)) + { + fprintf (stderr, "%s:cannot open\n", filename); + return NULL; + } + + /* Read magic. */ + magic = gcov_read_unsigned (); + if (magic != GCOV_DATA_MAGIC) + { + fprintf (stderr, "%s:not a gcov data file\n", filename); + gcov_close (); + return NULL; + } + + /* Read version. */ + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + fprintf (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION); + gcov_close (); + return NULL; + } + + /* Instantiate a gcov_info object. */ + curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) + + sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1); + + obj_info->version = version; + obstack_init (&fn_info); + num_fn_info = 0; + curr_fn_info = 0; + { + char *str_dup = (char*) xmalloc (strlen (filename) + 1); + int len; + + strcpy (str_dup, filename); + obj_info->filename = str_dup; + if ((len = strlen (filename)) > max_filename_len) + max_filename_len = len; + } + + /* Read stamp. */ + obj_info->stamp = gcov_read_unsigned (); + + while (1) + { + gcov_position_t base; + unsigned tag, length; + tag_format_t const *format; + unsigned tag_depth; + int error; + unsigned mask; + + tag = gcov_read_unsigned (); + if (!tag) + break; + length = gcov_read_unsigned (); + base = gcov_position (); + mask = GCOV_TAG_MASK (tag) >> 1; + for (tag_depth = 4; mask; mask >>= 8) + { + if (((mask & 0xff) != 0xff)) + { + fprintf (stderr, "warning: %s:tag `%08x' is invalid\n", filename, tag); + break; + } + tag_depth--; + } + for (format = tag_table; format->name; format++) + if (format->tag == tag) + goto found; + format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; + found:; + if (tag) + { + if (depth && depth < tag_depth) + { + if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) + fprintf (stderr, "warning: %s:tag `%08x' is incorrectly nested\n", + filename, tag); + } + depth = tag_depth; + tags[depth - 1] = tag; + } + + if (format->proc) + { + unsigned long actual_length; + + (*format->proc) (tag, length); + + actual_length = gcov_position () - base; + if (actual_length > length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes overread\n", + filename, actual_length - length); + else if (length > actual_length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes unread\n", + filename, length - actual_length); + } + + gcov_sync (base, length); + if ((error = gcov_is_error ())) + { + fprintf (stderr,error < 0 ? "warning:%s:counter overflow at %lu\n" : + "Warning:%s:read error at %lu\n", filename, + (long unsigned) gcov_position ()); + break; + } + } + + read_gcda_finalize (obj_info); + gcov_close (); + + return obj_info; +} + +/* This will be called by ftw(). It opens and read a gcda file FILENAME. + Return a non-zero value to stop the tree walk. */ + +static int +ftw_read_file (const char *filename, + const struct stat *status ATTRIBUTE_UNUSED, + int type) +{ + int filename_len; + int suffix_len; + struct gcov_info *obj_info; + + /* Only read regular files. */ + if (type != FTW_F) + return 0; + + filename_len = strlen (filename); + suffix_len = strlen (GCOV_DATA_SUFFIX); + + if (filename_len <= suffix_len) + return 0; + + if (strcmp(filename + filename_len - suffix_len, GCOV_DATA_SUFFIX)) + return 0; + + if (verbose) + fprintf (stderr, "reading file: %s\n", filename); + + obj_info = read_gcda_file (filename); + + obj_info->next = gcov_info_head; + gcov_info_head = obj_info; + + return 0; +} + +/* Initializer for reading a profile dir. */ + +static inline void +read_profile_dir_init (void) +{ + gcov_info_head = 0; +} + +/* Driver for read a profile directory and convert into gcov_info list in memory. + Return NULL on error, + Return the head of gcov_info list on success. + Note the file static variable GCOV_MAX_FILENAME is also set. */ + +struct gcov_info * +gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED) +{ + char *pwd; + int ret; + + read_profile_dir_init (); + + if (access (dir_name, R_OK) != 0) + { + fprintf (stderr, "cannot access directory %s\n", dir_name); + return NULL; + } + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (dir_name); + if (ret !=0) + { + fprintf (stderr, "%s is not a directory\n", dir_name); + return NULL; + } + ftw (".", ftw_read_file, 50); + ret = chdir (pwd); + free (pwd); + + + /* gcov_max_filename is defined in libgcov.c that records the + max filename len. We need to set it here to allocate the + array for dumping. */ + gcov_max_filename = max_filename_len; + + return gcov_info_head;; +} + +/* This part of the code is to merge profile counters. */ + +static gcov_type *gcov_value_buf; +static gcov_unsigned_t gcov_value_buf_size; +static gcov_unsigned_t gcov_value_buf_pos; +static unsigned gcov_merge_weight; + +/* Read a counter value from gcov_value_buf array. */ + +gcov_type +gcov_read_counter_mem (void) +{ + gcov_type ret; + gcc_assert (gcov_value_buf_pos < gcov_value_buf_size); + ret = *(gcov_value_buf + gcov_value_buf_pos); + ++gcov_value_buf_pos; + return ret; +} + +/* Return the recorded merge weight. */ + +unsigned +gcov_get_merge_weight (void) +{ + return gcov_merge_weight; +} + +/* A wrapper function for merge functions. It sets up the + value buffer and weights and then calls the merge function. */ + +static void +merge_wrapper (gcov_merge_fn f, gcov_type *v1, gcov_unsigned_t n, + gcov_type *v2, unsigned w) +{ + gcov_value_buf = v2; + gcov_value_buf_pos = 0; + gcov_value_buf_size = n; + gcov_merge_weight = w; + (*f) (v1, n); +} + +/* Offline tool to manipulate profile data. + This tool targets on matched profiles. But it has some tolerance on + unmatched profiles. + When merging p1 to p2 (p2 is the dst), + * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; + emit warning + * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiply by + specified weight; emit warning. + * m.gcda in both p1 and p2: + ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge. + ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep + p2->m.gcda->f and + drop p1->m.gcda->f. A warning is emitted. */ + +/* Add INFO2's counter to INFO1, multiplying by weight W. */ + +static int +gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w) +{ + unsigned f_ix; + unsigned n_functions = info1->n_functions; + int has_mismatch = 0; + + gcc_assert (info2->n_functions == n_functions); + for (f_ix = 0; f_ix < n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix]; + const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr1, *ci_ptr2; + + if (!gfi_ptr1 || gfi_ptr1->key != info1) + continue; + if (!gfi_ptr2 || gfi_ptr2->key != info2) + continue; + + if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum) + { + fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n", + info1->filename); + has_mismatch = 1; + continue; + } + ci_ptr1 = gfi_ptr1->ctrs; + ci_ptr2 = gfi_ptr2->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge1 = info1->merge[t_ix]; + gcov_merge_fn merge2 = info2->merge[t_ix]; + + gcc_assert (merge1 == merge2); + if (!merge1) + continue; + gcc_assert (ci_ptr1->num == ci_ptr2->num); + merge_wrapper (merge1, ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w); + ci_ptr1++; + ci_ptr2++; + } + } + + return has_mismatch; +} + +/* Find and return the match gcov_info object for INFO from ARRAY. + SIZE is the length of ARRAY. + Return NULL if there is no match. */ + +static struct gcov_info * +find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info) +{ + struct gcov_info *gi_ptr; + struct gcov_info *ret = NULL; + int i; + + for (i = 0; i < size; i++) + { + gi_ptr = array[i]; + if (gi_ptr == 0) + continue; + if (!strcmp (gi_ptr->filename, info->filename)) + { + ret = gi_ptr; + array[i] = 0; + break; + } + } + + if (ret && ret->n_functions != info->n_functions) + { + fprintf (stderr, "mismatched profiles in %s (%d functions" + " vs %d functions)\n", + ret->filename, + ret->n_functions, + info->n_functions); + ret = NULL; + } + return ret; +} + +/* Merge the list of gcov_info objects from SRC_PROFILE to TGT_PROFILE. + Return 0 on success: without mismatch. + Reutrn 1 on error. */ + +int +gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile, + int w1, int w2) +{ + struct gcov_info *gi_ptr; + struct gcov_info **tgt_infos; + struct gcov_info *tgt_tail; + struct gcov_info **in_src_not_tgt; + unsigned tgt_cnt = 0, src_cnt = 0; + unsigned unmatch_info_cnt = 0; + unsigned int i; + + for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next) + tgt_cnt++; + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + src_cnt++; + tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * tgt_cnt); + gcc_assert (tgt_infos); + in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * src_cnt); + gcc_assert (in_src_not_tgt); + + for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++) + tgt_infos[i] = gi_ptr; + + tgt_tail = tgt_infos[tgt_cnt - 1]; + + /* First pass on tgt_profile, we multiply w1 to all counters. */ + if (w1 > 1) + { + for (i = 0; i < tgt_cnt; i++) + gcov_merge (tgt_infos[i], tgt_infos[i], w1-1); + } + + /* Second pass, add src_profile to the tgt_profile. */ + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + { + struct gcov_info *gi_ptr1; + + gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr); + if (gi_ptr1 == NULL) + { + in_src_not_tgt[unmatch_info_cnt++] = gi_ptr; + continue; + } + gcov_merge (gi_ptr1, gi_ptr, w2); + } + + /* For modules in src but not in tgt. We adjust the counter and append. */ + for (i = 0; i < unmatch_info_cnt; i++) + { + gi_ptr = in_src_not_tgt[i]; + gcov_merge (gi_ptr, gi_ptr, w2 - 1); + tgt_tail->next = gi_ptr; + tgt_tail = gi_ptr; + } + + return 0; +} + +typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*); + +/* Performing FN upon arc counters. */ + +static void +__gcov_add_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = fn(val, data1, data2); + } +} + +/* Performing FN upon ior counters. */ + +static void +__gcov_ior_counter_op (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + counter_op_fn fn ATTRIBUTE_UNUSED, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +/* Performaing FN upon delta counters. */ + +static void +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 4)); + n_measures = n_counters / 4; + for (i = 0; i < n_measures; i++, counters += 4) + { + counters[2] = fn (counters[2], data1, data2); + counters[3] = fn (counters[3], data1, data2); + } +} + +/* Performing FN upon single counters. */ + +static void +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 3)); + n_measures = n_counters / 3; + for (i = 0; i < n_measures; i++, counters += 3) + { + counters[1] = fn (counters[1], data1, data2); + counters[2] = fn (counters[2], data1, data2); + } +} + +/* Scaling the counter value V by multiplying *(float*) DATA1. */ + +static gcov_type +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED) +{ + float f = *(float *) data1; + return (gcov_type) (v * f); +} + +/* Scaling the counter value V by multiplying DATA2/DATA1. */ + +static gcov_type +int_scale (gcov_type v, void *data1, void *data2) +{ + int n = *(int *) data1; + int d = *(int *) data2; + return (gcov_type) ((v / d) * n); +} + +/* Type of function used to process counters. */ +typedef void (*gcov_counter_fn) (gcov_type *, gcov_unsigned_t, + counter_op_fn, void *, void *); + +/* Function array to process profile counters. */ +static gcov_counter_fn ctr_functions[GCOV_COUNTERS] = { + __gcov_add_counter_op, + __gcov_add_counter_op, + __gcov_add_counter_op, + __gcov_single_counter_op, + __gcov_delta_counter_op, + __gcov_single_counter_op, + __gcov_add_counter_op, + __gcov_ior_counter_op, + __gcov_ior_counter_op, +}; + +/* Driver for scaling profile counters. */ + +int +gcov_profile_scale (struct gcov_info *profile, float scale_factor, int n, int d) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fprintf (stdout, "scale_factor is %f\n", scale_factor); + + /* Scaling the counters. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge = gi_ptr->merge[t_ix]; + + if (!merge) + continue; + if (d == 0) + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + fp_scale, &scale_factor, NULL); + else + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + int_scale, &n, &d); + ci_ptr++; + } + } + + return 0; +} + +/* Driver to normalize profile counters. */ + +int +gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val) +{ + struct gcov_info *gi_ptr; + gcov_type curr_max_val = 0; + unsigned f_ix; + unsigned int i; + float scale_factor; + + /* Find the largest count value. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix < 1; t_ix++) + { + for (i = 0; i < ci_ptr->num; i++) + if (ci_ptr->values[i] > curr_max_val) + curr_max_val = ci_ptr->values[i]; + ci_ptr++; + } + } + + scale_factor = (float)max_val / curr_max_val; + if (verbose) + fprintf (stdout, "max_val is %lld\n", (long long) curr_max_val); + + return gcov_profile_scale (profile, scale_factor, 0, 0); +} ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-03-03 22:02 ` Rong Xu @ 2014-04-15 22:05 ` Jan Hubicka 2014-04-16 17:28 ` Rong Xu 0 siblings, 1 reply; 23+ messages in thread From: Jan Hubicka @ 2014-04-15 22:05 UTC (permalink / raw) To: Rong Xu Cc: GCC Patches, Jan Hubicka, Xinliang David Li, Teresa Johnson, dehao Rong, David, Dehao, Teresa I would like to have some rought idea of what we could merge this stage1. There is certainly a lot of interesting stuff on the google branch including AutoFDO, LIPO, the multivalue profile counters that may be used by the new devirtualization bits and more. I also think we should switch counts into floating point representation so Teresa's splitting patch works. Can we get plans to make this effective? My personal schedule is quite free until April 29 when I go to Czech Republic for wedding and I will be back in Calgary at 14th. > 2014-03-03 Rong Xu <xur@google.com> > > * gcc/gcov-io.c (gcov_read_string): Make this routine available > to gcov-tool. > (gcov_sync): Ditto. > * gcc/Makefile.in: Build and install gcov-tool. > * gcc/gcov-tool.c (unlink_gcda_file): Remove one gcda file. > (unlink_profile_dir): Remove gcda files from the profile path. > (profile_merge): Merge two profiles in directory. > (print_merge_usage_message): Print merge usage. > (merge_usage): Print merge usage and exit. > (do_merge): Driver for profile merge sub-command. > (profile_rewrite): Rewrite profile. > (print_rewrite_usage_message): Print rewrite usage. > (rewrite_usage): Print rewrite usage and exit. > (do_rewrite): Driver for profile rewrite sub-command. > (print_usage): Print gcov-info usage and exit. > (print_version): Print gcov-info version. > (process_args): Process arguments. > (main): Main routine for gcov-tool. > * libgcc/libgcov.h : Include the set of base-type headers for > gcov-tool. > (struct gcov_info): Make the functions field mutable in gcov-tool > compilation. > * libgcc/libgcov-merge.c (gcov_get_counter): New wrapper function > to get the profile counter. > (gcov_get_counter_target): New wrapper function to get the profile > values that should not be scaled. > (__gcov_merge_add): Replace gcov_read_counter() with the wrapper > functions. > (__gcov_merge_ior): Ditto. > (__gcov_merge_time_profile): Ditto. > (__gcov_merge_single): Ditto. > (__gcov_merge_delta): Ditto. > * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag > in the utility functions. > (set_fn_ctrs): Utility function for reading gcda files to in-memory > gcov_list object link lists. > (tag_function): Ditto. > (tag_blocks): Ditto. > (tag_arcs): Ditto. > (tag_lines): Ditto. > (tag_counters): Ditto. > (tag_summary): Ditto. > (read_gcda_finalize): Ditto. > (read_gcda_file): Ditto. > (ftw_read_file): Ditto. > (read_profile_dir_init): Ditto. > (gcov_read_profile_dir): Ditto. > (gcov_read_counter_mem): Ditto. > (gcov_get_merge_weight): Ditto. > (merge_wrapper): A wrapper function that calls merging handler. > (gcov_merge): Merge two gcov_info objects with weights. > (find_match_gcov_info): Find the matched gcov_info in the list. > (gcov_profile_merge): Merge two gcov_info object lists. > (__gcov_add_counter_op): Process edge profile counter values. > (__gcov_ior_counter_op): Process IOR profile counter values. > (__gcov_delta_counter_op): Process delta profile counter values. > (__gcov_single_counter_op): Process single profile counter values. > (fp_scale): Callback function for float-point scaling. > (int_scale): Callback function for integer fraction scaling. > (gcov_profile_scale): Scaling profile counters. > (gcov_profile_normalize): Normalize profile counters. > > Index: gcc/gcov-io.c > =================================================================== > --- gcc/gcov-io.c (revision 208237) > +++ gcc/gcov-io.c (working copy) > @@ -564,7 +564,7 @@ gcov_read_counter (void) > buffer, or NULL on empty string. You must copy the string before > calling another gcov function. */ > > -#if !IN_LIBGCOV > +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) > GCOV_LINKAGE const char * > gcov_read_string (void) > { > @@ -641,7 +641,7 @@ gcov_read_summary (struct gcov_summary *summary) > } > } > > -#if !IN_LIBGCOV > +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) > /* Reset to a known position. BASE should have been obtained from > gcov_position, LENGTH should be a record length. */ I am slightly confused here, IN_LIBGCOV IMO means that the gcov-io is going to be linked into the gcov runtime as opposed to gcc, gcov, gcov-dump or gcov-tool. Why we define IN_LIBGCOV && IN_GCOV_TOOL? > Index: gcc/gcov-tool.c > =================================================================== > --- gcc/gcov-tool.c (revision 0) > +++ gcc/gcov-tool.c (revision 0) > @@ -0,0 +1,465 @@ > +/* Gcc offline profile processing tool support. */ > +/* Compile this one with gcc. */ > +/* Copyright (C) 2014 Free Software Foundation, Inc. > + Contributed by Rong Xu <xur@google.com>. > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +Under Section 7 of GPL version 3, you are granted additional > +permissions described in the GCC Runtime Library Exception, version > +3.1, as published by the Free Software Foundation. > + > +You should have received a copy of the GNU General Public License and > +a copy of the GCC Runtime Library Exception along with this program; > +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see > +<http://www.gnu.org/licenses/>. */ > + > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include "tm.h" > +#include "intl.h" > +#include "diagnostic.h" > +#include "version.h" > +#include "gcov-io.h" > +#include <stdlib.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <ftw.h> > +#include <getopt.h> > + I glanced only briefly over the sources, they look resonable. I suppose we will gain more functionality soon? > +#ifndef IN_GCOV_TOOL > +/* About the target. */ > + > #include "tconfig.h" > #include "tsystem.h" > #include "coretypes.h" > @@ -79,6 +82,25 @@ typedef unsigned gcov_type_unsigned __attribute__ > #define GCOV_LOCKED 0 > #endif > > +#else /* IN_GCOV_TOOL */ > +/* About the host. */ > + > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include "tm.h" > + > +typedef unsigned gcov_unsigned_t; > +typedef unsigned gcov_position_t; > +/* gcov_type is typedef'd elsewhere for the compiler */ > +#if defined (HOST_HAS_F_SETLKW) > +#define GCOV_LOCKED 1 > +#else > +#define GCOV_LOCKED 0 > +#endif > + > +#endif /* !IN_GCOV_TOOL */ I see, so the purepose of IN_GCOV_TOOL is that you link special purpose libgcov into gcov-tool. We at least need a documentation for this. > Index: libgcc/libgcov-merge.c > =================================================================== > --- libgcc/libgcov-merge.c (revision 208237) > +++ libgcc/libgcov-merge.c (working copy) > @@ -45,6 +45,26 @@ void __gcov_merge_delta (gcov_type *counters __at > > #else > > +static inline gcov_type > +gcov_get_counter (void) > +{ > +#ifndef IN_GCOV_TOOL > + return gcov_read_counter (); > +#else > + return gcov_read_counter_mem () * gcov_get_merge_weight (); > +#endif > +} > + > +static inline gcov_type > +gcov_get_counter_target (void) > +{ > +#ifndef IN_GCOV_TOOL > + return gcov_read_counter (); > +#else > + return gcov_read_counter_mem (); > +#endif > +} These functions needs documentation. We now have way to separate runtime from basic IO and yet we have those rather ugly ifdefs. Can't we put these into an separate include rather than spreading ifdefs around the code? > Index: libgcc/libgcov-util.c > =================================================================== > --- libgcc/libgcov-util.c (revision 0) > +++ libgcc/libgcov-util.c (revision 0) > @@ -0,0 +1,855 @@ > +/* Utility functions for reading gcda files into in-memory > + gcov_info structures and offline profile processing. */ > +/* Copyright (C) 2014 Free Software Foundation, Inc. > + Contributed by Rong Xu <xur@google.com>. > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +Under Section 7 of GPL version 3, you are granted additional > +permissions described in the GCC Runtime Library Exception, version > +3.1, as published by the Free Software Foundation. > + > +You should have received a copy of the GNU General Public License and > +a copy of the GCC Runtime Library Exception along with this program; > +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see > +<http://www.gnu.org/licenses/>. */ > + > + > +#define IN_GCOV_TOOL 1 > +#define L_gcov 1 > +#define L_gcov_merge_add 1 > +#define L_gcov_merge_single 1 > +#define L_gcov_merge_delta 1 > +#define L_gcov_merge_ior 1 > +#define L_gcov_merge_time_profile 1 > + > +#include "libgcov.h" > +#include "intl.h" > +#include "diagnostic.h" > +#include "version.h" > +#include "demangle.h" > + > +extern gcov_type gcov_read_counter_mem(); > +extern unsigned gcov_get_merge_weight(); > + > +/* We need the dumping and merge part of code in libgcov. */ > +#include "libgcov-driver.c" > +#include "libgcov-merge.c" This is rather ugly. I would preffer Makefile building libgcov-driver/libgcov-merge separately for gcov tool. Would that be too hard to arrange? How libgcov-util is linked into the tool? > + > +/* Merge functions for counters. */ > +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { > + __gcov_merge_add, > + __gcov_merge_add, > + __gcov_merge_add, > + __gcov_merge_single, > + __gcov_merge_delta, > + __gcov_merge_single, > + __gcov_merge_add, > + __gcov_merge_ior, > + __gcov_merge_time_profile, > +}; We are gathering more and more places info about profilers is spread and this seems to duplicate GCOV_MERGE_FUNCTIONS except for the "..". Can't we go with gcov-counters.def file that summarizes it at one place? > + > +/* Set the ctrs field in gcov_fn_info object FN_INFO. */ > + > +static void > +set_fn_ctrs (struct gcov_fn_info *fn_info) > +{ > + int j = 0, i; > + > + for (i = 0; i < GCOV_COUNTERS; i++) > + { > + if (k_ctrs_mask[i] == 0) > + continue; > + fn_info->ctrs[j].num = k_ctrs[i].num; > + fn_info->ctrs[j].values = k_ctrs[i].values; > + j++; > + } > + if (k_ctrs_types == 0) > + k_ctrs_types = j; > + else > + gcc_assert (j == k_ctrs_types); > +} > + > +typedef struct tag_format > +{ > + unsigned tag; > + char const *name; > + void (*proc) (unsigned, unsigned); > +} tag_format_t; > + > +static const tag_format_t tag_table[] = This needs documentation. > +{ > + {0, "NOP", NULL}, > + {0, "UNKNOWN", NULL}, > + {0, "COUNTERS", tag_counters}, > + {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, > + {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, > + {GCOV_TAG_ARCS, "ARCS", tag_arcs}, > + {GCOV_TAG_LINES, "LINES", tag_lines}, > + {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, > + {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, > + {0, NULL, NULL} > +}; > +/* Handler for reading block tag. */ > + > +static void > +tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) > +{ > + gcc_assert (0); > +} > + > +/* Handler for reading flow arc tag. */ > + > +static void > +tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) > +{ > + gcc_assert (0); > +} > + > +/* Handler for reading line tag. */ > + > +static void > +tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) > +{ > + gcc_assert (0); > +} gcc_unreachable? Perhaps with a comment why those are not read in gcda? > + > +/* Performaing FN upon delta counters. */ > + > +static void > +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters, > + counter_op_fn fn, void *data1, void *data2) > +{ > + unsigned i, n_measures; > + > + gcc_assert (!(n_counters % 4)); > + n_measures = n_counters / 4; > + for (i = 0; i < n_measures; i++, counters += 4) > + { > + counters[2] = fn (counters[2], data1, data2); > + counters[3] = fn (counters[3], data1, data2); > + } > +} > + > +/* Performing FN upon single counters. */ > + > +static void > +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters, > + counter_op_fn fn, void *data1, void *data2) > +{ > + unsigned i, n_measures; > + > + gcc_assert (!(n_counters % 3)); > + n_measures = n_counters / 3; > + for (i = 0; i < n_measures; i++, counters += 3) > + { > + counters[1] = fn (counters[1], data1, data2); > + counters[2] = fn (counters[2], data1, data2); > + } Won't this get wrong answer when counters[0] is not the same? I would expected the merging code to compare the counters first. Similarly for delta counter. > + > +/* Scaling the counter value V by multiplying *(float*) DATA1. */ > + > +static gcov_type > +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED) > +{ > + float f = *(float *) data1; > + return (gcov_type) (v * f); > +} > + > +/* Scaling the counter value V by multiplying DATA2/DATA1. */ > + > +static gcov_type > +int_scale (gcov_type v, void *data1, void *data2) > +{ > + int n = *(int *) data1; > + int d = *(int *) data2; > + return (gcov_type) ((v / d) * n); > +} Adding correct rounding may actually make difference for Martin's startup time work. The patch looks resonable in general. I am just concerned about the interfaces within libgcov. In a way it seems to me that there ought to be ways to make this cleaner without that many of ifdefs. Please send updated patch with the changes above and if you have any ideas for cleanups of the interfaces, I would definitely welcome them. Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-04-15 22:05 ` Jan Hubicka @ 2014-04-16 17:28 ` Rong Xu 2014-04-17 5:19 ` Jan Hubicka 0 siblings, 1 reply; 23+ messages in thread From: Rong Xu @ 2014-04-16 17:28 UTC (permalink / raw) To: Jan Hubicka; +Cc: GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen On Tue, Apr 15, 2014 at 2:38 PM, Jan Hubicka <hubicka@ucw.cz> wrote: > Rong, David, Dehao, Teresa > I would like to have some rought idea of what we could merge this stage1. There is > certainly a lot of interesting stuff on the google branch including AutoFDO, LIPO, > the multivalue profile counters that may be used by the new devirtualization bits > and more. I also think we should switch counts into floating point representation > so Teresa's splitting patch works. > > Can we get plans to make this effective? My personal schedule is quite free until > April 29 when I go to Czech Republic for wedding and I will be back in Calgary > at 14th. > >> 2014-03-03 Rong Xu <xur@google.com> >> >> * gcc/gcov-io.c (gcov_read_string): Make this routine available >> to gcov-tool. >> (gcov_sync): Ditto. >> * gcc/Makefile.in: Build and install gcov-tool. >> * gcc/gcov-tool.c (unlink_gcda_file): Remove one gcda file. >> (unlink_profile_dir): Remove gcda files from the profile path. >> (profile_merge): Merge two profiles in directory. >> (print_merge_usage_message): Print merge usage. >> (merge_usage): Print merge usage and exit. >> (do_merge): Driver for profile merge sub-command. >> (profile_rewrite): Rewrite profile. >> (print_rewrite_usage_message): Print rewrite usage. >> (rewrite_usage): Print rewrite usage and exit. >> (do_rewrite): Driver for profile rewrite sub-command. >> (print_usage): Print gcov-info usage and exit. >> (print_version): Print gcov-info version. >> (process_args): Process arguments. >> (main): Main routine for gcov-tool. >> * libgcc/libgcov.h : Include the set of base-type headers for >> gcov-tool. >> (struct gcov_info): Make the functions field mutable in gcov-tool >> compilation. >> * libgcc/libgcov-merge.c (gcov_get_counter): New wrapper function >> to get the profile counter. >> (gcov_get_counter_target): New wrapper function to get the profile >> values that should not be scaled. >> (__gcov_merge_add): Replace gcov_read_counter() with the wrapper >> functions. >> (__gcov_merge_ior): Ditto. >> (__gcov_merge_time_profile): Ditto. >> (__gcov_merge_single): Ditto. >> (__gcov_merge_delta): Ditto. >> * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag >> in the utility functions. >> (set_fn_ctrs): Utility function for reading gcda files to in-memory >> gcov_list object link lists. >> (tag_function): Ditto. >> (tag_blocks): Ditto. >> (tag_arcs): Ditto. >> (tag_lines): Ditto. >> (tag_counters): Ditto. >> (tag_summary): Ditto. >> (read_gcda_finalize): Ditto. >> (read_gcda_file): Ditto. >> (ftw_read_file): Ditto. >> (read_profile_dir_init): Ditto. >> (gcov_read_profile_dir): Ditto. >> (gcov_read_counter_mem): Ditto. >> (gcov_get_merge_weight): Ditto. >> (merge_wrapper): A wrapper function that calls merging handler. >> (gcov_merge): Merge two gcov_info objects with weights. >> (find_match_gcov_info): Find the matched gcov_info in the list. >> (gcov_profile_merge): Merge two gcov_info object lists. >> (__gcov_add_counter_op): Process edge profile counter values. >> (__gcov_ior_counter_op): Process IOR profile counter values. >> (__gcov_delta_counter_op): Process delta profile counter values. >> (__gcov_single_counter_op): Process single profile counter values. >> (fp_scale): Callback function for float-point scaling. >> (int_scale): Callback function for integer fraction scaling. >> (gcov_profile_scale): Scaling profile counters. >> (gcov_profile_normalize): Normalize profile counters. >> >> Index: gcc/gcov-io.c >> =================================================================== >> --- gcc/gcov-io.c (revision 208237) >> +++ gcc/gcov-io.c (working copy) >> @@ -564,7 +564,7 @@ gcov_read_counter (void) >> buffer, or NULL on empty string. You must copy the string before >> calling another gcov function. */ >> >> -#if !IN_LIBGCOV >> +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) >> GCOV_LINKAGE const char * >> gcov_read_string (void) >> { >> @@ -641,7 +641,7 @@ gcov_read_summary (struct gcov_summary *summary) >> } >> } >> >> -#if !IN_LIBGCOV >> +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) >> /* Reset to a known position. BASE should have been obtained from >> gcov_position, LENGTH should be a record length. */ > > I am slightly confused here, IN_LIBGCOV IMO means that the gcov-io is going > to be linked into the gcov runtime as opposed to gcc, gcov, gcov-dump or > gcov-tool. Why we define IN_LIBGCOV && IN_GCOV_TOOL? GCOT_TOOL needs to use this function to read the string in gcda file to memory to construct gcov_info objects. As you noticed, gcov runtime does not need this interface. But gcov-tool links with gcov runtime and it also uses the function. We could make it available in gcov_runtime, but that will slightly increase the memory footprint. >> Index: gcc/gcov-tool.c >> =================================================================== >> --- gcc/gcov-tool.c (revision 0) >> +++ gcc/gcov-tool.c (revision 0) >> @@ -0,0 +1,465 @@ >> +/* Gcc offline profile processing tool support. */ >> +/* Compile this one with gcc. */ >> +/* Copyright (C) 2014 Free Software Foundation, Inc. >> + Contributed by Rong Xu <xur@google.com>. >> + >> +This file is part of GCC. >> + >> +GCC is free software; you can redistribute it and/or modify it under >> +the terms of the GNU General Public License as published by the Free >> +Software Foundation; either version 3, or (at your option) any later >> +version. >> + >> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY >> +WARRANTY; without even the implied warranty of MERCHANTABILITY or >> +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License >> +for more details. >> + >> +Under Section 7 of GPL version 3, you are granted additional >> +permissions described in the GCC Runtime Library Exception, version >> +3.1, as published by the Free Software Foundation. >> + >> +You should have received a copy of the GNU General Public License and >> +a copy of the GCC Runtime Library Exception along with this program; >> +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see >> +<http://www.gnu.org/licenses/>. */ >> + >> +#include "config.h" >> +#include "system.h" >> +#include "coretypes.h" >> +#include "tm.h" >> +#include "intl.h" >> +#include "diagnostic.h" >> +#include "version.h" >> +#include "gcov-io.h" >> +#include <stdlib.h> >> +#include <stdio.h> >> +#include <unistd.h> >> +#include <ftw.h> >> +#include <getopt.h> >> + > > I glanced only briefly over the sources, they look resonable. I suppose we will > gain more functionality soon? The planned new functions for trunk version is: (1) overlap score b/w two profiles (2) better dumping (top hot objects/function/counters) and statistics. Once this basic version is in, we can start to add the new functionality. > >> +#ifndef IN_GCOV_TOOL >> +/* About the target. */ >> + >> #include "tconfig.h" >> #include "tsystem.h" >> #include "coretypes.h" >> @@ -79,6 +82,25 @@ typedef unsigned gcov_type_unsigned __attribute__ >> #define GCOV_LOCKED 0 >> #endif >> >> +#else /* IN_GCOV_TOOL */ >> +/* About the host. */ >> + >> +#include "config.h" >> +#include "system.h" >> +#include "coretypes.h" >> +#include "tm.h" >> + >> +typedef unsigned gcov_unsigned_t; >> +typedef unsigned gcov_position_t; >> +/* gcov_type is typedef'd elsewhere for the compiler */ >> +#if defined (HOST_HAS_F_SETLKW) >> +#define GCOV_LOCKED 1 >> +#else >> +#define GCOV_LOCKED 0 >> +#endif >> + >> +#endif /* !IN_GCOV_TOOL */ > > I see, so the purepose of IN_GCOV_TOOL is that you link special purpose libgcov > into gcov-tool. > We at least need a documentation for this. OK. Will document in the updated patch. >> Index: libgcc/libgcov-merge.c >> =================================================================== >> --- libgcc/libgcov-merge.c (revision 208237) >> +++ libgcc/libgcov-merge.c (working copy) >> @@ -45,6 +45,26 @@ void __gcov_merge_delta (gcov_type *counters __at >> >> #else >> >> +static inline gcov_type >> +gcov_get_counter (void) >> +{ >> +#ifndef IN_GCOV_TOOL >> + return gcov_read_counter (); >> +#else >> + return gcov_read_counter_mem () * gcov_get_merge_weight (); >> +#endif >> +} >> + >> +static inline gcov_type >> +gcov_get_counter_target (void) >> +{ >> +#ifndef IN_GCOV_TOOL >> + return gcov_read_counter (); >> +#else >> + return gcov_read_counter_mem (); >> +#endif >> +} > > These functions needs documentation. > We now have way to separate runtime from basic IO and yet we have those rather > ugly ifdefs. Can't we put these into an separate include rather than spreading > ifdefs around the code? OK. I'll try to refactor this in the new patch. > >> Index: libgcc/libgcov-util.c >> =================================================================== >> --- libgcc/libgcov-util.c (revision 0) >> +++ libgcc/libgcov-util.c (revision 0) >> @@ -0,0 +1,855 @@ >> +/* Utility functions for reading gcda files into in-memory >> + gcov_info structures and offline profile processing. */ >> +/* Copyright (C) 2014 Free Software Foundation, Inc. >> + Contributed by Rong Xu <xur@google.com>. >> + >> +This file is part of GCC. >> + >> +GCC is free software; you can redistribute it and/or modify it under >> +the terms of the GNU General Public License as published by the Free >> +Software Foundation; either version 3, or (at your option) any later >> +version. >> + >> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY >> +WARRANTY; without even the implied warranty of MERCHANTABILITY or >> +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License >> +for more details. >> + >> +Under Section 7 of GPL version 3, you are granted additional >> +permissions described in the GCC Runtime Library Exception, version >> +3.1, as published by the Free Software Foundation. >> + >> +You should have received a copy of the GNU General Public License and >> +a copy of the GCC Runtime Library Exception along with this program; >> +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see >> +<http://www.gnu.org/licenses/>. */ >> + >> + >> +#define IN_GCOV_TOOL 1 >> +#define L_gcov 1 >> +#define L_gcov_merge_add 1 >> +#define L_gcov_merge_single 1 >> +#define L_gcov_merge_delta 1 >> +#define L_gcov_merge_ior 1 >> +#define L_gcov_merge_time_profile 1 >> + >> +#include "libgcov.h" >> +#include "intl.h" >> +#include "diagnostic.h" >> +#include "version.h" >> +#include "demangle.h" >> + >> +extern gcov_type gcov_read_counter_mem(); >> +extern unsigned gcov_get_merge_weight(); >> + >> +/* We need the dumping and merge part of code in libgcov. */ >> +#include "libgcov-driver.c" >> +#include "libgcov-merge.c" > > This is rather ugly. I would preffer Makefile building libgcov-driver/libgcov-merge > separately for gcov tool. Would that be too hard to arrange? > > How libgcov-util is linked into the tool? libgcov-util.o is built in gcc/ directory, rather in libgcc. It's directly linked to gcov-tool. So libgcov-util.o is built for HOST, not TARGET. With makefile changes, we can built HOST version of libgcov-driver.o and libgcov-merge.o. I also need to make some static functions/variables public. I did the way in this patch because it incurs least change in existing files. But, yes, your preferred is doable. >> + >> +/* Merge functions for counters. */ >> +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { >> + __gcov_merge_add, >> + __gcov_merge_add, >> + __gcov_merge_add, >> + __gcov_merge_single, >> + __gcov_merge_delta, >> + __gcov_merge_single, >> + __gcov_merge_add, >> + __gcov_merge_ior, >> + __gcov_merge_time_profile, >> +}; > > We are gathering more and more places info about profilers is spread > and this seems to duplicate GCOV_MERGE_FUNCTIONS except for the "..". > Can't we go with gcov-counters.def file that summarizes it at one place? OK. I'll try to re-factor this. >> + >> +/* Set the ctrs field in gcov_fn_info object FN_INFO. */ >> + >> +static void >> +set_fn_ctrs (struct gcov_fn_info *fn_info) >> +{ >> + int j = 0, i; >> + >> + for (i = 0; i < GCOV_COUNTERS; i++) >> + { >> + if (k_ctrs_mask[i] == 0) >> + continue; >> + fn_info->ctrs[j].num = k_ctrs[i].num; >> + fn_info->ctrs[j].values = k_ctrs[i].values; >> + j++; >> + } >> + if (k_ctrs_types == 0) >> + k_ctrs_types = j; >> + else >> + gcc_assert (j == k_ctrs_types); >> +} >> + >> +typedef struct tag_format >> +{ >> + unsigned tag; >> + char const *name; >> + void (*proc) (unsigned, unsigned); >> +} tag_format_t; >> + >> +static const tag_format_t tag_table[] = > > This needs documentation. Will do in the new patch. >> +{ >> + {0, "NOP", NULL}, >> + {0, "UNKNOWN", NULL}, >> + {0, "COUNTERS", tag_counters}, >> + {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, >> + {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, >> + {GCOV_TAG_ARCS, "ARCS", tag_arcs}, >> + {GCOV_TAG_LINES, "LINES", tag_lines}, >> + {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, >> + {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, >> + {0, NULL, NULL} >> +}; >> +/* Handler for reading block tag. */ >> + >> +static void >> +tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) >> +{ >> + gcc_assert (0); >> +} >> + >> +/* Handler for reading flow arc tag. */ >> + >> +static void >> +tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) >> +{ >> + gcc_assert (0); >> +} >> + >> +/* Handler for reading line tag. */ >> + >> +static void >> +tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) >> +{ >> + gcc_assert (0); >> +} > gcc_unreachable? Perhaps with a comment why those are not read in gcda? gcov-tool currently does not handle gcno file. Will add comments here. >> + >> +/* Performaing FN upon delta counters. */ >> + >> +static void >> +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters, >> + counter_op_fn fn, void *data1, void *data2) >> +{ >> + unsigned i, n_measures; >> + >> + gcc_assert (!(n_counters % 4)); >> + n_measures = n_counters / 4; >> + for (i = 0; i < n_measures; i++, counters += 4) >> + { >> + counters[2] = fn (counters[2], data1, data2); >> + counters[3] = fn (counters[3], data1, data2); >> + } >> +} >> + >> +/* Performing FN upon single counters. */ >> + >> +static void >> +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters, >> + counter_op_fn fn, void *data1, void *data2) >> +{ >> + unsigned i, n_measures; >> + >> + gcc_assert (!(n_counters % 3)); >> + n_measures = n_counters / 3; >> + for (i = 0; i < n_measures; i++, counters += 3) >> + { >> + counters[1] = fn (counters[1], data1, data2); >> + counters[2] = fn (counters[2], data1, data2); >> + } > > Won't this get wrong answer when counters[0] is not the same? > I would expected the merging code to compare the counters first. Similarly for delta counter. These *_op functions are for scaling only. So there is only one profile involved (thus there is no comparison). The merge handles are in libgcov-merge.c which have the code to handle mismatched profile targets. >> + >> +/* Scaling the counter value V by multiplying *(float*) DATA1. */ >> + >> +static gcov_type >> +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED) >> +{ >> + float f = *(float *) data1; >> + return (gcov_type) (v * f); >> +} >> + >> +/* Scaling the counter value V by multiplying DATA2/DATA1. */ >> + >> +static gcov_type >> +int_scale (gcov_type v, void *data1, void *data2) >> +{ >> + int n = *(int *) data1; >> + int d = *(int *) data2; >> + return (gcov_type) ((v / d) * n); >> +} > > Adding correct rounding may actually make difference for Martin's startup time work. Do you mean to use something like in RDIV macro? > > The patch looks resonable in general. I am just concerned about the interfaces within libgcov. > In a way it seems to me that there ought to be ways to make this cleaner without that many of > ifdefs. > Please send updated patch with the changes above and if you have any ideas for cleanups > of the interfaces, I would definitely welcome them. Thanks for the review and suggestion. I'll send an updated patch. > > Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-04-16 17:28 ` Rong Xu @ 2014-04-17 5:19 ` Jan Hubicka 2014-05-01 22:37 ` Rong Xu 0 siblings, 1 reply; 23+ messages in thread From: Jan Hubicka @ 2014-04-17 5:19 UTC (permalink / raw) To: Rong Xu Cc: Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen > GCOT_TOOL needs to use this function to read the string in gcda file > to memory to construct gcov_info objects. > As you noticed, gcov runtime does not need this interface. But > gcov-tool links with gcov runtime and it also uses the function. > We could make it available in gcov_runtime, but that will slightly > increase the memory footprint. Yep, it is not really pretty. I wrote bellow some plan how things may be structured in more convenient way. Any work in that direction would be welcome. > > The planned new functions for trunk version is: (1) overlap score b/w > two profiles (2) better dumping (top hot objects/function/counters) > and statistics. > Once this basic version is in, we can start to add the new functionality. Sounds good. I assume the autoFDO does not go via gcov tool but rather uses custom reader of profile data at GCC side? I wonder, are there any recent overview papers/slides/text of design of AutoFDO and other features to be merged? I remember the talk from GCC Summit and I did read some of pre-LTO LIPO sources & papers, but it would be nice to have somethin up to date. > > libgcov-util.o is built in gcc/ directory, rather in libgcc. > It's directly linked to gcov-tool. > So libgcov-util.o is built for HOST, not TARGET. > With makefile changes, we can built HOST version of libgcov-driver.o > and libgcov-merge.o. > I also need to make some static functions/variables public. I suppose that can go with IN_GCOV_TOOL ifdef. So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed by libgcc/libgcov* code. We also will get libgcov-util.c in libgcc directory that is actually borrowed by by gcc/gcov-tool.c code. We now have one runtime using STDIO for streaming and kernel has custom version that goes via /proc interface (last time I looked). We added some abstraction into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c code and now we have in-memory handling code in libgcov-util. I guess it would make most sentse to put all the gcov code into a new directory (libgcov) and make it stand-alone library that can be configured 1) for stdio based runtime as we do not 2) for runtime missing the interface and relyin on user providing it 3) for use within gcov file manipulation tools with reorg of GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces. In a longer term, I would like to make FDO profiling intstrumentation to happen at linktime. For that I need to make the instrumentation code (minimal spaning tree & friends) to work w/o cgraph that would ideally be done in a shared implementation > > Won't this get wrong answer when counters[0] is not the same? > > I would expected the merging code to compare the counters first. Similarly for delta counter. > > These *_op functions are for scaling only. So there is only one > profile involved (thus there is no comparison). > The merge handles are in libgcov-merge.c which have the code to handle > mismatched profile targets. I see, OK then. > > > > Adding correct rounding may actually make difference for Martin's startup time work. > > Do you mean to use something like in RDIV macro? Yes. Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-04-17 5:19 ` Jan Hubicka @ 2014-05-01 22:37 ` Rong Xu 2014-05-05 17:17 ` Rong Xu 2014-05-15 20:37 ` Jan Hubicka 0 siblings, 2 replies; 23+ messages in thread From: Rong Xu @ 2014-05-01 22:37 UTC (permalink / raw) To: Jan Hubicka; +Cc: GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen [-- Attachment #1: Type: text/plain, Size: 4119 bytes --] Hi, Honza, Please find the new patch in the attachment. This patch integrated your earlier suggestions. The noticeable changes are: (1) build specialized object for libgcov-driver.c and libgcov-merge.c and link into gcov-tool, rather including the source file. (2) split some gcov counter definition code to gcov-counter.def to avoid code duplication. (3) use a macro for gcov_read_counter(), so gcov-tool can use existing code in libgcov-merge.c with minimal change. This patch does not address the suggestion of creating a new directory for libgcov. I agree with you that's a much better and cleaner structure we should go for. We can do that in follow-up patches. I tested this patch with boostrap and profiledbootstrap. Other tests are on-going. Thanks, -Rong On Wed, Apr 16, 2014 at 8:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote: >> GCOT_TOOL needs to use this function to read the string in gcda file >> to memory to construct gcov_info objects. >> As you noticed, gcov runtime does not need this interface. But >> gcov-tool links with gcov runtime and it also uses the function. >> We could make it available in gcov_runtime, but that will slightly >> increase the memory footprint. > > Yep, it is not really pretty. I wrote bellow some plan how things may be > structured in more convenient way. Any work in that direction would be welcome. >> >> The planned new functions for trunk version is: (1) overlap score b/w >> two profiles (2) better dumping (top hot objects/function/counters) >> and statistics. >> Once this basic version is in, we can start to add the new functionality. > > Sounds good. I assume the autoFDO does not go via gcov tool but rather uses > custom reader of profile data at GCC side? > I wonder, are there any recent overview papers/slides/text of design of AutoFDO > and other features to be merged? > I remember the talk from GCC Summit and I did read some of pre-LTO LIPO > sources & papers, but it would be nice to have somethin up to date. >> >> libgcov-util.o is built in gcc/ directory, rather in libgcc. >> It's directly linked to gcov-tool. >> So libgcov-util.o is built for HOST, not TARGET. >> With makefile changes, we can built HOST version of libgcov-driver.o >> and libgcov-merge.o. >> I also need to make some static functions/variables public. > > I suppose that can go with IN_GCOV_TOOL ifdef. > > So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed > by libgcc/libgcov* code. We also will get libgcov-util.c in libgcc directory > that is actually borrowed by by gcc/gcov-tool.c code. > > We now have one runtime using STDIO for streaming and kernel has custom version > that goes via /proc interface (last time I looked). We added some abstraction > into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c > code and now we have in-memory handling code in libgcov-util. > > I guess it would make most sentse to put all the gcov code into a new directory > (libgcov) and make it stand-alone library that can be configured > 1) for stdio based runtime as we do not > 2) for runtime missing the interface and relyin on user providing it > 3) for use within gcov file manipulation tools with reorg of > GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces. > In a longer term, I would like to make FDO profiling intstrumentation to happen > at linktime. For that I need to make the instrumentation code (minimal spaning > tree & friends) to work w/o cgraph that would ideally be done in a shared > implementation >> > Won't this get wrong answer when counters[0] is not the same? >> > I would expected the merging code to compare the counters first. Similarly for delta counter. >> >> These *_op functions are for scaling only. So there is only one >> profile involved (thus there is no comparison). >> The merge handles are in libgcov-merge.c which have the code to handle >> mismatched profile targets. > > I see, OK then. >> > >> > Adding correct rounding may actually make difference for Martin's startup time work. >> >> Do you mean to use something like in RDIV macro? > > Yes. > > Honza [-- Attachment #2: gcov_tool_patch_v3.txt --] [-- Type: text/plain, Size: 58670 bytes --] 2014-05-01 Rong Xu <xur@google.com> * gcc/gcov-io.c (gcov_position): Make avaialble to gcov-tool. (gcov_is_error): Ditto. (gcov_read_string): Ditto. (gcov_read_sync): Ditto. * gcc/gcov-io.h: Move counter defines to gcov-counter.def. * gcc/gcov-dump.c (tag_counters): Use gcov-counter.def. * gcc/coverage.c: Ditto. * gcc/gcov-tool.c: Offline gcda profile processing tool. (unlink_gcda_file): Remove one gcda file. (unlink_profile_dir): Remove gcda files from the profile path. (profile_merge): Merge two profiles in directory. (print_merge_usage_message): Print merge usage. (merge_usage): Print merge usage and exit. (do_merge): Driver for profile merge sub-command. (profile_rewrite): Rewrite profile. (print_rewrite_usage_message): Print rewrite usage. (rewrite_usage): Print rewrite usage and exit. (do_rewrite): Driver for profile rewrite sub-command. (print_usage): Print gcov-info usage and exit. (print_version): Print gcov-info version. (process_args): Process arguments. (main): Main routine for gcov-tool. * gcc/Makefile.in: Build and install gcov-tool. * gcc/gcov-counter.def: New file split from gcov-io.h. * libgcc/libgcov-driver.c (gcov_max_filename): Make available to gcov-tool. * libgcc/libgcov-merge.c (__gcov_merge_add): Replace gcov_read_counter() with a Macro. (__gcov_merge_ior): Ditto. (__gcov_merge_time_profile): Ditto. (__gcov_merge_single): Ditto. (__gcov_merge_delta): Ditto. * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag in the utility functions. (set_fn_ctrs): Utility function for reading gcda files to in-memory gcov_list object link lists. (tag_function): Ditto. (tag_blocks): Ditto. (tag_arcs): Ditto. (tag_lines): Ditto. (tag_counters): Ditto. (tag_summary): Ditto. (read_gcda_finalize): Ditto. (read_gcda_file): Ditto. (ftw_read_file): Ditto. (read_profile_dir_init): Ditto. (gcov_read_profile_dir): Ditto. (gcov_read_counter_mem): Ditto. (gcov_get_merge_weight): Ditto. (merge_wrapper): A wrapper function that calls merging handler. (gcov_merge): Merge two gcov_info objects with weights. (find_match_gcov_info): Find the matched gcov_info in the list. (gcov_profile_merge): Merge two gcov_info object lists. (__gcov_add_counter_op): Process edge profile counter values. (__gcov_ior_counter_op): Process IOR profile counter values. (__gcov_delta_counter_op): Process delta profile counter values. (__gcov_single_counter_op): Process single profile counter values. (fp_scale): Callback function for float-point scaling. (int_scale): Callback function for integer fraction scaling. (gcov_profile_scale): Scaling profile counters. (gcov_profile_normalize): Normalize profile counters. * libgcc/libgcov.h: Add headers and macros for gcov-tool use. (GCOV_GET_COUNTER): New. (GCOV_GET_COUNTER_TARGET): Ditto. (struct gcov_info): Make the functions field mutable in gcov-tool compilation. Index: gcc/gcov-io.c =================================================================== --- gcc/gcov-io.c (revision 209981) +++ gcc/gcov-io.c (working copy) @@ -64,7 +64,11 @@ GCOV_LINKAGE struct gcov_var } gcov_var; /* Save the current position in the gcov file. */ -static inline gcov_position_t +/* We need to expose this function when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static inline +#endif +gcov_position_t gcov_position (void) { gcc_assert (gcov_var.mode > 0); @@ -72,7 +76,11 @@ gcov_position (void) } /* Return nonzero if the error flag is set. */ -static inline int +/* We need to expose this function when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static inline +#endif +int gcov_is_error (void) { return gcov_var.file ? gcov_var.error : 1; @@ -560,11 +568,13 @@ gcov_read_counter (void) return value; } +/* We need to expose the below function when compiling for gcov-tool. */ + +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) /* Read string from coverage file. Returns a pointer to a static buffer, or NULL on empty string. You must copy the string before calling another gcov function. */ -#if !IN_LIBGCOV GCOV_LINKAGE const char * gcov_read_string (void) { @@ -641,7 +651,9 @@ gcov_read_summary (struct gcov_summary *summary) } } -#if !IN_LIBGCOV +/* We need to expose the below function when compiling for gcov-tool. */ + +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) /* Reset to a known position. BASE should have been obtained from gcov_position, LENGTH should be a record length. */ Index: gcc/gcov-io.h =================================================================== --- gcc/gcov-io.h (revision 209981) +++ gcc/gcov-io.h (working copy) @@ -240,50 +240,31 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne /* Counters that are collected. */ -#define GCOV_COUNTER_ARCS 0 /* Arc transitions. */ -#define GCOV_COUNTERS_SUMMABLE 1 /* Counters which can be - summaried. */ -#define GCOV_FIRST_VALUE_COUNTER 1 /* The first of counters used for value - profiling. They must form a consecutive - interval and their order must match - the order of HIST_TYPEs in - value-prof.h. */ -#define GCOV_COUNTER_V_INTERVAL 1 /* Histogram of value inside an interval. */ -#define GCOV_COUNTER_V_POW2 2 /* Histogram of exact power2 logarithm - of a value. */ -#define GCOV_COUNTER_V_SINGLE 3 /* The most common value of expression. */ -#define GCOV_COUNTER_V_DELTA 4 /* The most common difference between - consecutive values of expression. */ -#define GCOV_COUNTER_V_INDIR 5 /* The most common indirect address */ -#define GCOV_COUNTER_AVERAGE 6 /* Compute average value passed to the - counter. */ -#define GCOV_COUNTER_IOR 7 /* IOR of the all values passed to - counter. */ -#define GCOV_TIME_PROFILER 8 /* Time profile collecting first run of a function */ -#define GCOV_LAST_VALUE_COUNTER 8 /* The last of counters used for value - profiling. */ -#define GCOV_COUNTERS 9 +#ifdef DEF_GCOV_COUNTER +#undef DEF_GCOV_COUNTER +#endif +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) COUNTER, +enum { +#include "gcov-counter.def" +GCOV_COUNTERS +}; +/* Counters which can be summaried. */ +#define GCOV_COUNTERS_SUMMABLE (GCOV_COUNTER_ARCS + 1) + +/* The first of counters used for value profiling. They must form a + consecutive interval and their order must match the order of + HIST_TYPEs in value-prof.h. */ +#define GCOV_FIRST_VALUE_COUNTER GCOV_COUNTERS_SUMMABLE + +/* The last of counters used for value profiling. */ +#define GCOV_LAST_VALUE_COUNTER (GCOV_COUNTERS - 1) + /* Number of counters used for value profiling. */ #define GCOV_N_VALUE_COUNTERS \ (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1) - /* A list of human readable names of the counters */ -#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \ - "delta", "indirect_call", "average", "ior", "time_profiler"} - - /* Names of merge functions for counters. */ -#define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \ - "__gcov_merge_add", \ - "__gcov_merge_add", \ - "__gcov_merge_single", \ - "__gcov_merge_delta", \ - "__gcov_merge_single", \ - "__gcov_merge_add", \ - "__gcov_merge_ior", \ - "__gcov_merge_time_profile" } - /* Convert a counter index to a tag. */ #define GCOV_TAG_FOR_COUNTER(COUNT) \ (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17)) Index: gcc/gcov-dump.c =================================================================== --- gcc/gcov-dump.c (revision 209981) +++ gcc/gcov-dump.c (working copy) @@ -422,7 +422,11 @@ static void tag_counters (const char *filename ATTRIBUTE_UNUSED, unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) { - static const char *const counter_names[] = GCOV_COUNTER_NAMES; +#undef DEF_GCOV_COUNTER +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME, + static const char *const counter_names[] = { +#include "gcov-counter.def" +}; unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); printf (" %s %u counts", Index: gcc/coverage.c =================================================================== --- gcc/coverage.c (revision 209981) +++ gcc/coverage.c (working copy) @@ -120,9 +120,20 @@ static unsigned bbg_file_stamp; static char *da_file_name; /* The names of merge functions for counters. */ -static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS; -static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES; +#ifdef DEF_GCOV_COUNTER +#undef DEF_GCOV_COUNTER +#endif +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) #MERGE_FN, +static const char *const ctr_merge_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; +#undef DEF_GCOV_COUNTER +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME, +static const char *const ctr_names[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; + /* Forward declarations. */ static void read_counts_file (void); static tree build_var (tree, tree, int); Index: gcc/gcov-tool.c =================================================================== --- gcc/gcov-tool.c (revision 0) +++ gcc/gcov-tool.c (revision 0) @@ -0,0 +1,466 @@ +/* Gcc offline profile processing tool support. */ +/* Compile this one with gcc. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "gcov-io.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <ftw.h> +#include <getopt.h> + +extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int); +extern int gcov_profile_normalize (struct gcov_info*, gcov_type); +extern int gcov_profile_scale (struct gcov_info*, float, int, int); +extern struct gcov_info* gcov_read_profile_dir (const char*, int); +extern void gcov_exit (void); +extern void set_gcov_list (struct gcov_info *); +extern void gcov_set_verbose (void); + +static int verbose; + +/* Remove file NAME if it has a gcda suffix. */ + +static int +unlink_gcda_file (const char *name, + const struct stat *status ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED, + struct FTW *ftwbuf ATTRIBUTE_UNUSED) +{ + int ret = 0; + int len = strlen (name); + int len1 = strlen (GCOV_DATA_SUFFIX); + + if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1)) + remove (name); + + if (ret) + { + fnotice (stderr, "error in removing %s\n", name); + exit (FATAL_EXIT_CODE); + } + + return ret; +} + +/* Remove the gcda files in PATH recursively. */ + +static int +unlink_profile_dir (const char *path) +{ + return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS); +} + +/* Merging profile D1 and D2 with weight as W1 and W2, respectively. + The result profile is written to directory OUT. + Return 0 on success. */ + +static int +profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + struct gcov_info * d2_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + if (d2) + { + d2_profile = gcov_read_profile_dir (d2, 0); + if (!d2_profile) + return 1; + + /* The actual merge: we overwrite to d1_profile. */ + ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2); + + if (ret) + return ret; + } + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage message for profile merge. */ + +static void +print_merge_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n"); +} + +static const struct option merge_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "weight", required_argument, NULL, 'w' }, + { 0, 0, 0, 0 } +}; + +/* Print merge usage and exit. */ + +static void +merge_usage (void) +{ + fnotice (stderr, "Merge subcomand usage:"); + print_merge_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile merge sub-command. */ + +static int +do_merge (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + int w1 = 1, w2 = 1; + + optind = 0; + while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'w': + sscanf (optarg, "%d,%d", &w1, &w2); + if (w1 < 0 || w2 < 0) + { + fnotice (stderr, "weights need to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + break; + default: + merge_usage (); + } + } + + if (output_dir == NULL) + output_dir = "merged_profile"; + + if (argc - optind == 2) + ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2); + else + merge_usage (); + + return ret; +} + +/* If N_VAL is no-zero, normalize the profile by setting the largest counter + counter value to N_VAL and scale others counters proportionally. + Otherwise, multiply the all counters by SCALE. */ + +static int +profile_rewrite (const char *d1, const char *out, long long n_val, + float scale, int n, int d) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + if (n_val) + gcov_profile_normalize (d1_profile, (gcov_type) n_val); + else + gcov_profile_scale (d1_profile, scale, n, d); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage function for profile rewrite. */ + +static void +print_rewrite_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n"); + fnotice (file, " -n, --normalize <long long> Normalize the profile\n"); +} + +static const struct option rewrite_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "scale", required_argument, NULL, 's' }, + { "normalize", required_argument, NULL, 'n' }, + { 0, 0, 0, 0 } +}; + +/* Print profile rewrite usage and exit. */ + +static void +rewrite_usage (void) +{ + fnotice (stderr, "Rewrite subcommand usage:"); + print_rewrite_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile rewrite sub-command. */ + +static int +do_rewrite (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + long long normalize_val = 0; + float scale = 0.0; + int numerator = 1; + int denominator = 1; + int do_scaling = 0; + + optind = 0; + while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'n': + if (!do_scaling) + normalize_val = atoll (optarg); + else + fnotice (stderr, "scaling cannot co-exist with normalization," + " skipping\n"); + break; + case 's': + ret = 0; + do_scaling = 1; + if (strstr (optarg, "/")) + { + ret = sscanf (optarg, "%d/%d", &numerator, &denominator); + if (ret == 2) + { + gcc_assert (numerator >= 0); + gcc_assert (denominator > 0); + } + } + if (ret != 2) + { + ret = sscanf (optarg, "%f", &scale); + gcc_assert (ret == 1); + denominator = 0; + } + + if (scale < 0.0) + { + fnotice (stderr, "scale needs to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + if (normalize_val != 0) + { + fnotice (stderr, "normalization cannot co-exist with scaling\n"); + normalize_val = 0; + } + break; + default: + rewrite_usage (); + } + } + + if (output_dir == NULL) + output_dir = "rewrite_profile"; + + if (argc - optind == 1) + { + if (denominator > 0) + ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator); + else + ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0); + } + else + rewrite_usage (); + + return ret; +} + +/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, + otherwise the output of --help. */ + +static void +print_usage (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; + + fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname); + fnotice (file, "Offline tool to handle gcda counts\n\n"); + fnotice (file, " -h, --help Print this help, then exit\n"); + fnotice (file, " -v, --version Print version number, then exit\n"); + print_merge_usage_message (error_p); + print_rewrite_usage_message (error_p); + fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", + bug_report_url); + exit (status); +} + +/* Print version information and exit. */ + +static void +print_version (void) +{ + fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); + fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n", + _("(C)")); + fnotice (stdout, + _("This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or \n" + "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); + exit (SUCCESS_EXIT_CODE); +} + +static const struct option options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } +}; + +/* Process args, return index to first non-arg. */ + +static int +process_args (int argc, char **argv) +{ + int opt; + + while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1) + { + switch (opt) + { + case 'h': + print_usage (false); + /* Print_usage will exit. */ + case 'v': + print_version (); + /* Print_version will exit. */ + default: + print_usage (true); + /* Print_usage will exit. */ + } + } + + return optind; +} + +/* Main function for gcov-tool. */ + +int +main (int argc, char **argv) +{ + const char *p; + const char *sub_command; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) + --p; + progname = p; + + xmalloc_set_program_name (progname); + + /* Unlock the stdio streams. */ + unlock_std_streams (); + + gcc_init_libintl (); + + diagnostic_initialize (global_dc, 0); + + /* Handle response files. */ + expandargv (&argc, &argv); + + process_args (argc, argv); + if (optind >= argc) + print_usage (true); + + sub_command = argv[optind]; + + if (!strcmp (sub_command, "merge")) + return do_merge (argc - optind, argv + optind); + else if (!strcmp (sub_command, "rewrite")) + return do_rewrite (argc - optind, argv + optind); + + print_usage (true); +} Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 209981) +++ gcc/Makefile.in (working copy) @@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build # Selection of languages to be made. CONFIG_LANGUAGES = @all_selected_languages@ -LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES) +LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) gcov-tool$(exeext) \ + $(CONFIG_LANGUAGES) # Default values for variables overridden in Makefile fragments. # CFLAGS is for the user to override to, e.g., do a cross build with -O2. @@ -196,6 +197,9 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $( # flex output may yield harmless "no previous prototype" warnings build/gengtype-lex.o-warn = -Wno-error gengtype-lex.o-warn = -Wno-error +libgcov-util.o-warn = -Wno-error +libgcov-driver-tool.o-warn = -Wno-error +libgcov-merge-tool.o-warn = -Wno-error # All warnings have to be shut off in stage1 if the compiler used then # isn't gcc; configure determines that. WARN_CFLAGS will be either @@ -769,6 +773,7 @@ GCC_INSTALL_NAME := $(shell echo gcc|sed '$(progra GCC_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gcc|sed '$(program_transform_name)') CPP_INSTALL_NAME := $(shell echo cpp|sed '$(program_transform_name)') GCOV_INSTALL_NAME := $(shell echo gcov|sed '$(program_transform_name)') +GCOV_TOOL_INSTALL_NAME := $(shell echo gcov-tool|sed '$(program_transform_name)') # Setup the testing framework, if you have one EXPECT = `if [ -f $${rootme}/../expect/expect ] ; then \ @@ -890,7 +895,7 @@ BASIC_BLOCK_H = basic-block.h $(PREDICT_H) $(VEC_H GIMPLE_H = gimple.h gimple.def gsstruct.def pointer-set.h $(VEC_H) \ $(GGC_H) $(BASIC_BLOCK_H) $(TREE_H) tree-ssa-operands.h \ tree-ssa-alias.h $(INTERNAL_FN_H) $(HASH_TABLE_H) is-a.h -GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h +GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h gcov-counter.def RECOG_H = recog.h EMIT_RTL_H = emit-rtl.h FLAGS_H = flags.h flag-types.h $(OPTIONS_H) @@ -1486,7 +1491,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \ $(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \ $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \ - lto-wrapper.o + $(GCOV_TOOL_OBJS) lto-wrapper.o # This lists all host object files, whether they are included in this # compilation or not. @@ -1511,6 +1516,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn $(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \ gcc-ranlib$(exeext) \ gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \ + gcov-tool$(exeect) \ gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \ libcommon-target.a libcommon.a libgcc.mk @@ -2569,6 +2575,22 @@ GCOV_DUMP_OBJS = gcov-dump.o gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \ $(LIBS) -o $@ + +GCOV_TOOL_DEP_FILES = $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \ + $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \ + $(srcdir)/../libgcc/libgcov-merge.c \ + $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H) +libgcov-util.o: $(srcdir)/../libgcc/libgcov-util.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $< +libgcov-driver-tool.o: $(srcdir)/../libgcc/libgcov-driver.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + -DIN_GCOV_TOOL=1 -o $@ $< +libgcov-merge-tool.o: $(srcdir)/../libgcc/libgcov-merge.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + -DIN_GCOV_TOOL=1 -o $@ $< +GCOV_TOOL_OBJS = gcov-tool.o libgcov-util.o libgcov-driver-tool.o libgcov-merge-tool.o +gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS) + +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_TOOL_OBJS) $(LIBS) -o $@ #\f # Build the include directories. The stamp files are stmp-* rather than # s-* so that mostlyclean does not force the include directory to @@ -3205,6 +3227,13 @@ install-common: native lang.install-common install rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ fi +# Install gcov-tool if it was compiled. + -if [ -f gcov-tool$(exeext) ]; \ + then \ + rm -f $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + $(INSTALL_PROGRAM) \ + gcov-tool$(exeext) $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + fi # Install the driver program as $(target_noncanonical)-gcc, # $(target_noncanonical)-gcc-$(version), and also as gcc if native. Index: gcc/gcov-counter.def =================================================================== --- gcc/gcov-counter.def (revision 0) +++ gcc/gcov-counter.def (revision 0) @@ -0,0 +1,52 @@ +/* Definitions for the gcov counters in the GNU compiler. + Copyright (C) 2001-2014 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Before including this file, define a macro: + + DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) + + This macro will be expanded to all supported gcov counters, their + names, or the merge functions. */ + +/* Arc transitions. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_ARCS=0, "arcs", __gcov_merge_add) + +/* Histogram of value inside an interval. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_INTERVAL, "interval", __gcov_merge_add) + +/* Histogram of exact power2 logarithm of a value. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_POW2, "pow2", __gcov_merge_add) + +/* The most common value of expression. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_SINGLE, "single", __gcov_merge_single) + +/* The most common difference between consecutive values of expression. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_DELTA, "delta", __gcov_merge_delta) + +/* The most common indirect address. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_INDIR, "indirect_call", __gcov_merge_single) + +/* Compute average value passed to the counter. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_AVERAGE, "average", __gcov_merge_add) + +/* IOR of the all values passed to counter. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_IOR, "ior", __gcov_merge_ior) + +/* Time profile collecting first run of a function */ +DEF_GCOV_COUNTER(GCOV_TIME_PROFILER, "time_profiler", __gcov_merge_time_profile) Index: libgcc/libgcov-driver.c =================================================================== --- libgcc/libgcov-driver.c (revision 209981) +++ libgcc/libgcov-driver.c (working copy) @@ -77,7 +77,11 @@ set_gcov_list (struct gcov_info *head) } /* Size of the longest file name. */ -static size_t gcov_max_filename = 0; +/* We need to expose this static variable when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static +#endif +size_t gcov_max_filename = 0; /* Flag when the profile has already been dumped via __gcov_dump(). */ static int gcov_dump_complete; Index: libgcc/libgcov-merge.c =================================================================== --- libgcc/libgcov-merge.c (revision 209981) +++ libgcc/libgcov-merge.c (working copy) @@ -53,7 +53,7 @@ void __gcov_merge_add (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters += gcov_read_counter (); + *counters += GCOV_GET_COUNTER; } #endif /* L_gcov_merge_add */ @@ -65,7 +65,7 @@ void __gcov_merge_ior (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters |= gcov_read_counter (); + *counters |= GCOV_GET_COUNTER; } #endif @@ -81,7 +81,7 @@ __gcov_merge_time_profile (gcov_type *counters, un for (i = 0; i < n_counters; i++) { - value = gcov_read_counter (); + value = GCOV_GET_COUNTER; if (value && (!counters[i] || value < counters[i])) counters[i] = value; @@ -109,9 +109,9 @@ __gcov_merge_single (gcov_type *counters, unsigned n_measures = n_counters / 3; for (i = 0; i < n_measures; i++, counters += 3) { - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + value = GCOV_GET_COUNTER_TARGET; + counter = GCOV_GET_COUNTER; + all = GCOV_GET_COUNTER; if (counters[0] == value) counters[1] += counter; @@ -148,10 +148,10 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_measures = n_counters / 4; for (i = 0; i < n_measures; i++, counters += 4) { - /* last = */ gcov_read_counter (); - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + /* last = */ GCOV_GET_COUNTER; + value = GCOV_GET_COUNTER_TARGET; + counter = GCOV_GET_COUNTER; + all = GCOV_GET_COUNTER; if (counters[1] == value) counters[2] += counter; Index: libgcc/libgcov-util.c =================================================================== --- libgcc/libgcov-util.c (revision 0) +++ libgcc/libgcov-util.c (revision 0) @@ -0,0 +1,862 @@ +/* Utility functions for reading gcda files into in-memory + gcov_info structures and offline profile processing. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + + +#define IN_GCOV_TOOL 1 + +#include "libgcov.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "demangle.h" + +/* Borrowed from basic-block.h. */ +#define RDIV(X,Y) (((X) + (Y) / 2) / (Y)) + +/*extern gcov_type gcov_read_counter_mem(); +extern unsigned gcov_get_merge_weight(); */ + +/* TBD: xur */ +extern gcov_position_t gcov_position(); +extern int gcov_is_error(); +extern gcov_unsigned_t gcov_max_filename; + +/* We need the dumping and merge part of code in libgcov. */ +/*#include "libgcov-driver.c" +#include "libgcov-merge.c" */ + +/* Verbose mode for debug. */ +static int verbose; + +/* Set verbose flag. */ +void gcov_set_verbose (void) +{ + verbose = 1; +} + +/* The following part is to read Gcda and reconstruct GCOV_INFO. */ + +#include "obstack.h" +#include <unistd.h> +#include <ftw.h> + +static void tag_function (unsigned, unsigned); +static void tag_blocks (unsigned, unsigned); +static void tag_arcs (unsigned, unsigned); +static void tag_lines (unsigned, unsigned); +static void tag_counters (unsigned, unsigned); +static void tag_summary (unsigned, unsigned); + +/* The gcov_info for the first module. */ +static struct gcov_info *curr_gcov_info; +/* The gcov_info being processed. */ +static struct gcov_info *gcov_info_head; +/* This variable contains all the functions in current module. */ +static struct obstack fn_info; +/* The function being processed. */ +static struct gcov_fn_info *curr_fn_info; +/* The number of functions seen so far. */ +static unsigned num_fn_info; +/* This variable contains all the counters for current module. */ +static int k_ctrs_mask[GCOV_COUNTERS]; +/* The kind of counters that have been seen. */ +static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS]; +/* Number of kind of counters that have been seen. */ +static int k_ctrs_types; +/* The longest length of all the filenames. */ +static int max_filename_len; + +/* Merge functions for counters. */ +#ifdef DEF_GCOV_COUNTER +#undef DEF_GCOV_COUNTER +#endif +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) MERGE_FN, +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; + +/* Set the ctrs field in gcov_fn_info object FN_INFO. */ + +static void +set_fn_ctrs (struct gcov_fn_info *fn_info) +{ + int j = 0, i; + + for (i = 0; i < GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i] == 0) + continue; + fn_info->ctrs[j].num = k_ctrs[i].num; + fn_info->ctrs[j].values = k_ctrs[i].values; + j++; + } + if (k_ctrs_types == 0) + k_ctrs_types = j; + else + gcc_assert (j == k_ctrs_types); +} + +/* For each tag in gcda file, we have an entry here. + TAG is the tag value; NAME is the tag name; and + PROC is the handler function. */ + +typedef struct tag_format +{ + unsigned tag; + char const *name; + void (*proc) (unsigned, unsigned); +} tag_format_t; + +/* Handler table for various Tags. */ + +static const tag_format_t tag_table[] = +{ + {0, "NOP", NULL}, + {0, "UNKNOWN", NULL}, + {0, "COUNTERS", tag_counters}, + {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, + {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, + {GCOV_TAG_ARCS, "ARCS", tag_arcs}, + {GCOV_TAG_LINES, "LINES", tag_lines}, + {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, + {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, + {0, NULL, NULL} +}; + +/* Handler for reading function tag. */ + +static void +tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + int i; + + /* write out previous fn_info. */ + if (num_fn_info) + { + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + } + + /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active + counter types. */ + curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info) + + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1); + + for (i = 0; i < GCOV_COUNTERS; i++) + k_ctrs[i].num = 0; + k_ctrs_types = 0; + + curr_fn_info->key = curr_gcov_info; + curr_fn_info->ident = gcov_read_unsigned (); + curr_fn_info->lineno_checksum = gcov_read_unsigned (); + curr_fn_info->cfg_checksum = gcov_read_unsigned (); + num_fn_info++; + + if (verbose) + fprintf (stdout, "tag one function id=%d\n", curr_fn_info->ident); +} + +/* Handler for reading block tag. */ + +static void +tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading flow arc tag. */ + +static void +tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading line tag. */ + +static void +tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading counters array tag with value as TAG and length of LENGTH. */ + +static void +tag_counters (unsigned tag, unsigned length) +{ + unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); + gcov_type *values; + unsigned ix; + unsigned tag_ix; + + tag_ix = GCOV_COUNTER_FOR_TAG (tag); + gcc_assert (tag_ix < GCOV_COUNTERS); + k_ctrs_mask [tag_ix] = 1; + gcc_assert (k_ctrs[tag_ix].num == 0); + k_ctrs[tag_ix].num = n_counts; + + k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type)); + gcc_assert (values); + + for (ix = 0; ix != n_counts; ix++) + values[ix] = gcov_read_counter (); +} + +/* Handler for reading summary tag. */ + +static void +tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + struct gcov_summary summary; + + gcov_read_summary (&summary); +} + +/* This function is called at the end of reading a gcda file. + It flushes the contents in curr_fn_info to gcov_info object OBJ_INFO. */ + +static void +read_gcda_finalize (struct gcov_info *obj_info) +{ + int i; + + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + + /* We set the following fields: merge, n_functions, and functions. */ + obj_info->n_functions = num_fn_info; + obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info); + + /* wrap all the counter array. */ + for (i=0; i< GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i]) + obj_info->merge[i] = ctr_merge_functions[i]; + } +} + +/* Read the content of a gcda file FILENAME, and return a gcov_info data structure. + Program level summary CURRENT_SUMMARY will also be updated. */ + +static struct gcov_info * +read_gcda_file (const char *filename) +{ + unsigned tags[4]; + unsigned depth = 0; + unsigned magic, version; + struct gcov_info *obj_info; + int i; + + for (i=0; i< GCOV_COUNTERS; i++) + k_ctrs_mask[i] = 0; + k_ctrs_types = 0; + + if (!gcov_open (filename)) + { + fprintf (stderr, "%s:cannot open\n", filename); + return NULL; + } + + /* Read magic. */ + magic = gcov_read_unsigned (); + if (magic != GCOV_DATA_MAGIC) + { + fprintf (stderr, "%s:not a gcov data file\n", filename); + gcov_close (); + return NULL; + } + + /* Read version. */ + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + fprintf (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION); + gcov_close (); + return NULL; + } + + /* Instantiate a gcov_info object. */ + curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) + + sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1); + + obj_info->version = version; + obstack_init (&fn_info); + num_fn_info = 0; + curr_fn_info = 0; + { + char *str_dup = (char*) xmalloc (strlen (filename) + 1); + int len; + + strcpy (str_dup, filename); + obj_info->filename = str_dup; + if ((len = strlen (filename)) > max_filename_len) + max_filename_len = len; + } + + /* Read stamp. */ + obj_info->stamp = gcov_read_unsigned (); + + while (1) + { + gcov_position_t base; + unsigned tag, length; + tag_format_t const *format; + unsigned tag_depth; + int error; + unsigned mask; + + tag = gcov_read_unsigned (); + if (!tag) + break; + length = gcov_read_unsigned (); + base = gcov_position (); + mask = GCOV_TAG_MASK (tag) >> 1; + for (tag_depth = 4; mask; mask >>= 8) + { + if (((mask & 0xff) != 0xff)) + { + fprintf (stderr, "warning: %s:tag `%08x' is invalid\n", filename, tag); + break; + } + tag_depth--; + } + for (format = tag_table; format->name; format++) + if (format->tag == tag) + goto found; + format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; + found:; + if (tag) + { + if (depth && depth < tag_depth) + { + if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) + fprintf (stderr, "warning: %s:tag `%08x' is incorrectly nested\n", + filename, tag); + } + depth = tag_depth; + tags[depth - 1] = tag; + } + + if (format->proc) + { + unsigned long actual_length; + + (*format->proc) (tag, length); + + actual_length = gcov_position () - base; + if (actual_length > length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes overread\n", + filename, actual_length - length); + else if (length > actual_length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes unread\n", + filename, length - actual_length); + } + + gcov_sync (base, length); + if ((error = gcov_is_error ())) + { + fprintf (stderr,error < 0 ? "warning:%s:counter overflow at %lu\n" : + "Warning:%s:read error at %lu\n", filename, + (long unsigned) gcov_position ()); + break; + } + } + + read_gcda_finalize (obj_info); + gcov_close (); + + return obj_info; +} + +/* This will be called by ftw(). It opens and read a gcda file FILENAME. + Return a non-zero value to stop the tree walk. */ + +static int +ftw_read_file (const char *filename, + const struct stat *status ATTRIBUTE_UNUSED, + int type) +{ + int filename_len; + int suffix_len; + struct gcov_info *obj_info; + + /* Only read regular files. */ + if (type != FTW_F) + return 0; + + filename_len = strlen (filename); + suffix_len = strlen (GCOV_DATA_SUFFIX); + + if (filename_len <= suffix_len) + return 0; + + if (strcmp(filename + filename_len - suffix_len, GCOV_DATA_SUFFIX)) + return 0; + + if (verbose) + fprintf (stderr, "reading file: %s\n", filename); + + obj_info = read_gcda_file (filename); + + obj_info->next = gcov_info_head; + gcov_info_head = obj_info; + + return 0; +} + +/* Initializer for reading a profile dir. */ + +static inline void +read_profile_dir_init (void) +{ + gcov_info_head = 0; +} + +/* Driver for read a profile directory and convert into gcov_info list in memory. + Return NULL on error, + Return the head of gcov_info list on success. + Note the file static variable GCOV_MAX_FILENAME is also set. */ + +struct gcov_info * +gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED) +{ + char *pwd; + int ret; + + read_profile_dir_init (); + + if (access (dir_name, R_OK) != 0) + { + fprintf (stderr, "cannot access directory %s\n", dir_name); + return NULL; + } + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (dir_name); + if (ret !=0) + { + fprintf (stderr, "%s is not a directory\n", dir_name); + return NULL; + } + ftw (".", ftw_read_file, 50); + ret = chdir (pwd); + free (pwd); + + + /* gcov_max_filename is defined in libgcov.c that records the + max filename len. We need to set it here to allocate the + array for dumping. */ + gcov_max_filename = max_filename_len; + + return gcov_info_head;; +} + +/* This part of the code is to merge profile counters. */ + +static gcov_type *gcov_value_buf; +static gcov_unsigned_t gcov_value_buf_size; +static gcov_unsigned_t gcov_value_buf_pos; +static unsigned gcov_merge_weight; + +/* Read a counter value from gcov_value_buf array. */ + +gcov_type +gcov_read_counter_mem (void) +{ + gcov_type ret; + gcc_assert (gcov_value_buf_pos < gcov_value_buf_size); + ret = *(gcov_value_buf + gcov_value_buf_pos); + ++gcov_value_buf_pos; + return ret; +} + +/* Return the recorded merge weight. */ + +unsigned +gcov_get_merge_weight (void) +{ + return gcov_merge_weight; +} + +/* A wrapper function for merge functions. It sets up the + value buffer and weights and then calls the merge function. */ + +static void +merge_wrapper (gcov_merge_fn f, gcov_type *v1, gcov_unsigned_t n, + gcov_type *v2, unsigned w) +{ + gcov_value_buf = v2; + gcov_value_buf_pos = 0; + gcov_value_buf_size = n; + gcov_merge_weight = w; + (*f) (v1, n); +} + +/* Offline tool to manipulate profile data. + This tool targets on matched profiles. But it has some tolerance on + unmatched profiles. + When merging p1 to p2 (p2 is the dst), + * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; + emit warning + * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiply by + specified weight; emit warning. + * m.gcda in both p1 and p2: + ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge. + ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep + p2->m.gcda->f and + drop p1->m.gcda->f. A warning is emitted. */ + +/* Add INFO2's counter to INFO1, multiplying by weight W. */ + +static int +gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w) +{ + unsigned f_ix; + unsigned n_functions = info1->n_functions; + int has_mismatch = 0; + + gcc_assert (info2->n_functions == n_functions); + for (f_ix = 0; f_ix < n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix]; + const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr1, *ci_ptr2; + + if (!gfi_ptr1 || gfi_ptr1->key != info1) + continue; + if (!gfi_ptr2 || gfi_ptr2->key != info2) + continue; + + if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum) + { + fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n", + info1->filename); + has_mismatch = 1; + continue; + } + ci_ptr1 = gfi_ptr1->ctrs; + ci_ptr2 = gfi_ptr2->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge1 = info1->merge[t_ix]; + gcov_merge_fn merge2 = info2->merge[t_ix]; + + gcc_assert (merge1 == merge2); + if (!merge1) + continue; + gcc_assert (ci_ptr1->num == ci_ptr2->num); + merge_wrapper (merge1, ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w); + ci_ptr1++; + ci_ptr2++; + } + } + + return has_mismatch; +} + +/* Find and return the match gcov_info object for INFO from ARRAY. + SIZE is the length of ARRAY. + Return NULL if there is no match. */ + +static struct gcov_info * +find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info) +{ + struct gcov_info *gi_ptr; + struct gcov_info *ret = NULL; + int i; + + for (i = 0; i < size; i++) + { + gi_ptr = array[i]; + if (gi_ptr == 0) + continue; + if (!strcmp (gi_ptr->filename, info->filename)) + { + ret = gi_ptr; + array[i] = 0; + break; + } + } + + if (ret && ret->n_functions != info->n_functions) + { + fprintf (stderr, "mismatched profiles in %s (%d functions" + " vs %d functions)\n", + ret->filename, + ret->n_functions, + info->n_functions); + ret = NULL; + } + return ret; +} + +/* Merge the list of gcov_info objects from SRC_PROFILE to TGT_PROFILE. + Return 0 on success: without mismatch. + Reutrn 1 on error. */ + +int +gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile, + int w1, int w2) +{ + struct gcov_info *gi_ptr; + struct gcov_info **tgt_infos; + struct gcov_info *tgt_tail; + struct gcov_info **in_src_not_tgt; + unsigned tgt_cnt = 0, src_cnt = 0; + unsigned unmatch_info_cnt = 0; + unsigned int i; + + for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next) + tgt_cnt++; + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + src_cnt++; + tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * tgt_cnt); + gcc_assert (tgt_infos); + in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * src_cnt); + gcc_assert (in_src_not_tgt); + + for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++) + tgt_infos[i] = gi_ptr; + + tgt_tail = tgt_infos[tgt_cnt - 1]; + + /* First pass on tgt_profile, we multiply w1 to all counters. */ + if (w1 > 1) + { + for (i = 0; i < tgt_cnt; i++) + gcov_merge (tgt_infos[i], tgt_infos[i], w1-1); + } + + /* Second pass, add src_profile to the tgt_profile. */ + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + { + struct gcov_info *gi_ptr1; + + gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr); + if (gi_ptr1 == NULL) + { + in_src_not_tgt[unmatch_info_cnt++] = gi_ptr; + continue; + } + gcov_merge (gi_ptr1, gi_ptr, w2); + } + + /* For modules in src but not in tgt. We adjust the counter and append. */ + for (i = 0; i < unmatch_info_cnt; i++) + { + gi_ptr = in_src_not_tgt[i]; + gcov_merge (gi_ptr, gi_ptr, w2 - 1); + tgt_tail->next = gi_ptr; + tgt_tail = gi_ptr; + } + + return 0; +} + +typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*); + +/* Performing FN upon arc counters. */ + +static void +__gcov_add_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = fn(val, data1, data2); + } +} + +/* Performing FN upon ior counters. */ + +static void +__gcov_ior_counter_op (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + counter_op_fn fn ATTRIBUTE_UNUSED, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +/* Performaing FN upon delta counters. */ + +static void +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 4)); + n_measures = n_counters / 4; + for (i = 0; i < n_measures; i++, counters += 4) + { + counters[2] = fn (counters[2], data1, data2); + counters[3] = fn (counters[3], data1, data2); + } +} + +/* Performing FN upon single counters. */ + +static void +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 3)); + n_measures = n_counters / 3; + for (i = 0; i < n_measures; i++, counters += 3) + { + counters[1] = fn (counters[1], data1, data2); + counters[2] = fn (counters[2], data1, data2); + } +} + +/* Scaling the counter value V by multiplying *(float*) DATA1. */ + +static gcov_type +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED) +{ + float f = *(float *) data1; + return (gcov_type) (v * f); +} + +/* Scaling the counter value V by multiplying DATA2/DATA1. */ + +static gcov_type +int_scale (gcov_type v, void *data1, void *data2) +{ + int n = *(int *) data1; + int d = *(int *) data2; + return (gcov_type) ( RDIV (v,d) * n); +} + +/* Type of function used to process counters. */ +typedef void (*gcov_counter_fn) (gcov_type *, gcov_unsigned_t, + counter_op_fn, void *, void *); + +/* Function array to process profile counters. */ +static gcov_counter_fn ctr_functions[GCOV_COUNTERS] = { + __gcov_add_counter_op, + __gcov_add_counter_op, + __gcov_add_counter_op, + __gcov_single_counter_op, + __gcov_delta_counter_op, + __gcov_single_counter_op, + __gcov_add_counter_op, + __gcov_ior_counter_op, + __gcov_ior_counter_op, +}; + +/* Driver for scaling profile counters. */ + +int +gcov_profile_scale (struct gcov_info *profile, float scale_factor, int n, int d) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fprintf (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d); + + /* Scaling the counters. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge = gi_ptr->merge[t_ix]; + + if (!merge) + continue; + if (d == 0) + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + fp_scale, &scale_factor, NULL); + else + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + int_scale, &n, &d); + ci_ptr++; + } + } + + return 0; +} + +/* Driver to normalize profile counters. */ + +int +gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val) +{ + struct gcov_info *gi_ptr; + gcov_type curr_max_val = 0; + unsigned f_ix; + unsigned int i; + float scale_factor; + + /* Find the largest count value. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix < 1; t_ix++) + { + for (i = 0; i < ci_ptr->num; i++) + if (ci_ptr->values[i] > curr_max_val) + curr_max_val = ci_ptr->values[i]; + ci_ptr++; + } + } + + scale_factor = (float)max_val / curr_max_val; + if (verbose) + fprintf (stdout, "max_val is %lld\n", (long long) curr_max_val); + + return gcov_profile_scale (profile, scale_factor, 0, 0); +} Index: libgcc/libgcov.h =================================================================== --- libgcc/libgcov.h (revision 209981) +++ libgcc/libgcov.h (working copy) @@ -33,6 +33,10 @@ #define xcalloc calloc #endif +#ifndef IN_GCOV_TOOL +/* About the target. */ +/* This path will be used by libgcov runtime. */ + #include "tconfig.h" #include "tsystem.h" #include "coretypes.h" @@ -79,6 +83,60 @@ typedef unsigned gcov_type_unsigned __attribute__ #define GCOV_LOCKED 0 #endif +/* "Counts" stored in gcda files can be a real counter value, or + an target address. When differentiate these two types because + when manipulating counts, we should only change real counter values, + rather target addresses. + Macro for Reading count values in libgcov runtime: + we read from gcda files. */ +#define GCOV_GET_COUNTER (gcov_read_counter ()) +#define GCOV_GET_COUNTER_TARGET (gcov_read_counter ()) + +#else /* IN_GCOV_TOOL */ +/* About the host. */ +/* This path will be compiled for the host and linked into + gcov-tool binary. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" + +typedef unsigned gcov_unsigned_t; +typedef unsigned gcov_position_t; +/* gcov_type is typedef'd elsewhere for the compiler */ +#if defined (HOST_HAS_F_SETLKW) +#define GCOV_LOCKED 1 +#else +#define GCOV_LOCKED 0 +#endif + +/* Some Macros specific to gcov-tool. */ + +#define L_gcov 1 +#define L_gcov_merge_add 1 +#define L_gcov_merge_single 1 +#define L_gcov_merge_delta 1 +#define L_gcov_merge_ior 1 +#define L_gcov_merge_time_profile 1 + +/* Rerfer the comment to same macro above. This version is for gcov-tool. + We read the value from memory and multiply it by the merge weight. + For target value, we do NOT multiply the weight. */ + +#define GCOV_GET_COUNTER (gcov_read_counter_mem () * gcov_get_merge_weight ()) +#define GCOV_GET_COUNTER_TARGET (gcov_read_counter_mem ()) + +/* Make certian internal functions/variables in libgcov available for + gcov-tool access. */ +#define GCOV_TOOL_LINKAGE + +extern gcov_type gcov_read_counter_mem(); +extern unsigned gcov_get_merge_weight(); + + +#endif /* !IN_GCOV_TOOL */ + #if defined(inhibit_libc) #define IN_LIBGCOV (-1) #else @@ -159,8 +217,13 @@ struct gcov_info unused) */ unsigned n_functions; /* number of functions */ + +#ifndef IN_GCOV_TOOL const struct gcov_fn_info *const *functions; /* pointer to pointers - to function information */ + to function information */ +#else + const struct gcov_fn_info **functions; +#endif /* !IN_GCOV_TOOL */ }; /* Register a new object file module. */ ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-05-01 22:37 ` Rong Xu @ 2014-05-05 17:17 ` Rong Xu 2014-07-11 8:07 ` Richard Biener 2014-05-15 20:37 ` Jan Hubicka 1 sibling, 1 reply; 23+ messages in thread From: Rong Xu @ 2014-05-05 17:17 UTC (permalink / raw) To: Jan Hubicka; +Cc: GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen [-- Attachment #1: Type: text/plain, Size: 4526 bytes --] Here is the updated patch. The difference with patch set 3 is (1) use the gcov-counter.def for scaling operation. (2) fix wrong scaling function for time-profiler. passed with bootstrap, profiledboostrap and SPEC2006. Thanks, -Rong On Thu, May 1, 2014 at 3:37 PM, Rong Xu <xur@google.com> wrote: > Hi, Honza, > > Please find the new patch in the attachment. This patch integrated > your earlier suggestions. The noticeable changes are: > (1) build specialized object for libgcov-driver.c and libgcov-merge.c > and link into gcov-tool, rather including the source file. > (2) split some gcov counter definition code to gcov-counter.def to > avoid code duplication. > (3) use a macro for gcov_read_counter(), so gcov-tool can use existing > code in libgcov-merge.c with minimal change. > > This patch does not address the suggestion of creating a new directory > for libgcov. I agree with you that's a much better > and cleaner structure we should go for. We can do that in follow-up patches. > > I tested this patch with boostrap and profiledbootstrap. Other tests > are on-going. > > Thanks, > > -Rong > > On Wed, Apr 16, 2014 at 8:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote: >>> GCOT_TOOL needs to use this function to read the string in gcda file >>> to memory to construct gcov_info objects. >>> As you noticed, gcov runtime does not need this interface. But >>> gcov-tool links with gcov runtime and it also uses the function. >>> We could make it available in gcov_runtime, but that will slightly >>> increase the memory footprint. >> >> Yep, it is not really pretty. I wrote bellow some plan how things may be >> structured in more convenient way. Any work in that direction would be welcome. >>> >>> The planned new functions for trunk version is: (1) overlap score b/w >>> two profiles (2) better dumping (top hot objects/function/counters) >>> and statistics. >>> Once this basic version is in, we can start to add the new functionality. >> >> Sounds good. I assume the autoFDO does not go via gcov tool but rather uses >> custom reader of profile data at GCC side? >> I wonder, are there any recent overview papers/slides/text of design of AutoFDO >> and other features to be merged? >> I remember the talk from GCC Summit and I did read some of pre-LTO LIPO >> sources & papers, but it would be nice to have somethin up to date. >>> >>> libgcov-util.o is built in gcc/ directory, rather in libgcc. >>> It's directly linked to gcov-tool. >>> So libgcov-util.o is built for HOST, not TARGET. >>> With makefile changes, we can built HOST version of libgcov-driver.o >>> and libgcov-merge.o. >>> I also need to make some static functions/variables public. >> >> I suppose that can go with IN_GCOV_TOOL ifdef. >> >> So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed >> by libgcc/libgcov* code. We also will get libgcov-util.c in libgcc directory >> that is actually borrowed by by gcc/gcov-tool.c code. >> >> We now have one runtime using STDIO for streaming and kernel has custom version >> that goes via /proc interface (last time I looked). We added some abstraction >> into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c >> code and now we have in-memory handling code in libgcov-util. >> >> I guess it would make most sentse to put all the gcov code into a new directory >> (libgcov) and make it stand-alone library that can be configured >> 1) for stdio based runtime as we do not >> 2) for runtime missing the interface and relyin on user providing it >> 3) for use within gcov file manipulation tools with reorg of >> GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces. >> In a longer term, I would like to make FDO profiling intstrumentation to happen >> at linktime. For that I need to make the instrumentation code (minimal spaning >> tree & friends) to work w/o cgraph that would ideally be done in a shared >> implementation >>> > Won't this get wrong answer when counters[0] is not the same? >>> > I would expected the merging code to compare the counters first. Similarly for delta counter. >>> >>> These *_op functions are for scaling only. So there is only one >>> profile involved (thus there is no comparison). >>> The merge handles are in libgcov-merge.c which have the code to handle >>> mismatched profile targets. >> >> I see, OK then. >>> > >>> > Adding correct rounding may actually make difference for Martin's startup time work. >>> >>> Do you mean to use something like in RDIV macro? >> >> Yes. >> >> Honza [-- Attachment #2: gcov_tool_patch_v4.txt --] [-- Type: text/plain, Size: 61405 bytes --] 2014-05-05 Rong Xu <xur@google.com> * gcc/gcov-io.c (gcov_position): Make avaialble to gcov-tool. (gcov_is_error): Ditto. (gcov_read_string): Ditto. (gcov_read_sync): Ditto. * gcc/gcov-io.h: Move counter defines to gcov-counter.def. * gcc/gcov-dump.c (tag_counters): Use gcov-counter.def. * gcc/coverage.c: Ditto. * gcc/gcov-tool.c: Offline gcda profile processing tool. (unlink_gcda_file): Remove one gcda file. (unlink_profile_dir): Remove gcda files from the profile path. (profile_merge): Merge two profiles in directory. (print_merge_usage_message): Print merge usage. (merge_usage): Print merge usage and exit. (do_merge): Driver for profile merge sub-command. (profile_rewrite): Rewrite profile. (print_rewrite_usage_message): Print rewrite usage. (rewrite_usage): Print rewrite usage and exit. (do_rewrite): Driver for profile rewrite sub-command. (print_usage): Print gcov-info usage and exit. (print_version): Print gcov-info version. (process_args): Process arguments. (main): Main routine for gcov-tool. * gcc/Makefile.in: Build and install gcov-tool. * gcc/gcov-counter.def: New file split from gcov-io.h. * libgcc/libgcov-driver.c (gcov_max_filename): Make available to gcov-tool. * libgcc/libgcov-merge.c (__gcov_merge_add): Replace gcov_read_counter() with a Macro. (__gcov_merge_ior): Ditto. (__gcov_merge_time_profile): Ditto. (__gcov_merge_single): Ditto. (__gcov_merge_delta): Ditto. * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag in the utility functions. (set_fn_ctrs): Utility function for reading gcda files to in-memory gcov_list object link lists. (tag_function): Ditto. (tag_blocks): Ditto. (tag_arcs): Ditto. (tag_lines): Ditto. (tag_counters): Ditto. (tag_summary): Ditto. (read_gcda_finalize): Ditto. (read_gcda_file): Ditto. (ftw_read_file): Ditto. (read_profile_dir_init): Ditto. (gcov_read_profile_dir): Ditto. (gcov_read_counter_mem): Ditto. (gcov_get_merge_weight): Ditto. (merge_wrapper): A wrapper function that calls merging handler. (gcov_merge): Merge two gcov_info objects with weights. (find_match_gcov_info): Find the matched gcov_info in the list. (gcov_profile_merge): Merge two gcov_info object lists. (__gcov_add_counter_op): Process edge profile counter values. (__gcov_ior_counter_op): Process IOR profile counter values. (__gcov_delta_counter_op): Process delta profile counter values. (__gcov_single_counter_op): Process single profile counter values. (fp_scale): Callback function for float-point scaling. (int_scale): Callback function for integer fraction scaling. (gcov_profile_scale): Scaling profile counters. (gcov_profile_normalize): Normalize profile counters. * libgcc/libgcov.h: Add headers and macros for gcov-tool use. (GCOV_GET_COUNTER): New. (GCOV_GET_COUNTER_TARGET): Ditto. (struct gcov_info): Make the functions field mutable in gcov-tool compilation. Index: gcc/gcov-io.c =================================================================== --- gcc/gcov-io.c (revision 209981) +++ gcc/gcov-io.c (working copy) @@ -64,7 +64,11 @@ GCOV_LINKAGE struct gcov_var } gcov_var; /* Save the current position in the gcov file. */ -static inline gcov_position_t +/* We need to expose this function when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static inline +#endif +gcov_position_t gcov_position (void) { gcc_assert (gcov_var.mode > 0); @@ -72,7 +76,11 @@ gcov_position (void) } /* Return nonzero if the error flag is set. */ -static inline int +/* We need to expose this function when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static inline +#endif +int gcov_is_error (void) { return gcov_var.file ? gcov_var.error : 1; @@ -560,11 +568,13 @@ gcov_read_counter (void) return value; } +/* We need to expose the below function when compiling for gcov-tool. */ + +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) /* Read string from coverage file. Returns a pointer to a static buffer, or NULL on empty string. You must copy the string before calling another gcov function. */ -#if !IN_LIBGCOV GCOV_LINKAGE const char * gcov_read_string (void) { @@ -641,7 +651,9 @@ gcov_read_summary (struct gcov_summary *summary) } } -#if !IN_LIBGCOV +/* We need to expose the below function when compiling for gcov-tool. */ + +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) /* Reset to a known position. BASE should have been obtained from gcov_position, LENGTH should be a record length. */ Index: gcc/gcov-io.h =================================================================== --- gcc/gcov-io.h (revision 209981) +++ gcc/gcov-io.h (working copy) @@ -240,50 +240,31 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne /* Counters that are collected. */ -#define GCOV_COUNTER_ARCS 0 /* Arc transitions. */ -#define GCOV_COUNTERS_SUMMABLE 1 /* Counters which can be - summaried. */ -#define GCOV_FIRST_VALUE_COUNTER 1 /* The first of counters used for value - profiling. They must form a consecutive - interval and their order must match - the order of HIST_TYPEs in - value-prof.h. */ -#define GCOV_COUNTER_V_INTERVAL 1 /* Histogram of value inside an interval. */ -#define GCOV_COUNTER_V_POW2 2 /* Histogram of exact power2 logarithm - of a value. */ -#define GCOV_COUNTER_V_SINGLE 3 /* The most common value of expression. */ -#define GCOV_COUNTER_V_DELTA 4 /* The most common difference between - consecutive values of expression. */ -#define GCOV_COUNTER_V_INDIR 5 /* The most common indirect address */ -#define GCOV_COUNTER_AVERAGE 6 /* Compute average value passed to the - counter. */ -#define GCOV_COUNTER_IOR 7 /* IOR of the all values passed to - counter. */ -#define GCOV_TIME_PROFILER 8 /* Time profile collecting first run of a function */ -#define GCOV_LAST_VALUE_COUNTER 8 /* The last of counters used for value - profiling. */ -#define GCOV_COUNTERS 9 +#ifdef DEF_GCOV_COUNTER +#undef DEF_GCOV_COUNTER +#endif +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) COUNTER, +enum { +#include "gcov-counter.def" +GCOV_COUNTERS +}; +/* Counters which can be summaried. */ +#define GCOV_COUNTERS_SUMMABLE (GCOV_COUNTER_ARCS + 1) + +/* The first of counters used for value profiling. They must form a + consecutive interval and their order must match the order of + HIST_TYPEs in value-prof.h. */ +#define GCOV_FIRST_VALUE_COUNTER GCOV_COUNTERS_SUMMABLE + +/* The last of counters used for value profiling. */ +#define GCOV_LAST_VALUE_COUNTER (GCOV_COUNTERS - 1) + /* Number of counters used for value profiling. */ #define GCOV_N_VALUE_COUNTERS \ (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1) - /* A list of human readable names of the counters */ -#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \ - "delta", "indirect_call", "average", "ior", "time_profiler"} - - /* Names of merge functions for counters. */ -#define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \ - "__gcov_merge_add", \ - "__gcov_merge_add", \ - "__gcov_merge_single", \ - "__gcov_merge_delta", \ - "__gcov_merge_single", \ - "__gcov_merge_add", \ - "__gcov_merge_ior", \ - "__gcov_merge_time_profile" } - /* Convert a counter index to a tag. */ #define GCOV_TAG_FOR_COUNTER(COUNT) \ (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17)) Index: gcc/gcov-dump.c =================================================================== --- gcc/gcov-dump.c (revision 209981) +++ gcc/gcov-dump.c (working copy) @@ -149,14 +149,14 @@ print_version (void) } static void -print_prefix (const char *filename, unsigned depth, gcov_position_t position) +print_prefix (const char *filename ATTRIBUTE_UNUSED, unsigned depth ATTRIBUTE_UNUSED, gcov_position_t position) { static const char prefix[] = " "; - printf ("%s:", filename); + printf ("%s:", "xur"); if (flag_dump_positions) printf ("%lu:", (unsigned long) position); - printf ("%.*s", (int) depth, prefix); + printf ("%.*s", (int) 0, prefix); } static void @@ -193,7 +193,7 @@ dump_gcov_file (const char *filename) GCOV_UNSIGNED2STRING (v, version); GCOV_UNSIGNED2STRING (m, magic); - printf ("%s:%s:magic `%.4s':version `%.4s'%s\n", filename, type, + printf ("%s:magic `%.4s':version `%.4s'%s\n", type, m, v, endianness < 0 ? " (swapped endianness)" : ""); if (version != GCOV_VERSION) { @@ -208,7 +208,7 @@ dump_gcov_file (const char *filename) { unsigned stamp = gcov_read_unsigned (); - printf ("%s:stamp %lu\n", filename, (unsigned long)stamp); + printf ("stamp %lu\n", (unsigned long)stamp); } while (1) @@ -253,7 +253,7 @@ dump_gcov_file (const char *filename) } print_prefix (filename, tag_depth, position); - printf ("%08x:%4u:%s", tag, length, format->name); + printf ("%08x:%4u:%s", tag, /*length*/0, format->name); if (format->proc) (*format->proc) (filename, tag, length); @@ -422,7 +422,11 @@ static void tag_counters (const char *filename ATTRIBUTE_UNUSED, unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) { - static const char *const counter_names[] = GCOV_COUNTER_NAMES; +#undef DEF_GCOV_COUNTER +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME, + static const char *const counter_names[] = { +#include "gcov-counter.def" +}; unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); printf (" %s %u counts", @@ -454,12 +458,13 @@ tag_summary (const char *filename ATTRIBUTE_UNUSED unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) { struct gcov_summary summary; - unsigned ix, h_ix; - gcov_bucket_type *histo_bucket; + /*unsigned ix, h_ix; + gcov_bucket_type *histo_bucket; */ gcov_read_summary (&summary); - printf (" checksum=0x%08x", summary.checksum); +/* printf (" checksum=0x%08x", summary.checksum); */ +#if 0 for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++) { printf ("\n"); @@ -495,9 +500,12 @@ tag_summary (const char *filename ATTRIBUTE_UNUSED if (flag_dump_working_sets) dump_working_sets (filename, &summary.ctrs[ix]); } +#endif } -static void +#if 1 + +ATTRIBUTE_UNUSED void dump_working_sets (const char *filename ATTRIBUTE_UNUSED, const struct gcov_ctr_summary *summary) { @@ -528,3 +536,4 @@ dump_working_sets (const char *filename ATTRIBUTE_ (HOST_WIDEST_INT)ws_info->min_counter); } } +#endif Index: gcc/coverage.c =================================================================== --- gcc/coverage.c (revision 209981) +++ gcc/coverage.c (working copy) @@ -120,9 +120,22 @@ static unsigned bbg_file_stamp; static char *da_file_name; /* The names of merge functions for counters. */ -static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS; -static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES; +#ifdef DEF_GCOV_COUNTER +#undef DEF_GCOV_COUNTER +#endif +#define STR(str) #str +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) STR(__gcov_merge ## FN_TYPE), +static const char *const ctr_merge_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; +#undef STR +#undef DEF_GCOV_COUNTER +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) NAME, +static const char *const ctr_names[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; + /* Forward declarations. */ static void read_counts_file (void); static tree build_var (tree, tree, int); Index: gcc/gcov-tool.c =================================================================== --- gcc/gcov-tool.c (revision 0) +++ gcc/gcov-tool.c (revision 0) @@ -0,0 +1,466 @@ +/* Gcc offline profile processing tool support. */ +/* Compile this one with gcc. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "gcov-io.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <ftw.h> +#include <getopt.h> + +extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int); +extern int gcov_profile_normalize (struct gcov_info*, gcov_type); +extern int gcov_profile_scale (struct gcov_info*, float, int, int); +extern struct gcov_info* gcov_read_profile_dir (const char*, int); +extern void gcov_exit (void); +extern void set_gcov_list (struct gcov_info *); +extern void gcov_set_verbose (void); + +static int verbose; + +/* Remove file NAME if it has a gcda suffix. */ + +static int +unlink_gcda_file (const char *name, + const struct stat *status ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED, + struct FTW *ftwbuf ATTRIBUTE_UNUSED) +{ + int ret = 0; + int len = strlen (name); + int len1 = strlen (GCOV_DATA_SUFFIX); + + if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1)) + remove (name); + + if (ret) + { + fnotice (stderr, "error in removing %s\n", name); + exit (FATAL_EXIT_CODE); + } + + return ret; +} + +/* Remove the gcda files in PATH recursively. */ + +static int +unlink_profile_dir (const char *path) +{ + return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS); +} + +/* Merging profile D1 and D2 with weight as W1 and W2, respectively. + The result profile is written to directory OUT. + Return 0 on success. */ + +static int +profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + struct gcov_info * d2_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + if (d2) + { + d2_profile = gcov_read_profile_dir (d2, 0); + if (!d2_profile) + return 1; + + /* The actual merge: we overwrite to d1_profile. */ + ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2); + + if (ret) + return ret; + } + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage message for profile merge. */ + +static void +print_merge_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n"); +} + +static const struct option merge_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "weight", required_argument, NULL, 'w' }, + { 0, 0, 0, 0 } +}; + +/* Print merge usage and exit. */ + +static void +merge_usage (void) +{ + fnotice (stderr, "Merge subcomand usage:"); + print_merge_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile merge sub-command. */ + +static int +do_merge (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + int w1 = 1, w2 = 1; + + optind = 0; + while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'w': + sscanf (optarg, "%d,%d", &w1, &w2); + if (w1 < 0 || w2 < 0) + { + fnotice (stderr, "weights need to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + break; + default: + merge_usage (); + } + } + + if (output_dir == NULL) + output_dir = "merged_profile"; + + if (argc - optind == 2) + ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2); + else + merge_usage (); + + return ret; +} + +/* If N_VAL is no-zero, normalize the profile by setting the largest counter + counter value to N_VAL and scale others counters proportionally. + Otherwise, multiply the all counters by SCALE. */ + +static int +profile_rewrite (const char *d1, const char *out, long long n_val, + float scale, int n, int d) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + if (n_val) + gcov_profile_normalize (d1_profile, (gcov_type) n_val); + else + gcov_profile_scale (d1_profile, scale, n, d); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage function for profile rewrite. */ + +static void +print_rewrite_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n"); + fnotice (file, " -n, --normalize <long long> Normalize the profile\n"); +} + +static const struct option rewrite_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "scale", required_argument, NULL, 's' }, + { "normalize", required_argument, NULL, 'n' }, + { 0, 0, 0, 0 } +}; + +/* Print profile rewrite usage and exit. */ + +static void +rewrite_usage (void) +{ + fnotice (stderr, "Rewrite subcommand usage:"); + print_rewrite_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile rewrite sub-command. */ + +static int +do_rewrite (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + long long normalize_val = 0; + float scale = 0.0; + int numerator = 1; + int denominator = 1; + int do_scaling = 0; + + optind = 0; + while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'n': + if (!do_scaling) + normalize_val = atoll (optarg); + else + fnotice (stderr, "scaling cannot co-exist with normalization," + " skipping\n"); + break; + case 's': + ret = 0; + do_scaling = 1; + if (strstr (optarg, "/")) + { + ret = sscanf (optarg, "%d/%d", &numerator, &denominator); + if (ret == 2) + { + gcc_assert (numerator >= 0); + gcc_assert (denominator > 0); + } + } + if (ret != 2) + { + ret = sscanf (optarg, "%f", &scale); + gcc_assert (ret == 1); + denominator = 0; + } + + if (scale < 0.0) + { + fnotice (stderr, "scale needs to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + if (normalize_val != 0) + { + fnotice (stderr, "normalization cannot co-exist with scaling\n"); + normalize_val = 0; + } + break; + default: + rewrite_usage (); + } + } + + if (output_dir == NULL) + output_dir = "rewrite_profile"; + + if (argc - optind == 1) + { + if (denominator > 0) + ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator); + else + ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0); + } + else + rewrite_usage (); + + return ret; +} + +/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, + otherwise the output of --help. */ + +static void +print_usage (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; + + fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname); + fnotice (file, "Offline tool to handle gcda counts\n\n"); + fnotice (file, " -h, --help Print this help, then exit\n"); + fnotice (file, " -v, --version Print version number, then exit\n"); + print_merge_usage_message (error_p); + print_rewrite_usage_message (error_p); + fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", + bug_report_url); + exit (status); +} + +/* Print version information and exit. */ + +static void +print_version (void) +{ + fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); + fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n", + _("(C)")); + fnotice (stdout, + _("This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or \n" + "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); + exit (SUCCESS_EXIT_CODE); +} + +static const struct option options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } +}; + +/* Process args, return index to first non-arg. */ + +static int +process_args (int argc, char **argv) +{ + int opt; + + while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1) + { + switch (opt) + { + case 'h': + print_usage (false); + /* Print_usage will exit. */ + case 'v': + print_version (); + /* Print_version will exit. */ + default: + print_usage (true); + /* Print_usage will exit. */ + } + } + + return optind; +} + +/* Main function for gcov-tool. */ + +int +main (int argc, char **argv) +{ + const char *p; + const char *sub_command; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) + --p; + progname = p; + + xmalloc_set_program_name (progname); + + /* Unlock the stdio streams. */ + unlock_std_streams (); + + gcc_init_libintl (); + + diagnostic_initialize (global_dc, 0); + + /* Handle response files. */ + expandargv (&argc, &argv); + + process_args (argc, argv); + if (optind >= argc) + print_usage (true); + + sub_command = argv[optind]; + + if (!strcmp (sub_command, "merge")) + return do_merge (argc - optind, argv + optind); + else if (!strcmp (sub_command, "rewrite")) + return do_rewrite (argc - optind, argv + optind); + + print_usage (true); +} Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 209981) +++ gcc/Makefile.in (working copy) @@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build # Selection of languages to be made. CONFIG_LANGUAGES = @all_selected_languages@ -LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES) +LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) gcov-tool$(exeext) \ + $(CONFIG_LANGUAGES) # Default values for variables overridden in Makefile fragments. # CFLAGS is for the user to override to, e.g., do a cross build with -O2. @@ -196,6 +197,9 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $( # flex output may yield harmless "no previous prototype" warnings build/gengtype-lex.o-warn = -Wno-error gengtype-lex.o-warn = -Wno-error +libgcov-util.o-warn = -Wno-error +libgcov-driver-tool.o-warn = -Wno-error +libgcov-merge-tool.o-warn = -Wno-error # All warnings have to be shut off in stage1 if the compiler used then # isn't gcc; configure determines that. WARN_CFLAGS will be either @@ -769,6 +773,7 @@ GCC_INSTALL_NAME := $(shell echo gcc|sed '$(progra GCC_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gcc|sed '$(program_transform_name)') CPP_INSTALL_NAME := $(shell echo cpp|sed '$(program_transform_name)') GCOV_INSTALL_NAME := $(shell echo gcov|sed '$(program_transform_name)') +GCOV_TOOL_INSTALL_NAME := $(shell echo gcov-tool|sed '$(program_transform_name)') # Setup the testing framework, if you have one EXPECT = `if [ -f $${rootme}/../expect/expect ] ; then \ @@ -890,7 +895,7 @@ BASIC_BLOCK_H = basic-block.h $(PREDICT_H) $(VEC_H GIMPLE_H = gimple.h gimple.def gsstruct.def pointer-set.h $(VEC_H) \ $(GGC_H) $(BASIC_BLOCK_H) $(TREE_H) tree-ssa-operands.h \ tree-ssa-alias.h $(INTERNAL_FN_H) $(HASH_TABLE_H) is-a.h -GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h +GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h gcov-counter.def RECOG_H = recog.h EMIT_RTL_H = emit-rtl.h FLAGS_H = flags.h flag-types.h $(OPTIONS_H) @@ -1486,7 +1491,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \ $(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \ $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \ - lto-wrapper.o + $(GCOV_TOOL_OBJS) lto-wrapper.o # This lists all host object files, whether they are included in this # compilation or not. @@ -1511,6 +1516,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn $(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \ gcc-ranlib$(exeext) \ gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \ + gcov-tool$(exeect) \ gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \ libcommon-target.a libcommon.a libgcc.mk @@ -2569,6 +2575,22 @@ GCOV_DUMP_OBJS = gcov-dump.o gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \ $(LIBS) -o $@ + +GCOV_TOOL_DEP_FILES = $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \ + $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \ + $(srcdir)/../libgcc/libgcov-merge.c \ + $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H) +libgcov-util.o: $(srcdir)/../libgcc/libgcov-util.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $< +libgcov-driver-tool.o: $(srcdir)/../libgcc/libgcov-driver.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + -DIN_GCOV_TOOL=1 -o $@ $< +libgcov-merge-tool.o: $(srcdir)/../libgcc/libgcov-merge.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + -DIN_GCOV_TOOL=1 -o $@ $< +GCOV_TOOL_OBJS = gcov-tool.o libgcov-util.o libgcov-driver-tool.o libgcov-merge-tool.o +gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS) + +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_TOOL_OBJS) $(LIBS) -o $@ #\f # Build the include directories. The stamp files are stmp-* rather than # s-* so that mostlyclean does not force the include directory to @@ -3205,6 +3227,13 @@ install-common: native lang.install-common install rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ fi +# Install gcov-tool if it was compiled. + -if [ -f gcov-tool$(exeext) ]; \ + then \ + rm -f $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + $(INSTALL_PROGRAM) \ + gcov-tool$(exeext) $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + fi # Install the driver program as $(target_noncanonical)-gcc, # $(target_noncanonical)-gcc-$(version), and also as gcc if native. Index: gcc/gcov-counter.def =================================================================== --- gcc/gcov-counter.def (revision 0) +++ gcc/gcov-counter.def (revision 0) @@ -0,0 +1,54 @@ +/* Definitions for the gcov counters in the GNU compiler. + Copyright (C) 2001-2014 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Before including this file, define a macro: + + DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) + + This macro will be expanded to all supported gcov counters, their + names, or the type of handler functions. FN_TYPE will be + expanded to a handler function, like in gcov_merge, it is + expanded to __gcov_merge ## FN_TYPE. */ + +/* Arc transitions. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_ARCS=0, "arcs", _add) + +/* Histogram of value inside an interval. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_INTERVAL, "interval", _add) + +/* Histogram of exact power2 logarithm of a value. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_POW2, "pow2", _add) + +/* The most common value of expression. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_SINGLE, "single", _single) + +/* The most common difference between consecutive values of expression. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_DELTA, "delta", _delta) + +/* The most common indirect address. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_INDIR, "indirect_call", _single) + +/* Compute average value passed to the counter. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_AVERAGE, "average", _add) + +/* IOR of the all values passed to counter. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_IOR, "ior", _ior) + +/* Time profile collecting first run of a function */ +DEF_GCOV_COUNTER(GCOV_TIME_PROFILER, "time_profiler", _time_profile) Index: libgcc/libgcov-driver.c =================================================================== --- libgcc/libgcov-driver.c (revision 209981) +++ libgcc/libgcov-driver.c (working copy) @@ -77,7 +77,11 @@ set_gcov_list (struct gcov_info *head) } /* Size of the longest file name. */ -static size_t gcov_max_filename = 0; +/* We need to expose this static variable when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static +#endif +size_t gcov_max_filename = 0; /* Flag when the profile has already been dumped via __gcov_dump(). */ static int gcov_dump_complete; Index: libgcc/libgcov-merge.c =================================================================== --- libgcc/libgcov-merge.c (revision 209981) +++ libgcc/libgcov-merge.c (working copy) @@ -53,7 +53,7 @@ void __gcov_merge_add (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters += gcov_read_counter (); + *counters += GCOV_GET_COUNTER; } #endif /* L_gcov_merge_add */ @@ -65,7 +65,7 @@ void __gcov_merge_ior (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters |= gcov_read_counter (); + *counters |= GCOV_GET_COUNTER; } #endif @@ -81,7 +81,7 @@ __gcov_merge_time_profile (gcov_type *counters, un for (i = 0; i < n_counters; i++) { - value = gcov_read_counter (); + value = GCOV_GET_COUNTER; if (value && (!counters[i] || value < counters[i])) counters[i] = value; @@ -109,9 +109,9 @@ __gcov_merge_single (gcov_type *counters, unsigned n_measures = n_counters / 3; for (i = 0; i < n_measures; i++, counters += 3) { - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + value = GCOV_GET_COUNTER_TARGET; + counter = GCOV_GET_COUNTER; + all = GCOV_GET_COUNTER; if (counters[0] == value) counters[1] += counter; @@ -148,10 +148,10 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_measures = n_counters / 4; for (i = 0; i < n_measures; i++, counters += 4) { - /* last = */ gcov_read_counter (); - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + /* last = */ GCOV_GET_COUNTER; + value = GCOV_GET_COUNTER_TARGET; + counter = GCOV_GET_COUNTER; + all = GCOV_GET_COUNTER; if (counters[1] == value) counters[2] += counter; Index: libgcc/libgcov-util.c =================================================================== --- libgcc/libgcov-util.c (revision 0) +++ libgcc/libgcov-util.c (revision 0) @@ -0,0 +1,866 @@ +/* Utility functions for reading gcda files into in-memory + gcov_info structures and offline profile processing. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + + +#define IN_GCOV_TOOL 1 + +#include "libgcov.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "demangle.h" + +/* Borrowed from basic-block.h. */ +#define RDIV(X,Y) (((X) + (Y) / 2) / (Y)) + +/*extern gcov_type gcov_read_counter_mem(); +extern unsigned gcov_get_merge_weight(); */ + +/* TBD: xur */ +extern gcov_position_t gcov_position(); +extern int gcov_is_error(); +extern gcov_unsigned_t gcov_max_filename; + +/* We need the dumping and merge part of code in libgcov. */ +/*#include "libgcov-driver.c" +#include "libgcov-merge.c" */ + +/* Verbose mode for debug. */ +static int verbose; + +/* Set verbose flag. */ +void gcov_set_verbose (void) +{ + verbose = 1; +} + +/* The following part is to read Gcda and reconstruct GCOV_INFO. */ + +#include "obstack.h" +#include <unistd.h> +#include <ftw.h> + +static void tag_function (unsigned, unsigned); +static void tag_blocks (unsigned, unsigned); +static void tag_arcs (unsigned, unsigned); +static void tag_lines (unsigned, unsigned); +static void tag_counters (unsigned, unsigned); +static void tag_summary (unsigned, unsigned); + +/* The gcov_info for the first module. */ +static struct gcov_info *curr_gcov_info; +/* The gcov_info being processed. */ +static struct gcov_info *gcov_info_head; +/* This variable contains all the functions in current module. */ +static struct obstack fn_info; +/* The function being processed. */ +static struct gcov_fn_info *curr_fn_info; +/* The number of functions seen so far. */ +static unsigned num_fn_info; +/* This variable contains all the counters for current module. */ +static int k_ctrs_mask[GCOV_COUNTERS]; +/* The kind of counters that have been seen. */ +static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS]; +/* Number of kind of counters that have been seen. */ +static int k_ctrs_types; +/* The longest length of all the filenames. */ +static int max_filename_len; + +/* Merge functions for counters. */ +#ifdef DEF_GCOV_COUNTER +#undef DEF_GCOV_COUNTER +#endif +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) __gcov_merge ## FN_TYPE, +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; + +/* Set the ctrs field in gcov_fn_info object FN_INFO. */ + +static void +set_fn_ctrs (struct gcov_fn_info *fn_info) +{ + int j = 0, i; + + for (i = 0; i < GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i] == 0) + continue; + fn_info->ctrs[j].num = k_ctrs[i].num; + fn_info->ctrs[j].values = k_ctrs[i].values; + j++; + } + if (k_ctrs_types == 0) + k_ctrs_types = j; + else + gcc_assert (j == k_ctrs_types); +} + +/* For each tag in gcda file, we have an entry here. + TAG is the tag value; NAME is the tag name; and + PROC is the handler function. */ + +typedef struct tag_format +{ + unsigned tag; + char const *name; + void (*proc) (unsigned, unsigned); +} tag_format_t; + +/* Handler table for various Tags. */ + +static const tag_format_t tag_table[] = +{ + {0, "NOP", NULL}, + {0, "UNKNOWN", NULL}, + {0, "COUNTERS", tag_counters}, + {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, + {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, + {GCOV_TAG_ARCS, "ARCS", tag_arcs}, + {GCOV_TAG_LINES, "LINES", tag_lines}, + {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, + {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, + {0, NULL, NULL} +}; + +/* Handler for reading function tag. */ + +static void +tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + int i; + + /* write out previous fn_info. */ + if (num_fn_info) + { + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + } + + /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active + counter types. */ + curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info) + + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1); + + for (i = 0; i < GCOV_COUNTERS; i++) + k_ctrs[i].num = 0; + k_ctrs_types = 0; + + curr_fn_info->key = curr_gcov_info; + curr_fn_info->ident = gcov_read_unsigned (); + curr_fn_info->lineno_checksum = gcov_read_unsigned (); + curr_fn_info->cfg_checksum = gcov_read_unsigned (); + num_fn_info++; + + if (verbose) + fprintf (stdout, "tag one function id=%d\n", curr_fn_info->ident); +} + +/* Handler for reading block tag. */ + +static void +tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading flow arc tag. */ + +static void +tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading line tag. */ + +static void +tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading counters array tag with value as TAG and length of LENGTH. */ + +static void +tag_counters (unsigned tag, unsigned length) +{ + unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); + gcov_type *values; + unsigned ix; + unsigned tag_ix; + + tag_ix = GCOV_COUNTER_FOR_TAG (tag); + gcc_assert (tag_ix < GCOV_COUNTERS); + k_ctrs_mask [tag_ix] = 1; + gcc_assert (k_ctrs[tag_ix].num == 0); + k_ctrs[tag_ix].num = n_counts; + + k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type)); + gcc_assert (values); + + for (ix = 0; ix != n_counts; ix++) + values[ix] = gcov_read_counter (); +} + +/* Handler for reading summary tag. */ + +static void +tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + struct gcov_summary summary; + + gcov_read_summary (&summary); +} + +/* This function is called at the end of reading a gcda file. + It flushes the contents in curr_fn_info to gcov_info object OBJ_INFO. */ + +static void +read_gcda_finalize (struct gcov_info *obj_info) +{ + int i; + + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + + /* We set the following fields: merge, n_functions, and functions. */ + obj_info->n_functions = num_fn_info; + obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info); + + /* wrap all the counter array. */ + for (i=0; i< GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i]) + obj_info->merge[i] = ctr_merge_functions[i]; + } +} + +/* Read the content of a gcda file FILENAME, and return a gcov_info data structure. + Program level summary CURRENT_SUMMARY will also be updated. */ + +static struct gcov_info * +read_gcda_file (const char *filename) +{ + unsigned tags[4]; + unsigned depth = 0; + unsigned magic, version; + struct gcov_info *obj_info; + int i; + + for (i=0; i< GCOV_COUNTERS; i++) + k_ctrs_mask[i] = 0; + k_ctrs_types = 0; + + if (!gcov_open (filename)) + { + fprintf (stderr, "%s:cannot open\n", filename); + return NULL; + } + + /* Read magic. */ + magic = gcov_read_unsigned (); + if (magic != GCOV_DATA_MAGIC) + { + fprintf (stderr, "%s:not a gcov data file\n", filename); + gcov_close (); + return NULL; + } + + /* Read version. */ + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + fprintf (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION); + gcov_close (); + return NULL; + } + + /* Instantiate a gcov_info object. */ + curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) + + sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1); + + obj_info->version = version; + obstack_init (&fn_info); + num_fn_info = 0; + curr_fn_info = 0; + { + char *str_dup = (char*) xmalloc (strlen (filename) + 1); + int len; + + strcpy (str_dup, filename); + obj_info->filename = str_dup; + if ((len = strlen (filename)) > max_filename_len) + max_filename_len = len; + } + + /* Read stamp. */ + obj_info->stamp = gcov_read_unsigned (); + + while (1) + { + gcov_position_t base; + unsigned tag, length; + tag_format_t const *format; + unsigned tag_depth; + int error; + unsigned mask; + + tag = gcov_read_unsigned (); + if (!tag) + break; + length = gcov_read_unsigned (); + base = gcov_position (); + mask = GCOV_TAG_MASK (tag) >> 1; + for (tag_depth = 4; mask; mask >>= 8) + { + if (((mask & 0xff) != 0xff)) + { + fprintf (stderr, "warning: %s:tag `%08x' is invalid\n", filename, tag); + break; + } + tag_depth--; + } + for (format = tag_table; format->name; format++) + if (format->tag == tag) + goto found; + format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; + found:; + if (tag) + { + if (depth && depth < tag_depth) + { + if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) + fprintf (stderr, "warning: %s:tag `%08x' is incorrectly nested\n", + filename, tag); + } + depth = tag_depth; + tags[depth - 1] = tag; + } + + if (format->proc) + { + unsigned long actual_length; + + (*format->proc) (tag, length); + + actual_length = gcov_position () - base; + if (actual_length > length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes overread\n", + filename, actual_length - length); + else if (length > actual_length) + fprintf (stderr,"warning: %s:record size mismatch %lu bytes unread\n", + filename, length - actual_length); + } + + gcov_sync (base, length); + if ((error = gcov_is_error ())) + { + fprintf (stderr,error < 0 ? "warning:%s:counter overflow at %lu\n" : + "Warning:%s:read error at %lu\n", filename, + (long unsigned) gcov_position ()); + break; + } + } + + read_gcda_finalize (obj_info); + gcov_close (); + + return obj_info; +} + +/* This will be called by ftw(). It opens and read a gcda file FILENAME. + Return a non-zero value to stop the tree walk. */ + +static int +ftw_read_file (const char *filename, + const struct stat *status ATTRIBUTE_UNUSED, + int type) +{ + int filename_len; + int suffix_len; + struct gcov_info *obj_info; + + /* Only read regular files. */ + if (type != FTW_F) + return 0; + + filename_len = strlen (filename); + suffix_len = strlen (GCOV_DATA_SUFFIX); + + if (filename_len <= suffix_len) + return 0; + + if (strcmp(filename + filename_len - suffix_len, GCOV_DATA_SUFFIX)) + return 0; + + if (verbose) + fprintf (stderr, "reading file: %s\n", filename); + + obj_info = read_gcda_file (filename); + + obj_info->next = gcov_info_head; + gcov_info_head = obj_info; + + return 0; +} + +/* Initializer for reading a profile dir. */ + +static inline void +read_profile_dir_init (void) +{ + gcov_info_head = 0; +} + +/* Driver for read a profile directory and convert into gcov_info list in memory. + Return NULL on error, + Return the head of gcov_info list on success. + Note the file static variable GCOV_MAX_FILENAME is also set. */ + +struct gcov_info * +gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED) +{ + char *pwd; + int ret; + + read_profile_dir_init (); + + if (access (dir_name, R_OK) != 0) + { + fprintf (stderr, "cannot access directory %s\n", dir_name); + return NULL; + } + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (dir_name); + if (ret !=0) + { + fprintf (stderr, "%s is not a directory\n", dir_name); + return NULL; + } + ftw (".", ftw_read_file, 50); + ret = chdir (pwd); + free (pwd); + + + /* gcov_max_filename is defined in libgcov.c that records the + max filename len. We need to set it here to allocate the + array for dumping. */ + gcov_max_filename = max_filename_len; + + return gcov_info_head;; +} + +/* This part of the code is to merge profile counters. */ + +static gcov_type *gcov_value_buf; +static gcov_unsigned_t gcov_value_buf_size; +static gcov_unsigned_t gcov_value_buf_pos; +static unsigned gcov_merge_weight; + +/* Read a counter value from gcov_value_buf array. */ + +gcov_type +gcov_read_counter_mem (void) +{ + gcov_type ret; + gcc_assert (gcov_value_buf_pos < gcov_value_buf_size); + ret = *(gcov_value_buf + gcov_value_buf_pos); + ++gcov_value_buf_pos; + return ret; +} + +/* Return the recorded merge weight. */ + +unsigned +gcov_get_merge_weight (void) +{ + return gcov_merge_weight; +} + +/* A wrapper function for merge functions. It sets up the + value buffer and weights and then calls the merge function. */ + +static void +merge_wrapper (gcov_merge_fn f, gcov_type *v1, gcov_unsigned_t n, + gcov_type *v2, unsigned w) +{ + gcov_value_buf = v2; + gcov_value_buf_pos = 0; + gcov_value_buf_size = n; + gcov_merge_weight = w; + (*f) (v1, n); +} + +/* Offline tool to manipulate profile data. + This tool targets on matched profiles. But it has some tolerance on + unmatched profiles. + When merging p1 to p2 (p2 is the dst), + * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; + emit warning + * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiply by + specified weight; emit warning. + * m.gcda in both p1 and p2: + ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge. + ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep + p2->m.gcda->f and + drop p1->m.gcda->f. A warning is emitted. */ + +/* Add INFO2's counter to INFO1, multiplying by weight W. */ + +static int +gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w) +{ + unsigned f_ix; + unsigned n_functions = info1->n_functions; + int has_mismatch = 0; + + gcc_assert (info2->n_functions == n_functions); + for (f_ix = 0; f_ix < n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix]; + const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr1, *ci_ptr2; + + if (!gfi_ptr1 || gfi_ptr1->key != info1) + continue; + if (!gfi_ptr2 || gfi_ptr2->key != info2) + continue; + + if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum) + { + fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n", + info1->filename); + has_mismatch = 1; + continue; + } + ci_ptr1 = gfi_ptr1->ctrs; + ci_ptr2 = gfi_ptr2->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge1 = info1->merge[t_ix]; + gcov_merge_fn merge2 = info2->merge[t_ix]; + + gcc_assert (merge1 == merge2); + if (!merge1) + continue; + gcc_assert (ci_ptr1->num == ci_ptr2->num); + merge_wrapper (merge1, ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w); + ci_ptr1++; + ci_ptr2++; + } + } + + return has_mismatch; +} + +/* Find and return the match gcov_info object for INFO from ARRAY. + SIZE is the length of ARRAY. + Return NULL if there is no match. */ + +static struct gcov_info * +find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info) +{ + struct gcov_info *gi_ptr; + struct gcov_info *ret = NULL; + int i; + + for (i = 0; i < size; i++) + { + gi_ptr = array[i]; + if (gi_ptr == 0) + continue; + if (!strcmp (gi_ptr->filename, info->filename)) + { + ret = gi_ptr; + array[i] = 0; + break; + } + } + + if (ret && ret->n_functions != info->n_functions) + { + fprintf (stderr, "mismatched profiles in %s (%d functions" + " vs %d functions)\n", + ret->filename, + ret->n_functions, + info->n_functions); + ret = NULL; + } + return ret; +} + +/* Merge the list of gcov_info objects from SRC_PROFILE to TGT_PROFILE. + Return 0 on success: without mismatch. + Reutrn 1 on error. */ + +int +gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile, + int w1, int w2) +{ + struct gcov_info *gi_ptr; + struct gcov_info **tgt_infos; + struct gcov_info *tgt_tail; + struct gcov_info **in_src_not_tgt; + unsigned tgt_cnt = 0, src_cnt = 0; + unsigned unmatch_info_cnt = 0; + unsigned int i; + + for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next) + tgt_cnt++; + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + src_cnt++; + tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * tgt_cnt); + gcc_assert (tgt_infos); + in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * src_cnt); + gcc_assert (in_src_not_tgt); + + for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++) + tgt_infos[i] = gi_ptr; + + tgt_tail = tgt_infos[tgt_cnt - 1]; + + /* First pass on tgt_profile, we multiply w1 to all counters. */ + if (w1 > 1) + { + for (i = 0; i < tgt_cnt; i++) + gcov_merge (tgt_infos[i], tgt_infos[i], w1-1); + } + + /* Second pass, add src_profile to the tgt_profile. */ + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + { + struct gcov_info *gi_ptr1; + + gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr); + if (gi_ptr1 == NULL) + { + in_src_not_tgt[unmatch_info_cnt++] = gi_ptr; + continue; + } + gcov_merge (gi_ptr1, gi_ptr, w2); + } + + /* For modules in src but not in tgt. We adjust the counter and append. */ + for (i = 0; i < unmatch_info_cnt; i++) + { + gi_ptr = in_src_not_tgt[i]; + gcov_merge (gi_ptr, gi_ptr, w2 - 1); + tgt_tail->next = gi_ptr; + tgt_tail = gi_ptr; + } + + return 0; +} + +typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*); + +/* Performing FN upon arc counters. */ + +static void +__gcov_add_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = fn(val, data1, data2); + } +} + +/* Performing FN upon ior counters. */ + +static void +__gcov_ior_counter_op (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + counter_op_fn fn ATTRIBUTE_UNUSED, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +/* Performing FN upon time-profile counters. */ + +static void +__gcov_time_profile_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + __gcov_add_counter_op (counters, n_counters, fn, data1, data2); +} + +/* Performaing FN upon delta counters. */ + +static void +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 4)); + n_measures = n_counters / 4; + for (i = 0; i < n_measures; i++, counters += 4) + { + counters[2] = fn (counters[2], data1, data2); + counters[3] = fn (counters[3], data1, data2); + } +} + +/* Performing FN upon single counters. */ + +static void +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 3)); + n_measures = n_counters / 3; + for (i = 0; i < n_measures; i++, counters += 3) + { + counters[1] = fn (counters[1], data1, data2); + counters[2] = fn (counters[2], data1, data2); + } +} + +/* Scaling the counter value V by multiplying *(float*) DATA1. */ + +static gcov_type +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED) +{ + float f = *(float *) data1; + return (gcov_type) (v * f); +} + +/* Scaling the counter value V by multiplying DATA2/DATA1. */ + +static gcov_type +int_scale (gcov_type v, void *data1, void *data2) +{ + int n = *(int *) data1; + int d = *(int *) data2; + return (gcov_type) ( RDIV (v,d) * n); +} + +/* Type of function used to process counters. */ +typedef void (*gcov_counter_fn) (gcov_type *, gcov_unsigned_t, + counter_op_fn, void *, void *); + +/* Function array to process profile counters. */ +#undef DEF_GCOV_COUNTER +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) \ + __gcov ## FN_TYPE ## _counter_op, +static gcov_counter_fn ctr_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; + +/* Driver for scaling profile counters. */ + +int +gcov_profile_scale (struct gcov_info *profile, float scale_factor, int n, int d) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fprintf (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d); + + /* Scaling the counters. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge = gi_ptr->merge[t_ix]; + + if (!merge) + continue; + if (d == 0) + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + fp_scale, &scale_factor, NULL); + else + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + int_scale, &n, &d); + ci_ptr++; + } + } + + return 0; +} + +/* Driver to normalize profile counters. */ + +int +gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val) +{ + struct gcov_info *gi_ptr; + gcov_type curr_max_val = 0; + unsigned f_ix; + unsigned int i; + float scale_factor; + + /* Find the largest count value. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix < 1; t_ix++) + { + for (i = 0; i < ci_ptr->num; i++) + if (ci_ptr->values[i] > curr_max_val) + curr_max_val = ci_ptr->values[i]; + ci_ptr++; + } + } + + scale_factor = (float)max_val / curr_max_val; + if (verbose) + fprintf (stdout, "max_val is %lld\n", (long long) curr_max_val); + + return gcov_profile_scale (profile, scale_factor, 0, 0); +} Index: libgcc/libgcov.h =================================================================== --- libgcc/libgcov.h (revision 209981) +++ libgcc/libgcov.h (working copy) @@ -33,6 +33,10 @@ #define xcalloc calloc #endif +#ifndef IN_GCOV_TOOL +/* About the target. */ +/* This path will be used by libgcov runtime. */ + #include "tconfig.h" #include "tsystem.h" #include "coretypes.h" @@ -79,6 +83,60 @@ typedef unsigned gcov_type_unsigned __attribute__ #define GCOV_LOCKED 0 #endif +/* "Counts" stored in gcda files can be a real counter value, or + an target address. When differentiate these two types because + when manipulating counts, we should only change real counter values, + rather target addresses. + Macro for Reading count values in libgcov runtime: + we read from gcda files. */ +#define GCOV_GET_COUNTER (gcov_read_counter ()) +#define GCOV_GET_COUNTER_TARGET (gcov_read_counter ()) + +#else /* IN_GCOV_TOOL */ +/* About the host. */ +/* This path will be compiled for the host and linked into + gcov-tool binary. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" + +typedef unsigned gcov_unsigned_t; +typedef unsigned gcov_position_t; +/* gcov_type is typedef'd elsewhere for the compiler */ +#if defined (HOST_HAS_F_SETLKW) +#define GCOV_LOCKED 1 +#else +#define GCOV_LOCKED 0 +#endif + +/* Some Macros specific to gcov-tool. */ + +#define L_gcov 1 +#define L_gcov_merge_add 1 +#define L_gcov_merge_single 1 +#define L_gcov_merge_delta 1 +#define L_gcov_merge_ior 1 +#define L_gcov_merge_time_profile 1 + +/* Rerfer the comment to same macro above. This version is for gcov-tool. + We read the value from memory and multiply it by the merge weight. + For target value, we do NOT multiply the weight. */ + +#define GCOV_GET_COUNTER (gcov_read_counter_mem () * gcov_get_merge_weight ()) +#define GCOV_GET_COUNTER_TARGET (gcov_read_counter_mem ()) + +/* Make certian internal functions/variables in libgcov available for + gcov-tool access. */ +#define GCOV_TOOL_LINKAGE + +extern gcov_type gcov_read_counter_mem(); +extern unsigned gcov_get_merge_weight(); + + +#endif /* !IN_GCOV_TOOL */ + #if defined(inhibit_libc) #define IN_LIBGCOV (-1) #else @@ -159,8 +217,13 @@ struct gcov_info unused) */ unsigned n_functions; /* number of functions */ + +#ifndef IN_GCOV_TOOL const struct gcov_fn_info *const *functions; /* pointer to pointers - to function information */ + to function information */ +#else + const struct gcov_fn_info **functions; +#endif /* !IN_GCOV_TOOL */ }; /* Register a new object file module. */ ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-05-05 17:17 ` Rong Xu @ 2014-07-11 8:07 ` Richard Biener 2014-07-11 8:12 ` Christophe Lyon 2014-07-11 15:42 ` Xinliang David Li 0 siblings, 2 replies; 23+ messages in thread From: Richard Biener @ 2014-07-11 8:07 UTC (permalink / raw) To: Rong Xu Cc: Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen On Mon, May 5, 2014 at 7:17 PM, Rong Xu <xur@google.com> wrote: > Here is the updated patch. The difference with patch set 3 is > (1) use the gcov-counter.def for scaling operation. > (2) fix wrong scaling function for time-profiler. > > passed with bootstrap, profiledboostrap and SPEC2006. One of the patches breaks bootstrap for me: /space/rguenther/src/svn/trunk/gcc/../libgcc/libgcov.h:184: error: ISO C++ forbids zero-size array ‘ctrs’ make[3]: *** [libgcov-util.o] Error 1 host compiler is gcc 4.3.4 Richard. > Thanks, > > -Rong > > On Thu, May 1, 2014 at 3:37 PM, Rong Xu <xur@google.com> wrote: >> Hi, Honza, >> >> Please find the new patch in the attachment. This patch integrated >> your earlier suggestions. The noticeable changes are: >> (1) build specialized object for libgcov-driver.c and libgcov-merge.c >> and link into gcov-tool, rather including the source file. >> (2) split some gcov counter definition code to gcov-counter.def to >> avoid code duplication. >> (3) use a macro for gcov_read_counter(), so gcov-tool can use existing >> code in libgcov-merge.c with minimal change. >> >> This patch does not address the suggestion of creating a new directory >> for libgcov. I agree with you that's a much better >> and cleaner structure we should go for. We can do that in follow-up patches. >> >> I tested this patch with boostrap and profiledbootstrap. Other tests >> are on-going. >> >> Thanks, >> >> -Rong >> >> On Wed, Apr 16, 2014 at 8:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote: >>>> GCOT_TOOL needs to use this function to read the string in gcda file >>>> to memory to construct gcov_info objects. >>>> As you noticed, gcov runtime does not need this interface. But >>>> gcov-tool links with gcov runtime and it also uses the function. >>>> We could make it available in gcov_runtime, but that will slightly >>>> increase the memory footprint. >>> >>> Yep, it is not really pretty. I wrote bellow some plan how things may be >>> structured in more convenient way. Any work in that direction would be welcome. >>>> >>>> The planned new functions for trunk version is: (1) overlap score b/w >>>> two profiles (2) better dumping (top hot objects/function/counters) >>>> and statistics. >>>> Once this basic version is in, we can start to add the new functionality. >>> >>> Sounds good. I assume the autoFDO does not go via gcov tool but rather uses >>> custom reader of profile data at GCC side? >>> I wonder, are there any recent overview papers/slides/text of design of AutoFDO >>> and other features to be merged? >>> I remember the talk from GCC Summit and I did read some of pre-LTO LIPO >>> sources & papers, but it would be nice to have somethin up to date. >>>> >>>> libgcov-util.o is built in gcc/ directory, rather in libgcc. >>>> It's directly linked to gcov-tool. >>>> So libgcov-util.o is built for HOST, not TARGET. >>>> With makefile changes, we can built HOST version of libgcov-driver.o >>>> and libgcov-merge.o. >>>> I also need to make some static functions/variables public. >>> >>> I suppose that can go with IN_GCOV_TOOL ifdef. >>> >>> So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed >>> by libgcc/libgcov* code. We also will get libgcov-util.c in libgcc directory >>> that is actually borrowed by by gcc/gcov-tool.c code. >>> >>> We now have one runtime using STDIO for streaming and kernel has custom version >>> that goes via /proc interface (last time I looked). We added some abstraction >>> into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c >>> code and now we have in-memory handling code in libgcov-util. >>> >>> I guess it would make most sentse to put all the gcov code into a new directory >>> (libgcov) and make it stand-alone library that can be configured >>> 1) for stdio based runtime as we do not >>> 2) for runtime missing the interface and relyin on user providing it >>> 3) for use within gcov file manipulation tools with reorg of >>> GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces. >>> In a longer term, I would like to make FDO profiling intstrumentation to happen >>> at linktime. For that I need to make the instrumentation code (minimal spaning >>> tree & friends) to work w/o cgraph that would ideally be done in a shared >>> implementation >>>> > Won't this get wrong answer when counters[0] is not the same? >>>> > I would expected the merging code to compare the counters first. Similarly for delta counter. >>>> >>>> These *_op functions are for scaling only. So there is only one >>>> profile involved (thus there is no comparison). >>>> The merge handles are in libgcov-merge.c which have the code to handle >>>> mismatched profile targets. >>> >>> I see, OK then. >>>> > >>>> > Adding correct rounding may actually make difference for Martin's startup time work. >>>> >>>> Do you mean to use something like in RDIV macro? >>> >>> Yes. >>> >>> Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 8:07 ` Richard Biener @ 2014-07-11 8:12 ` Christophe Lyon 2014-07-11 15:50 ` Xinliang David Li 2014-07-11 15:42 ` Xinliang David Li 1 sibling, 1 reply; 23+ messages in thread From: Christophe Lyon @ 2014-07-11 8:12 UTC (permalink / raw) To: Richard Biener Cc: Rong Xu, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen On 11 July 2014 10:07, Richard Biener <richard.guenther@gmail.com> wrote: > On Mon, May 5, 2014 at 7:17 PM, Rong Xu <xur@google.com> wrote: >> Here is the updated patch. The difference with patch set 3 is >> (1) use the gcov-counter.def for scaling operation. >> (2) fix wrong scaling function for time-profiler. >> >> passed with bootstrap, profiledboostrap and SPEC2006. > > One of the patches breaks bootstrap for me: > > /space/rguenther/src/svn/trunk/gcc/../libgcc/libgcov.h:184: error: ISO > C++ forbids zero-size array ‘ctrs’ > make[3]: *** [libgcov-util.o] Error 1 > > host compiler is gcc 4.3.4 > > Richard. > On my side, commit 212448 breaks the cross-build of GCC for targets using newlib: * arm-none-eabi * aarch64-none-elf /usr/include/sys/stat.h: In function <80><98>void gcov_output_files(const char*, gcov_info*)<80><99>: /usr/include/sys/stat.h:317: error: too few arguments to function <80><98>int mkdir(const char*, __mode_t)<80><99> /tmp/1392958_1.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/gcov-tool.c:96: error: at this point in file make[2]: *** [gcov-tool.o] Error 1 Christophe. >> Thanks, >> >> -Rong >> >> On Thu, May 1, 2014 at 3:37 PM, Rong Xu <xur@google.com> wrote: >>> Hi, Honza, >>> >>> Please find the new patch in the attachment. This patch integrated >>> your earlier suggestions. The noticeable changes are: >>> (1) build specialized object for libgcov-driver.c and libgcov-merge.c >>> and link into gcov-tool, rather including the source file. >>> (2) split some gcov counter definition code to gcov-counter.def to >>> avoid code duplication. >>> (3) use a macro for gcov_read_counter(), so gcov-tool can use existing >>> code in libgcov-merge.c with minimal change. >>> >>> This patch does not address the suggestion of creating a new directory >>> for libgcov. I agree with you that's a much better >>> and cleaner structure we should go for. We can do that in follow-up patches. >>> >>> I tested this patch with boostrap and profiledbootstrap. Other tests >>> are on-going. >>> >>> Thanks, >>> >>> -Rong >>> >>> On Wed, Apr 16, 2014 at 8:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote: >>>>> GCOT_TOOL needs to use this function to read the string in gcda file >>>>> to memory to construct gcov_info objects. >>>>> As you noticed, gcov runtime does not need this interface. But >>>>> gcov-tool links with gcov runtime and it also uses the function. >>>>> We could make it available in gcov_runtime, but that will slightly >>>>> increase the memory footprint. >>>> >>>> Yep, it is not really pretty. I wrote bellow some plan how things may be >>>> structured in more convenient way. Any work in that direction would be welcome. >>>>> >>>>> The planned new functions for trunk version is: (1) overlap score b/w >>>>> two profiles (2) better dumping (top hot objects/function/counters) >>>>> and statistics. >>>>> Once this basic version is in, we can start to add the new functionality. >>>> >>>> Sounds good. I assume the autoFDO does not go via gcov tool but rather uses >>>> custom reader of profile data at GCC side? >>>> I wonder, are there any recent overview papers/slides/text of design of AutoFDO >>>> and other features to be merged? >>>> I remember the talk from GCC Summit and I did read some of pre-LTO LIPO >>>> sources & papers, but it would be nice to have somethin up to date. >>>>> >>>>> libgcov-util.o is built in gcc/ directory, rather in libgcc. >>>>> It's directly linked to gcov-tool. >>>>> So libgcov-util.o is built for HOST, not TARGET. >>>>> With makefile changes, we can built HOST version of libgcov-driver.o >>>>> and libgcov-merge.o. >>>>> I also need to make some static functions/variables public. >>>> >>>> I suppose that can go with IN_GCOV_TOOL ifdef. >>>> >>>> So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed >>>> by libgcc/libgcov* code. We also will get libgcov-util.c in libgcc directory >>>> that is actually borrowed by by gcc/gcov-tool.c code. >>>> >>>> We now have one runtime using STDIO for streaming and kernel has custom version >>>> that goes via /proc interface (last time I looked). We added some abstraction >>>> into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c >>>> code and now we have in-memory handling code in libgcov-util. >>>> >>>> I guess it would make most sentse to put all the gcov code into a new directory >>>> (libgcov) and make it stand-alone library that can be configured >>>> 1) for stdio based runtime as we do not >>>> 2) for runtime missing the interface and relyin on user providing it >>>> 3) for use within gcov file manipulation tools with reorg of >>>> GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces. >>>> In a longer term, I would like to make FDO profiling intstrumentation to happen >>>> at linktime. For that I need to make the instrumentation code (minimal spaning >>>> tree & friends) to work w/o cgraph that would ideally be done in a shared >>>> implementation >>>>> > Won't this get wrong answer when counters[0] is not the same? >>>>> > I would expected the merging code to compare the counters first. Similarly for delta counter. >>>>> >>>>> These *_op functions are for scaling only. So there is only one >>>>> profile involved (thus there is no comparison). >>>>> The merge handles are in libgcov-merge.c which have the code to handle >>>>> mismatched profile targets. >>>> >>>> I see, OK then. >>>>> > >>>>> > Adding correct rounding may actually make difference for Martin's startup time work. >>>>> >>>>> Do you mean to use something like in RDIV macro? >>>> >>>> Yes. >>>> >>>> Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 8:12 ` Christophe Lyon @ 2014-07-11 15:50 ` Xinliang David Li 2014-07-11 16:44 ` Rong Xu 0 siblings, 1 reply; 23+ messages in thread From: Xinliang David Li @ 2014-07-11 15:50 UTC (permalink / raw) To: Christophe Lyon Cc: Richard Biener, Rong Xu, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen What is the macro to test POSIX IO on host? The guard uses TARGET_POSIX_IO which is not right. David On Fri, Jul 11, 2014 at 1:12 AM, Christophe Lyon <christophe.lyon@linaro.org> wrote: > On 11 July 2014 10:07, Richard Biener <richard.guenther@gmail.com> wrote: >> On Mon, May 5, 2014 at 7:17 PM, Rong Xu <xur@google.com> wrote: >>> Here is the updated patch. The difference with patch set 3 is >>> (1) use the gcov-counter.def for scaling operation. >>> (2) fix wrong scaling function for time-profiler. >>> >>> passed with bootstrap, profiledboostrap and SPEC2006. >> >> One of the patches breaks bootstrap for me: >> >> /space/rguenther/src/svn/trunk/gcc/../libgcc/libgcov.h:184: error: ISO >> C++ forbids zero-size array ‘ctrs’ >> make[3]: *** [libgcov-util.o] Error 1 >> >> host compiler is gcc 4.3.4 >> >> Richard. >> > > On my side, commit 212448 breaks the cross-build of GCC for targets > using newlib: > * arm-none-eabi > * aarch64-none-elf > /usr/include/sys/stat.h: In function <80><98>void > gcov_output_files(const char*, gcov_info*)<80><99>: > /usr/include/sys/stat.h:317: error: too few arguments to function > <80><98>int mkdir(const char*, __mode_t)<80><99> > /tmp/1392958_1.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/gcov-tool.c:96: > error: at this point in file > make[2]: *** [gcov-tool.o] Error 1 > > Christophe. > >>> Thanks, >>> >>> -Rong >>> >>> On Thu, May 1, 2014 at 3:37 PM, Rong Xu <xur@google.com> wrote: >>>> Hi, Honza, >>>> >>>> Please find the new patch in the attachment. This patch integrated >>>> your earlier suggestions. The noticeable changes are: >>>> (1) build specialized object for libgcov-driver.c and libgcov-merge.c >>>> and link into gcov-tool, rather including the source file. >>>> (2) split some gcov counter definition code to gcov-counter.def to >>>> avoid code duplication. >>>> (3) use a macro for gcov_read_counter(), so gcov-tool can use existing >>>> code in libgcov-merge.c with minimal change. >>>> >>>> This patch does not address the suggestion of creating a new directory >>>> for libgcov. I agree with you that's a much better >>>> and cleaner structure we should go for. We can do that in follow-up patches. >>>> >>>> I tested this patch with boostrap and profiledbootstrap. Other tests >>>> are on-going. >>>> >>>> Thanks, >>>> >>>> -Rong >>>> >>>> On Wed, Apr 16, 2014 at 8:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote: >>>>>> GCOT_TOOL needs to use this function to read the string in gcda file >>>>>> to memory to construct gcov_info objects. >>>>>> As you noticed, gcov runtime does not need this interface. But >>>>>> gcov-tool links with gcov runtime and it also uses the function. >>>>>> We could make it available in gcov_runtime, but that will slightly >>>>>> increase the memory footprint. >>>>> >>>>> Yep, it is not really pretty. I wrote bellow some plan how things may be >>>>> structured in more convenient way. Any work in that direction would be welcome. >>>>>> >>>>>> The planned new functions for trunk version is: (1) overlap score b/w >>>>>> two profiles (2) better dumping (top hot objects/function/counters) >>>>>> and statistics. >>>>>> Once this basic version is in, we can start to add the new functionality. >>>>> >>>>> Sounds good. I assume the autoFDO does not go via gcov tool but rather uses >>>>> custom reader of profile data at GCC side? >>>>> I wonder, are there any recent overview papers/slides/text of design of AutoFDO >>>>> and other features to be merged? >>>>> I remember the talk from GCC Summit and I did read some of pre-LTO LIPO >>>>> sources & papers, but it would be nice to have somethin up to date. >>>>>> >>>>>> libgcov-util.o is built in gcc/ directory, rather in libgcc. >>>>>> It's directly linked to gcov-tool. >>>>>> So libgcov-util.o is built for HOST, not TARGET. >>>>>> With makefile changes, we can built HOST version of libgcov-driver.o >>>>>> and libgcov-merge.o. >>>>>> I also need to make some static functions/variables public. >>>>> >>>>> I suppose that can go with IN_GCOV_TOOL ifdef. >>>>> >>>>> So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed >>>>> by libgcc/libgcov* code. We also will get libgcov-util.c in libgcc directory >>>>> that is actually borrowed by by gcc/gcov-tool.c code. >>>>> >>>>> We now have one runtime using STDIO for streaming and kernel has custom version >>>>> that goes via /proc interface (last time I looked). We added some abstraction >>>>> into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c >>>>> code and now we have in-memory handling code in libgcov-util. >>>>> >>>>> I guess it would make most sentse to put all the gcov code into a new directory >>>>> (libgcov) and make it stand-alone library that can be configured >>>>> 1) for stdio based runtime as we do not >>>>> 2) for runtime missing the interface and relyin on user providing it >>>>> 3) for use within gcov file manipulation tools with reorg of >>>>> GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces. >>>>> In a longer term, I would like to make FDO profiling intstrumentation to happen >>>>> at linktime. For that I need to make the instrumentation code (minimal spaning >>>>> tree & friends) to work w/o cgraph that would ideally be done in a shared >>>>> implementation >>>>>> > Won't this get wrong answer when counters[0] is not the same? >>>>>> > I would expected the merging code to compare the counters first. Similarly for delta counter. >>>>>> >>>>>> These *_op functions are for scaling only. So there is only one >>>>>> profile involved (thus there is no comparison). >>>>>> The merge handles are in libgcov-merge.c which have the code to handle >>>>>> mismatched profile targets. >>>>> >>>>> I see, OK then. >>>>>> > >>>>>> > Adding correct rounding may actually make difference for Martin's startup time work. >>>>>> >>>>>> Do you mean to use something like in RDIV macro? >>>>> >>>>> Yes. >>>>> >>>>> Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 15:50 ` Xinliang David Li @ 2014-07-11 16:44 ` Rong Xu 0 siblings, 0 replies; 23+ messages in thread From: Rong Xu @ 2014-07-11 16:44 UTC (permalink / raw) To: Xinliang David Li Cc: Christophe Lyon, Richard Biener, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen I should use _WIN32 macro. This code is for windows mkdir api. I'll commit a patch for this shortly (approved by honza). -Rong On Fri, Jul 11, 2014 at 8:49 AM, Xinliang David Li <davidxl@google.com> wrote: > What is the macro to test POSIX IO on host? The guard uses > TARGET_POSIX_IO which is not right. > > David > > On Fri, Jul 11, 2014 at 1:12 AM, Christophe Lyon > <christophe.lyon@linaro.org> wrote: >> On 11 July 2014 10:07, Richard Biener <richard.guenther@gmail.com> wrote: >>> On Mon, May 5, 2014 at 7:17 PM, Rong Xu <xur@google.com> wrote: >>>> Here is the updated patch. The difference with patch set 3 is >>>> (1) use the gcov-counter.def for scaling operation. >>>> (2) fix wrong scaling function for time-profiler. >>>> >>>> passed with bootstrap, profiledboostrap and SPEC2006. >>> >>> One of the patches breaks bootstrap for me: >>> >>> /space/rguenther/src/svn/trunk/gcc/../libgcc/libgcov.h:184: error: ISO >>> C++ forbids zero-size array ‘ctrs’ >>> make[3]: *** [libgcov-util.o] Error 1 >>> >>> host compiler is gcc 4.3.4 >>> >>> Richard. >>> >> >> On my side, commit 212448 breaks the cross-build of GCC for targets >> using newlib: >> * arm-none-eabi >> * aarch64-none-elf >> /usr/include/sys/stat.h: In function <80><98>void >> gcov_output_files(const char*, gcov_info*)<80><99>: >> /usr/include/sys/stat.h:317: error: too few arguments to function >> <80><98>int mkdir(const char*, __mode_t)<80><99> >> /tmp/1392958_1.tmpdir/aci-gcc-fsf/sources/gcc-fsf/gccsrc/gcc/gcov-tool.c:96: >> error: at this point in file >> make[2]: *** [gcov-tool.o] Error 1 >> >> Christophe. >> >>>> Thanks, >>>> >>>> -Rong >>>> >>>> On Thu, May 1, 2014 at 3:37 PM, Rong Xu <xur@google.com> wrote: >>>>> Hi, Honza, >>>>> >>>>> Please find the new patch in the attachment. This patch integrated >>>>> your earlier suggestions. The noticeable changes are: >>>>> (1) build specialized object for libgcov-driver.c and libgcov-merge.c >>>>> and link into gcov-tool, rather including the source file. >>>>> (2) split some gcov counter definition code to gcov-counter.def to >>>>> avoid code duplication. >>>>> (3) use a macro for gcov_read_counter(), so gcov-tool can use existing >>>>> code in libgcov-merge.c with minimal change. >>>>> >>>>> This patch does not address the suggestion of creating a new directory >>>>> for libgcov. I agree with you that's a much better >>>>> and cleaner structure we should go for. We can do that in follow-up patches. >>>>> >>>>> I tested this patch with boostrap and profiledbootstrap. Other tests >>>>> are on-going. >>>>> >>>>> Thanks, >>>>> >>>>> -Rong >>>>> >>>>> On Wed, Apr 16, 2014 at 8:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote: >>>>>>> GCOT_TOOL needs to use this function to read the string in gcda file >>>>>>> to memory to construct gcov_info objects. >>>>>>> As you noticed, gcov runtime does not need this interface. But >>>>>>> gcov-tool links with gcov runtime and it also uses the function. >>>>>>> We could make it available in gcov_runtime, but that will slightly >>>>>>> increase the memory footprint. >>>>>> >>>>>> Yep, it is not really pretty. I wrote bellow some plan how things may be >>>>>> structured in more convenient way. Any work in that direction would be welcome. >>>>>>> >>>>>>> The planned new functions for trunk version is: (1) overlap score b/w >>>>>>> two profiles (2) better dumping (top hot objects/function/counters) >>>>>>> and statistics. >>>>>>> Once this basic version is in, we can start to add the new functionality. >>>>>> >>>>>> Sounds good. I assume the autoFDO does not go via gcov tool but rather uses >>>>>> custom reader of profile data at GCC side? >>>>>> I wonder, are there any recent overview papers/slides/text of design of AutoFDO >>>>>> and other features to be merged? >>>>>> I remember the talk from GCC Summit and I did read some of pre-LTO LIPO >>>>>> sources & papers, but it would be nice to have somethin up to date. >>>>>>> >>>>>>> libgcov-util.o is built in gcc/ directory, rather in libgcc. >>>>>>> It's directly linked to gcov-tool. >>>>>>> So libgcov-util.o is built for HOST, not TARGET. >>>>>>> With makefile changes, we can built HOST version of libgcov-driver.o >>>>>>> and libgcov-merge.o. >>>>>>> I also need to make some static functions/variables public. >>>>>> >>>>>> I suppose that can go with IN_GCOV_TOOL ifdef. >>>>>> >>>>>> So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed >>>>>> by libgcc/libgcov* code. We also will get libgcov-util.c in libgcc directory >>>>>> that is actually borrowed by by gcc/gcov-tool.c code. >>>>>> >>>>>> We now have one runtime using STDIO for streaming and kernel has custom version >>>>>> that goes via /proc interface (last time I looked). We added some abstraction >>>>>> into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c >>>>>> code and now we have in-memory handling code in libgcov-util. >>>>>> >>>>>> I guess it would make most sentse to put all the gcov code into a new directory >>>>>> (libgcov) and make it stand-alone library that can be configured >>>>>> 1) for stdio based runtime as we do not >>>>>> 2) for runtime missing the interface and relyin on user providing it >>>>>> 3) for use within gcov file manipulation tools with reorg of >>>>>> GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces. >>>>>> In a longer term, I would like to make FDO profiling intstrumentation to happen >>>>>> at linktime. For that I need to make the instrumentation code (minimal spaning >>>>>> tree & friends) to work w/o cgraph that would ideally be done in a shared >>>>>> implementation >>>>>>> > Won't this get wrong answer when counters[0] is not the same? >>>>>>> > I would expected the merging code to compare the counters first. Similarly for delta counter. >>>>>>> >>>>>>> These *_op functions are for scaling only. So there is only one >>>>>>> profile involved (thus there is no comparison). >>>>>>> The merge handles are in libgcov-merge.c which have the code to handle >>>>>>> mismatched profile targets. >>>>>> >>>>>> I see, OK then. >>>>>>> > >>>>>>> > Adding correct rounding may actually make difference for Martin's startup time work. >>>>>>> >>>>>>> Do you mean to use something like in RDIV macro? >>>>>> >>>>>> Yes. >>>>>> >>>>>> Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 8:07 ` Richard Biener 2014-07-11 8:12 ` Christophe Lyon @ 2014-07-11 15:42 ` Xinliang David Li 2014-07-11 16:03 ` Jakub Jelinek 1 sibling, 1 reply; 23+ messages in thread From: Xinliang David Li @ 2014-07-11 15:42 UTC (permalink / raw) To: Richard Biener Cc: Rong Xu, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen I wonder why. The struct definition for gcov_fn_info has not changed in this patch. David On Fri, Jul 11, 2014 at 1:07 AM, Richard Biener <richard.guenther@gmail.com> wrote: > On Mon, May 5, 2014 at 7:17 PM, Rong Xu <xur@google.com> wrote: >> Here is the updated patch. The difference with patch set 3 is >> (1) use the gcov-counter.def for scaling operation. >> (2) fix wrong scaling function for time-profiler. >> >> passed with bootstrap, profiledboostrap and SPEC2006. > > One of the patches breaks bootstrap for me: > > /space/rguenther/src/svn/trunk/gcc/../libgcc/libgcov.h:184: error: ISO > C++ forbids zero-size array ‘ctrs’ > make[3]: *** [libgcov-util.o] Error 1 > > host compiler is gcc 4.3.4 > > Richard. > >> Thanks, >> >> -Rong >> >> On Thu, May 1, 2014 at 3:37 PM, Rong Xu <xur@google.com> wrote: >>> Hi, Honza, >>> >>> Please find the new patch in the attachment. This patch integrated >>> your earlier suggestions. The noticeable changes are: >>> (1) build specialized object for libgcov-driver.c and libgcov-merge.c >>> and link into gcov-tool, rather including the source file. >>> (2) split some gcov counter definition code to gcov-counter.def to >>> avoid code duplication. >>> (3) use a macro for gcov_read_counter(), so gcov-tool can use existing >>> code in libgcov-merge.c with minimal change. >>> >>> This patch does not address the suggestion of creating a new directory >>> for libgcov. I agree with you that's a much better >>> and cleaner structure we should go for. We can do that in follow-up patches. >>> >>> I tested this patch with boostrap and profiledbootstrap. Other tests >>> are on-going. >>> >>> Thanks, >>> >>> -Rong >>> >>> On Wed, Apr 16, 2014 at 8:34 PM, Jan Hubicka <hubicka@ucw.cz> wrote: >>>>> GCOT_TOOL needs to use this function to read the string in gcda file >>>>> to memory to construct gcov_info objects. >>>>> As you noticed, gcov runtime does not need this interface. But >>>>> gcov-tool links with gcov runtime and it also uses the function. >>>>> We could make it available in gcov_runtime, but that will slightly >>>>> increase the memory footprint. >>>> >>>> Yep, it is not really pretty. I wrote bellow some plan how things may be >>>> structured in more convenient way. Any work in that direction would be welcome. >>>>> >>>>> The planned new functions for trunk version is: (1) overlap score b/w >>>>> two profiles (2) better dumping (top hot objects/function/counters) >>>>> and statistics. >>>>> Once this basic version is in, we can start to add the new functionality. >>>> >>>> Sounds good. I assume the autoFDO does not go via gcov tool but rather uses >>>> custom reader of profile data at GCC side? >>>> I wonder, are there any recent overview papers/slides/text of design of AutoFDO >>>> and other features to be merged? >>>> I remember the talk from GCC Summit and I did read some of pre-LTO LIPO >>>> sources & papers, but it would be nice to have somethin up to date. >>>>> >>>>> libgcov-util.o is built in gcc/ directory, rather in libgcc. >>>>> It's directly linked to gcov-tool. >>>>> So libgcov-util.o is built for HOST, not TARGET. >>>>> With makefile changes, we can built HOST version of libgcov-driver.o >>>>> and libgcov-merge.o. >>>>> I also need to make some static functions/variables public. >>>> >>>> I suppose that can go with IN_GCOV_TOOL ifdef. >>>> >>>> So we currently have basic gcov io handling in gcc/gcov-io.c that is borrowed >>>> by libgcc/libgcov* code. We also will get libgcov-util.c in libgcc directory >>>> that is actually borrowed by by gcc/gcov-tool.c code. >>>> >>>> We now have one runtime using STDIO for streaming and kernel has custom version >>>> that goes via /proc interface (last time I looked). We added some abstraction >>>> into libgcov-interface that is the part that relies on STDIO, partly via gcov-io.c >>>> code and now we have in-memory handling code in libgcov-util. >>>> >>>> I guess it would make most sentse to put all the gcov code into a new directory >>>> (libgcov) and make it stand-alone library that can be configured >>>> 1) for stdio based runtime as we do not >>>> 2) for runtime missing the interface and relyin on user providing it >>>> 3) for use within gcov file manipulation tools with reorg of >>>> GCC/gcov/gcov-dump/gcov-tool to all use the same low-level interfaces. >>>> In a longer term, I would like to make FDO profiling intstrumentation to happen >>>> at linktime. For that I need to make the instrumentation code (minimal spaning >>>> tree & friends) to work w/o cgraph that would ideally be done in a shared >>>> implementation >>>>> > Won't this get wrong answer when counters[0] is not the same? >>>>> > I would expected the merging code to compare the counters first. Similarly for delta counter. >>>>> >>>>> These *_op functions are for scaling only. So there is only one >>>>> profile involved (thus there is no comparison). >>>>> The merge handles are in libgcov-merge.c which have the code to handle >>>>> mismatched profile targets. >>>> >>>> I see, OK then. >>>>> > >>>>> > Adding correct rounding may actually make difference for Martin's startup time work. >>>>> >>>>> Do you mean to use something like in RDIV macro? >>>> >>>> Yes. >>>> >>>> Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 15:42 ` Xinliang David Li @ 2014-07-11 16:03 ` Jakub Jelinek 2014-07-11 16:13 ` Xinliang David Li 0 siblings, 1 reply; 23+ messages in thread From: Jakub Jelinek @ 2014-07-11 16:03 UTC (permalink / raw) To: Xinliang David Li Cc: Richard Biener, Rong Xu, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen On Fri, Jul 11, 2014 at 08:42:27AM -0700, Xinliang David Li wrote: > I wonder why. The struct definition for gcov_fn_info has not changed > in this patch. Perhaps it has been used only in C until now? Jakub ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 16:03 ` Jakub Jelinek @ 2014-07-11 16:13 ` Xinliang David Li 2014-07-11 16:48 ` Rong Xu 0 siblings, 1 reply; 23+ messages in thread From: Xinliang David Li @ 2014-07-11 16:13 UTC (permalink / raw) To: Jakub Jelinek Cc: Richard Biener, Rong Xu, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen right. Rong, the fix would be just change ctr array size to 1. For each function, there should be at least one kind of counters -- see the assertion in build_fn_info_type. There are some code that do 'sizeof(gcov_fn_info)' when computing heap size -- they can be adjusted or leave it as it is (if not doing memcpy for the whole array). David On Fri, Jul 11, 2014 at 8:44 AM, Jakub Jelinek <jakub@redhat.com> wrote: > On Fri, Jul 11, 2014 at 08:42:27AM -0700, Xinliang David Li wrote: >> I wonder why. The struct definition for gcov_fn_info has not changed >> in this patch. > > Perhaps it has been used only in C until now? > > Jakub ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 16:13 ` Xinliang David Li @ 2014-07-11 16:48 ` Rong Xu 2014-07-11 18:30 ` Rong Xu 0 siblings, 1 reply; 23+ messages in thread From: Rong Xu @ 2014-07-11 16:48 UTC (permalink / raw) To: Xinliang David Li Cc: Jakub Jelinek, Richard Biener, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen I did see the warning in the bootstrap, but it did not exit the build. I thought it was ok. I'll have a patch for this and send for review. -Rong On Fri, Jul 11, 2014 at 9:13 AM, Xinliang David Li <davidxl@google.com> wrote: > right. > > Rong, the fix would be just change ctr array size to 1. For each > function, there should be at least one kind of counters -- see the > assertion in build_fn_info_type. There are some code that do > 'sizeof(gcov_fn_info)' when computing heap size -- they can be > adjusted or leave it as it is (if not doing memcpy for the whole > array). > > David > > On Fri, Jul 11, 2014 at 8:44 AM, Jakub Jelinek <jakub@redhat.com> wrote: >> On Fri, Jul 11, 2014 at 08:42:27AM -0700, Xinliang David Li wrote: >>> I wonder why. The struct definition for gcov_fn_info has not changed >>> in this patch. >> >> Perhaps it has been used only in C until now? >> >> Jakub ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 16:48 ` Rong Xu @ 2014-07-11 18:30 ` Rong Xu 2014-07-11 18:46 ` Jan Hubicka 2014-07-13 7:49 ` Andreas Schwab 0 siblings, 2 replies; 23+ messages in thread From: Rong Xu @ 2014-07-11 18:30 UTC (permalink / raw) To: Xinliang David Li Cc: Jakub Jelinek, Richard Biener, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen Richard, I looked at my patch again. I already add -Wno-error to libgcov-util.o compilation: In line 200 of gcc/Makefile.in libgcov-util.o-warn = -Wno-error In my test, I used gcc-4.6 as the host compiler. I got warning like this: In file included from ../../gcc/gcc/../libgcc/libgcov-util.c:30:0: ../../gcc/gcc/../libgcc/libgcov.h:184:30: warning: ISO C++ forbids zero-size array ‘ctrs’ [-pedantic] Can you check your buildlog to see if -Wno-error is added to the command line? Thanks, -Rong On Fri, Jul 11, 2014 at 9:47 AM, Rong Xu <xur@google.com> wrote: > I did see the warning in the bootstrap, but it did not exit the build. > I thought it was ok. > > I'll have a patch for this and send for review. > > -Rong > > On Fri, Jul 11, 2014 at 9:13 AM, Xinliang David Li <davidxl@google.com> wrote: >> right. >> >> Rong, the fix would be just change ctr array size to 1. For each >> function, there should be at least one kind of counters -- see the >> assertion in build_fn_info_type. There are some code that do >> 'sizeof(gcov_fn_info)' when computing heap size -- they can be >> adjusted or leave it as it is (if not doing memcpy for the whole >> array). >> >> David >> >> On Fri, Jul 11, 2014 at 8:44 AM, Jakub Jelinek <jakub@redhat.com> wrote: >>> On Fri, Jul 11, 2014 at 08:42:27AM -0700, Xinliang David Li wrote: >>>> I wonder why. The struct definition for gcov_fn_info has not changed >>>> in this patch. >>> >>> Perhaps it has been used only in C until now? >>> >>> Jakub ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 18:30 ` Rong Xu @ 2014-07-11 18:46 ` Jan Hubicka 2014-07-11 18:53 ` Rong Xu 2014-07-13 7:49 ` Andreas Schwab 1 sibling, 1 reply; 23+ messages in thread From: Jan Hubicka @ 2014-07-11 18:46 UTC (permalink / raw) To: Rong Xu Cc: Xinliang David Li, Jakub Jelinek, Richard Biener, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen > Richard, > > I looked at my patch again. I already add -Wno-error to libgcov-util.o > compilation: > In line 200 of gcc/Makefile.in > libgcov-util.o-warn = -Wno-error > > In my test, I used gcc-4.6 as the host compiler. I got warning like this: > > In file included from ../../gcc/gcc/../libgcc/libgcov-util.c:30:0: > ../../gcc/gcc/../libgcc/libgcov.h:184:30: warning: ISO C++ forbids > zero-size array ???ctrs??? [-pedantic] > > Can you check your buildlog to see if -Wno-error is added to the command line? I would preffer the libgcov.h (that is interface header to libgcov users) to be valid C++, so we still ought to fix it. Honza > > Thanks, > > -Rong > > On Fri, Jul 11, 2014 at 9:47 AM, Rong Xu <xur@google.com> wrote: > > I did see the warning in the bootstrap, but it did not exit the build. > > I thought it was ok. > > > > I'll have a patch for this and send for review. > > > > -Rong > > > > On Fri, Jul 11, 2014 at 9:13 AM, Xinliang David Li <davidxl@google.com> wrote: > >> right. > >> > >> Rong, the fix would be just change ctr array size to 1. For each > >> function, there should be at least one kind of counters -- see the > >> assertion in build_fn_info_type. There are some code that do > >> 'sizeof(gcov_fn_info)' when computing heap size -- they can be > >> adjusted or leave it as it is (if not doing memcpy for the whole > >> array). > >> > >> David > >> > >> On Fri, Jul 11, 2014 at 8:44 AM, Jakub Jelinek <jakub@redhat.com> wrote: > >>> On Fri, Jul 11, 2014 at 08:42:27AM -0700, Xinliang David Li wrote: > >>>> I wonder why. The struct definition for gcov_fn_info has not changed > >>>> in this patch. > >>> > >>> Perhaps it has been used only in C until now? > >>> > >>> Jakub ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 18:46 ` Jan Hubicka @ 2014-07-11 18:53 ` Rong Xu 0 siblings, 0 replies; 23+ messages in thread From: Rong Xu @ 2014-07-11 18:53 UTC (permalink / raw) To: Jan Hubicka Cc: Xinliang David Li, Jakub Jelinek, Richard Biener, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen On Fri, Jul 11, 2014 at 11:46 AM, Jan Hubicka <hubicka@ucw.cz> wrote: >> Richard, >> >> I looked at my patch again. I already add -Wno-error to libgcov-util.o >> compilation: >> In line 200 of gcc/Makefile.in >> libgcov-util.o-warn = -Wno-error >> >> In my test, I used gcc-4.6 as the host compiler. I got warning like this: >> >> In file included from ../../gcc/gcc/../libgcc/libgcov-util.c:30:0: >> ../../gcc/gcc/../libgcc/libgcov.h:184:30: warning: ISO C++ forbids >> zero-size array ???ctrs??? [-pedantic] >> >> Can you check your buildlog to see if -Wno-error is added to the command line? > > I would preffer the libgcov.h (that is interface header to libgcov users) to be > valid C++, so we still ought to fix it. > > Honza OK. I will send out a patch for review. -Rong >> >> Thanks, >> >> -Rong >> >> On Fri, Jul 11, 2014 at 9:47 AM, Rong Xu <xur@google.com> wrote: >> > I did see the warning in the bootstrap, but it did not exit the build. >> > I thought it was ok. >> > >> > I'll have a patch for this and send for review. >> > >> > -Rong >> > >> > On Fri, Jul 11, 2014 at 9:13 AM, Xinliang David Li <davidxl@google.com> wrote: >> >> right. >> >> >> >> Rong, the fix would be just change ctr array size to 1. For each >> >> function, there should be at least one kind of counters -- see the >> >> assertion in build_fn_info_type. There are some code that do >> >> 'sizeof(gcov_fn_info)' when computing heap size -- they can be >> >> adjusted or leave it as it is (if not doing memcpy for the whole >> >> array). >> >> >> >> David >> >> >> >> On Fri, Jul 11, 2014 at 8:44 AM, Jakub Jelinek <jakub@redhat.com> wrote: >> >>> On Fri, Jul 11, 2014 at 08:42:27AM -0700, Xinliang David Li wrote: >> >>>> I wonder why. The struct definition for gcov_fn_info has not changed >> >>>> in this patch. >> >>> >> >>> Perhaps it has been used only in C until now? >> >>> >> >>> Jakub ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-07-11 18:30 ` Rong Xu 2014-07-11 18:46 ` Jan Hubicka @ 2014-07-13 7:49 ` Andreas Schwab 1 sibling, 0 replies; 23+ messages in thread From: Andreas Schwab @ 2014-07-13 7:49 UTC (permalink / raw) To: Rong Xu Cc: Xinliang David Li, Jakub Jelinek, Richard Biener, Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen Rong Xu <xur@google.com> writes: > In my test, I used gcc-4.6 as the host compiler. I got warning like this: > > In file included from ../../gcc/gcc/../libgcc/libgcov-util.c:30:0: > ../../gcc/gcc/../libgcc/libgcov.h:184:30: warning: ISO C++ forbids > zero-size array âctrsâ [-pedantic] With gcc 4.3 this is an error, so -Wno-error has no effect. Andreas. -- Andreas Schwab, schwab@linux-m68k.org GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5 "And now for something completely different." ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-05-01 22:37 ` Rong Xu 2014-05-05 17:17 ` Rong Xu @ 2014-05-15 20:37 ` Jan Hubicka 2014-05-20 22:59 ` Rong Xu 1 sibling, 1 reply; 23+ messages in thread From: Jan Hubicka @ 2014-05-15 20:37 UTC (permalink / raw) To: Rong Xu Cc: Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen > Hi, Honza, > > Please find the new patch in the attachment. This patch integrated > your earlier suggestions. The noticeable changes are: > (1) build specialized object for libgcov-driver.c and libgcov-merge.c > and link into gcov-tool, rather including the source file. > (2) split some gcov counter definition code to gcov-counter.def to > avoid code duplication. > (3) use a macro for gcov_read_counter(), so gcov-tool can use existing > code in libgcov-merge.c with minimal change. > > This patch does not address the suggestion of creating a new directory > for libgcov. I agree with you that's a much better > and cleaner structure we should go for. We can do that in follow-up patches. Yep, lets do this incrementally. thanks! > > I tested this patch with boostrap and profiledbootstrap. Other tests > are on-going. > > Thanks, > > -Rong > 2014-05-01 Rong Xu <xur@google.com> > > * gcc/gcov-io.c (gcov_position): Make avaialble to gcov-tool. > (gcov_is_error): Ditto. > (gcov_read_string): Ditto. > (gcov_read_sync): Ditto. > * gcc/gcov-io.h: Move counter defines to gcov-counter.def. > * gcc/gcov-dump.c (tag_counters): Use gcov-counter.def. > * gcc/coverage.c: Ditto. > * gcc/gcov-tool.c: Offline gcda profile processing tool. > (unlink_gcda_file): Remove one gcda file. > (unlink_profile_dir): Remove gcda files from the profile path. > (profile_merge): Merge two profiles in directory. > (print_merge_usage_message): Print merge usage. > (merge_usage): Print merge usage and exit. > (do_merge): Driver for profile merge sub-command. > (profile_rewrite): Rewrite profile. > (print_rewrite_usage_message): Print rewrite usage. > (rewrite_usage): Print rewrite usage and exit. > (do_rewrite): Driver for profile rewrite sub-command. > (print_usage): Print gcov-info usage and exit. > (print_version): Print gcov-info version. > (process_args): Process arguments. > (main): Main routine for gcov-tool. > * gcc/Makefile.in: Build and install gcov-tool. > * gcc/gcov-counter.def: New file split from gcov-io.h. > * libgcc/libgcov-driver.c (gcov_max_filename): Make available > to gcov-tool. > * libgcc/libgcov-merge.c (__gcov_merge_add): Replace > gcov_read_counter() with a Macro. > (__gcov_merge_ior): Ditto. > (__gcov_merge_time_profile): Ditto. > (__gcov_merge_single): Ditto. > (__gcov_merge_delta): Ditto. > * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag > in the utility functions. > (set_fn_ctrs): Utility function for reading gcda files to in-memory > gcov_list object link lists. > (tag_function): Ditto. > (tag_blocks): Ditto. > (tag_arcs): Ditto. > (tag_lines): Ditto. > (tag_counters): Ditto. > (tag_summary): Ditto. > (read_gcda_finalize): Ditto. > (read_gcda_file): Ditto. > (ftw_read_file): Ditto. > (read_profile_dir_init): Ditto. > (gcov_read_profile_dir): Ditto. > (gcov_read_counter_mem): Ditto. > (gcov_get_merge_weight): Ditto. > (merge_wrapper): A wrapper function that calls merging handler. > (gcov_merge): Merge two gcov_info objects with weights. > (find_match_gcov_info): Find the matched gcov_info in the list. > (gcov_profile_merge): Merge two gcov_info object lists. > (__gcov_add_counter_op): Process edge profile counter values. > (__gcov_ior_counter_op): Process IOR profile counter values. > (__gcov_delta_counter_op): Process delta profile counter values. > (__gcov_single_counter_op): Process single profile counter values. > (fp_scale): Callback function for float-point scaling. > (int_scale): Callback function for integer fraction scaling. > (gcov_profile_scale): Scaling profile counters. > (gcov_profile_normalize): Normalize profile counters. > * libgcc/libgcov.h: Add headers and macros for gcov-tool use. > (GCOV_GET_COUNTER): New. > (GCOV_GET_COUNTER_TARGET): Ditto. > (struct gcov_info): Make the functions field mutable in gcov-tool > compilation. > > Index: gcc/gcov-io.c > =================================================================== > --- gcc/gcov-io.c (revision 209981) > +++ gcc/gcov-io.c (working copy) > @@ -64,7 +64,11 @@ GCOV_LINKAGE struct gcov_var > } gcov_var; > > /* Save the current position in the gcov file. */ > -static inline gcov_position_t > +/* We need to expose this function when compiling for gcov-tool. */ > +#ifndef IN_GCOV_TOOL > +static inline > +#endif > +gcov_position_t > gcov_position (void) > { > gcc_assert (gcov_var.mode > 0); > @@ -72,7 +76,11 @@ gcov_position (void) > } > > /* Return nonzero if the error flag is set. */ > -static inline int > +/* We need to expose this function when compiling for gcov-tool. */ > +#ifndef IN_GCOV_TOOL > +static inline > +#endif > +int I am still not too happy about the ifdef noise here, but I suppose it is bettter than bloating libgcov by making those hidden everywhere.... > +#ifdef DEF_GCOV_COUNTER > +#undef DEF_GCOV_COUNTER > +#endif > +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) COUNTER, > +enum { > +#include "gcov-counter.def" > +GCOV_COUNTERS > +}; Please consistently undef DEF_GCOV_COUNTER after each use and remove the ifdef/undef/endif blocks I think it is cleaner, even though we seem to have multiple practices when dealing with .def files across the tree. > + > +/* Arc transitions. */ > +DEF_GCOV_COUNTER(GCOV_COUNTER_ARCS=0, "arcs", __gcov_merge_add) Is =0 really needed here? It looks bit ugly ;) > Index: libgcc/libgcov-driver.c > =================================================================== > --- libgcc/libgcov-driver.c (revision 209981) > +++ libgcc/libgcov-driver.c (working copy) > @@ -77,7 +77,11 @@ set_gcov_list (struct gcov_info *head) > } > > /* Size of the longest file name. */ > -static size_t gcov_max_filename = 0; > +/* We need to expose this static variable when compiling for gcov-tool. */ > +#ifndef IN_GCOV_TOOL > +static > +#endif > +size_t gcov_max_filename = 0; Why max_filename needs to be exported? > > /* Flag when the profile has already been dumped via __gcov_dump(). */ > static int gcov_dump_complete; > Index: libgcc/libgcov-merge.c > =================================================================== > --- libgcc/libgcov-merge.c (revision 209981) > +++ libgcc/libgcov-merge.c (working copy) > @@ -53,7 +53,7 @@ void > __gcov_merge_add (gcov_type *counters, unsigned n_counters) > { > for (; n_counters; counters++, n_counters--) > - *counters += gcov_read_counter (); > + *counters += GCOV_GET_COUNTER; We seem to be on transition to C++ writting style, why we don't make GCOV_GET_COUNTER an inline function? > + > +/*extern gcov_type gcov_read_counter_mem(); > +extern unsigned gcov_get_merge_weight(); */ > + > +/* TBD: xur */ Forgoten hacks? What is xur? > +extern gcov_position_t gcov_position(); > +extern int gcov_is_error(); > +extern gcov_unsigned_t gcov_max_filename; > + > +/* We need the dumping and merge part of code in libgcov. */ > +/*#include "libgcov-driver.c" > +#include "libgcov-merge.c" */ Here too > + > +/* Verbose mode for debug. */ > +static int verbose; > + > +/* Set verbose flag. */ > +void gcov_set_verbose (void) > +{ > + verbose = 1; > +} > + > +/* The following part is to read Gcda and reconstruct GCOV_INFO. */ > + > +#include "obstack.h" > +#include <unistd.h> > +#include <ftw.h> Why the includes appear after definitions/inline functions? > + if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum) > + { > + fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n", > + info1->filename); It is custom for GCC related tools to use error/warning/fnotice. GCOV runtime is an exception since it is not linked with diagnostic.c, but otherwise I think we should use it in gcov-tool, too. Please update it. Please also add texinfo documentation for the tool, like there is gcov.texi. The patch looks OK with these changes (or rahter I think we can solve other issues incrementally) Thanks, Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-05-15 20:37 ` Jan Hubicka @ 2014-05-20 22:59 ` Rong Xu 2014-05-25 23:43 ` Jan Hubicka 0 siblings, 1 reply; 23+ messages in thread From: Rong Xu @ 2014-05-20 22:59 UTC (permalink / raw) To: Jan Hubicka; +Cc: GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen [-- Attachment #1: Type: text/plain, Size: 9511 bytes --] On Thu, May 15, 2014 at 1:37 PM, Jan Hubicka <hubicka@ucw.cz> wrote: >> Hi, Honza, >> >> Please find the new patch in the attachment. This patch integrated >> your earlier suggestions. The noticeable changes are: >> (1) build specialized object for libgcov-driver.c and libgcov-merge.c >> and link into gcov-tool, rather including the source file. >> (2) split some gcov counter definition code to gcov-counter.def to >> avoid code duplication. >> (3) use a macro for gcov_read_counter(), so gcov-tool can use existing >> code in libgcov-merge.c with minimal change. >> >> This patch does not address the suggestion of creating a new directory >> for libgcov. I agree with you that's a much better >> and cleaner structure we should go for. We can do that in follow-up patches. > > Yep, lets do this incrementally. thanks! >> >> I tested this patch with boostrap and profiledbootstrap. Other tests >> are on-going. >> >> Thanks, >> >> -Rong >> 2014-05-01 Rong Xu <xur@google.com> >> >> * gcc/gcov-io.c (gcov_position): Make avaialble to gcov-tool. >> (gcov_is_error): Ditto. >> (gcov_read_string): Ditto. >> (gcov_read_sync): Ditto. >> * gcc/gcov-io.h: Move counter defines to gcov-counter.def. >> * gcc/gcov-dump.c (tag_counters): Use gcov-counter.def. >> * gcc/coverage.c: Ditto. >> * gcc/gcov-tool.c: Offline gcda profile processing tool. >> (unlink_gcda_file): Remove one gcda file. >> (unlink_profile_dir): Remove gcda files from the profile path. >> (profile_merge): Merge two profiles in directory. >> (print_merge_usage_message): Print merge usage. >> (merge_usage): Print merge usage and exit. >> (do_merge): Driver for profile merge sub-command. >> (profile_rewrite): Rewrite profile. >> (print_rewrite_usage_message): Print rewrite usage. >> (rewrite_usage): Print rewrite usage and exit. >> (do_rewrite): Driver for profile rewrite sub-command. >> (print_usage): Print gcov-info usage and exit. >> (print_version): Print gcov-info version. >> (process_args): Process arguments. >> (main): Main routine for gcov-tool. >> * gcc/Makefile.in: Build and install gcov-tool. >> * gcc/gcov-counter.def: New file split from gcov-io.h. >> * libgcc/libgcov-driver.c (gcov_max_filename): Make available >> to gcov-tool. >> * libgcc/libgcov-merge.c (__gcov_merge_add): Replace >> gcov_read_counter() with a Macro. >> (__gcov_merge_ior): Ditto. >> (__gcov_merge_time_profile): Ditto. >> (__gcov_merge_single): Ditto. >> (__gcov_merge_delta): Ditto. >> * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag >> in the utility functions. >> (set_fn_ctrs): Utility function for reading gcda files to in-memory >> gcov_list object link lists. >> (tag_function): Ditto. >> (tag_blocks): Ditto. >> (tag_arcs): Ditto. >> (tag_lines): Ditto. >> (tag_counters): Ditto. >> (tag_summary): Ditto. >> (read_gcda_finalize): Ditto. >> (read_gcda_file): Ditto. >> (ftw_read_file): Ditto. >> (read_profile_dir_init): Ditto. >> (gcov_read_profile_dir): Ditto. >> (gcov_read_counter_mem): Ditto. >> (gcov_get_merge_weight): Ditto. >> (merge_wrapper): A wrapper function that calls merging handler. >> (gcov_merge): Merge two gcov_info objects with weights. >> (find_match_gcov_info): Find the matched gcov_info in the list. >> (gcov_profile_merge): Merge two gcov_info object lists. >> (__gcov_add_counter_op): Process edge profile counter values. >> (__gcov_ior_counter_op): Process IOR profile counter values. >> (__gcov_delta_counter_op): Process delta profile counter values. >> (__gcov_single_counter_op): Process single profile counter values. >> (fp_scale): Callback function for float-point scaling. >> (int_scale): Callback function for integer fraction scaling. >> (gcov_profile_scale): Scaling profile counters. >> (gcov_profile_normalize): Normalize profile counters. >> * libgcc/libgcov.h: Add headers and macros for gcov-tool use. >> (GCOV_GET_COUNTER): New. >> (GCOV_GET_COUNTER_TARGET): Ditto. >> (struct gcov_info): Make the functions field mutable in gcov-tool >> compilation. >> >> Index: gcc/gcov-io.c >> =================================================================== >> --- gcc/gcov-io.c (revision 209981) >> +++ gcc/gcov-io.c (working copy) >> @@ -64,7 +64,11 @@ GCOV_LINKAGE struct gcov_var >> } gcov_var; >> >> /* Save the current position in the gcov file. */ >> -static inline gcov_position_t >> +/* We need to expose this function when compiling for gcov-tool. */ >> +#ifndef IN_GCOV_TOOL >> +static inline >> +#endif >> +gcov_position_t >> gcov_position (void) >> { >> gcc_assert (gcov_var.mode > 0); >> @@ -72,7 +76,11 @@ gcov_position (void) >> } >> >> /* Return nonzero if the error flag is set. */ >> -static inline int >> +/* We need to expose this function when compiling for gcov-tool. */ >> +#ifndef IN_GCOV_TOOL >> +static inline >> +#endif >> +int > > I am still not too happy about the ifdef noise here, but I suppose it is bettter > than bloating libgcov by making those hidden everywhere.... Thanks for the understanding. It does not look good to me either. I'll keep this as it's. We can change it later if we find a better way. >> +#ifdef DEF_GCOV_COUNTER >> +#undef DEF_GCOV_COUNTER >> +#endif >> +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) COUNTER, >> +enum { >> +#include "gcov-counter.def" >> +GCOV_COUNTERS >> +}; > > Please consistently undef DEF_GCOV_COUNTER after each use and > remove the ifdef/undef/endif blocks > I think it is cleaner, even though we seem to have multiple practices > when dealing with .def files across the tree. Fixed. >> + >> +/* Arc transitions. */ >> +DEF_GCOV_COUNTER(GCOV_COUNTER_ARCS=0, "arcs", __gcov_merge_add) > > Is =0 really needed here? It looks bit ugly ;) Not really needed as enum values start with 0 by default. Removed "=0". >> Index: libgcc/libgcov-driver.c >> =================================================================== >> --- libgcc/libgcov-driver.c (revision 209981) >> +++ libgcc/libgcov-driver.c (working copy) >> @@ -77,7 +77,11 @@ set_gcov_list (struct gcov_info *head) >> } >> >> /* Size of the longest file name. */ >> -static size_t gcov_max_filename = 0; >> +/* We need to expose this static variable when compiling for gcov-tool. */ >> +#ifndef IN_GCOV_TOOL >> +static >> +#endif >> +size_t gcov_max_filename = 0; > > > Why max_filename needs to be exported? For code efficiency, we allocate the gi_filename buffer only once in the dumping, using the maximum filename length, which is set in gcov_init(). For gcov-tool, we don't have gcov_init, and we set the value when reading the gcda files. Now libgcov-driver.o is a separated compilation unit. I need to make then static gi_filename global to allow the access. > >> >> /* Flag when the profile has already been dumped via __gcov_dump(). */ >> static int gcov_dump_complete; >> Index: libgcc/libgcov-merge.c >> =================================================================== >> --- libgcc/libgcov-merge.c (revision 209981) >> +++ libgcc/libgcov-merge.c (working copy) >> @@ -53,7 +53,7 @@ void >> __gcov_merge_add (gcov_type *counters, unsigned n_counters) >> { >> for (; n_counters; counters++, n_counters--) >> - *counters += gcov_read_counter (); >> + *counters += GCOV_GET_COUNTER; > > We seem to be on transition to C++ writting style, why we don't make > GCOV_GET_COUNTER an inline function? Sure, I changed them to static inline functions. >> + >> +/*extern gcov_type gcov_read_counter_mem(); >> +extern unsigned gcov_get_merge_weight(); */ >> + >> +/* TBD: xur */ > > Forgoten hacks? What is xur? That from previous patch that I forgot to delete. Cleaned. >> +extern gcov_position_t gcov_position(); >> +extern int gcov_is_error(); >> +extern gcov_unsigned_t gcov_max_filename; >> + >> +/* We need the dumping and merge part of code in libgcov. */ >> +/*#include "libgcov-driver.c" >> +#include "libgcov-merge.c" */ > > Here too Cleaned. >> + >> +/* Verbose mode for debug. */ >> +static int verbose; >> + >> +/* Set verbose flag. */ >> +void gcov_set_verbose (void) >> +{ >> + verbose = 1; >> +} >> + >> +/* The following part is to read Gcda and reconstruct GCOV_INFO. */ >> + >> +#include "obstack.h" >> +#include <unistd.h> >> +#include <ftw.h> > > Why the includes appear after definitions/inline functions? There is no particular reason for this. I moved them to the head of the file. >> + if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum) >> + { >> + fprintf (stderr, "in %s, cfg_checksum mismatch, skipping\n", >> + info1->filename); > > It is custom for GCC related tools to use error/warning/fnotice. GCOV runtime is an exception > since it is not linked with diagnostic.c, but otherwise I think we should use it in gcov-tool, > too. Please update it. Thanks for this info. Updated all use to warning or fnotice. > > Please also add texinfo documentation for the tool, like there is gcov.texi. > The patch looks OK with these changes (or rahter I think we can solve other issues > incrementally) Included in the new patch. Could you take a look? Thanks a lot! > > Thanks, > Honza [-- Attachment #2: patch_set_4.txt --] [-- Type: text/plain, Size: 66549 bytes --] 2014-05-20 Rong Xu <xur@google.com> * gcc/gcov-io.c (gcov_position): Make avaialble to gcov-tool. (gcov_is_error): Ditto. (gcov_read_string): Ditto. (gcov_read_sync): Ditto. * gcc/gcov-io.h: Move counter defines to gcov-counter.def. * gcc/gcov-dump.c (tag_counters): Use gcov-counter.def. * gcc/coverage.c: Ditto. * gcc/gcov-tool.c: Offline gcda profile processing tool. (unlink_gcda_file): Remove one gcda file. (unlink_profile_dir): Remove gcda files from the profile path. (profile_merge): Merge two profiles in directory. (print_merge_usage_message): Print merge usage. (merge_usage): Print merge usage and exit. (do_merge): Driver for profile merge sub-command. (profile_rewrite): Rewrite profile. (print_rewrite_usage_message): Print rewrite usage. (rewrite_usage): Print rewrite usage and exit. (do_rewrite): Driver for profile rewrite sub-command. (print_usage): Print gcov-info usage and exit. (print_version): Print gcov-info version. (process_args): Process arguments. (main): Main routine for gcov-tool. * gcc/Makefile.in: Build and install gcov-tool. * gcc/gcov-counter.def: New file split from gcov-io.h. * libgcc/libgcov-driver.c (gcov_max_filename): Make available to gcov-tool. * libgcc/libgcov-merge.c (__gcov_merge_add): Replace gcov_read_counter() with a Macro. (__gcov_merge_ior): Ditto. (__gcov_merge_time_profile): Ditto. (__gcov_merge_single): Ditto. (__gcov_merge_delta): Ditto. * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag in the utility functions. (set_fn_ctrs): Utility function for reading gcda files to in-memory gcov_list object link lists. (tag_function): Ditto. (tag_blocks): Ditto. (tag_arcs): Ditto. (tag_lines): Ditto. (tag_counters): Ditto. (tag_summary): Ditto. (read_gcda_finalize): Ditto. (read_gcda_file): Ditto. (ftw_read_file): Ditto. (read_profile_dir_init): Ditto. (gcov_read_profile_dir): Ditto. (gcov_read_counter_mem): Ditto. (gcov_get_merge_weight): Ditto. (merge_wrapper): A wrapper function that calls merging handler. (gcov_merge): Merge two gcov_info objects with weights. (find_match_gcov_info): Find the matched gcov_info in the list. (gcov_profile_merge): Merge two gcov_info object lists. (__gcov_add_counter_op): Process edge profile counter values. (__gcov_ior_counter_op): Process IOR profile counter values. (__gcov_delta_counter_op): Process delta profile counter values. (__gcov_single_counter_op): Process single profile counter values. (fp_scale): Callback function for float-point scaling. (int_scale): Callback function for integer fraction scaling. (gcov_profile_scale): Scaling profile counters. (gcov_profile_normalize): Normalize profile counters. * libgcc/libgcov.h: Add headers and functions for gcov-tool use. (gcov_get_counter): New. (gcov_get_counter_target): Ditto. (struct gcov_info): Make the functions field mutable in gcov-tool compilation. * gcc/doc/gcc.texi: Include gcov-tool.texi. * gcc/doc/gcov-tool.texi: Document for gcov-tool. Index: gcc/gcov-io.c =================================================================== --- gcc/gcov-io.c (revision 210660) +++ gcc/gcov-io.c (working copy) @@ -64,7 +64,11 @@ GCOV_LINKAGE struct gcov_var } gcov_var; /* Save the current position in the gcov file. */ -static inline gcov_position_t +/* We need to expose this function when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static inline +#endif +gcov_position_t gcov_position (void) { gcc_assert (gcov_var.mode > 0); @@ -72,7 +76,11 @@ gcov_position (void) } /* Return nonzero if the error flag is set. */ -static inline int +/* We need to expose this function when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static inline +#endif +int gcov_is_error (void) { return gcov_var.file ? gcov_var.error : 1; @@ -560,11 +568,13 @@ gcov_read_counter (void) return value; } +/* We need to expose the below function when compiling for gcov-tool. */ + +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) /* Read string from coverage file. Returns a pointer to a static buffer, or NULL on empty string. You must copy the string before calling another gcov function. */ -#if !IN_LIBGCOV GCOV_LINKAGE const char * gcov_read_string (void) { @@ -641,7 +651,9 @@ gcov_read_summary (struct gcov_summary *summary) } } -#if !IN_LIBGCOV +/* We need to expose the below function when compiling for gcov-tool. */ + +#if !IN_LIBGCOV || defined (IN_GCOV_TOOL) /* Reset to a known position. BASE should have been obtained from gcov_position, LENGTH should be a record length. */ Index: gcc/gcov-io.h =================================================================== --- gcc/gcov-io.h (revision 210660) +++ gcc/gcov-io.h (working copy) @@ -240,50 +240,29 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigne /* Counters that are collected. */ -#define GCOV_COUNTER_ARCS 0 /* Arc transitions. */ -#define GCOV_COUNTERS_SUMMABLE 1 /* Counters which can be - summaried. */ -#define GCOV_FIRST_VALUE_COUNTER 1 /* The first of counters used for value - profiling. They must form a consecutive - interval and their order must match - the order of HIST_TYPEs in - value-prof.h. */ -#define GCOV_COUNTER_V_INTERVAL 1 /* Histogram of value inside an interval. */ -#define GCOV_COUNTER_V_POW2 2 /* Histogram of exact power2 logarithm - of a value. */ -#define GCOV_COUNTER_V_SINGLE 3 /* The most common value of expression. */ -#define GCOV_COUNTER_V_DELTA 4 /* The most common difference between - consecutive values of expression. */ -#define GCOV_COUNTER_V_INDIR 5 /* The most common indirect address */ -#define GCOV_COUNTER_AVERAGE 6 /* Compute average value passed to the - counter. */ -#define GCOV_COUNTER_IOR 7 /* IOR of the all values passed to - counter. */ -#define GCOV_TIME_PROFILER 8 /* Time profile collecting first run of a function */ -#define GCOV_LAST_VALUE_COUNTER 8 /* The last of counters used for value - profiling. */ -#define GCOV_COUNTERS 9 +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) COUNTER, +enum { +#include "gcov-counter.def" +GCOV_COUNTERS +}; +#undef DEF_GCOV_COUNTER +/* Counters which can be summaried. */ +#define GCOV_COUNTERS_SUMMABLE (GCOV_COUNTER_ARCS + 1) + +/* The first of counters used for value profiling. They must form a + consecutive interval and their order must match the order of + HIST_TYPEs in value-prof.h. */ +#define GCOV_FIRST_VALUE_COUNTER GCOV_COUNTERS_SUMMABLE + +/* The last of counters used for value profiling. */ +#define GCOV_LAST_VALUE_COUNTER (GCOV_COUNTERS - 1) + /* Number of counters used for value profiling. */ #define GCOV_N_VALUE_COUNTERS \ (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1) - /* A list of human readable names of the counters */ -#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \ - "delta", "indirect_call", "average", "ior", "time_profiler"} - - /* Names of merge functions for counters. */ -#define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \ - "__gcov_merge_add", \ - "__gcov_merge_add", \ - "__gcov_merge_single", \ - "__gcov_merge_delta", \ - "__gcov_merge_single", \ - "__gcov_merge_add", \ - "__gcov_merge_ior", \ - "__gcov_merge_time_profile" } - /* Convert a counter index to a tag. */ #define GCOV_TAG_FOR_COUNTER(COUNT) \ (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17)) Index: gcc/gcov-dump.c =================================================================== --- gcc/gcov-dump.c (revision 210660) +++ gcc/gcov-dump.c (working copy) @@ -422,7 +422,11 @@ static void tag_counters (const char *filename ATTRIBUTE_UNUSED, unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) { - static const char *const counter_names[] = GCOV_COUNTER_NAMES; +#define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME, + static const char *const counter_names[] = { +#include "gcov-counter.def" +}; +#undef DEF_GCOV_COUNTER unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); printf (" %s %u counts", Index: gcc/coverage.c =================================================================== --- gcc/coverage.c (revision 210660) +++ gcc/coverage.c (working copy) @@ -120,9 +120,20 @@ static unsigned bbg_file_stamp; static char *da_file_name; /* The names of merge functions for counters. */ -static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS; -static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES; +#define STR(str) #str +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) STR(__gcov_merge ## FN_TYPE), +static const char *const ctr_merge_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; +#undef DEF_GCOV_COUNTER +#undef STR +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) NAME, +static const char *const ctr_names[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; +#undef DEF_GCOV_COUNTER + /* Forward declarations. */ static void read_counts_file (void); static tree build_var (tree, tree, int); Index: gcc/gcov-tool.c =================================================================== --- gcc/gcov-tool.c (revision 0) +++ gcc/gcov-tool.c (revision 0) @@ -0,0 +1,466 @@ +/* Gcc offline profile processing tool support. */ +/* Compile this one with gcc. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "gcov-io.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <ftw.h> +#include <getopt.h> + +extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int); +extern int gcov_profile_normalize (struct gcov_info*, gcov_type); +extern int gcov_profile_scale (struct gcov_info*, float, int, int); +extern struct gcov_info* gcov_read_profile_dir (const char*, int); +extern void gcov_exit (void); +extern void set_gcov_list (struct gcov_info *); +extern void gcov_set_verbose (void); + +static int verbose; + +/* Remove file NAME if it has a gcda suffix. */ + +static int +unlink_gcda_file (const char *name, + const struct stat *status ATTRIBUTE_UNUSED, + int type ATTRIBUTE_UNUSED, + struct FTW *ftwbuf ATTRIBUTE_UNUSED) +{ + int ret = 0; + int len = strlen (name); + int len1 = strlen (GCOV_DATA_SUFFIX); + + if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1)) + remove (name); + + if (ret) + { + fnotice (stderr, "error in removing %s\n", name); + exit (FATAL_EXIT_CODE); + } + + return ret; +} + +/* Remove the gcda files in PATH recursively. */ + +static int +unlink_profile_dir (const char *path) +{ + return nftw(path, unlink_gcda_file, 64, FTW_DEPTH | FTW_PHYS); +} + +/* Merging profile D1 and D2 with weight as W1 and W2, respectively. + The result profile is written to directory OUT. + Return 0 on success. */ + +static int +profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + struct gcov_info * d2_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + if (d2) + { + d2_profile = gcov_read_profile_dir (d2, 0); + if (!d2_profile) + return 1; + + /* The actual merge: we overwrite to d1_profile. */ + ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2); + + if (ret) + return ret; + } + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage message for profile merge. */ + +static void +print_merge_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n"); +} + +static const struct option merge_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "weight", required_argument, NULL, 'w' }, + { 0, 0, 0, 0 } +}; + +/* Print merge usage and exit. */ + +static void +merge_usage (void) +{ + fnotice (stderr, "Merge subcomand usage:"); + print_merge_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile merge sub-command. */ + +static int +do_merge (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + int w1 = 1, w2 = 1; + + optind = 0; + while ((opt = getopt_long (argc, argv, "vo:w:", merge_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'w': + sscanf (optarg, "%d,%d", &w1, &w2); + if (w1 < 0 || w2 < 0) + { + fnotice (stderr, "weights need to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + break; + default: + merge_usage (); + } + } + + if (output_dir == NULL) + output_dir = "merged_profile"; + + if (argc - optind == 2) + ret = profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2); + else + merge_usage (); + + return ret; +} + +/* If N_VAL is no-zero, normalize the profile by setting the largest counter + counter value to N_VAL and scale others counters proportionally. + Otherwise, multiply the all counters by SCALE. */ + +static int +profile_rewrite (const char *d1, const char *out, long long n_val, + float scale, int n, int d) +{ + char *pwd; + int ret; + struct gcov_info * d1_profile; + + + d1_profile = gcov_read_profile_dir (d1, 0); + if (!d1_profile) + return 1; + + /* Output new profile. */ + unlink_profile_dir (out); + mkdir (out, 0755); + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (out); + gcc_assert (ret == 0); + + if (n_val) + gcov_profile_normalize (d1_profile, (gcov_type) n_val); + else + gcov_profile_scale (d1_profile, scale, n, d); + + set_gcov_list (d1_profile); + gcov_exit (); + + ret = chdir (pwd); + free (pwd); + return 0; +} + +/* Usage function for profile rewrite. */ + +static void +print_rewrite_usage_message (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + + fnotice (file, " rewrite [options] <dir> Rewrite coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output <dir> Output directory\n"); + fnotice (file, " -s, --scale <float or simple-frac> Scale the profile counters\n"); + fnotice (file, " -n, --normalize <long long> Normalize the profile\n"); +} + +static const struct option rewrite_options[] = +{ + { "verbose", no_argument, NULL, 'v' }, + { "output", required_argument, NULL, 'o' }, + { "scale", required_argument, NULL, 's' }, + { "normalize", required_argument, NULL, 'n' }, + { 0, 0, 0, 0 } +}; + +/* Print profile rewrite usage and exit. */ + +static void +rewrite_usage (void) +{ + fnotice (stderr, "Rewrite subcommand usage:"); + print_rewrite_usage_message (true); + exit (FATAL_EXIT_CODE); +} + +/* Driver for profile rewrite sub-command. */ + +static int +do_rewrite (int argc, char **argv) +{ + int opt; + int ret; + const char *output_dir = 0; + long long normalize_val = 0; + float scale = 0.0; + int numerator = 1; + int denominator = 1; + int do_scaling = 0; + + optind = 0; + while ((opt = getopt_long (argc, argv, "vo:s:n:", rewrite_options, NULL)) != -1) + { + switch (opt) + { + case 'v': + verbose = 1; + gcov_set_verbose (); + break; + case 'o': + output_dir = optarg; + break; + case 'n': + if (!do_scaling) + normalize_val = atoll (optarg); + else + fnotice (stderr, "scaling cannot co-exist with normalization," + " skipping\n"); + break; + case 's': + ret = 0; + do_scaling = 1; + if (strstr (optarg, "/")) + { + ret = sscanf (optarg, "%d/%d", &numerator, &denominator); + if (ret == 2) + { + gcc_assert (numerator >= 0); + gcc_assert (denominator > 0); + } + } + if (ret != 2) + { + ret = sscanf (optarg, "%f", &scale); + gcc_assert (ret == 1); + denominator = 0; + } + + if (scale < 0.0) + { + fnotice (stderr, "scale needs to be non-negative\n"); + exit (FATAL_EXIT_CODE); + } + if (normalize_val != 0) + { + fnotice (stderr, "normalization cannot co-exist with scaling\n"); + normalize_val = 0; + } + break; + default: + rewrite_usage (); + } + } + + if (output_dir == NULL) + output_dir = "rewrite_profile"; + + if (argc - optind == 1) + { + if (denominator > 0) + ret = profile_rewrite (argv[optind], output_dir, 0, 0.0, numerator, denominator); + else + ret = profile_rewrite (argv[optind], output_dir, normalize_val, scale, 0, 0); + } + else + rewrite_usage (); + + return ret; +} + +/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, + otherwise the output of --help. */ + +static void +print_usage (int error_p) +{ + FILE *file = error_p ? stderr : stdout; + int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; + + fnotice (file, "Usage: %s [OPTION]... SUB_COMMAND [OPTION]...\n\n", progname); + fnotice (file, "Offline tool to handle gcda counts\n\n"); + fnotice (file, " -h, --help Print this help, then exit\n"); + fnotice (file, " -v, --version Print version number, then exit\n"); + print_merge_usage_message (error_p); + print_rewrite_usage_message (error_p); + fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", + bug_report_url); + exit (status); +} + +/* Print version information and exit. */ + +static void +print_version (void) +{ + fnotice (stdout, "%s %s%s\n", progname, pkgversion_string, version_string); + fnotice (stdout, "Copyright %s 2014 Free Software Foundation, Inc.\n", + _("(C)")); + fnotice (stdout, + _("This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or \n" + "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); + exit (SUCCESS_EXIT_CODE); +} + +static const struct option options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } +}; + +/* Process args, return index to first non-arg. */ + +static int +process_args (int argc, char **argv) +{ + int opt; + + while ((opt = getopt_long (argc, argv, "+hv", options, NULL)) != -1) + { + switch (opt) + { + case 'h': + print_usage (false); + /* Print_usage will exit. */ + case 'v': + print_version (); + /* Print_version will exit. */ + default: + print_usage (true); + /* Print_usage will exit. */ + } + } + + return optind; +} + +/* Main function for gcov-tool. */ + +int +main (int argc, char **argv) +{ + const char *p; + const char *sub_command; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) + --p; + progname = p; + + xmalloc_set_program_name (progname); + + /* Unlock the stdio streams. */ + unlock_std_streams (); + + gcc_init_libintl (); + + diagnostic_initialize (global_dc, 0); + + /* Handle response files. */ + expandargv (&argc, &argv); + + process_args (argc, argv); + if (optind >= argc) + print_usage (true); + + sub_command = argv[optind]; + + if (!strcmp (sub_command, "merge")) + return do_merge (argc - optind, argv + optind); + else if (!strcmp (sub_command, "rewrite")) + return do_rewrite (argc - optind, argv + optind); + + print_usage (true); +} Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 210660) +++ gcc/Makefile.in (working copy) @@ -123,7 +123,8 @@ SUBDIRS =@subdirs@ build # Selection of languages to be made. CONFIG_LANGUAGES = @all_selected_languages@ -LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) $(CONFIG_LANGUAGES) +LANGUAGES = c gcov$(exeext) gcov-dump$(exeext) gcov-tool$(exeext) \ + $(CONFIG_LANGUAGES) # Default values for variables overridden in Makefile fragments. # CFLAGS is for the user to override to, e.g., do a cross build with -O2. @@ -196,6 +197,9 @@ GCC_WARN_CXXFLAGS = $(LOOSE_WARN) $($(@D)-warn) $( # flex output may yield harmless "no previous prototype" warnings build/gengtype-lex.o-warn = -Wno-error gengtype-lex.o-warn = -Wno-error +libgcov-util.o-warn = -Wno-error +libgcov-driver-tool.o-warn = -Wno-error +libgcov-merge-tool.o-warn = -Wno-error # All warnings have to be shut off in stage1 if the compiler used then # isn't gcc; configure determines that. WARN_CFLAGS will be either @@ -769,6 +773,7 @@ GCC_INSTALL_NAME := $(shell echo gcc|sed '$(progra GCC_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gcc|sed '$(program_transform_name)') CPP_INSTALL_NAME := $(shell echo cpp|sed '$(program_transform_name)') GCOV_INSTALL_NAME := $(shell echo gcov|sed '$(program_transform_name)') +GCOV_TOOL_INSTALL_NAME := $(shell echo gcov-tool|sed '$(program_transform_name)') # Setup the testing framework, if you have one EXPECT = `if [ -f $${rootme}/../expect/expect ] ; then \ @@ -890,7 +895,7 @@ BASIC_BLOCK_H = basic-block.h $(PREDICT_H) $(VEC_H GIMPLE_H = gimple.h gimple.def gsstruct.def pointer-set.h $(VEC_H) \ $(GGC_H) $(BASIC_BLOCK_H) $(TREE_H) tree-ssa-operands.h \ tree-ssa-alias.h $(INTERNAL_FN_H) $(HASH_TABLE_H) is-a.h -GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h +GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h gcov-counter.def RECOG_H = recog.h EMIT_RTL_H = emit-rtl.h FLAGS_H = flags.h flag-types.h $(OPTIONS_H) @@ -1490,7 +1495,7 @@ ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANG ALL_HOST_BACKEND_OBJS = $(GCC_OBJS) $(OBJS) $(OBJS-libcommon) \ $(OBJS-libcommon-target) @TREEBROWSER@ main.o c-family/cppspec.o \ $(COLLECT2_OBJS) $(EXTRA_GCC_OBJS) $(GCOV_OBJS) $(GCOV_DUMP_OBJS) \ - lto-wrapper.o + $(GCOV_TOOL_OBJS) lto-wrapper.o # This lists all host object files, whether they are included in this # compilation or not. @@ -1515,6 +1520,7 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h insn $(SPECS) collect2$(exeext) gcc-ar$(exeext) gcc-nm$(exeext) \ gcc-ranlib$(exeext) \ gcov-iov$(build_exeext) gcov$(exeext) gcov-dump$(exeext) \ + gcov-tool$(exeect) \ gengtype$(exeext) *.[0-9][0-9].* *.[si] *-checksum.c libbackend.a \ libcommon-target.a libcommon.a libgcc.mk @@ -2572,6 +2578,22 @@ GCOV_DUMP_OBJS = gcov-dump.o gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \ $(LIBS) -o $@ + +GCOV_TOOL_DEP_FILES = $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \ + $(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \ + $(srcdir)/../libgcc/libgcov-merge.c \ + $(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H) +libgcov-util.o: $(srcdir)/../libgcc/libgcov-util.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $< +libgcov-driver-tool.o: $(srcdir)/../libgcc/libgcov-driver.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + -DIN_GCOV_TOOL=1 -o $@ $< +libgcov-merge-tool.o: $(srcdir)/../libgcc/libgcov-merge.c $(GCOV_TOOL_DEP_FILES) + +$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ + -DIN_GCOV_TOOL=1 -o $@ $< +GCOV_TOOL_OBJS = gcov-tool.o libgcov-util.o libgcov-driver-tool.o libgcov-merge-tool.o +gcov-tool$(exeext): $(GCOV_TOOL_OBJS) $(LIBDEPS) + +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_TOOL_OBJS) $(LIBS) -o $@ #\f # Build the include directories. The stamp files are stmp-* rather than # s-* so that mostlyclean does not force the include directory to @@ -2800,7 +2822,7 @@ TEXI_GCC_FILES = gcc.texi gcc-common.texi gcc-vers contribute.texi compat.texi funding.texi gnu.texi gpl_v3.texi \ fdl.texi contrib.texi cppenv.texi cppopts.texi avr-mmcu.texi \ implement-c.texi implement-cxx.texi arm-neon-intrinsics.texi \ - arm-acle-intrinsics.texi + arm-acle-intrinsics.texi gcov-tool.texi # we explicitly use $(srcdir)/doc/tm.texi here to avoid confusion with # the generated tm.texi; the latter might have a more recent timestamp, @@ -3208,6 +3230,13 @@ install-common: native lang.install-common install rm -f $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ $(INSTALL_PROGRAM) gcov$(exeext) $(DESTDIR)$(bindir)/$(GCOV_INSTALL_NAME)$(exeext); \ fi +# Install gcov-tool if it was compiled. + -if [ -f gcov-tool$(exeext) ]; \ + then \ + rm -f $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + $(INSTALL_PROGRAM) \ + gcov-tool$(exeext) $(DESTDIR)$(bindir)/$(GCOV_TOOL_INSTALL_NAME)$(exeext); \ + fi # Install the driver program as $(target_noncanonical)-gcc, # $(target_noncanonical)-gcc-$(version), and also as gcc if native. Index: gcc/gcov-counter.def =================================================================== --- gcc/gcov-counter.def (revision 0) +++ gcc/gcov-counter.def (revision 0) @@ -0,0 +1,54 @@ +/* Definitions for the gcov counters in the GNU compiler. + Copyright (C) 2001-2014 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Before including this file, define a macro: + + DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) + + This macro will be expanded to all supported gcov counters, their + names, or the type of handler functions. FN_TYPE will be + expanded to a handler function, like in gcov_merge, it is + expanded to __gcov_merge ## FN_TYPE. */ + +/* Arc transitions. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_ARCS, "arcs", _add) + +/* Histogram of value inside an interval. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_INTERVAL, "interval", _add) + +/* Histogram of exact power2 logarithm of a value. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_POW2, "pow2", _add) + +/* The most common value of expression. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_SINGLE, "single", _single) + +/* The most common difference between consecutive values of expression. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_DELTA, "delta", _delta) + +/* The most common indirect address. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_V_INDIR, "indirect_call", _single) + +/* Compute average value passed to the counter. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_AVERAGE, "average", _add) + +/* IOR of the all values passed to counter. */ +DEF_GCOV_COUNTER(GCOV_COUNTER_IOR, "ior", _ior) + +/* Time profile collecting first run of a function */ +DEF_GCOV_COUNTER(GCOV_TIME_PROFILER, "time_profiler", _time_profile) Index: libgcc/libgcov-driver.c =================================================================== --- libgcc/libgcov-driver.c (revision 210652) +++ libgcc/libgcov-driver.c (working copy) @@ -77,7 +77,11 @@ set_gcov_list (struct gcov_info *head) } /* Size of the longest file name. */ -static size_t gcov_max_filename = 0; +/* We need to expose this static variable when compiling for gcov-tool. */ +#ifndef IN_GCOV_TOOL +static +#endif +size_t gcov_max_filename = 0; /* Flag when the profile has already been dumped via __gcov_dump(). */ static int gcov_dump_complete; Index: libgcc/libgcov-merge.c =================================================================== --- libgcc/libgcov-merge.c (revision 210652) +++ libgcc/libgcov-merge.c (working copy) @@ -53,7 +53,7 @@ void __gcov_merge_add (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters += gcov_read_counter (); + *counters += gcov_get_counter (); } #endif /* L_gcov_merge_add */ @@ -65,7 +65,7 @@ void __gcov_merge_ior (gcov_type *counters, unsigned n_counters) { for (; n_counters; counters++, n_counters--) - *counters |= gcov_read_counter (); + *counters |= gcov_get_counter (); } #endif @@ -81,7 +81,7 @@ __gcov_merge_time_profile (gcov_type *counters, un for (i = 0; i < n_counters; i++) { - value = gcov_read_counter (); + value = gcov_get_counter (); if (value && (!counters[i] || value < counters[i])) counters[i] = value; @@ -109,9 +109,9 @@ __gcov_merge_single (gcov_type *counters, unsigned n_measures = n_counters / 3; for (i = 0; i < n_measures; i++, counters += 3) { - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + value = gcov_get_counter_target (); + counter = gcov_get_counter (); + all = gcov_get_counter (); if (counters[0] == value) counters[1] += counter; @@ -148,10 +148,10 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_measures = n_counters / 4; for (i = 0; i < n_measures; i++, counters += 4) { - /* last = */ gcov_read_counter (); - value = gcov_read_counter (); - counter = gcov_read_counter (); - all = gcov_read_counter (); + /* last = */ gcov_get_counter (); + value = gcov_get_counter_target (); + counter = gcov_get_counter (); + all = gcov_get_counter (); if (counters[1] == value) counters[2] += counter; Index: libgcc/libgcov-util.c =================================================================== --- libgcc/libgcov-util.c (revision 0) +++ libgcc/libgcov-util.c (revision 0) @@ -0,0 +1,856 @@ +/* Utility functions for reading gcda files into in-memory + gcov_info structures and offline profile processing. */ +/* Copyright (C) 2014 Free Software Foundation, Inc. + Contributed by Rong Xu <xur@google.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + + +#define IN_GCOV_TOOL 1 + +#include "libgcov.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "demangle.h" + +/* Borrowed from basic-block.h. */ +#define RDIV(X,Y) (((X) + (Y) / 2) / (Y)) + +extern gcov_position_t gcov_position(); +extern int gcov_is_error(); +extern gcov_unsigned_t gcov_max_filename; + +/* Verbose mode for debug. */ +static int verbose; + +/* Set verbose flag. */ +void gcov_set_verbose (void) +{ + verbose = 1; +} + +/* The following part is to read Gcda and reconstruct GCOV_INFO. */ + +#include "obstack.h" +#include <unistd.h> +#include <ftw.h> + +static void tag_function (unsigned, unsigned); +static void tag_blocks (unsigned, unsigned); +static void tag_arcs (unsigned, unsigned); +static void tag_lines (unsigned, unsigned); +static void tag_counters (unsigned, unsigned); +static void tag_summary (unsigned, unsigned); + +/* The gcov_info for the first module. */ +static struct gcov_info *curr_gcov_info; +/* The gcov_info being processed. */ +static struct gcov_info *gcov_info_head; +/* This variable contains all the functions in current module. */ +static struct obstack fn_info; +/* The function being processed. */ +static struct gcov_fn_info *curr_fn_info; +/* The number of functions seen so far. */ +static unsigned num_fn_info; +/* This variable contains all the counters for current module. */ +static int k_ctrs_mask[GCOV_COUNTERS]; +/* The kind of counters that have been seen. */ +static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS]; +/* Number of kind of counters that have been seen. */ +static int k_ctrs_types; +/* The longest length of all the filenames. */ +static int max_filename_len; + +/* Merge functions for counters. */ +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) __gcov_merge ## FN_TYPE, +static gcov_merge_fn ctr_merge_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; +#undef DEF_GCOV_COUNTER + +/* Set the ctrs field in gcov_fn_info object FN_INFO. */ + +static void +set_fn_ctrs (struct gcov_fn_info *fn_info) +{ + int j = 0, i; + + for (i = 0; i < GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i] == 0) + continue; + fn_info->ctrs[j].num = k_ctrs[i].num; + fn_info->ctrs[j].values = k_ctrs[i].values; + j++; + } + if (k_ctrs_types == 0) + k_ctrs_types = j; + else + gcc_assert (j == k_ctrs_types); +} + +/* For each tag in gcda file, we have an entry here. + TAG is the tag value; NAME is the tag name; and + PROC is the handler function. */ + +typedef struct tag_format +{ + unsigned tag; + char const *name; + void (*proc) (unsigned, unsigned); +} tag_format_t; + +/* Handler table for various Tags. */ + +static const tag_format_t tag_table[] = +{ + {0, "NOP", NULL}, + {0, "UNKNOWN", NULL}, + {0, "COUNTERS", tag_counters}, + {GCOV_TAG_FUNCTION, "FUNCTION", tag_function}, + {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks}, + {GCOV_TAG_ARCS, "ARCS", tag_arcs}, + {GCOV_TAG_LINES, "LINES", tag_lines}, + {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary}, + {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary}, + {0, NULL, NULL} +}; + +/* Handler for reading function tag. */ + +static void +tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + int i; + + /* write out previous fn_info. */ + if (num_fn_info) + { + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + } + + /* Here we over allocate a bit, using GCOV_COUNTERS instead of the actual active + counter types. */ + curr_fn_info = (struct gcov_fn_info *) xcalloc (sizeof (struct gcov_fn_info) + + GCOV_COUNTERS * sizeof (struct gcov_ctr_info), 1); + + for (i = 0; i < GCOV_COUNTERS; i++) + k_ctrs[i].num = 0; + k_ctrs_types = 0; + + curr_fn_info->key = curr_gcov_info; + curr_fn_info->ident = gcov_read_unsigned (); + curr_fn_info->lineno_checksum = gcov_read_unsigned (); + curr_fn_info->cfg_checksum = gcov_read_unsigned (); + num_fn_info++; + + if (verbose) + fnotice (stdout, "tag one function id=%d\n", curr_fn_info->ident); +} + +/* Handler for reading block tag. */ + +static void +tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading flow arc tag. */ + +static void +tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading line tag. */ + +static void +tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + /* TBD: gcov-tool currently does not handle gcno files. Assert here. */ + gcc_unreachable (); +} + +/* Handler for reading counters array tag with value as TAG and length of LENGTH. */ + +static void +tag_counters (unsigned tag, unsigned length) +{ + unsigned n_counts = GCOV_TAG_COUNTER_NUM (length); + gcov_type *values; + unsigned ix; + unsigned tag_ix; + + tag_ix = GCOV_COUNTER_FOR_TAG (tag); + gcc_assert (tag_ix < GCOV_COUNTERS); + k_ctrs_mask [tag_ix] = 1; + gcc_assert (k_ctrs[tag_ix].num == 0); + k_ctrs[tag_ix].num = n_counts; + + k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type)); + gcc_assert (values); + + for (ix = 0; ix != n_counts; ix++) + values[ix] = gcov_read_counter (); +} + +/* Handler for reading summary tag. */ + +static void +tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) +{ + struct gcov_summary summary; + + gcov_read_summary (&summary); +} + +/* This function is called at the end of reading a gcda file. + It flushes the contents in curr_fn_info to gcov_info object OBJ_INFO. */ + +static void +read_gcda_finalize (struct gcov_info *obj_info) +{ + int i; + + set_fn_ctrs (curr_fn_info); + obstack_ptr_grow (&fn_info, curr_fn_info); + + /* We set the following fields: merge, n_functions, and functions. */ + obj_info->n_functions = num_fn_info; + obj_info->functions = (const struct gcov_fn_info**) obstack_finish (&fn_info); + + /* wrap all the counter array. */ + for (i=0; i< GCOV_COUNTERS; i++) + { + if (k_ctrs_mask[i]) + obj_info->merge[i] = ctr_merge_functions[i]; + } +} + +/* Read the content of a gcda file FILENAME, and return a gcov_info data structure. + Program level summary CURRENT_SUMMARY will also be updated. */ + +static struct gcov_info * +read_gcda_file (const char *filename) +{ + unsigned tags[4]; + unsigned depth = 0; + unsigned magic, version; + struct gcov_info *obj_info; + int i; + + for (i=0; i< GCOV_COUNTERS; i++) + k_ctrs_mask[i] = 0; + k_ctrs_types = 0; + + if (!gcov_open (filename)) + { + fnotice (stderr, "%s:cannot open\n", filename); + return NULL; + } + + /* Read magic. */ + magic = gcov_read_unsigned (); + if (magic != GCOV_DATA_MAGIC) + { + fnotice (stderr, "%s:not a gcov data file\n", filename); + gcov_close (); + return NULL; + } + + /* Read version. */ + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + fnotice (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION); + gcov_close (); + return NULL; + } + + /* Instantiate a gcov_info object. */ + curr_gcov_info = obj_info = (struct gcov_info *) xcalloc (sizeof (struct gcov_info) + + sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1); + + obj_info->version = version; + obstack_init (&fn_info); + num_fn_info = 0; + curr_fn_info = 0; + { + char *str_dup = (char*) xmalloc (strlen (filename) + 1); + int len; + + strcpy (str_dup, filename); + obj_info->filename = str_dup; + if ((len = strlen (filename)) > max_filename_len) + max_filename_len = len; + } + + /* Read stamp. */ + obj_info->stamp = gcov_read_unsigned (); + + while (1) + { + gcov_position_t base; + unsigned tag, length; + tag_format_t const *format; + unsigned tag_depth; + int error; + unsigned mask; + + tag = gcov_read_unsigned (); + if (!tag) + break; + length = gcov_read_unsigned (); + base = gcov_position (); + mask = GCOV_TAG_MASK (tag) >> 1; + for (tag_depth = 4; mask; mask >>= 8) + { + if (((mask & 0xff) != 0xff)) + { + warning (0, "%s:tag `%08x' is invalid\n", filename, tag); + break; + } + tag_depth--; + } + for (format = tag_table; format->name; format++) + if (format->tag == tag) + goto found; + format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1]; + found:; + if (tag) + { + if (depth && depth < tag_depth) + { + if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag)) + warning (0, "%s:tag `%08x' is incorrectly nested\n", + filename, tag); + } + depth = tag_depth; + tags[depth - 1] = tag; + } + + if (format->proc) + { + unsigned long actual_length; + + (*format->proc) (tag, length); + + actual_length = gcov_position () - base; + if (actual_length > length) + warning (0, "%s:record size mismatch %lu bytes overread\n", + filename, actual_length - length); + else if (length > actual_length) + warning (0, "%s:record size mismatch %lu bytes unread\n", + filename, length - actual_length); + } + + gcov_sync (base, length); + if ((error = gcov_is_error ())) + { + warning (0, error < 0 ? "%s:counter overflow at %lu\n" : + "%s:read error at %lu\n", filename, + (long unsigned) gcov_position ()); + break; + } + } + + read_gcda_finalize (obj_info); + gcov_close (); + + return obj_info; +} + +/* This will be called by ftw(). It opens and read a gcda file FILENAME. + Return a non-zero value to stop the tree walk. */ + +static int +ftw_read_file (const char *filename, + const struct stat *status ATTRIBUTE_UNUSED, + int type) +{ + int filename_len; + int suffix_len; + struct gcov_info *obj_info; + + /* Only read regular files. */ + if (type != FTW_F) + return 0; + + filename_len = strlen (filename); + suffix_len = strlen (GCOV_DATA_SUFFIX); + + if (filename_len <= suffix_len) + return 0; + + if (strcmp(filename + filename_len - suffix_len, GCOV_DATA_SUFFIX)) + return 0; + + if (verbose) + fnotice (stderr, "reading file: %s\n", filename); + + obj_info = read_gcda_file (filename); + + obj_info->next = gcov_info_head; + gcov_info_head = obj_info; + + return 0; +} + +/* Initializer for reading a profile dir. */ + +static inline void +read_profile_dir_init (void) +{ + gcov_info_head = 0; +} + +/* Driver for read a profile directory and convert into gcov_info list in memory. + Return NULL on error, + Return the head of gcov_info list on success. + Note the file static variable GCOV_MAX_FILENAME is also set. */ + +struct gcov_info * +gcov_read_profile_dir (const char* dir_name, int recompute_summary ATTRIBUTE_UNUSED) +{ + char *pwd; + int ret; + + read_profile_dir_init (); + + if (access (dir_name, R_OK) != 0) + { + fnotice (stderr, "cannot access directory %s\n", dir_name); + return NULL; + } + pwd = getcwd (NULL, 0); + gcc_assert (pwd); + ret = chdir (dir_name); + if (ret !=0) + { + fnotice (stderr, "%s is not a directory\n", dir_name); + return NULL; + } + ftw (".", ftw_read_file, 50); + ret = chdir (pwd); + free (pwd); + + + /* gcov_max_filename is defined in libgcov.c that records the + max filename len. We need to set it here to allocate the + array for dumping. */ + gcov_max_filename = max_filename_len; + + return gcov_info_head;; +} + +/* This part of the code is to merge profile counters. */ + +static gcov_type *gcov_value_buf; +static gcov_unsigned_t gcov_value_buf_size; +static gcov_unsigned_t gcov_value_buf_pos; +static unsigned gcov_merge_weight; + +/* Read a counter value from gcov_value_buf array. */ + +gcov_type +gcov_read_counter_mem (void) +{ + gcov_type ret; + gcc_assert (gcov_value_buf_pos < gcov_value_buf_size); + ret = *(gcov_value_buf + gcov_value_buf_pos); + ++gcov_value_buf_pos; + return ret; +} + +/* Return the recorded merge weight. */ + +unsigned +gcov_get_merge_weight (void) +{ + return gcov_merge_weight; +} + +/* A wrapper function for merge functions. It sets up the + value buffer and weights and then calls the merge function. */ + +static void +merge_wrapper (gcov_merge_fn f, gcov_type *v1, gcov_unsigned_t n, + gcov_type *v2, unsigned w) +{ + gcov_value_buf = v2; + gcov_value_buf_pos = 0; + gcov_value_buf_size = n; + gcov_merge_weight = w; + (*f) (v1, n); +} + +/* Offline tool to manipulate profile data. + This tool targets on matched profiles. But it has some tolerance on + unmatched profiles. + When merging p1 to p2 (p2 is the dst), + * m.gcda in p1 but not in p2: append m.gcda to p2 with specified weight; + emit warning + * m.gcda in p2 but not in p1: keep m.gcda in p2 and multiply by + specified weight; emit warning. + * m.gcda in both p1 and p2: + ** p1->m.gcda->f checksum matches p2->m.gcda->f: simple merge. + ** p1->m.gcda->f checksum does not matches p2->m.gcda->f: keep + p2->m.gcda->f and + drop p1->m.gcda->f. A warning is emitted. */ + +/* Add INFO2's counter to INFO1, multiplying by weight W. */ + +static int +gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w) +{ + unsigned f_ix; + unsigned n_functions = info1->n_functions; + int has_mismatch = 0; + + gcc_assert (info2->n_functions == n_functions); + for (f_ix = 0; f_ix < n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr1 = info1->functions[f_ix]; + const struct gcov_fn_info *gfi_ptr2 = info2->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr1, *ci_ptr2; + + if (!gfi_ptr1 || gfi_ptr1->key != info1) + continue; + if (!gfi_ptr2 || gfi_ptr2->key != info2) + continue; + + if (gfi_ptr1->cfg_checksum != gfi_ptr2->cfg_checksum) + { + fnotice (stderr, "in %s, cfg_checksum mismatch, skipping\n", + info1->filename); + has_mismatch = 1; + continue; + } + ci_ptr1 = gfi_ptr1->ctrs; + ci_ptr2 = gfi_ptr2->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge1 = info1->merge[t_ix]; + gcov_merge_fn merge2 = info2->merge[t_ix]; + + gcc_assert (merge1 == merge2); + if (!merge1) + continue; + gcc_assert (ci_ptr1->num == ci_ptr2->num); + merge_wrapper (merge1, ci_ptr1->values, ci_ptr1->num, ci_ptr2->values, w); + ci_ptr1++; + ci_ptr2++; + } + } + + return has_mismatch; +} + +/* Find and return the match gcov_info object for INFO from ARRAY. + SIZE is the length of ARRAY. + Return NULL if there is no match. */ + +static struct gcov_info * +find_match_gcov_info (struct gcov_info **array, int size, struct gcov_info *info) +{ + struct gcov_info *gi_ptr; + struct gcov_info *ret = NULL; + int i; + + for (i = 0; i < size; i++) + { + gi_ptr = array[i]; + if (gi_ptr == 0) + continue; + if (!strcmp (gi_ptr->filename, info->filename)) + { + ret = gi_ptr; + array[i] = 0; + break; + } + } + + if (ret && ret->n_functions != info->n_functions) + { + fnotice (stderr, "mismatched profiles in %s (%d functions" + " vs %d functions)\n", + ret->filename, + ret->n_functions, + info->n_functions); + ret = NULL; + } + return ret; +} + +/* Merge the list of gcov_info objects from SRC_PROFILE to TGT_PROFILE. + Return 0 on success: without mismatch. + Reutrn 1 on error. */ + +int +gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile, + int w1, int w2) +{ + struct gcov_info *gi_ptr; + struct gcov_info **tgt_infos; + struct gcov_info *tgt_tail; + struct gcov_info **in_src_not_tgt; + unsigned tgt_cnt = 0, src_cnt = 0; + unsigned unmatch_info_cnt = 0; + unsigned int i; + + for (gi_ptr = tgt_profile; gi_ptr; gi_ptr = gi_ptr->next) + tgt_cnt++; + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + src_cnt++; + tgt_infos = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * tgt_cnt); + gcc_assert (tgt_infos); + in_src_not_tgt = (struct gcov_info **) xmalloc (sizeof (struct gcov_info *) + * src_cnt); + gcc_assert (in_src_not_tgt); + + for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++) + tgt_infos[i] = gi_ptr; + + tgt_tail = tgt_infos[tgt_cnt - 1]; + + /* First pass on tgt_profile, we multiply w1 to all counters. */ + if (w1 > 1) + { + for (i = 0; i < tgt_cnt; i++) + gcov_merge (tgt_infos[i], tgt_infos[i], w1-1); + } + + /* Second pass, add src_profile to the tgt_profile. */ + for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next) + { + struct gcov_info *gi_ptr1; + + gi_ptr1 = find_match_gcov_info (tgt_infos, tgt_cnt, gi_ptr); + if (gi_ptr1 == NULL) + { + in_src_not_tgt[unmatch_info_cnt++] = gi_ptr; + continue; + } + gcov_merge (gi_ptr1, gi_ptr, w2); + } + + /* For modules in src but not in tgt. We adjust the counter and append. */ + for (i = 0; i < unmatch_info_cnt; i++) + { + gi_ptr = in_src_not_tgt[i]; + gcov_merge (gi_ptr, gi_ptr, w2 - 1); + tgt_tail->next = gi_ptr; + tgt_tail = gi_ptr; + } + + return 0; +} + +typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*); + +/* Performing FN upon arc counters. */ + +static void +__gcov_add_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + for (; n_counters; counters++, n_counters--) + { + gcov_type val = *counters; + *counters = fn(val, data1, data2); + } +} + +/* Performing FN upon ior counters. */ + +static void +__gcov_ior_counter_op (gcov_type *counters ATTRIBUTE_UNUSED, + unsigned n_counters ATTRIBUTE_UNUSED, + counter_op_fn fn ATTRIBUTE_UNUSED, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +/* Performing FN upon time-profile counters. */ + +static void +__gcov_time_profile_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + __gcov_add_counter_op (counters, n_counters, fn, data1, data2); +} + +/* Performaing FN upon delta counters. */ + +static void +__gcov_delta_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 4)); + n_measures = n_counters / 4; + for (i = 0; i < n_measures; i++, counters += 4) + { + counters[2] = fn (counters[2], data1, data2); + counters[3] = fn (counters[3], data1, data2); + } +} + +/* Performing FN upon single counters. */ + +static void +__gcov_single_counter_op (gcov_type *counters, unsigned n_counters, + counter_op_fn fn, void *data1, void *data2) +{ + unsigned i, n_measures; + + gcc_assert (!(n_counters % 3)); + n_measures = n_counters / 3; + for (i = 0; i < n_measures; i++, counters += 3) + { + counters[1] = fn (counters[1], data1, data2); + counters[2] = fn (counters[2], data1, data2); + } +} + +/* Scaling the counter value V by multiplying *(float*) DATA1. */ + +static gcov_type +fp_scale (gcov_type v, void *data1, void *data2 ATTRIBUTE_UNUSED) +{ + float f = *(float *) data1; + return (gcov_type) (v * f); +} + +/* Scaling the counter value V by multiplying DATA2/DATA1. */ + +static gcov_type +int_scale (gcov_type v, void *data1, void *data2) +{ + int n = *(int *) data1; + int d = *(int *) data2; + return (gcov_type) ( RDIV (v,d) * n); +} + +/* Type of function used to process counters. */ +typedef void (*gcov_counter_fn) (gcov_type *, gcov_unsigned_t, + counter_op_fn, void *, void *); + +/* Function array to process profile counters. */ +#define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) \ + __gcov ## FN_TYPE ## _counter_op, +static gcov_counter_fn ctr_functions[GCOV_COUNTERS] = { +#include "gcov-counter.def" +}; +#undef DEF_GCOV_COUNTER + +/* Driver for scaling profile counters. */ + +int +gcov_profile_scale (struct gcov_info *profile, float scale_factor, int n, int d) +{ + struct gcov_info *gi_ptr; + unsigned f_ix; + + if (verbose) + fnotice (stdout, "scale_factor is %f or %d/%d\n", scale_factor, n, d); + + /* Scaling the counters. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++) + { + gcov_merge_fn merge = gi_ptr->merge[t_ix]; + + if (!merge) + continue; + if (d == 0) + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + fp_scale, &scale_factor, NULL); + else + (*ctr_functions[t_ix]) (ci_ptr->values, ci_ptr->num, + int_scale, &n, &d); + ci_ptr++; + } + } + + return 0; +} + +/* Driver to normalize profile counters. */ + +int +gcov_profile_normalize (struct gcov_info *profile, gcov_type max_val) +{ + struct gcov_info *gi_ptr; + gcov_type curr_max_val = 0; + unsigned f_ix; + unsigned int i; + float scale_factor; + + /* Find the largest count value. */ + for (gi_ptr = profile; gi_ptr; gi_ptr = gi_ptr->next) + for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) + { + unsigned t_ix; + const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix]; + const struct gcov_ctr_info *ci_ptr; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = gfi_ptr->ctrs; + for (t_ix = 0; t_ix < 1; t_ix++) + { + for (i = 0; i < ci_ptr->num; i++) + if (ci_ptr->values[i] > curr_max_val) + curr_max_val = ci_ptr->values[i]; + ci_ptr++; + } + } + + scale_factor = (float)max_val / curr_max_val; + if (verbose) + fnotice (stdout, "max_val is %lld\n", (long long) curr_max_val); + + return gcov_profile_scale (profile, scale_factor, 0, 0); +} Index: libgcc/libgcov.h =================================================================== --- libgcc/libgcov.h (revision 210652) +++ libgcc/libgcov.h (working copy) @@ -33,6 +33,10 @@ #define xcalloc calloc #endif +#ifndef IN_GCOV_TOOL +/* About the target. */ +/* This path will be used by libgcov runtime. */ + #include "tconfig.h" #include "tsystem.h" #include "coretypes.h" @@ -79,6 +83,83 @@ typedef unsigned gcov_type_unsigned __attribute__ #define GCOV_LOCKED 0 #endif +/* "Counts" stored in gcda files can be a real counter value, or + an target address. When differentiate these two types because + when manipulating counts, we should only change real counter values, + rather target addresses. + This version is for reading count values in libgcov runtime: + we read from gcda files. */ +static inline gcov_type +gcov_get_counter (void) +{ + return gcov_read_counter (); +} + +/* This version is for reading count target values in libgcov runtime: + we read from gcda files. */ + +static inline gcov_type +gcov_get_counter_target (void) +{ + return gcov_read_counter (); +} + +#else /* IN_GCOV_TOOL */ +/* About the host. */ +/* This path will be compiled for the host and linked into + gcov-tool binary. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" + +typedef unsigned gcov_unsigned_t; +typedef unsigned gcov_position_t; +/* gcov_type is typedef'd elsewhere for the compiler */ +#if defined (HOST_HAS_F_SETLKW) +#define GCOV_LOCKED 1 +#else +#define GCOV_LOCKED 0 +#endif + +/* Some Macros specific to gcov-tool. */ + +#define L_gcov 1 +#define L_gcov_merge_add 1 +#define L_gcov_merge_single 1 +#define L_gcov_merge_delta 1 +#define L_gcov_merge_ior 1 +#define L_gcov_merge_time_profile 1 + +extern gcov_type gcov_read_counter_mem(); +extern unsigned gcov_get_merge_weight(); + +/* Rerfer the comment to same functions above. This version is for gcov-tool. + We read the value from memory and multiply it by the merge weight. */ + +static inline gcov_type +gcov_get_counter (void) +{ + return gcov_read_counter_mem () * gcov_get_merge_weight (); +} + +/* Rerfer the comment to same functions above. This version is for gcov-tool. + We read the value from memory and we do NOT multiply it by the merge + weight. */ + +static inline gcov_type +gcov_get_counter_target (void) +{ + return gcov_read_counter_mem (); +} + +/* Make certian internal functions/variables in libgcov available for + gcov-tool access. */ +#define GCOV_TOOL_LINKAGE + +#endif /* !IN_GCOV_TOOL */ + #if defined(inhibit_libc) #define IN_LIBGCOV (-1) #else @@ -159,8 +240,13 @@ struct gcov_info unused) */ unsigned n_functions; /* number of functions */ + +#ifndef IN_GCOV_TOOL const struct gcov_fn_info *const *functions; /* pointer to pointers - to function information */ + to function information */ +#else + const struct gcov_fn_info **functions; +#endif /* !IN_GCOV_TOOL */ }; /* Register a new object file module. */ Index: gcc/doc/gcc.texi =================================================================== --- gcc/doc/gcc.texi (revision 210660) +++ gcc/doc/gcc.texi (working copy) @@ -66,6 +66,7 @@ Texts being (a) (see below), and with the Back-Cov * gcc: (gcc). The GNU Compiler Collection. * g++: (gcc). The GNU C++ compiler. * gcov: (gcc) Gcov. @command{gcov}---a test coverage program. +* gcov-tool: (gcc) Gcov-tool. @command{gcov-tool}---an offline gcda profile processing program. @end direntry This file documents the use of the GNU compilers. @sp 1 @@ -138,6 +139,7 @@ Introduction, gccint, GNU Compiler Collection (GCC * Objective-C:: GNU Objective-C runtime features. * Compatibility:: Binary Compatibility * Gcov:: @command{gcov}---a test coverage program. +* Gcov-tool:: @command{gcov-tool}---an offline gcda profile processing program. * Trouble:: If you have trouble using GCC. * Bugs:: How, why and where to report bugs. * Service:: How To Get Help with GCC @@ -164,6 +166,7 @@ Introduction, gccint, GNU Compiler Collection (GCC @include objc.texi @include compat.texi @include gcov.texi +@include gcov-tool.texi @include trouble.texi @include bugreport.texi @include service.texi Index: gcc/doc/gcov-tool.texi =================================================================== --- gcc/doc/gcov-tool.texi (revision 0) +++ gcc/doc/gcov-tool.texi (revision 0) @@ -0,0 +1,173 @@ +@c Copyright (C) 1996-2014 Free Software Foundation, Inc. +@c This is part of the GCC manual. +@c For copying conditions, see the file gcc.texi. + +@ignore +@c man begin COPYRIGHT +Copyright @copyright{} 1996-2014 Free Software Foundation, Inc. + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with the +Invariant Sections being ``GNU General Public License'' and ``Funding +Free Software'', the Front-Cover texts being (a) (see below), and with +the Back-Cover Texts being (b) (see below). A copy of the license is +included in the gfdl(7) man page. + +(a) The FSF's Front-Cover Text is: + + A GNU Manual + +(b) The FSF's Back-Cover Text is: + + You have freedom to copy and modify this GNU Manual, like GNU + software. Copies published by the Free Software Foundation raise + funds for GNU development. +@c man end +@c Set file name and title for the man page. +@setfilename gcov-tool +@settitle offline gcda profile processing tool +@end ignore + +@node Gcov-tool +@chapter @command{gcov-tool}---an offline gcda profile processing tool + +@command{gcov-tool} is a tool you can use in conjunction with GCC to +manipulate or process gcda profile files offline. + +@menu +* Gcov-tool Intro:: Introduction to gcov-tool. +* Invoking Gcov-tool:: How to use gcov-tool. +@end menu + +@node Gcov-tool Intro +@section Introduction to @command{gcov-tool} +@c man begin DESCRIPTION + +@command{gcov-tool} is an offline tool to process gcc's gcda profile files. + +Current gcov-tool supports the following functionalities: + +@itemize @bullet +@item +merge two sets of profiles with weights. + +@item +read one set of profile and rewrite profile contents. One can scale or +normalize the count values. +@end itemize + +Note that for the merging operation, this profile generated offline may +contain a slight different values from the online merged profile. Here are +a list of typical differences: + +@itemize @bullet +@item +histogram difference: This offline tool recomputes the histogram after merging +the counters. The resulting histogram, therefore, is precise. The online +merging does not have this capability -- the histogram is merged from two +histograms and the result is an approximation. + +@item +summary checksum difference: Summary checksum uses a CRC32 operation. The value +depends on the link list order of gcov-info objects. This order is different in +gcov-tool from that in the online merge. It's expected to have different +summary checksums. It does not really matter as the compiler does not use this +checksum anywhere. + +@item +value profile counter values difference: Some counter values for value profile +are runtime dependent, like heap addresses. It's normal to see some difference +in these kind of counters. +@end itemize + +@c man end + +@node Invoking Gcov-tool +@section Invoking @command{gcov-tool} + +@smallexample +gcov-tool @r{[}@var{global-options}@r{]} SUB_COMMAND +@r{[}@var{sub_command-options}@r{]} @var{profile_dir} +@end smallexample + +@command{gcov-tool} accepts the following options: + +@ignore +@c man begin SYNOPSIS +gcov-tool [@option{-v}|@option{--version}] [@option{-h}|@option{--help}] + +gcov-tool merge [merge-options] @var{directory1} @var{directory2} + [@option{-v}|@option{--verbose}] + [@option{-o}|@option{ --output} @var{directory}] + [@option{-w}|@option{--weight} @var{w1,w2}] + +gcov-tool rewrite [rewrite-options] @var{directory} + [@option{-v}|@option{--verbose}] + [@option{-o}|@option{--output} @var{directory}] + [@option{-s}|@option{--scale} @var{float_or_simple-frac_value}] + [@option{-n}|@option{--normalize} @var{long_long_value}] +@c man end +@c man begin SEEALSO +gpl(7), gfdl(7), fsf-funding(7), gcc(1), gcov(1) and the Info entry for +@file{gcc}. +@c man end +@end ignore + +@c man begin OPTIONS +@table @gcctabopt +@item -h +@itemx --help +Display help about using @command{gcov-tool} (on the standard output), and +exit without doing any further processing. + +@item -v +@itemx --version +Display the @command{gcov-tool} version number (on the standard output), +and exit without doing any further processing. + +@item merge +Merge two profile directories. + +@table @gcctabopt +@item -v +@itemx --verbose +Set the verbose mode. + +@item -o @var{directory} +@itemx --output @var{directory} +Set the output profile directory. Default output directory name is +@var{merged_profile}. + +@item -w @var{w1,w2} +@itemx --weight @var{w1,w2} +Set the merge weights of the @var{directory1} and @var{directory2}, +respectively. The default weights are 1 for both. +@end table + +@item rewrite +Read the specified profile directory and rewrite to a new directory. + +@table @gcctabopt +@item -v +@itemx --verbose +Set the verbose mode. + +@item -o @var{directory} +@itemx --output @var{directory} +Set the output profile directory. Default output name is @var{rewrite_profile}. + +@item -s @var{float_or_simple-frac_value} +@itemx --scale @var{float_or_simple-frac_value} +Scale the profile counters. The specified value can be in floating point value, +or simple fraction value form, such 1, 2, 2/3, and 5/3. + +@item -n @var{long_long_value} +@itemx --normalize <long_long_value> +Normalize the profile. The specified value is the max counter value +in the new profile. + +@end table +@end table + +@c man end ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH] offline gcda profile processing tool 2014-05-20 22:59 ` Rong Xu @ 2014-05-25 23:43 ` Jan Hubicka 0 siblings, 0 replies; 23+ messages in thread From: Jan Hubicka @ 2014-05-25 23:43 UTC (permalink / raw) To: Rong Xu Cc: Jan Hubicka, GCC Patches, Xinliang David Li, Teresa Johnson, Dehao Chen > >> /* Size of the longest file name. */ > >> -static size_t gcov_max_filename = 0; > >> +/* We need to expose this static variable when compiling for gcov-tool. */ > >> +#ifndef IN_GCOV_TOOL > >> +static > >> +#endif > >> +size_t gcov_max_filename = 0; > > > > > > Why max_filename needs to be exported? > > For code efficiency, we allocate the gi_filename buffer only once in > the dumping, using the maximum filename length, which > is set in gcov_init(). For gcov-tool, we don't have gcov_init, and we > set the value when reading the gcda files. > Now libgcov-driver.o is a separated compilation unit. I need to make > then static gi_filename global to allow the access. Note sure if code efficiency wins here over convolutin APIs, but I guess it is OK. Please update comment to explain how it is used, so we keep track of it more easily. > 2014-05-20 Rong Xu <xur@google.com> > > * gcc/gcov-io.c (gcov_position): Make avaialble to gcov-tool. > (gcov_is_error): Ditto. > (gcov_read_string): Ditto. > (gcov_read_sync): Ditto. > * gcc/gcov-io.h: Move counter defines to gcov-counter.def. > * gcc/gcov-dump.c (tag_counters): Use gcov-counter.def. > * gcc/coverage.c: Ditto. > * gcc/gcov-tool.c: Offline gcda profile processing tool. > (unlink_gcda_file): Remove one gcda file. > (unlink_profile_dir): Remove gcda files from the profile path. > (profile_merge): Merge two profiles in directory. > (print_merge_usage_message): Print merge usage. > (merge_usage): Print merge usage and exit. > (do_merge): Driver for profile merge sub-command. > (profile_rewrite): Rewrite profile. > (print_rewrite_usage_message): Print rewrite usage. > (rewrite_usage): Print rewrite usage and exit. > (do_rewrite): Driver for profile rewrite sub-command. > (print_usage): Print gcov-info usage and exit. > (print_version): Print gcov-info version. > (process_args): Process arguments. > (main): Main routine for gcov-tool. > * gcc/Makefile.in: Build and install gcov-tool. > * gcc/gcov-counter.def: New file split from gcov-io.h. > * libgcc/libgcov-driver.c (gcov_max_filename): Make available > to gcov-tool. > * libgcc/libgcov-merge.c (__gcov_merge_add): Replace > gcov_read_counter() with a Macro. > (__gcov_merge_ior): Ditto. > (__gcov_merge_time_profile): Ditto. > (__gcov_merge_single): Ditto. > (__gcov_merge_delta): Ditto. > * libgcc/libgcov-util.c (void gcov_set_verbose): Set the verbose flag > in the utility functions. > (set_fn_ctrs): Utility function for reading gcda files to in-memory > gcov_list object link lists. > (tag_function): Ditto. > (tag_blocks): Ditto. > (tag_arcs): Ditto. > (tag_lines): Ditto. > (tag_counters): Ditto. > (tag_summary): Ditto. > (read_gcda_finalize): Ditto. > (read_gcda_file): Ditto. > (ftw_read_file): Ditto. > (read_profile_dir_init): Ditto. > (gcov_read_profile_dir): Ditto. > (gcov_read_counter_mem): Ditto. > (gcov_get_merge_weight): Ditto. > (merge_wrapper): A wrapper function that calls merging handler. > (gcov_merge): Merge two gcov_info objects with weights. > (find_match_gcov_info): Find the matched gcov_info in the list. > (gcov_profile_merge): Merge two gcov_info object lists. > (__gcov_add_counter_op): Process edge profile counter values. > (__gcov_ior_counter_op): Process IOR profile counter values. > (__gcov_delta_counter_op): Process delta profile counter values. > (__gcov_single_counter_op): Process single profile counter values. > (fp_scale): Callback function for float-point scaling. > (int_scale): Callback function for integer fraction scaling. > (gcov_profile_scale): Scaling profile counters. > (gcov_profile_normalize): Normalize profile counters. > * libgcc/libgcov.h: Add headers and functions for gcov-tool use. > (gcov_get_counter): New. > (gcov_get_counter_target): Ditto. > (struct gcov_info): Make the functions field mutable in gcov-tool > compilation. > * gcc/doc/gcc.texi: Include gcov-tool.texi. > * gcc/doc/gcov-tool.texi: Document for gcov-tool. OK, with changes bellow. I apologize for the delay - it is a long patch and I am bit swamped in tasks these days. > Index: gcc/gcov-tool.c > =================================================================== > --- gcc/gcov-tool.c (revision 0) > +++ gcc/gcov-tool.c (revision 0) > @@ -0,0 +1,466 @@ > +/* Gcc offline profile processing tool support. */ > +/* Compile this one with gcc. */ What "compile this one with gcc" means? :) > + > +static int verbose; Perhaps in C++ times, it could be bool and have comment in front of it (per coding standards) > + > +/* Remove file NAME if it has a gcda suffix. */ > + > +static int > +unlink_gcda_file (const char *name, > + const struct stat *status ATTRIBUTE_UNUSED, > + int type ATTRIBUTE_UNUSED, > + struct FTW *ftwbuf ATTRIBUTE_UNUSED) > +{ > + int ret = 0; > + int len = strlen (name); > + int len1 = strlen (GCOV_DATA_SUFFIX); > + > + if (len > len1 && !strncmp (len -len1 + name, GCOV_DATA_SUFFIX, len1)) > + remove (name); > + > + if (ret) ret is not set here. > + { > + fnotice (stderr, "error in removing %s\n", name); > + exit (FATAL_EXIT_CODE); > + } I think you want to use fatal here, that exists for you. > + > +/* Merging profile D1 and D2 with weight as W1 and W2, respectively. > + The result profile is written to directory OUT. > + Return 0 on success. */ > + > +static int > +profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2) > +{ > + char *pwd; > + int ret; > + struct gcov_info * d1_profile; > + struct gcov_info * d2_profile; > + > + > + d1_profile = gcov_read_profile_dir (d1, 0); > + if (!d1_profile) > + return 1; > + > + if (d2) > + { > + d2_profile = gcov_read_profile_dir (d2, 0); > + if (!d2_profile) > + return 1; > + > + /* The actual merge: we overwrite to d1_profile. */ > + ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2); > + > + if (ret) > + return ret; > + } > + > + /* Output new profile. */ > + unlink_profile_dir (out); > + mkdir (out, 0755); > + pwd = getcwd (NULL, 0); > + gcc_assert (pwd); > + ret = chdir (out); > + gcc_assert (ret == 0); > + > + set_gcov_list (d1_profile); > + gcov_exit (); > + > + ret = chdir (pwd); ret is unused here, I suppose we should fatal if it fails. I wonder how safe this is considered in command tools (i.e. change directory and go back) > + free (pwd); > + return 0; > +} > + > +/* Usage message for profile merge. */ > + > +static void > +print_merge_usage_message (int error_p) > +{ > + FILE *file = error_p ? stderr : stdout; > + > + fnotice (file, " merge [options] <dir1> <dir2> Merge coverage file contents\n"); > + fnotice (file, " -v, --verbose Verbose mode\n"); > + fnotice (file, " -o, --output <dir> Output directory\n"); > + fnotice (file, " -w, --weight <w1,w2> Set weights (float point values)\n"); > +} > + > +static const struct option merge_options[] = > +{ > + { "verbose", no_argument, NULL, 'v' }, > + { "output", required_argument, NULL, 'o' }, > + { "weight", required_argument, NULL, 'w' }, > + { 0, 0, 0, 0 } > +}; > + > +/* Print merge usage and exit. */ > + > +static void > +merge_usage (void) > +{ > + fnotice (stderr, "Merge subcomand usage:"); > + print_merge_usage_message (true); > + exit (FATAL_EXIT_CODE); > +} > + > +/* If N_VAL is no-zero, normalize the profile by setting the largest counter > + counter value to N_VAL and scale others counters proportionally. > + Otherwise, multiply the all counters by SCALE. */ > + > +static int > +profile_rewrite (const char *d1, const char *out, long long n_val, > + float scale, int n, int d) > +{ > + char *pwd; > + int ret; > + struct gcov_info * d1_profile; > + > + > + d1_profile = gcov_read_profile_dir (d1, 0); > + if (!d1_profile) > + return 1; > + > + /* Output new profile. */ > + unlink_profile_dir (out); > + mkdir (out, 0755); > + pwd = getcwd (NULL, 0); > + gcc_assert (pwd); > + ret = chdir (out); > + gcc_assert (ret == 0); Perhaps we want user readable error messages in both cases? I think both can legally fail if you remove the CWD or when you specify wrong output directory. > + > + ret = chdir (pwd); > + free (pwd); Again, I think you want to check (probably have chdir wrapper that will do the fatal for you?) > + case 's': > + ret = 0; > + do_scaling = 1; > + if (strstr (optarg, "/")) > + { > + ret = sscanf (optarg, "%d/%d", &numerator, &denominator); > + if (ret == 2) > + { > + gcc_assert (numerator >= 0); > + gcc_assert (denominator > 0); > + } > + } > + if (ret != 2) > + { > + ret = sscanf (optarg, "%f", &scale); > + gcc_assert (ret == 1); > + denominator = 0; > + } Probably user readable sanity checking here, too (rather than assert) > + > + if (scale < 0.0) > + { > + fnotice (stderr, "scale needs to be non-negative\n"); > + exit (FATAL_EXIT_CODE); fatal (here and at other cases) > +/* This part of the code is to merge profile counters. */ > + > +static gcov_type *gcov_value_buf; > +static gcov_unsigned_t gcov_value_buf_size; > +static gcov_unsigned_t gcov_value_buf_pos; > +static unsigned gcov_merge_weight; Add comments for individual vars. > +@c Copyright (C) 1996-2014 Free Software Foundation, Inc. > +@c This is part of the GCC manual. > +@c For copying conditions, see the file gcc.texi. > + > +@ignore > +@c man begin COPYRIGHT > +Copyright @copyright{} 1996-2014 Free Software Foundation, Inc. Perhaps just 2014? > + > +Permission is granted to copy, distribute and/or modify this document > +under the terms of the GNU Free Documentation License, Version 1.3 or > +any later version published by the Free Software Foundation; with the > +Invariant Sections being ``GNU General Public License'' and ``Funding > +Free Software'', the Front-Cover texts being (a) (see below), and with > +the Back-Cover Texts being (b) (see below). A copy of the license is > +included in the gfdl(7) man page. > + > +(a) The FSF's Front-Cover Text is: > + > + A GNU Manual > + > +(b) The FSF's Back-Cover Text is: > + > + You have freedom to copy and modify this GNU Manual, like GNU > + software. Copies published by the Free Software Foundation raise > + funds for GNU development. > +@c man end > +@c Set file name and title for the man page. > +@setfilename gcov-tool > +@settitle offline gcda profile processing tool > +@end ignore > + > +@node Gcov-tool > +@chapter @command{gcov-tool}---an offline gcda profile processing tool > + > +@command{gcov-tool} is a tool you can use in conjunction with GCC to > +manipulate or process gcda profile files offline. > + > +@menu > +* Gcov-tool Intro:: Introduction to gcov-tool. > +* Invoking Gcov-tool:: How to use gcov-tool. > +@end menu > + > +@node Gcov-tool Intro > +@section Introduction to @command{gcov-tool} > +@c man begin DESCRIPTION > + > +@command{gcov-tool} is an offline tool to process gcc's gcda profile files. > + > +Current gcov-tool supports the following functionalities: > + > +@itemize @bullet > +@item > +merge two sets of profiles with weights. > + > +@item > +read one set of profile and rewrite profile contents. One can scale or > +normalize the count values. > +@end itemize > + > +Note that for the merging operation, this profile generated offline may > +contain a slight different values from the online merged profile. Here are > +a list of typical differences: > + > +@itemize @bullet > +@item > +histogram difference: This offline tool recomputes the histogram after merging > +the counters. The resulting histogram, therefore, is precise. The online > +merging does not have this capability -- the histogram is merged from two > +histograms and the result is an approximation. > + > +@item > +summary checksum difference: Summary checksum uses a CRC32 operation. The value > +depends on the link list order of gcov-info objects. This order is different in > +gcov-tool from that in the online merge. It's expected to have different > +summary checksums. It does not really matter as the compiler does not use this > +checksum anywhere. > + > +@item > +value profile counter values difference: Some counter values for value profile > +are runtime dependent, like heap addresses. It's normal to see some difference > +in these kind of counters. > +@end itemize > + > +@c man end > + > +@node Invoking Gcov-tool > +@section Invoking @command{gcov-tool} > + > +@smallexample > +gcov-tool @r{[}@var{global-options}@r{]} SUB_COMMAND > +@r{[}@var{sub_command-options}@r{]} @var{profile_dir} > +@end smallexample > + > +@command{gcov-tool} accepts the following options: > + > +@ignore > +@c man begin SYNOPSIS > +gcov-tool [@option{-v}|@option{--version}] [@option{-h}|@option{--help}] > + > +gcov-tool merge [merge-options] @var{directory1} @var{directory2} > + [@option{-v}|@option{--verbose}] > + [@option{-o}|@option{ --output} @var{directory}] > + [@option{-w}|@option{--weight} @var{w1,w2}] > + > +gcov-tool rewrite [rewrite-options] @var{directory} > + [@option{-v}|@option{--verbose}] > + [@option{-o}|@option{--output} @var{directory}] > + [@option{-s}|@option{--scale} @var{float_or_simple-frac_value}] > + [@option{-n}|@option{--normalize} @var{long_long_value}] > +@c man end > +@c man begin SEEALSO > +gpl(7), gfdl(7), fsf-funding(7), gcc(1), gcov(1) and the Info entry for > +@file{gcc}. > +@c man end > +@end ignore > + > +@c man begin OPTIONS > +@table @gcctabopt > +@item -h > +@itemx --help > +Display help about using @command{gcov-tool} (on the standard output), and > +exit without doing any further processing. > + > +@item -v > +@itemx --version > +Display the @command{gcov-tool} version number (on the standard output), > +and exit without doing any further processing. > + > +@item merge > +Merge two profile directories. > + > +@table @gcctabopt > +@item -v > +@itemx --verbose > +Set the verbose mode. > + > +@item -o @var{directory} > +@itemx --output @var{directory} > +Set the output profile directory. Default output directory name is > +@var{merged_profile}. > + > +@item -w @var{w1,w2} > +@itemx --weight @var{w1,w2} > +Set the merge weights of the @var{directory1} and @var{directory2}, > +respectively. The default weights are 1 for both. > +@end table > + > +@item rewrite > +Read the specified profile directory and rewrite to a new directory. > + > +@table @gcctabopt > +@item -v > +@itemx --verbose > +Set the verbose mode. > + > +@item -o @var{directory} > +@itemx --output @var{directory} > +Set the output profile directory. Default output name is @var{rewrite_profile}. > + > +@item -s @var{float_or_simple-frac_value} > +@itemx --scale @var{float_or_simple-frac_value} > +Scale the profile counters. The specified value can be in floating point value, > +or simple fraction value form, such 1, 2, 2/3, and 5/3. > + > +@item -n @var{long_long_value} > +@itemx --normalize <long_long_value> > +Normalize the profile. The specified value is the max counter value > +in the new profile. > + > +@end table > +@end table > + > +@c man end Thanks for writting the docs. Perhaps you could add bit extra information about when one would like to do the supported operations. Thanks! Honza ^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2014-07-13 7:49 UTC | newest] Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2014-01-13 20:43 [PATCH] offline gcda profile processing tool Rong Xu 2014-01-16 17:30 ` Rong Xu 2014-03-03 22:02 ` Rong Xu 2014-04-15 22:05 ` Jan Hubicka 2014-04-16 17:28 ` Rong Xu 2014-04-17 5:19 ` Jan Hubicka 2014-05-01 22:37 ` Rong Xu 2014-05-05 17:17 ` Rong Xu 2014-07-11 8:07 ` Richard Biener 2014-07-11 8:12 ` Christophe Lyon 2014-07-11 15:50 ` Xinliang David Li 2014-07-11 16:44 ` Rong Xu 2014-07-11 15:42 ` Xinliang David Li 2014-07-11 16:03 ` Jakub Jelinek 2014-07-11 16:13 ` Xinliang David Li 2014-07-11 16:48 ` Rong Xu 2014-07-11 18:30 ` Rong Xu 2014-07-11 18:46 ` Jan Hubicka 2014-07-11 18:53 ` Rong Xu 2014-07-13 7:49 ` Andreas Schwab 2014-05-15 20:37 ` Jan Hubicka 2014-05-20 22:59 ` Rong Xu 2014-05-25 23:43 ` Jan Hubicka
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).