Blame malloc/mtrace.c

Packit Service 82fcde
/* More debugging hooks for `malloc'.
Packit Service 82fcde
   Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
                 Written April 2, 1991 by John Gilmore of Cygnus Support.
Packit Service 82fcde
                 Based on mcheck.c by Mike Haertel.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#ifndef _MALLOC_INTERNAL
Packit Service 82fcde
# define _MALLOC_INTERNAL
Packit Service 82fcde
# include <malloc.h>
Packit Service 82fcde
# include <mcheck.h>
Packit Service 82fcde
# include <libc-lock.h>
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
#include <dlfcn.h>
Packit Service 82fcde
#include <fcntl.h>
Packit Service 82fcde
#include <stdio.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
Packit Service 82fcde
#include <_itoa.h>
Packit Service 82fcde
Packit Service 82fcde
#include <libc-internal.h>
Packit Service 82fcde
#include <dso_handle.h>
Packit Service 82fcde
Packit Service 82fcde
#include <libio/iolibio.h>
Packit Service 82fcde
#define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
Packit Service 82fcde
#define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
Packit Service 82fcde
Packit Service 82fcde
#include <kernel-features.h>
Packit Service 82fcde
Packit Service 82fcde
#define TRACE_BUFFER_SIZE 512
Packit Service 82fcde
Packit Service 82fcde
static FILE *mallstream;
Packit Service 82fcde
static const char mallenv[] = "MALLOC_TRACE";
Packit Service 82fcde
static char *malloc_trace_buffer;
Packit Service 82fcde
Packit Service 82fcde
__libc_lock_define_initialized (static, lock);
Packit Service 82fcde
Packit Service 82fcde
/* Address to breakpoint on accesses to... */
Packit Service 82fcde
void *mallwatch;
Packit Service 82fcde
Packit Service 82fcde
/* Old hook values.  */
Packit Service 82fcde
static void (*tr_old_free_hook) (void *ptr, const void *);
Packit Service 82fcde
static void *(*tr_old_malloc_hook) (size_t size, const void *);
Packit Service 82fcde
static void *(*tr_old_realloc_hook) (void *ptr, size_t size,
Packit Service 82fcde
				     const void *);
Packit Service 82fcde
static void *(*tr_old_memalign_hook) (size_t __alignment, size_t __size,
Packit Service 82fcde
				      const void *);
Packit Service 82fcde
Packit Service 82fcde
/* This function is called when the block being alloc'd, realloc'd, or
Packit Service 82fcde
   freed has an address matching the variable "mallwatch".  In a debugger,
Packit Service 82fcde
   set "mallwatch" to the address of interest, then put a breakpoint on
Packit Service 82fcde
   tr_break.  */
