Blame elf/dl-profile.c

Packit 6c4009
/* Profiling of shared libraries.
Packit 6c4009
   Copyright (C) 1997-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
Packit 6c4009
   Based on the BSD mcount implementation.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <inttypes.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include <sys/gmon.h>
Packit 6c4009
#include <sys/gmon_out.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <not-cancel.h>
Packit 6c4009
Packit 6c4009
/* The LD_PROFILE feature has to be implemented different to the
Packit 6c4009
   normal profiling using the gmon/ functions.  The problem is that an
Packit 6c4009
   arbitrary amount of processes simulataneously can be run using
Packit 6c4009
   profiling and all write the results in the same file.  To provide
Packit 6c4009
   this mechanism one could implement a complicated mechanism to merge
Packit 6c4009
   the content of two profiling runs or one could extend the file
Packit 6c4009
   format to allow more than one data set.  For the second solution we
Packit 6c4009
   would have the problem that the file can grow in size beyond any
Packit 6c4009
   limit and both solutions have the problem that the concurrency of
Packit 6c4009
   writing the results is a big problem.
Packit 6c4009
Packit 6c4009
   Another much simpler method is to use mmap to map the same file in
Packit 6c4009
   all using programs and modify the data in the mmap'ed area and so
Packit 6c4009
   also automatically on the disk.  Using the MAP_SHARED option of
Packit 6c4009
   mmap(2) this can be done without big problems in more than one
Packit 6c4009
   file.
Packit 6c4009
Packit 6c4009
   This approach is very different from the normal profiling.  We have
Packit 6c4009
   to use the profiling data in exactly the way they are expected to
Packit 6c4009
   be written to disk.  But the normal format used by gprof is not usable
Packit 6c4009
   to do this.  It is optimized for size.  It writes the tags as single
Packit 6c4009
   bytes but this means that the following 32/64 bit values are
Packit 6c4009
   unaligned.
Packit 6c4009
Packit 6c4009
   Therefore we use a new format.  This will look like this
Packit 6c4009
Packit 6c4009
					0  1  2  3	<- byte is 32 bit word
Packit 6c4009
	0000				g  m  o  n
Packit 6c4009
	0004				*version*	<- GMON_SHOBJ_VERSION
Packit 6c4009
	0008				00 00 00 00
Packit 6c4009
	000c				00 00 00 00
Packit 6c4009
	0010				00 00 00 00
Packit 6c4009
Packit 6c4009
	0014				*tag*		<- GMON_TAG_TIME_HIST
Packit 6c4009
	0018				?? ?? ?? ??
Packit 6c4009
					?? ?? ?? ??	<- 32/64 bit LowPC
Packit 6c4009
	0018+A				?? ?? ?? ??
Packit 6c4009
					?? ?? ?? ??	<- 32/64 bit HighPC
Packit 6c4009
	0018+2*A			*histsize*
Packit 6c4009
	001c+2*A			*profrate*
Packit 6c4009
	0020+2*A			s  e  c  o
Packit 6c4009
	0024+2*A			n  d  s  \0
Packit 6c4009
	0028+2*A			\0 \0 \0 \0
Packit 6c4009
	002c+2*A			\0 \0 \0
Packit 6c4009
	002f+2*A			s
Packit 6c4009
Packit 6c4009
	0030+2*A			?? ?? ?? ??	<- Count data
Packit 6c4009
	...				...
Packit 6c4009
	0030+2*A+K			?? ?? ?? ??
Packit 6c4009
Packit 6c4009
	0030+2*A+K			*tag*		<- GMON_TAG_CG_ARC
Packit 6c4009
	0034+2*A+K			*lastused*
Packit 6c4009
	0038+2*A+K			?? ?? ?? ??
Packit 6c4009
					?? ?? ?? ??	<- FromPC#1
Packit 6c4009
	0038+3*A+K			?? ?? ?? ??
Packit 6c4009
					?? ?? ?? ??	<- ToPC#1
Packit 6c4009
	0038+4*A+K			?? ?? ?? ??	<- Count#1
Packit 6c4009
	...				...		   ...
Packit 6c4009
	0038+(2*(CN-1)+2)*A+(CN-1)*4+K	?? ?? ?? ??
Packit 6c4009
					?? ?? ?? ??	<- FromPC#CGN
Packit 6c4009
	0038+(2*(CN-1)+3)*A+(CN-1)*4+K	?? ?? ?? ??
Packit 6c4009
					?? ?? ?? ??	<- ToPC#CGN
Packit 6c4009
	0038+(2*CN+2)*A+(CN-1)*4+K	?? ?? ?? ??	<- Count#CGN
Packit 6c4009
Packit 6c4009
   We put (for now?) no basic block information in the file since this would
Packit 6c4009
   introduce rase conditions among all the processes who want to write them.
Packit 6c4009
Packit 6c4009
   `K' is the number of count entries which is computed as
Packit 6c4009
Packit 6c4009
 		textsize / HISTFRACTION
Packit 6c4009
Packit 6c4009
   `CG' in the above table is the number of call graph arcs.  Normally,
Packit 6c4009
   the table is sparse and the profiling code writes out only the those
Packit 6c4009
   entries which are really used in the program run.  But since we must
Packit 6c4009
   not extend this table (the profiling file) we'll keep them all here.
Packit 6c4009
   So CN can be executed in advance as
Packit 6c4009
Packit 6c4009
		MINARCS <= textsize*(ARCDENSITY/100) <= MAXARCS
Packit 6c4009
Packit 6c4009
   Now the remaining question is: how to build the data structures we can
Packit 6c4009
   work with from this data.  We need the from set and must associate the
Packit 6c4009
   froms with all the associated tos.  We will do this by constructing this
Packit 6c4009
   data structures at the program start.  To do this we'll simply visit all
Packit 6c4009
   entries in the call graph table and add it to the appropriate list.  */
