(root)/
glibc-2.38/
gmon/
gmon.c
       1  /*-
       2   * Copyright (c) 1983, 1992, 1993, 2011
       3   *	The Regents of the University of California.  All rights reserved.
       4   *
       5   * Redistribution and use in source and binary forms, with or without
       6   * modification, are permitted provided that the following conditions
       7   * are met:
       8   * 1. Redistributions of source code must retain the above copyright
       9   *    notice, this list of conditions and the following disclaimer.
      10   * 2. Redistributions in binary form must reproduce the above copyright
      11   *    notice, this list of conditions and the following disclaimer in the
      12   *    documentation and/or other materials provided with the distribution.
      13   * 4. Neither the name of the University nor the names of its contributors
      14   *    may be used to endorse or promote products derived from this software
      15   *    without specific prior written permission.
      16   *
      17   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
      18   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      19   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      20   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
      21   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      22   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      23   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      24   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      25   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      26   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      27   * SUCH DAMAGE.
      28   */
      29  #include <sys/param.h>
      30  #include <sys/time.h>
      31  #include <sys/gmon.h>
      32  #include <sys/gmon_out.h>
      33  #include <sys/uio.h>
      34  
      35  #include <errno.h>
      36  #include <stdio.h>
      37  #include <fcntl.h>
      38  #include <unistd.h>
      39  #include <wchar.h>
      40  
      41  #include <stdio.h>
      42  #include <stdlib.h>
      43  #include <string.h>
      44  #include <stddef.h>
      45  #include <unistd.h>
      46  #include <libc-internal.h>
      47  #include <not-cancel.h>
      48  
      49  #define TUNABLE_NAMESPACE gmon
      50  #include <elf/dl-tunables.h>
      51  
      52  #ifdef PIC
      53  # include <link.h>
      54  
      55  static int
      56  callback (struct dl_phdr_info *info, size_t size, void *data)
      57  {
      58    if (info->dlpi_name[0] == '\0')
      59      {
      60        /* The link map for the executable is created by calling
      61  	 _dl_new_object with "" as filename.  dl_iterate_phdr
      62  	 calls the callback function with filename from the
      63  	 link map as dlpi_name.  */
      64        u_long *load_address = data;
      65        *load_address = (u_long) info->dlpi_addr;
      66        return 1;
      67      }
      68  
      69    return 0;
      70  }
      71  #endif
      72  
      73  /*  Head of basic-block list or NULL. */
      74  struct __bb *__bb_head attribute_hidden;
      75  
      76  struct gmonparam _gmonparam attribute_hidden = { GMON_PROF_OFF };
      77  
      78  /*
      79   * See profil(2) where this is described:
      80   */
      81  static int	s_scale;
      82  #define		SCALE_1_TO_1	0x10000L
      83  
      84  #define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1)
      85  
      86  void moncontrol (int mode);
      87  void __moncontrol (int mode);
      88  libc_hidden_proto (__moncontrol)
      89  static void write_hist (int fd, u_long load_address);
      90  static void write_call_graph (int fd, u_long load_address);
      91  static void write_bb_counts (int fd);
      92  
      93  /*
      94   * Control profiling
      95   *	profiling is what mcount checks to see if
      96   *	all the data structures are ready.
      97   */
      98  void
      99  __moncontrol (int mode)
     100  {
     101    struct gmonparam *p = &_gmonparam;
     102  
     103    /* Treat start request as stop if error or gmon not initialized. */
     104    if (mode && p->state != GMON_PROF_ERROR && p->tos != NULL)
     105      {
     106        /* start */
     107        __profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale);
     108        p->state = GMON_PROF_ON;
     109      }
     110    else
     111      {
     112        /* stop */
     113        __profil(NULL, 0, 0, 0);
     114        /* Don't change the state if we ran into an error. */
     115        if (p->state != GMON_PROF_ERROR)
     116          p->state = GMON_PROF_OFF;
     117      }
     118  }
     119  libc_hidden_def (__moncontrol)
     120  weak_alias (__moncontrol, moncontrol)
     121  
     122  
     123  void
     124  __monstartup (u_long lowpc, u_long highpc)
     125  {
     126    int o;
     127    char *cp;
     128    struct gmonparam *p = &_gmonparam;
     129    long int minarcs, maxarcs;
     130  
     131    /* Read minarcs/maxarcs tunables. */
     132    minarcs = TUNABLE_GET (minarcs, int32_t, NULL);
     133    maxarcs = TUNABLE_GET (maxarcs, int32_t, NULL);
     134    if (maxarcs < minarcs)
     135      {
     136        ERR("monstartup: maxarcs < minarcs, setting maxarcs = minarcs\n");
     137        maxarcs = minarcs;
     138      }
     139  
     140    /*
     141     * If we are incorrectly called twice in a row (without an
     142     * intervening call to _mcleanup), ignore the second call to
     143     * prevent leaking memory.
     144     */
     145    if (p->tos != NULL)
     146        return;
     147  
     148    /*
     149     * round lowpc and highpc to multiples of the density we're using
     150     * so the rest of the scaling (here and in gprof) stays in ints.
     151     */
     152    p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
     153    p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
     154    p->textsize = p->highpc - p->lowpc;
     155    /* This looks like a typo, but it's here to align the p->froms
     156       section.  */
     157    p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms));
     158    p->hashfraction = HASHFRACTION;
     159    p->log_hashfraction = -1;
     160    /* The following test must be kept in sync with the corresponding
     161       test in mcount.c.  */
     162    if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) {
     163        /* if HASHFRACTION is a power of two, mcount can use shifting
     164  	 instead of integer division.  Precompute shift amount. */
     165        p->log_hashfraction = ffs(p->hashfraction * sizeof(*p->froms)) - 1;
     166    }
     167    p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms));
     168    p->tolimit = p->textsize * ARCDENSITY / 100;
     169    if (p->tolimit < minarcs)
     170      p->tolimit = minarcs;
     171    else if (p->tolimit > maxarcs)
     172      p->tolimit = maxarcs;
     173    p->tossize = p->tolimit * sizeof(struct tostruct);
     174  
     175    cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1);
     176    if (! cp)
     177      {
     178        ERR("monstartup: out of memory\n");
     179        p->tos = NULL;
     180        p->state = GMON_PROF_ERROR;
     181        return;
     182      }
     183    p->tos = (struct tostruct *)cp;
     184    cp += p->tossize;
     185    p->kcount = (HISTCOUNTER *)cp;
     186    cp += p->kcountsize;
     187    p->froms = (ARCINDEX *)cp;
     188  
     189    p->tos[0].link = 0;
     190  
     191    o = p->highpc - p->lowpc;
     192    if (p->kcountsize < (u_long) o)
     193      {
     194  #ifndef hp300
     195        s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
     196  #else
     197        /* avoid floating point operations */
     198        int quot = o / p->kcountsize;
     199  
     200        if (quot >= 0x10000)
     201  	s_scale = 1;
     202        else if (quot >= 0x100)
     203  	s_scale = 0x10000 / quot;
     204        else if (o >= 0x800000)
     205  	s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
     206        else
     207  	s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
     208  #endif
     209      } else
     210        s_scale = SCALE_1_TO_1;
     211  
     212    __moncontrol(1);
     213  }
     214  weak_alias (__monstartup, monstartup)
     215  
     216  
     217  static void
     218  write_hist (int fd, u_long load_address)
     219  {
     220    u_char tag = GMON_TAG_TIME_HIST;
     221  
     222    if (_gmonparam.kcountsize > 0)
     223      {
     224        struct real_gmon_hist_hdr
     225        {
     226  	char *low_pc;
     227  	char *high_pc;
     228  	int32_t hist_size;
     229  	int32_t prof_rate;
     230  	char dimen[15];
     231  	char dimen_abbrev;
     232        } thdr;
     233        struct iovec iov[3] =
     234  	{
     235  	  { &tag, sizeof (tag) },
     236  	  { &thdr, sizeof (struct gmon_hist_hdr) },
     237  	  { _gmonparam.kcount, _gmonparam.kcountsize }
     238  	};
     239  
     240        if (sizeof (thdr) != sizeof (struct gmon_hist_hdr)
     241  	  || (offsetof (struct real_gmon_hist_hdr, low_pc)
     242  	      != offsetof (struct gmon_hist_hdr, low_pc))
     243  	  || (offsetof (struct real_gmon_hist_hdr, high_pc)
     244  	      != offsetof (struct gmon_hist_hdr, high_pc))
     245  	  || (offsetof (struct real_gmon_hist_hdr, hist_size)
     246  	      != offsetof (struct gmon_hist_hdr, hist_size))
     247  	  || (offsetof (struct real_gmon_hist_hdr, prof_rate)
     248  	      != offsetof (struct gmon_hist_hdr, prof_rate))
     249  	  || (offsetof (struct real_gmon_hist_hdr, dimen)
     250  	      != offsetof (struct gmon_hist_hdr, dimen))
     251  	  || (offsetof (struct real_gmon_hist_hdr, dimen_abbrev)
     252  	      != offsetof (struct gmon_hist_hdr, dimen_abbrev)))
     253  	abort ();
     254  
     255        thdr.low_pc = (char *) _gmonparam.lowpc - load_address;
     256        thdr.high_pc = (char *) _gmonparam.highpc - load_address;
     257        thdr.hist_size = _gmonparam.kcountsize / sizeof (HISTCOUNTER);
     258        thdr.prof_rate = __profile_frequency ();
     259        strncpy (thdr.dimen, "seconds", sizeof (thdr.dimen));
     260        thdr.dimen_abbrev = 's';
     261  
     262        __writev_nocancel_nostatus (fd, iov, 3);
     263      }
     264  }
     265  
     266  
     267  static void
     268  write_call_graph (int fd, u_long load_address)
     269  {
     270  #define NARCS_PER_WRITEV	32
     271    u_char tag = GMON_TAG_CG_ARC;
     272    struct gmon_cg_arc_record raw_arc[NARCS_PER_WRITEV]
     273      __attribute__ ((aligned (__alignof__ (char*))));
     274    ARCINDEX from_index, to_index;
     275    u_long from_len;
     276    u_long frompc;
     277    struct iovec iov[2 * NARCS_PER_WRITEV];
     278    int nfilled;
     279  
     280    for (nfilled = 0; nfilled < NARCS_PER_WRITEV; ++nfilled)
     281      {
     282        iov[2 * nfilled].iov_base = &tag;
     283        iov[2 * nfilled].iov_len = sizeof (tag);
     284  
     285        iov[2 * nfilled + 1].iov_base = &raw_arc[nfilled];
     286        iov[2 * nfilled + 1].iov_len = sizeof (struct gmon_cg_arc_record);
     287      }
     288  
     289    nfilled = 0;
     290    from_len = _gmonparam.fromssize / sizeof (*_gmonparam.froms);
     291    for (from_index = 0; from_index < from_len; ++from_index)
     292      {
     293        if (_gmonparam.froms[from_index] == 0)
     294  	continue;
     295  
     296        frompc = _gmonparam.lowpc;
     297        frompc += (from_index * _gmonparam.hashfraction
     298  		 * sizeof (*_gmonparam.froms));
     299        for (to_index = _gmonparam.froms[from_index];
     300  	   to_index != 0;
     301  	   to_index = _gmonparam.tos[to_index].link)
     302  	{
     303  	  struct arc
     304  	    {
     305  	      char *frompc;
     306  	      char *selfpc;
     307  	      int32_t count;
     308  	    }
     309  	  arc;
     310  
     311  	  arc.frompc = (char *) frompc - load_address;
     312  	  arc.selfpc = ((char *) _gmonparam.tos[to_index].selfpc
     313  			- load_address);
     314  	  arc.count  = _gmonparam.tos[to_index].count;
     315  	  memcpy (raw_arc + nfilled, &arc, sizeof (raw_arc [0]));
     316  
     317  	  if (++nfilled == NARCS_PER_WRITEV)
     318  	    {
     319  	      __writev_nocancel_nostatus (fd, iov, 2 * nfilled);
     320  	      nfilled = 0;
     321  	    }
     322  	}
     323      }
     324    if (nfilled > 0)
     325      __writev_nocancel_nostatus (fd, iov, 2 * nfilled);
     326  }
     327  
     328  
     329  static void
     330  write_bb_counts (int fd)
     331  {
     332    struct __bb *grp;
     333    u_char tag = GMON_TAG_BB_COUNT;
     334    size_t ncounts;
     335    size_t i;
     336  
     337    struct iovec bbhead[2] =
     338      {
     339        { &tag, sizeof (tag) },
     340        { &ncounts, sizeof (ncounts) }
     341      };
     342    struct iovec bbbody[8];
     343    size_t nfilled;
     344  
     345    for (i = 0; i < (sizeof (bbbody) / sizeof (bbbody[0])); i += 2)
     346      {
     347        bbbody[i].iov_len = sizeof (grp->addresses[0]);
     348        bbbody[i + 1].iov_len = sizeof (grp->counts[0]);
     349      }
     350  
     351    /* Write each group of basic-block info (all basic-blocks in a
     352       compilation unit form a single group). */
     353  
     354    for (grp = __bb_head; grp; grp = grp->next)
     355      {
     356        ncounts = grp->ncounts;
     357        __writev_nocancel_nostatus (fd, bbhead, 2);
     358        for (nfilled = i = 0; i < ncounts; ++i)
     359  	{
     360  	  if (nfilled > (sizeof (bbbody) / sizeof (bbbody[0])) - 2)
     361  	    {
     362  	      __writev_nocancel_nostatus (fd, bbbody, nfilled);
     363  	      nfilled = 0;
     364  	    }
     365  
     366  	  bbbody[nfilled++].iov_base = (char *) &grp->addresses[i];
     367  	  bbbody[nfilled++].iov_base = &grp->counts[i];
     368  	}
     369        if (nfilled > 0)
     370  	__writev_nocancel_nostatus (fd, bbbody, nfilled);
     371      }
     372  }
     373  
     374  
     375  static void
     376  write_gmon (void)
     377  {
     378      int fd = -1;
     379      char *env;
     380  
     381      env = getenv ("GMON_OUT_PREFIX");
     382      if (env != NULL && !__libc_enable_secure)
     383        {
     384  	size_t len = strlen (env);
     385  	char buf[len + 20];
     386  	__snprintf (buf, sizeof (buf), "%s.%u", env, __getpid ());
     387  	fd = __open_nocancel (buf, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW
     388  			      | O_CLOEXEC, 0666);
     389        }
     390  
     391      if (fd == -1)
     392        {
     393  	fd = __open_nocancel ("gmon.out", O_CREAT | O_TRUNC | O_WRONLY
     394  			      | O_NOFOLLOW | O_CLOEXEC, 0666);
     395  	if (fd < 0)
     396  	  {
     397  	    char buf[300];
     398  	    int errnum = errno;
     399  	    __fxprintf (NULL, "_mcleanup: gmon.out: %s\n",
     400  			__strerror_r (errnum, buf, sizeof buf));
     401  	    return;
     402  	  }
     403        }
     404  
     405      /* write gmon.out header: */
     406      struct real_gmon_hdr
     407      {
     408        char cookie[4];
     409        int32_t version;
     410        char spare[3 * 4];
     411      } ghdr;
     412      if (sizeof (ghdr) != sizeof (struct gmon_hdr)
     413  	|| (offsetof (struct real_gmon_hdr, cookie)
     414  	    != offsetof (struct gmon_hdr, cookie))
     415  	|| (offsetof (struct real_gmon_hdr, version)
     416  	    != offsetof (struct gmon_hdr, version)))
     417        abort ();
     418      memcpy (&ghdr.cookie[0], GMON_MAGIC, sizeof (ghdr.cookie));
     419      ghdr.version = GMON_VERSION;
     420      memset (ghdr.spare, '\0', sizeof (ghdr.spare));
     421      __write_nocancel (fd, &ghdr, sizeof (struct gmon_hdr));
     422  
     423      /* Get load_address to profile PIE.  */
     424      u_long load_address = 0;
     425  #ifdef PIC
     426      __dl_iterate_phdr (callback, &load_address);
     427  #endif
     428  
     429      /* write PC histogram: */
     430      write_hist (fd, load_address);
     431  
     432      /* write call-graph: */
     433      write_call_graph (fd, load_address);
     434  
     435      /* write basic-block execution counts: */
     436      write_bb_counts (fd);
     437  
     438      __close_nocancel_nostatus (fd);
     439  }
     440  
     441  
     442  void
     443  __write_profiling (void)
     444  {
     445    int save = _gmonparam.state;
     446    _gmonparam.state = GMON_PROF_OFF;
     447    if (save == GMON_PROF_ON)
     448      write_gmon ();
     449    _gmonparam.state = save;
     450  }
     451  #ifndef SHARED
     452  /* This symbol isn't used anywhere in the DSO and it is not exported.
     453     This would normally mean it should be removed to get the same API
     454     in static libraries.  But since profiling is special in static libs
     455     anyway we keep it.  But not when building the DSO since some
     456     quality assurance tests will otherwise trigger.  */
     457  weak_alias (__write_profiling, write_profiling)
     458  #endif
     459  
     460  
     461  void
     462  _mcleanup (void)
     463  {
     464    __moncontrol (0);
     465  
     466    if (_gmonparam.state != GMON_PROF_ERROR && _gmonparam.tos != NULL)
     467      write_gmon ();
     468  
     469    /* free the memory. */
     470    free (_gmonparam.tos);
     471  
     472    /* reset buffer to initial state for safety */
     473    memset(&_gmonparam, 0, sizeof _gmonparam);
     474    /* somewhat confusingly, ON=0, OFF=3 */
     475    _gmonparam.state = GMON_PROF_OFF;
     476  }