Blame malloc/mtrace.c

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