Back to index

glibc  2.9
Classes | Defines | Functions | Variables
dl-profile.c File Reference
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ldsodefs.h>
#include <sys/gmon.h>
#include <sys/gmon_out.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <atomic.h>

Go to the source code of this file.

Classes

struct  here_fromstruct

Defines

#define SCALE_1_TO_1   0x10000L
#define EXTRA_FLAGS

Functions

int __profile_frequency (void)
 libc_hidden_proto (__profile_frequency)
 __attribute__ ((packed))
void internal_function _dl_start_profile (void)
void _dl_mcount (ElfW(Addr) frompc, ElfW(Addr) selfpc)

Variables

static struct here_cg_arc_recorddata
static int running
static uint32_t narcs
static volatile uint32_tnarcsp
static volatile uint16_ttos
static struct here_fromstructfroms
static uint32_t fromlimit
static volatile uint32_t fromidx
static uintptr_t lowpc
static size_t textsize
static unsigned int log_hashfraction

Class Documentation

struct here_fromstruct

Definition at line 151 of file dl-profile.c.

Collaboration diagram for here_fromstruct:
Class Members
struct here_cg_arc_record * here
uint16_t link

Define Documentation

#define EXTRA_FLAGS
#define SCALE_1_TO_1   0x10000L

Function Documentation

__attribute__ ( (packed)  )

Definition at line 37 of file prof-freq.c.

{
  /*
   * Discover the tick frequency of the machine if something goes wrong,
   * we return 0, an impossible hertz.
   */
  struct itimerval tim;

  tim.it_interval.tv_sec = 0;
  tim.it_interval.tv_usec = 1;
  tim.it_value.tv_sec = 0;
  tim.it_value.tv_usec = 0;
  __setitimer(ITIMER_REAL, &tim, 0);
  __setitimer(ITIMER_REAL, 0, &tim);
  if (tim.it_interval.tv_usec < 2)
    return 0;
  return (1000000 / tim.it_interval.tv_usec);
}

Here is the caller graph for this function:

void _dl_mcount ( ElfW(Addr)  frompc,
ElfW(Addr)  selfpc 
)

Definition at line 450 of file dl-profile.c.

{
  volatile uint16_t *topcindex;
  size_t i, fromindex;
  struct here_fromstruct *fromp;

  if (! running)
    return;

  /* Compute relative addresses.  The shared object can be loaded at
     any address.  The value of frompc could be anything.  We cannot
     restrict it in any way, just set to a fixed value (0) in case it
     is outside the allowed range.  These calls show up as calls from
     <external> in the gprof output.  */
  frompc -= lowpc;
  if (frompc >= textsize)
    frompc = 0;
  selfpc -= lowpc;
  if (selfpc >= textsize)
    goto done;

  /* Getting here we now have to find out whether the location was
     already used.  If yes we are lucky and only have to increment a
     counter (this also has to be atomic).  If the entry is new things
     are getting complicated...  */

  /* Avoid integer divide if possible.  */
  if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
    i = selfpc >> log_hashfraction;
  else
    i = selfpc / (HASHFRACTION * sizeof (*tos));

  topcindex = &tos[i];
  fromindex = *topcindex;

  if (fromindex == 0)
    goto check_new_or_add;

  fromp = &froms[fromindex];

  /* We have to look through the chain of arcs whether there is already
     an entry for our arc.  */
  while (fromp->here->from_pc != frompc)
    {
      if (fromp->link != 0)
       do
         fromp = &froms[fromp->link];
       while (fromp->link != 0 && fromp->here->from_pc != frompc);

      if (fromp->here->from_pc != frompc)
       {
         topcindex = &fromp->link;

       check_new_or_add:
         /* Our entry is not among the entries we read so far from the
            data file.  Now see whether we have to update the list.  */
         while (narcs != *narcsp && narcs < fromlimit)
           {
             size_t to_index;
             size_t newfromidx;
             to_index = (data[narcs].self_pc
                       / (HASHFRACTION * sizeof (*tos)));
             newfromidx = catomic_exchange_and_add (&fromidx, 1) + 1;
             froms[newfromidx].here = &data[narcs];
             froms[newfromidx].link = tos[to_index];
             tos[to_index] = newfromidx;
             catomic_increment (&narcs);
           }

         /* If we still have no entry stop searching and insert.  */
         if (*topcindex == 0)
           {
             uint_fast32_t newarc = catomic_exchange_and_add (narcsp, 1);

             /* In rare cases it could happen that all entries in FROMS are
               occupied.  So we cannot count this anymore.  */
             if (newarc >= fromlimit)
              goto done;

             *topcindex = catomic_exchange_and_add (&fromidx, 1) + 1;
             fromp = &froms[*topcindex];

             fromp->here = &data[newarc];
             data[newarc].from_pc = frompc;
             data[newarc].self_pc = selfpc;
             data[newarc].count = 0;
             fromp->link = 0;
             catomic_increment (&narcs);

             break;
           }

         fromp = &froms[*topcindex];
       }
      else
       /* Found in.  */
       break;
    }

  /* Increment the counter.  */
  catomic_increment (&fromp->here->count);

 done:
  ;
}

