From 0634c9676a343dd01dbdde3ba7e51a1fba3b8575 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 13 Jan 2009 13:46:15 -0800 Subject: [PATCH] Implement simple memory leak detector. Initially, make it available only in secchan and vswitchd. But it's easy to add it elsewhere too. --- debian/openflow-common.install | 1 + lib/automake.mk | 2 + lib/leak-checker.c | 312 +++++++++++++++++++++++++++++++++ lib/leak-checker.h | 58 ++++++ lib/leak-checker.man | 7 + lib/queue.c | 2 + lib/vconn-ssl.c | 1 + lib/vconn-stream.c | 1 + lib/vlog-modules.def | 1 + m4/libopenflow.m4 | 21 +++ secchan/secchan.8.in | 1 + secchan/secchan.c | 8 +- utilities/automake.mk | 4 +- utilities/ofp-parse-leaks.in | 185 +++++++++++++++++++ vswitchd/vswitchd.8.in | 1 + vswitchd/vswitchd.c | 7 +- 16 files changed, 609 insertions(+), 3 deletions(-) create mode 100644 lib/leak-checker.c create mode 100644 lib/leak-checker.h create mode 100644 lib/leak-checker.man create mode 100755 utilities/ofp-parse-leaks.in diff --git a/debian/openflow-common.install b/debian/openflow-common.install index 624bc344..eed7413e 100644 --- a/debian/openflow-common.install +++ b/debian/openflow-common.install @@ -1,2 +1,3 @@ +_debian/utilities/ofp-parse-leaks usr/bin _debian/utilities/ofp-pki usr/sbin _debian/utilities/vlogconf usr/sbin diff --git a/lib/automake.mk b/lib/automake.mk index 53b17289..d3f5db00 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -29,6 +29,8 @@ lib_libopenflow_a_SOURCES = \ lib/hash.h \ lib/hmap.c \ lib/hmap.h \ + lib/leak-checker.c \ + lib/leak-checker.h \ lib/learning-switch.c \ lib/learning-switch.h \ lib/list.c \ diff --git a/lib/leak-checker.c b/lib/leak-checker.c new file mode 100644 index 00000000..defc1edb --- /dev/null +++ b/lib/leak-checker.c @@ -0,0 +1,312 @@ +/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#include +#include "leak-checker.h" +#include + +#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 +#include +#include +#include + +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; + } + 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 uintptr_t UNUSED +read_maps(void) +{ + static const char file_name[] = "/proc/self/maps"; + char line[1024]; + int line_number; + FILE *f; + + f = fopen(file_name, "r"); + if (f == NULL) { + VLOG_WARN("opening %s failed: %s", file_name, strerror(errno)); + return -1; + } + + for (line_number = 1; fgets(line, sizeof line, f); line_number++) { + if (strstr(line, "[stack]")) { + uintptr_t end; + if (sscanf(line, "%*"SCNxPTR"-%"SCNxPTR, &end) != 1) { + VLOG_WARN("%s:%d: parse error", file_name, line_number); + continue; + } + fclose(f); + return end; + } + } + fclose(f); + + VLOG_WARN("%s: no stack found", file_name); + return -1; +} + +static void +get_stack_limits(uintptr_t *lowp, uintptr_t *highp) +{ +#ifndef __x86__ + static bool warned = false; + if (!warned) { + warned = true; + VLOG_WARN("stack limit detection not implemented on " + "this architecture, possible segfaults from leak checker"); + } + *lowp = 0; + *highp = (uintptr_t) -1; +#else + static uintptr_t high; + if (!high) { + high = get_max_stack(); + } + asm("movl %%esp,%0" : "g" (*lowp)); + *highp = high; +#endif +} + +static bool +in_stack(void *p) +{ + uintptr_t address = (uintptr_t) p; + uintptr_t low, high; + get_stack_limits(&low, &high); + return address >= low && address < high; +} + +static void PRINTF_FORMAT(1, 2) +log_callers(const char *format, ...) +{ + va_list args; + void **frame; + + va_start(args, format); + vfprintf(file, format, args); + va_end(args); + + putc(':', file); + for (frame = __builtin_frame_address(0); + frame != NULL && in_stack(frame) && frame[0] != NULL; + frame = frame[0]) { + fprintf(file, " %p", frame[1]); + } + putc('\n', file); +} + +static void +reset_hooks(void) +{ + static int count; + + if (count++ >= 100 && limit && file) { + struct stat s; + count = 0; + if (fstat(fileno(file), &s) < 0) { + VLOG_WARN("cannot fstat leak checker log file: %s", + strerror(errno)); + 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 */ diff --git a/lib/leak-checker.h b/lib/leak-checker.h new file mode 100644 index 00000000..fb16d34d --- /dev/null +++ b/lib/leak-checker.h @@ -0,0 +1,58 @@ +/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford + * Junior University + * + * We are making the OpenFlow specification and associated documentation + * (Software) available for public use and benefit with the expectation + * that others will use, modify and enhance the Software and contribute + * those enhancements back to the community. However, since we would + * like to make the Software available for broadest use, with as few + * restrictions as possible permission is hereby granted, free of + * charge, to any person obtaining a copy of this Software to deal in + * the Software under the copyrights without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The name and trademarks of copyright holder(s) may NOT be used in + * advertising or publicity pertaining to the Software or any + * derivatives without specific, written prior permission. + */ + +#ifndef LEAK_CHECKER_H +#define LEAK_CHECKER_H 1 + +#include + +#define LEAK_CHECKER_OPTION_ENUMS \ + OPT_CHECK_LEAKS, \ + OPT_LEAK_LIMIT +#define LEAK_CHECKER_LONG_OPTIONS \ + {"check-leaks", required_argument, 0, OPT_CHECK_LEAKS}, \ + {"leak-limit", required_argument, 0, OPT_LEAK_LIMIT} +#define LEAK_CHECKER_OPTION_HANDLERS \ + case OPT_CHECK_LEAKS: \ + leak_checker_start(optarg); \ + break; \ + case OPT_LEAK_LIMIT: \ + leak_checker_set_limit(atol(optarg)); \ + break; +void leak_checker_start(const char *file_name); +void leak_checker_set_limit(off_t limit); +void leak_checker_stop(void); +void leak_checker_claim(const void *); +void leak_checker_usage(void); + +#endif /* dynamic-string.h */ diff --git a/lib/leak-checker.man b/lib/leak-checker.man new file mode 100644 index 00000000..7b376e1a --- /dev/null +++ b/lib/leak-checker.man @@ -0,0 +1,7 @@ +.TP +\fB--check-leaks=\fIfile\fR +. +Logs information about memory allocation and deallocation to +\fIfile\fR, to allow for debugging memory leaks in \fB\*(PN\fR. This +option slows down \fB\*(PN\fR considerably, so it should only be used +when a memory leak is suspected. diff --git a/lib/queue.c b/lib/queue.c index 24eb01d7..33e50067 100644 --- a/lib/queue.c +++ b/lib/queue.c @@ -35,6 +35,7 @@ #include "queue.h" #include #include "compiler.h" +#include "leak-checker.h" #include "ofpbuf.h" static void check_queue(struct ofp_queue *q); @@ -90,6 +91,7 @@ void queue_push_tail(struct ofp_queue *q, struct ofpbuf *b) { check_queue(q); + leak_checker_claim(b); b->next = NULL; if (q->n++) { diff --git a/lib/vconn-ssl.c b/lib/vconn-ssl.c index 10354426..c6fde670 100644 --- a/lib/vconn-ssl.c +++ b/lib/vconn-ssl.c @@ -718,6 +718,7 @@ ssl_send(struct vconn *vconn, struct ofpbuf *buffer) ssl_clear_txbuf(sslv); return 0; case EAGAIN: + leak_checker_claim(buffer); ssl_register_tx_waiter(vconn); return 0; default: diff --git a/lib/vconn-stream.c b/lib/vconn-stream.c index cdbea1fb..cdba45f2 100644 --- a/lib/vconn-stream.c +++ b/lib/vconn-stream.c @@ -213,6 +213,7 @@ stream_send(struct vconn *vconn, struct ofpbuf *buffer) ofpbuf_delete(buffer); return 0; } else if (retval >= 0 || errno == EAGAIN) { + leak_checker_claim(buffer); s->txbuf = buffer; if (retval > 0) { ofpbuf_pull(buffer, retval); diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def index 434bcfa8..a78b04e4 100644 --- a/lib/vlog-modules.def +++ b/lib/vlog-modules.def @@ -18,6 +18,7 @@ VLOG_MODULE(fault) VLOG_MODULE(flow) VLOG_MODULE(flow_end) VLOG_MODULE(in_band) +VLOG_MODULE(leak_checker) VLOG_MODULE(learning_switch) VLOG_MODULE(mac_learning) VLOG_MODULE(netdev) diff --git a/m4/libopenflow.m4 b/m4/libopenflow.m4 index c1787f4f..294932b0 100644 --- a/m4/libopenflow.m4 +++ b/m4/libopenflow.m4 @@ -150,6 +150,26 @@ AC_DEFUN([OFP_CHECK_LOGDIR], [LOGDIR='${localstatedir}/log/${PACKAGE}']) AC_SUBST([LOGDIR])]) +dnl Checks for __malloc_hook, etc., supported by glibc. +AC_DEFUN([OFP_CHECK_MALLOC_HOOKS], + [AC_CACHE_CHECK( + [whether libc supports hooks for malloc and related functions], + [ofp_cv_malloc_hooks], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include + ], + [(void) __malloc_hook; + (void) __realloc_hook; + (void) __free_hook;])], + [ofp_cv_malloc_hooks=yes], + [ofp_cv_malloc_hooks=no])]) + if test $ofp_cv_malloc_hooks = yes; then + AC_DEFINE([HAVE_MALLOC_HOOKS], [1], + [Define to 1 if you have __malloc_hook, __realloc_hook, and + __free_hook in .]) + fi]) + dnl Runs the checks required to include the headers in include/ and dnl link against lib/libopenflow.a. AC_DEFUN([OFP_CHECK_LIBOPENFLOW], @@ -163,5 +183,6 @@ AC_DEFUN([OFP_CHECK_LIBOPENFLOW], AC_REQUIRE([OFP_CHECK_PKIDIR]) AC_REQUIRE([OFP_CHECK_RUNDIR]) AC_REQUIRE([OFP_CHECK_LOGDIR]) + AC_REQUIRE([OFP_CHECK_MALLOC_HOOKS]) AC_CHECK_FUNCS([strlcpy])]) diff --git a/secchan/secchan.8.in b/secchan/secchan.8.in index c536f007..bcb3970c 100644 --- a/secchan/secchan.8.in +++ b/secchan/secchan.8.in @@ -434,6 +434,7 @@ require the controller to send the CA certificate, but .so lib/daemon.man .so lib/vlog.man .so lib/common.man +.so lib/leak-checker.man .SH "SEE ALSO" diff --git a/secchan/secchan.c b/secchan/secchan.c index 04c5d9d6..03a5b791 100644 --- a/secchan/secchan.c +++ b/secchan/secchan.c @@ -50,6 +50,7 @@ #include "fail-open.h" #include "fault.h" #include "in-band.h" +#include "leak-checker.h" #include "list.h" #include "ofpbuf.h" #include "openflow/openflow.h" @@ -590,7 +591,8 @@ parse_options(int argc, char *argv[], struct settings *s) OPT_COMMAND_ACL, OPT_COMMAND_DIR, OPT_NETFLOW, - VLOG_OPTION_ENUMS + VLOG_OPTION_ENUMS, + LEAK_CHECKER_OPTION_ENUMS }; static struct option long_options[] = { {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN}, @@ -615,6 +617,7 @@ parse_options(int argc, char *argv[], struct settings *s) {"version", no_argument, 0, 'V'}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, + LEAK_CHECKER_LONG_OPTIONS, #ifdef HAVE_OPENSSL VCONN_SSL_LONG_OPTIONS {"bootstrap-ca-cert", required_argument, 0, OPT_BOOTSTRAP_CA_CERT}, @@ -775,6 +778,8 @@ parse_options(int argc, char *argv[], struct settings *s) VLOG_OPTION_HANDLERS + LEAK_CHECKER_OPTION_HANDLERS + #ifdef HAVE_OPENSSL VCONN_SSL_OPTION_HANDLERS @@ -878,5 +883,6 @@ usage(void) printf("\nOther options:\n" " -h, --help display this help message\n" " -V, --version display version information\n"); + leak_checker_usage(); exit(EXIT_SUCCESS); } diff --git a/utilities/automake.mk b/utilities/automake.mk index f9ce28d6..d6f79a8c 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -4,12 +4,13 @@ bin_PROGRAMS += \ utilities/ofp-discover \ utilities/ofp-kill bin_SCRIPTS += utilities/ofp-pki -noinst_SCRIPTS += utilities/ofp-pki-cgi +noinst_SCRIPTS += utilities/ofp-pki-cgi utilities/ofp-parse-leaks EXTRA_DIST += \ utilities/dpctl.8.in \ utilities/ofp-discover.8.in \ utilities/ofp-kill.8.in \ + utilities/ofp-parse-leaks.in \ utilities/ofp-pki-cgi.in \ utilities/ofp-pki.8.in \ utilities/ofp-pki.in \ @@ -18,6 +19,7 @@ DISTCLEANFILES += \ utilities/dpctl.8 \ utilities/ofp-discover.8 \ utilities/ofp-kill.8 \ + utilities/ofp-parse-leaks \ utilities/ofp-pki \ utilities/ofp-pki.8 \ utilities/ofp-pki-cgi \ diff --git a/utilities/ofp-parse-leaks.in b/utilities/ofp-parse-leaks.in new file mode 100755 index 00000000..c632a59c --- /dev/null +++ b/utilities/ofp-parse-leaks.in @@ -0,0 +1,185 @@ +#! @PERL@ + +use strict; +use warnings; + +if (grep($_ eq '--help', @ARGV)) { + print < 1; +die "$0: $ARGV[0] does not exist" if @ARGV > 0 && ! -e $ARGV[0]; + +our ($binary); +our ($a2l) = search_path("addr2line"); +my ($no_syms) = "symbols will not be translated"; +if (!@ARGV) { + print "no binary specified; $no_syms\n"; +} elsif (! -e $ARGV[0]) { + print "$ARGV[0] does not exist; $no_syms"; +} elsif (!defined($a2l)) { + print "addr2line not found in PATH; $no_syms"; +} else { + $binary = $ARGV[0]; +} + +our %blocks; +while () { + my $ptr = "(0x[0-9a-fA-F]+|\\(nil\\))"; + my $callers = ":((?: $ptr)+)"; + if (/^malloc\((\d+)\) -> $ptr$callers$/) { + allocated($., $2, $1, $3); + } elsif (/^claim\($ptr\)$callers$/) { + claimed($., $1, $2); + } elsif (/realloc\($ptr, (\d+)\) -> $ptr$callers$/) { + my ($callers) = $4; + freed($., $1, $callers); + allocated($., $3, $2, $callers); + } elsif (/^free\($ptr\)$callers$/) { + freed($., $1, $2); + } else { + print "stdin:$.: syntax error\n"; + } +} +if (%blocks) { + my $n_blocks = scalar(keys(%blocks)); + my $n_bytes = 0; + $n_bytes += $_->{SIZE} foreach values(%blocks); + print "$n_bytes bytes in $n_blocks blocks not freed at end of run\n"; + my %blocks_by_callers; + foreach my $block (values(%blocks)) { + my ($trimmed_callers) = trim_callers($block->{CALLERS}); + push (@{$blocks_by_callers{$trimmed_callers}}, $block); + } + foreach my $callers (sort {@{$b} <=> @{$a}} (values(%blocks_by_callers))) { + $n_blocks = scalar(@{$callers}); + $n_bytes = 0; + $n_bytes += $_->{SIZE} foreach @{$callers}; + print "$n_bytes bytes in these $n_blocks blocks were not freed:\n"; + my $i = 0; + my $max = 5; + foreach my $block (sort {$a->{LINE} <=> $b->{LINE}} (@{$callers})) { + printf "\t%d-byte block at 0x%08x allocated on stdin:%d\n", + $block->{SIZE}, $block->{BASE}, $block->{LINE}; + last if $i++ > $max; + } + print "\t...and ", $n_blocks - $max, " others...\n" + if $n_blocks > $max; + print "The blocks listed above were allocated by:\n"; + print_callers("\t", ${$callers}[0]->{CALLERS}); + } +} +sub interp_pointer { + my ($s_ptr) = @_; + return $s_ptr eq '(nil)' ? 0 : hex($s_ptr); +} + +sub allocated { + my ($line, $s_base, $size, $callers) = @_; + my ($base) = interp_pointer($s_base); + return if !$base; + my ($info) = {LINE => $line, + BASE => $base, + SIZE => $size, + CALLERS => $callers}; + if (exists($blocks{$base})) { + print "In-use address returned by allocator:\n"; + print "\tInitial allocation:\n"; + print_block("\t\t", $blocks{$base}); + print "\tNew allocation:\n"; + print_block("\t\t", $info); + } + $blocks{$base} = $info; +} + +sub claimed { + my ($line, $s_base, $callers) = @_; + my ($base) = interp_pointer($s_base); + return if !$base; + if (exists($blocks{$base})) { + $blocks{$base}{LINE} = $line; + $blocks{$base}{CALLERS} = $callers; + } else { + printf "Claim asserted on not-in-use block 0x%08x by:\n", $base; + print_callers('', $callers); + } +} + +sub freed { + my ($line, $s_base, $callers) = @_; + my ($base) = interp_pointer($s_base); + return if !$base; + + if (!delete($blocks{$base})) { + printf "Bad free of not-allocated address 0x%08x on stdin:%d by:\n", $base, $line; + print_callers('', $callers); + } +} + +sub print_block { + my ($prefix, $info) = @_; + printf '%s%d-byte block at 0x%08x allocated on stdin:%d by:' . "\n", + $prefix, $info->{SIZE}, $info->{BASE}, $info->{LINE}; + print_callers($prefix, $info->{CALLERS}); +} + +sub print_callers { + my ($prefix, $callers) = @_; + foreach my $pc (split(' ', $callers)) { + print "$prefix\t", lookup_pc($pc), "\n"; + } +} + +our (%cache); +sub lookup_pc { + my ($s_pc) = @_; + if (defined($binary)) { + my ($pc) = hex($s_pc); + my ($output) = "$s_pc: "; + if (!exists($cache{$pc})) { + open(A2L, "$a2l -fe $binary $s_pc|"); + chomp(my $function = ); + chomp(my $line = ); + close(A2L); + $line =~ s/^(\.\.\/)*//; + $line = "..." . substr($line, -25) if length($line) > 28; + $cache{$pc} = "$s_pc: $function ($line)"; + } + return $cache{$pc}; + } else { + return "$s_pc"; + } +} + +sub trim_callers { + my ($in) = @_; + my (@out); + foreach my $pc (split(' ', $in)) { + my $xlated = lookup_pc($pc); + if ($xlated =~ /\?\?/) { + push(@out, "...") if !@out || $out[$#out] ne '...'; + } else { + push(@out, $pc); + } + } + return join(' ', @out); +} + +sub search_path { + my ($target) = @_; + for my $dir (split (':', $ENV{PATH})) { + my ($file) = "$dir/$target"; + return $file if -e $file; + } + return undef; +} + +# Local Variables: +# mode: perl +# End: diff --git a/vswitchd/vswitchd.8.in b/vswitchd/vswitchd.8.in index 76ad20e4..ec7ac1be 100644 --- a/vswitchd/vswitchd.8.in +++ b/vswitchd/vswitchd.8.in @@ -97,6 +97,7 @@ loaded. .so lib/daemon.man .so lib/vlog.man .so lib/common.man +.so lib/leak-checker.man . .SH "BUGS" . diff --git a/vswitchd/vswitchd.c b/vswitchd/vswitchd.c index 65bf041f..dc1cfa1d 100644 --- a/vswitchd/vswitchd.c +++ b/vswitchd/vswitchd.c @@ -41,6 +41,7 @@ #include "compiler.h" #include "daemon.h" #include "fault.h" +#include "leak-checker.h" #include "poll-loop.h" #include "process.h" #include "signals.h" @@ -117,7 +118,8 @@ parse_options(int argc, char *argv[]) { enum { OPT_PEER_CA_CERT, - VLOG_OPTION_ENUMS + VLOG_OPTION_ENUMS, + LEAK_CHECKER_OPTION_ENUMS }; static struct option long_options[] = { {"brcompat", required_argument, 0, 'b'}, @@ -126,6 +128,7 @@ parse_options(int argc, char *argv[]) {"version", no_argument, 0, 'V'}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, + LEAK_CHECKER_LONG_OPTIONS, #ifdef HAVE_OPENSSL VCONN_SSL_LONG_OPTIONS {"peer-ca-cert", required_argument, 0, OPT_PEER_CA_CERT}, @@ -170,6 +173,7 @@ parse_options(int argc, char *argv[]) VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS VCONN_SSL_OPTION_HANDLERS + LEAK_CHECKER_OPTION_HANDLERS #ifdef HAVE_OPENSSL case OPT_PEER_CA_CERT: @@ -209,5 +213,6 @@ usage(void) printf("\nOther options:\n" " -h, --help display this help message\n" " -V, --version display version information\n"); + leak_checker_usage(); exit(EXIT_SUCCESS); } -- 2.30.2