/*************************************************************************** * * 0.new.cpp - test exercising replacement operator new and delete * * $Id: 0.new.cpp 641117 2008-03-26 02:32:31Z sebor $ * *************************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * * Copyright 1994-2008 Rogue Wave Software, Inc. * **************************************************************************/ #include // for bad_alloc #include // for size_t #include // for longjmp(), setjmp() #include // for SIGABRT, signal() #include #include /**************************************************************************/ // program exit status int exit_status /* = 0 */; extern "C" { volatile int line; // currently executed line volatile int fail; // non-zero when line failed jmp_buf env; static void handle_ABRT (int) { fail = 0; longjmp (env, 1); } } #define FAIL(code) \ fail = line = __LINE__; \ signal (SIGABRT, handle_ABRT); \ if (0 == setjmp (env)) { \ code; \ exit_status = 1; \ rw_assert (0, __FILE__, line, "expected assertion"); \ } \ else \ (void)0 #define PASS(code) \ fail = -1; line = __LINE__; \ signal (SIGABRT, handle_ABRT); \ if (0 == setjmp (env)) { \ code; \ } \ else if (fail != line) { \ exit_status = 1; \ rw_assert (0, __FILE__, line, "unexpected assertion"); \ } (void)0 /**************************************************************************/ void test_new_delete () { rw_info (0, __FILE__, __LINE__, "exercising successful allocation and deallocation"); { void *p = 0; PASS (p = operator new (0)); rw_assert (p != 0, __FILE__, __LINE__, "operator new(0) != 0"); operator delete (p); } { void *p = 0; PASS (p = operator new (1)); rw_assert (p != 0, __FILE__, __LINE__, "operator new(1) != 0"); operator delete (p); } { void *p = 0; PASS (p = operator new (2)); rw_assert (p != 0, __FILE__, __LINE__, "operator new(2) != 0"); operator delete (p); } { void *p = 0; PASS (p = operator new (1024)); rw_assert (p != 0, __FILE__, __LINE__, "operator new(1024) != 0"); operator delete (p); } } /**************************************************************************/ #define CATCH(code) \ try { code; fail = 1; } \ catch (_RWSTD_BAD_ALLOC) { fail = 0; } \ catch (...) { fail = 2; } \ rw_assert (!fail, __FILE__, __LINE__, \ "%s", 1 == fail ? "failed to throw" : \ "threw an unknown exception") #define NOTHROW(code) \ try { fail = 0; code; } \ catch (...) { fail = 1; } \ rw_assert (!fail, __FILE__, __LINE__, \ "unexpected exception") void test_bad_alloc () { rw_info (0, __FILE__, __LINE__, "exercising the ability of ordinary " "operator new to throw std::bad_alloc"); rwt_free_store* const pst = rwt_get_free_store (0); *pst->throw_at_blocks_ [0] = pst->blocks_ [0]; CATCH (operator new (0)); CATCH (operator new (1)); CATCH (operator new (2)); CATCH (operator new (1024)); { void *p = 0; NOTHROW (p = operator new[](0)); operator delete[](p); } { void *p = 0; NOTHROW (p = operator new[](1)); operator delete[](p); } { void *p = 0; NOTHROW (p = operator new[](2)); operator delete[](p); } { void *p = 0; NOTHROW (p = operator new[](1024)); operator delete[](p); } rw_info (0, __FILE__, __LINE__, "exercising the ability of the array form " "of operator new to throw std::bad_alloc"); *pst->throw_at_blocks_ [0] = std::size_t (-1); *pst->throw_at_blocks_ [1] = pst->blocks_ [1]; CATCH (operator new[](0)); CATCH (operator new[](1)); CATCH (operator new[](2)); CATCH (operator new[](1024)); { void *p = 0; NOTHROW (p = operator new (0)); rw_assert (p != 0, __FILE__, __LINE__, "operator new[](0) != 0"); operator delete (p); } { void *p = 0; NOTHROW (p = operator new (1)); rw_assert (p != 0, __FILE__, __LINE__, "operator new[](1) != 0"); operator delete (p); } { void *p = 0; NOTHROW (p = operator new (32)); rw_assert (p != 0, __FILE__, __LINE__, "operator new[](32) != 0"); operator delete (p); } { void *p = 0; NOTHROW (p = operator new (4096)); rw_assert (p != 0, __FILE__, __LINE__, "operator new[](4096) != 0"); operator delete (p); } *pst->throw_at_blocks_ [1] = std::size_t (-1); } /**************************************************************************/ void test_mismatch () { rw_info (0, __FILE__, __LINE__, "exercising the ability to detect " "allocation/deallocation mismatches"); { // detect allocations by operator new() deallocated // using (the array form of) operator delete[] void *p = 0; PASS (p = operator new (0)); FAIL (operator delete[](p)); PASS (operator delete (p)); } { void *p = 0; PASS (p = operator new (1)); FAIL (operator delete[](p)); PASS (operator delete (p)); } { void *p = 0; PASS (p = operator new[](33)); FAIL (operator delete (p)); PASS (operator delete[] (p)); } } /**************************************************************************/ void test_bad_delete () { rw_info (0, __FILE__, __LINE__, "exercising the ability to detect " "deletion of unallocated storage"); { char *p = 0; PASS (p = new char); FAIL (delete (p - 1)); FAIL (delete (p + 1)); PASS (delete (p)); } { char *p = 0; PASS (p = new char [4]); FAIL (delete (p - 1)); FAIL (delete (p + 1)); FAIL (delete (p + 2)); FAIL (delete (p + 3)); FAIL (delete (p + 4)); FAIL (delete[] (p - 1)); FAIL (delete[] (p + 1)); FAIL (delete[] (p + 2)); FAIL (delete[] (p + 3)); FAIL (delete[] (p + 4)); PASS (delete[] p); } } /**************************************************************************/ void test_double_delete () { rw_info (0, __FILE__, __LINE__, "exercising the ability to detect double deletion"); { char *p = 0; PASS (p = new char); PASS (delete (p)); FAIL (delete (p)); } { char *p = 0; PASS (p = new char [32]); PASS (delete[] p); FAIL (delete[] p); FAIL (delete p); } } /**************************************************************************/ void test_corruption () { rw_info (0, __FILE__, __LINE__, "exercising the ability to detect memory corruption"); // corrupt (and restore) memory past the end of the allocated block for (std::size_t i = 1; i != 8; ++i) { char *p = 0; PASS (p = new char); // save the value of the byte past the end of the block // and temporarily overwrite it with another value const char save = p [i]; p [i] = ~p [i]; // expect operator delete to diagnose the corruption // and call abort() without actually freeing the block FAIL (delete p); // restore the corrupted byte to its original value p [i] = save; // expect operator delete not to complain PASS (delete p); } } /**************************************************************************/ #define LEAK(code, bytes, blks) \ do { \ /* establish a checkpoint for memory leaks */ \ rwt_check_leaks (0, 0); \ \ code; \ \ /* find memory leaks since the last checkpoint */ \ std::size_t nbytes; \ const std::size_t nblocks = rwt_check_leaks (&nbytes, 0); \ \ rw_assert (blks == nblocks && bytes == nbytes, \ __FILE__, __LINE__, \ "failed to detect a leak of %d bytes in " \ "%d blocks: got %zu bytes in %zu blocks", \ bytes, blks, nbytes, nblocks); \ } while (0) void test_leaks () { rw_info (0, __FILE__, __LINE__, "exercising the ability to detect memory leaks"); { void *p = 0; LEAK (p = operator new (0), 0, 1); PASS (operator delete (p)); } { void *p = 0; LEAK (p = operator new (1), 1, 1); PASS (operator delete (p)); } { void *p = 0; LEAK (p = operator new (1234), 1234, 1); PASS (operator delete (p)); } { void *p0 = 0; void *p1 = 0; LEAK (p0 = operator new (32); p1 = operator new (64), 96, 2); PASS (operator delete (p0)); PASS (operator delete (p1)); } { void *p = 0; LEAK (p = operator new[] (12345), 12345, 1); PASS (operator delete[] (p)); } } /**************************************************************************/ void test_stress () { rw_info (0, __FILE__, __LINE__, "stress-testing replacement operators new and delete"); rwt_free_store* const pst = rwt_get_free_store (0); std::size_t nblocks = pst->blocks_ [0] + pst->blocks_ [1]; std::size_t nbytes = pst->bytes_ [0] + pst->bytes_ [1]; void* ptrs [1000]; const std::size_t N = sizeof ptrs / sizeof *ptrs; for (std::size_t i = 0; i != N; ++i) { if (i % 2) { PASS (ptrs [i] = operator new[](i)); } else { PASS (ptrs [i] = operator new (i)); } } for (std::size_t i = 0; i < N; ++i) { const std::size_t j = (i * (i + 17)) % N; if (j % 2) { PASS (operator delete[](ptrs [j])); } else { PASS (operator delete (ptrs [j])); } ptrs [j] = 0; } for (std::size_t i = 0; i < N; ++i) { if (i % 2) { PASS (operator delete[](ptrs [i])); } else { PASS (operator delete (ptrs [i])); } } nblocks = pst->blocks_ [0] + pst->blocks_ [1] - nblocks; nbytes = pst->bytes_ [0] + pst->bytes_ [1] - nbytes; rw_assert (0 == nblocks && 0 == nbytes, __FILE__, __LINE__, "false leaks detected: %zu bytes in %zu blocks", nbytes, nblocks); } /**************************************************************************/ static int rw_opt_no_new_delete; // for --no-new-delete static int rw_opt_no_bad_alloc; // for --no-bad_alloc static int rw_opt_no_mismatch; // for --no-mismatch static int rw_opt_no_bad_delete; // for --no-bad-delete static int rw_opt_no_double_delete; // for --no-double-delete static int rw_opt_no_corruption; // for --no-corruption static int rw_opt_no_leaks; // for --no-leaks static int rw_opt_no_stress; // for --no-stress int run_test (int, char**) { #ifndef _RWSTD_NO_REPLACEABLE_NEW_DELETE // disable diagnostics issued by the replacement operator new // and delete defined by the test driver in response to deliberate // errors caused by this test rw_enable (rw_error, false); # define TEST(name) \ if (rw_opt_no_ ## name) \ rw_note (0, 0, __LINE__, "%s test disabled", #name); \ else \ test_ ## name () TEST (bad_alloc); TEST (mismatch); TEST (double_delete); TEST (bad_delete); TEST (corruption); TEST (leaks); TEST (stress); #else // _RWSTD_NO_REPLACEABLE_NEW_DELETE rw_note (0, 0, __LINE__, "Test disabled"); #endif // _RWSTD_NO_REPLACEABLE_NEW_DELETE return 0; } /**************************************************************************/ int main (int argc, char** argv) { return rw_test (argc, argv, __FILE__, 0 /* no clause */, 0 /* no comment */, run_test, "|-no-new-delete# " "|-no-bad_alloc# " "|-no-mismatch# " "|-no-bad-delete# " "|-no-double-delete# " "|-no-corruption# " "|-no-leaks# " "|-no-stress-test#", &rw_opt_no_new_delete, &rw_opt_no_bad_alloc, &rw_opt_no_mismatch, &rw_opt_no_bad_delete, &rw_opt_no_double_delete, &rw_opt_no_corruption, &rw_opt_no_leaks, &rw_opt_no_stress); }