| // Copyright (c) 2007, 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: Fred Akalin |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <vector> |
| #include "gperftools/malloc_extension.h" |
| #include "base/logging.h" |
| |
| 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) |
| |
| // This flag won't be compiled in in opt mode. |
| DECLARE_int32(max_free_queue_size); |
| |
| // 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. |
| #ifndef __APPLE__ |
| TEST(DebugAllocationTest, DeallocMismatch) { |
| // malloc can be matched only by free |
| // new can be matched only by delete and delete(nothrow) |
| // new[] can be matched only by delete[] and delete[](nothrow) |
| // new(nothrow) can be matched only by delete and delete(nothrow) |
| // new(nothrow)[] can be matched only by delete[] and delete[](nothrow) |
| |
| // Allocate with malloc. |
| { |
| int* x = static_cast<int*>(malloc(sizeof(*x))); |
| IF_DEBUG_EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete"); |
| IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]"); |
| // Should work fine. |
| free(x); |
| } |
| |
| // Allocate with new. |
| { |
| int* x = new int; |
| int* y = new int; |
| IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free"); |
| IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]"); |
| delete x; |
| ::operator delete(y, std::nothrow); |
| } |
| |
| // Allocate with new[]. |
| { |
| int* x = new int[1]; |
| int* y = new int[1]; |
| IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free"); |
| IF_DEBUG_EXPECT_DEATH(delete x, "mismatch.*being dealloc.*delete"); |
| delete [] x; |
| ::operator delete[](y, std::nothrow); |
| } |
| |
| // Allocate with new(nothrow). |
| { |
| int* x = new(std::nothrow) int; |
| int* y = new(std::nothrow) int; |
| IF_DEBUG_EXPECT_DEATH(free(x), "mismatch.*being dealloc.*free"); |
| IF_DEBUG_EXPECT_DEATH(delete [] x, "mismatch.*being dealloc.*delete *[[]"); |
| delete x; |
| ::operator delete(y, std::nothrow); |
| } |
| |
| // Allocate with new(nothrow)[]. |
| { |
| int* x = new(std::nothrow) int[1]; |
| int* y = 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"); |
| delete [] x; |
| ::operator delete[](y, std::nothrow); |
| } |
| } |
| #endif // #ifdef OS_MACOSX |
| |
| TEST(DebugAllocationTest, DoubleFree) { |
| int* pint = new int; |
| delete pint; |
| IF_DEBUG_EXPECT_DEATH(delete pint, "has been already deallocated"); |
| } |
| |
| TEST(DebugAllocationTest, StompBefore) { |
| int* pint = new int; |
| #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 |
| } |
| |
| TEST(DebugAllocationTest, StompAfter) { |
| int* pint = new int; |
| #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 |
| } |
| |
| TEST(DebugAllocationTest, FreeQueueTest) { |
| // Verify that the allocator doesn't return blocks that were recently freed. |
| int* x = new int; |
| int* old_x = x; |
| delete x; |
| x = new int; |
| #if 1 |
| // This check should not be read as a universal guarantee of behavior. If |
| // other threads are executing, it would be theoretically possible for this |
| // check to fail despite the efforts of debugallocation.cc to the contrary. |
| // It should always hold under the controlled conditions of this unittest, |
| // however. |
| EXPECT_NE(x, old_x); // Allocator shouldn't return recently freed blocks |
| #else |
| // The below check passes, but since it isn't *required* to pass, I've left |
| // it commented out. |
| // EXPECT_EQ(x, old_x); |
| #endif |
| old_x = NULL; // avoid breaking opt build with an unused variable warning. |
| delete x; |
| } |
| |
| TEST(DebugAllocationTest, DanglingPointerWriteTest) { |
| // This test can only be run if debugging. |
| // |
| // If not debugging, the 'new' following the dangling write might not be |
| // safe. When debugging, we expect the (trashed) deleted block to be on the |
| // list of recently-freed blocks, so the following 'new' will be safe. |
| #if 1 |
| int* x = new int; |
| delete x; |
| int poisoned_x_value = *x; |
| *x = 1; // a dangling write. |
| |
| char* s = new char[FLAGS_max_free_queue_size]; |
| // 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."); |
| |
| // restore the poisoned value of x so that we can delete s without causing a |
| // crash. |
| *x = poisoned_x_value; |
| delete [] s; |
| #endif |
| } |
| |
| TEST(DebugAllocationTest, DanglingWriteAtExitTest) { |
| int *x = new int; |
| delete x; |
| int old_x_value = *x; |
| *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."); |
| *x = old_x_value; // restore x so that the test can exit successfully. |
| } |
| |
| TEST(DebugAllocationTest, StackTraceWithDanglingWriteAtExitTest) { |
| int *x = new int; |
| delete x; |
| int old_x_value = *x; |
| *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"); |
| *x = old_x_value; // restore x so that the test can exit successfully. |
| } |
| |
| static size_t CurrentlyAllocatedBytes() { |
| size_t value; |
| CHECK(MallocExtension::instance()->GetNumericProperty( |
| "generic.current_allocated_bytes", &value)); |
| return value; |
| } |
| |
| TEST(DebugAllocationTest, CurrentlyAllocated) { |
| // Clear the free queue |
| #if 1 |
| FLAGS_max_free_queue_size = 0; |
| // Force a round-trip through the queue management code so that the |
| // new size is seen and the queue of recently-freed blocks is flushed. |
| free(malloc(1)); |
| FLAGS_max_free_queue_size = 1048576; |
| #endif |
| |
| // Free something and check that it disappears from allocated bytes |
| // immediately. |
| char* p = new char[1000]; |
| size_t after_malloc = CurrentlyAllocatedBytes(); |
| delete[] p; |
| size_t after_free = CurrentlyAllocatedBytes(); |
| EXPECT_LE(after_free, after_malloc - 1000); |
| } |
| |
| TEST(DebugAllocationTest, GetAllocatedSizeTest) { |
| #if 1 |
| // When debug_allocation is in effect, GetAllocatedSize should return |
| // exactly requested size, since debug_allocation doesn't allow users |
| // to write more than that. |
| for (int i = 0; i < 10; ++i) { |
| void *p = malloc(i); |
| EXPECT_EQ(i, MallocExtension::instance()->GetAllocatedSize(p)); |
| free(p); |
| } |
| #endif |
| void* a = malloc(1000); |
| EXPECT_GE(MallocExtension::instance()->GetAllocatedSize(a), 1000); |
| // This is just a sanity check. If we allocated too much, alloc is broken |
| EXPECT_LE(MallocExtension::instance()->GetAllocatedSize(a), 5000); |
| EXPECT_GE(MallocExtension::instance()->GetEstimatedAllocatedSize(1000), 1000); |
| free(a); |
| } |
| |
| TEST(DebugAllocationTest, HugeAlloc) { |
| // This must not be a const variable so it doesn't form an |
| // 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; |
| |
| #ifndef NDEBUG |
| |
| a = malloc(kTooBig); |
| EXPECT_EQ(NULL, 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 |
| // a non-const variable. See kTooBig for more details. |
| size_t kAlsoTooBig = kTooBig - 1024; |
| |
| a = malloc(kAlsoTooBig); |
| EXPECT_EQ(NULL, a); |
| #endif |
| } |
| |
| 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(); |
| } |