diff -rupN a/malloc/Makefile b/malloc/Makefile
--- a/malloc/Makefile	2017-08-03 07:39:54.000000000 -0400
+++ b/malloc/Makefile	2017-08-15 18:37:06.562792424 -0400
@@ -74,7 +74,7 @@ install-lib := libmcheck.a
 non-lib.a := libmcheck.a
 
 # Additional library.
-extra-libs = libmemusage
+extra-libs = libmemusage libmtracectl
 extra-libs-others = $(extra-libs)
 
 # Helper objects for some tests.
@@ -89,6 +89,9 @@ test-extras = \
 libmemusage-routines = memusage
 libmemusage-inhibit-o = $(filter-out .os,$(object-suffixes))
 
+libmtracectl-routines = mtrace-ctl
+libmtracectl-inhibit-o = $(filter-out .os,$(object-suffixes))
+
 $(objpfx)tst-malloc-backtrace: $(shared-thread-library)
 $(objpfx)tst-malloc-thread-exit: $(shared-thread-library)
 $(objpfx)tst-malloc-thread-fail: $(shared-thread-library)
diff -rupN a/malloc/Versions b/malloc/Versions
--- a/malloc/Versions	2017-08-03 07:39:54.000000000 -0400
+++ b/malloc/Versions	2017-08-15 18:37:24.188808807 -0400
@@ -92,5 +92,12 @@ libc {
     __libc_alloc_buffer_copy_bytes;
     __libc_alloc_buffer_copy_string;
     __libc_alloc_buffer_create_failure;
+
+    # malloc trace hooks for mtrace-ctl
+    __malloc_trace_init;
+    __malloc_trace_pause;
+    __malloc_trace_unpause;
+    __malloc_trace_stop;
+    __malloc_trace_sync;
   }
 }