Definition at line 173 of file dl-profile.c.

{
  char *filename;
  int fd;
  struct stat64 st;
  const ElfW(Phdr) *ph;
  ElfW(Addr) mapstart = ~((ElfW(Addr)) 0);
  ElfW(Addr) mapend = 0;
  struct gmon_hdr gmon_hdr;
  struct gmon_hist_hdr hist_hdr;
  char *hist, *cp;
  size_t idx;
  size_t tossize;
  size_t fromssize;
  uintptr_t highpc;
  uint16_t *kcount;
  size_t kcountsize;
  struct gmon_hdr *addr = NULL;
  off_t expected_size;
  /* See profil(2) where this is described.  */
  int s_scale;
#define SCALE_1_TO_1 0x10000L
  const char *errstr = NULL;

  /* Compute the size of the sections which contain program code.  */
  for (ph = GL(dl_profile_map)->l_phdr;
       ph < &GL(dl_profile_map)->l_phdr[GL(dl_profile_map)->l_phnum]; ++ph)
    if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X))
      {
       ElfW(Addr) start = (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
       ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + GLRO(dl_pagesize) - 1)
                       & ~(GLRO(dl_pagesize) - 1));

       if (start < mapstart)
         mapstart = start;
       if (end > mapend)
         mapend = end;
      }

  /* Now we can compute the size of the profiling data.  This is done
     with the same formulars as in `monstartup' (see gmon.c).  */
  running = 0;
  lowpc = ROUNDDOWN (mapstart + GL(dl_profile_map)->l_addr,
                   HISTFRACTION * sizeof (HISTCOUNTER));
  highpc = ROUNDUP (mapend + GL(dl_profile_map)->l_addr,
                  HISTFRACTION * sizeof (HISTCOUNTER));
  textsize = highpc - lowpc;
  kcountsize = textsize / HISTFRACTION;
  if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
    {
      /* If HASHFRACTION is a power of two, mcount can use shifting
        instead of integer division.  Precompute shift amount.

        This is a constant but the compiler cannot compile the
        expression away since the __ffs implementation is not known
        to the compiler.  Help the compiler by precomputing the
        usual cases.  */
      assert (HASHFRACTION == 2);

      if (sizeof (*froms) == 8)
       log_hashfraction = 4;
      else if (sizeof (*froms) == 16)
       log_hashfraction = 5;
      else
       log_hashfraction = __ffs (HASHFRACTION * sizeof (*froms)) - 1;
    }
  else
    log_hashfraction = -1;
  tossize = textsize / HASHFRACTION;
  fromlimit = textsize * ARCDENSITY / 100;
  if (fromlimit < MINARCS)
    fromlimit = MINARCS;
  if (fromlimit > MAXARCS)
    fromlimit = MAXARCS;
  fromssize = fromlimit * sizeof (struct here_fromstruct);

  expected_size = (sizeof (struct gmon_hdr)
                 + 4 + sizeof (struct gmon_hist_hdr) + kcountsize
                 + 4 + 4 + fromssize * sizeof (struct here_cg_arc_record));

  /* Create the gmon_hdr we expect or write.  */
  memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr));
  memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie));
  *(int32_t *) gmon_hdr.version = GMON_SHOBJ_VERSION;

  /* Create the hist_hdr we expect or write.  */
  *(char **) hist_hdr.low_pc = (char *) mapstart;
  *(char **) hist_hdr.high_pc = (char *) mapend;
  *(int32_t *) hist_hdr.hist_size = kcountsize / sizeof (HISTCOUNTER);
  *(int32_t *) hist_hdr.prof_rate = __profile_frequency ();
  if (sizeof (hist_hdr.dimen) >= sizeof ("seconds"))
    {
      memcpy (hist_hdr.dimen, "seconds", sizeof ("seconds"));
      memset (hist_hdr.dimen + sizeof ("seconds"), '\0',
             sizeof (hist_hdr.dimen) - sizeof ("seconds"));
    }
  else
    strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
  hist_hdr.dimen_abbrev = 's';

  /* First determine the output name.  We write in the directory
     OUTPUT_DIR and the name is composed from the shared objects
     soname (or the file name) and the ending ".profile".  */
  filename = (char *) alloca (strlen (GLRO(dl_profile_output)) + 1
                           + strlen (GLRO(dl_profile)) + sizeof ".profile");
  cp = __stpcpy (filename, GLRO(dl_profile_output));
  *cp++ = '/';
  __stpcpy (__stpcpy (cp, GLRO(dl_profile)), ".profile");

