+_debian/utilities/ofp-parse-leaks usr/bin
_debian/utilities/ofp-pki usr/sbin
_debian/utilities/vlogconf usr/sbin
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 \
--- /dev/null
+/* 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 <config.h>
+#include "leak-checker.h"
+#include <inttypes.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;
+ }
+ 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 */
--- /dev/null
+/* 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 <sys/types.h>
+
+#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 */
--- /dev/null
+.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.
#include "queue.h"
#include <assert.h>
#include "compiler.h"
+#include "leak-checker.h"
#include "ofpbuf.h"
static void check_queue(struct ofp_queue *q);
queue_push_tail(struct ofp_queue *q, struct ofpbuf *b)
{
check_queue(q);
+ leak_checker_claim(b);
b->next = NULL;
if (q->n++) {
ssl_clear_txbuf(sslv);
return 0;
case EAGAIN:
+ leak_checker_claim(buffer);
ssl_register_tx_waiter(vconn);
return 0;
default:
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);
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)
[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 <malloc.h>
+ ],
+ [(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 <malloc.h>.])
+ fi])
+
dnl Runs the checks required to include the headers in include/ and
dnl link against lib/libopenflow.a.
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])])
.so lib/daemon.man
.so lib/vlog.man
.so lib/common.man
+.so lib/leak-checker.man
.SH "SEE ALSO"
#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"
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},
{"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},
VLOG_OPTION_HANDLERS
+ LEAK_CHECKER_OPTION_HANDLERS
+
#ifdef HAVE_OPENSSL
VCONN_SSL_OPTION_HANDLERS
printf("\nOther options:\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
+ leak_checker_usage();
exit(EXIT_SUCCESS);
}
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 \
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 \
--- /dev/null
+#! @PERL@
+
+use strict;
+use warnings;
+
+if (grep($_ eq '--help', @ARGV)) {
+ print <<EOF;
+$0, for parsing leak checker logs
+usage: $0 [BINARY] < LOG
+where LOG is a log file produced by an OpenFlow program's --check-leaks option
+ and BINARY is the binary that wrote LOG.
+EOF
+ exit 0;
+}
+
+die "$0: zero or one arguments required; use --help for help\n" if @ARGV > 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 (<STDIN>) {
+ 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 = <A2L>);
+ chomp(my $line = <A2L>);
+ 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:
.so lib/daemon.man
.so lib/vlog.man
.so lib/common.man
+.so lib/leak-checker.man
.
.SH "BUGS"
.
#include "compiler.h"
#include "daemon.h"
#include "fault.h"
+#include "leak-checker.h"
#include "poll-loop.h"
#include "process.h"
#include "signals.h"
{
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'},
{"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},
VLOG_OPTION_HANDLERS
DAEMON_OPTION_HANDLERS
VCONN_SSL_OPTION_HANDLERS
+ LEAK_CHECKER_OPTION_HANDLERS
#ifdef HAVE_OPENSSL
case OPT_PEER_CA_CERT:
printf("\nOther options:\n"
" -h, --help display this help message\n"
" -V, --version display version information\n");
+ leak_checker_usage();
exit(EXIT_SUCCESS);
}