From patchwork Tue May 20 22:59:27 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rong Xu X-Patchwork-Id: 350898 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id E5560140081 for ; Wed, 21 May 2014 08:59:45 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:in-reply-to:references:date:message-id:subject :from:to:cc:content-type; q=dns; s=default; b=e6icywrhoc2S7ayICq wT6IMIW7rgFTyXADOqspCMpXhupDhiQ8X5GfnnwRznfR2HVqMdVpnEhqYYJPQY1x hcRCfhwYE5u7n+Bg9njNga4RZS2bee2Ltsscm9Cu3x0zl9FEg64gfKJDbqX6RHD6 UrGJ3AMpo4huAYaWyc3mNZY04= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :mime-version:in-reply-to:references:date:message-id:subject :from:to:cc:content-type; s=default; bh=IvDoNMoYlerxIyvs089PRgBk FI4=; b=yCDe83KwaguGJVH04/we4ecrlExPj0EElTnsM3ELf3KOUZt3pSqJ5WDT kwjMLwpu/yJsZjjgZHyOA8irx2aP7NAq7XWXP3/IXl9hb2w/X3Yl/RErVjMRqpGk qmC0Lyf1Pc4ECQSfgt2fNfOtS91mn67SRMabefKJ3raYFRA2S0E= Received: (qmail 20057 invoked by alias); 20 May 2014 22:59:36 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 20047 invoked by uid 89); 20 May 2014 22:59:35 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.2 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD, SPF_PASS, UNSUBSCRIBE_BODY autolearn=no version=3.3.2 X-HELO: mail-oa0-f41.google.com Received: from mail-oa0-f41.google.com (HELO mail-oa0-f41.google.com) (209.85.219.41) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Tue, 20 May 2014 22:59:29 +0000 Received: by mail-oa0-f41.google.com with SMTP id m1so1356508oag.0 for ; Tue, 20 May 2014 15:59:27 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; bh=hzanB5Bv2FIh0zEyQZWQm6wW4lvvkeFWIGAEugRSmzc=; b=ZDfGL/UrJDsKcYopJwV6WknaryrisnvrbNN/e5fyqmDATmAmy/8IfWSvQv6+jSwY/z cuMNgkzj3PmXNKxsitLBlXTalpGbq5Sjq1EtNWJN9FaIkBAlYXSnt2ccjblh6gD3nVjY //7Hjpelvav7IUcFQ/Vy/WBTsj5X+Fv5gwrTZih3yPGmNtXK6evGYPXtZfTDpv+qsq7b e7hgqIQBaNf3CLu0XrLkaPa1ooCJenyU1KSl5ZbAFrgcq0wbHPLYbLt30JRQNWZ+Eynk skGXrm1UvPz54agtTggmHA1AV7nYFxDWxvLzgy5N6a+es44Q9m45LCygSAahy259ftfe zvZw== X-Gm-Message-State: ALoCoQmnZ5L4Mp/TF/DnrjRf3zo+UCmB7aLe2LK6gofwMRfor2eVXQdAiHjUESl1su2Ec3xUV0f6 MIME-Version: 1.0 X-Received: by 10.60.135.106 with SMTP id pr10mr47987283oeb.15.1400626767393; Tue, 20 May 2014 15:59:27 -0700 (PDT) Received: by 10.182.176.98 with HTTP; Tue, 20 May 2014 15:59:27 -0700 (PDT) In-Reply-To: <20140515203700.GA29194@kam.mff.cuni.cz> References: <20140415213850.GA23141@atrey.karlin.mff.cuni.cz> <20140417033448.GC3157@kam.mff.cuni.cz> <20140515203700.GA29194@kam.mff.cuni.cz> Date: Tue, 20 May 2014 15:59:27 -0700 Message-ID: Subject: Re: [PATCH] offline gcda profile processing tool From: Rong Xu To: Jan Hubicka Cc: GCC Patches , Xinliang David Li , Teresa Johnson , Dehao Chen X-IsSubscribed: yes On Thu, May 15, 2014 at 1:37 PM, Jan Hubicka 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 >> >> * 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 >> +#include > > 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 2014-05-20 Rong Xu * 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 . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "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 +#include +#include +#include +#include + +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] Merge coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output Output directory\n"); + fnotice (file, " -w, --weight 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] Rewrite coverage file contents\n"); + fnotice (file, " -v, --verbose Verbose mode\n"); + fnotice (file, " -o, --output Output directory\n"); + fnotice (file, " -s, --scale Scale the profile counters\n"); + fnotice (file, " -n, --normalize 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 $@ # # 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 +. */ + +/* 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 . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#define IN_GCOV_TOOL 1 + +#include "libgcov.h" +#include "intl.h" +#include "diagnostic.h" +#include "version.h" +#include "demangle.h" + +/* Borrowed from basic-block.h. */ +#define RDIV(X,Y) (((X) + (Y) / 2) / (Y)) + +extern gcov_position_t gcov_position(); +extern int gcov_is_error(); +extern 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 +#include + +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 +Normalize the profile. The specified value is the max counter value +in the new profile. + +@end table +@end table + +@c man end