Back to index

glibc  2.9
sprofil.c
Go to the documentation of this file.
00001 /* Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
00002    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <assert.h>
00021 #include <signal.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <unistd.h>
00025 
00026 #include <sys/time.h>
00027 #include <sys/profil.h>
00028 
00029 #ifndef SIGPROF
00030 # include <gmon/sprofil.c>
00031 #else
00032 
00033 #include <libc-internal.h>
00034 
00035 struct region
00036   {
00037     size_t offset;
00038     size_t nsamples;
00039     unsigned int scale;
00040     union
00041       {
00042        void *vp;
00043        unsigned short *us;
00044        unsigned int *ui;
00045       }
00046     sample;
00047     size_t start;
00048     size_t end;
00049   };
00050 
00051 struct prof_info
00052   {
00053     unsigned int num_regions;
00054     struct region *region;
00055     struct region *last, *overflow;
00056     struct itimerval saved_timer;
00057     struct sigaction saved_action;
00058   };
00059 
00060 static unsigned int overflow_counter;
00061 
00062 static struct region default_overflow_region =
00063   {
00064     .offset   = 0,
00065     .nsamples = 1,
00066     .scale    = 2,
00067     .sample   = { &overflow_counter },
00068     .start    = 0,
00069     .end      = ~(size_t) 0
00070   };
00071 
00072 static struct prof_info prof_info;
00073 
00074 static unsigned long int
00075 pc_to_index (size_t pc, size_t offset, unsigned int scale, int prof_uint)
00076 {
00077   size_t i = (pc - offset) / (prof_uint ? sizeof (int) : sizeof (short));
00078 
00079   if (sizeof (unsigned long long int) > sizeof (size_t))
00080     return (unsigned long long int) i * scale / 65536;
00081   else
00082     return i / 65536 * scale + i % 65536 * scale / 65536;
00083 }
00084 
00085 static inline size_t
00086 index_to_pc (unsigned long int n, size_t offset, unsigned int scale,
00087             int prof_uint)
00088 {
00089   size_t pc, bin_size = (prof_uint ? sizeof (int) : sizeof (short));
00090 
00091   if (sizeof (unsigned long long int) > sizeof (size_t))
00092     pc = offset + (unsigned long long int) n * bin_size * 65536ull / scale;
00093   else
00094     pc = (offset + n * bin_size / scale * 65536
00095          + n * bin_size % scale * 65536 / scale);
00096 
00097   if (pc_to_index (pc, offset, scale, prof_uint) < n)
00098     /* Adjust for rounding error.  */
00099     ++pc;
00100 
00101   assert (pc_to_index (pc - 1, offset, scale, prof_uint) < n
00102          && pc_to_index (pc, offset, scale, prof_uint) >= n);
00103 
00104   return pc;
00105 }
00106 
00107 static void
00108 profil_count (void *pcp, int prof_uint)
00109 {
00110   struct region *region, *r = prof_info.last;
00111   size_t lo, hi, mid, pc = (unsigned long int) pcp;
00112   unsigned long int i;
00113 
00114   /* Fast path: pc is in same region as before.  */
00115   if (pc >= r->start && pc < r->end)
00116     region = r;
00117   else
00118     {
00119       /* Slow path: do a binary search for the right region.  */
00120       lo = 0; hi = prof_info.num_regions - 1;
00121       while (lo <= hi)
00122        {
00123          mid = (lo + hi) / 2;
00124 
00125          r = prof_info.region + mid;
00126          if (pc >= r->start && pc < r->end)
00127            {
00128              prof_info.last = r;
00129              region = r;
00130              break;
00131            }
00132 
00133          if (pc < r->start)
00134            hi = mid - 1;
00135          else
00136            lo = mid + 1;
00137        }
00138 
00139       /* No matching region: increment overflow count.  There is no point
00140         in updating the cache here, as it won't hit anyhow.  */
00141       region = prof_info.overflow;
00142     }
00143 
00144   i = pc_to_index (pc, region->offset, region->scale, prof_uint);
00145   if (i < r->nsamples)
00146     {
00147       if (prof_uint)
00148        {
00149          if (r->sample.ui[i] < (unsigned int) ~0)
00150            ++r->sample.ui[i];
00151        }
00152       else
00153        {
00154          if (r->sample.us[i] < (unsigned short) ~0)
00155            ++r->sample.us[i];
00156        }
00157     }
00158   else
00159     {
00160       if (prof_uint)
00161        ++prof_info.overflow->sample.ui[0];
00162       else
00163        ++prof_info.overflow->sample.us[0];
00164     }
00165 }
00166 
00167 static inline void
00168 profil_count_ushort (void *pcp)
00169 {
00170   profil_count (pcp, 0);
00171 }
00172 
00173 static inline void
00174 profil_count_uint (void *pcp)
00175 {
00176   profil_count (pcp, 1);
00177 }
00178 
00179 /* Get the machine-dependent definition of `profil_counter', the signal
00180    handler for SIGPROF.  It calls `profil_count' (above) with the PC of the
00181    interrupted code.  */
00182 #define profil_counter             profil_counter_ushort
00183 #define profil_count(pc)    profil_count (pc, 0)
00184 #include <profil-counter.h>
00185 
00186 #undef profil_counter
00187 #undef profil_count
00188 
00189 #define profil_counter             profil_counter_uint
00190 #define profil_count(pc)    profil_count (pc, 1)
00191 #include <profil-counter.h>
00192 
00193 static int
00194 insert (int i, unsigned long int start, unsigned long int end, struct prof *p,
00195        int prof_uint)
00196 {
00197   struct region *r;
00198   size_t to_copy;
00199 
00200   if (start >= end)
00201     return 0;        /* don't bother with empty regions */
00202 
00203   if (prof_info.num_regions == 0)
00204     r = malloc (sizeof (*r));
00205   else
00206     r = realloc (prof_info.region, (prof_info.num_regions + 1) * sizeof (*r));
00207   if (r == NULL)
00208     return -1;
00209 
00210   to_copy = prof_info.num_regions - i;
00211   if (to_copy > 0)
00212     memmove (r + i + 1, r + i, to_copy * sizeof (*r));
00213 
00214   r[i].offset = p->pr_off;
00215   r[i].nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
00216   r[i].scale = p->pr_scale;
00217   r[i].sample.vp = p->pr_base;
00218   r[i].start = start;
00219   r[i].end = end;
00220 
00221   prof_info.region = r;
00222   ++prof_info.num_regions;
00223 
00224   if (p->pr_off == 0 && p->pr_scale == 2)
00225     prof_info.overflow = r;
00226 
00227   return 0;
00228 }
00229 
00230 /* Add a new profiling region.  If the new region overlaps with
00231    existing ones, this may add multiple subregions so that the final
00232    data structure is free of overlaps.  The absence of overlaps makes
00233    it possible to use a binary search in profil_count().  Note that
00234    this function depends on new regions being presented in DECREASING
00235    ORDER of starting address.  */
00236 
00237 static int
00238 add_region (struct prof *p, int prof_uint)
00239 {
00240   unsigned long int nsamples;
00241   size_t start, end;
00242   unsigned int i;
00243 
00244   if (p->pr_scale < 2)
00245     return 0;
00246 
00247   nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
00248 
00249   start = p->pr_off;
00250   end = index_to_pc (nsamples, p->pr_off, p->pr_scale, prof_uint);
00251 
00252   /* Merge with existing regions.  */
00253   for (i = 0; i < prof_info.num_regions; ++i)
00254     {
00255       if (start < prof_info.region[i].start)
00256        {
00257          if (end < prof_info.region[i].start)
00258            break;
00259          else if (insert (i, start, prof_info.region[i].start, p, prof_uint)
00260                  < 0)
00261            return -1;
00262        }
00263       start = prof_info.region[i].end;
00264     }
00265   return insert (i, start, end, p, prof_uint);
00266 }
00267 
00268 static int
00269 pcmp (const void *left, const void *right)
00270 {
00271   struct prof *l = *(struct prof **) left;
00272   struct prof *r = *(struct prof **) right;
00273 
00274   if (l->pr_off < r->pr_off)
00275     return 1;
00276   else if (l->pr_off > r->pr_off)
00277     return -1;
00278   return 0;
00279 }
00280 
00281 int
00282 __sprofil (struct prof *profp, int profcnt, struct timeval *tvp,
00283           unsigned int flags)
00284 {
00285   struct prof *p[profcnt];
00286   struct itimerval timer;
00287   struct sigaction act;
00288   int i;
00289 
00290   if (tvp != NULL)
00291     {
00292       /* Return profiling period.  */
00293       unsigned long int t = 1000000 / __profile_frequency ();
00294       tvp->tv_sec  = t / 1000000;
00295       tvp->tv_usec = t % 1000000;
00296     }
00297 
00298   if (prof_info.num_regions > 0)
00299     {
00300       /* Disable profiling.  */
00301       if (__setitimer (ITIMER_PROF, &prof_info.saved_timer, NULL) < 0)
00302        return -1;
00303 
00304       if (__sigaction (SIGPROF, &prof_info.saved_action, NULL) < 0)
00305        return -1;
00306 
00307       free (prof_info.region);
00308       return 0;
00309     }
00310 
00311   prof_info.num_regions = 0;
00312   prof_info.region = NULL;
00313   prof_info.overflow = &default_overflow_region;
00314 
00315   for (i = 0; i < profcnt; ++i)
00316     p[i] = profp + i;
00317 
00318   /* Sort in order of decreasing starting address: */
00319   qsort (p, profcnt, sizeof (p[0]), pcmp);
00320 
00321   /* Add regions in order of decreasing starting address: */
00322   for (i = 0; i < profcnt; ++i)
00323     if (add_region (p[i], (flags & PROF_UINT) != 0) < 0)
00324       {
00325        free (prof_info.region);
00326        prof_info.num_regions = 0;
00327        prof_info.region = NULL;
00328        return -1;
00329       }
00330 
00331   if (prof_info.num_regions == 0)
00332     return 0;
00333 
00334   prof_info.last = prof_info.region;
00335 
00336   /* Install SIGPROF handler.  */
00337   if (flags & PROF_UINT)
00338     act.sa_handler = (sighandler_t) &profil_counter_uint;
00339   else
00340     act.sa_handler = (sighandler_t) &profil_counter_ushort;
00341   act.sa_flags = SA_RESTART;
00342   __sigfillset (&act.sa_mask);
00343   if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0)
00344     return -1;
00345 
00346   /* Setup profiling timer.  */
00347   timer.it_value.tv_sec  = 0;
00348   timer.it_value.tv_usec = 1;
00349   timer.it_interval = timer.it_value;
00350   return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer);
00351 }
00352 
00353 weak_alias (__sprofil, sprofil)
00354 
00355 #endif /* SIGPROF */