diff -rupN a/malloc/libmtracectl.abilist b/malloc/libmtracectl.abilist
--- a/malloc/libmtracectl.abilist	1969-12-31 19:00:00.000000000 -0500
+++ b/malloc/libmtracectl.abilist	2017-08-16 14:55:49.214941260 -0400
@@ -0,0 +1,13 @@
+Base __bss_start Base g ? D .bss 0000000000000000
+Base _edata Base g ? D .got.plt 0000000000000000
+Base _end Base g ? D .bss 0000000000000000
+Base _fini F
+Base _init F
+Base mtrace_buffer D 0x8
+Base mtrace_buffer_bytesize D 0x8
+Base mtrace_ctor F
+Base mtrace_dtor F
+Base mtrace_reset F
+Base mtrace_start F
+Base mtrace_stop F
+Base mtrace_sync F
diff -rupN a/malloc/malloc.c b/malloc/malloc.c
--- a/malloc/malloc.c	2017-08-15 17:09:17.457100945 -0400
+++ b/malloc/malloc.c	2017-08-15 19:58:17.132454422 -0400
@@ -1035,6 +1035,345 @@ static void*   realloc_check(void* oldme
 static void*   memalign_check(size_t alignment, size_t bytes,
 				const void *caller);
 
+/* ------------------ TRACE support ------------------  */
+#define USE_MTRACE 1
+#if USE_MTRACE
+#include "mtrace.h"
+#include <fcntl.h>
+
+#ifndef atomic_load_acquire
+#define atomic_load_acquire(x) catomic_exchange_and_add ((x), 0)
+#define atomic_load_relaxed(x) catomic_exchange_and_add ((x), 0)
+#define atomic_store_release(x,v) atomic_exchange_rel ((x), (v))
+#endif
+
+
+typedef struct __malloc_trace_map_entry_s {
+  int ref_count;
+  __malloc_trace_buffer_ptr window;
+} __malloc_trace_map_entry;
+
+/* 16 Tb max file size, 64 Mb per window */
+#define TRACE_MAPPING_SIZE 67108864
+#define TRACE_N_PER_MAPPING (TRACE_MAPPING_SIZE / sizeof (struct __malloc_trace_buffer_s))
+#define TRACE_N_MAPPINGS 262144
+#define TRACE_MAX_COUNT (TRACE_N_PER_MAPPING * TRACE_N_MAPPINGS)
+
+/* Index into __malloc_trace_buffer[] */
+#define TRACE_COUNT_TO_MAPPING_NUM(count) ((count) / TRACE_N_PER_MAPPING)
+/* Index info __malloc_trace_buffer[n][] */
+#define TRACE_COUNT_TO_MAPPING_IDX(count) ((count) % TRACE_N_PER_MAPPING)
+
+/* Global mutex for the trace buffer tree itself.  */
+__libc_lock_define (, __malloc_trace_mutex);
+
+/* Global counter, "full" when equal to TRACE_MAX_COUNT.  Points to
+   the next available slot, so POST-INCREMENT it.  */
+volatile size_t __malloc_trace_count = 0;
+
+/* Array of TRACE_N_MAPPINGS pointers to potentially mapped trace buffers.  */
+volatile __malloc_trace_map_entry *__malloc_trace_buffer = NULL;
+/* The file we're mapping them to.  */
+char * __malloc_trace_filename = NULL;
+
+/* Global trace enable flag.  Default off.
+   If global trace enable is 1 then tracing is carried out for all
+   threads.  Otherwise no threads trace calls.  */
+volatile int __malloc_trace_enabled = 0;
+
+/* Per-thread trace enable flag.  Default on.
+   If thread trace enable is 1 then tracing for the thread behaves as expected
+   per the global trace enabled value.
+   If thread trace enable is 0 then __MTB_TRACE_ENTRY and __MTB_TRACE_SET
+   do nothing, only __MTB_TRACE_PATH sets path bits i.e. no new traces are
+   created, the existing trace is used to store path bits.
+   The purpose of this is to allow the implementation to nest public API
+   calls, track paths, without creating multiple nested trace events.  */
+__thread int __malloc_thread_trace_enabled = 1;
+
+static __thread int __malloc_trace_last_num = -1;
+static __thread __malloc_trace_buffer_ptr trace_ptr;
+
+static inline pid_t
+__gettid (void)
+{
+  struct pthread *pd = THREAD_SELF;
+  pid_t selftid = THREAD_GETMEM (pd, tid);
+  if (selftid == 0)
+    {
+      /* This system call is not supposed to fail.  */
+#ifdef INTERNAL_SYSCALL
+      INTERNAL_SYSCALL_DECL (err);
+      selftid = INTERNAL_SYSCALL (gettid, err, 0);
+#else
+      selftid = INLINE_SYSCALL (gettid, 0);
+#endif
+      THREAD_SETMEM (pd, tid, selftid);
+    }
+
+  return selftid;
+}
+
+static void
+__mtb_trace_entry (uint32_t type, size_t size, void *ptr1)
+{
+  size_t my_trace_count;
+  size_t old_trace_count;
+  int my_num;
+
+  /* START T: Log trace event.  */
+ alg_t1:
+  /* T1. Perform a load-acq of the global trace offset.  */
+  my_trace_count = atomic_load_acquire (&__malloc_trace_count);
+
+  /* T2. If the window number is different from the current
+     thread-local window number, proceed with algorithm W below.  */
+  my_num = TRACE_COUNT_TO_MAPPING_NUM (my_trace_count);
+  if (my_num != __malloc_trace_last_num)
+    {
+      long new_window;
+      int new_ref_count;
+
+      /* START W: Switch window.  */
+
+      /* W1. Acquire the global window lock.  */
+      __libc_lock_lock (__malloc_trace_mutex);
+
+      /* W2. If the thread-local window number is not -1, decrement the reference
+	 counter for the current thread window.  */
+      if (__malloc_trace_last_num != -1)
+	{
+	  int old_window = TRACE_COUNT_TO_MAPPING_NUM (__malloc_trace_last_num);
+	  int old_ref_count = catomic_exchange_and_add (&__malloc_trace_buffer[old_window].ref_count, -1);
+	  /* W3. If that reference counter reached 0, unmap the window. */
+	  if (old_ref_count == 1)
+	    {
+	      munmap (__malloc_trace_buffer[old_window].window, TRACE_MAPPING_SIZE);
+	      __malloc_trace_buffer[old_window].window = NULL;
+	    }
+	}
+
+      /* W4. Perform a load-relaxed of the global trace offset.  */
+      my_trace_count = atomic_load_relaxed (&__malloc_trace_count);
+
+      /* W5. Increment the reference counter of the corresponding window. */
+      new_window = TRACE_COUNT_TO_MAPPING_NUM (my_trace_count);
+      new_ref_count = catomic_exchange_and_add (&__malloc_trace_buffer[new_window].ref_count, 1);
+
+      /* W6. If the incremented reference counter is 1, perform algorithm F.  */
+      if (new_ref_count == 0)
+	{
+	  /* START F: Map window from file.  */
+
+	  /* Note: There are security issues wrt opening a file by
+	     name many times.  We know this, and the risk is low (if
+	     you have root access, there are better ways to wreak
+	     havoc).  We choose this design so that there isn't an
+	     open file handle which may interefere with, or be
+	     corrupted by, the running application.  */
+
+	  /* F1. Open the trace file.  */
+	  int trace_fd = open (__malloc_trace_filename, O_RDWR|O_CREAT, 0666);
+	  if (trace_fd < 0)
+	    {
+	      /* FIXME: Better handling of errors?  */
+	      __fxprintf(NULL, "Can't open trace_buffer file %s\n", __malloc_trace_filename);
+	      __malloc_trace_enabled = 0;
+	      return;
+	    }
+
+	  /* F2. Extend the file length so that it covers the end of the current
+	     window (using ftruncate, needed to avoid SIGBUS).  */
+	  ftruncate (trace_fd, (new_window + 1) * TRACE_MAPPING_SIZE);
+
+	  /* F3. Map the window from the file offset corresponding to
+	     the current window.  */
+	  void *ptr =
+	    mmap (NULL, TRACE_MAPPING_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+		  trace_fd, new_window * TRACE_MAPPING_SIZE);
+	  if (ptr == NULL)
+	    {
+	      /* FIXME: Better handling of errors?  */
+	      __fxprintf(NULL, "Can't map trace_buffer file %s\n", __malloc_trace_filename);
+	      __malloc_trace_enabled = 0;
+	      return;
+	    }
+
+	  /* F4. Update the mapping pointer in the active window array.  */
+	  __malloc_trace_buffer[new_window].window = ptr;
+
+	  /* F5. Close the file.  */
+	  close (trace_fd);
+
+	  /* F6. Continue with step W7.  */
+	  /* END F */
+	}
+
+      /* W7. Assign the window number to the thread-local window number,
+	 switching the thread window.  */
+      __malloc_trace_last_num = new_window;
+
+      /* W8. Release the global window lock.  */
+      __libc_lock_unlock (__malloc_trace_mutex);
+
+      /* W9. Continue at T1.  */
+      goto alg_t1;
+
+      /* END W */
+    }
+
+  /* T3. CAS-acqrel the incremented global trace offset.  If CAS
+     fails, go back to T1.  */
+  old_trace_count = catomic_exchange_and_add (&__malloc_trace_count, 1);
+  /* See if someone else incremented it while we weren't looking.  */
+  if (old_trace_count != my_trace_count)
+    goto alg_t1;
+
+  /* T4. Write the trace data.  */
+  /* At this point, __malloc_trace_buffer[my_num] is valid because we
+     DIDN'T go through algorithm W, and it's reference counted for us,
+     and my_trace_count points to our record.  */
+  trace_ptr = __malloc_trace_buffer[my_num].window + TRACE_COUNT_TO_MAPPING_IDX (my_trace_count);
+
+  trace_ptr->thread = __gettid ();
+  trace_ptr->type = type;
+  trace_ptr->path_thread_cache = 0;
+  trace_ptr->path_cpu_cache = 0;
+  trace_ptr->path_cpu_cache2 = 0;
+  trace_ptr->path_sbrk = 0;
+  trace_ptr->path_mmap = 0;
+  trace_ptr->path_munmap = 0;
+  trace_ptr->path_m_f_realloc = 0;
+  trace_ptr->path = 0;
+  trace_ptr->size = size;
+  trace_ptr->ptr1 = ptr1;
+  trace_ptr->ptr2 = 0;
+}
+
+/* Initialize the trace buffer and backing file.  The file is
+   overwritten if it already exists.  */
+void
+__malloc_trace_init (char *filename)
+{
+  int pagesize = sysconf(_SC_PAGE_SIZE);
+  int main_length = TRACE_N_MAPPINGS * sizeof (__malloc_trace_buffer[0]);
+  int total_length = (main_length + strlen(filename) + 1 + pagesize-1) & (~(pagesize-1));
+  char *mapping;
+
+  mapping = mmap (NULL, total_length, PROT_READ | PROT_WRITE,
+		  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (mapping == NULL)
+    return;
+
+  strcpy (mapping + main_length, filename);
+  __malloc_trace_filename = mapping + main_length;
+
+  __malloc_trace_buffer = (__malloc_trace_map_entry *) mapping;
+
+  __malloc_trace_mutex = _LIBC_LOCK_INITIALIZER;
+  __malloc_trace_count = 0;
+
+  __mtb_trace_entry (__MTB_TYPE_MAGIC, sizeof(void *), (void *)0x1234);
+
+  atomic_store_release (&__malloc_trace_enabled, 1);
+}
+
+/* All remaining functions return current count of trace records.  */
+
+/* Pause - but don't stop - tracing.  */
+size_t __malloc_trace_pause (void)
+{
+  atomic_store_release (&__malloc_trace_enabled, 0);
+  return atomic_load_relaxed (&__malloc_trace_count);
+}
+
+/* Resume tracing where it left off when paused.  */
+size_t __malloc_trace_unpause (void)
+{
+  if (__malloc_trace_buffer != NULL)
+    atomic_store_release (&__malloc_trace_enabled, 1);
+  return atomic_load_relaxed (&__malloc_trace_count);
+}
+
+/* Stop tracing and clean up all the trace buffer mappings.  */
+size_t __malloc_trace_stop (void)
+{
+  atomic_store_release (&__malloc_trace_enabled, 0);
+  /* FIXME: we can't actually release everything until all threads
+     have finished accessing the buffer, but we have no way of doing
+     that...  */
+
+  /* For convenience, reduce the file size to only what's needed, else
+     the minimum file size we'll see if 64 Mb.  */
+  int trace_fd = open (__malloc_trace_filename, O_RDWR|O_CREAT, 0666);
+  if (trace_fd >= 0)
+    {
+      ftruncate (trace_fd, __malloc_trace_count * sizeof (struct __malloc_trace_buffer_s));
+      close (trace_fd);
+    }
+
+  return atomic_load_relaxed (&__malloc_trace_count);
+}
+
+/* Sync all buffer data to file (typically a no-op on Linux).  */
+size_t __malloc_trace_sync (void)
+{
+  return atomic_load_relaxed (&__malloc_trace_count);
+}
+
+/* CONCURRENCY NOTES: The load acquire here synchronizes with the store release
+   from __malloc_trace_init to ensure that all threads see the initialization
+   done by the first thread that calls __malloc_trace_init.  The load acquire
+   also synchronizes with the store releases in __mtb_trace_entry to ensure
+   that all error cleanup is visible.  Lastly it synchronizes with the store
+   releases from __malloc_trace_pause, __malloc_trace_unpause, and
+   __malloc_trace_top to ensure that all external changes are visible to the
+   current thread.  */
+#define __MTB_TRACE_ENTRY(type, size, ptr1)		   		      \
+  if (__glibc_unlikely (atomic_load_acquire (&__malloc_trace_enabled))	      \
+      && __glibc_unlikely (__malloc_thread_trace_enabled))		      \
+    __mtb_trace_entry (__MTB_TYPE_##type,size,ptr1);
+
+/* Ignore __malloc_thread_trace_enabled and set path bits.  This allows us to
+   track the path of a call without additional traces.  For example realloc
+   can call malloc and free without making new trace, but we record the paths
+   taken in malloc and free.  */
+#define __MTB_TRACE_PATH(mpath)						      \
+  if (__glibc_unlikely (trace_ptr != NULL))				      \
+    trace_ptr->path_##mpath = 1;
+
+#define __MTB_TRACE_SET(var,value)					      \
+  if (__glibc_unlikely (__malloc_thread_trace_enabled)			      \
+      && __glibc_unlikely (trace_ptr != NULL))				      \
+    trace_ptr->var = value;
+
+/* Allow __MTB_TRACE_ENTRY to create new trace entries.  */
+#define __MTB_THREAD_TRACE_ENABLE()					      \
+  ({									      \
+    __malloc_thread_trace_enabled = 1;					      \
+  })
+
+/* Disallow __MTB_TRACE_ENTRY from creating new trace
+   entries. Use of __MTB_TRACE_SET becomes a NOOP, but
+   __MTB_TRACE_PATH still sets the unique path bit in
+   the trace (all path bits are unique).  */
+#define __MTB_THREAD_TRACE_DISABLE()					      \
+  ({									      \
+    __malloc_thread_trace_enabled = 0;					      \
+  })
+
+#else
+void __malloc_trace_init (char *filename) {}
+size_t __malloc_trace_pause (void) { return 0; }
+size_t __malloc_trace_unpause (void) { return 0; }
+size_t __malloc_trace_stop (void) { return 0; }
+size_t __malloc_trace_sync (void) { return 0; }
+
+#define __MTB_TRACE_ENTRY(type,size,ptr1)
+#define __MTB_TRACE_PATH(mpath)
+#define __MTB_TRACE_SET(var,value)
+#endif
+
 /* ------------------ MMAP support ------------------  */
 
 
@@ -2340,6 +2679,7 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av
       /* Don't try if size wraps around 0 */
       if ((unsigned long) (size) > (unsigned long) (nb))
         {
+	  __MTB_TRACE_PATH (mmap);
           mm = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0));
 
           if (mm != MAP_FAILED)
@@ -2873,6 +3213,7 @@ munmap_chunk (mchunkptr p)
   /* If munmap failed the process virtual memory address space is in a
      bad shape.  Just leave the block hanging around, the process will
      terminate shortly anyway since not much can be done.  */
+  __MTB_TRACE_PATH (munmap);
   __munmap ((char *) block, total_size);
 }
 
@@ -2950,6 +3291,7 @@ tcache_put (mchunkptr chunk, size_t tc_i
 {
   tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
   assert (tc_idx < TCACHE_MAX_BINS);
+  __MTB_TRACE_PATH (thread_cache);
   e->next = tcache->entries[tc_idx];
   tcache->entries[tc_idx] = e;
   ++(tcache->counts[tc_idx]);
@@ -2963,6 +3305,7 @@ tcache_get (size_t tc_idx)
   tcache_entry *e = tcache->entries[tc_idx];
   assert (tc_idx < TCACHE_MAX_BINS);
   assert (tcache->entries[tc_idx] > 0);
+  __MTB_TRACE_PATH (thread_cache);
   tcache->entries[tc_idx] = e->next;
   --(tcache->counts[tc_idx]);
   return (void *) e;
@@ -3044,10 +3387,21 @@ __libc_malloc (size_t bytes)
   mstate ar_ptr;
   void *victim;
 
+  __MTB_TRACE_ENTRY (MALLOC, bytes, NULL);
+
   void *(*hook) (size_t, const void *)
     = atomic_forced_read (__malloc_hook);
   if (__builtin_expect (hook != NULL, 0))
-    return (*hook)(bytes, RETURN_ADDRESS (0));
+    {
+      __MTB_TRACE_PATH (hook);
+      __MTB_THREAD_TRACE_DISABLE ();
+      victim = (*hook)(bytes, RETURN_ADDRESS (0));
+      __MTB_THREAD_TRACE_ENABLE ();
+      if (victim != NULL)
+	__MTB_TRACE_SET (size3, chunksize (mem2chunk (victim)));
+      return victim;
+    }
+ 
 #if USE_TCACHE
   /* int_free also calls request2size, be careful to not pad twice.  */
   size_t tbytes = request2size (bytes);
@@ -3061,11 +3415,16 @@ __libc_malloc (size_t bytes)
       && tcache
       && tcache->entries[tc_idx] != NULL)
     {
-      return tcache_get (tc_idx);
+      victim = tcache_get (tc_idx);
+      __MTB_TRACE_SET(ptr2, victim);
+      if (victim != NULL)
+	__MTB_TRACE_SET (size3, chunksize (mem2chunk (victim)));
+      return victim;
     }
   DIAG_POP_NEEDS_COMMENT;
 #endif
 
+  __MTB_TRACE_PATH (cpu_cache);
   arena_get (ar_ptr, bytes);
 
   victim = _int_malloc (ar_ptr, bytes);
@@ -3073,6 +3432,7 @@ __libc_malloc (size_t bytes)
      before.  */
   if (!victim && ar_ptr != NULL)
     {
+      __MTB_TRACE_PATH (cpu_cache2);
       LIBC_PROBE (memory_malloc_retry, 1, bytes);
       ar_ptr = arena_get_retry (ar_ptr, bytes);
       victim = _int_malloc (ar_ptr, bytes);
@@ -3083,6 +3443,9 @@ __libc_malloc (size_t bytes)
 
   assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
           ar_ptr == arena_for_chunk (mem2chunk (victim)));
+  __MTB_TRACE_SET(ptr2, victim);
+  if (victim != NULL)
+    __MTB_TRACE_SET (size3, chunksize (mem2chunk (victim)));
   return victim;
 }
 libc_hidden_def (__libc_malloc)
@@ -3093,11 +3456,22 @@ __libc_free (void *mem)
   mstate ar_ptr;
   mchunkptr p;                          /* chunk corresponding to mem */
 
+  __MTB_TRACE_ENTRY (FREE, 0, mem);
+  /* It's very important we record the free size in the trace.
+     This makes verification of tracked malloc<->free's much
+     easier by adding size as a way to correlate the entries also.
+     It also makes it easier to do running tallies of ideal RSS usage
+     from trace data.  */
+  if (mem != 0)
+    __MTB_TRACE_SET (size2, chunksize (mem2chunk (mem)));
+
   void (*hook) (void *, const void *)
     = atomic_forced_read (__free_hook);
   if (__builtin_expect (hook != NULL, 0))
     {
+      __MTB_THREAD_TRACE_DISABLE ();
       (*hook)(mem, RETURN_ADDRESS (0));
+      __MTB_THREAD_TRACE_ENABLE ();
       return;
     }
 
@@ -3139,21 +3513,45 @@ __libc_realloc (void *oldmem, size_t byt
 
   void *newp;             /* chunk to return */
 
+  /* The realloc event may include calls to malloc and free, and in those
+     cases we disable the recursive tracing, but continue to record the
+     traced paths.  */
+  __MTB_TRACE_ENTRY (REALLOC, bytes, oldmem);
+
   void *(*hook) (void *, size_t, const void *) =
     atomic_forced_read (__realloc_hook);
   if (__builtin_expect (hook != NULL, 0))
-    return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));
+    {
+      __MTB_THREAD_TRACE_DISABLE ();
+      newp = (*hook)(oldmem, bytes, RETURN_ADDRESS (0));
+      __MTB_THREAD_TRACE_ENABLE ();
+      __MTB_TRACE_SET (ptr2, newp);
+      if (newp != 0)
+	__MTB_TRACE_SET (size3, chunksize (mem2chunk (newp)));
+      return newp;
+    }
 
 #if REALLOC_ZERO_BYTES_FREES
   if (bytes == 0 && oldmem != NULL)
     {
-      __libc_free (oldmem); return 0;
+      __MTB_THREAD_TRACE_DISABLE ();
+      __libc_free (oldmem);
+      __MTB_THREAD_TRACE_ENABLE ();
+      return 0;
     }
 #endif
 
   /* realloc of null is supposed to be same as malloc */
   if (oldmem == 0)