Packit 6c4009
Packit 6c4009
extern int __profile_frequency (void);
Packit 6c4009
libc_hidden_proto (__profile_frequency)
Packit 6c4009
Packit 6c4009
/* We define a special type to address the elements of the arc table.
Packit 6c4009
   This is basically the `gmon_cg_arc_record' format but it includes
Packit 6c4009
   the room for the tag and it uses real types.  */
Packit 6c4009
struct here_cg_arc_record
Packit 6c4009
  {
Packit 6c4009
    uintptr_t from_pc;
Packit 6c4009
    uintptr_t self_pc;
Packit 6c4009
    /* The count field is atomically incremented in _dl_mcount, which
Packit 6c4009
       requires it to be properly aligned for its type, and for this
Packit 6c4009
       alignment to be visible to the compiler.  The amount of data
Packit 6c4009
       before an array of this structure is calculated as
Packit 6c4009
       expected_size in _dl_start_profile.  Everything in that
Packit 6c4009
       calculation is a multiple of 4 bytes (in the case of
Packit 6c4009
       kcountsize, because it is derived from a subtraction of
Packit 6c4009
       page-aligned values, and the corresponding calculation in
Packit 6c4009
       __monstartup also ensures it is at least a multiple of the size
Packit 6c4009
       of u_long), so all copies of this field do in fact have the
Packit 6c4009
       appropriate alignment.  */
Packit 6c4009
    uint32_t count __attribute__ ((aligned (__alignof__ (uint32_t))));
Packit 6c4009
  } __attribute__ ((packed));
Packit 6c4009
Packit 6c4009
static struct here_cg_arc_record *data;
Packit 6c4009
Packit 6c4009
/* Nonzero if profiling is under way.  */
Packit 6c4009
static int running;
Packit 6c4009
Packit 6c4009
/* This is the number of entry which have been incorporated in the toset.  */
Packit 6c4009
static uint32_t narcs;
Packit 6c4009
/* This is a pointer to the object representing the number of entries
Packit 6c4009
   currently in the mmaped file.  At no point of time this has to be the
Packit 6c4009
   same as NARCS.  If it is equal all entries from the file are in our
Packit 6c4009
   lists.  */
