WARN_CFLAGS=$save_WARN_CFLAGS
fi
-LIBVIRT_ARG_ENABLE([TEST_OOM], [memory allocation failure checking], [no])
-case "$enable_test_oom" in
- yes|no) ;;
- *) AC_MSG_ERROR([bad value ${enable_test_oom} for test-oom option]) ;;
-esac
-
-if test "$enable_test_oom" = yes; then
- have_trace=yes
- AC_CHECK_HEADER([execinfo.h],[],[have_trace=no])
- AC_CHECK_FUNC([backtrace],[],[have_trace=no])
- if test "$have_trace" = "yes"; then
- AC_DEFINE([TEST_OOM_TRACE], 1, [Whether backtrace() is available])
- fi
- AC_DEFINE([TEST_OOM], 1, [Whether malloc OOM checking is enabled])
-fi
-
LIBVIRT_ARG_ENABLE([TEST_LOCKING], [thread locking tests using CIL], [no])
case "$enable_test_locking" in
yes|no) ;;
AC_MSG_NOTICE([Test suite])
AC_MSG_NOTICE([])
LIBVIRT_RESULT([Coverage], [$enable_test_coverage])
-LIBVIRT_RESULT([Alloc OOM], [$enable_test_oom])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Miscellaneous])
AC_MSG_NOTICE([])
<dt><a href="internals/locking.html">Lock managers</a></dt>
<dd>Use lock managers to protect disk content</dd>
- <dt><a href="internals/oomtesting.html">Out of memory testing</a></dt>
- <dd>Simulating OOM conditions in the test suite</dd>
-
<dt><a href="testsuites.html">Functional testing</a></dt>
<dd>Testing libvirt with <a href="testtck.html">TCK test suite</a> and
<a href="testapi.html">Libvirt-test-API</a></dd>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml">
- <body>
- <h1>Out of memory testing</h1>
-
- <ul id="toc"></ul>
-
-
- <p>
- This page describes how to use the test suite todo out of memory
- testing.
- </p>
-
- <h2>Building with OOM testing</h2>
-
- <p>
- Since OOM testing requires hooking into the malloc APIs, it is
- not enabled by default. The flag <code>--enable-test-oom</code>
- must be given to <code>configure</code>. When this is done the
- libvirt allocation APIs will have some hooks enabled.
- </p>
-
- <pre>
-$ ./configure --enable-test-oom
-</pre>
-
-
- <h2><a id="basicoom">Basic OOM testing support</a></h2>
-
- <p>
- The first step in validating OOM usage is to run a test suite
- with full OOM testing enabled. This is done by setting the
- <code>VIR_TEST_OOM=1</code> environment variable. The way this
- works is that it runs the test once normally to "prime" any
- static memory allocations. Then it runs it once more counting
- the total number of memory allocations. Then it runs it in a
- loop failing a different memory allocation each time. For every
- memory allocation failure triggered, it expects the test case
- to return an error. OOM testing is quite slow requiring each
- test case to be executed O(n) times, where 'n' is the total
- number of memory allocations. This results in a total number
- of memory allocations of '(n * (n + 1) ) / 2'
- </p>
-
- <pre>
-$ VIR_TEST_OOM=1 ./qemuxml2argvtest
- 1) QEMU XML-2-ARGV minimal ... OK
- Test OOM for nalloc=42 .......................................... OK
- 2) QEMU XML-2-ARGV minimal-s390 ... OK
- Test OOM for nalloc=28 ............................ OK
- 3) QEMU XML-2-ARGV machine-aliases1 ... OK
- Test OOM for nalloc=38 ...................................... OK
- 4) QEMU XML-2-ARGV machine-aliases2 ... OK
- Test OOM for nalloc=38 ...................................... OK
- 5) QEMU XML-2-ARGV machine-core-on ... OK
- Test OOM for nalloc=37 ..................................... OK
-...snip...
-</pre>
-
- <p>
- In this output, the first line shows the normal execution and
- the test number, and the second line shows the total number
- of memory allocations from that test case.
- </p>
-
- <h3><a id="valgrind">Tracking failures with valgrind</a></h3>
-
- <p>
- The test suite should obviously *not* crash during OOM testing.
- If it does crash, then to assist in tracking down the problem
- it is worth using valgrind and only running a single test case.
- For example, supposing test case 5 crashed. Then re-run the
- test with
- </p>
-
- <pre>
-$ VIR_TEST_OOM=1 VIR_TEST_RANGE=5 ../run valgrind ./qemuxml2argvtest
-...snip...
- 5) QEMU XML-2-ARGV machine-core-on ... OK
- Test OOM for nalloc=37 ..................................... OK
-...snip...
- </pre>
-
- <p>
- Valgrind should report the cause of the crash - for example a
- double free or use of uninitialized memory or NULL pointer
- access.
- </p>
-
- <h3><a id="stacktraces">Tracking failures with stack traces</a></h3>
-
- <p>
- With some really difficult bugs valgrind is not sufficient to
- identify the cause. In this case, it is useful to identify the
- precise allocation which was failed, to allow the code path
- to the error to be traced. The <code>VIR_TEST_OOM</code>
- env variable can be given a range of memory allocations to
- test. So if a test case has 150 allocations, it can be told
- to only test allocation numbers 7-10. The <code>VIR_TEST_OOM_TRACE</code>
- variable can be used to print out stack traces.
- </p>
-
- <pre>
-$ VIR_TEST_OOM_TRACE=2 VIR_TEST_OOM=1:7-10 VIR_TEST_RANGE=5 \
- ../run valgrind ./qemuxml2argvtest
- 5) QEMU XML-2-ARGV machine-core-on ... OK
- Test OOM for nalloc=37 !virAllocN
-/home/berrange/src/virt/libvirt/src/util/viralloc.c:180
-virDomainDefParseXML
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:11786 (discriminator 1)
-virDomainDefParseNode
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:12677
-virDomainDefParse
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:12621
-testCompareXMLToArgvFiles
-/home/berrange/src/virt/libvirt/tests/qemuxml2argvtest.c:107
-virtTestRun
-/home/berrange/src/virt/libvirt/tests/testutils.c:266
-mymain
-/home/berrange/src/virt/libvirt/tests/qemuxml2argvtest.c:388 (discriminator 2)
-virtTestMain
-/home/berrange/src/virt/libvirt/tests/testutils.c:791
-__libc_start_main
-??:?
-_start
-??:?
-!virAlloc
-/home/berrange/src/virt/libvirt/src/util/viralloc.c:133
-virDomainDiskDefParseXML
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:4790
-virDomainDefParseXML
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:11797
-virDomainDefParseNode
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:12677
-virDomainDefParse
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:12621
-testCompareXMLToArgvFiles
-/home/berrange/src/virt/libvirt/tests/qemuxml2argvtest.c:107
-virtTestRun
-/home/berrange/src/virt/libvirt/tests/testutils.c:266
-mymain
-/home/berrange/src/virt/libvirt/tests/qemuxml2argvtest.c:388 (discriminator 2)
-virtTestMain
-/home/berrange/src/virt/libvirt/tests/testutils.c:791
-__libc_start_main
-??:?
-_start
-??:?
-!virAllocN
-/home/berrange/src/virt/libvirt/src/util/viralloc.c:180
-virXPathNodeSet
-/home/berrange/src/virt/libvirt/src/util/virxml.c:609
-virDomainDefParseXML
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:11805
-virDomainDefParseNode
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:12677
-virDomainDefParse
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:12621
-testCompareXMLToArgvFiles
-/home/berrange/src/virt/libvirt/tests/qemuxml2argvtest.c:107
-virtTestRun
-/home/berrange/src/virt/libvirt/tests/testutils.c:266
-mymain
-/home/berrange/src/virt/libvirt/tests/qemuxml2argvtest.c:388 (discriminator 2)
-virtTestMain
-/home/berrange/src/virt/libvirt/tests/testutils.c:791
-__libc_start_main
-??:?
-_start
-??:?
-!virAllocN
-/home/berrange/src/virt/libvirt/src/util/viralloc.c:180
-virDomainDefParseXML
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:11808 (discriminator 1)
-virDomainDefParseNode
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:12677
-virDomainDefParse
-/home/berrange/src/virt/libvirt/src/conf/domain_conf.c:12621
-testCompareXMLToArgvFiles
-/home/berrange/src/virt/libvirt/tests/qemuxml2argvtest.c:107
-virtTestRun
-/home/berrange/src/virt/libvirt/tests/testutils.c:266
-mymain
-/home/berrange/src/virt/libvirt/tests/qemuxml2argvtest.c:388 (discriminator 2)
-virtTestMain
-/home/berrange/src/virt/libvirt/tests/testutils.c:791
-__libc_start_main
-??:?
-_start
-??:?
- </pre>
-
- <h3><a id="noncrash">Non-crash related problems</a></h3>
-
- <p>
- Not all memory allocation bugs result in code crashing. Sometimes
- the code will be silently ignoring the allocation failure, resulting
- in incorrect data being produced. For example the XML parser may
- mistakenly treat an allocation failure as indicating that an XML
- attribute was not set in the input document. It is hard to identify
- these problems from the test suite automatically. For this, the
- test suites should be run with <code>VIR_TEST_DEBUG=1</code> set
- and then stderr analysed for any unexpected data. For example,
- the XML conversion may show an embedded "(null)" literal, or the
- test suite might complain about missing elements / attributes
- in the actual vs expected data. These are all signs of bugs in
- OOM handling. In the future the OOM tests will be enhanced to
- validate that an error VIR_ERR_NO_MEMORY is returned for each
- allocation failed, rather than some other error.
- </p>
- </body>
-</html>
# util/viralloc.h
virAlloc;
virAllocN;
-virAllocTestCount;
-virAllocTestHook;
-virAllocTestInit;
-virAllocTestOOM;
virAllocVar;
virDeleteElementsN;
virDispose;
VIR_LOG_INIT("util.alloc");
-#if TEST_OOM
-static int testMallocNext;
-static int testMallocFailFirst;
-static int testMallocFailLast;
-static void (*testMallocHook)(int, void*);
-static void *testMallocHookData;
-
-void virAllocTestInit(void)
-{
- testMallocNext = 1;
- testMallocFailFirst = 0;
- testMallocFailLast = 0;
-}
-
-int virAllocTestCount(void)
-{
- return testMallocNext - 1;
-}
-
-void virAllocTestHook(void (*func)(int, void*), void *data)
-{
- testMallocHook = func;
- testMallocHookData = data;
-}
-
-void virAllocTestOOM(int n, int m)
-{
- testMallocNext = 1;
- testMallocFailFirst = n;
- testMallocFailLast = n + m - 1;
-}
-
-static int virAllocTestFail(void)
-{
- int fail = 0;
- if (testMallocNext == 0)
- return 0;
-
- fail =
- testMallocNext >= testMallocFailFirst &&
- testMallocNext <= testMallocFailLast;
-
- if (fail && testMallocHook)
- (testMallocHook)(testMallocNext, testMallocHookData);
-
- testMallocNext++;
- return fail;
-}
-
-#else
-
-void virAllocTestOOM(int n ATTRIBUTE_UNUSED,
- int m ATTRIBUTE_UNUSED)
-{
- /* nada */
-}
-
-int virAllocTestCount(void)
-{
- return 0;
-}
-
-void virAllocTestInit(void)
-{
- /* nada */
-}
-
-void virAllocTestHook(void (*func)(int, void*) ATTRIBUTE_UNUSED,
- void *data ATTRIBUTE_UNUSED)
-{
- /* nada */
-}
-#endif
-
/**
* virAlloc:
const char *funcname,
size_t linenr)
{
-#if TEST_OOM
- if (virAllocTestFail()) {
- *(void **)ptrptr = NULL;
- if (report)
- virReportOOMErrorFull(domcode, filename, funcname, linenr);
- errno = ENOMEM;
- return -1;
- }
-#endif
-
*(void **)ptrptr = calloc(1, size);
if (*(void **)ptrptr == NULL) {
if (report)
const char *funcname,
size_t linenr)
{
-#if TEST_OOM
- if (virAllocTestFail()) {
- *(void **)ptrptr = NULL;
- if (report)
- virReportOOMErrorFull(domcode, filename, funcname, linenr);
- errno = ENOMEM;
- return -1;
- }
-#endif
-
*(void**)ptrptr = calloc(count, size);
if (*(void**)ptrptr == NULL) {
if (report)
size_t linenr)
{
void *tmp;
-#if TEST_OOM
- if (virAllocTestFail()) {
- if (report)
- virReportOOMErrorFull(domcode, filename, funcname, linenr);
- errno = ENOMEM;
- return -1;
- }
-#endif
if (xalloc_oversized(count, size)) {
if (report)
{
size_t alloc_size = 0;
-#if TEST_OOM
- if (virAllocTestFail()) {
- if (report)
- virReportOOMErrorFull(domcode, filename, funcname, linenr);
- errno = ENOMEM;
- return -1;
- }
-#endif
-
if (VIR_ALLOC_VAR_OVERSIZED(struct_size, count, element_size)) {
if (report)
virReportOOMErrorFull(domcode, filename, funcname, linenr);
sizeof(*(ptr)), NULL)
-void virAllocTestInit(void);
-int virAllocTestCount(void);
-void virAllocTestOOM(int n, int m);
-void virAllocTestHook(void (*func)(int, void*), void *data);
-
/**
* VIR_AUTOFREE:
* @type: type of the variable to be freed automatically
nwfilterxml2firewalldata \
nwfilterxml2xmlin \
nwfilterxml2xmlout \
- oomtrace.pl \
qemuagentdata \
qemublocktestdata \
qemucapabilitiesdata \
+++ /dev/null
-#!/usr/bin/env perl
-
-use strict;
-use warnings;
-
-(my $ME = $0) =~ s|.*/||;
-# use File::Coda; # http://meyering.net/code/Coda/
-END {
- defined fileno STDOUT or return;
- close STDOUT and return;
- warn "$ME: failed to close standard output: $!\n";
- $? ||= 1;
-}
-
-
-my @data = <>;
-
-
-my %trace;
-my %lines;
-
-foreach (@data) {
- if (/^\s*TRACE:\s+(\S+?)(?:\(.*\))?\s+\[0x(.*)\]\s*$/ ) {
- $trace{$2} = $1;
- }
-}
-
-foreach my $key (keys %trace) {
- my $val = $trace{$key};
- my $info = $val =~ /\?\?/ ? $val : `addr2line -e $val $key`;
- $lines{$key} = $info;
-}
-
-
-foreach (@data) {
- if (/^\s*TRACE:\s+(\S+?)(?:\(.*\))?\s+\[0x(.*)\]\s*$/ ) {
- print $lines{$2};
- } else {
- print;
- }
-}
VIR_TEST_DEBUG("Error expected but there wasn't any.");
goto cleanup;
}
- if (!virTestOOMActive()) {
- if (flags & FLAG_EXPECT_FAILURE) {
- if ((log = virTestLogContentAndReset()))
- VIR_TEST_DEBUG("Got expected error: \n%s", log);
- }
- virResetLastError();
- ret = 0;
+ if (flags & FLAG_EXPECT_FAILURE) {
+ if ((log = virTestLogContentAndReset()))
+ VIR_TEST_DEBUG("Got expected error: \n%s", log);
}
+ virResetLastError();
+ ret = 0;
cleanup:
VIR_FREE(log);
#include "virprocess.h"
#include "virstring.h"
-#ifdef TEST_OOM
-# ifdef TEST_OOM_TRACE
-# include <dlfcn.h>
-# include <execinfo.h>
-# endif
-#endif
-
#define VIR_FROM_THIS VIR_FROM_NONE
VIR_LOG_INIT("tests.testutils");
static unsigned int testExpensive = -1;
static unsigned int testRegenerate = -1;
-#ifdef TEST_OOM
-static unsigned int testOOM;
-static unsigned int testOOMStart = -1;
-static unsigned int testOOMEnd = -1;
-static unsigned int testOOMTrace;
-# ifdef TEST_OOM_TRACE
-void *testAllocStack[30];
-int ntestAllocStack;
-# endif
-#endif
-static bool testOOMActive;
static size_t testCounter;
static virBitmapPtr testBitmap;
char *progname;
static char *perl;
-bool virTestOOMActive(void)
-{
- return testOOMActive;
-}
-
static int virTestUseTerminalColors(void)
{
return isatty(STDOUT_FILENO);
return flag;
}
-#ifdef TEST_OOM_TRACE
-static void virTestAllocHook(int nalloc ATTRIBUTE_UNUSED,
- void *opaque ATTRIBUTE_UNUSED)
-{
- ntestAllocStack = backtrace(testAllocStack, ARRAY_CARDINALITY(testAllocStack));
-}
-#endif
-
-#ifdef TEST_OOM_TRACE
-static void
-virTestShowTrace(void)
-{
- size_t j;
- for (j = 2; j < ntestAllocStack; j++) {
- Dl_info info;
- char *cmd;
-
- dladdr(testAllocStack[j], &info);
- if (info.dli_fname &&
- strstr(info.dli_fname, ".so")) {
- if (virAsprintf(&cmd, ADDR2LINE " -f -e %s %p",
- info.dli_fname,
- ((void*)((unsigned long long)testAllocStack[j]
- - (unsigned long long)info.dli_fbase))) < 0)
- continue;
- } else {
- if (virAsprintf(&cmd, ADDR2LINE " -f -e %s %p",
- (char*)(info.dli_fname ? info.dli_fname : "<unknown>"),
- testAllocStack[j]) < 0)
- continue;
- }
- ignore_value(system(cmd));
- VIR_FREE(cmd);
- }
-}
-#endif
/*
* Runs test
fprintf(stderr, "!");
}
-#ifdef TEST_OOM
- if (testOOM && ret != EXIT_AM_SKIP) {
- int nalloc;
- int oomret;
- int start, end;
- size_t i;
- virResetLastError();
- virAllocTestInit();
-# ifdef TEST_OOM_TRACE
- virAllocTestHook(virTestAllocHook, NULL);
-# endif
- oomret = body(data);
- nalloc = virAllocTestCount();
- fprintf(stderr, " Test OOM for nalloc=%d ", nalloc);
- if (testOOMStart == -1 ||
- testOOMEnd == -1) {
- start = 0;
- end = nalloc;
- } else {
- start = testOOMStart;
- end = testOOMEnd + 1;
- }
- testOOMActive = true;
- for (i = start; i < end; i++) {
- bool missingFail = false;
-# ifdef TEST_OOM_TRACE
- memset(testAllocStack, 0, sizeof(testAllocStack));
- ntestAllocStack = 0;
-# endif
- virAllocTestOOM(i + 1, 1);
- oomret = body(data);
-
- /* fprintf() disabled because XML parsing APIs don't allow
- * distinguish between element / attribute not present
- * in the XML (which is non-fatal), vs OOM / malformed
- * which should be fatal. Thus error reporting for
- * optionally present XML is mostly broken.
- */
- if (oomret == 0) {
- missingFail = true;
-# if 0
- fprintf(stderr, " alloc %zu failed but no err status\n", i + 1);
-# endif
- } else {
- if (virGetLastErrorCode() == VIR_ERR_OK) {
-# if 0
- fprintf(stderr, " alloc %zu failed but no error report\n", i + 1);
-# endif
- missingFail = true;
- }
- }
- if ((missingFail && testOOMTrace) || (testOOMTrace > 1)) {
- fprintf(stderr, "%s", "!");
-# ifdef TEST_OOM_TRACE
- virTestShowTrace();
-# endif
- ret = -1;
- } else {
- fprintf(stderr, "%s", ".");
- }
- }
- testOOMActive = false;
- if (ret == 0)
- fprintf(stderr, " OK\n");
- else
- fprintf(stderr, " FAILED\n");
- virAllocTestInit();
- }
-#endif /* TEST_OOM */
-
unsetenv("VIR_TEST_MOCK_TESTNAME");
return ret;
}
{
struct virtTestLogData *log = data;
virCheckFlags(VIR_LOG_STACK_TRACE,);
- if (!testOOMActive)
- virBufferAsprintf(&log->buf, "%s: %s", timestamp, str);
+ virBufferAsprintf(&log->buf, "%s: %s", timestamp, str);
}
static void
va_list ap;
int ret;
char *testRange = NULL;
-#ifdef TEST_OOM
- char *oomstr;
-#endif
size_t noutputs = 0;
virLogOutputPtr output = NULL;
virLogOutputPtr *outputs = NULL;
}
}
-#ifdef TEST_OOM
- if ((oomstr = getenv("VIR_TEST_OOM")) != NULL) {
- char *next;
- if (testDebug == -1)
- testDebug = 1;
- testOOM = 1;
- if (oomstr[0] != '\0' &&
- oomstr[1] == ':') {
- if (virStrToLong_ui(oomstr + 2, &next, 10, &testOOMStart) < 0) {
- fprintf(stderr, "Cannot parse range %s\n", oomstr);
- return EXIT_FAILURE;
- }
- if (*next == '\0') {
- testOOMEnd = testOOMStart;
- } else {
- if (*next != '-') {
- fprintf(stderr, "Cannot parse range %s\n", oomstr);
- return EXIT_FAILURE;
- }
- if (virStrToLong_ui(next+1, NULL, 10, &testOOMEnd) < 0) {
- fprintf(stderr, "Cannot parse range %s\n", oomstr);
- return EXIT_FAILURE;
- }
- }
- } else {
- testOOMStart = -1;
- testOOMEnd = -1;
- }
- }
-
-# ifdef TEST_OOM_TRACE
- if ((oomstr = getenv("VIR_TEST_OOM_TRACE")) != NULL) {
- if (virStrToLong_ui(oomstr, NULL, 10, &testOOMTrace) < 0) {
- fprintf(stderr, "Cannot parse oom trace %s\n", oomstr);
- return EXIT_FAILURE;
- }
- }
-# else
- if (getenv("VIR_TEST_OOM_TRACE")) {
- fprintf(stderr, "%s", "OOM test tracing not enabled in this build\n");
- return EXIT_FAILURE;
- }
-# endif
-#else /* TEST_OOM */
- if (getenv("VIR_TEST_OOM")) {
- fprintf(stderr, "%s", "OOM testing not enabled in this build\n");
- return EXIT_FAILURE;
- }
- if (getenv("VIR_TEST_OOM_TRACE")) {
- fprintf(stderr, "%s", "OOM test tracing not enabled in this build\n");
- return EXIT_FAILURE;
- }
-#endif /* TEST_OOM */
-
/* Find perl early because some tests override PATH */
perl = virFindFileInPath("perl");
# error Fix Makefile.am
#endif
-bool virTestOOMActive(void);
-
int virTestRun(const char *title,
int (*body)(const void *data),
const void *data);
goto cleanup;
}
- if (virTestOOMActive())
- goto cleanup;
-
if (virBufferError(&cmdbuf))
goto cleanup;
goto cleanup;
}
- if (virTestOOMActive())
- goto cleanup;
-
if (virBufferError(&cmdbuf))
goto cleanup;
goto cleanup;
}
- if (virTestOOMActive())
- goto cleanup;
-
if (virBufferError(&cmdbuf))
goto cleanup;
goto cleanup;
}
- if (virTestOOMActive())
- goto cleanup;
-
if (virBufferError(&cmdbuf))
goto cleanup;