Home | History | Annotate | Download | only in gcov-src
      1 /* Routines required for instrumenting a program.  */
      2 /* Compile this one with gcc.  */
      3 /* Copyright (C) 1989-2014 Free Software Foundation, Inc.
      4 
      5 This file is part of GCC.
      6 
      7 GCC is free software; you can redistribute it and/or modify it under
      8 the terms of the GNU General Public License as published by the Free
      9 Software Foundation; either version 3, or (at your option) any later
     10 version.
     11 
     12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15 for more details.
     16 
     17 Under Section 7 of GPL version 3, you are granted additional
     18 permissions described in the GCC Runtime Library Exception, version
     19 3.1, as published by the Free Software Foundation.
     20 
     21 You should have received a copy of the GNU General Public License and
     22 a copy of the GCC Runtime Library Exception along with this program;
     23 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     24 <http://www.gnu.org/licenses/>.  */
     25 
     26 #include "libgcov.h"
     27 
     28 #if defined(inhibit_libc)
     29 /* If libc and its header files are not available, provide dummy functions.  */
     30 
     31 #ifdef L_gcov_merge_add
     32 void __gcov_merge_add (gcov_type *counters  __attribute__ ((unused)),
     33                        unsigned n_counters __attribute__ ((unused))) {}
     34 #endif
     35 
     36 #ifdef L_gcov_merge_single
     37 void __gcov_merge_single (gcov_type *counters  __attribute__ ((unused)),
     38                           unsigned n_counters __attribute__ ((unused))) {}
     39 #endif
     40 
     41 #ifdef L_gcov_merge_delta
     42 void __gcov_merge_delta (gcov_type *counters  __attribute__ ((unused)),
     43                          unsigned n_counters __attribute__ ((unused))) {}
     44 #endif
     45 
     46 #else
     47 
     48 #ifdef L_gcov_merge_add
     49 /* The profile merging function that just adds the counters.  It is given
     50    an array COUNTERS of N_COUNTERS old counters and it reads the same number
     51    of counters from the gcov file.  */
     52 void
     53 __gcov_merge_add (gcov_type *counters, unsigned n_counters)
     54 {
     55   for (; n_counters; counters++, n_counters--)
     56     *counters += gcov_get_counter ();
     57 }
     58 #endif /* L_gcov_merge_add */
     59 
     60 #ifdef L_gcov_merge_ior
     61 /* The profile merging function that just adds the counters.  It is given
     62    an array COUNTERS of N_COUNTERS old counters and it reads the same number
     63    of counters from the gcov file.  */
     64 void
     65 __gcov_merge_ior (gcov_type *counters, unsigned n_counters)
     66 {
     67   for (; n_counters; counters++, n_counters--)
     68     *counters |= gcov_get_counter_target ();
     69 }
     70 #endif
     71 
     72 
     73 #ifdef L_gcov_merge_dc
     74 
     75 /* Returns 1 if the function global id GID is not valid.  */
     76 
     77 static int
     78 __gcov_is_gid_insane (gcov_type gid)
     79 {
     80   if (EXTRACT_MODULE_ID_FROM_GLOBAL_ID (gid) == 0
     81       || EXTRACT_FUNC_ID_FROM_GLOBAL_ID (gid) == 0)
     82     return 1;
     83   return 0;
     84 }
     85 
     86 /* The profile merging function used for merging direct call counts
     87    This function is given array COUNTERS of N_COUNTERS old counters and it
     88    reads the same number of counters from the gcov file.  */
     89 
     90 void
     91 __gcov_merge_dc (gcov_type *counters, unsigned n_counters)
     92 {
     93   unsigned i;
     94 
     95   gcc_assert (!(n_counters % 2));
     96   for (i = 0; i < n_counters; i += 2)
     97     {
     98       gcov_type global_id = gcov_get_counter_target ();
     99       gcov_type call_count = gcov_get_counter ();
    100 
    101       /* Note that global id counter may never have been set if no calls were
    102 	 made from this call-site.  */
    103       if (counters[i] && global_id)
    104         {
    105           /* TODO race condition requires us do the following correction.  */
    106           if (__gcov_is_gid_insane (counters[i]))
    107             counters[i] = global_id;
    108           else if (__gcov_is_gid_insane (global_id))
    109             global_id = counters[i];
    110 
    111 #if !defined(__KERNEL__)
    112           /* In the case of inconsistency, use the src's target.  */
    113           if (counters[i] != global_id)
    114             fprintf (stderr, "Warning: Inconsistent call targets in"
    115                      " direct-call profile.\n");
    116 #endif
    117         }
    118       else if (global_id)
    119 	counters[i] = global_id;
    120 
    121       counters[i + 1] += call_count;
    122 
    123       /* Reset. */
    124       if (__gcov_is_gid_insane (counters[i]))
    125         counters[i] = counters[i + 1] = 0;
    126 
    127       /* Assert that the invariant (global_id == 0) <==> (call_count == 0)
    128 	 holds true after merging.  */
    129       if (counters[i] == 0)
    130         counters[i+1] = 0;
    131       if (counters[i + 1] == 0)
    132         counters[i] = 0;
    133     }
    134 }
    135 #endif
    136 
    137 
    138 #ifdef L_gcov_merge_icall_topn
    139 /* The profile merging function used for merging indirect call counts
    140    This function is given array COUNTERS of N_COUNTERS old counters and it
    141    reads the same number of counters from the gcov file.  */
    142 
    143 void
    144 __gcov_merge_icall_topn (gcov_type *counters, unsigned n_counters)
    145 {
    146   unsigned i, j, k, m;
    147 
    148   gcc_assert (!(n_counters % GCOV_ICALL_TOPN_NCOUNTS));
    149   for (i = 0; i < n_counters; i += GCOV_ICALL_TOPN_NCOUNTS)
    150     {
    151       gcov_type *value_array = &counters[i + 1];
    152       unsigned tmp_size = 2 * (GCOV_ICALL_TOPN_NCOUNTS - 1);
    153       gcov_type *tmp_array
    154           = (gcov_type *) alloca (tmp_size * sizeof (gcov_type));
    155 
    156       for (j = 0; j < tmp_size; j++)
    157         tmp_array[j] = 0;
    158 
    159       for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
    160         {
    161           tmp_array[j] = value_array[j];
    162           tmp_array[j + 1] = value_array [j + 1];
    163         }
    164 
    165       /* Skip the number_of_eviction entry.  */
    166       gcov_get_counter ();
    167       for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2)
    168         {
    169           int found = 0;
    170           gcov_type global_id = gcov_get_counter_target ();
    171           gcov_type call_count = gcov_get_counter ();
    172           for (m = 0; m < j; m += 2)
    173             {
    174               if (tmp_array[m] == global_id)
    175                 {
    176                   found = 1;
    177                   tmp_array[m + 1] += call_count;
    178                   break;
    179                 }
    180             }
    181           if (!found)
    182             {
    183               tmp_array[j] = global_id;
    184               tmp_array[j + 1] = call_count;
    185               j += 2;
    186             }
    187         }
    188       /* Now sort the temp array */
    189       gcov_sort_n_vals (tmp_array, j);
    190 
    191       /* Now copy back the top half of the temp array */
    192       for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2)
    193         {
    194           value_array[k] = tmp_array[k];
    195           value_array[k + 1] = tmp_array[k + 1];
    196         }
    197     }
    198 }
    199 #endif
    200 
    201 
    202 #ifdef L_gcov_merge_time_profile
    203 /* Time profiles are merged so that minimum from all valid (greater than zero)
    204    is stored. There could be a fork that creates new counters. To have
    205    the profile stable, we chosen to pick the smallest function visit time.  */
    206 void
    207 __gcov_merge_time_profile (gcov_type *counters, unsigned n_counters)
    208 {
    209   unsigned int i;
    210   gcov_type value;
    211 
    212   for (i = 0; i < n_counters; i++)
    213     {
    214       value = gcov_get_counter_target ();
    215 
    216       if (value && (!counters[i] || value < counters[i]))
    217         counters[i] = value;
    218     }
    219 }
    220 #endif /* L_gcov_merge_time_profile */
    221 
    222 #ifdef L_gcov_merge_single
    223 /* The profile merging function for choosing the most common value.
    224    It is given an array COUNTERS of N_COUNTERS old counters and it
    225    reads the same number of counters from the gcov file.  The counters
    226    are split into 3-tuples where the members of the tuple have
    227    meanings:
    228 
    229    -- the stored candidate on the most common value of the measured entity
    230    -- counter
    231    -- total number of evaluations of the value  */
    232 void
    233 __gcov_merge_single (gcov_type *counters, unsigned n_counters)
    234 {
    235   unsigned i, n_measures;
    236   gcov_type value, counter, all;
    237 
    238   gcc_assert (!(n_counters % 3));
    239   n_measures = n_counters / 3;
    240   for (i = 0; i < n_measures; i++, counters += 3)
    241     {
    242       value = gcov_get_counter_target ();
    243       counter = gcov_get_counter ();
    244       all = gcov_get_counter ();
    245 
    246       if (counters[0] == value)
    247         counters[1] += counter;
    248       else if (counter > counters[1])
    249         {
    250           counters[0] = value;
    251           counters[1] = counter - counters[1];
    252         }
    253       else
    254         counters[1] -= counter;
    255       counters[2] += all;
    256     }
    257 }
    258 #endif /* L_gcov_merge_single */
    259 
    260 #ifdef L_gcov_merge_delta
    261 /* The profile merging function for choosing the most common
    262    difference between two consecutive evaluations of the value.  It is
    263    given an array COUNTERS of N_COUNTERS old counters and it reads the
    264    same number of counters from the gcov file.  The counters are split
    265    into 4-tuples where the members of the tuple have meanings:
    266 
    267    -- the last value of the measured entity
    268    -- the stored candidate on the most common difference
    269    -- counter
    270    -- total number of evaluations of the value  */
    271 void
    272 __gcov_merge_delta (gcov_type *counters, unsigned n_counters)
    273 {
    274   unsigned i, n_measures;
    275   gcov_type value, counter, all;
    276 
    277   gcc_assert (!(n_counters % 4));
    278   n_measures = n_counters / 4;
    279   for (i = 0; i < n_measures; i++, counters += 4)
    280     {
    281       /* last = */ gcov_get_counter ();
    282       value = gcov_get_counter_target ();
    283       counter = gcov_get_counter ();
    284       all = gcov_get_counter ();
    285 
    286       if (counters[1] == value)
    287         counters[2] += counter;
    288       else if (counter > counters[2])
    289         {
    290           counters[1] = value;
    291           counters[2] = counter - counters[2];
    292         }
    293       else
    294         counters[2] -= counter;
    295       counters[3] += all;
    296     }
    297 }
    298 #endif /* L_gcov_merge_delta */
    299 #endif /* inhibit_libc */
    300