Packit 6c4009
static volatile uint32_t *narcsp;
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct here_fromstruct
Packit 6c4009
  {
Packit 6c4009
    struct here_cg_arc_record volatile *here;
Packit 6c4009
    uint16_t link;
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
static volatile uint16_t *tos;
Packit 6c4009
Packit 6c4009
static struct here_fromstruct *froms;
Packit 6c4009
static uint32_t fromlimit;
Packit 6c4009
static volatile uint32_t fromidx;
Packit 6c4009
Packit 6c4009
static uintptr_t lowpc;
Packit 6c4009
static size_t textsize;
Packit 6c4009
static unsigned int log_hashfraction;
Packit 6c4009
Packit 6c4009
Packit 6c4009

Packit 6c4009
/* Set up profiling data to profile object desribed by MAP.  The output
Packit 6c4009
   file is found (or created) in OUTPUT_DIR.  */
Packit 6c4009
void
Packit 6c4009
_dl_start_profile (void)
Packit 6c4009
{
Packit 6c4009
  char *filename;
Packit 6c4009
  int fd;
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  const ElfW(Phdr) *ph;
Packit 6c4009
  ElfW(Addr) mapstart = ~((ElfW(Addr)) 0);
Packit 6c4009
  ElfW(Addr) mapend = 0;
Packit 6c4009
  char *hist, *cp;
Packit 6c4009
  size_t idx;
Packit 6c4009
  size_t tossize;
Packit 6c4009
  size_t fromssize;
Packit 6c4009
  uintptr_t highpc;
Packit 6c4009
  uint16_t *kcount;
Packit 6c4009
  size_t kcountsize;
Packit 6c4009
  struct gmon_hdr *addr = NULL;
Packit 6c4009
  off_t expected_size;
Packit 6c4009
  /* See profil(2) where this is described.  */
Packit 6c4009
  int s_scale;
Packit 6c4009
#define SCALE_1_TO_1	0x10000L
Packit 6c4009
  const char *errstr = NULL;
Packit 6c4009
Packit 6c4009
  /* Compute the size of the sections which contain program code.  */
Packit 6c4009
  for (ph = GL(dl_profile_map)->l_phdr;
Packit 6c4009
       ph < &GL(dl_profile_map)->l_phdr[GL(dl_profile_map)->l_phnum]; ++ph)
Packit 6c4009
    if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X))
Packit 6c4009
      {
Packit 6c4009
	ElfW(Addr) start = (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1));
Packit 6c4009
	ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + GLRO(dl_pagesize) - 1)
Packit 6c4009
			  & ~(GLRO(dl_pagesize) - 1));
Packit 6c4009
Packit 6c4009
	if (start < mapstart)
Packit 6c4009
	  mapstart = start;
Packit 6c4009
	if (end > mapend)
Packit 6c4009
	  mapend = end;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  /* Now we can compute the size of the profiling data.  This is done
Packit 6c4009
     with the same formulars as in `monstartup' (see gmon.c).  */
Packit 6c4009
  running = 0;
Packit 6c4009
  lowpc = ROUNDDOWN (mapstart + GL(dl_profile_map)->l_addr,
Packit 6c4009
		     HISTFRACTION * sizeof (HISTCOUNTER));
Packit 6c4009
  highpc = ROUNDUP (mapend + GL(dl_profile_map)->l_addr,
Packit 6c4009
		    HISTFRACTION * sizeof (HISTCOUNTER));
Packit 6c4009
  textsize = highpc - lowpc;
Packit 6c4009
  kcountsize = textsize / HISTFRACTION;
Packit 6c4009
  if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
