summaryrefslogtreecommitdiff
path: root/src/debug/debug_new.cpp
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2017-07-13 22:28:03 +0300
committerAndrei Karas <akaras@inbox.ru>2017-07-13 22:28:03 +0300
commitf2b51ca5984fc464fbcee652b13c0287752d3c7a (patch)
treef320495b9c67a92a242bdacb64e84824cb6de996 /src/debug/debug_new.cpp
parentd8a5cfe68b1e3826ea9999396c888e2baae062ad (diff)
downloadplus-f2b51ca5984fc464fbcee652b13c0287752d3c7a.tar.gz
plus-f2b51ca5984fc464fbcee652b13c0287752d3c7a.tar.bz2
plus-f2b51ca5984fc464fbcee652b13c0287752d3c7a.tar.xz
plus-f2b51ca5984fc464fbcee652b13c0287752d3c7a.zip
Move memory debug files into nvwa directory.
Diffstat (limited to 'src/debug/debug_new.cpp')
-rw-r--r--src/debug/debug_new.cpp1197
1 files changed, 0 insertions, 1197 deletions
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 <adah at users dot sourceforge dot net>
- *
- * 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 <new> // std::bad_alloc/nothrow_t
-#include <assert.h> // assert
-#include <stdio.h> // fprintf/stderr
-#include <stdlib.h> // abort
-#include <string.h> // strcpy/strncpy/sprintf
-#include "debug/_nvwa.h" // NVWA macros
-
-#if NVWA_UNIX
-#include <alloca.h> // alloca
-#endif
-#if NVWA_WIN32
-#include <malloc.h> // alloca
-#endif
-
-#if NVWA_LINUX || NVWA_APPLE
-#include <execinfo.h> // backtrace
-#endif
-
-#if NVWA_WINDOWS
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-#include <windows.h> // 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 <code>-D_DEBUG_NEW_PROGNAME=\\"a.out\"</code>
- * in \e bash, or <code>-D_DEBUG_NEW_PROGNAME=\\"a.exe\"</code> 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
- * <code>operator new</code> 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 `<code>new
- * NonPODType[size]</code>'.
- */
-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 <em>new[]</em> 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 <code>operator new</code>.
- */
-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 <code>argv[0]</code> 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:
- * `<code>new_progname = getenv("_");</code>'.
- */
-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, "<Unknown>");
- }
-}
-
-#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&mdash;only stack frame pointers are
- * printed&mdash;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
- * <code>delete[]</code> 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)