Packit Service 82fcde
Packit Service 82fcde
extern void tr_break (void) __THROW;
Packit Service 82fcde
libc_hidden_proto (tr_break)
Packit Service 82fcde
void
Packit Service 82fcde
tr_break (void)
Packit Service 82fcde
{
Packit Service 82fcde
}
Packit Service 82fcde
libc_hidden_def (tr_break)
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
tr_where (const void *caller, Dl_info *info)
Packit Service 82fcde
{
Packit Service 82fcde
  if (caller != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (info != NULL)
Packit Service 82fcde
        {
Packit Service 82fcde
          char *buf = (char *) "";
Packit Service 82fcde
          if (info->dli_sname != NULL)
Packit Service 82fcde
            {
Packit Service 82fcde
              size_t len = strlen (info->dli_sname);
Packit Service 82fcde
              buf = alloca (len + 6 + 2 * sizeof (void *));
Packit Service 82fcde
Packit Service 82fcde
              buf[0] = '(';
Packit Service 82fcde
              __stpcpy (_fitoa (caller >= (const void *) info->dli_saddr
Packit Service 82fcde
                                ? caller - (const void *) info->dli_saddr
Packit Service 82fcde
                                : (const void *) info->dli_saddr - caller,
Packit Service 82fcde
                                __stpcpy (__mempcpy (buf + 1, info->dli_sname,
Packit Service 82fcde
                                                     len),
Packit Service 82fcde
                                          caller >= (void *) info->dli_saddr
Packit Service 82fcde
                                          ? "+0x" : "-0x"),
Packit Service 82fcde
                                16, 0),
Packit Service 82fcde
                        ")");
Packit Service 82fcde
            }
Packit Service 82fcde
Packit Service 82fcde
          fprintf (mallstream, "@ %s%s%s[%p] ",
Packit Service 82fcde
                   info->dli_fname ? : "", info->dli_fname ? ":" : "",
Packit Service 82fcde
                   buf, caller);
Packit Service 82fcde
        }
Packit Service 82fcde
      else
Packit Service 82fcde
        fprintf (mallstream, "@ [%p] ", caller);
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static Dl_info *
Packit Service 82fcde
lock_and_info (const void *caller, Dl_info *mem)
Packit Service 82fcde
{
Packit Service 82fcde
  if (caller == NULL)
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL;
Packit Service 82fcde
Packit Service 82fcde
  __libc_lock_lock (lock);
Packit Service 82fcde
Packit Service 82fcde
  return res;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 35e39c
static void tr_freehook (void *, const void *);
Packit Service 35e39c
static void * tr_mallochook (size_t, const void *);
Packit Service 35e39c
static void * tr_reallochook (void *, size_t, const void *);
Packit Service 35e39c
static void * tr_memalignhook (size_t, size_t, const void *);
Packit Service 35e39c
Packit Service 35e39c
/* Set all the default non-trace hooks.  */
Packit Service 35e39c
static __always_inline void
Packit Service 35e39c
set_default_hooks (void)
Packit Service 35e39c
{
Packit Service 35e39c
  __free_hook = tr_old_free_hook;
Packit Service 35e39c
  __malloc_hook = tr_old_malloc_hook;
Packit Service 35e39c
  __realloc_hook = tr_old_realloc_hook;
Packit Service 35e39c
  __memalign_hook = tr_old_memalign_hook;
Packit Service 35e39c
}
Packit Service 35e39c
Packit Service 35e39c
/* Set all of the tracing hooks used for mtrace.  */
Packit Service 35e39c
static __always_inline void
Packit Service 35e39c
set_trace_hooks (void)
Packit Service 35e39c
{
Packit Service 35e39c
  __free_hook = tr_freehook;
Packit Service 35e39c
  __malloc_hook = tr_mallochook;
Packit Service 35e39c
  __realloc_hook = tr_reallochook;
Packit Service 35e39c
  __memalign_hook = tr_memalignhook;
Packit Service 35e39c
}
Packit Service 35e39c
Packit Service 35e39c
/* Save the current set of hooks as the default hooks.  */
Packit Service 35e39c
static __always_inline void
Packit Service 35e39c
save_default_hooks (void)
Packit Service 35e39c
{
Packit Service 35e39c
  tr_old_free_hook = __free_hook;
Packit Service 35e39c
  tr_old_malloc_hook = __malloc_hook;
Packit Service 35e39c
  tr_old_realloc_hook = __realloc_hook;
Packit Service 35e39c
  tr_old_memalign_hook = __memalign_hook;
Packit Service 35e39c
}
Packit Service 35e39c
Packit Service 82fcde
static void
Packit Service 82fcde
tr_freehook (void *ptr, const void *caller)
Packit Service 82fcde
{
Packit Service 82fcde
  if (ptr == NULL)
Packit Service 82fcde
    return;
Packit Service 82fcde
Packit Service 82fcde
  Dl_info mem;
Packit Service 82fcde
  Dl_info *info = lock_and_info (caller, &mem;;
Packit Service 82fcde
  tr_where (caller, info);
Packit Service 82fcde
  /* Be sure to print it first.  */
Packit Service 82fcde
  fprintf (mallstream, "- %p\n", ptr);
Packit Service 82fcde
  if (ptr == mallwatch)
Packit Service 82fcde
    {
Packit Service 82fcde
      __libc_lock_unlock (lock);
Packit Service 82fcde
      tr_break ();
Packit Service 82fcde
      __libc_lock_lock (lock);
Packit Service 82fcde
    }
Packit Service 35e39c
  set_default_hooks ();
Packit Service 82fcde
  if (tr_old_free_hook != NULL)
Packit Service 82fcde
    (*tr_old_free_hook)(ptr, caller);
Packit Service 82fcde
  else
Packit Service 82fcde
    free (ptr);
Packit Service 35e39c
  set_trace_hooks ();
Packit Service 82fcde
  __libc_lock_unlock (lock);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void *
Packit Service 82fcde
tr_mallochook (size_t size, const void *caller)
Packit Service 82fcde
{
Packit Service 82fcde
  void *hdr;
Packit Service 82fcde
Packit Service 82fcde
  Dl_info mem;
Packit Service 82fcde
  Dl_info *info = lock_and_info (caller, &mem;;
Packit Service 82fcde
Packit Service 35e39c
  set_default_hooks ();
Packit Service 82fcde
  if (tr_old_malloc_hook != NULL)
Packit Service 82fcde
    hdr = (void *) (*tr_old_malloc_hook)(size, caller);
Packit Service 82fcde
  else
Packit Service 82fcde
    hdr = (void *) malloc (size);
Packit Service 35e39c
  set_trace_hooks ();
Packit Service 82fcde
Packit Service 82fcde
  tr_where (caller, info);
Packit Service 82fcde
  /* We could be printing a NULL here; that's OK.  */
Packit Service 82fcde
  fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
Packit Service 82fcde
Packit Service 82fcde
  __libc_lock_unlock (lock);
Packit Service 82fcde
Packit Service 82fcde
  if (hdr == mallwatch)
Packit Service 82fcde
    tr_break ();
Packit Service 82fcde
Packit Service 82fcde
  return hdr;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void *
Packit Service 82fcde
tr_reallochook (void *ptr, size_t size, const void *caller)
Packit Service 82fcde
{
Packit Service 82fcde
  void *hdr;
Packit Service 82fcde
Packit Service 82fcde
  if (ptr == mallwatch)
Packit Service 82fcde
    tr_break ();
Packit Service 82fcde
Packit Service 82fcde
  Dl_info mem;
Packit Service 82fcde
  Dl_info *info = lock_and_info (caller, &mem;;
Packit Service 82fcde
Packit Service 35e39c
  set_default_hooks ();
Packit Service 82fcde
  if (tr_old_realloc_hook != NULL)
Packit Service 82fcde
    hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller);
Packit Service 82fcde
  else
Packit Service 82fcde
    hdr = (void *) realloc (ptr, size);
Packit Service 35e39c
  set_trace_hooks ();
Packit Service 82fcde
Packit Service 82fcde
  tr_where (caller, info);
Packit Service 82fcde
  if (hdr == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (size != 0)
Packit Service 82fcde
        /* Failed realloc.  */
Packit Service 82fcde
        fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
Packit Service 82fcde
      else
Packit Service 82fcde
        fprintf (mallstream, "- %p\n", ptr);
Packit Service 82fcde
    }
Packit Service 82fcde
  else if (ptr == NULL)
Packit Service 82fcde
    fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      fprintf (mallstream, "< %p\n", ptr);
Packit Service 82fcde
      tr_where (caller, info);
Packit Service 82fcde
      fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  __libc_lock_unlock (lock);
Packit Service 82fcde
Packit Service 82fcde
  if (hdr == mallwatch)
Packit Service 82fcde
    tr_break ();
Packit Service 82fcde
Packit Service 82fcde
  return hdr;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void *
Packit Service 82fcde
tr_memalignhook (size_t alignment, size_t size, const void *caller)
Packit Service 82fcde
{
Packit Service 82fcde
  void *hdr;
Packit Service 82fcde
Packit Service 82fcde
  Dl_info mem;
Packit Service 82fcde
  Dl_info *info = lock_and_info (caller, &mem;;
Packit Service 82fcde
Packit Service 35e39c
  set_default_hooks ();
Packit Service 82fcde
  if (tr_old_memalign_hook != NULL)
Packit Service 82fcde
    hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller);
Packit Service 82fcde
  else
Packit Service 82fcde
    hdr = (void *) memalign (alignment, size);
Packit Service 35e39c
  set_trace_hooks ();
Packit Service 82fcde
Packit Service 82fcde
  tr_where (caller, info);
Packit Service 82fcde
  /* We could be printing a NULL here; that's OK.  */
Packit Service 82fcde
  fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
Packit Service 82fcde
Packit Service 82fcde
  __libc_lock_unlock (lock);
Packit Service 82fcde
Packit Service 82fcde
  if (hdr == mallwatch)
Packit Service 82fcde
    tr_break ();
Packit Service 82fcde
Packit Service 82fcde
  return hdr;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
#ifdef _LIBC
Packit Service 82fcde
Packit Service 82fcde
/* This function gets called to make sure all memory the library
Packit Service 82fcde
   allocates get freed and so does not irritate the user when studying
Packit Service 82fcde
   the mtrace output.  */
Packit Service 82fcde
static void __libc_freeres_fn_section
Packit Service 82fcde
release_libc_mem (void)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Only call the free function if we still are running in mtrace mode.  */
Packit Service 82fcde
  if (mallstream != NULL)
Packit Service 82fcde
    __libc_freeres ();
Packit Service 82fcde
}
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* We enable tracing if either the environment variable MALLOC_TRACE
Packit Service 82fcde
   is set, or if the variable mallwatch has been patched to an address
Packit Service 82fcde
   that the debugging user wants us to stop on.  When patching mallwatch,
Packit Service 82fcde
   don't forget to set a breakpoint on tr_break!  */
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
mtrace (void)
Packit Service 82fcde
{
Packit Service 82fcde
#ifdef _LIBC
Packit Service 82fcde
  static int added_atexit_handler;
Packit Service 82fcde
#endif
Packit Service 82fcde
  char *mallfile;
Packit Service 82fcde
Packit Service 82fcde
  /* Don't panic if we're called more than once.  */
Packit Service 82fcde
  if (mallstream != NULL)
Packit Service 82fcde
    return;
Packit Service 82fcde
Packit Service 82fcde
#ifdef _LIBC
Packit Service 82fcde
  /* When compiling the GNU libc we use the secure getenv function
Packit Service 82fcde
     which prevents the misuse in case of SUID or SGID enabled
Packit Service 82fcde
     programs.  */
Packit Service 82fcde
  mallfile = __libc_secure_getenv (mallenv);
Packit Service 82fcde
#else
Packit Service 82fcde
  mallfile = getenv (mallenv);
Packit Service 82fcde
#endif
Packit Service 82fcde
  if (mallfile != NULL || mallwatch != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      char *mtb = malloc (TRACE_BUFFER_SIZE);
Packit Service 82fcde
      if (mtb == NULL)
Packit Service 82fcde
        return;
Packit Service 82fcde
Packit Service 82fcde
      mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
Packit Service 82fcde
      if (mallstream != NULL)
Packit Service 82fcde
        {
Packit Service 82fcde
          /* Be sure it doesn't malloc its buffer!  */
Packit Service 82fcde
          malloc_trace_buffer = mtb;
Packit Service 82fcde
          setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
Packit Service 82fcde
          fprintf (mallstream, "= Start\n");
Packit Service 35e39c
	  save_default_hooks ();
Packit Service 35e39c
	  set_trace_hooks ();
Packit Service 82fcde
#ifdef _LIBC
Packit Service 82fcde
          if (!added_atexit_handler)
Packit Service 82fcde
            {
Packit Service 82fcde
              added_atexit_handler = 1;
Packit Service 82fcde
              __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
Packit Service 82fcde
			    __dso_handle);
Packit Service 82fcde
            }
Packit Service 82fcde
#endif
Packit Service 82fcde
        }
Packit Service 82fcde
      else
Packit Service 82fcde
        free (mtb);
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
void
Packit Service 82fcde
muntrace (void)
Packit Service 82fcde
{
Packit Service 82fcde
  if (mallstream == NULL)
Packit Service 82fcde
    return;
Packit Service 82fcde
Packit Service 82fcde
  /* Do the reverse of what done in mtrace: first reset the hooks and
Packit Service 82fcde
     MALLSTREAM, and only after that write the trailer and close the
Packit Service 82fcde
     file.  */
Packit Service 82fcde
  FILE *f = mallstream;
Packit Service 82fcde
  mallstream = NULL;
Packit Service 35e39c
  set_default_hooks ();
Packit Service 82fcde
Packit Service 82fcde
  fprintf (f, "= End\n");
Packit Service 82fcde
  fclose (f);
Packit Service 82fcde
}