Packit 6c4009
    {
Packit 6c4009
      /* If HASHFRACTION is a power of two, mcount can use shifting
Packit 6c4009
	 instead of integer division.  Precompute shift amount.
Packit 6c4009
Packit 6c4009
	 This is a constant but the compiler cannot compile the
Packit 6c4009
	 expression away since the __ffs implementation is not known
Packit 6c4009
	 to the compiler.  Help the compiler by precomputing the
Packit 6c4009
	 usual cases.  */
Packit 6c4009
      assert (HASHFRACTION == 2);
Packit 6c4009
Packit 6c4009
      if (sizeof (*froms) == 8)
Packit 6c4009
	log_hashfraction = 4;
Packit 6c4009
      else if (sizeof (*froms) == 16)
Packit 6c4009
	log_hashfraction = 5;
Packit 6c4009
      else
Packit 6c4009
	log_hashfraction = __ffs (HASHFRACTION * sizeof (*froms)) - 1;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    log_hashfraction = -1;
Packit 6c4009
  tossize = textsize / HASHFRACTION;
Packit 6c4009
  fromlimit = textsize * ARCDENSITY / 100;
Packit 6c4009
  if (fromlimit < MINARCS)
Packit 6c4009
    fromlimit = MINARCS;
Packit 6c4009
  if (fromlimit > MAXARCS)
Packit 6c4009
    fromlimit = MAXARCS;
Packit 6c4009
  fromssize = fromlimit * sizeof (struct here_fromstruct);
Packit 6c4009
Packit 6c4009
  expected_size = (sizeof (struct gmon_hdr)
Packit 6c4009
		   + 4 + sizeof (struct gmon_hist_hdr) + kcountsize
Packit 6c4009
		   + 4 + 4 + fromssize * sizeof (struct here_cg_arc_record));
Packit 6c4009
Packit 6c4009
  /* Create the gmon_hdr we expect or write.  */
Packit 6c4009
  struct real_gmon_hdr
Packit 6c4009
  {
Packit 6c4009
    char cookie[4];
Packit 6c4009
    int32_t version;
Packit 6c4009
    char spare[3 * 4];
Packit 6c4009
  } gmon_hdr;
Packit 6c4009
  if (sizeof (gmon_hdr) != sizeof (struct gmon_hdr)
Packit 6c4009
      || (offsetof (struct real_gmon_hdr, cookie)
Packit 6c4009
	  != offsetof (struct gmon_hdr, cookie))
Packit 6c4009
      || (offsetof (struct real_gmon_hdr, version)
Packit 6c4009
	  != offsetof (struct gmon_hdr, version)))
Packit 6c4009
    abort ();
Packit 6c4009
Packit 6c4009
  memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie));
Packit 6c4009
  gmon_hdr.version = GMON_SHOBJ_VERSION;
Packit 6c4009
  memset (gmon_hdr.spare, '\0', sizeof (gmon_hdr.spare));
Packit 6c4009
Packit 6c4009
  /* Create the hist_hdr we expect or write.  */
Packit 6c4009
  struct real_gmon_hist_hdr
Packit 6c4009
  {
Packit 6c4009
    char *low_pc;
Packit 6c4009
    char *high_pc;
Packit 6c4009
    int32_t hist_size;
Packit 6c4009
    int32_t prof_rate;
Packit 6c4009
    char dimen[15];
Packit 6c4009
    char dimen_abbrev;
Packit 6c4009
  } hist_hdr;
Packit 6c4009
  if (sizeof (hist_hdr) != sizeof (struct gmon_hist_hdr)
Packit 6c4009
      || (offsetof (struct real_gmon_hist_hdr, low_pc)
Packit 6c4009
	  != offsetof (struct gmon_hist_hdr, low_pc))
Packit 6c4009
      || (offsetof (struct real_gmon_hist_hdr, high_pc)
Packit 6c4009
	  != offsetof (struct gmon_hist_hdr, high_pc))
Packit 6c4009
      || (offsetof (struct real_gmon_hist_hdr, hist_size)
Packit 6c4009
	  != offsetof (struct gmon_hist_hdr, hist_size))
Packit 6c4009
      || (offsetof (struct real_gmon_hist_hdr, prof_rate)
Packit 6c4009
	  != offsetof (struct gmon_hist_hdr, prof_rate))
Packit 6c4009
      || (offsetof (struct real_gmon_hist_hdr, dimen)
Packit 6c4009
	  != offsetof (struct gmon_hist_hdr, dimen))
Packit 6c4009
      || (offsetof (struct real_gmon_hist_hdr, dimen_abbrev)
Packit 6c4009
	  != offsetof (struct gmon_hist_hdr, dimen_abbrev)))
