From f2b51ca5984fc464fbcee652b13c0287752d3c7a Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Thu, 13 Jul 2017 22:28:03 +0300 Subject: Move memory debug files into nvwa directory. --- src/Makefile.am | 24 +- src/debug.h | 2 +- src/debug/_nvwa.h | 112 ---- src/debug/c++11.h | 340 ------------ src/debug/debug_new.cpp | 1197 ---------------------------------------- src/debug/debug_new.h | 207 ------- src/debug/fast_mutex.h | 427 -------------- src/debug/nvwa/_nvwa.h | 112 ++++ src/debug/nvwa/c++11.h | 340 ++++++++++++ src/debug/nvwa/debug_new.cpp | 1197 ++++++++++++++++++++++++++++++++++++++++ src/debug/nvwa/debug_new.h | 207 +++++++ src/debug/nvwa/fast_mutex.h | 427 ++++++++++++++ src/debug/nvwa/static_assert.h | 62 +++ src/debug/static_assert.h | 62 --- 14 files changed, 2358 insertions(+), 2358 deletions(-) delete mode 100644 src/debug/_nvwa.h delete mode 100644 src/debug/c++11.h delete mode 100644 src/debug/debug_new.cpp delete mode 100644 src/debug/debug_new.h delete mode 100644 src/debug/fast_mutex.h create mode 100644 src/debug/nvwa/_nvwa.h create mode 100644 src/debug/nvwa/c++11.h create mode 100644 src/debug/nvwa/debug_new.cpp create mode 100644 src/debug/nvwa/debug_new.h create mode 100644 src/debug/nvwa/fast_mutex.h create mode 100644 src/debug/nvwa/static_assert.h delete mode 100644 src/debug/static_assert.h diff --git a/src/Makefile.am b/src/Makefile.am index 9f5094e34..5bf511b92 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -115,18 +115,18 @@ if ENABLE_MEM_DEBUG dyecmd_CXXFLAGS += -DENABLE_MEM_DEBUG -DDEBUG_DUMP_LEAKS manaplus_CXXFLAGS += -DENABLE_MEM_DEBUG -DDEBUG_DUMP_LEAKS -manaplus_SOURCES = debug/_nvwa.h \ - debug/c++11.h \ - debug/debug_new.cpp \ - debug/debug_new.h \ - debug/fast_mutex.h \ - debug/static_assert.h -dyecmd_SOURCES = debug/_nvwa.h \ - debug/c++11.h \ - debug/debug_new.cpp \ - debug/debug_new.h \ - debug/fast_mutex.h \ - debug/static_assert.h +manaplus_SOURCES = debug/nvwa/_nvwa.h \ + debug/nvwa/c++11.h \ + debug/nvwa/debug_new.cpp \ + debug/nvwa/debug_new.h \ + debug/nvwa/fast_mutex.h \ + debug/nvwa/static_assert.h +dyecmd_SOURCES = debug/nvwa/_nvwa.h \ + debug/nvwa/c++11.h \ + debug/nvwa/debug_new.cpp \ + debug/nvwa/debug_new.h \ + debug/nvwa/fast_mutex.h \ + debug/nvwa/static_assert.h else manaplus_SOURCES = dyecmd_SOURCES = diff --git a/src/debug.h b/src/debug.h index a4feb574b..307766806 100644 --- a/src/debug.h +++ b/src/debug.h @@ -25,7 +25,7 @@ #ifdef ENABLE_MEM_DEBUG // #define _DEBUG_NEW_EMULATE_MALLOC 1 -#include "debug/debug_new.h" +#include "debug/nvwa/debug_new.h" #endif // ENABLE_MEM_DEBUG diff --git a/src/debug/_nvwa.h b/src/debug/_nvwa.h deleted file mode 100644 index 096893c59..000000000 --- a/src/debug/_nvwa.h +++ /dev/null @@ -1,112 +0,0 @@ -// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -// vim:tabstop=4:shiftwidth=4:expandtab: - -/* - * Copyright (C) 2013-2015 Wu Yongwei - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute - * it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgement in the product - * documentation would be appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source - * distribution. - * - * This file is part of Stones of Nvwa: - * http://sourceforge.net/projects/nvwa - * - */ - -/** - * @file _nvwa.h - * - * Common definitions for preprocessing. - * - * @date 2015-10-28 - */ - -#ifndef NVWA_NVWA_H -#define NVWA_NVWA_H - -/** - * @namespace nvwa - * Namespace of the nvwa project. Most functions and global variables - * are defined in this namespace. - */ - -#ifndef NVWA_USE_NAMESPACE -#ifdef __cplusplus -#define NVWA_USE_NAMESPACE 1 -#else -#define NVWA_USE_NAMESPACE 0 -#endif // __cplusplus -#endif // NVWA_USE_NAMESPACE - -#if NVWA_USE_NAMESPACE -#define NVWA_NAMESPACE_BEGIN namespace nvwa { -#define NVWA_NAMESPACE_END } -#define NVWA nvwa -#else // NVWA_USE_NAMESPACE -#define NVWA_NAMESPACE_BEGIN -#define NVWA_NAMESPACE_END -#define NVWA -#endif // NVWA_USE_NAMESPACE - -#ifndef NVWA_APPLE -#if defined(__APPLE__) && defined(__MACH__) -#define NVWA_APPLE 1 -#else -#define NVWA_APPLE 0 -#endif -#endif // NVWA_APPLE - -#ifndef NVWA_CYGWIN -#if defined(__CYGWIN__) -#define NVWA_CYGWIN 1 -#else -#define NVWA_CYGWIN 0 -#endif -#endif // NVWA_CYGWIN - -#ifndef NVWA_LINUX -#if defined(__linux__) || defined(__linux) -#define NVWA_LINUX 1 -#else -#define NVWA_LINUX 0 -#endif -#endif // NVWA_LINUX - -#ifndef NVWA_UNIX -#if defined(__unix__) || defined(__unix) || NVWA_APPLE -#define NVWA_UNIX 1 -#else -#define NVWA_UNIX 0 -#endif -#endif // NVWA_UNIX - -#ifndef NVWA_WIN32 -#if defined(_WIN32) -#define NVWA_WIN32 1 -#else -#define NVWA_WIN32 0 -#endif -#endif // NVWA_WIN32 - -#ifndef NVWA_WINDOWS -#if NVWA_CYGWIN || NVWA_WIN32 -#define NVWA_WINDOWS 1 -#else -#define NVWA_WINDOWS 0 -#endif -#endif // NVWA_WINDOWS - -#endif // NVWA_NVWA_H diff --git a/src/debug/c++11.h b/src/debug/c++11.h deleted file mode 100644 index 8d8e2d8af..000000000 --- a/src/debug/c++11.h +++ /dev/null @@ -1,340 +0,0 @@ -// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -// vim:tabstop=4:shiftwidth=4:expandtab: - -/* - * Copyright (C) 2013-2017 Wu Yongwei - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute - * it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgement in the product - * documentation would be appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source - * distribution. - * - * This file is part of Stones of Nvwa: - * http://sourceforge.net/projects/nvwa - * - */ - -/** - * @file c++11.h - * - * C++11 feature detection macros and workarounds. - * - * @date 2017-04-03 - */ - -#ifndef NVWA_CXX11_H -#define NVWA_CXX11_H - -// Only Clang provides these macros; they need to be defined as follows -// to get a valid expression in preprocessing by other compilers. -#ifndef __has_extension -#define __has_extension(x) 0 -#endif -#ifndef __has_feature -#define __has_feature(x) 0 -#endif -#ifndef __has_include -#define __has_include(x) 0 -#endif - -// Detect whether C++11 mode is on (for GCC and Clang). MSVC does not -// have a special C++11 mode, so it is always on for Visual C++ 2010 and -// later. -#if __cplusplus >= 201103L || \ - defined(__GXX_EXPERIMENTAL_CXX0X__) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) -#define NVWA_CXX11_MODE 1 -#else -#define NVWA_CXX11_MODE 0 -#endif - - -/* Feature checks */ - -#if !defined(HAVE_CXX11_ATOMIC) -#if NVWA_CXX11_MODE && \ - ((__has_include() && !defined(__MINGW32__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) || \ - (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405) || \ - defined(__clang__)) && \ - (!defined(__MINGW32__) || defined(_POSIX_THREADS)))) -// Note: MinGW GCC, unless built with POSIX threads (as in -// MinGW-builds), does not support atomics as of 4.8. -#define HAVE_CXX11_ATOMIC 1 -#else -#define HAVE_CXX11_ATOMIC 0 -#endif -#endif - -#if !defined(HAVE_CXX11_AUTO_TYPE) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_auto_type) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404)) -#define HAVE_CXX11_AUTO_TYPE 1 -#else -#define HAVE_CXX11_AUTO_TYPE 0 -#endif -#endif - -#if !defined(HAVE_CXX11_DELETED_FUNCTION) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_deleted_functions) || \ - (defined(_MSC_VER) && _MSC_VER >= 1800) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404)) -#define HAVE_CXX11_DELETED_FUNCTION 1 -#else -#define HAVE_CXX11_DELETED_FUNCTION 0 -#endif -#endif - -#if !defined(HAVE_CXX11_EXPLICIT_CONVERSION) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_explicit_conversions) || \ - (defined(_MSC_VER) && _MSC_VER >= 1900) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405)) -#define HAVE_CXX11_EXPLICIT_CONVERSION 1 -#else -#define HAVE_CXX11_EXPLICIT_CONVERSION 0 -#endif -#endif - -#if !defined(HAVE_CXX11_FINAL) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_override_control) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 407)) -#define HAVE_CXX11_FINAL 1 -#else -#define HAVE_CXX11_FINAL 0 -#endif -#endif - -#if !defined(HAVE_CXX11_FUTURE) -#if NVWA_CXX11_MODE && \ - ((__has_include() && !defined(__MINGW32__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) || \ - (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405) || \ - defined(__clang__)) && \ - (!defined(__MINGW32__) || defined(_POSIX_THREADS)))) -// Note: MinGW GCC, unless built with POSIX threads (as in -// MinGW-builds), does not support futures as of 4.8. -#define HAVE_CXX11_FUTURE 1 -#else -#define HAVE_CXX11_FUTURE 0 -#endif -#endif - -#if !defined(HAVE_CXX11_GENERALIZED_INITIALIZER) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_generalized_initializers) || \ - (defined(_MSC_VER) && _MSC_VER >= 1800) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404)) -#define HAVE_CXX11_GENERALIZED_INITIALIZER 1 -#else -#define HAVE_CXX11_GENERALIZED_INITIALIZER 0 -#endif -#endif - -#if !defined(HAVE_CXX11_LAMBDA) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_lambdas) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405)) -#define HAVE_CXX11_LAMBDA 1 -#else -#define HAVE_CXX11_LAMBDA 0 -#endif -#endif - -#if !defined(HAVE_CXX11_MUTEX) -#if NVWA_CXX11_MODE && \ - ((__has_include() && !defined(__MINGW32__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) || \ - (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403) || \ - defined(__clang__)) && \ - (!defined(__MINGW32__) || defined(_POSIX_THREADS)))) -// Note: MinGW GCC, unless built with POSIX threads (as in -// MinGW-builds), does not support std::mutex as of 4.8. -#define HAVE_CXX11_MUTEX 1 -#else -#define HAVE_CXX11_MUTEX 0 -#endif -#endif - -#if !defined(HAVE_CXX11_NOEXCEPT) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_noexcept) || \ - (defined(_MSC_VER) && _MSC_VER >= 1900) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406)) -#define HAVE_CXX11_NOEXCEPT 1 -#else -#define HAVE_CXX11_NOEXCEPT 0 -#endif -#endif - -#if !defined(HAVE_CXX11_NULLPTR) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_nullptr) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406)) -#define HAVE_CXX11_NULLPTR 1 -#else -#define HAVE_CXX11_NULLPTR 0 -#endif -#endif - -#if !defined(HAVE_CXX11_OVERRIDE) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_override_control) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 407)) -#define HAVE_CXX11_OVERRIDE 1 -#else -#define HAVE_CXX11_OVERRIDE 0 -#endif -#endif - -#if !defined(HAVE_CXX11_RANGE_FOR) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_range_for) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406)) -#define HAVE_CXX11_RANGE_FOR 1 -#else -#define HAVE_CXX11_RANGE_FOR 0 -#endif -#endif - -#if !defined(HAVE_CXX11_RVALUE_REFERENCE) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_rvalue_references) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405)) -#define HAVE_CXX11_RVALUE_REFERENCE 1 -#else -#define HAVE_CXX11_RVALUE_REFERENCE 0 -#endif -#endif - -#if !defined(HAVE_CXX11_STATIC_ASSERT) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_static_assert) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403)) -#define HAVE_CXX11_STATIC_ASSERT 1 -#else -#define HAVE_CXX11_STATIC_ASSERT 0 -#endif -#endif - -#if !defined(HAVE_CXX11_THREAD) -#if NVWA_CXX11_MODE && \ - ((__has_include() && !defined(__MINGW32__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) || \ - (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404) || \ - defined(__clang__)) && \ - (!defined(__MINGW32__) || defined(_POSIX_THREADS)))) -// Note: MinGW GCC, unless built with POSIX threads (as in -// MinGW-builds), does not support std::thread as of 4.8. -#define HAVE_CXX11_THREAD 1 -#else -#define HAVE_CXX11_THREAD 0 -#endif -#endif - -#if !defined(HAVE_CXX11_THREAD_LOCAL) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_thread_local) || \ - (defined(_MSC_VER) && _MSC_VER >= 1900) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 408)) -#define HAVE_CXX11_THREAD_LOCAL 1 -#else -#define HAVE_CXX11_THREAD_LOCAL 0 -#endif -#endif - -#if !defined(HAVE_CXX11_TYPE_TRAITS) -#if NVWA_CXX11_MODE && \ - (__has_include() || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403)) -#define HAVE_CXX11_TYPE_TRAITS 1 -#else -#define HAVE_CXX11_TYPE_TRAITS 0 -#endif -#endif - -#if !defined(HAVE_CXX11_UNICODE_LITERAL) -#if NVWA_CXX11_MODE && \ - (__has_feature(cxx_unicode_literals) || \ - (defined(_MSC_VER) && _MSC_VER >= 1900) || \ - (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405)) -#define HAVE_CXX11_UNICODE_LITERAL 1 -#else -#define HAVE_CXX11_UNICODE_LITERAL 0 -#endif -#endif - - -/* Workarounds */ - -#if HAVE_CXX11_DELETED_FUNCTION -#define _DELETED = delete -#else -#define _DELETED -#endif - -#if HAVE_CXX11_FINAL -#define _FINAL final -#else -#define _FINAL -#endif - -#if HAVE_CXX11_OVERRIDE -#define _OVERRIDE override -#else -#define _OVERRIDE -#endif - -#if HAVE_CXX11_NOEXCEPT -#define _NOEXCEPT noexcept -#define _NOEXCEPT_(x) noexcept(x) -#else -#ifdef _MSC_VER -#define _NOEXCEPT throw () -#else -#define _NOEXCEPT throw() -#endif -#define _NOEXCEPT_(x) -#endif - -#if HAVE_CXX11_NULLPTR -#define _NULLPTR nullptr -#else -#define _NULLPTR NULL -#endif - -#if HAVE_CXX11_THREAD_LOCAL -#define _THREAD_LOCAL thread_local -#else -#ifdef _MSC_VER -#define _THREAD_LOCAL __declspec(thread) -#else -#define _THREAD_LOCAL __thread -#endif -#endif - -#endif // NVWA_CXX11_H diff --git a/src/debug/debug_new.cpp b/src/debug/debug_new.cpp deleted file mode 100644 index 594796570..000000000 --- a/src/debug/debug_new.cpp +++ /dev/null @@ -1,1197 +0,0 @@ -// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -// vim:tabstop=4:shiftwidth=4:expandtab: - -/* - * Copyright (C) 2004-2016 Wu Yongwei - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute - * it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgement in the product - * documentation would be appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source - * distribution. - * - * This file is part of Stones of Nvwa: - * http://sourceforge.net/projects/nvwa - * - */ - -/** - * @file debug_new.cpp - * - * Implementation of debug versions of new and delete to check leakage. - * - * @date 2016-10-14 - */ - -#include "localconsts.h" - -PRAGMACLANG6GCC(GCC diagnostic push) -PRAGMACLANG6GCC(GCC diagnostic ignored "-Wold-style-cast") - -PRAGMA45(GCC diagnostic push) -PRAGMA45(GCC diagnostic ignored "-Wcast-qual") - -#include // std::bad_alloc/nothrow_t -#include // assert -#include // fprintf/stderr -#include // abort -#include // strcpy/strncpy/sprintf -#include "debug/_nvwa.h" // NVWA macros - -#if NVWA_UNIX -#include // alloca -#endif -#if NVWA_WIN32 -#include // alloca -#endif - -#if NVWA_LINUX || NVWA_APPLE -#include // backtrace -#endif - -#if NVWA_WINDOWS -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include // CaptureStackBackTrace -#endif - -#include "debug/c++11.h" // _NOEXCEPT/_NULLPTR -#include "debug/fast_mutex.h" // nvwa::fast_mutex -#include "debug/static_assert.h" // STATIC_ASSERT - -#undef _DEBUG_NEW_EMULATE_MALLOC -#undef _DEBUG_NEW_REDEFINE_NEW -/** - * Macro to indicate whether redefinition of \c new is wanted. Here it - * is defined to \c 0 to disable the redefinition of \c new. - */ -#define _DEBUG_NEW_REDEFINE_NEW 0 -#include "debug/debug_new.h" - -#if !_FAST_MUTEX_CHECK_INITIALIZATION && !defined(_NOTHREADS) -#error "_FAST_MUTEX_CHECK_INITIALIZATION not set: check_leaks may not work" -#endif - -/** - * @def _DEBUG_NEW_ALIGNMENT - * - * The alignment requirement of allocated memory blocks. It must be a - * power of two. - */ -#ifndef _DEBUG_NEW_ALIGNMENT -#define _DEBUG_NEW_ALIGNMENT 16 -#endif - -/** - * @def _DEBUG_NEW_CALLER_ADDRESS - * - * The expression to return the caller address. nvwa#print_position will - * later on use this address to print the position information of memory - * operation points. - */ -#ifndef _DEBUG_NEW_CALLER_ADDRESS -#ifdef __GNUC__ -#define _DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0) -#else -#define _DEBUG_NEW_CALLER_ADDRESS _NULLPTR -#endif -#endif - -/** - * @def _DEBUG_NEW_ERROR_ACTION - * - * The action to take when an error occurs. The default behaviour is to - * call \e abort, unless \c _DEBUG_NEW_ERROR_CRASH is defined, in which - * case a segmentation fault will be triggered instead (which can be - * useful on platforms like Windows that do not generate a core dump - * when \e abort is called). - */ -#ifndef _DEBUG_NEW_ERROR_ACTION -#ifndef _DEBUG_NEW_ERROR_CRASH -#define _DEBUG_NEW_ERROR_ACTION abort() -#else -#define _DEBUG_NEW_ERROR_ACTION do { *((char*)0) = 0; abort(); } while (0) -#endif -#endif - -/** - * @def _DEBUG_NEW_FILENAME_LEN - * - * The length of file name stored if greater than zero. If it is zero, - * only a const char pointer will be stored. Currently the default - * value is non-zero (thus to copy the file name) on non-Windows - * platforms, because I once found that the exit leakage check could not - * access the address of the file name on Linux (in my case, a core dump - * occurred when check_leaks tried to access the file name in a shared - * library after a \c SIGINT). This value makes the size of - * new_ptr_list_t \c 64 on non-Windows 32-bit platforms (w/o stack - * backtrace). - */ -#ifndef _DEBUG_NEW_FILENAME_LEN -#if NVWA_WINDOWS -#define _DEBUG_NEW_FILENAME_LEN 0 -#else -#define _DEBUG_NEW_FILENAME_LEN 44 -#endif -#endif - -/** - * @def _DEBUG_NEW_PROGNAME - * - * The program (executable) name to be set at compile time. It is - * better to assign the full program path to nvwa#new_progname in \e main - * (at run time) than to use this (compile-time) macro, but this macro - * serves well as a quick hack. Note also that double quotation marks - * need to be used around the program name, i.e., one should specify a - * command-line option like -D_DEBUG_NEW_PROGNAME=\\"a.out\" - * in \e bash, or -D_DEBUG_NEW_PROGNAME=\\"a.exe\" in the - * Windows command prompt. - */ -#ifndef _DEBUG_NEW_PROGNAME -#define _DEBUG_NEW_PROGNAME _NULLPTR -#endif - -/** - * @def _DEBUG_NEW_REMEMBER_STACK_TRACE - * - * Macro to indicate whether stack traces of allocations should be - * included in NVWA allocation information and be printed while leaks - * are reported. Useful to pinpoint leaks caused by strdup and other - * custom allocation functions. It is also very helpful in filtering - * out false positives caused by internal STL/C runtime operations. It - * is off by default because it is quite memory heavy. Set it to \c 1 - * to make all allocations have stack trace attached, or set to \c 2 to - * make only allocations that lack calling source code information (file - * and line) have the stack trace attached. - */ -#ifndef _DEBUG_NEW_REMEMBER_STACK_TRACE -#define _DEBUG_NEW_REMEMBER_STACK_TRACE 0 -#endif - -/** - * @def _DEBUG_NEW_STD_OPER_NEW - * - * Macro to indicate whether the standard-conformant behaviour of - * operator new is wanted. It is on by default now, but - * the user may set it to \c 0 to revert to the old behaviour. - */ -#ifndef _DEBUG_NEW_STD_OPER_NEW -#define _DEBUG_NEW_STD_OPER_NEW 1 -#endif - -/** - * @def _DEBUG_NEW_TAILCHECK - * - * Macro to indicate whether a writing-past-end check will be performed. - * Define it to a positive integer as the number of padding bytes at the - * end of a memory block for checking. - */ -#ifndef _DEBUG_NEW_TAILCHECK -#define _DEBUG_NEW_TAILCHECK 0 -#endif - -/** - * @def _DEBUG_NEW_TAILCHECK_CHAR - * - * Value of the padding bytes at the end of a memory block. - */ -//#ifndef _DEBUG_NEW_TAILCHECK_CHAR -//#define _DEBUG_NEW_TAILCHECK_CHAR 0xCC -//#endif - -/** - * @def _DEBUG_NEW_USE_ADDR2LINE - * - * Whether to use \e addr2line to convert a caller address to file/line - * information. Defining it to a non-zero value will enable the - * conversion (automatically done if GCC is detected). Defining it to - * zero will disable the conversion. - */ -#ifndef _DEBUG_NEW_USE_ADDR2LINE -#ifdef __GNUC__ -#define _DEBUG_NEW_USE_ADDR2LINE 1 -#else -#define _DEBUG_NEW_USE_ADDR2LINE 0 -#endif -#endif - -#ifdef _MSC_VER -#pragma warning(disable: 4074) // #pragma init_seg(compiler) used -#pragma warning(disable: 4290) // C++ exception specification ignored -#if _MSC_VER >= 1400 // Visual Studio 2005 or later -#pragma warning(disable: 4996) // Use the `unsafe' strncpy -#endif -#pragma init_seg(compiler) -#endif - -/** - * Gets the aligned value of memory block size. - */ -#define ALIGN(s) \ - (((s) + _DEBUG_NEW_ALIGNMENT - 1) & ~(_DEBUG_NEW_ALIGNMENT - 1)) - -NVWA_NAMESPACE_BEGIN - -/** - * The platform memory alignment. The current value works well in - * platforms I have tested: Windows XP, Windows 7 x64, and Mac OS X - * Leopard. It may be smaller than the real alignment, but must be - * bigger than \c sizeof(size_t) for it work. nvwa#debug_new_recorder - * uses it to detect misaligned pointer returned by `new - * NonPODType[size]'. - */ -const size_t PLATFORM_MEM_ALIGNMENT = sizeof(size_t) * 2; - -/** - * Structure to store the position information where \c new occurs. - */ -struct new_ptr_list_t -{ - new_ptr_list_t* next; ///< Pointer to the next memory block - new_ptr_list_t* prev; ///< Pointer to the previous memory block - size_t size; ///< Size of the memory block - union - { -#if _DEBUG_NEW_FILENAME_LEN == 0 - const char* file; ///< Pointer to the file name of the caller -#else - char file[_DEBUG_NEW_FILENAME_LEN]; ///< File name of the caller -#endif - void* addr; ///< Address of the caller to \e new - }; - unsigned line :31; ///< Line number of the caller; or \c 0 - unsigned is_array:1; ///< Non-zero iff new[] is used -#if _DEBUG_NEW_REMEMBER_STACK_TRACE - void** stacktrace; ///< Pointer to stack trace information -#endif - unsigned magic; ///< Magic number for error detection -}; - -/** - * Definition of the constant magic number used for error detection. - */ -static const unsigned DEBUG_NEW_MAGIC = 0x4442474E; - -/** - * The extra memory allocated by operator new. - */ -static const int ALIGNED_LIST_ITEM_SIZE = ALIGN(sizeof(new_ptr_list_t)); - -/** - * List of all new'd pointers. - */ -static new_ptr_list_t new_ptr_list = { - &new_ptr_list, - &new_ptr_list, - 0, - { -#if _DEBUG_NEW_FILENAME_LEN == 0 - _NULLPTR -#else - "" -#endif - }, - 0, - 0, -#if _DEBUG_NEW_REMEMBER_STACK_TRACE - _NULLPTR, -#endif - DEBUG_NEW_MAGIC -}; - -/** - * The mutex guard to protect simultaneous access to the pointer list. - */ -static fast_mutex new_ptr_lock; - -/** - * The mutex guard to protect simultaneous output to #new_output_fp. - */ -static fast_mutex new_output_lock; - -/** - * Total memory allocated in bytes. - */ -static size_t total_mem_alloc = 0; - -/** - * Flag to control whether nvwa#check_leaks will be automatically called - * on program exit. - */ -bool new_autocheck_flag = true; - -/** - * Flag to control whether verbose messages are output. - */ -bool new_verbose_flag = false; - -/** - * Pointer to the output stream. The default output is \e stderr, and - * one may change it to a user stream if needed (say, #new_verbose_flag - * is \c true and there are a lot of (de)allocations). - */ -FILE* new_output_fp = stderr; - -/** - * Pointer to the program name. Its initial value is the macro - * #_DEBUG_NEW_PROGNAME. You should try to assign the program path to - * it early in your application. Assigning argv[0] to it - * in \e main is one way. If you use \e bash or \e ksh (or similar), - * the following statement is probably what you want: - * `new_progname = getenv("_");'. - */ -const char* new_progname = _DEBUG_NEW_PROGNAME; - -/** - * Pointer to the callback used to print the stack backtrace in case of - * a memory problem. A null value causes the default stack trace - * printing routine to be used. - */ -stacktrace_print_callback_t stacktrace_print_callback = _NULLPTR; - -/** - * Pointer to the callback used to filter out false positives from leak - * reports. A null value means the lack of filtering. - */ -leak_whitelist_callback_t leak_whitelist_callback = _NULLPTR; - -#if _DEBUG_NEW_USE_ADDR2LINE -/** - * Tries printing the position information from an instruction address. - * This is the version that uses \e addr2line. - * - * @param addr the instruction address to convert and print - * @return \c true if the address is converted successfully (and - * the result is printed); \c false if no useful - * information is got (and nothing is printed) - */ -static bool print_position_from_addr(const void* addr) -{ - static const void* last_addr = _NULLPTR; - static char last_info[256] = ""; - if (addr == last_addr) - { - if (last_info[0] == '\0') - return false; - fprintf(new_output_fp, "%s", last_info); - return true; - } - if (new_progname) - { -#if NVWA_APPLE - const char addr2line_cmd[] = "atos -o "; -#else - const char addr2line_cmd[] = "addr2line -e "; -#endif - -#if NVWA_WINDOWS - const int exeext_len = 4; -#else - const int exeext_len = 0; -#endif - -#if NVWA_UNIX && !NVWA_CYGWIN - const char ignore_err[] = " 2>/dev/null"; -#elif NVWA_CYGWIN || \ - (NVWA_WIN32 && defined(WINVER) && WINVER >= 0x0500) - const char ignore_err[] = " 2>nul"; -#else - const char ignore_err[] = ""; -#endif - char* cmd = (char*)alloca(strlen(new_progname) - + exeext_len - + sizeof addr2line_cmd - 1 - + sizeof ignore_err - 1 - + sizeof(void*) * 2 - + 4 /* SP + "0x" + null */); - strcpy(cmd, addr2line_cmd); - strcpy(cmd + sizeof addr2line_cmd - 1, new_progname); - size_t len = strlen(cmd); -#if NVWA_WINDOWS - if (len <= 4 - || (strcmp(cmd + len - 4, ".exe") != 0 && - strcmp(cmd + len - 4, ".EXE") != 0)) - { - strcpy(cmd + len, ".exe"); - len += 4; - } -#endif - sprintf(cmd + len, " %p%s", addr, ignore_err); - FILE* fp = popen(cmd, "r"); - if (fp) - { - char buffer[sizeof last_info] = ""; - len = 0; - if (fgets(buffer, sizeof buffer, fp)) - { - len = strlen(buffer); - if (buffer[len - 1] == '\n') - buffer[--len] = '\0'; - } - int res = pclose(fp); - // Display the file/line information only if the command - // is executed successfully and the output points to a - // valid position, but the result will be cached if only - // the command is executed successfully. - if (res == 0 && len > 0) - { - last_addr = addr; - if (buffer[len - 1] == '0' && buffer[len - 2] == ':') - last_info[0] = '\0'; - else - { - fprintf(new_output_fp, "%s", buffer); - strcpy(last_info, buffer); - return true; - } - } - } - } - return false; -} -#else -/** - * Tries printing the position information from an instruction address. - * This is the stub version that does nothing at all. - * - * @return \c false always - */ -static bool print_position_from_addr(const void*) -{ - return false; -} -#endif // _DEBUG_NEW_USE_ADDR2LINE - -/** - * Prints the position information of a memory operation point. When \c - * _DEBUG_NEW_USE_ADDR2LINE is defined to a non-zero value, this - * function will try to convert a given caller address to file/line - * information with \e addr2line. - * - * @param ptr source file name if \e line is non-zero; caller address - * otherwise - * @param line source line number if non-zero; indication that \e ptr - * is the caller address otherwise - */ -static void print_position(const void* ptr, int line) -{ - if (line != 0) // Is file/line information present? - { - fprintf(new_output_fp, "%s:%d", (const char*)ptr, line); - } - else if (ptr != _NULLPTR) // Is caller address present? - { - if (!print_position_from_addr(ptr)) // Fail to get source position? - fprintf(new_output_fp, "%p", ptr); - } - else // No information is present - { - fprintf(new_output_fp, ""); - } -} - -#if _DEBUG_NEW_REMEMBER_STACK_TRACE -/** - * Prints the stack backtrace. - * - * When nvwa#stacktrace_print_callback is not null, it is used for - * printing the stacktrace items. Default implementation of call stack - * printing is very spartan—only stack frame pointers are - * printed—but even that output is still useful. Just do address - * lookup in LLDB etc. - * - * @param stacktrace pointer to the stack trace array - */ -static void print_stacktrace(void** stacktrace) -{ - if (stacktrace_print_callback == _NULLPTR) - { - fprintf(new_output_fp, "Stack backtrace:\n"); - for (size_t i = 0; stacktrace[i] != _NULLPTR; ++i) - fprintf(new_output_fp, "%p\n", stacktrace[i]); - } - else - { - stacktrace_print_callback(new_output_fp, stacktrace); - } -} -#endif - -/** - * Checks whether a leak should be ignored. Its runtime performance - * depends on the callback nvwa#leak_whitelist_callback. - * - * @param ptr pointer to a new_ptr_list_t struct - * @return \c true if the leak should be whitelisted; \c false - * otherwise - */ -static bool is_leak_whitelisted(new_ptr_list_t* ptr) -{ - if (leak_whitelist_callback == _NULLPTR) - return false; - - char const* file = ptr->line != 0 ? ptr->file : _NULLPTR; - int line = ptr->line; - void* addr = ptr->line == 0 ? ptr->addr : _NULLPTR; -#if _DEBUG_NEW_REMEMBER_STACK_TRACE - void** stacktrace = ptr->stacktrace; -#else - void** stacktrace = _NULLPTR; -#endif - - return leak_whitelist_callback(file, line, addr, stacktrace); -} - -#if _DEBUG_NEW_TAILCHECK -/** - * Checks whether the padding bytes at the end of a memory block is - * tampered with. - * - * @param ptr pointer to a new_ptr_list_t struct - * @return \c true if the padding bytes are untouched; \c false - * otherwise - */ -static bool check_tail(new_ptr_list_t* ptr) -{ - const unsigned char* const tail_ptr = (unsigned char*)ptr + - ALIGNED_LIST_ITEM_SIZE + ptr->size; - for (int i = 0; i < _DEBUG_NEW_TAILCHECK; ++i) - if (tail_ptr[i] != _DEBUG_NEW_TAILCHECK_CHAR) - return false; - return true; -} -#endif - -/** - * Allocates memory and initializes control data. - * - * @param size size of the required memory block - * @param file null-terminated string of the file name - * @param line line number - * @param is_array boolean value whether this is an array operation - * @return pointer to the user-requested memory area; null if - * memory allocation is not successful - */ -static void* alloc_mem(size_t size, const char* file, int line, bool is_array) -{ - assert(line >= 0); -#if _DEBUG_NEW_TYPE == 1 - STATIC_ASSERT(_DEBUG_NEW_ALIGNMENT >= PLATFORM_MEM_ALIGNMENT, - Alignment_too_small); -#endif - STATIC_ASSERT((_DEBUG_NEW_ALIGNMENT & (_DEBUG_NEW_ALIGNMENT - 1)) == 0, - Alignment_must_be_power_of_two); - STATIC_ASSERT(_DEBUG_NEW_TAILCHECK >= 0, Invalid_tail_check_length); - size_t s = size + ALIGNED_LIST_ITEM_SIZE + _DEBUG_NEW_TAILCHECK; - new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s); - if (ptr == _NULLPTR) - { -#if _DEBUG_NEW_STD_OPER_NEW - return _NULLPTR; -#else - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "Out of memory when allocating %lu bytes\n", - (unsigned long)size); - fflush(new_output_fp); - _DEBUG_NEW_ERROR_ACTION; -#endif - } - void* usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; -#if _DEBUG_NEW_FILENAME_LEN == 0 - ptr->file = file; -#else - if (line) - strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1) - [_DEBUG_NEW_FILENAME_LEN - 1] = '\0'; - else - ptr->addr = (void*)file; -#endif - ptr->line = line; -#if _DEBUG_NEW_REMEMBER_STACK_TRACE - ptr->stacktrace = _NULLPTR; - -#if _DEBUG_NEW_REMEMBER_STACK_TRACE == 2 - if (line == 0) -#endif - { - void* buffer [255]; - size_t buffer_length = sizeof(buffer) / sizeof(*buffer); - -#if NVWA_UNIX - int stacktrace_length = backtrace(buffer, int(buffer_length)); -#endif - -#if NVWA_WINDOWS - USHORT stacktrace_length = CaptureStackBackTrace( - 0, DWORD(buffer_length), buffer, _NULLPTR); -#endif - - size_t stacktrace_size = stacktrace_length * sizeof(void*); - ptr->stacktrace = (void**)malloc(stacktrace_size + sizeof(void*)); - - if (ptr->stacktrace != _NULLPTR) - { - memcpy(ptr->stacktrace, buffer, stacktrace_size); - ptr->stacktrace[stacktrace_length] = _NULLPTR; - } - } -#endif - ptr->is_array = is_array; - ptr->size = size; - ptr->magic = DEBUG_NEW_MAGIC; - { - fast_mutex_autolock lock(new_ptr_lock); - ptr->prev = new_ptr_list.prev; - ptr->next = &new_ptr_list; - new_ptr_list.prev->next = ptr; - new_ptr_list.prev = ptr; - } -#if _DEBUG_NEW_TAILCHECK - memset((char*)usr_ptr + size, _DEBUG_NEW_TAILCHECK_CHAR, - _DEBUG_NEW_TAILCHECK); -#endif - if (new_verbose_flag) - { - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "new%s: allocated %p (size %lu, ", - is_array ? "[]" : "", - usr_ptr, (unsigned long)size); - if (line != 0) - print_position(ptr->file, ptr->line); - else - print_position(ptr->addr, ptr->line); - fprintf(new_output_fp, ")\n"); - } - total_mem_alloc += size; - return usr_ptr; -} - -/** - * Frees memory and adjusts pointers. - * - * @param usr_ptr pointer to the previously allocated memory - * @param addr pointer to the caller - * @param is_array flag indicating whether it is invoked by a - * delete[] call - */ -static void free_pointer(void* usr_ptr, void* addr, bool is_array) -{ - if (usr_ptr == _NULLPTR) - return; - new_ptr_list_t* ptr = - (new_ptr_list_t*)((char*)usr_ptr - ALIGNED_LIST_ITEM_SIZE); - if (ptr->magic != DEBUG_NEW_MAGIC) - { - { - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, "delete%s: invalid pointer %p (", - is_array ? "[]" : "", usr_ptr); - print_position(addr, 0); - fprintf(new_output_fp, ")\n"); - } - check_mem_corruption(); - fflush(new_output_fp); - _DEBUG_NEW_ERROR_ACTION; - } - if (is_array != ptr->is_array) - { - const char* msg; - if (is_array) - msg = "delete[] after new"; - else - msg = "delete after new[]"; - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "%s: pointer %p (size %lu)\n\tat ", - msg, - (char*)ptr + ALIGNED_LIST_ITEM_SIZE, - (unsigned long)ptr->size); - print_position(addr, 0); - fprintf(new_output_fp, "\n\toriginally allocated at "); - if (ptr->line != 0) - print_position(ptr->file, ptr->line); - else - print_position(ptr->addr, ptr->line); - fprintf(new_output_fp, "\n"); - fflush(new_output_fp); - _DEBUG_NEW_ERROR_ACTION; - } -#if _DEBUG_NEW_TAILCHECK - if (!check_tail(ptr)) - { - check_mem_corruption(); - fflush(new_output_fp); - _DEBUG_NEW_ERROR_ACTION; - } -#endif - { - fast_mutex_autolock lock(new_ptr_lock); - total_mem_alloc -= ptr->size; - ptr->magic = 0; - ptr->prev->next = ptr->next; - ptr->next->prev = ptr->prev; - } - if (new_verbose_flag) - { - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "delete%s: freed %p (size %lu, %lu bytes still allocated)\n", - is_array ? "[]" : "", - (char*)ptr + ALIGNED_LIST_ITEM_SIZE, - (unsigned long)ptr->size, (unsigned long)total_mem_alloc); - } -#if _DEBUG_NEW_REMEMBER_STACK_TRACE - free(ptr->stacktrace); -#endif - free(ptr); - return; -} - -/** - * Checks for memory leaks. - * - * @return zero if no leakage is found; the number of leaks otherwise - */ -int check_leaks() -{ - int leak_cnt = 0; - int whitelisted_leak_cnt = 0; - fast_mutex_autolock lock_ptr(new_ptr_lock); - fast_mutex_autolock lock_output(new_output_lock); - new_ptr_list_t* ptr = new_ptr_list.next; - - while (ptr != &new_ptr_list) - { - const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; - if (ptr->magic != DEBUG_NEW_MAGIC) - { - fprintf(new_output_fp, - "warning: heap data corrupt near %p\n", - usr_ptr); - } -#if _DEBUG_NEW_TAILCHECK - if (!check_tail(ptr)) - { - fprintf(new_output_fp, - "warning: overwritten past end of object at %p\n", - usr_ptr); - } -#endif - - if (is_leak_whitelisted(ptr)) - { - ++whitelisted_leak_cnt; - } - else - { - fprintf(new_output_fp, - "Leaked object at %p (size %lu, ", - usr_ptr, - (unsigned long)ptr->size); - - if (ptr->line != 0) - print_position(ptr->file, ptr->line); - else - print_position(ptr->addr, ptr->line); - - fprintf(new_output_fp, ")\n"); - -#if _DEBUG_NEW_REMEMBER_STACK_TRACE - if (ptr->stacktrace != _NULLPTR) - print_stacktrace(ptr->stacktrace); -#endif - } - - ptr = ptr->next; - ++leak_cnt; - } - if (new_verbose_flag || leak_cnt) - { - if (whitelisted_leak_cnt > 0) - { - fprintf(new_output_fp, "*** %d leaks found (%d whitelisted)\n", - leak_cnt, whitelisted_leak_cnt); - } - else - { - fprintf(new_output_fp, "*** %d leaks found\n", leak_cnt); - } - } - - return leak_cnt; -} - -/** - * Checks for heap corruption. - * - * @return zero if no problem is found; the number of found memory - * corruptions otherwise - */ -int check_mem_corruption() -{ - int corrupt_cnt = 0; - fast_mutex_autolock lock_ptr(new_ptr_lock); - fast_mutex_autolock lock_output(new_output_lock); - fprintf(new_output_fp, "*** Checking for memory corruption: START\n"); - for (new_ptr_list_t* ptr = new_ptr_list.next; - ptr != &new_ptr_list; - ptr = ptr->next) - { - const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; - if (ptr->magic == DEBUG_NEW_MAGIC -#if _DEBUG_NEW_TAILCHECK - && check_tail(ptr) -#endif - ) - continue; -#if _DEBUG_NEW_TAILCHECK - if (ptr->magic != DEBUG_NEW_MAGIC) - { -#endif - fprintf(new_output_fp, - "Heap data corrupt near %p (size %lu, ", - usr_ptr, - (unsigned long)ptr->size); -#if _DEBUG_NEW_TAILCHECK - } - else - { - fprintf(new_output_fp, - "Overwritten past end of object at %p (size %lu, ", - usr_ptr, - (unsigned long)ptr->size); - } -#endif - if (ptr->line != 0) - print_position(ptr->file, ptr->line); - else - print_position(ptr->addr, ptr->line); - fprintf(new_output_fp, ")\n"); - -#if _DEBUG_NEW_REMEMBER_STACK_TRACE - if (ptr->stacktrace != _NULLPTR) - print_stacktrace(ptr->stacktrace); -#endif - - ++corrupt_cnt; - } - fprintf(new_output_fp, "*** Checking for memory corruption: %d FOUND\n", - corrupt_cnt); - return corrupt_cnt; -} - -/** - * Processes the allocated memory and inserts file/line informatin. - * It will only be done when it can ensure the memory is allocated by - * one of our operator new variants. - * - * @param usr_ptr pointer returned by a new-expression - */ -void debug_new_recorder::_M_process(void* usr_ptr) -{ - if (usr_ptr == _NULLPTR) - return; - - // In an expression `new NonPODType[size]', the pointer returned is - // not the pointer returned by operator new[], but offset by size_t - // to leave room for the size. It needs to be compensated here. - size_t offset = (char*)usr_ptr - (char*)_NULLPTR; - if (offset % PLATFORM_MEM_ALIGNMENT != 0) { - offset -= sizeof(size_t); - if (offset % PLATFORM_MEM_ALIGNMENT != 0) { - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "warning: memory unaligned; skipping processing (%s:%d)\n", - _M_file, _M_line); - return; - } - usr_ptr = (char*)usr_ptr - sizeof(size_t); - } - - new_ptr_list_t* ptr = - (new_ptr_list_t*)((char*)usr_ptr - ALIGNED_LIST_ITEM_SIZE); - if (ptr->magic != DEBUG_NEW_MAGIC || ptr->line != 0) - { - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "warning: debug_new used with placement new (%s:%d)\n", - _M_file, _M_line); - return; - } - if (new_verbose_flag) { - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "info: pointer %p allocated from %s:%d\n", - usr_ptr, _M_file, _M_line); - } -#if _DEBUG_NEW_FILENAME_LEN == 0 - ptr->file = _M_file; -#else - strncpy(ptr->file, _M_file, _DEBUG_NEW_FILENAME_LEN - 1) - [_DEBUG_NEW_FILENAME_LEN - 1] = '\0'; -#endif - ptr->line = _M_line; -#if _DEBUG_NEW_REMEMBER_STACK_TRACE == 2 - free(ptr->stacktrace); - ptr->stacktrace = _NULLPTR; -#endif -} - -/** - * Count of source files that use debug_new. - */ -int debug_new_counter::_S_count = 0; - -/** - * Constructor to increment the count. - */ -debug_new_counter::debug_new_counter() -{ - ++_S_count; -} - -/** - * Destructor to decrement the count. When the count is zero, - * nvwa#check_leaks will be called. - */ -debug_new_counter::~debug_new_counter() -{ - if (--_S_count == 0 && new_autocheck_flag) - if (check_leaks()) - { - new_verbose_flag = true; -#if defined(__GNUC__) && __GNUC__ == 3 - if (!getenv("GLIBCPP_FORCE_NEW") && !getenv("GLIBCXX_FORCE_NEW")) - fprintf(new_output_fp, -"*** WARNING: GCC 3 is detected, please make sure the environment\n" -" variable GLIBCPP_FORCE_NEW (GCC 3.2 and 3.3) or GLIBCXX_FORCE_NEW\n" -" (GCC 3.4) is defined. Check the README file for details.\n"); -#endif - } -} - -NVWA_NAMESPACE_END - -#if NVWA_USE_NAMESPACE -using namespace nvwa; -#endif // NVWA_USE_NAMESPACE - -/** - * Allocates memory with file/line information. - * - * @param size size of the required memory block - * @param file null-terminated string of the file name - * @param line line number - * @return pointer to the memory allocated; or null if memory is - * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0) - * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1) - */ -void* operator new(size_t size, const char* file, int line) -{ - void* ptr = alloc_mem(size, file, line, false); -#if _DEBUG_NEW_STD_OPER_NEW - if (ptr) - return ptr; - else - throw std::bad_alloc(); -#else - return ptr; -#endif -} - -/** - * Allocates array memory with file/line information. - * - * @param size size of the required memory block - * @param file null-terminated string of the file name - * @param line line number - * @return pointer to the memory allocated; or null if memory is - * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0) - * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1) - */ -void* operator new[](size_t size, const char* file, int line) -{ - void* ptr = alloc_mem(size, file, line, true); -#if _DEBUG_NEW_STD_OPER_NEW - if (ptr) - return ptr; - else - throw std::bad_alloc(); -#else - return ptr; -#endif -} - -/** - * Allocates memory without file/line information. - * - * @param size size of the required memory block - * @return pointer to the memory allocated; or null if memory is - * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0) - * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1) - */ -void* operator new(size_t size) -{ - return operator new(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); -} - -/** - * Allocates array memory without file/line information. - * - * @param size size of the required memory block - * @return pointer to the memory allocated; or null if memory is - * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0) - * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1) - */ -void* operator new[](size_t size) -{ - return operator new[](size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); -} - -/** - * Allocates memory with no-throw guarantee. - * - * @param size size of the required memory block - * @return pointer to the memory allocated; or null if memory is - * insufficient - */ -void* operator new(size_t size, const std::nothrow_t&) _NOEXCEPT -{ - return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, false); -} - -/** - * Allocates array memory with no-throw guarantee. - * - * @param size size of the required memory block - * @return pointer to the memory allocated; or null if memory is - * insufficient - */ -void* operator new[](size_t size, const std::nothrow_t&) _NOEXCEPT -{ - return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, true); -} - -/** - * Deallocates memory. - * - * @param ptr pointer to the previously allocated memory - */ -void operator delete(void* ptr) _NOEXCEPT -{ - free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, false); -} - -/** - * Deallocates array memory. - * - * @param ptr pointer to the previously allocated memory - */ -void operator delete[](void* ptr) _NOEXCEPT -{ - free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, true); -} - -#if __cplusplus >= 201402L -// GCC under C++14 wants these definitions - -void operator delete(void* ptr, size_t) _NOEXCEPT -{ - free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, false); -} - -void operator delete[](void* ptr, size_t) _NOEXCEPT -{ - free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, true); -} -#endif - -/** - * Placement deallocation function. For details, please check Section - * 5.3.4 of the C++ 1998 or 2011 Standard. - * - * @param ptr pointer to the previously allocated memory - * @param file null-terminated string of the file name - * @param line line number - * - * @see http://www.csci.csusb.edu/dick/c++std/cd2/expr.html#expr.new - * @see http://wyw.dcweb.cn/leakage.htm - */ -void operator delete(void* ptr, const char* file, int line) _NOEXCEPT -{ - if (new_verbose_flag) - { - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "info: exception thrown on initializing object at %p (", - ptr); - print_position(file, line); - fprintf(new_output_fp, ")\n"); - } - operator delete(ptr); -} - -/** - * Placement deallocation function. For details, please check Section - * 5.3.4 of the C++ 1998 or 2011 Standard. - * - * @param ptr pointer to the previously allocated memory - * @param file null-terminated string of the file name - * @param line line number - */ -void operator delete[](void* ptr, const char* file, int line) _NOEXCEPT -{ - if (new_verbose_flag) - { - fast_mutex_autolock lock(new_output_lock); - fprintf(new_output_fp, - "info: exception thrown on initializing objects at %p (", - ptr); - print_position(file, line); - fprintf(new_output_fp, ")\n"); - } - operator delete[](ptr); -} - -/** - * Placement deallocation function. For details, please check Section - * 5.3.4 of the C++ 1998 or 2011 Standard. - * - * @param ptr pointer to the previously allocated memory - */ -void operator delete(void* ptr, const std::nothrow_t&) _NOEXCEPT -{ - operator delete(ptr, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); -} - -/** - * Placement deallocation function. For details, please check Section - * 5.3.4 of the C++ 1998 or 2011 Standard. - * - * @param ptr pointer to the previously allocated memory - */ -void operator delete[](void* ptr, const std::nothrow_t&) _NOEXCEPT -{ - operator delete[](ptr, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); -} - -// This is to make Doxygen happy -#undef _DEBUG_NEW_REMEMBER_STACK_TRACE -//#define _DEBUG_NEW_REMEMBER_STACK_TRACE 0 - -PRAGMA45(GCC diagnostic pop) -PRAGMACLANG6GCC(GCC diagnostic pop) diff --git a/src/debug/debug_new.h b/src/debug/debug_new.h deleted file mode 100644 index 926de99de..000000000 --- a/src/debug/debug_new.h +++ /dev/null @@ -1,207 +0,0 @@ -// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -// vim:tabstop=4:shiftwidth=4:expandtab: - -/* - * Copyright (C) 2004-2015 Wu Yongwei - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute - * it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgement in the product - * documentation would be appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source - * distribution. - * - * This file is part of Stones of Nvwa: - * http://sourceforge.net/projects/nvwa - * - */ - -/** - * @file debug_new.h - * - * Header file for checking leaks caused by unmatched new/delete. - * - * @date 2015-10-25 - */ - -#ifndef NVWA_DEBUG_NEW_H -#define NVWA_DEBUG_NEW_H - -#include // size_t/std::bad_alloc -#include // FILE -#include "debug/_nvwa.h" // NVWA_NAMESPACE_* -#include "debug/c++11.h" // _NOEXCEPT - -/* Special allocation/deallocation functions in the global scope */ -void* operator new(size_t size, const char* file, int line); -void* operator new[](size_t size, const char* file, int line); -void operator delete(void* ptr, const char* file, int line) _NOEXCEPT; -void operator delete[](void* ptr, const char* file, int line) _NOEXCEPT; - -NVWA_NAMESPACE_BEGIN - -/** - * @def _DEBUG_NEW_REDEFINE_NEW - * - * Macro to indicate whether redefinition of \c new is wanted. If one - * wants to define one's own operator new, or to call - * operator new directly, it should be defined to \c 0 to - * alter the default behaviour. Unless, of course, one is willing to - * take the trouble to write something like: - * @code - * # ifdef new - * # define _NEW_REDEFINED - * # undef new - * # endif - * - * // Code that uses new is here - * - * # ifdef _NEW_REDEFINED - * # ifdef DEBUG_NEW - * # define new DEBUG_NEW - * # endif - * # undef _NEW_REDEFINED - * # endif - * @endcode - */ -#ifndef _DEBUG_NEW_REDEFINE_NEW -#define _DEBUG_NEW_REDEFINE_NEW 1 -#endif - -/** - * @def _DEBUG_NEW_TYPE - * - * Macro to indicate which variant of #DEBUG_NEW is wanted. The - * default value \c 1 allows the use of placement new (like - * %new(std::nothrow)), but the verbose output (when - * nvwa#new_verbose_flag is \c true) looks worse than some older - * versions (no file/line information for allocations). Define it - * to \c 2 to revert to the old behaviour that records file and line - * information directly on the call to operator new. - */ -#ifndef _DEBUG_NEW_TYPE -#define _DEBUG_NEW_TYPE 1 -#endif - -/** - * Callback type for stack trace printing. - * - * @param fp pointer to the output stream - * @param stacktrace pointer to the stack trace array (null-terminated) - */ -typedef void (*stacktrace_print_callback_t)(FILE* fp, void** stacktrace); - -/** - * Callback type for the leak whitelist function. \a file, \a address, - * and \a backtrace might be null depending on library configuration, - * platform, and amount of runtime information available. \a line can - * be 0 when line number info is not available at runtime. - * - * @param file null-terminated string of the file name - * @param line line number - * @param addr address of code where leakage happens - * @param stacktrace pointer to the stack trace array (null-terminated) - * @return \c true if the leak should be whitelisted; - * \c false otherwise - */ -typedef bool (*leak_whitelist_callback_t)(char const* file, int line, - void* addr, void** stacktrace); - -/* Prototypes */ -int check_leaks(); -int check_mem_corruption(); - -/* Control variables */ -extern bool new_autocheck_flag; // default to true: call check_leaks() on exit -extern bool new_verbose_flag; // default to false: no verbose information -extern FILE* new_output_fp; // default to stderr: output to console -extern const char* new_progname;// default to null; should be assigned argv[0] -extern stacktrace_print_callback_t stacktrace_print_callback;// default to null -extern leak_whitelist_callback_t leak_whitelist_callback; // default to null - -/** - * @def DEBUG_NEW - * - * Macro to catch file/line information on allocation. If - * #_DEBUG_NEW_REDEFINE_NEW is \c 0, one can use this macro directly; - * otherwise \c new will be defined to it, and one must use \c new - * instead. - */ -# if _DEBUG_NEW_TYPE == 1 -# define DEBUG_NEW NVWA::debug_new_recorder(__FILE__, __LINE__) ->* new -# else -# define DEBUG_NEW new(__FILE__, __LINE__) -# endif - -# if _DEBUG_NEW_REDEFINE_NEW -# define new DEBUG_NEW -# endif -# ifdef _DEBUG_NEW_EMULATE_MALLOC -# include -# ifdef new -# define malloc(s) ((void*)(new char[s])) -# else -# define malloc(s) ((void*)(DEBUG_NEW char[s])) -# endif -# define free(p) delete[] (char*)(p) -# endif - -/** - * Recorder class to remember the call context. - * - * The idea comes from Greg Herlihy's post in comp.lang.c++.moderated. - */ -class debug_new_recorder -{ - const char* _M_file; - const int _M_line; - void _M_process(void* ptr); -public: - /** - * Constructor to remember the call context. The information will - * be used in debug_new_recorder::operator->*. - */ - debug_new_recorder(const char* file, int line) - : _M_file(file), _M_line(line) {} - /** - * Operator to write the context information to memory. - * operator->* is chosen because it has the right - * precedence, it is rarely used, and it looks good: so people can - * tell the special usage more quickly. - */ - template _Tp* operator->*(_Tp* ptr) - { _M_process(ptr); return ptr; } -private: - debug_new_recorder(const debug_new_recorder&); - debug_new_recorder& operator=(const debug_new_recorder&); -}; - -/** - * Counter class for on-exit leakage check. - * - * This technique is learnt from The C++ Programming Language by - * Bjarne Stroustup. - */ -class debug_new_counter -{ - static int _S_count; -public: - debug_new_counter(); - ~debug_new_counter(); -}; -/** Counting object for each file including debug_new.h. */ -static debug_new_counter __debug_new_count; - -NVWA_NAMESPACE_END - -#endif // NVWA_DEBUG_NEW_H diff --git a/src/debug/fast_mutex.h b/src/debug/fast_mutex.h deleted file mode 100644 index 39b5fcdc2..000000000 --- a/src/debug/fast_mutex.h +++ /dev/null @@ -1,427 +0,0 @@ -// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -// vim:tabstop=4:shiftwidth=4:expandtab: - -/* - * Copyright (C) 2004-2015 Wu Yongwei - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute - * it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgement in the product - * documentation would be appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source - * distribution. - * - * This file is part of Stones of Nvwa: - * http://sourceforge.net/projects/nvwa - * - */ - -/** - * @file fast_mutex.h - * - * A fast mutex implementation for POSIX, Win32, and modern C++. - * - * @date 2015-05-19 - */ - -#ifndef NVWA_FAST_MUTEX_H -#define NVWA_FAST_MUTEX_H - -#include "debug/_nvwa.h" // NVWA_NAMESPACE_* -#include "debug/c++11.h" // HAVE_CXX11_MUTEX - -# if !defined(_NOTHREADS) -# if !defined(NVWA_USE_CXX11_MUTEX) && HAVE_CXX11_MUTEX != 0 && \ - !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS) && \ - !defined(NVWA_PTHREADS) && !defined(NVWA_NOTHREADS) && \ - defined(_WIN32) && defined(_MT) && \ - (!defined(_MSC_VER) || defined(_DLL)) -// Prefer using std::mutex on Windows to avoid the namespace -// pollution caused by . However, MSVC has a re-entry -// issue with its std::mutex implementation, and std::mutex should -// not be used unless /MD or /MDd is used. For more information, -// check out: -// -// https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag -// http://stackoverflow.com/questions/14319344/stdmutex-lock-hangs-when-overriding-the-new-operator -// -# define NVWA_USE_CXX11_MUTEX 1 -# endif - -# if !defined(_WIN32THREADS) && \ - (defined(_WIN32) && defined(_MT)) -// Automatically use _WIN32THREADS when specifying -MT/-MD in MSVC, -// or -mthreads in MinGW GCC. -# define _WIN32THREADS -# elif !defined(_PTHREADS) && \ - defined(_REENTRANT) -// Automatically use _PTHREADS when specifying -pthread in GCC or Clang. -# define _PTHREADS -# endif -# endif - -# ifndef NVWA_USE_CXX11_MUTEX -# if HAVE_CXX11_MUTEX != 0 && \ - !defined(_NOTHREADS) && !defined(NVWA_NOTHREADS) && \ - !defined(_PTHREADS) && !defined(NVWA_PTHREADS) && \ - !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS) -# define NVWA_USE_CXX11_MUTEX 1 -# else -# define NVWA_USE_CXX11_MUTEX 0 -# endif -# endif - -# if !defined(_PTHREADS) && !defined(_WIN32THREADS) && \ - !defined(_NOTHREADS) && NVWA_USE_CXX11_MUTEX == 0 -# define _NOTHREADS -# endif - -# if defined(_NOTHREADS) -# if defined(_PTHREADS) || defined(_WIN32THREADS) || \ - NVWA_USE_CXX11_MUTEX != 0 -# undef _NOTHREADS -# error "Cannot define multi-threaded mode with -D_NOTHREADS" -# endif -# endif - -# if defined(__MINGW32__) && defined(_WIN32THREADS) && !defined(_MT) -# error "Be sure to specify -mthreads with -D_WIN32THREADS" -# endif - -// With all the heuristics above, things may still go wrong, maybe even -// due to a specific inclusion order. So they may be overridden by -// manually defining the NVWA_* macros below. -# if NVWA_USE_CXX11_MUTEX == 0 && \ - !defined(NVWA_WIN32THREADS) && \ - !defined(NVWA_PTHREADS) && \ - !defined(NVWA_NOTHREADS) -// _WIN32THREADS takes precedence, as some C++ libraries have _PTHREADS -// defined even on Win32 platforms. -# if defined(_WIN32THREADS) -# define NVWA_WIN32THREADS -# elif defined(_PTHREADS) -# define NVWA_PTHREADS -# else -# define NVWA_NOTHREADS -# endif -# endif - -# ifndef _FAST_MUTEX_CHECK_INITIALIZATION -/** - * Macro to control whether to check for initialization status for each - * lock/unlock operation. Defining it to a non-zero value will enable - * the check, so that the construction/destruction of a static object - * using a static fast_mutex not yet constructed or already destroyed - * will work (with lock/unlock operations ignored). Defining it to zero - * will disable to check. - */ -# define _FAST_MUTEX_CHECK_INITIALIZATION 1 -# endif - -# ifdef _DEBUG -# include -# include -/** Macro for fast_mutex assertions. Real version (for debug mode). */ -# define _FAST_MUTEX_ASSERT(_Expr, _Msg) \ - if (!(_Expr)) { \ - fprintf(stderr, "fast_mutex::%s\n", _Msg); \ - abort(); \ - } -# else -/** Macro for fast_mutex assertions. Fake version (for release mode). */ -# define _FAST_MUTEX_ASSERT(_Expr, _Msg) \ - ((void)0) -# endif - -# if NVWA_USE_CXX11_MUTEX != 0 -# include -NVWA_NAMESPACE_BEGIN -/** - * Macro alias to `volatile' semantics. Here it is truly volatile since - * it is in a multi-threaded (C++11) environment. - */ -# define __VOLATILE volatile - /** - * Class for non-reentrant fast mutexes. This is the implementation - * using the C++11 mutex. - */ - class fast_mutex - { - std::mutex _M_mtx_impl; -# if _FAST_MUTEX_CHECK_INITIALIZATION - bool _M_initialized; -# endif -# ifdef _DEBUG - bool _M_locked; -# endif - public: - fast_mutex() -# ifdef _DEBUG - : _M_locked(false) -# endif - { -# if _FAST_MUTEX_CHECK_INITIALIZATION - _M_initialized = true; -# endif - } - ~fast_mutex() - { - _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); -# if _FAST_MUTEX_CHECK_INITIALIZATION - _M_initialized = false; -# endif - } - void lock() - { -# if _FAST_MUTEX_CHECK_INITIALIZATION - if (!_M_initialized) - return; -# endif - _M_mtx_impl.lock(); -# ifdef _DEBUG - _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); - _M_locked = true; -# endif - } - void unlock() - { -# if _FAST_MUTEX_CHECK_INITIALIZATION - if (!_M_initialized) - return; -# endif -# ifdef _DEBUG - _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); - _M_locked = false; -# endif - _M_mtx_impl.unlock(); - } - private: - fast_mutex(const fast_mutex&); - fast_mutex& operator=(const fast_mutex&); - }; -NVWA_NAMESPACE_END -# elif defined(NVWA_PTHREADS) -# include -NVWA_NAMESPACE_BEGIN -/** - * Macro alias to `volatile' semantics. Here it is truly volatile since - * it is in a multi-threaded (POSIX threads) environment. - */ -# define __VOLATILE volatile - /** - * Class for non-reentrant fast mutexes. This is the implementation - * for POSIX threads. - */ - class fast_mutex - { - pthread_mutex_t _M_mtx_impl; -# if _FAST_MUTEX_CHECK_INITIALIZATION - bool _M_initialized; -# endif -# ifdef _DEBUG - bool _M_locked; -# endif - public: - fast_mutex() -# ifdef _DEBUG - : _M_locked(false) -# endif - { - ::pthread_mutex_init(&_M_mtx_impl, _NULLPTR); -# if _FAST_MUTEX_CHECK_INITIALIZATION - _M_initialized = true; -# endif - } - ~fast_mutex() - { - _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); -# if _FAST_MUTEX_CHECK_INITIALIZATION - _M_initialized = false; -# endif - ::pthread_mutex_destroy(&_M_mtx_impl); - } - void lock() - { -# if _FAST_MUTEX_CHECK_INITIALIZATION - if (!_M_initialized) - return; -# endif - ::pthread_mutex_lock(&_M_mtx_impl); -# ifdef _DEBUG - // The following assertion should _always_ be true for a - // real `fast' pthread_mutex. However, this assertion can - // help sometimes, when people forget to use `-lpthread' and - // glibc provides an empty implementation. Having this - // assertion is also more consistent. - _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); - _M_locked = true; -# endif - } - void unlock() - { -# if _FAST_MUTEX_CHECK_INITIALIZATION - if (!_M_initialized) - return; -# endif -# ifdef _DEBUG - _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); - _M_locked = false; -# endif - ::pthread_mutex_unlock(&_M_mtx_impl); - } - private: - fast_mutex(const fast_mutex&); - fast_mutex& operator=(const fast_mutex&); - }; -NVWA_NAMESPACE_END -# elif defined(NVWA_WIN32THREADS) -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif /* WIN32_LEAN_AND_MEAN */ -# include -NVWA_NAMESPACE_BEGIN -/** - * Macro alias to `volatile' semantics. Here it is truly volatile since - * it is in a multi-threaded (Win32 threads) environment. - */ -# define __VOLATILE volatile - /** - * Class for non-reentrant fast mutexes. This is the implementation - * for Win32 threads. - */ - class fast_mutex - { - CRITICAL_SECTION _M_mtx_impl; -# if _FAST_MUTEX_CHECK_INITIALIZATION - bool _M_initialized; -# endif -# ifdef _DEBUG - bool _M_locked; -# endif - public: - fast_mutex() -# ifdef _DEBUG - : _M_locked(false) -# endif - { - ::InitializeCriticalSection(&_M_mtx_impl); -# if _FAST_MUTEX_CHECK_INITIALIZATION - _M_initialized = true; -# endif - } - ~fast_mutex() - { - _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); -# if _FAST_MUTEX_CHECK_INITIALIZATION - _M_initialized = false; -# endif - ::DeleteCriticalSection(&_M_mtx_impl); - } - void lock() - { -# if _FAST_MUTEX_CHECK_INITIALIZATION - if (!_M_initialized) - return; -# endif - ::EnterCriticalSection(&_M_mtx_impl); -# ifdef _DEBUG - _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); - _M_locked = true; -# endif - } - void unlock() - { -# if _FAST_MUTEX_CHECK_INITIALIZATION - if (!_M_initialized) - return; -# endif -# ifdef _DEBUG - _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); - _M_locked = false; -# endif - ::LeaveCriticalSection(&_M_mtx_impl); - } - private: - fast_mutex(const fast_mutex&); - fast_mutex& operator=(const fast_mutex&); - }; -NVWA_NAMESPACE_END -# elif defined(NVWA_NOTHREADS) -NVWA_NAMESPACE_BEGIN -/** - * Macro alias to `volatile' semantics. Here it is not truly volatile - * since it is in a single-threaded environment. - */ -# define __VOLATILE - /** - * Class for non-reentrant fast mutexes. This is the null - * implementation for single-threaded environments. - */ - class fast_mutex - { -# ifdef _DEBUG - bool _M_locked; -# endif - public: - fast_mutex() -# ifdef _DEBUG - : _M_locked(false) -# endif - { - } - ~fast_mutex() - { - _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); - } - void lock() - { -# ifdef _DEBUG - _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); - _M_locked = true; -# endif - } - void unlock() - { -# ifdef _DEBUG - _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); - _M_locked = false; -# endif - } - private: - fast_mutex(const fast_mutex&); - fast_mutex& operator=(const fast_mutex&); - }; -NVWA_NAMESPACE_END -# endif // Definition of class fast_mutex - -NVWA_NAMESPACE_BEGIN -/** RAII lock class for fast_mutex. */ -class fast_mutex_autolock -{ - fast_mutex& _M_mtx; -public: - explicit fast_mutex_autolock(fast_mutex& mtx) : _M_mtx(mtx) - { - _M_mtx.lock(); - } - ~fast_mutex_autolock() - { - _M_mtx.unlock(); - } -private: - fast_mutex_autolock(const fast_mutex_autolock&); - fast_mutex_autolock& operator=(const fast_mutex_autolock&); -}; -NVWA_NAMESPACE_END - -#endif // NVWA_FAST_MUTEX_H diff --git a/src/debug/nvwa/_nvwa.h b/src/debug/nvwa/_nvwa.h new file mode 100644 index 000000000..096893c59 --- /dev/null +++ b/src/debug/nvwa/_nvwa.h @@ -0,0 +1,112 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2013-2015 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file _nvwa.h + * + * Common definitions for preprocessing. + * + * @date 2015-10-28 + */ + +#ifndef NVWA_NVWA_H +#define NVWA_NVWA_H + +/** + * @namespace nvwa + * Namespace of the nvwa project. Most functions and global variables + * are defined in this namespace. + */ + +#ifndef NVWA_USE_NAMESPACE +#ifdef __cplusplus +#define NVWA_USE_NAMESPACE 1 +#else +#define NVWA_USE_NAMESPACE 0 +#endif // __cplusplus +#endif // NVWA_USE_NAMESPACE + +#if NVWA_USE_NAMESPACE +#define NVWA_NAMESPACE_BEGIN namespace nvwa { +#define NVWA_NAMESPACE_END } +#define NVWA nvwa +#else // NVWA_USE_NAMESPACE +#define NVWA_NAMESPACE_BEGIN +#define NVWA_NAMESPACE_END +#define NVWA +#endif // NVWA_USE_NAMESPACE + +#ifndef NVWA_APPLE +#if defined(__APPLE__) && defined(__MACH__) +#define NVWA_APPLE 1 +#else +#define NVWA_APPLE 0 +#endif +#endif // NVWA_APPLE + +#ifndef NVWA_CYGWIN +#if defined(__CYGWIN__) +#define NVWA_CYGWIN 1 +#else +#define NVWA_CYGWIN 0 +#endif +#endif // NVWA_CYGWIN + +#ifndef NVWA_LINUX +#if defined(__linux__) || defined(__linux) +#define NVWA_LINUX 1 +#else +#define NVWA_LINUX 0 +#endif +#endif // NVWA_LINUX + +#ifndef NVWA_UNIX +#if defined(__unix__) || defined(__unix) || NVWA_APPLE +#define NVWA_UNIX 1 +#else +#define NVWA_UNIX 0 +#endif +#endif // NVWA_UNIX + +#ifndef NVWA_WIN32 +#if defined(_WIN32) +#define NVWA_WIN32 1 +#else +#define NVWA_WIN32 0 +#endif +#endif // NVWA_WIN32 + +#ifndef NVWA_WINDOWS +#if NVWA_CYGWIN || NVWA_WIN32 +#define NVWA_WINDOWS 1 +#else +#define NVWA_WINDOWS 0 +#endif +#endif // NVWA_WINDOWS + +#endif // NVWA_NVWA_H diff --git a/src/debug/nvwa/c++11.h b/src/debug/nvwa/c++11.h new file mode 100644 index 000000000..8d8e2d8af --- /dev/null +++ b/src/debug/nvwa/c++11.h @@ -0,0 +1,340 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2013-2017 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file c++11.h + * + * C++11 feature detection macros and workarounds. + * + * @date 2017-04-03 + */ + +#ifndef NVWA_CXX11_H +#define NVWA_CXX11_H + +// Only Clang provides these macros; they need to be defined as follows +// to get a valid expression in preprocessing by other compilers. +#ifndef __has_extension +#define __has_extension(x) 0 +#endif +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +#ifndef __has_include +#define __has_include(x) 0 +#endif + +// Detect whether C++11 mode is on (for GCC and Clang). MSVC does not +// have a special C++11 mode, so it is always on for Visual C++ 2010 and +// later. +#if __cplusplus >= 201103L || \ + defined(__GXX_EXPERIMENTAL_CXX0X__) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) +#define NVWA_CXX11_MODE 1 +#else +#define NVWA_CXX11_MODE 0 +#endif + + +/* Feature checks */ + +#if !defined(HAVE_CXX11_ATOMIC) +#if NVWA_CXX11_MODE && \ + ((__has_include() && !defined(__MINGW32__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405) || \ + defined(__clang__)) && \ + (!defined(__MINGW32__) || defined(_POSIX_THREADS)))) +// Note: MinGW GCC, unless built with POSIX threads (as in +// MinGW-builds), does not support atomics as of 4.8. +#define HAVE_CXX11_ATOMIC 1 +#else +#define HAVE_CXX11_ATOMIC 0 +#endif +#endif + +#if !defined(HAVE_CXX11_AUTO_TYPE) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_auto_type) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404)) +#define HAVE_CXX11_AUTO_TYPE 1 +#else +#define HAVE_CXX11_AUTO_TYPE 0 +#endif +#endif + +#if !defined(HAVE_CXX11_DELETED_FUNCTION) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_deleted_functions) || \ + (defined(_MSC_VER) && _MSC_VER >= 1800) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404)) +#define HAVE_CXX11_DELETED_FUNCTION 1 +#else +#define HAVE_CXX11_DELETED_FUNCTION 0 +#endif +#endif + +#if !defined(HAVE_CXX11_EXPLICIT_CONVERSION) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_explicit_conversions) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405)) +#define HAVE_CXX11_EXPLICIT_CONVERSION 1 +#else +#define HAVE_CXX11_EXPLICIT_CONVERSION 0 +#endif +#endif + +#if !defined(HAVE_CXX11_FINAL) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_override_control) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 407)) +#define HAVE_CXX11_FINAL 1 +#else +#define HAVE_CXX11_FINAL 0 +#endif +#endif + +#if !defined(HAVE_CXX11_FUTURE) +#if NVWA_CXX11_MODE && \ + ((__has_include() && !defined(__MINGW32__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405) || \ + defined(__clang__)) && \ + (!defined(__MINGW32__) || defined(_POSIX_THREADS)))) +// Note: MinGW GCC, unless built with POSIX threads (as in +// MinGW-builds), does not support futures as of 4.8. +#define HAVE_CXX11_FUTURE 1 +#else +#define HAVE_CXX11_FUTURE 0 +#endif +#endif + +#if !defined(HAVE_CXX11_GENERALIZED_INITIALIZER) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_generalized_initializers) || \ + (defined(_MSC_VER) && _MSC_VER >= 1800) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404)) +#define HAVE_CXX11_GENERALIZED_INITIALIZER 1 +#else +#define HAVE_CXX11_GENERALIZED_INITIALIZER 0 +#endif +#endif + +#if !defined(HAVE_CXX11_LAMBDA) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_lambdas) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405)) +#define HAVE_CXX11_LAMBDA 1 +#else +#define HAVE_CXX11_LAMBDA 0 +#endif +#endif + +#if !defined(HAVE_CXX11_MUTEX) +#if NVWA_CXX11_MODE && \ + ((__has_include() && !defined(__MINGW32__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403) || \ + defined(__clang__)) && \ + (!defined(__MINGW32__) || defined(_POSIX_THREADS)))) +// Note: MinGW GCC, unless built with POSIX threads (as in +// MinGW-builds), does not support std::mutex as of 4.8. +#define HAVE_CXX11_MUTEX 1 +#else +#define HAVE_CXX11_MUTEX 0 +#endif +#endif + +#if !defined(HAVE_CXX11_NOEXCEPT) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_noexcept) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406)) +#define HAVE_CXX11_NOEXCEPT 1 +#else +#define HAVE_CXX11_NOEXCEPT 0 +#endif +#endif + +#if !defined(HAVE_CXX11_NULLPTR) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_nullptr) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406)) +#define HAVE_CXX11_NULLPTR 1 +#else +#define HAVE_CXX11_NULLPTR 0 +#endif +#endif + +#if !defined(HAVE_CXX11_OVERRIDE) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_override_control) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 407)) +#define HAVE_CXX11_OVERRIDE 1 +#else +#define HAVE_CXX11_OVERRIDE 0 +#endif +#endif + +#if !defined(HAVE_CXX11_RANGE_FOR) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_range_for) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406)) +#define HAVE_CXX11_RANGE_FOR 1 +#else +#define HAVE_CXX11_RANGE_FOR 0 +#endif +#endif + +#if !defined(HAVE_CXX11_RVALUE_REFERENCE) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_rvalue_references) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405)) +#define HAVE_CXX11_RVALUE_REFERENCE 1 +#else +#define HAVE_CXX11_RVALUE_REFERENCE 0 +#endif +#endif + +#if !defined(HAVE_CXX11_STATIC_ASSERT) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_static_assert) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403)) +#define HAVE_CXX11_STATIC_ASSERT 1 +#else +#define HAVE_CXX11_STATIC_ASSERT 0 +#endif +#endif + +#if !defined(HAVE_CXX11_THREAD) +#if NVWA_CXX11_MODE && \ + ((__has_include() && !defined(__MINGW32__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (((defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 404) || \ + defined(__clang__)) && \ + (!defined(__MINGW32__) || defined(_POSIX_THREADS)))) +// Note: MinGW GCC, unless built with POSIX threads (as in +// MinGW-builds), does not support std::thread as of 4.8. +#define HAVE_CXX11_THREAD 1 +#else +#define HAVE_CXX11_THREAD 0 +#endif +#endif + +#if !defined(HAVE_CXX11_THREAD_LOCAL) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_thread_local) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 408)) +#define HAVE_CXX11_THREAD_LOCAL 1 +#else +#define HAVE_CXX11_THREAD_LOCAL 0 +#endif +#endif + +#if !defined(HAVE_CXX11_TYPE_TRAITS) +#if NVWA_CXX11_MODE && \ + (__has_include() || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 403)) +#define HAVE_CXX11_TYPE_TRAITS 1 +#else +#define HAVE_CXX11_TYPE_TRAITS 0 +#endif +#endif + +#if !defined(HAVE_CXX11_UNICODE_LITERAL) +#if NVWA_CXX11_MODE && \ + (__has_feature(cxx_unicode_literals) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 405)) +#define HAVE_CXX11_UNICODE_LITERAL 1 +#else +#define HAVE_CXX11_UNICODE_LITERAL 0 +#endif +#endif + + +/* Workarounds */ + +#if HAVE_CXX11_DELETED_FUNCTION +#define _DELETED = delete +#else +#define _DELETED +#endif + +#if HAVE_CXX11_FINAL +#define _FINAL final +#else +#define _FINAL +#endif + +#if HAVE_CXX11_OVERRIDE +#define _OVERRIDE override +#else +#define _OVERRIDE +#endif + +#if HAVE_CXX11_NOEXCEPT +#define _NOEXCEPT noexcept +#define _NOEXCEPT_(x) noexcept(x) +#else +#ifdef _MSC_VER +#define _NOEXCEPT throw () +#else +#define _NOEXCEPT throw() +#endif +#define _NOEXCEPT_(x) +#endif + +#if HAVE_CXX11_NULLPTR +#define _NULLPTR nullptr +#else +#define _NULLPTR NULL +#endif + +#if HAVE_CXX11_THREAD_LOCAL +#define _THREAD_LOCAL thread_local +#else +#ifdef _MSC_VER +#define _THREAD_LOCAL __declspec(thread) +#else +#define _THREAD_LOCAL __thread +#endif +#endif + +#endif // NVWA_CXX11_H diff --git a/src/debug/nvwa/debug_new.cpp b/src/debug/nvwa/debug_new.cpp new file mode 100644 index 000000000..1a59353a3 --- /dev/null +++ b/src/debug/nvwa/debug_new.cpp @@ -0,0 +1,1197 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2016 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file debug_new.cpp + * + * Implementation of debug versions of new and delete to check leakage. + * + * @date 2016-10-14 + */ + +#include "localconsts.h" + +PRAGMACLANG6GCC(GCC diagnostic push) +PRAGMACLANG6GCC(GCC diagnostic ignored "-Wold-style-cast") + +PRAGMA45(GCC diagnostic push) +PRAGMA45(GCC diagnostic ignored "-Wcast-qual") + +#include // std::bad_alloc/nothrow_t +#include // assert +#include // fprintf/stderr +#include // abort +#include // strcpy/strncpy/sprintf +#include "debug/_nvwa.h" // NVWA macros + +#if NVWA_UNIX +#include // alloca +#endif +#if NVWA_WIN32 +#include // alloca +#endif + +#if NVWA_LINUX || NVWA_APPLE +#include // backtrace +#endif + +#if NVWA_WINDOWS +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include // CaptureStackBackTrace +#endif + +#include "debug/c++11.h" // _NOEXCEPT/_NULLPTR +#include "debug/fast_mutex.h" // nvwa::fast_mutex +#include "debug/static_assert.h" // STATIC_ASSERT + +#undef _DEBUG_NEW_EMULATE_MALLOC +#undef _DEBUG_NEW_REDEFINE_NEW +/** + * Macro to indicate whether redefinition of \c new is wanted. Here it + * is defined to \c 0 to disable the redefinition of \c new. + */ +#define _DEBUG_NEW_REDEFINE_NEW 0 +#include "debug/nvwa/debug_new.h" + +#if !_FAST_MUTEX_CHECK_INITIALIZATION && !defined(_NOTHREADS) +#error "_FAST_MUTEX_CHECK_INITIALIZATION not set: check_leaks may not work" +#endif + +/** + * @def _DEBUG_NEW_ALIGNMENT + * + * The alignment requirement of allocated memory blocks. It must be a + * power of two. + */ +#ifndef _DEBUG_NEW_ALIGNMENT +#define _DEBUG_NEW_ALIGNMENT 16 +#endif + +/** + * @def _DEBUG_NEW_CALLER_ADDRESS + * + * The expression to return the caller address. nvwa#print_position will + * later on use this address to print the position information of memory + * operation points. + */ +#ifndef _DEBUG_NEW_CALLER_ADDRESS +#ifdef __GNUC__ +#define _DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0) +#else +#define _DEBUG_NEW_CALLER_ADDRESS _NULLPTR +#endif +#endif + +/** + * @def _DEBUG_NEW_ERROR_ACTION + * + * The action to take when an error occurs. The default behaviour is to + * call \e abort, unless \c _DEBUG_NEW_ERROR_CRASH is defined, in which + * case a segmentation fault will be triggered instead (which can be + * useful on platforms like Windows that do not generate a core dump + * when \e abort is called). + */ +#ifndef _DEBUG_NEW_ERROR_ACTION +#ifndef _DEBUG_NEW_ERROR_CRASH +#define _DEBUG_NEW_ERROR_ACTION abort() +#else +#define _DEBUG_NEW_ERROR_ACTION do { *((char*)0) = 0; abort(); } while (0) +#endif +#endif + +/** + * @def _DEBUG_NEW_FILENAME_LEN + * + * The length of file name stored if greater than zero. If it is zero, + * only a const char pointer will be stored. Currently the default + * value is non-zero (thus to copy the file name) on non-Windows + * platforms, because I once found that the exit leakage check could not + * access the address of the file name on Linux (in my case, a core dump + * occurred when check_leaks tried to access the file name in a shared + * library after a \c SIGINT). This value makes the size of + * new_ptr_list_t \c 64 on non-Windows 32-bit platforms (w/o stack + * backtrace). + */ +#ifndef _DEBUG_NEW_FILENAME_LEN +#if NVWA_WINDOWS +#define _DEBUG_NEW_FILENAME_LEN 0 +#else +#define _DEBUG_NEW_FILENAME_LEN 44 +#endif +#endif + +/** + * @def _DEBUG_NEW_PROGNAME + * + * The program (executable) name to be set at compile time. It is + * better to assign the full program path to nvwa#new_progname in \e main + * (at run time) than to use this (compile-time) macro, but this macro + * serves well as a quick hack. Note also that double quotation marks + * need to be used around the program name, i.e., one should specify a + * command-line option like -D_DEBUG_NEW_PROGNAME=\\"a.out\" + * in \e bash, or -D_DEBUG_NEW_PROGNAME=\\"a.exe\" in the + * Windows command prompt. + */ +#ifndef _DEBUG_NEW_PROGNAME +#define _DEBUG_NEW_PROGNAME _NULLPTR +#endif + +/** + * @def _DEBUG_NEW_REMEMBER_STACK_TRACE + * + * Macro to indicate whether stack traces of allocations should be + * included in NVWA allocation information and be printed while leaks + * are reported. Useful to pinpoint leaks caused by strdup and other + * custom allocation functions. It is also very helpful in filtering + * out false positives caused by internal STL/C runtime operations. It + * is off by default because it is quite memory heavy. Set it to \c 1 + * to make all allocations have stack trace attached, or set to \c 2 to + * make only allocations that lack calling source code information (file + * and line) have the stack trace attached. + */ +#ifndef _DEBUG_NEW_REMEMBER_STACK_TRACE +#define _DEBUG_NEW_REMEMBER_STACK_TRACE 0 +#endif + +/** + * @def _DEBUG_NEW_STD_OPER_NEW + * + * Macro to indicate whether the standard-conformant behaviour of + * operator new is wanted. It is on by default now, but + * the user may set it to \c 0 to revert to the old behaviour. + */ +#ifndef _DEBUG_NEW_STD_OPER_NEW +#define _DEBUG_NEW_STD_OPER_NEW 1 +#endif + +/** + * @def _DEBUG_NEW_TAILCHECK + * + * Macro to indicate whether a writing-past-end check will be performed. + * Define it to a positive integer as the number of padding bytes at the + * end of a memory block for checking. + */ +#ifndef _DEBUG_NEW_TAILCHECK +#define _DEBUG_NEW_TAILCHECK 0 +#endif + +/** + * @def _DEBUG_NEW_TAILCHECK_CHAR + * + * Value of the padding bytes at the end of a memory block. + */ +//#ifndef _DEBUG_NEW_TAILCHECK_CHAR +//#define _DEBUG_NEW_TAILCHECK_CHAR 0xCC +//#endif + +/** + * @def _DEBUG_NEW_USE_ADDR2LINE + * + * Whether to use \e addr2line to convert a caller address to file/line + * information. Defining it to a non-zero value will enable the + * conversion (automatically done if GCC is detected). Defining it to + * zero will disable the conversion. + */ +#ifndef _DEBUG_NEW_USE_ADDR2LINE +#ifdef __GNUC__ +#define _DEBUG_NEW_USE_ADDR2LINE 1 +#else +#define _DEBUG_NEW_USE_ADDR2LINE 0 +#endif +#endif + +#ifdef _MSC_VER +#pragma warning(disable: 4074) // #pragma init_seg(compiler) used +#pragma warning(disable: 4290) // C++ exception specification ignored +#if _MSC_VER >= 1400 // Visual Studio 2005 or later +#pragma warning(disable: 4996) // Use the `unsafe' strncpy +#endif +#pragma init_seg(compiler) +#endif + +/** + * Gets the aligned value of memory block size. + */ +#define ALIGN(s) \ + (((s) + _DEBUG_NEW_ALIGNMENT - 1) & ~(_DEBUG_NEW_ALIGNMENT - 1)) + +NVWA_NAMESPACE_BEGIN + +/** + * The platform memory alignment. The current value works well in + * platforms I have tested: Windows XP, Windows 7 x64, and Mac OS X + * Leopard. It may be smaller than the real alignment, but must be + * bigger than \c sizeof(size_t) for it work. nvwa#debug_new_recorder + * uses it to detect misaligned pointer returned by `new + * NonPODType[size]'. + */ +const size_t PLATFORM_MEM_ALIGNMENT = sizeof(size_t) * 2; + +/** + * Structure to store the position information where \c new occurs. + */ +struct new_ptr_list_t +{ + new_ptr_list_t* next; ///< Pointer to the next memory block + new_ptr_list_t* prev; ///< Pointer to the previous memory block + size_t size; ///< Size of the memory block + union + { +#if _DEBUG_NEW_FILENAME_LEN == 0 + const char* file; ///< Pointer to the file name of the caller +#else + char file[_DEBUG_NEW_FILENAME_LEN]; ///< File name of the caller +#endif + void* addr; ///< Address of the caller to \e new + }; + unsigned line :31; ///< Line number of the caller; or \c 0 + unsigned is_array:1; ///< Non-zero iff new[] is used +#if _DEBUG_NEW_REMEMBER_STACK_TRACE + void** stacktrace; ///< Pointer to stack trace information +#endif + unsigned magic; ///< Magic number for error detection +}; + +/** + * Definition of the constant magic number used for error detection. + */ +static const unsigned DEBUG_NEW_MAGIC = 0x4442474E; + +/** + * The extra memory allocated by operator new. + */ +static const int ALIGNED_LIST_ITEM_SIZE = ALIGN(sizeof(new_ptr_list_t)); + +/** + * List of all new'd pointers. + */ +static new_ptr_list_t new_ptr_list = { + &new_ptr_list, + &new_ptr_list, + 0, + { +#if _DEBUG_NEW_FILENAME_LEN == 0 + _NULLPTR +#else + "" +#endif + }, + 0, + 0, +#if _DEBUG_NEW_REMEMBER_STACK_TRACE + _NULLPTR, +#endif + DEBUG_NEW_MAGIC +}; + +/** + * The mutex guard to protect simultaneous access to the pointer list. + */ +static fast_mutex new_ptr_lock; + +/** + * The mutex guard to protect simultaneous output to #new_output_fp. + */ +static fast_mutex new_output_lock; + +/** + * Total memory allocated in bytes. + */ +static size_t total_mem_alloc = 0; + +/** + * Flag to control whether nvwa#check_leaks will be automatically called + * on program exit. + */ +bool new_autocheck_flag = true; + +/** + * Flag to control whether verbose messages are output. + */ +bool new_verbose_flag = false; + +/** + * Pointer to the output stream. The default output is \e stderr, and + * one may change it to a user stream if needed (say, #new_verbose_flag + * is \c true and there are a lot of (de)allocations). + */ +FILE* new_output_fp = stderr; + +/** + * Pointer to the program name. Its initial value is the macro + * #_DEBUG_NEW_PROGNAME. You should try to assign the program path to + * it early in your application. Assigning argv[0] to it + * in \e main is one way. If you use \e bash or \e ksh (or similar), + * the following statement is probably what you want: + * `new_progname = getenv("_");'. + */ +const char* new_progname = _DEBUG_NEW_PROGNAME; + +/** + * Pointer to the callback used to print the stack backtrace in case of + * a memory problem. A null value causes the default stack trace + * printing routine to be used. + */ +stacktrace_print_callback_t stacktrace_print_callback = _NULLPTR; + +/** + * Pointer to the callback used to filter out false positives from leak + * reports. A null value means the lack of filtering. + */ +leak_whitelist_callback_t leak_whitelist_callback = _NULLPTR; + +#if _DEBUG_NEW_USE_ADDR2LINE +/** + * Tries printing the position information from an instruction address. + * This is the version that uses \e addr2line. + * + * @param addr the instruction address to convert and print + * @return \c true if the address is converted successfully (and + * the result is printed); \c false if no useful + * information is got (and nothing is printed) + */ +static bool print_position_from_addr(const void* addr) +{ + static const void* last_addr = _NULLPTR; + static char last_info[256] = ""; + if (addr == last_addr) + { + if (last_info[0] == '\0') + return false; + fprintf(new_output_fp, "%s", last_info); + return true; + } + if (new_progname) + { +#if NVWA_APPLE + const char addr2line_cmd[] = "atos -o "; +#else + const char addr2line_cmd[] = "addr2line -e "; +#endif + +#if NVWA_WINDOWS + const int exeext_len = 4; +#else + const int exeext_len = 0; +#endif + +#if NVWA_UNIX && !NVWA_CYGWIN + const char ignore_err[] = " 2>/dev/null"; +#elif NVWA_CYGWIN || \ + (NVWA_WIN32 && defined(WINVER) && WINVER >= 0x0500) + const char ignore_err[] = " 2>nul"; +#else + const char ignore_err[] = ""; +#endif + char* cmd = (char*)alloca(strlen(new_progname) + + exeext_len + + sizeof addr2line_cmd - 1 + + sizeof ignore_err - 1 + + sizeof(void*) * 2 + + 4 /* SP + "0x" + null */); + strcpy(cmd, addr2line_cmd); + strcpy(cmd + sizeof addr2line_cmd - 1, new_progname); + size_t len = strlen(cmd); +#if NVWA_WINDOWS + if (len <= 4 + || (strcmp(cmd + len - 4, ".exe") != 0 && + strcmp(cmd + len - 4, ".EXE") != 0)) + { + strcpy(cmd + len, ".exe"); + len += 4; + } +#endif + sprintf(cmd + len, " %p%s", addr, ignore_err); + FILE* fp = popen(cmd, "r"); + if (fp) + { + char buffer[sizeof last_info] = ""; + len = 0; + if (fgets(buffer, sizeof buffer, fp)) + { + len = strlen(buffer); + if (buffer[len - 1] == '\n') + buffer[--len] = '\0'; + } + int res = pclose(fp); + // Display the file/line information only if the command + // is executed successfully and the output points to a + // valid position, but the result will be cached if only + // the command is executed successfully. + if (res == 0 && len > 0) + { + last_addr = addr; + if (buffer[len - 1] == '0' && buffer[len - 2] == ':') + last_info[0] = '\0'; + else + { + fprintf(new_output_fp, "%s", buffer); + strcpy(last_info, buffer); + return true; + } + } + } + } + return false; +} +#else +/** + * Tries printing the position information from an instruction address. + * This is the stub version that does nothing at all. + * + * @return \c false always + */ +static bool print_position_from_addr(const void*) +{ + return false; +} +#endif // _DEBUG_NEW_USE_ADDR2LINE + +/** + * Prints the position information of a memory operation point. When \c + * _DEBUG_NEW_USE_ADDR2LINE is defined to a non-zero value, this + * function will try to convert a given caller address to file/line + * information with \e addr2line. + * + * @param ptr source file name if \e line is non-zero; caller address + * otherwise + * @param line source line number if non-zero; indication that \e ptr + * is the caller address otherwise + */ +static void print_position(const void* ptr, int line) +{ + if (line != 0) // Is file/line information present? + { + fprintf(new_output_fp, "%s:%d", (const char*)ptr, line); + } + else if (ptr != _NULLPTR) // Is caller address present? + { + if (!print_position_from_addr(ptr)) // Fail to get source position? + fprintf(new_output_fp, "%p", ptr); + } + else // No information is present + { + fprintf(new_output_fp, ""); + } +} + +#if _DEBUG_NEW_REMEMBER_STACK_TRACE +/** + * Prints the stack backtrace. + * + * When nvwa#stacktrace_print_callback is not null, it is used for + * printing the stacktrace items. Default implementation of call stack + * printing is very spartan—only stack frame pointers are + * printed—but even that output is still useful. Just do address + * lookup in LLDB etc. + * + * @param stacktrace pointer to the stack trace array + */ +static void print_stacktrace(void** stacktrace) +{ + if (stacktrace_print_callback == _NULLPTR) + { + fprintf(new_output_fp, "Stack backtrace:\n"); + for (size_t i = 0; stacktrace[i] != _NULLPTR; ++i) + fprintf(new_output_fp, "%p\n", stacktrace[i]); + } + else + { + stacktrace_print_callback(new_output_fp, stacktrace); + } +} +#endif + +/** + * Checks whether a leak should be ignored. Its runtime performance + * depends on the callback nvwa#leak_whitelist_callback. + * + * @param ptr pointer to a new_ptr_list_t struct + * @return \c true if the leak should be whitelisted; \c false + * otherwise + */ +static bool is_leak_whitelisted(new_ptr_list_t* ptr) +{ + if (leak_whitelist_callback == _NULLPTR) + return false; + + char const* file = ptr->line != 0 ? ptr->file : _NULLPTR; + int line = ptr->line; + void* addr = ptr->line == 0 ? ptr->addr : _NULLPTR; +#if _DEBUG_NEW_REMEMBER_STACK_TRACE + void** stacktrace = ptr->stacktrace; +#else + void** stacktrace = _NULLPTR; +#endif + + return leak_whitelist_callback(file, line, addr, stacktrace); +} + +#if _DEBUG_NEW_TAILCHECK +/** + * Checks whether the padding bytes at the end of a memory block is + * tampered with. + * + * @param ptr pointer to a new_ptr_list_t struct + * @return \c true if the padding bytes are untouched; \c false + * otherwise + */ +static bool check_tail(new_ptr_list_t* ptr) +{ + const unsigned char* const tail_ptr = (unsigned char*)ptr + + ALIGNED_LIST_ITEM_SIZE + ptr->size; + for (int i = 0; i < _DEBUG_NEW_TAILCHECK; ++i) + if (tail_ptr[i] != _DEBUG_NEW_TAILCHECK_CHAR) + return false; + return true; +} +#endif + +/** + * Allocates memory and initializes control data. + * + * @param size size of the required memory block + * @param file null-terminated string of the file name + * @param line line number + * @param is_array boolean value whether this is an array operation + * @return pointer to the user-requested memory area; null if + * memory allocation is not successful + */ +static void* alloc_mem(size_t size, const char* file, int line, bool is_array) +{ + assert(line >= 0); +#if _DEBUG_NEW_TYPE == 1 + STATIC_ASSERT(_DEBUG_NEW_ALIGNMENT >= PLATFORM_MEM_ALIGNMENT, + Alignment_too_small); +#endif + STATIC_ASSERT((_DEBUG_NEW_ALIGNMENT & (_DEBUG_NEW_ALIGNMENT - 1)) == 0, + Alignment_must_be_power_of_two); + STATIC_ASSERT(_DEBUG_NEW_TAILCHECK >= 0, Invalid_tail_check_length); + size_t s = size + ALIGNED_LIST_ITEM_SIZE + _DEBUG_NEW_TAILCHECK; + new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s); + if (ptr == _NULLPTR) + { +#if _DEBUG_NEW_STD_OPER_NEW + return _NULLPTR; +#else + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "Out of memory when allocating %lu bytes\n", + (unsigned long)size); + fflush(new_output_fp); + _DEBUG_NEW_ERROR_ACTION; +#endif + } + void* usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; +#if _DEBUG_NEW_FILENAME_LEN == 0 + ptr->file = file; +#else + if (line) + strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1) + [_DEBUG_NEW_FILENAME_LEN - 1] = '\0'; + else + ptr->addr = (void*)file; +#endif + ptr->line = line; +#if _DEBUG_NEW_REMEMBER_STACK_TRACE + ptr->stacktrace = _NULLPTR; + +#if _DEBUG_NEW_REMEMBER_STACK_TRACE == 2 + if (line == 0) +#endif + { + void* buffer [255]; + size_t buffer_length = sizeof(buffer) / sizeof(*buffer); + +#if NVWA_UNIX + int stacktrace_length = backtrace(buffer, int(buffer_length)); +#endif + +#if NVWA_WINDOWS + USHORT stacktrace_length = CaptureStackBackTrace( + 0, DWORD(buffer_length), buffer, _NULLPTR); +#endif + + size_t stacktrace_size = stacktrace_length * sizeof(void*); + ptr->stacktrace = (void**)malloc(stacktrace_size + sizeof(void*)); + + if (ptr->stacktrace != _NULLPTR) + { + memcpy(ptr->stacktrace, buffer, stacktrace_size); + ptr->stacktrace[stacktrace_length] = _NULLPTR; + } + } +#endif + ptr->is_array = is_array; + ptr->size = size; + ptr->magic = DEBUG_NEW_MAGIC; + { + fast_mutex_autolock lock(new_ptr_lock); + ptr->prev = new_ptr_list.prev; + ptr->next = &new_ptr_list; + new_ptr_list.prev->next = ptr; + new_ptr_list.prev = ptr; + } +#if _DEBUG_NEW_TAILCHECK + memset((char*)usr_ptr + size, _DEBUG_NEW_TAILCHECK_CHAR, + _DEBUG_NEW_TAILCHECK); +#endif + if (new_verbose_flag) + { + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "new%s: allocated %p (size %lu, ", + is_array ? "[]" : "", + usr_ptr, (unsigned long)size); + if (line != 0) + print_position(ptr->file, ptr->line); + else + print_position(ptr->addr, ptr->line); + fprintf(new_output_fp, ")\n"); + } + total_mem_alloc += size; + return usr_ptr; +} + +/** + * Frees memory and adjusts pointers. + * + * @param usr_ptr pointer to the previously allocated memory + * @param addr pointer to the caller + * @param is_array flag indicating whether it is invoked by a + * delete[] call + */ +static void free_pointer(void* usr_ptr, void* addr, bool is_array) +{ + if (usr_ptr == _NULLPTR) + return; + new_ptr_list_t* ptr = + (new_ptr_list_t*)((char*)usr_ptr - ALIGNED_LIST_ITEM_SIZE); + if (ptr->magic != DEBUG_NEW_MAGIC) + { + { + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, "delete%s: invalid pointer %p (", + is_array ? "[]" : "", usr_ptr); + print_position(addr, 0); + fprintf(new_output_fp, ")\n"); + } + check_mem_corruption(); + fflush(new_output_fp); + _DEBUG_NEW_ERROR_ACTION; + } + if (is_array != ptr->is_array) + { + const char* msg; + if (is_array) + msg = "delete[] after new"; + else + msg = "delete after new[]"; + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "%s: pointer %p (size %lu)\n\tat ", + msg, + (char*)ptr + ALIGNED_LIST_ITEM_SIZE, + (unsigned long)ptr->size); + print_position(addr, 0); + fprintf(new_output_fp, "\n\toriginally allocated at "); + if (ptr->line != 0) + print_position(ptr->file, ptr->line); + else + print_position(ptr->addr, ptr->line); + fprintf(new_output_fp, "\n"); + fflush(new_output_fp); + _DEBUG_NEW_ERROR_ACTION; + } +#if _DEBUG_NEW_TAILCHECK + if (!check_tail(ptr)) + { + check_mem_corruption(); + fflush(new_output_fp); + _DEBUG_NEW_ERROR_ACTION; + } +#endif + { + fast_mutex_autolock lock(new_ptr_lock); + total_mem_alloc -= ptr->size; + ptr->magic = 0; + ptr->prev->next = ptr->next; + ptr->next->prev = ptr->prev; + } + if (new_verbose_flag) + { + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "delete%s: freed %p (size %lu, %lu bytes still allocated)\n", + is_array ? "[]" : "", + (char*)ptr + ALIGNED_LIST_ITEM_SIZE, + (unsigned long)ptr->size, (unsigned long)total_mem_alloc); + } +#if _DEBUG_NEW_REMEMBER_STACK_TRACE + free(ptr->stacktrace); +#endif + free(ptr); + return; +} + +/** + * Checks for memory leaks. + * + * @return zero if no leakage is found; the number of leaks otherwise + */ +int check_leaks() +{ + int leak_cnt = 0; + int whitelisted_leak_cnt = 0; + fast_mutex_autolock lock_ptr(new_ptr_lock); + fast_mutex_autolock lock_output(new_output_lock); + new_ptr_list_t* ptr = new_ptr_list.next; + + while (ptr != &new_ptr_list) + { + const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; + if (ptr->magic != DEBUG_NEW_MAGIC) + { + fprintf(new_output_fp, + "warning: heap data corrupt near %p\n", + usr_ptr); + } +#if _DEBUG_NEW_TAILCHECK + if (!check_tail(ptr)) + { + fprintf(new_output_fp, + "warning: overwritten past end of object at %p\n", + usr_ptr); + } +#endif + + if (is_leak_whitelisted(ptr)) + { + ++whitelisted_leak_cnt; + } + else + { + fprintf(new_output_fp, + "Leaked object at %p (size %lu, ", + usr_ptr, + (unsigned long)ptr->size); + + if (ptr->line != 0) + print_position(ptr->file, ptr->line); + else + print_position(ptr->addr, ptr->line); + + fprintf(new_output_fp, ")\n"); + +#if _DEBUG_NEW_REMEMBER_STACK_TRACE + if (ptr->stacktrace != _NULLPTR) + print_stacktrace(ptr->stacktrace); +#endif + } + + ptr = ptr->next; + ++leak_cnt; + } + if (new_verbose_flag || leak_cnt) + { + if (whitelisted_leak_cnt > 0) + { + fprintf(new_output_fp, "*** %d leaks found (%d whitelisted)\n", + leak_cnt, whitelisted_leak_cnt); + } + else + { + fprintf(new_output_fp, "*** %d leaks found\n", leak_cnt); + } + } + + return leak_cnt; +} + +/** + * Checks for heap corruption. + * + * @return zero if no problem is found; the number of found memory + * corruptions otherwise + */ +int check_mem_corruption() +{ + int corrupt_cnt = 0; + fast_mutex_autolock lock_ptr(new_ptr_lock); + fast_mutex_autolock lock_output(new_output_lock); + fprintf(new_output_fp, "*** Checking for memory corruption: START\n"); + for (new_ptr_list_t* ptr = new_ptr_list.next; + ptr != &new_ptr_list; + ptr = ptr->next) + { + const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; + if (ptr->magic == DEBUG_NEW_MAGIC +#if _DEBUG_NEW_TAILCHECK + && check_tail(ptr) +#endif + ) + continue; +#if _DEBUG_NEW_TAILCHECK + if (ptr->magic != DEBUG_NEW_MAGIC) + { +#endif + fprintf(new_output_fp, + "Heap data corrupt near %p (size %lu, ", + usr_ptr, + (unsigned long)ptr->size); +#if _DEBUG_NEW_TAILCHECK + } + else + { + fprintf(new_output_fp, + "Overwritten past end of object at %p (size %lu, ", + usr_ptr, + (unsigned long)ptr->size); + } +#endif + if (ptr->line != 0) + print_position(ptr->file, ptr->line); + else + print_position(ptr->addr, ptr->line); + fprintf(new_output_fp, ")\n"); + +#if _DEBUG_NEW_REMEMBER_STACK_TRACE + if (ptr->stacktrace != _NULLPTR) + print_stacktrace(ptr->stacktrace); +#endif + + ++corrupt_cnt; + } + fprintf(new_output_fp, "*** Checking for memory corruption: %d FOUND\n", + corrupt_cnt); + return corrupt_cnt; +} + +/** + * Processes the allocated memory and inserts file/line informatin. + * It will only be done when it can ensure the memory is allocated by + * one of our operator new variants. + * + * @param usr_ptr pointer returned by a new-expression + */ +void debug_new_recorder::_M_process(void* usr_ptr) +{ + if (usr_ptr == _NULLPTR) + return; + + // In an expression `new NonPODType[size]', the pointer returned is + // not the pointer returned by operator new[], but offset by size_t + // to leave room for the size. It needs to be compensated here. + size_t offset = (char*)usr_ptr - (char*)_NULLPTR; + if (offset % PLATFORM_MEM_ALIGNMENT != 0) { + offset -= sizeof(size_t); + if (offset % PLATFORM_MEM_ALIGNMENT != 0) { + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "warning: memory unaligned; skipping processing (%s:%d)\n", + _M_file, _M_line); + return; + } + usr_ptr = (char*)usr_ptr - sizeof(size_t); + } + + new_ptr_list_t* ptr = + (new_ptr_list_t*)((char*)usr_ptr - ALIGNED_LIST_ITEM_SIZE); + if (ptr->magic != DEBUG_NEW_MAGIC || ptr->line != 0) + { + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "warning: debug_new used with placement new (%s:%d)\n", + _M_file, _M_line); + return; + } + if (new_verbose_flag) { + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "info: pointer %p allocated from %s:%d\n", + usr_ptr, _M_file, _M_line); + } +#if _DEBUG_NEW_FILENAME_LEN == 0 + ptr->file = _M_file; +#else + strncpy(ptr->file, _M_file, _DEBUG_NEW_FILENAME_LEN - 1) + [_DEBUG_NEW_FILENAME_LEN - 1] = '\0'; +#endif + ptr->line = _M_line; +#if _DEBUG_NEW_REMEMBER_STACK_TRACE == 2 + free(ptr->stacktrace); + ptr->stacktrace = _NULLPTR; +#endif +} + +/** + * Count of source files that use debug_new. + */ +int debug_new_counter::_S_count = 0; + +/** + * Constructor to increment the count. + */ +debug_new_counter::debug_new_counter() +{ + ++_S_count; +} + +/** + * Destructor to decrement the count. When the count is zero, + * nvwa#check_leaks will be called. + */ +debug_new_counter::~debug_new_counter() +{ + if (--_S_count == 0 && new_autocheck_flag) + if (check_leaks()) + { + new_verbose_flag = true; +#if defined(__GNUC__) && __GNUC__ == 3 + if (!getenv("GLIBCPP_FORCE_NEW") && !getenv("GLIBCXX_FORCE_NEW")) + fprintf(new_output_fp, +"*** WARNING: GCC 3 is detected, please make sure the environment\n" +" variable GLIBCPP_FORCE_NEW (GCC 3.2 and 3.3) or GLIBCXX_FORCE_NEW\n" +" (GCC 3.4) is defined. Check the README file for details.\n"); +#endif + } +} + +NVWA_NAMESPACE_END + +#if NVWA_USE_NAMESPACE +using namespace nvwa; +#endif // NVWA_USE_NAMESPACE + +/** + * Allocates memory with file/line information. + * + * @param size size of the required memory block + * @param file null-terminated string of the file name + * @param line line number + * @return pointer to the memory allocated; or null if memory is + * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0) + * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1) + */ +void* operator new(size_t size, const char* file, int line) +{ + void* ptr = alloc_mem(size, file, line, false); +#if _DEBUG_NEW_STD_OPER_NEW + if (ptr) + return ptr; + else + throw std::bad_alloc(); +#else + return ptr; +#endif +} + +/** + * Allocates array memory with file/line information. + * + * @param size size of the required memory block + * @param file null-terminated string of the file name + * @param line line number + * @return pointer to the memory allocated; or null if memory is + * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0) + * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1) + */ +void* operator new[](size_t size, const char* file, int line) +{ + void* ptr = alloc_mem(size, file, line, true); +#if _DEBUG_NEW_STD_OPER_NEW + if (ptr) + return ptr; + else + throw std::bad_alloc(); +#else + return ptr; +#endif +} + +/** + * Allocates memory without file/line information. + * + * @param size size of the required memory block + * @return pointer to the memory allocated; or null if memory is + * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0) + * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1) + */ +void* operator new(size_t size) +{ + return operator new(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); +} + +/** + * Allocates array memory without file/line information. + * + * @param size size of the required memory block + * @return pointer to the memory allocated; or null if memory is + * insufficient (#_DEBUG_NEW_STD_OPER_NEW is 0) + * @throw bad_alloc memory is insufficient (#_DEBUG_NEW_STD_OPER_NEW is 1) + */ +void* operator new[](size_t size) +{ + return operator new[](size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); +} + +/** + * Allocates memory with no-throw guarantee. + * + * @param size size of the required memory block + * @return pointer to the memory allocated; or null if memory is + * insufficient + */ +void* operator new(size_t size, const std::nothrow_t&) _NOEXCEPT +{ + return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, false); +} + +/** + * Allocates array memory with no-throw guarantee. + * + * @param size size of the required memory block + * @return pointer to the memory allocated; or null if memory is + * insufficient + */ +void* operator new[](size_t size, const std::nothrow_t&) _NOEXCEPT +{ + return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, true); +} + +/** + * Deallocates memory. + * + * @param ptr pointer to the previously allocated memory + */ +void operator delete(void* ptr) _NOEXCEPT +{ + free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, false); +} + +/** + * Deallocates array memory. + * + * @param ptr pointer to the previously allocated memory + */ +void operator delete[](void* ptr) _NOEXCEPT +{ + free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, true); +} + +#if __cplusplus >= 201402L +// GCC under C++14 wants these definitions + +void operator delete(void* ptr, size_t) _NOEXCEPT +{ + free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, false); +} + +void operator delete[](void* ptr, size_t) _NOEXCEPT +{ + free_pointer(ptr, _DEBUG_NEW_CALLER_ADDRESS, true); +} +#endif + +/** + * Placement deallocation function. For details, please check Section + * 5.3.4 of the C++ 1998 or 2011 Standard. + * + * @param ptr pointer to the previously allocated memory + * @param file null-terminated string of the file name + * @param line line number + * + * @see http://www.csci.csusb.edu/dick/c++std/cd2/expr.html#expr.new + * @see http://wyw.dcweb.cn/leakage.htm + */ +void operator delete(void* ptr, const char* file, int line) _NOEXCEPT +{ + if (new_verbose_flag) + { + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "info: exception thrown on initializing object at %p (", + ptr); + print_position(file, line); + fprintf(new_output_fp, ")\n"); + } + operator delete(ptr); +} + +/** + * Placement deallocation function. For details, please check Section + * 5.3.4 of the C++ 1998 or 2011 Standard. + * + * @param ptr pointer to the previously allocated memory + * @param file null-terminated string of the file name + * @param line line number + */ +void operator delete[](void* ptr, const char* file, int line) _NOEXCEPT +{ + if (new_verbose_flag) + { + fast_mutex_autolock lock(new_output_lock); + fprintf(new_output_fp, + "info: exception thrown on initializing objects at %p (", + ptr); + print_position(file, line); + fprintf(new_output_fp, ")\n"); + } + operator delete[](ptr); +} + +/** + * Placement deallocation function. For details, please check Section + * 5.3.4 of the C++ 1998 or 2011 Standard. + * + * @param ptr pointer to the previously allocated memory + */ +void operator delete(void* ptr, const std::nothrow_t&) _NOEXCEPT +{ + operator delete(ptr, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); +} + +/** + * Placement deallocation function. For details, please check Section + * 5.3.4 of the C++ 1998 or 2011 Standard. + * + * @param ptr pointer to the previously allocated memory + */ +void operator delete[](void* ptr, const std::nothrow_t&) _NOEXCEPT +{ + operator delete[](ptr, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0); +} + +// This is to make Doxygen happy +#undef _DEBUG_NEW_REMEMBER_STACK_TRACE +//#define _DEBUG_NEW_REMEMBER_STACK_TRACE 0 + +PRAGMA45(GCC diagnostic pop) +PRAGMACLANG6GCC(GCC diagnostic pop) diff --git a/src/debug/nvwa/debug_new.h b/src/debug/nvwa/debug_new.h new file mode 100644 index 000000000..50b168c2e --- /dev/null +++ b/src/debug/nvwa/debug_new.h @@ -0,0 +1,207 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2015 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file debug_new.h + * + * Header file for checking leaks caused by unmatched new/delete. + * + * @date 2015-10-25 + */ + +#ifndef NVWA_DEBUG_NEW_H +#define NVWA_DEBUG_NEW_H + +#include // size_t/std::bad_alloc +#include // FILE +#include "debug/nvwa/_nvwa.h" // NVWA_NAMESPACE_* +#include "debug/nvwa/c++11.h" // _NOEXCEPT + +/* Special allocation/deallocation functions in the global scope */ +void* operator new(size_t size, const char* file, int line); +void* operator new[](size_t size, const char* file, int line); +void operator delete(void* ptr, const char* file, int line) _NOEXCEPT; +void operator delete[](void* ptr, const char* file, int line) _NOEXCEPT; + +NVWA_NAMESPACE_BEGIN + +/** + * @def _DEBUG_NEW_REDEFINE_NEW + * + * Macro to indicate whether redefinition of \c new is wanted. If one + * wants to define one's own operator new, or to call + * operator new directly, it should be defined to \c 0 to + * alter the default behaviour. Unless, of course, one is willing to + * take the trouble to write something like: + * @code + * # ifdef new + * # define _NEW_REDEFINED + * # undef new + * # endif + * + * // Code that uses new is here + * + * # ifdef _NEW_REDEFINED + * # ifdef DEBUG_NEW + * # define new DEBUG_NEW + * # endif + * # undef _NEW_REDEFINED + * # endif + * @endcode + */ +#ifndef _DEBUG_NEW_REDEFINE_NEW +#define _DEBUG_NEW_REDEFINE_NEW 1 +#endif + +/** + * @def _DEBUG_NEW_TYPE + * + * Macro to indicate which variant of #DEBUG_NEW is wanted. The + * default value \c 1 allows the use of placement new (like + * %new(std::nothrow)), but the verbose output (when + * nvwa#new_verbose_flag is \c true) looks worse than some older + * versions (no file/line information for allocations). Define it + * to \c 2 to revert to the old behaviour that records file and line + * information directly on the call to operator new. + */ +#ifndef _DEBUG_NEW_TYPE +#define _DEBUG_NEW_TYPE 1 +#endif + +/** + * Callback type for stack trace printing. + * + * @param fp pointer to the output stream + * @param stacktrace pointer to the stack trace array (null-terminated) + */ +typedef void (*stacktrace_print_callback_t)(FILE* fp, void** stacktrace); + +/** + * Callback type for the leak whitelist function. \a file, \a address, + * and \a backtrace might be null depending on library configuration, + * platform, and amount of runtime information available. \a line can + * be 0 when line number info is not available at runtime. + * + * @param file null-terminated string of the file name + * @param line line number + * @param addr address of code where leakage happens + * @param stacktrace pointer to the stack trace array (null-terminated) + * @return \c true if the leak should be whitelisted; + * \c false otherwise + */ +typedef bool (*leak_whitelist_callback_t)(char const* file, int line, + void* addr, void** stacktrace); + +/* Prototypes */ +int check_leaks(); +int check_mem_corruption(); + +/* Control variables */ +extern bool new_autocheck_flag; // default to true: call check_leaks() on exit +extern bool new_verbose_flag; // default to false: no verbose information +extern FILE* new_output_fp; // default to stderr: output to console +extern const char* new_progname;// default to null; should be assigned argv[0] +extern stacktrace_print_callback_t stacktrace_print_callback;// default to null +extern leak_whitelist_callback_t leak_whitelist_callback; // default to null + +/** + * @def DEBUG_NEW + * + * Macro to catch file/line information on allocation. If + * #_DEBUG_NEW_REDEFINE_NEW is \c 0, one can use this macro directly; + * otherwise \c new will be defined to it, and one must use \c new + * instead. + */ +# if _DEBUG_NEW_TYPE == 1 +# define DEBUG_NEW NVWA::debug_new_recorder(__FILE__, __LINE__) ->* new +# else +# define DEBUG_NEW new(__FILE__, __LINE__) +# endif + +# if _DEBUG_NEW_REDEFINE_NEW +# define new DEBUG_NEW +# endif +# ifdef _DEBUG_NEW_EMULATE_MALLOC +# include +# ifdef new +# define malloc(s) ((void*)(new char[s])) +# else +# define malloc(s) ((void*)(DEBUG_NEW char[s])) +# endif +# define free(p) delete[] (char*)(p) +# endif + +/** + * Recorder class to remember the call context. + * + * The idea comes from Greg Herlihy's post in comp.lang.c++.moderated. + */ +class debug_new_recorder +{ + const char* _M_file; + const int _M_line; + void _M_process(void* ptr); +public: + /** + * Constructor to remember the call context. The information will + * be used in debug_new_recorder::operator->*. + */ + debug_new_recorder(const char* file, int line) + : _M_file(file), _M_line(line) {} + /** + * Operator to write the context information to memory. + * operator->* is chosen because it has the right + * precedence, it is rarely used, and it looks good: so people can + * tell the special usage more quickly. + */ + template _Tp* operator->*(_Tp* ptr) + { _M_process(ptr); return ptr; } +private: + debug_new_recorder(const debug_new_recorder&); + debug_new_recorder& operator=(const debug_new_recorder&); +}; + +/** + * Counter class for on-exit leakage check. + * + * This technique is learnt from The C++ Programming Language by + * Bjarne Stroustup. + */ +class debug_new_counter +{ + static int _S_count; +public: + debug_new_counter(); + ~debug_new_counter(); +}; +/** Counting object for each file including debug_new.h. */ +static debug_new_counter __debug_new_count; + +NVWA_NAMESPACE_END + +#endif // NVWA_DEBUG_NEW_H diff --git a/src/debug/nvwa/fast_mutex.h b/src/debug/nvwa/fast_mutex.h new file mode 100644 index 000000000..faccc4893 --- /dev/null +++ b/src/debug/nvwa/fast_mutex.h @@ -0,0 +1,427 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2015 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file fast_mutex.h + * + * A fast mutex implementation for POSIX, Win32, and modern C++. + * + * @date 2015-05-19 + */ + +#ifndef NVWA_FAST_MUTEX_H +#define NVWA_FAST_MUTEX_H + +#include "debug/nvwa/_nvwa.h" // NVWA_NAMESPACE_* +#include "debug/nvwa/c++11.h" // HAVE_CXX11_MUTEX + +# if !defined(_NOTHREADS) +# if !defined(NVWA_USE_CXX11_MUTEX) && HAVE_CXX11_MUTEX != 0 && \ + !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS) && \ + !defined(NVWA_PTHREADS) && !defined(NVWA_NOTHREADS) && \ + defined(_WIN32) && defined(_MT) && \ + (!defined(_MSC_VER) || defined(_DLL)) +// Prefer using std::mutex on Windows to avoid the namespace +// pollution caused by . However, MSVC has a re-entry +// issue with its std::mutex implementation, and std::mutex should +// not be used unless /MD or /MDd is used. For more information, +// check out: +// +// https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag +// http://stackoverflow.com/questions/14319344/stdmutex-lock-hangs-when-overriding-the-new-operator +// +# define NVWA_USE_CXX11_MUTEX 1 +# endif + +# if !defined(_WIN32THREADS) && \ + (defined(_WIN32) && defined(_MT)) +// Automatically use _WIN32THREADS when specifying -MT/-MD in MSVC, +// or -mthreads in MinGW GCC. +# define _WIN32THREADS +# elif !defined(_PTHREADS) && \ + defined(_REENTRANT) +// Automatically use _PTHREADS when specifying -pthread in GCC or Clang. +# define _PTHREADS +# endif +# endif + +# ifndef NVWA_USE_CXX11_MUTEX +# if HAVE_CXX11_MUTEX != 0 && \ + !defined(_NOTHREADS) && !defined(NVWA_NOTHREADS) && \ + !defined(_PTHREADS) && !defined(NVWA_PTHREADS) && \ + !defined(_WIN32THREADS) && !defined(NVWA_WIN32THREADS) +# define NVWA_USE_CXX11_MUTEX 1 +# else +# define NVWA_USE_CXX11_MUTEX 0 +# endif +# endif + +# if !defined(_PTHREADS) && !defined(_WIN32THREADS) && \ + !defined(_NOTHREADS) && NVWA_USE_CXX11_MUTEX == 0 +# define _NOTHREADS +# endif + +# if defined(_NOTHREADS) +# if defined(_PTHREADS) || defined(_WIN32THREADS) || \ + NVWA_USE_CXX11_MUTEX != 0 +# undef _NOTHREADS +# error "Cannot define multi-threaded mode with -D_NOTHREADS" +# endif +# endif + +# if defined(__MINGW32__) && defined(_WIN32THREADS) && !defined(_MT) +# error "Be sure to specify -mthreads with -D_WIN32THREADS" +# endif + +// With all the heuristics above, things may still go wrong, maybe even +// due to a specific inclusion order. So they may be overridden by +// manually defining the NVWA_* macros below. +# if NVWA_USE_CXX11_MUTEX == 0 && \ + !defined(NVWA_WIN32THREADS) && \ + !defined(NVWA_PTHREADS) && \ + !defined(NVWA_NOTHREADS) +// _WIN32THREADS takes precedence, as some C++ libraries have _PTHREADS +// defined even on Win32 platforms. +# if defined(_WIN32THREADS) +# define NVWA_WIN32THREADS +# elif defined(_PTHREADS) +# define NVWA_PTHREADS +# else +# define NVWA_NOTHREADS +# endif +# endif + +# ifndef _FAST_MUTEX_CHECK_INITIALIZATION +/** + * Macro to control whether to check for initialization status for each + * lock/unlock operation. Defining it to a non-zero value will enable + * the check, so that the construction/destruction of a static object + * using a static fast_mutex not yet constructed or already destroyed + * will work (with lock/unlock operations ignored). Defining it to zero + * will disable to check. + */ +# define _FAST_MUTEX_CHECK_INITIALIZATION 1 +# endif + +# ifdef _DEBUG +# include +# include +/** Macro for fast_mutex assertions. Real version (for debug mode). */ +# define _FAST_MUTEX_ASSERT(_Expr, _Msg) \ + if (!(_Expr)) { \ + fprintf(stderr, "fast_mutex::%s\n", _Msg); \ + abort(); \ + } +# else +/** Macro for fast_mutex assertions. Fake version (for release mode). */ +# define _FAST_MUTEX_ASSERT(_Expr, _Msg) \ + ((void)0) +# endif + +# if NVWA_USE_CXX11_MUTEX != 0 +# include +NVWA_NAMESPACE_BEGIN +/** + * Macro alias to `volatile' semantics. Here it is truly volatile since + * it is in a multi-threaded (C++11) environment. + */ +# define __VOLATILE volatile + /** + * Class for non-reentrant fast mutexes. This is the implementation + * using the C++11 mutex. + */ + class fast_mutex + { + std::mutex _M_mtx_impl; +# if _FAST_MUTEX_CHECK_INITIALIZATION + bool _M_initialized; +# endif +# ifdef _DEBUG + bool _M_locked; +# endif + public: + fast_mutex() +# ifdef _DEBUG + : _M_locked(false) +# endif + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = true; +# endif + } + ~fast_mutex() + { + _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = false; +# endif + } + void lock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif + _M_mtx_impl.lock(); +# ifdef _DEBUG + _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); + _M_locked = true; +# endif + } + void unlock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif +# ifdef _DEBUG + _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); + _M_locked = false; +# endif + _M_mtx_impl.unlock(); + } + private: + fast_mutex(const fast_mutex&); + fast_mutex& operator=(const fast_mutex&); + }; +NVWA_NAMESPACE_END +# elif defined(NVWA_PTHREADS) +# include +NVWA_NAMESPACE_BEGIN +/** + * Macro alias to `volatile' semantics. Here it is truly volatile since + * it is in a multi-threaded (POSIX threads) environment. + */ +# define __VOLATILE volatile + /** + * Class for non-reentrant fast mutexes. This is the implementation + * for POSIX threads. + */ + class fast_mutex + { + pthread_mutex_t _M_mtx_impl; +# if _FAST_MUTEX_CHECK_INITIALIZATION + bool _M_initialized; +# endif +# ifdef _DEBUG + bool _M_locked; +# endif + public: + fast_mutex() +# ifdef _DEBUG + : _M_locked(false) +# endif + { + ::pthread_mutex_init(&_M_mtx_impl, _NULLPTR); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = true; +# endif + } + ~fast_mutex() + { + _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = false; +# endif + ::pthread_mutex_destroy(&_M_mtx_impl); + } + void lock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif + ::pthread_mutex_lock(&_M_mtx_impl); +# ifdef _DEBUG + // The following assertion should _always_ be true for a + // real `fast' pthread_mutex. However, this assertion can + // help sometimes, when people forget to use `-lpthread' and + // glibc provides an empty implementation. Having this + // assertion is also more consistent. + _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); + _M_locked = true; +# endif + } + void unlock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif +# ifdef _DEBUG + _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); + _M_locked = false; +# endif + ::pthread_mutex_unlock(&_M_mtx_impl); + } + private: + fast_mutex(const fast_mutex&); + fast_mutex& operator=(const fast_mutex&); + }; +NVWA_NAMESPACE_END +# elif defined(NVWA_WIN32THREADS) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif /* WIN32_LEAN_AND_MEAN */ +# include +NVWA_NAMESPACE_BEGIN +/** + * Macro alias to `volatile' semantics. Here it is truly volatile since + * it is in a multi-threaded (Win32 threads) environment. + */ +# define __VOLATILE volatile + /** + * Class for non-reentrant fast mutexes. This is the implementation + * for Win32 threads. + */ + class fast_mutex + { + CRITICAL_SECTION _M_mtx_impl; +# if _FAST_MUTEX_CHECK_INITIALIZATION + bool _M_initialized; +# endif +# ifdef _DEBUG + bool _M_locked; +# endif + public: + fast_mutex() +# ifdef _DEBUG + : _M_locked(false) +# endif + { + ::InitializeCriticalSection(&_M_mtx_impl); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = true; +# endif + } + ~fast_mutex() + { + _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); +# if _FAST_MUTEX_CHECK_INITIALIZATION + _M_initialized = false; +# endif + ::DeleteCriticalSection(&_M_mtx_impl); + } + void lock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif + ::EnterCriticalSection(&_M_mtx_impl); +# ifdef _DEBUG + _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); + _M_locked = true; +# endif + } + void unlock() + { +# if _FAST_MUTEX_CHECK_INITIALIZATION + if (!_M_initialized) + return; +# endif +# ifdef _DEBUG + _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); + _M_locked = false; +# endif + ::LeaveCriticalSection(&_M_mtx_impl); + } + private: + fast_mutex(const fast_mutex&); + fast_mutex& operator=(const fast_mutex&); + }; +NVWA_NAMESPACE_END +# elif defined(NVWA_NOTHREADS) +NVWA_NAMESPACE_BEGIN +/** + * Macro alias to `volatile' semantics. Here it is not truly volatile + * since it is in a single-threaded environment. + */ +# define __VOLATILE + /** + * Class for non-reentrant fast mutexes. This is the null + * implementation for single-threaded environments. + */ + class fast_mutex + { +# ifdef _DEBUG + bool _M_locked; +# endif + public: + fast_mutex() +# ifdef _DEBUG + : _M_locked(false) +# endif + { + } + ~fast_mutex() + { + _FAST_MUTEX_ASSERT(!_M_locked, "~fast_mutex(): still locked"); + } + void lock() + { +# ifdef _DEBUG + _FAST_MUTEX_ASSERT(!_M_locked, "lock(): already locked"); + _M_locked = true; +# endif + } + void unlock() + { +# ifdef _DEBUG + _FAST_MUTEX_ASSERT(_M_locked, "unlock(): not locked"); + _M_locked = false; +# endif + } + private: + fast_mutex(const fast_mutex&); + fast_mutex& operator=(const fast_mutex&); + }; +NVWA_NAMESPACE_END +# endif // Definition of class fast_mutex + +NVWA_NAMESPACE_BEGIN +/** RAII lock class for fast_mutex. */ +class fast_mutex_autolock +{ + fast_mutex& _M_mtx; +public: + explicit fast_mutex_autolock(fast_mutex& mtx) : _M_mtx(mtx) + { + _M_mtx.lock(); + } + ~fast_mutex_autolock() + { + _M_mtx.unlock(); + } +private: + fast_mutex_autolock(const fast_mutex_autolock&); + fast_mutex_autolock& operator=(const fast_mutex_autolock&); +}; +NVWA_NAMESPACE_END + +#endif // NVWA_FAST_MUTEX_H diff --git a/src/debug/nvwa/static_assert.h b/src/debug/nvwa/static_assert.h new file mode 100644 index 000000000..877ef0f97 --- /dev/null +++ b/src/debug/nvwa/static_assert.h @@ -0,0 +1,62 @@ +// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- +// vim:tabstop=4:shiftwidth=4:expandtab: + +/* + * Copyright (C) 2004-2013 Wu Yongwei + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgement in the product + * documentation would be appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source + * distribution. + * + * This file is part of Stones of Nvwa: + * http://sourceforge.net/projects/nvwa + * + */ + +/** + * @file static_assert.h + * + * Template class to check validity duing compile time (adapted from Loki). + * + * @date 2013-09-07 + */ + +#ifndef STATIC_ASSERT + +#include "debug/nvwa/c++11.h" + +#if HAVE_CXX11_STATIC_ASSERT + +#define STATIC_ASSERT(_Expr, _Msg) static_assert(_Expr, #_Msg) + +#else + +namespace nvwa { + +template struct compile_time_error; +template <> struct compile_time_error {}; + +#define STATIC_ASSERT(_Expr, _Msg) \ + { \ + nvwa::compile_time_error<((_Expr) != 0)> ERROR_##_Msg; \ + (void)ERROR_##_Msg; \ + } + +} + +#endif // HAVE_CXX11_STATIC_ASSERT + +#endif // STATIC_ASSERT diff --git a/src/debug/static_assert.h b/src/debug/static_assert.h deleted file mode 100644 index 49248611d..000000000 --- a/src/debug/static_assert.h +++ /dev/null @@ -1,62 +0,0 @@ -// -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- -// vim:tabstop=4:shiftwidth=4:expandtab: - -/* - * Copyright (C) 2004-2013 Wu Yongwei - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any - * damages arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute - * it freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgement in the product - * documentation would be appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source - * distribution. - * - * This file is part of Stones of Nvwa: - * http://sourceforge.net/projects/nvwa - * - */ - -/** - * @file static_assert.h - * - * Template class to check validity duing compile time (adapted from Loki). - * - * @date 2013-09-07 - */ - -#ifndef STATIC_ASSERT - -#include "debug/c++11.h" - -#if HAVE_CXX11_STATIC_ASSERT - -#define STATIC_ASSERT(_Expr, _Msg) static_assert(_Expr, #_Msg) - -#else - -namespace nvwa { - -template struct compile_time_error; -template <> struct compile_time_error {}; - -#define STATIC_ASSERT(_Expr, _Msg) \ - { \ - nvwa::compile_time_error<((_Expr) != 0)> ERROR_##_Msg; \ - (void)ERROR_##_Msg; \ - } - -} - -#endif // HAVE_CXX11_STATIC_ASSERT - -#endif // STATIC_ASSERT -- cgit v1.2.3-60-g2f50