#ifdef O_NOFOLLOW
# define EXTRA_FLAGS | O_NOFOLLOW
#else
# define EXTRA_FLAGS
#endif
  fd = __open (filename, O_RDWR | O_CREAT EXTRA_FLAGS, DEFFILEMODE);
  if (fd == -1)
    {
      char buf[400];
      int errnum;

      /* We cannot write the profiling data so don't do anything.  */
      errstr = "%s: cannot open file: %s\n";
    print_error:
      errnum = errno;
      if (fd != -1)
       __close (fd);
      _dl_error_printf (errstr, filename,
                     __strerror_r (errnum, buf, sizeof buf));
      return;
    }

  if (__fxstat64 (_STAT_VER, fd, &st) < 0 || !S_ISREG (st.st_mode))
    {
      /* Not stat'able or not a regular file => don't use it.  */
      errstr = "%s: cannot stat file: %s\n";
      goto print_error;
    }

  /* Test the size.  If it does not match what we expect from the size
     values in the map MAP we don't use it and warn the user.  */
  if (st.st_size == 0)
    {
      /* We have to create the file.  */
      char buf[GLRO(dl_pagesize)];

      memset (buf, '\0', GLRO(dl_pagesize));

      if (__lseek (fd, expected_size & ~(GLRO(dl_pagesize) - 1), SEEK_SET) == -1)
       {
       cannot_create:
         errstr = "%s: cannot create file: %s\n";
         goto print_error;
       }

      if (TEMP_FAILURE_RETRY (__libc_write (fd, buf, (expected_size
                                                & (GLRO(dl_pagesize)
                                                  - 1))))
         < 0)
       goto cannot_create;
    }
  else if (st.st_size != expected_size)
    {
      __close (fd);
    wrong_format:

      if (addr != NULL)
       __munmap ((void *) addr, expected_size);

      _dl_error_printf ("%s: file is no correct profile data file for `%s'\n",
                     filename, GLRO(dl_profile));
      return;
    }

  addr = (struct gmon_hdr *) __mmap (NULL, expected_size, PROT_READ|PROT_WRITE,
                                 MAP_SHARED|MAP_FILE, fd, 0);
  if (addr == (struct gmon_hdr *) MAP_FAILED)
    {
      errstr = "%s: cannot map file: %s\n";
      goto print_error;
    }

  /* We don't need the file descriptor anymore.  */
  __close (fd);

  /* Pointer to data after the header.  */
  hist = (char *) (addr + 1);
  kcount = (uint16_t *) ((char *) hist + sizeof (uint32_t)
                      + sizeof (struct gmon_hist_hdr));

  /* Compute pointer to array of the arc information.  */
  narcsp = (uint32_t *) ((char *) kcount + kcountsize + sizeof (uint32_t));
  data = (struct here_cg_arc_record *) ((char *) narcsp + sizeof (uint32_t));

  if (st.st_size == 0)
    {
      /* Create the signature.  */
      memcpy (addr, &gmon_hdr, sizeof (struct gmon_hdr));

      *(uint32_t *) hist = GMON_TAG_TIME_HIST;
      memcpy (hist + sizeof (uint32_t), &hist_hdr,
             sizeof (struct gmon_hist_hdr));

      narcsp[-1] = GMON_TAG_CG_ARC;
    }
  else
    {
      /* Test the signature in the file.  */
      if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
         || *(uint32_t *) hist != GMON_TAG_TIME_HIST
         || memcmp (hist + sizeof (uint32_t), &hist_hdr,
                   sizeof (struct gmon_hist_hdr)) != 0
         || narcsp[-1] != GMON_TAG_CG_ARC)
       goto wrong_format;
    }

  /* Allocate memory for the froms data and the pointer to the tos records.  */
  tos = (uint16_t *) calloc (tossize + fromssize, 1);
  if (tos == NULL)
    {
      __munmap ((void *) addr, expected_size);
      _dl_fatal_printf ("Out of memory while initializing profiler\n");
      /* NOTREACHED */
    }

  froms = (struct here_fromstruct *) ((char *) tos + tossize);
  fromidx = 0;

  /* Now we have to process all the arc count entries.  BTW: it is
     not critical whether the *NARCSP value changes meanwhile.  Before
     we enter a new entry in to toset we will check that everything is
     available in TOS.  This happens in _dl_mcount.

     Loading the entries in reverse order should help to get the most
     frequently used entries at the front of the list.  */
  for (idx = narcs = MIN (*narcsp, fromlimit); idx > 0; )
    {
      size_t to_index;
      size_t newfromidx;
      --idx;
      to_index = (data[idx].self_pc / (HASHFRACTION * sizeof (*tos)));
      newfromidx = fromidx++;
      froms[newfromidx].here = &data[idx];
      froms[newfromidx].link = tos[to_index];
      tos[to_index] = newfromidx;
    }

  /* Setup counting data.  */
  if (kcountsize < highpc - lowpc)
    {
#if 0
      s_scale = ((double) kcountsize / (highpc - lowpc)) * SCALE_1_TO_1;
#else
      size_t range = highpc - lowpc;
      size_t quot = range / kcountsize;

      if (quot >= SCALE_1_TO_1)
       s_scale = 1;
      else if (quot >= SCALE_1_TO_1 / 256)
       s_scale = SCALE_1_TO_1 / quot;
      else if (range > ULONG_MAX / 256)
       s_scale = (SCALE_1_TO_1 * 256) / (range / (kcountsize / 256));
      else
       s_scale = (SCALE_1_TO_1 * 256) / ((range * 256) / kcountsize);
#endif
    }
  else
    s_scale = SCALE_1_TO_1;

  /* Start the profiler.  */
  __profil ((void *) kcount, kcountsize, lowpc, s_scale);

  /* Turn on profiling.  */
  running = 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 125 of file dl-profile.c.


Variable Documentation

struct here_cg_arc_record* data [static]

Definition at line 137 of file dl-profile.c.

volatile uint32_t fromidx [static]

Definition at line 161 of file dl-profile.c.

uint32_t fromlimit [static]

Definition at line 160 of file dl-profile.c.

struct here_fromstruct* froms [static]

Definition at line 159 of file dl-profile.c.

unsigned int log_hashfraction [static]

Definition at line 165 of file dl-profile.c.

uintptr_t lowpc [static]

Definition at line 163 of file dl-profile.c.

uint32_t narcs [static]

Definition at line 143 of file dl-profile.c.

volatile uint32_t* narcsp [static]

Definition at line 148 of file dl-profile.c.

int running [static]

Definition at line 140 of file dl-profile.c.

size_t textsize [static]

Definition at line 164 of file dl-profile.c.

volatile uint16_t* tos [static]

Definition at line 157 of file dl-profile.c.