| /*************************************************************************** |
| * |
| * 0.new.cpp - test exercising replacement operator new and delete |
| * |
| * $Id$ |
| * |
| *************************************************************************** |
| * |
| * 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 <new> // for bad_alloc |
| |
| #include <cstddef> // for size_t |
| |
| #include <setjmp.h> // for longjmp(), setjmp() |
| #include <signal.h> // for SIGABRT, signal() |
| |
| #include <rw_new.h> |
| #include <rw_driver.h> |
| |
| /**************************************************************************/ |
| |
| // 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); |
| } |