diff --git a/Makefile.objs b/Makefile.objs
index 59ec879..82a4fac 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -7,7 +7,7 @@ qobject-obj-y += qerror.o
 #######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
 
-block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
+block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o leakcheck.o
 block-obj-y += nbd.o block.o aio.o aes.o osdep.o
 block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
diff --git a/leakcheck.c b/leakcheck.c
new file mode 100644
index 0000000..a5fa51a
--- /dev/null
+++ b/leakcheck.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+
+static FILE *fp;
+
+extern void leakcheck_log(char action, void *old_addr, void *addr, size_t size, void *ret1);
+
+void leakcheck_log(char action, void *old_addr, void *addr, size_t size, void *ret1)
+{
+	if (!fp) {
+		fp = fopen("/tmp/leakcheck.log", "w");
+		if (!fp) {
+			return;
+		}
+	}
+
+	fprintf(fp, "%c %p %p %zd %p\n", action, old_addr, addr, size, ret1);
+}
diff --git a/leakcheck.py b/leakcheck.py
new file mode 100755
index 0000000..64b1a1b
--- /dev/null
+++ b/leakcheck.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+import sys
+
+class Event(object):
+    def __init__(self, num, action, old_addr, addr, size, ret_addr):
+        self.num = num
+        self.action = action
+        self.old_addr = old_addr
+        self.addr = addr
+        self.size = size
+        self.ret_addr = ret_addr
+
+    def __str__(self):
+        return '%d %s %s %s %s %s' % (self.num, self.action, self.old_addr, self.addr, self.size, self.ret_addr)
+
+def malloc(event):
+    if event.addr in allocs:
+        sys.stderr.write('malloc returned duplicate address from %s\n' % event)
+    allocs[event.addr] = event
+
+def free(event):
+    if event.addr == '(nil)':
+        return
+    if event.addr not in allocs:
+        sys.stderr.write('free of unallocated address from %s\n' % event)
+        return
+    malloc_event = allocs[event.addr]
+    del allocs[event.addr]
+    if (malloc_event.action in 'msz' and event.action == 'f') or \
+       (malloc_event.action == 'a' and event.action == 'v'):
+        return
+    sys.stderr.write('mismatched actions for %s and %s\n' % (malloc_event, event))
+
+def realloc(event):
+    free(Event(event.num, 'f', event.old_addr, '(nil)', 0, event.ret_addr))
+    malloc(Event(event.num, 'm', '(nil)', event.addr, event.size, event.ret_addr))
+
+allocs = {}
+watermark = 0
+event_num = 0
+for line in sys.stdin:
+    event_num += 1
+
+    cmd = line.strip()
+    if cmd == 'watermark':
+        watermark = event_num
+        continue
+
+    action, old_addr, addr, size, ret_addr = cmd.split()
+    event = Event(event_num, action, old_addr, addr, size, ret_addr)
+    if action in 'amsz':
+        malloc(event)
+    elif action in 'fv':
+        free(event)
+    elif action == 'r':
+        realloc(event)
+    else:
+        sys.stderr.write('invalid action "%c"\n' % action)
+        sys.exit(1)
+
+for event in sorted(allocs.itervalues(), key=lambda e: e.num):
+    if event.num > watermark:
+        print event
diff --git a/osdep.c b/osdep.c
index 8a710e7..40788e5 100644
--- a/osdep.c
+++ b/osdep.c
@@ -95,6 +95,8 @@ void qemu_vfree(void *ptr)
 
 #else
 
+extern void leakcheck_log(char action, void *old_addr, void *addr, size_t size, void *ret1);
+
 void *qemu_memalign(size_t alignment, size_t size)
 {
 #if defined(_POSIX_C_SOURCE) && !defined(__sun__)
@@ -110,7 +112,9 @@ void *qemu_memalign(size_t alignment, size_t size)
 #elif defined(CONFIG_BSD)
     return oom_check(valloc(size));
 #else
-    return oom_check(memalign(alignment, size));
+    void *p = oom_check(memalign(alignment, size));
+    leakcheck_log('a', NULL, p, size, __builtin_return_address(0));
+    return p;
 #endif
 }
 
@@ -126,6 +130,7 @@ void *qemu_vmalloc(size_t size)
 
 void qemu_vfree(void *ptr)
 {
+    leakcheck_log('v', NULL, ptr, 0, __builtin_return_address(0));
     free(ptr);
 }
 
diff --git a/qemu-malloc.c b/qemu-malloc.c
index 6cdc5de..bf832f2 100644
--- a/qemu-malloc.c
+++ b/qemu-malloc.c
@@ -24,6 +24,8 @@
 #include "qemu-common.h"
 #include <stdlib.h>
 
+extern void leakcheck_log(char action, void *old_addr, void *addr, size_t size, void *ret1);
+
 static void *oom_check(void *ptr)
 {
     if (ptr == NULL) {
@@ -39,6 +41,7 @@ void *get_mmap_addr(unsigned long size)
 
 void qemu_free(void *ptr)
 {
+    leakcheck_log('f', NULL, ptr, 0, __builtin_return_address(0));
     free(ptr);
 }
 
@@ -51,7 +54,7 @@ static int allow_zero_malloc(void)
 #endif
 }
 
-void *qemu_malloc(size_t size)
+static void *qemu_malloc_common(size_t size)
 {
     if (!size && !allow_zero_malloc()) {
         abort();
@@ -59,19 +62,30 @@ void *qemu_malloc(size_t size)
     return oom_check(malloc(size ? size : 1));
 }
 
+void *qemu_malloc(size_t size)
+{
+    void *p = qemu_malloc_common(size);
+    leakcheck_log('m', NULL, p, size, __builtin_return_address(0));
+    return p;
+}
+
 void *qemu_realloc(void *ptr, size_t size)
 {
     if (!size && !allow_zero_malloc()) {
         abort();
     }
-    return oom_check(realloc(ptr, size ? size : 1));
+    size = size ? size : 1;
+    void *p = oom_check(realloc(ptr, size));
+    leakcheck_log('r', ptr, p, size, __builtin_return_address(0));
+    return p;
 }
 
 void *qemu_mallocz(size_t size)
 {
     void *ptr;
-    ptr = qemu_malloc(size);
+    ptr = qemu_malloc_common(size);
     memset(ptr, 0, size);
+    leakcheck_log('z', NULL, ptr, size, __builtin_return_address(0));
     return ptr;
 }
 
@@ -79,8 +93,9 @@ char *qemu_strdup(const char *str)
 {
     char *ptr;
     size_t len = strlen(str);
-    ptr = qemu_malloc(len + 1);
+    ptr = qemu_malloc_common(len + 1);
     memcpy(ptr, str, len + 1);
+    leakcheck_log('s', NULL, ptr, len + 1, __builtin_return_address(0));
     return ptr;
 }
 
@@ -93,8 +108,9 @@ char *qemu_strndup(const char *str, size_t size)
         size = end - str;
     }
 
-    new = qemu_malloc(size + 1);
+    new = qemu_malloc_common(size + 1);
     new[size] = 0;
 
+    leakcheck_log('s', NULL, new, size + 1, __builtin_return_address(0));
     return memcpy(new, str, size);
 }