Packit 6c4009
    abort ();
Packit 6c4009
Packit 6c4009
  hist_hdr.low_pc = (char *) mapstart;
Packit 6c4009
  hist_hdr.high_pc = (char *) mapend;
Packit 6c4009
  hist_hdr.hist_size = kcountsize / sizeof (HISTCOUNTER);
Packit 6c4009
  hist_hdr.prof_rate = __profile_frequency ();
Packit 6c4009
  if (sizeof (hist_hdr.dimen) >= sizeof ("seconds"))
Packit 6c4009
    {
Packit 6c4009
      memcpy (hist_hdr.dimen, "seconds", sizeof ("seconds"));
Packit 6c4009
      memset (hist_hdr.dimen + sizeof ("seconds"), '\0',
Packit 6c4009
	      sizeof (hist_hdr.dimen) - sizeof ("seconds"));
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
Packit 6c4009
  hist_hdr.dimen_abbrev = 's';
Packit 6c4009
Packit 6c4009
  /* First determine the output name.  We write in the directory
Packit 6c4009
     OUTPUT_DIR and the name is composed from the shared objects
Packit 6c4009
     soname (or the file name) and the ending ".profile".  */
Packit 6c4009
  filename = (char *) alloca (strlen (GLRO(dl_profile_output)) + 1
Packit 6c4009
			      + strlen (GLRO(dl_profile)) + sizeof ".profile");
Packit 6c4009
  cp = __stpcpy (filename, GLRO(dl_profile_output));
Packit 6c4009
  *cp++ = '/';
Packit 6c4009
  __stpcpy (__stpcpy (cp, GLRO(dl_profile)), ".profile");
Packit 6c4009
Packit 6c4009
  fd = __open64_nocancel (filename, O_RDWR|O_CREAT|O_NOFOLLOW, DEFFILEMODE);
Packit 6c4009
  if (fd == -1)
Packit 6c4009
    {
Packit 6c4009
      char buf[400];
Packit 6c4009
      int errnum;
Packit 6c4009
Packit 6c4009
      /* We cannot write the profiling data so don't do anything.  */
Packit 6c4009
      errstr = "%s: cannot open file: %s\n";
Packit 6c4009
    print_error:
Packit 6c4009
      errnum = errno;
Packit 6c4009
      if (fd != -1)
Packit 6c4009
	__close_nocancel (fd);
Packit 6c4009
      _dl_error_printf (errstr, filename,
Packit 6c4009
			__strerror_r (errnum, buf, sizeof buf));
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__fxstat64 (_STAT_VER, fd, &st) < 0 || !S_ISREG (st.st_mode))
Packit 6c4009
    {
Packit 6c4009
      /* Not stat'able or not a regular file => don't use it.  */
Packit 6c4009
      errstr = "%s: cannot stat file: %s\n";
Packit 6c4009
      goto print_error;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Test the size.  If it does not match what we expect from the size
Packit 6c4009
     values in the map MAP we don't use it and warn the user.  */
Packit 6c4009
  if (st.st_size == 0)
Packit 6c4009
    {
Packit 6c4009
      /* We have to create the file.  */
Packit 6c4009
      char buf[GLRO(dl_pagesize)];
Packit 6c4009
Packit 6c4009
      memset (buf, '\0', GLRO(dl_pagesize));
Packit 6c4009
Packit 6c4009
      if (__lseek (fd, expected_size & ~(GLRO(dl_pagesize) - 1), SEEK_SET) == -1)
Packit 6c4009
	{
Packit 6c4009
	cannot_create:
Packit 6c4009
	  errstr = "%s: cannot create file: %s\n";
Packit 6c4009
	  goto print_error;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (TEMP_FAILURE_RETRY
Packit 6c4009
	  (__write_nocancel (fd, buf, (expected_size & (GLRO(dl_pagesize) - 1))))
Packit 6c4009
	  < 0)
Packit 6c4009
	goto cannot_create;
Packit 6c4009
    }
Packit 6c4009
  else if (st.st_size != expected_size)
Packit 6c4009
    {
Packit 6c4009
      __close_nocancel (fd);
Packit 6c4009
    wrong_format:
Packit 6c4009
Packit 6c4009
      if (addr != NULL)
Packit 6c4009
	__munmap ((void *) addr, expected_size);
Packit 6c4009
Packit 6c4009
      _dl_error_printf ("%s: file is no correct profile data file for `%s'\n",
Packit 6c4009
			filename, GLRO(dl_profile));
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  addr = (struct gmon_hdr *) __mmap (NULL, expected_size, PROT_READ|PROT_WRITE,
Packit 6c4009
				     MAP_SHARED|MAP_FILE, fd, 0);
Packit 6c4009
  if (addr == (struct gmon_hdr *) MAP_FAILED)
Packit 6c4009
    {
Packit 6c4009
      errstr = "%s: cannot map file: %s\n";
Packit 6c4009
      goto print_error;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* We don't need the file descriptor anymore.  */
Packit 6c4009
  __close_nocancel (fd);
Packit 6c4009
Packit 6c4009
  /* Pointer to data after the header.  */
Packit 6c4009
  hist = (char *) (addr + 1);
Packit 6c4009
  kcount = (uint16_t *) ((char *) hist + sizeof (uint32_t)
Packit 6c4009
			 + sizeof (struct gmon_hist_hdr));
Packit 6c4009
Packit 6c4009
  /* Compute pointer to array of the arc information.  */
Packit 6c4009
  narcsp = (uint32_t *) ((char *) kcount + kcountsize + sizeof (uint32_t));
Packit 6c4009
  data = (struct here_cg_arc_record *) ((char *) narcsp + sizeof (uint32_t));
Packit 6c4009
Packit 6c4009
  if (st.st_size == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Create the signature.  */
Packit 6c4009
      memcpy (addr, &gmon_hdr, sizeof (struct gmon_hdr));
Packit 6c4009
Packit 6c4009
      *(uint32_t *) hist = GMON_TAG_TIME_HIST;
Packit 6c4009
      memcpy (hist + sizeof (uint32_t), &hist_hdr,
Packit 6c4009
	      sizeof (struct gmon_hist_hdr));
Packit 6c4009
Packit 6c4009
      narcsp[-1] = GMON_TAG_CG_ARC;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* Test the signature in the file.  */
Packit 6c4009
      if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
Packit 6c4009
	  || *(uint32_t *) hist != GMON_TAG_TIME_HIST
Packit 6c4009
	  || memcmp (hist + sizeof (uint32_t), &hist_hdr,
Packit 6c4009
		     sizeof (struct gmon_hist_hdr)) != 0
Packit 6c4009
	  || narcsp[-1] != GMON_TAG_CG_ARC)
Packit 6c4009
	goto wrong_format;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Allocate memory for the froms data and the pointer to the tos records.  */
Packit 6c4009
  tos = (uint16_t *) calloc (tossize + fromssize, 1);
Packit 6c4009
  if (tos == NULL)
Packit 6c4009
    {
Packit 6c4009
      __munmap ((void *) addr, expected_size);
Packit 6c4009
      _dl_fatal_printf ("Out of memory while initializing profiler\n");
Packit 6c4009
      /* NOTREACHED */
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  froms = (struct here_fromstruct *) ((char *) tos + tossize);
Packit 6c4009
  fromidx = 0;
Packit 6c4009
Packit 6c4009
  /* Now we have to process all the arc count entries.  BTW: it is
Packit 6c4009
     not critical whether the *NARCSP value changes meanwhile.  Before
Packit 6c4009
     we enter a new entry in to toset we will check that everything is
Packit 6c4009
     available in TOS.  This happens in _dl_mcount.
Packit 6c4009
Packit 6c4009
     Loading the entries in reverse order should help to get the most
Packit 6c4009
     frequently used entries at the front of the list.  */
Packit 6c4009
  for (idx = narcs = MIN (*narcsp, fromlimit); idx > 0; )
Packit 6c4009
    {
Packit 6c4009
      size_t to_index;
Packit 6c4009
      size_t newfromidx;
Packit 6c4009
      --idx;
Packit 6c4009
      to_index = (data[idx].self_pc / (HASHFRACTION * sizeof (*tos)));
Packit 6c4009
      newfromidx = fromidx++;
Packit 6c4009
      froms[newfromidx].here = &data[idx];
Packit 6c4009
      froms[newfromidx].link = tos[to_index];
Packit 6c4009
      tos[to_index] = newfromidx;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Setup counting data.  */
Packit 6c4009
  if (kcountsize < highpc - lowpc)
Packit 6c4009
    {
Packit 6c4009
#if 0
Packit 6c4009
      s_scale = ((double) kcountsize / (highpc - lowpc)) * SCALE_1_TO_1;
Packit 6c4009
#else
Packit 6c4009
      size_t range = highpc - lowpc;
Packit 6c4009
      size_t quot = range / kcountsize;
Packit 6c4009
Packit 6c4009
      if (quot >= SCALE_1_TO_1)
Packit 6c4009
	s_scale = 1;
Packit 6c4009
      else if (quot >= SCALE_1_TO_1 / 256)
Packit 6c4009
	s_scale = SCALE_1_TO_1 / quot;
Packit 6c4009
      else if (range > ULONG_MAX / 256)
Packit 6c4009
	s_scale = (SCALE_1_TO_1 * 256) / (range / (kcountsize / 256));
Packit 6c4009
      else
Packit 6c4009
	s_scale = (SCALE_1_TO_1 * 256) / ((range * 256) / kcountsize);
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    s_scale = SCALE_1_TO_1;
Packit 6c4009
Packit 6c4009
  /* Start the profiler.  */
Packit 6c4009
  __profil ((void *) kcount, kcountsize, lowpc, s_scale);
Packit 6c4009
Packit 6c4009
  /* Turn on profiling.  */
Packit 6c4009
  running = 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_dl_mcount (ElfW(Addr) frompc, ElfW(Addr) selfpc)
Packit 6c4009
{
Packit 6c4009
  volatile uint16_t *topcindex;
Packit 6c4009
  size_t i, fromindex;
Packit 6c4009
  struct here_fromstruct *fromp;
Packit 6c4009
Packit 6c4009
  if (! running)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  /* Compute relative addresses.  The shared object can be loaded at
Packit 6c4009
     any address.  The value of frompc could be anything.  We cannot
Packit 6c4009
     restrict it in any way, just set to a fixed value (0) in case it
Packit 6c4009
     is outside the allowed range.  These calls show up as calls from
Packit 6c4009
     <external> in the gprof output.  */
Packit 6c4009
  frompc -= lowpc;
Packit 6c4009
  if (frompc >= textsize)
Packit 6c4009
    frompc = 0;
Packit 6c4009
  selfpc -= lowpc;
Packit 6c4009
  if (selfpc >= textsize)
Packit 6c4009
    goto done;
Packit 6c4009
Packit 6c4009
  /* Getting here we now have to find out whether the location was
Packit 6c4009
     already used.  If yes we are lucky and only have to increment a
Packit 6c4009
     counter (this also has to be atomic).  If the entry is new things
Packit 6c4009
     are getting complicated...  */
Packit 6c4009
Packit 6c4009
  /* Avoid integer divide if possible.  */
Packit 6c4009
  if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
Packit 6c4009
    i = selfpc >> log_hashfraction;
Packit 6c4009
  else
Packit 6c4009
    i = selfpc / (HASHFRACTION * sizeof (*tos));
Packit 6c4009
Packit 6c4009
  topcindex = &tos[i];
Packit 6c4009
  fromindex = *topcindex;
Packit 6c4009
Packit 6c4009
  if (fromindex == 0)
Packit 6c4009
    goto check_new_or_add;
Packit 6c4009
Packit 6c4009
  fromp = &froms[fromindex];
Packit 6c4009
Packit 6c4009
  /* We have to look through the chain of arcs whether there is already
Packit 6c4009
     an entry for our arc.  */
Packit 6c4009
  while (fromp->here->from_pc != frompc)
Packit 6c4009
    {
Packit 6c4009
      if (fromp->link != 0)
Packit 6c4009
	do
Packit 6c4009
	  fromp = &froms[fromp->link];
Packit 6c4009
	while (fromp->link != 0 && fromp->here->from_pc != frompc);
Packit 6c4009
Packit 6c4009
      if (fromp->here->from_pc != frompc)
Packit 6c4009
	{
Packit 6c4009
	  topcindex = &fromp->link;
Packit 6c4009
Packit 6c4009
	check_new_or_add:
Packit 6c4009
	  /* Our entry is not among the entries we read so far from the
Packit 6c4009
	     data file.  Now see whether we have to update the list.  */
Packit 6c4009
	  while (narcs != *narcsp && narcs < fromlimit)
Packit 6c4009
	    {
Packit 6c4009
	      size_t to_index;
Packit 6c4009
	      size_t newfromidx;
Packit 6c4009
	      to_index = (data[narcs].self_pc
Packit 6c4009
			  / (HASHFRACTION * sizeof (*tos)));
Packit 6c4009
	      newfromidx = catomic_exchange_and_add (&fromidx, 1) + 1;
Packit 6c4009
	      froms[newfromidx].here = &data[narcs];
Packit 6c4009
	      froms[newfromidx].link = tos[to_index];
Packit 6c4009
	      tos[to_index] = newfromidx;
Packit 6c4009
	      catomic_increment (&narcs);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* If we still have no entry stop searching and insert.  */
Packit 6c4009
	  if (*topcindex == 0)
Packit 6c4009
	    {
Packit 6c4009
	      uint_fast32_t newarc = catomic_exchange_and_add (narcsp, 1);
Packit 6c4009
Packit 6c4009
	      /* In rare cases it could happen that all entries in FROMS are
Packit 6c4009
		 occupied.  So we cannot count this anymore.  */
Packit 6c4009
	      if (newarc >= fromlimit)
Packit 6c4009
		goto done;
Packit 6c4009
Packit 6c4009
	      *topcindex = catomic_exchange_and_add (&fromidx, 1) + 1;
Packit 6c4009
	      fromp = &froms[*topcindex];
Packit 6c4009
Packit 6c4009
	      fromp->here = &data[newarc];
Packit 6c4009
	      data[newarc].from_pc = frompc;
Packit 6c4009
	      data[newarc].self_pc = selfpc;
Packit 6c4009
	      data[newarc].count = 0;
Packit 6c4009
	      fromp->link = 0;
Packit 6c4009
	      catomic_increment (&narcs);
Packit 6c4009
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  fromp = &froms[*topcindex];
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	/* Found in.  */
Packit 6c4009
	break;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Increment the counter.  */
Packit 6c4009
  catomic_increment (&fromp->here->count);
Packit 6c4009
Packit 6c4009
 done:
Packit 6c4009
  ;
Packit 6c4009
}
Packit 6c4009
rtld_hidden_def (_dl_mcount)