summaryrefslogtreecommitdiff
path: root/lib/leak-checker.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/leak-checker.c')
-rw-r--r--lib/leak-checker.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/lib/leak-checker.c b/lib/leak-checker.c
new file mode 100644
index 000000000..a25fa71dc
--- /dev/null
+++ b/lib/leak-checker.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2008, 2009 Nicira Networks.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include "leak-checker.h"
+#include <inttypes.h>
+#include "backtrace.h"
+
+#define THIS_MODULE VLM_leak_checker
+#include "vlog.h"
+
+#ifndef HAVE_MALLOC_HOOKS
+void
+leak_checker_start(const char *file_name UNUSED)
+{
+ VLOG_WARN("not enabling leak checker because the libc in use does not "
+ "have the required hooks");
+}
+
+void
+leak_checker_set_limit(off_t max_size UNUSED)
+{
+}
+
+void
+leak_checker_claim(const void *p UNUSED)
+{
+}
+
+void
+leak_checker_usage(void)
+{
+ printf(" --check-leaks=FILE (accepted but ignored in this build)\n");
+}
+#else /* HAVE_MALLOC_HOOKS */
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <sys/stat.h>
+
+typedef void *malloc_hook_type(size_t, const void *);
+typedef void *realloc_hook_type(void *, size_t, const void *);
+typedef void free_hook_type(void *, const void *);
+
+struct hooks {
+ malloc_hook_type *malloc_hook_func;
+ realloc_hook_type *realloc_hook_func;
+ free_hook_type *free_hook_func;
+};
+
+static malloc_hook_type hook_malloc;
+static realloc_hook_type hook_realloc;
+static free_hook_type hook_free;
+
+static struct hooks libc_hooks;
+static const struct hooks our_hooks = { hook_malloc, hook_realloc, hook_free };
+
+static FILE *file;
+static off_t limit = 10 * 1000 * 1000;
+
+static void
+get_hooks(struct hooks *hooks)
+{
+ hooks->malloc_hook_func = __malloc_hook;
+ hooks->realloc_hook_func = __realloc_hook;
+ hooks->free_hook_func = __free_hook;
+}
+
+static void
+set_hooks(const struct hooks *hooks)
+{
+ __malloc_hook = hooks->malloc_hook_func;
+ __realloc_hook = hooks->realloc_hook_func;
+ __free_hook = hooks->free_hook_func;
+}
+
+void
+leak_checker_start(const char *file_name)
+{
+ if (!file) {
+ file = fopen(file_name, "w");
+ if (!file) {
+ VLOG_WARN("failed to create \"%s\": %s",
+ file_name, strerror(errno));
+ return;
+ }
+ setvbuf(file, NULL, _IOLBF, 0);
+ VLOG_WARN("enabled memory leak logging to \"%s\"", file_name);
+ get_hooks(&libc_hooks);
+ set_hooks(&our_hooks);
+ }
+}
+
+void
+leak_checker_stop(void)
+{
+ if (file) {
+ fclose(file);
+ file = NULL;
+ set_hooks(&libc_hooks);
+ VLOG_WARN("disabled memory leak logging");
+ }
+}
+
+void
+leak_checker_set_limit(off_t limit_)
+{
+ limit = limit_;
+}
+
+void
+leak_checker_usage(void)
+{
+ printf(" --check-leaks=FILE log malloc and free calls to FILE\n");
+}
+
+static void PRINTF_FORMAT(1, 2)
+log_callers(const char *format, ...)
+{
+ struct backtrace backtrace;
+ va_list args;
+ int i;
+
+ va_start(args, format);
+ vfprintf(file, format, args);
+ va_end(args);
+
+ putc(':', file);
+ backtrace_capture(&backtrace);
+ for (i = 0; i < backtrace.n_frames; i++) {
+ fprintf(file, " 0x%x", backtrace.frames[i]);
+ }
+ putc('\n', file);
+}
+
+static void
+reset_hooks(void)
+{
+ static int count;
+
+ if (file) {
+ if (ferror(file)) {
+ VLOG_WARN("error writing leak checker log file");
+ leak_checker_stop();
+ return;
+ }
+
+ if (count++ >= 100 && limit) {
+ struct stat s;
+ count = 0;
+ if (fstat(fileno(file), &s) < 0) {
+ VLOG_WARN("cannot fstat leak checker log file: %s",
+ strerror(errno));
+ leak_checker_stop();
+ return;
+ }
+ if (s.st_size > limit) {
+ VLOG_WARN("leak checker log file size exceeded limit");
+ leak_checker_stop();
+ return;
+ }
+ }
+ }
+ if (file) {
+ set_hooks(&our_hooks);
+ }
+}
+
+static void *
+hook_malloc(size_t size, const void *caller UNUSED)
+{
+ void *p;
+
+ set_hooks(&libc_hooks);
+ p = malloc(size);
+ get_hooks(&libc_hooks);
+
+ log_callers("malloc(%zu) -> %p", size, p);
+
+ reset_hooks();
+ return p;
+}
+
+void
+leak_checker_claim(const void *p)
+{
+ if (!file) {
+ return;
+ }
+
+ if (p) {
+ set_hooks(&libc_hooks);
+ log_callers("claim(%p)", p);
+ reset_hooks();
+ }
+}
+
+static void
+hook_free(void *p, const void *caller UNUSED)
+{
+ if (!p) {
+ return;
+ }
+
+ set_hooks(&libc_hooks);
+ free(p);
+ get_hooks(&libc_hooks);
+
+ log_callers("free(%p)", p);
+
+ reset_hooks();
+}
+
+static void *
+hook_realloc(void *p, size_t size, const void *caller UNUSED)
+{
+ void *q;
+
+ set_hooks(&libc_hooks);
+ q = realloc(p, size);
+ get_hooks(&libc_hooks);
+
+ if (p != q) {
+ log_callers("realloc(%p, %zu) -> %p", p, size, q);
+ }
+
+ reset_hooks();
+
+ return q;
+}
+#endif /* HAVE_MALLOC_HOOKS */