-    return __libc_malloc (bytes);
+    {
+      __MTB_THREAD_TRACE_DISABLE ();
+      newp = __libc_malloc (bytes);
+      __MTB_THREAD_TRACE_ENABLE ();
+      __MTB_TRACE_SET (ptr2, newp);
+      if (newp != 0)
+	__MTB_TRACE_SET (size3, chunksize (mem2chunk (newp)));
+      return newp;
+    }
 
   /* chunk corresponding to oldmem */
   const mchunkptr oldp = mem2chunk (oldmem);
@@ -3210,19 +3608,29 @@ __libc_realloc (void *oldmem, size_t byt
 #if HAVE_MREMAP
       newp = mremap_chunk (oldp, nb);
       if (newp)
-        return chunk2mem (newp);
+	{
+	  __MTB_TRACE_SET(ptr2, newp);
+	  return chunk2mem (newp);
+	}
 #endif
       /* Note the extra SIZE_SZ overhead. */
       if (oldsize - SIZE_SZ >= nb)
-        return oldmem;                         /* do nothing */
+	{
+	  __MTB_TRACE_SET(ptr2, oldmem);
+	  return oldmem;                         /* do nothing */
+	}
 
       /* Must alloc, copy, free. */
+      __MTB_THREAD_TRACE_DISABLE ();
       newmem = __libc_malloc (bytes);
+      __MTB_THREAD_TRACE_ENABLE ();
       if (newmem == 0)
         return 0;              /* propagate failure */
 
       memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ);
       munmap_chunk (oldp);
