gtestify debugallocation_test
As part of that we eliminate use of shell script and rely on gtest's death testing facility instead (which this test was originally using).
This commit is contained in:
parent
14598dd4ac
commit
8cb41da4f9
|
@ -723,10 +723,8 @@ if(GPERFTOOLS_BUILD_DEBUGALLOC)
|
|||
|
||||
if(WITH_STACK_TRACE)
|
||||
add_executable(debugallocation_test src/tests/debugallocation_test.cc)
|
||||
target_link_libraries(debugallocation_test tcmalloc_minimal_debug)
|
||||
|
||||
add_test(NAME debugallocation_test
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/tests/debugallocation_test.sh)
|
||||
target_link_libraries(debugallocation_test tcmalloc_debug gtest)
|
||||
add_test(debugallocation_test debugallocation_test)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
@ -803,6 +801,7 @@ if(GPERFTOOLS_BUILD_HEAP_CHECKER OR GPERFTOOLS_BUILD_HEAP_PROFILER)
|
|||
# sampler_test and sampling_test both require sampling to be turned
|
||||
# on, which it's not by default. Use the "standard" value of 2^19.
|
||||
list(APPEND TESTS_ENVIRONMENT TCMALLOC_SAMPLE_PARAMETER=524288)
|
||||
list(APPEND TESTS_ENVIRONMENT PPROF_PATH=${CMAKE_CURRENT_SOURCE_DIR}/src/pprof)
|
||||
|
||||
add_executable(sampler_test src/tests/sampler_test.cc src/sampler.cc)
|
||||
target_link_libraries(sampler_test gtest)
|
||||
|
|
13
Makefile.am
13
Makefile.am
|
@ -555,19 +555,12 @@ realloc_debug_unittest_LDADD = libtcmalloc_minimal_debug.la libgtest.la
|
|||
# debugallocation_test checks that we print a proper stacktrace when
|
||||
# debug-allocs fail, so we can't run it if we don't have stacktrace info.
|
||||
if WITH_STACK_TRACE
|
||||
TESTS += debugallocation_test.sh$(EXEEXT)
|
||||
debugallocation_test_sh_SOURCES = src/tests/debugallocation_test.sh
|
||||
noinst_SCRIPTS += $(debugallocation_test_sh_SOURCES)
|
||||
debugallocation_test.sh$(EXEEXT): $(top_srcdir)/$(debugallocation_test_sh_SOURCES) \
|
||||
debugallocation_test
|
||||
rm -f $@
|
||||
cp -p $(top_srcdir)/$(debugallocation_test_sh_SOURCES) $@
|
||||
|
||||
# This is the sub-program used by debugallocation_test.sh
|
||||
noinst_PROGRAMS += debugallocation_test
|
||||
TESTS += debugallocation_test
|
||||
debugallocation_test_SOURCES = src/tests/debugallocation_test.cc
|
||||
debugallocation_test_LDFLAGS = $(TCMALLOC_FLAGS) $(AM_LDFLAGS)
|
||||
debugallocation_test_LDADD = libtcmalloc_debug.la
|
||||
debugallocation_test_CPPFLAGS = $(gtest_CPPFLAGS)
|
||||
debugallocation_test_LDADD = libtcmalloc_debug.la libgtest.la
|
||||
endif WITH_STACK_TRACE
|
||||
|
||||
endif WITH_DEBUGALLOC
|
||||
|
|
|
@ -36,54 +36,18 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // for memcmp
|
||||
#include <vector>
|
||||
|
||||
#include "gperftools/malloc_extension.h"
|
||||
#include "gperftools/tcmalloc.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "testing_portal.h"
|
||||
#include "tests/testutil.h"
|
||||
|
||||
#include "tests/legacy_assertions.h"
|
||||
|
||||
#include "testing_portal.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using tcmalloc::TestingPortal;
|
||||
|
||||
using std::vector;
|
||||
|
||||
vector<void (*)()> g_testlist; // the tests to run
|
||||
|
||||
#define TEST(a, b) \
|
||||
struct Test_##a##_##b { \
|
||||
Test_##a##_##b() { g_testlist.push_back(&Run); } \
|
||||
static void Run(); \
|
||||
}; \
|
||||
static Test_##a##_##b g_test_##a##_##b; \
|
||||
void Test_##a##_##b::Run()
|
||||
|
||||
|
||||
static int RUN_ALL_TESTS() {
|
||||
vector<void (*)()>::const_iterator it;
|
||||
for (it = g_testlist.begin(); it != g_testlist.end(); ++it) {
|
||||
(*it)(); // The test will error-exit if there's a problem.
|
||||
}
|
||||
fprintf(stderr, "\nPassed %d tests\n\nPASS\n",
|
||||
static_cast<int>(g_testlist.size()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The death tests are meant to be run from a shell-script driver, which
|
||||
// passes in an integer saying which death test to run. We store that
|
||||
// test-to-run here, and in the macro use a counter to see when we get
|
||||
// to that test, so we can run it.
|
||||
static int test_to_run = 0; // set in main() based on argv
|
||||
static int test_counter = 0; // incremented every time the macro is called
|
||||
#define IF_DEBUG_EXPECT_DEATH(statement, regex) do { \
|
||||
if (test_counter++ == test_to_run) { \
|
||||
fprintf(stderr, "Expected regex:%s\n", regex); \
|
||||
statement; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
// Test match as well as mismatch rules. But do not test on OS X; on
|
||||
// OS X the OS converts new/new[] to malloc before it gets to us, so
|
||||
// we are unable to catch these mismatch errors.
|
||||
|
@ -98,8 +62,8 @@ TEST(DebugAllocationTest, DeallocMismatch) {
|
|||
// Allocate with malloc.
|
||||
{
|
||||
int* x = static_cast<int*>(noopt(malloc(sizeof(*x))));
|
||||
IF_DEBUG_EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
|
||||
IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
|
||||
EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
|
||||
EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
|
||||
// Should work fine.
|
||||
free(x);
|
||||
}
|
||||
|
@ -108,8 +72,8 @@ TEST(DebugAllocationTest, DeallocMismatch) {
|
|||
{
|
||||
int* x = noopt(new int);
|
||||
int* y = noopt(new int);
|
||||
IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
|
||||
IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
|
||||
EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
|
||||
EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
|
||||
delete x;
|
||||
::operator delete(y, std::nothrow);
|
||||
}
|
||||
|
@ -118,8 +82,8 @@ TEST(DebugAllocationTest, DeallocMismatch) {
|
|||
{
|
||||
int* x = noopt(new int[1]);
|
||||
int* y = noopt(new int[1]);
|
||||
IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
|
||||
IF_DEBUG_EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
|
||||
EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
|
||||
EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
|
||||
delete [] x;
|
||||
::operator delete[](y, std::nothrow);
|
||||
}
|
||||
|
@ -128,8 +92,8 @@ TEST(DebugAllocationTest, DeallocMismatch) {
|
|||
{
|
||||
int* x = noopt(new (std::nothrow) int);
|
||||
int* y = noopt(new (std::nothrow) int);
|
||||
IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
|
||||
IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
|
||||
EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
|
||||
EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]");
|
||||
delete x;
|
||||
::operator delete(y, std::nothrow);
|
||||
}
|
||||
|
@ -138,8 +102,8 @@ TEST(DebugAllocationTest, DeallocMismatch) {
|
|||
{
|
||||
int* x = noopt(new (std::nothrow) int[1]);
|
||||
int* y = noopt(new (std::nothrow) int[1]);
|
||||
IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
|
||||
IF_DEBUG_EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
|
||||
EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free");
|
||||
EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete");
|
||||
delete [] x;
|
||||
::operator delete[](y, std::nothrow);
|
||||
}
|
||||
|
@ -149,25 +113,21 @@ TEST(DebugAllocationTest, DeallocMismatch) {
|
|||
TEST(DebugAllocationTest, DoubleFree) {
|
||||
int* pint = noopt(new int);
|
||||
delete pint;
|
||||
IF_DEBUG_EXPECT_DEATH(delete pint, "has been already deallocated");
|
||||
EXPECT_DEATH(delete pint, "has been already deallocated");
|
||||
}
|
||||
|
||||
TEST(DebugAllocationTest, StompBefore) {
|
||||
int* pint = noopt(new int);
|
||||
(void)pint;
|
||||
#ifndef NDEBUG // don't stomp memory if we're not in a position to detect it
|
||||
pint[-1] = 5;
|
||||
IF_DEBUG_EXPECT_DEATH(delete pint, "a word before object");
|
||||
#endif
|
||||
EXPECT_DEATH(delete pint, "a word before object");
|
||||
}
|
||||
|
||||
TEST(DebugAllocationTest, StompAfter) {
|
||||
int* pint = noopt(new int);
|
||||
(void)pint;
|
||||
#ifndef NDEBUG // don't stomp memory if we're not in a position to detect it
|
||||
pint[1] = 5;
|
||||
IF_DEBUG_EXPECT_DEATH(delete pint, "a word after object");
|
||||
#endif
|
||||
EXPECT_DEATH(delete pint, "a word after object");
|
||||
}
|
||||
|
||||
TEST(DebugAllocationTest, FreeQueueTest) {
|
||||
|
@ -188,7 +148,7 @@ TEST(DebugAllocationTest, FreeQueueTest) {
|
|||
// it commented out.
|
||||
// EXPECT_EQ(x, old_x);
|
||||
#endif
|
||||
old_x = NULL; // avoid breaking opt build with an unused variable warning.
|
||||
old_x = nullptr; // avoid breaking opt build with an unused variable warning.
|
||||
delete x;
|
||||
}
|
||||
|
||||
|
@ -208,7 +168,7 @@ TEST(DebugAllocationTest, DanglingPointerWriteTest) {
|
|||
// When we delete s, we push the storage that was previously allocated to x
|
||||
// off the end of the free queue. At that point, the write to that memory
|
||||
// will be detected.
|
||||
IF_DEBUG_EXPECT_DEATH(delete [] s, "Memory was written to after being freed.");
|
||||
EXPECT_DEATH(delete [] s, "Memory was written to after being freed.");
|
||||
|
||||
// restore the poisoned value of x so that we can delete s without causing a
|
||||
// crash.
|
||||
|
@ -224,7 +184,7 @@ TEST(DebugAllocationTest, DanglingWriteAtExitTest) {
|
|||
*x = 1;
|
||||
// verify that dangling writes are caught at program termination if the
|
||||
// corrupted block never got pushed off of the end of the free queue.
|
||||
IF_DEBUG_EXPECT_DEATH(exit(0), "Memory was written to after being freed.");
|
||||
EXPECT_DEATH(exit(0), "Memory was written to after being freed.");
|
||||
*x = old_x_value; // restore x so that the test can exit successfully.
|
||||
}
|
||||
|
||||
|
@ -235,7 +195,7 @@ TEST(DebugAllocationTest, StackTraceWithDanglingWriteAtExitTest) {
|
|||
*x = 1;
|
||||
// verify that we also get a stack trace when we have a dangling write.
|
||||
// The " @ " is part of the stack trace output.
|
||||
IF_DEBUG_EXPECT_DEATH(exit(0), " @ .*main");
|
||||
EXPECT_DEATH(exit(0), " @ .*main");
|
||||
*x = old_x_value; // restore x so that the test can exit successfully.
|
||||
}
|
||||
|
||||
|
@ -306,14 +266,13 @@ TEST(DebugAllocationTest, HugeAlloc) {
|
|||
// integral-constant-expression which can be *statically* rejected by the
|
||||
// compiler as too large for the allocation.
|
||||
size_t kTooBig = ~static_cast<size_t>(0);
|
||||
void* a = NULL;
|
||||
void* a = nullptr;
|
||||
|
||||
(void)kTooBig;
|
||||
(void)a;
|
||||
#ifndef NDEBUG
|
||||
|
||||
a = noopt(malloc(noopt(kTooBig)));
|
||||
EXPECT_EQ(NULL, a);
|
||||
EXPECT_EQ(nullptr, a);
|
||||
|
||||
// kAlsoTooBig is small enough not to get caught by debugallocation's check,
|
||||
// but will still fall through to tcmalloc's check. This must also be
|
||||
|
@ -321,8 +280,7 @@ TEST(DebugAllocationTest, HugeAlloc) {
|
|||
size_t kAlsoTooBig = kTooBig - 1024;
|
||||
|
||||
a = noopt(malloc(noopt(kAlsoTooBig)));
|
||||
EXPECT_EQ(NULL, a);
|
||||
#endif
|
||||
EXPECT_EQ(nullptr, a);
|
||||
}
|
||||
|
||||
// based on test program contributed by mikesart@gmail.com aka
|
||||
|
@ -331,29 +289,12 @@ TEST(DebugAllocationTest, ReallocAfterMemalign) {
|
|||
char stuff[50];
|
||||
memset(stuff, 0x11, sizeof(stuff));
|
||||
void *p = tc_memalign(16, sizeof(stuff));
|
||||
EXPECT_NE(p, NULL);
|
||||
EXPECT_NE(p, nullptr);
|
||||
memcpy(stuff, p, sizeof(stuff));
|
||||
|
||||
p = noopt(realloc(p, sizeof(stuff) + 10));
|
||||
EXPECT_NE(p, NULL);
|
||||
EXPECT_NE(p, nullptr);
|
||||
|
||||
int rv = memcmp(stuff, p, sizeof(stuff));
|
||||
EXPECT_EQ(rv, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// If you run without args, we run the non-death parts of the test.
|
||||
// Otherwise, argv[1] should be a number saying which death-test
|
||||
// to run. We will output a regexp we expect the death-message
|
||||
// to include, and then run the given death test (which hopefully
|
||||
// will produce that error message). If argv[1] > the number of
|
||||
// death tests, we will run only the non-death parts. One way to
|
||||
// tell when you are done with all tests is when no 'expected
|
||||
// regexp' message is printed for a given argv[1].
|
||||
if (argc < 2) {
|
||||
test_to_run = -1; // will never match
|
||||
} else {
|
||||
test_to_run = atoi(argv[1]);
|
||||
}
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# ---
|
||||
# Author: Craig Silverstein
|
||||
|
||||
BINDIR="${BINDIR:-.}"
|
||||
# We expect PPROF_PATH to be set in the environment.
|
||||
# If not, we set it to some reasonable value
|
||||
export PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}"
|
||||
|
||||
if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then
|
||||
echo "USAGE: $0 [unittest dir]"
|
||||
echo " By default, unittest_dir=$BINDIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEBUGALLOCATION_TEST="${1:-$BINDIR/debugallocation_test}"
|
||||
|
||||
num_failures=0
|
||||
|
||||
# Run the i-th death test and make sure the test has the expected
|
||||
# regexp. We can depend on the first line of the output being
|
||||
# Expected regex:<regex>
|
||||
# Evaluates to "done" if we are not actually a death-test (so $1 is
|
||||
# too big a number, and we can stop). Evaluates to "" otherwise.
|
||||
# Increments num_failures if the death test does not succeed.
|
||||
OneDeathTest() {
|
||||
"$DEBUGALLOCATION_TEST" "$1" 2>&1 | {
|
||||
regex_line='dummy'
|
||||
# Normally the regex_line is the first line of output, but not
|
||||
# always (if tcmalloc itself does any logging to stderr).
|
||||
while test -n "$regex_line"; do
|
||||
read regex_line
|
||||
regex=`expr "$regex_line" : "Expected regex:\(.*\)"`
|
||||
test -n "$regex" && break # found the regex line
|
||||
done
|
||||
test -z "$regex" && echo "done" || grep "$regex" 2>&1
|
||||
}
|
||||
}
|
||||
|
||||
death_test_num=0 # which death test to run
|
||||
while :; do # same as 'while true', but more portable
|
||||
echo -n "Running death test $death_test_num..."
|
||||
output="`OneDeathTest $death_test_num`"
|
||||
case $output in
|
||||
# Empty string means grep didn't find anything.
|
||||
"") echo "FAILED"; num_failures=`expr $num_failures + 1`;;
|
||||
"done"*) echo "done with death tests"; break;;
|
||||
# Any other string means grep found something, like it ought to.
|
||||
*) echo "OK";;
|
||||
esac
|
||||
death_test_num=`expr $death_test_num + 1`
|
||||
done
|
||||
|
||||
# Test the non-death parts of the test too
|
||||
echo -n "Running non-death tests..."
|
||||
if "$DEBUGALLOCATION_TEST"; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
num_failures=`expr $num_failures + 1`
|
||||
fi
|
||||
|
||||
if [ "$num_failures" = 0 ]; then
|
||||
echo "PASS"
|
||||
else
|
||||
echo "Failed with $num_failures failures"
|
||||
fi
|
||||
exit $num_failures
|
Loading…
Reference in New Issue