+      __MTB_TRACE_SET(ptr2, newmem);
+      __MTB_TRACE_SET (size3, chunksize (mem2chunk (newmem)));
       return newmem;
     }
 
@@ -3238,7 +3646,10 @@ __libc_realloc (void *oldmem, size_t byt
     {
       /* Try harder to allocate memory in other arenas.  */
       LIBC_PROBE (memory_realloc_retry, 2, bytes, oldmem);
+      __MTB_TRACE_PATH (m_f_realloc);
+      __MTB_THREAD_TRACE_DISABLE ();
       newp = __libc_malloc (bytes);
+      __MTB_THREAD_TRACE_ENABLE ();
       if (newp != NULL)
         {
           memcpy (newp, oldmem, oldsize - SIZE_SZ);
@@ -3246,6 +3657,9 @@ __libc_realloc (void *oldmem, size_t byt
         }
     }
 
+  if (newp != 0)
+    __MTB_TRACE_SET (size3, chunksize (mem2chunk (newp)));
+  __MTB_TRACE_SET(ptr2, newp);
   return newp;
 }
 libc_hidden_def (__libc_realloc)
@@ -3254,6 +3668,10 @@ void *
 __libc_memalign (size_t alignment, size_t bytes)
 {
   void *address = RETURN_ADDRESS (0);
+
+  __MTB_TRACE_ENTRY (MEMALIGN, bytes, NULL);
+  __MTB_TRACE_SET (size2, alignment);
+
   return _mid_memalign (alignment, bytes, address);
 }
 
@@ -3266,11 +3684,27 @@ _mid_memalign (size_t alignment, size_t
   void *(*hook) (size_t, size_t, const void *) =
     atomic_forced_read (__memalign_hook);
   if (__builtin_expect (hook != NULL, 0))
-    return (*hook)(alignment, bytes, address);
+    {
+      __MTB_THREAD_TRACE_DISABLE ();
+      p = (*hook)(alignment, bytes, address);
+      __MTB_THREAD_TRACE_ENABLE ();
+      __MTB_TRACE_SET (ptr2, p);
+      if (p != NULL)
+	__MTB_TRACE_SET (size3, chunksize (mem2chunk (p)));
+      return p;
+    }
 
   /* If we need less alignment than we give anyway, just relay to malloc.  */
   if (alignment <= MALLOC_ALIGNMENT)
-    return __libc_malloc (bytes);
+    {
+      __MTB_THREAD_TRACE_DISABLE ();
+      p = __libc_malloc(bytes);
+      __MTB_THREAD_TRACE_ENABLE ();
+      __MTB_TRACE_SET (ptr2, p);
+      if (p != NULL)
+	__MTB_TRACE_SET (size3, chunksize (mem2chunk (p)));
+      return p;
+    }
 
   /* Otherwise, ensure that it is at least a minimum chunk size */
   if (alignment < MINSIZE)
@@ -3316,6 +3750,9 @@ _mid_memalign (size_t alignment, size_t
 
   assert (!p || chunk_is_mmapped (mem2chunk (p)) ||
           ar_ptr == arena_for_chunk (mem2chunk (p)));
+  __MTB_TRACE_SET (ptr2, p);
+  if (p != 0)
+    __MTB_TRACE_SET (size3, chunksize (mem2chunk (p)));
   return p;
 }
 /* For ISO C11.  */
@@ -3328,8 +3765,11 @@ __libc_valloc (size_t bytes)
   if (__malloc_initialized < 0)
     ptmalloc_init ();
 
+  __MTB_TRACE_ENTRY (VALLOC, bytes, NULL);
+
   void *address = RETURN_ADDRESS (0);
   size_t pagesize = GLRO (dl_pagesize);
+  __MTB_TRACE_SET (size2, pagesize);
   return _mid_memalign (pagesize, bytes, address);
 }
 
@@ -3343,6 +3783,9 @@ __libc_pvalloc (size_t bytes)
   size_t pagesize = GLRO (dl_pagesize);
   size_t rounded_bytes = ALIGN_UP (bytes, pagesize);
 
+  __MTB_TRACE_ENTRY (PVALLOC, bytes, NULL);
+  __MTB_TRACE_SET (size2, pagesize);
+
   /* Check for overflow.  */
   if (bytes > SIZE_MAX - 2 * pagesize - MINSIZE)
     {
@@ -5430,12 +5873,18 @@ __posix_memalign (void **memptr, size_t
 {
   void *mem;
 
+  __MTB_TRACE_ENTRY (POSIX_MEMALIGN, size, 0);
+  __MTB_TRACE_SET (size2, alignment);
+
   /* Test whether the SIZE argument is valid.  It must be a power of
      two multiple of sizeof (void *).  */
   if (alignment % sizeof (void *) != 0
       || !powerof2 (alignment / sizeof (void *))
       || alignment == 0)
-    return EINVAL;
+    {
+      __MTB_TRACE_SET (ptr1, (void *) EINVAL);
+      return EINVAL;
+    }
 
 
   void *address = RETURN_ADDRESS (0);
@@ -5447,6 +5896,7 @@ __posix_memalign (void **memptr, size_t
       return 0;
     }
 
+  __MTB_TRACE_SET (ptr1, (void *) ENOMEM);
   return ENOMEM;
 }
 weak_alias (__posix_memalign, posix_memalign)
diff -rupN a/malloc/mtrace-ctl.c b/malloc/mtrace-ctl.c
--- a/malloc/mtrace-ctl.c	1969-12-31 19:00:00.000000000 -0500
+++ b/malloc/mtrace-ctl.c	2017-08-15 19:32:51.617895455 -0400
@@ -0,0 +1,195 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* This module is a stand-alone control program for malloc's internal
+   trace buffer.  It is intended to be preloaded like this:
+
+   LD_PRELOAD=/usr/lib/libmtracectl.so ./myprog
+
+   This module uses the following environment variables:
+
+   MTRACE_CTL_COUNT - how many records to store (default: 1000).  Each
+   record is 32 bytes, and the entire buffer is mmap'd at once.  If
+   the buffer isn't big enough, it will overwrite early records with
+   newer ones.  The total number of trace records is reported in the
+   output file so that a larger buffer may be allocated on future runs.
+
+   MTRACE_CTL_FILE - the output file name (default:
+   /tmp/mtrace-$$.out).  Note that the default is per-pid but there is
+   no way to specify a per-pid pattern via this environment variable.
+
+   The output file will contain a header that says how many trace
+   records were seen (which is usually more or less than the trace
+   buffer size).  The trace buffer is then dumped one entry per line.
+
+*/
+
+#include "mtrace.h"
+
+#define estr(str) write (2, str, strlen (str))
+
+#if 0
+static void
+err(const char *str)
+{
+  estr (str);
+  estr ("\n");
+}
+#endif
+
+/*
+ * mtrace_start - checks for buffer, allocates one if needed, starts trace.
+ * mtrace_stop - stops tracing
+ * mtrace_sync - syncs the buffer
+ * mtrace_reset - resets buffer state to intial state
+ */
+
+struct _malloc_trace_buffer_s *mtrace_buffer = NULL;
+size_t mtrace_buffer_bytesize = 0;
+
+int
+mtrace_start (void)
+{
+  const char *e;
+  char *fname;
+  int sequence = 0;
+
+  e = getenv("MTRACE_CTL_COUNT");
+  if (!e)
+    e = "1000";
+
+  e = getenv("MTRACE_CTL_FILE");
+  if (!e)
+    e = "/tmp/mtrace.out";
+
+  fname = alloca (strlen(e) + 30);
+  sprintf(fname, "%s.%d", e, getpid());
+  while (access (fname, F_OK) == 0)
+    {
+      sequence ++;
+      sprintf(fname, "%s.%d.%d", e, getpid(), sequence);
+    }
+
+  estr ("mtrace-ctl: writing to ");
+  estr (fname);
+  estr ("\n");
+
+  __malloc_trace_init (fname);
+  return 0;
+}
+
+void
+mtrace_stop (void)
+{
+  size_t count;
+  char line[100];
+
+  count = __malloc_trace_stop ();
+  sprintf (line, "mtrace-ctl: %lld entries recorded\n", (long long)count);
+  estr (line);
+}
+
+void
+mtrace_sync (void)
+{
+  __malloc_trace_sync ();
+  //  __malloc_trace_buffer_ptr buf = __malloc_get_trace_buffer (&size, &head);
+  //  msync (buf, size * sizeof(struct __malloc_trace_buffer_s), MS_SYNC | MS_INVALIDATE);
+}
+
+void
+mtrace_reset (void)
+{
+
+  __malloc_trace_stop ();
+  mtrace_start ();
+}
+
+void __attribute__((constructor))
+mtrace_ctor(void)
+{
+  if (mtrace_start ())
+    exit (1);
+}
+
+void __attribute__((destructor))
+mtrace_dtor(void)
+{
+  mtrace_stop ();
+  mtrace_sync ();
+}
+
+#if 0
+
+const char * const typenames[] = {
+  "unused  ",
+  "malloc  ",
+  "calloc  ",
+  "free    ",
+  "realloc ",
+  "memalign",
+  "valloc  ",
+  "pvalloc  ",
+};
+
+void __attribute__((destructor))
+djend(void)
+{
+  char *e;
+  FILE *outf;
+  size_t head, size, i;
+
+  e = getenv("MTRACE_CTL_FILE");
+  if (!e)
+    {
+      static char fname[100];
+      sprintf(fname, "/tmp/mtrace-%d.out", getpid());
+      e = fname;
+    }
+
+  outf = fopen(e, "w");
+  if (!outf)
+    err("cannot open output file");
+  setbuf (outf, NULL);
+
+  fprintf (outf, "%ld out of %ld events captured\n", (long)head, (long)size);
+
+  fprintf (outf, "threadid type     path     ptr1             size             ptr2\n");
+  for (i=0; i<size; i++)
+    {
+      __malloc_trace_buffer_ptr t = buf + (i+head) % size;
+
+      switch (t->type)
+	{
+	case __MTB_TYPE_UNUSED:
+	  break;
+	default:
+	  fprintf (outf, "%08x %s %c%c%c%c%c%c%c%c %016llx %016llx %016llx\n",
+		   t->thread,
+		   typenames[t->type],
+		   t->path_thread_cache ? 'T' : '-',
+		   t->path_cpu_cache ? 'c' : '-',
+		   t->path_cpu_cache2 ? 'C' : '-',
+		   t->path_sbrk ? 's' : '-',
+		   t->path_mmap ? 'M' : '-',
+		   t->path_munmap ? 'U' : '-',
+		   t->path_m_f_realloc ? 'R' : '-',
+		   t->path_hook ? 'H' : '-',
+		   (long long unsigned int) (size_t) t->ptr1,
+		   (long long unsigned int) t->size,
+		   (long long unsigned int) (size_t) t->ptr2);
+	  break;
+	}
+    }
+  fclose (outf);
+
+  munmap (buf, size * sizeof(struct __malloc_trace_buffer_s));
+  return;
+}
+#endif
diff -rupN a/malloc/mtrace.h b/malloc/mtrace.h
--- a/malloc/mtrace.h	1969-12-31 19:00:00.000000000 -0500
+++ b/malloc/mtrace.h	2017-08-15 19:33:22.119924142 -0400
@@ -0,0 +1,139 @@
+/* Malloc trace buffer API and definitions.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by DJ Delorie <dj@redhat.com>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+/* The trace buffer contains a minimal amount of information which can
+   be used to help reconstruct the memory profile of the calling
+   application.  */
+
+struct __malloc_trace_buffer_s {
+  int32_t thread; /* if thread_t > 32 bits, this is the LSBs */
+
+  uint32_t type:8; /* call type - malloc, free, etc */
+
+  uint32_t path_thread_cache:1; /* malloc/free interacted with the thread cache */
+  uint32_t path_cpu_cache:1; /* malloc/free interacted with the per-cpu cache */
+  uint32_t path_cpu_cache2:1; /* per-cpu cache contention required realloc */
+  uint32_t path_sbrk:1; /* sbrk was called */
+  uint32_t path_mmap:1; /* mmap was called */
+  uint32_t path_munmap:1; /* munmap was called */
+  uint32_t path_m_f_realloc:1; /* realloc became malloc/free (i.e. next few records) */
+  uint32_t path_hook:1; /* A hook was used to complete the request */
+  uint32_t path:16; /* remaining bits */
+
+  /* FREE - pointer to allocation to free.
+     REALLOC - pointer to original allocation.
+     POSIX_MEMALIGN - error code */
+  void *ptr1;
+  /* pointer to new allocation. */
+  void *ptr2;
+  /* requested size. */
+  size_t size;
+  /* FREE - internal size of deallocation.
+     REALLOC - internal size of original allocation.
+     MEMALIGN - alignment.
+     POSIX_MEMALIGN - alignment.  */
+  size_t size2;
+  /* internal size of new allocation.  */
+  size_t size3;
+  /* Pad out to 64-bytes for future uses and mmap'd window alignment.  */
+  size_t pad[2];
+};
+
+typedef struct __malloc_trace_buffer_s *__malloc_trace_buffer_ptr;
+
+/* Initialize the trace buffer and backing file.  The file is
+   overwritten if it already exists.  */
+void __malloc_trace_init (char *filename);
+
+/* All remaining functions return current count of trace records.  */
+
+/* Pause - but don't stop - tracing.  */
+size_t __malloc_trace_pause (void);
+
+/* Resume tracing where it left off when paused.  */
+size_t __malloc_trace_unpause (void);
+
+/* Stop tracing and clean up all the trace buffer mappings.  */
+size_t __malloc_trace_stop (void);
+
+/* Sync all buffer data to file (typically a no-op on Linux).  */
+size_t __malloc_trace_sync (void);
+
+
+#define __MTB_TYPE_UNUSED		0
+
+/* ptr1 is 0x1234, size is sizeof(void *) - there is one of these at
+   the beginning of the trace.  */
+#define __MTB_TYPE_MAGIC		255
+
+/* ptr2 = malloc (size) */
+#define __MTB_TYPE_MALLOC		1
+
+/* ptr2 = calloc (size) */
+#define __MTB_TYPE_CALLOC		2
+
+/* free (ptr1) */
+#define __MTB_TYPE_FREE			3
+
+/* ptr2 = realloc (ptr1, size) */
+#define __MTB_TYPE_REALLOC		4
+
+/* ptr2 = memalign (size2, size) */
+#define __MTB_TYPE_MEMALIGN		5
+
+/* ptr2 = valloc (size) */
+#define __MTB_TYPE_VALLOC		6
+
+/* ptr2 = pvalloc (size) */
+#define __MTB_TYPE_PVALLOC		7
+
+/* ptr2 = posix_memalign (ptr1, size2, size)  */
+#define __MTB_TYPE_POSIX_MEMALIGN	8
+
+typedef enum {
+  MSCAN_UNUSED,
+  MSCAN_ARENA,
+  MSCAN_HEAP,
+  MSCAN_CHUNK_USED,
+  MSCAN_CHUNK_FREE,
+  MSCAN_FASTBIN_FREE,
+  MSCAN_UNSORTED,
+  MSCAN_TOP,
+  MSCAN_TCACHE
+} MSCAN_Types;
+
+void __malloc_scan_chunks (void (*callback)(void * /*ptr*/, size_t /*length*/, int /*type*/));
+
+/* Codes for the simulator/workload programs. */
+#define C_NOP 0
+#define C_DONE 1
+#define C_MALLOC 2
+#define C_CALLOC 3
+#define C_REALLOC 4
+#define C_FREE 5
+#define C_SYNC_W 6
+#define C_SYNC_R 7
+#define C_ALLOC_PTRS 8
+#define C_ALLOC_SYNCS 9
+#define C_NTHREADS 10
+#define C_START_THREAD 11
+#define C_MEMALIGN 12
+#define C_VALLOC 13
+#define C_PVALLOC 14
+#define C_POSIX_MEMALIGN 15
diff -rupN a/shlib-versions b/shlib-versions
--- a/shlib-versions	2017-08-03 07:39:54.000000000 -0400
+++ b/shlib-versions	2017-08-15 20:25:58.857820701 -0400
@@ -75,3 +75,5 @@ libgcc_s=1
 
 # The vector math library
 libmvec=1
+
+libmtracectl=1
