fix VFS bug preventing directories from being created:
- vfs_lookup: currentPath was of wrong type; simplified and fix create logic - archive_zip: fix: correctly handle filenames with / - vfs: remove workaround for above bug - io: temporarily disable block caching (works around problem with zip file created by 7z) add and use new wdbg_heap module (WIP) remove mmgr This was SVN commit r5571.
This commit is contained in:
parent
1700e90a15
commit
aebf8cbf68
@ -22,6 +22,10 @@
|
||||
#include "lib/sysdep/cpu.h" // cpu_CAS
|
||||
#include "lib/sysdep/sysdep.h"
|
||||
|
||||
#if OS_WIN
|
||||
#include "lib/sysdep/win/wdbg_heap.h"
|
||||
#endif
|
||||
|
||||
|
||||
ERROR_ASSOCIATE(ERR::SYM_NO_STACK_FRAMES_FOUND, "No stack frames found", -1);
|
||||
ERROR_ASSOCIATE(ERR::SYM_UNRETRIEVABLE_STATIC, "Value unretrievable (stored in external module)", -1);
|
||||
@ -396,7 +400,13 @@ static ErrorReaction carry_out_ErrorReaction(ErrorReaction er, uint flags, u8* s
|
||||
case ER_EXIT:
|
||||
exit_requested = true; // see declaration
|
||||
|
||||
abort();
|
||||
#if OS_WIN
|
||||
// prevent (slow) heap reporting since we're exiting abnormally and
|
||||
// thus probably leaking like a sieve.
|
||||
wdbg_heap_Enable(false);
|
||||
#endif
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return er;
|
||||
|
@ -15,7 +15,6 @@
|
||||
// diagnosing and reporting program errors.
|
||||
// - a symbol engine provides access to compiler-generated debug information and
|
||||
// can also give a stack trace including local variables;
|
||||
// - hooks into the memory allocator improve its leak detection;
|
||||
// - our more powerful assert() replacement gives a stack trace so
|
||||
// that the underlying problem becomes apparent;
|
||||
// - the output routines make for platform-independent logging and
|
||||
@ -35,17 +34,6 @@ extern void debug_break();
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// debug memory allocator
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* check heap integrity.
|
||||
* errors are reported by the CRT or via debug_display_error.
|
||||
**/
|
||||
LIB_API void debug_heap_check(void);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// output
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -71,11 +71,11 @@ public:
|
||||
size_t Size() const
|
||||
{
|
||||
debug_assert(m_magic == lfh_magic);
|
||||
const size_t fn_len = read_le16(&m_fn_len);
|
||||
const size_t e_len = read_le16(&m_e_len);
|
||||
size_t size = sizeof(LFH);
|
||||
size += read_le16(&m_fn_len);
|
||||
size += read_le16(&m_e_len);
|
||||
// note: LFH doesn't have a comment field!
|
||||
|
||||
return sizeof(LFH) + fn_len + e_len;
|
||||
return size;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -386,17 +386,12 @@ public:
|
||||
|
||||
std::string zipPathname;
|
||||
cdfh->GetPathname(zipPathname);
|
||||
const size_t lastSlash = zipPathname.find_last_of('/');
|
||||
if(lastSlash != zipPathname.length()-1) // we only want files
|
||||
const size_t lastSlashOfs = zipPathname.find_last_of('/');
|
||||
const size_t nameOfs = (lastSlashOfs == std::string::npos)? 0 : lastSlashOfs+1;
|
||||
if(nameOfs != zipPathname.length()) // ignore paths ending in slash (i.e. representing a directory)
|
||||
{
|
||||
std::string name;
|
||||
std::string* pname = &zipPathname; // assume zipPathname only has a name component
|
||||
if(lastSlash != std::string::npos)
|
||||
{
|
||||
name = zipPathname.substr(lastSlash, zipPathname.length()-lastSlash);
|
||||
pname = &name;
|
||||
}
|
||||
FileInfo fileInfo(*pname, cdfh->USize(), cdfh->MTime());
|
||||
const std::string name = zipPathname.substr(nameOfs, zipPathname.length()-nameOfs);
|
||||
FileInfo fileInfo(name, cdfh->USize(), cdfh->MTime());
|
||||
shared_ptr<ArchiveFile_Zip> archiveFile(new ArchiveFile_Zip(m_file, cdfh->HeaderOffset(), cdfh->CSize(), cdfh->Checksum(), cdfh->Method()));
|
||||
cb(zipPathname, fileInfo, archiveFile, cbData);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ public:
|
||||
{
|
||||
m_file = file;
|
||||
m_blockId = BlockId(file->Pathname(), alignedOfs);
|
||||
if(file->Mode() == 'r' && s_blockCache.Retrieve(m_blockId, m_cachedBlock))
|
||||
if(false && file->Mode() == 'r' && s_blockCache.Retrieve(m_blockId, m_cachedBlock))
|
||||
{
|
||||
stats_block_cache(CR_HIT);
|
||||
|
||||
|
@ -70,7 +70,6 @@ public:
|
||||
CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE));
|
||||
|
||||
PRealDirectory realDirectory = directory->AssociatedDirectory();
|
||||
if(!realDirectory) return ERR::FAIL; // WORKAROUND: vfs doesn't create real dirs by itself
|
||||
const std::string& name = pathname.leaf();
|
||||
RETURN_ERR(realDirectory->Store(name, fileContents, size));
|
||||
|
||||
|
@ -139,18 +139,25 @@ LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDi
|
||||
{
|
||||
TIMER_ACCRUE(tc_lookup);
|
||||
|
||||
directory = startDirectory;
|
||||
// extract and validate flags (ensure no unknown bits are set)
|
||||
const bool addMissingDirectories = (flags & VFS_LOOKUP_ADD) != 0;
|
||||
const bool createMissingDirectories = (flags & VFS_LOOKUP_CREATE) != 0;
|
||||
debug_assert((flags & ~(VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)) == 0);
|
||||
|
||||
if(pfile)
|
||||
*pfile = 0;
|
||||
|
||||
directory = startDirectory;
|
||||
RETURN_ERR(Populate(directory));
|
||||
|
||||
if(pathname.empty()) // early out for root directory
|
||||
return INFO::OK; // (prevents iterator error below)
|
||||
// early-out for pathname == "" when mounting into VFS root
|
||||
if(pathname.empty()) // (prevent iterator error in loop end condition)
|
||||
return INFO::OK;
|
||||
|
||||
VfsPath currentPath; // only used if flags & VFS_LOOKUP_CREATE
|
||||
VfsPath::iterator it;
|
||||
Path currentPath; // (.. thus far; used when createMissingDirectories)
|
||||
|
||||
// for each directory component:
|
||||
VfsPath::iterator it; // (used outside of loop to get filename)
|
||||
for(it = pathname.begin(); it != --pathname.end(); ++it)
|
||||
{
|
||||
const std::string& subdirectoryName = *it;
|
||||
@ -158,26 +165,29 @@ TIMER_ACCRUE(tc_lookup);
|
||||
VfsDirectory* subdirectory = directory->GetSubdirectory(subdirectoryName);
|
||||
if(!subdirectory)
|
||||
{
|
||||
if(!(flags & VFS_LOOKUP_ADD))
|
||||
if(addMissingDirectories)
|
||||
subdirectory = directory->AddSubdirectory(subdirectoryName);
|
||||
else
|
||||
return ERR::VFS_DIR_NOT_FOUND; // NOWARN
|
||||
}
|
||||
|
||||
subdirectory = directory->AddSubdirectory(subdirectoryName);
|
||||
|
||||
if(flags & VFS_LOOKUP_CREATE)
|
||||
if(createMissingDirectories)
|
||||
{
|
||||
if(subdirectory->AssociatedDirectory())
|
||||
currentPath /= subdirectory->AssociatedDirectory()->GetPath().leaf();
|
||||
else
|
||||
{
|
||||
currentPath /= subdirectoryName;
|
||||
|
||||
#if 0
|
||||
(void)mkdir(currentPath.external_directory_string().c_str(), S_IRWXO|S_IRWXU|S_IRWXG);
|
||||
|
||||
PRealDirectory realDirectory(new RealDirectory(currentPath.string(), 0, 0));
|
||||
subdirectory->Attach(realDirectory);
|
||||
#endif
|
||||
if(mkdir(currentPath.external_directory_string().c_str(), S_IRWXO|S_IRWXU|S_IRWXG) == 0)
|
||||
{
|
||||
PRealDirectory realDirectory(new RealDirectory(currentPath, 0, 0));
|
||||
subdirectory->Attach(realDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_ERR(Populate(subdirectory));
|
||||
directory = subdirectory;
|
||||
RETURN_ERR(Populate(directory));
|
||||
}
|
||||
|
||||
if(pfile)
|
||||
|
1423
source/lib/mmgr.cpp
1423
source/lib/mmgr.cpp
File diff suppressed because it is too large
Load Diff
@ -1,296 +0,0 @@
|
||||
#if 0
|
||||
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : mmgr.h
|
||||
* Project : 0 A.D.
|
||||
* Description : memory manager and tracker.
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
// license: GPL; see lib/license.txt
|
||||
|
||||
/*
|
||||
|
||||
purpose and history
|
||||
-------------------
|
||||
|
||||
our goal is to expose any memory handling bugs in the application as
|
||||
early as possible. various checks are performed upon each memory API call;
|
||||
if all options are on, we can spot the following:
|
||||
memory leaks, double-free, allocation over/underruns,
|
||||
unused memory, and use-after-free.
|
||||
|
||||
this code started life as Paul Nettle's memory manager (available
|
||||
at http:www.fluidstudios.com), and has been completely overhauled.
|
||||
in particular, it is now thread-safe and modularized;
|
||||
duplicated code has been eliminated.
|
||||
|
||||
|
||||
instructions for integrating into your project
|
||||
----------------------------------------------
|
||||
|
||||
1) #include this from all project source files [that will allocate memory].
|
||||
doing so from the precompiled header is recommended, since the
|
||||
compiler will make sure it has actually been included.
|
||||
2) all system headers must be #include-d before this header, so that
|
||||
we don't mess with any of their local operator new/delete.
|
||||
3) if project source/headers also use local operator new/delete, #include
|
||||
"nommgr.h" before that spot, and re-#include "mmgr.h" afterwards.
|
||||
|
||||
4) if using MFC:
|
||||
- set linker option /FORCE - works around conflict between our global
|
||||
operator new and that of MFC. be sure to check for other errors.
|
||||
- remove any #define new DEBUG_NEW from all source files.
|
||||
|
||||
|
||||
effects
|
||||
-------
|
||||
|
||||
many bugs are caught and announced with no further changes
|
||||
required, due to integrity checks inside the allocator.
|
||||
|
||||
at exit, three report files are generated: a listing of leaks,
|
||||
various statistics (e.g. total unused memory), and the log.
|
||||
this lists (depending on settings) all allocations, enter/exit
|
||||
indications for our functions, and failure notifications.
|
||||
|
||||
|
||||
digging deeper
|
||||
--------------
|
||||
|
||||
when tracking down hard-to-find bugs, more stringent checks can be
|
||||
activated via mmgr_set_option, or by changing the initial value of
|
||||
options in mmgr.cpp. however, they slow down the app considerably
|
||||
and need not always be enabled. see option declarations above.
|
||||
|
||||
you can also change padding_size in mmgr.cpp at compile-time to provide
|
||||
more safety vs. overruns, at the cost of wasting lots of memory per
|
||||
allocation (which must also be cleared). this is only done in
|
||||
CONFIG_PARANOIA builds, because overruns seldom 'skip' padding.
|
||||
|
||||
finally, you can induce memory allocations to fail a certain percentage
|
||||
of the time - this tests your application's error handling.
|
||||
adjust the RANDOM_FAILURE #define in mmgr.cpp.
|
||||
|
||||
|
||||
fixing your bugs
|
||||
----------------
|
||||
|
||||
if this code crashes or fails an debug_assert, it is most likely due to a bug
|
||||
in your application. consult the current Alloc for information;
|
||||
search the log for its address to determine what operation failed,
|
||||
and what piece of code owns the allocation.
|
||||
|
||||
if the cause isn't visible (i.e. the error is reported after the fact),
|
||||
you can try activating the more stringent checks to catch the problem
|
||||
earlier. you may also call the validation routines at checkpoints
|
||||
in your code to narrow the cause down. if all else fails, break on
|
||||
the allocation number to see what's happening.
|
||||
|
||||
good luck!
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// memory headers
|
||||
//
|
||||
|
||||
// these are all system headers that contain "new", "malloc" etc.; they must
|
||||
// come before the memory tracker headers to avoid conflicts with their
|
||||
// macros. therefore, they are always included, even if !CONFIG_PCH.
|
||||
|
||||
#if OS_WIN
|
||||
# include <malloc.h>
|
||||
# include <xdebug>
|
||||
# include <xtree>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
#include <memory>
|
||||
#include <locale> // operator new
|
||||
#include <valarray> // free() member function
|
||||
|
||||
|
||||
// VC debug memory allocator / leak detector
|
||||
// notes:
|
||||
// - PCH is required because it makes sure system headers are included
|
||||
// before redefining new (otherwise, tons of errors result);
|
||||
// - disabled on ICC9 because the ICC 9.0.006 beta appears to generate
|
||||
// incorrect code when we redefine new.
|
||||
// TODO: remove when no longer necessary
|
||||
#if MSC_VERSION && \
|
||||
(!defined(NDEBUG) || defined(TESTING)) && \
|
||||
HAVE_PCH && \
|
||||
ICC_VERSION != 900
|
||||
# define HAVE_VC_DEBUG_ALLOC 1
|
||||
#else
|
||||
# define HAVE_VC_DEBUG_ALLOC 0
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef INCLUDED_MMGR
|
||||
#define INCLUDED_MMGR
|
||||
|
||||
namespace ERR
|
||||
{
|
||||
const LibError MEM_ALLOC_NOT_FOUND = -100200;
|
||||
const LibError MEM_OVERWRITTEN = -100201;
|
||||
}
|
||||
|
||||
|
||||
// provide for completely disabling the memory manager
|
||||
// (e.g. when using other debug packages)
|
||||
//
|
||||
// note: this must go around the include-guarded part (constants+externs)
|
||||
// as well as the macros. we don't want to mess up compiler include-guard
|
||||
// optimizations, so duplicate this #if.
|
||||
#if CONFIG_USE_MMGR
|
||||
|
||||
//
|
||||
// optional additional checks, enabled via mmgr_set_options.
|
||||
// these slow down the application; see 'digging deeper' in documentation.
|
||||
//
|
||||
|
||||
// log all allocation/deallocation operations undertaken.
|
||||
const uint MMGR_LOG_ALL = 0x001;
|
||||
|
||||
// validate all allocations on every memory API call. slow!
|
||||
const uint MMGR_VALIDATE_ALL = 0x002;
|
||||
|
||||
// fill the user-visible part of each allocation with a certain pattern
|
||||
// on alloc and free. this is required for unused memory tracking.
|
||||
const uint MMGR_FILL = 0x004;
|
||||
|
||||
// log all enter/exit into our API. if there's an
|
||||
// unmatched pair in the log, we know where a crash occurred.
|
||||
const uint MMGR_TRACE = 0x008;
|
||||
|
||||
// use debug information to resolve owner address to file/line/function.
|
||||
// note: passing owner information to global operator delete via macro
|
||||
// isn't reliable, so a stack backtrace (list of function addresses) is all
|
||||
// we have there. this costs ~500us per unique call site on Windows.
|
||||
const uint MMGR_RESOLVE_OWNER = 0x010;
|
||||
|
||||
// force each log line to be written directly to disk. slow!
|
||||
// use only when the application is crashing, to make sure all
|
||||
// available information is written out.
|
||||
const uint MMGR_FLUSH_LOG = 0x020;
|
||||
|
||||
// an alias that includes all of the above. (more convenient)
|
||||
const uint MMGR_ALL = 0xfff;
|
||||
|
||||
// return the current options unchanged.
|
||||
const uint MMGR_QUERY = ~0;
|
||||
|
||||
extern uint mmgr_set_options(uint);
|
||||
|
||||
|
||||
// break when a certain allocation is created/reallocated/freed:
|
||||
extern void mmgr_break_on_alloc(uint count);
|
||||
extern void mmgr_break_on_realloc(const void*);
|
||||
extern void mmgr_break_on_free(const void*);
|
||||
|
||||
// "proactive" validation: (see 'digging deeper')
|
||||
extern bool mmgr_is_valid_ptr(const void*);
|
||||
extern bool mmgr_are_all_valid(void);
|
||||
|
||||
// write a report file
|
||||
extern void mmgr_write_report(void);
|
||||
extern void mmgr_write_leak_report(void);
|
||||
|
||||
|
||||
//
|
||||
// our wrappers for C++ memory handling functions
|
||||
//
|
||||
|
||||
// note that all line numbers are int, for compatibility with any external
|
||||
// overloaded operator new (in case someone forget to include "mmgr.h").
|
||||
|
||||
extern void* mmgr_malloc_dbg (size_t size, const char* file, int line, const char* func);
|
||||
extern void* mmgr_calloc_dbg (size_t num, size_t size, const char* file, int line, const char* func);
|
||||
extern void* mmgr_realloc_dbg(void* p, size_t size, const char* file, int line, const char* func);
|
||||
extern void mmgr_free_dbg (void* p, const char* file, int line, const char* func);
|
||||
|
||||
extern char* mmgr_strdup_dbg(const char*, const char* file, int line, const char* func);
|
||||
extern wchar_t* mmgr_wcsdup_dbg(const wchar_t*, const char* file, int line, const char* func);
|
||||
extern char* mmgr_getcwd_dbg(char*, size_t, const char* file, int line, const char* func);
|
||||
|
||||
|
||||
// .. global operator new (to catch allocs from STL/external libs)
|
||||
extern void* operator new (size_t size) throw(std::bad_alloc);
|
||||
extern void* operator new[](size_t size) throw(std::bad_alloc);
|
||||
// .. override commonly used global operator new overload (done e.g. by MFC),
|
||||
// in case someone hasn't included this file
|
||||
extern void* operator new (size_t size, const char* file, int line);
|
||||
extern void* operator new[](size_t size, const char* file, int line);
|
||||
// .. called by our global operator new hook macro
|
||||
extern void* operator new (size_t size, const char* file, int line, const char* func);
|
||||
extern void* operator new[](size_t size, const char* file, int line, const char* func);
|
||||
// .. global operator delete
|
||||
extern void operator delete (void* p) throw();
|
||||
extern void operator delete[](void* p) throw();
|
||||
// .. corresponding delete for first overloaded new,
|
||||
// only called if exception raised during ctor
|
||||
extern void operator delete (void* p, const char* file, int line) throw();
|
||||
extern void operator delete[](void* p, const char* file, int line) throw();
|
||||
// .. corresponding delete for our overloaded new,
|
||||
// only called if exception raised during ctor
|
||||
extern void operator delete (void* p, const char* file, int line, const char* func) throw();
|
||||
extern void operator delete[](void* p, const char* file, int line, const char* func) throw();
|
||||
|
||||
#endif // #if CONFIG_USE_MMGR
|
||||
|
||||
#endif // #ifdef INCLUDED_MMGR
|
||||
|
||||
|
||||
//
|
||||
// hook macros
|
||||
//
|
||||
|
||||
#include "nommgr.h"
|
||||
|
||||
#if CONFIG_USE_MMGR || HAVE_VC_DEBUG_ALLOC
|
||||
// notify user code that they must #include "nommgr.h" in places
|
||||
// where our macro would cause breakage (e.g. placement new)
|
||||
# define REDEFINED_NEW
|
||||
#endif
|
||||
|
||||
// mmgr version:
|
||||
// (to simplify code that may either use mmgr or the VC debug heap,
|
||||
// we support enabling/disabling both in this header)
|
||||
#if CONFIG_USE_MMGR
|
||||
|
||||
// get rid of __FUNCTION__ unless we know the compiler supports it.
|
||||
// (note: don't define if built-in - compiler will raise a warning)
|
||||
#if !MSC_VERSION && !GCC_VERSION
|
||||
#define __FUNCTION__ 0
|
||||
#endif
|
||||
|
||||
#define new new(__FILE__, __LINE__, __FUNCTION__)
|
||||
// hooking delete and setting global owner variables/pushing them on a stack
|
||||
// isn't thread-safe and can be fooled with destructor chains.
|
||||
// we instead rely on the call stack (works with VC and GCC)
|
||||
#define malloc(size) mmgr_malloc_dbg (size, __FILE__,__LINE__,__FUNCTION__)
|
||||
#define calloc(num, size) mmgr_calloc_dbg (num,size,__FILE__,__LINE__,__FUNCTION__)
|
||||
#define realloc(p,size) mmgr_realloc_dbg(p,size, __FILE__,__LINE__,__FUNCTION__)
|
||||
#define free(p) mmgr_free_dbg (p, __FILE__,__LINE__,__FUNCTION__)
|
||||
|
||||
#define strdup(p) mmgr_strdup_dbg(p, __FILE__,__LINE__,__FUNCTION__)
|
||||
#define wcsdup(p) mmgr_wcsdup_dbg(p, __FILE__,__LINE__,__FUNCTION__)
|
||||
#define getcwd(p,size) mmgr_getcwd_dbg(p, size, __FILE__,__LINE__,__FUNCTION__)
|
||||
|
||||
#elif HAVE_VC_DEBUG_ALLOC
|
||||
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <crtdbg.h>
|
||||
// crtdbg.h didn't define "new" (probably for compatibility); do so now.
|
||||
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
|
||||
|
||||
#endif // #if CONFIG_USE_MMGR
|
||||
|
||||
#endif
|
@ -1,18 +0,0 @@
|
||||
// remove all of mmgr.h's memory allocation "hooks" -
|
||||
// but only if we actually defined them!
|
||||
#if CONFIG_USE_MMGR || HAVE_VC_DEBUG_ALLOC
|
||||
# undef new
|
||||
# undef delete
|
||||
# undef malloc
|
||||
# undef calloc
|
||||
# undef realloc
|
||||
# undef free
|
||||
# undef strdup
|
||||
# undef wcsdup
|
||||
# undef getcwd
|
||||
#endif
|
||||
|
||||
// sanity check
|
||||
#ifdef new
|
||||
#error "nommgr.h - something is wrong, new is still defined"
|
||||
#endif
|
@ -174,11 +174,6 @@ static i32 last_in_use = -1; // don't search unused entries
|
||||
// also used by h_data, and alloc_idx to find a free entry.
|
||||
static HDATA* h_data_from_idx(const i32 idx)
|
||||
{
|
||||
// makes things *crawl*!
|
||||
#if CONFIG_PARANOIA
|
||||
debug_heap_check();
|
||||
#endif
|
||||
|
||||
// don't compare against last_in_use - this is called before allocating
|
||||
// new entries, and to check if the next (but possibly not yet valid)
|
||||
// entry is free. tag check protects against using unallocated entries.
|
||||
|
@ -14,64 +14,7 @@
|
||||
#include "lib/bits.h"
|
||||
#include "win.h"
|
||||
#include "wutil.h"
|
||||
#include "winit.h"
|
||||
|
||||
WINIT_REGISTER_CRITICAL_INIT(wdbg_Init);
|
||||
|
||||
|
||||
static NT_TIB* get_tib()
|
||||
{
|
||||
#if ARCH_IA32
|
||||
NT_TIB* tib;
|
||||
// ICC 10 doesn't support the NT_TIB.Self syntax, so we have to use
|
||||
// a constant (asm code isn't 64-bit safe anyway).
|
||||
__asm
|
||||
{
|
||||
mov eax, fs:[NT_TIB.Self]
|
||||
mov [tib], eax
|
||||
}
|
||||
return tib;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// debug heap
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void debug_heap_init()
|
||||
{
|
||||
uint flags = 0;
|
||||
flags |= _CRTDBG_ALLOC_MEM_DF; // enable checks at deallocation time
|
||||
flags |= _CRTDBG_LEAK_CHECK_DF; // report leaks at exit
|
||||
#if CONFIG_PARANOIA
|
||||
flags |= _CRTDBG_CHECK_ALWAYS_DF; // check during every heap operation (slow!)
|
||||
flags |= _CRTDBG_DELAY_FREE_MEM_DF; // blocks cannot be reused
|
||||
#endif
|
||||
_CrtSetDbgFlag(flags);
|
||||
}
|
||||
|
||||
|
||||
void debug_heap_check()
|
||||
{
|
||||
int ret;
|
||||
__try
|
||||
{
|
||||
// NB: this is a no-op if !_CRTDBG_ALLOC_MEM_DF.
|
||||
// we could call _heapchk but that would catch fewer errors.
|
||||
ret = _CrtCheckMemory();
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
ret = _HEAPBADNODE;
|
||||
}
|
||||
|
||||
if(ret != _HEAPOK)
|
||||
DEBUG_DISPLAY_ERROR(L"debug_heap_check: heap is corrupt");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// return 1 if the pointer appears to be totally bogus, otherwise 0.
|
||||
// this check is not authoritative (the pointer may be "valid" but incorrect)
|
||||
@ -112,6 +55,21 @@ bool debug_is_code_ptr(void* p)
|
||||
}
|
||||
|
||||
|
||||
static NT_TIB* get_tib()
|
||||
{
|
||||
#if ARCH_IA32
|
||||
NT_TIB* tib;
|
||||
// ICC 10 doesn't support the NT_TIB.Self syntax, so we have to use
|
||||
// a constant (asm code isn't 64-bit safe anyway).
|
||||
__asm
|
||||
{
|
||||
mov eax, fs:[NT_TIB.Self]
|
||||
mov [tib], eax
|
||||
}
|
||||
return tib;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool debug_is_stack_ptr(void* p)
|
||||
{
|
||||
uintptr_t addr = (uintptr_t)p;
|
||||
@ -167,10 +125,3 @@ void debug_set_thread_name(const char* name)
|
||||
debug_assert(0); // thread name hack doesn't work under this debugger
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static LibError wdbg_Init()
|
||||
{
|
||||
debug_heap_init();
|
||||
return INFO::OK;
|
||||
}
|
||||
|
94
source/lib/sysdep/win/wdbg_heap.cpp
Normal file
94
source/lib/sysdep/win/wdbg_heap.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "precompiled.h"
|
||||
#include "wdbg_heap.h"
|
||||
|
||||
#include <crtdbg.h>
|
||||
#include <excpt.h>
|
||||
#include "winit.h"
|
||||
|
||||
WINIT_REGISTER_CRITICAL_INIT(wdbg_heap_Init);
|
||||
|
||||
// CAUTION: called from critical init
|
||||
void wdbg_heap_Enable(bool enable)
|
||||
{
|
||||
uint flags = 0;
|
||||
if(enable)
|
||||
{
|
||||
flags |= _CRTDBG_ALLOC_MEM_DF; // enable checks at deallocation time
|
||||
flags |= _CRTDBG_LEAK_CHECK_DF; // report leaks at exit
|
||||
#if CONFIG_PARANOIA
|
||||
flags |= _CRTDBG_CHECK_ALWAYS_DF; // check during every heap operation (slow!)
|
||||
flags |= _CRTDBG_DELAY_FREE_MEM_DF; // blocks cannot be reused
|
||||
#endif
|
||||
}
|
||||
_CrtSetDbgFlag(flags);
|
||||
|
||||
// Send output to stdout as well as the debug window, so it works during
|
||||
// the normal build process as well as when debugging the test .exe
|
||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
|
||||
}
|
||||
|
||||
|
||||
void wdbg_heap_Validate()
|
||||
{
|
||||
int ret;
|
||||
__try
|
||||
{
|
||||
// NB: this is a no-op if !_CRTDBG_ALLOC_MEM_DF.
|
||||
// we could call _heapchk but that would catch fewer errors.
|
||||
ret = _CrtCheckMemory();
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
ret = _HEAPBADNODE;
|
||||
}
|
||||
|
||||
if(ret != _HEAPOK)
|
||||
DEBUG_DISPLAY_ERROR(L"Heap is corrupted!");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static int __cdecl ReportHook(int reportType, char* message, int* shouldTriggerBreakpoint)
|
||||
{
|
||||
if(message[0] == '{')
|
||||
{
|
||||
debug_printf("leak\n");
|
||||
}
|
||||
|
||||
*shouldTriggerBreakpoint = 0;
|
||||
return 1; // CRT is to display the message as normal
|
||||
}
|
||||
|
||||
static _CRT_ALLOC_HOOK previousAllocHook;
|
||||
|
||||
/**
|
||||
* @param userData is only valid (nonzero) for allocType == _HOOK_FREE because
|
||||
* we are called BEFORE the actual heap operation.
|
||||
**/
|
||||
static int __cdecl AllocHook(int allocType, void* userData, size_t size, int blockType, long requestNumber, const unsigned char* file, int line)
|
||||
{
|
||||
if(previousAllocHook)
|
||||
return previousAllocHook(allocType, userData, size, blockType, requestNumber, file, line);
|
||||
return 1; // continue as if the hook had never been called
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NB: called from critical init => can't use debug.h services
|
||||
static void InstallHooks()
|
||||
{
|
||||
int ret = _CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, ReportHook);
|
||||
if(ret == -1)
|
||||
abort();
|
||||
|
||||
previousAllocHook = _CrtSetAllocHook(AllocHook);
|
||||
}
|
||||
|
||||
static LibError wdbg_heap_Init()
|
||||
{
|
||||
InstallHooks();
|
||||
wdbg_heap_Enable(true);
|
||||
return INFO::OK;
|
||||
}
|
33
source/lib/sysdep/win/wdbg_heap.h
Normal file
33
source/lib/sysdep/win/wdbg_heap.h
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : wdbg_heap.h
|
||||
* Project : 0 A.D.
|
||||
* Description : improved debug heap using MS CRT
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
// license: GPL; see lib/license.txt
|
||||
|
||||
#ifndef INCLUDED_WDBG_HEAP
|
||||
#define INCLUDED_WDBG_HEAP
|
||||
|
||||
// this module provides a more convenient interface to the MS CRT's
|
||||
// debug heap checks. it also hooks into allocations to record the
|
||||
// caller/owner information without requiring macros (which break code
|
||||
// using placement new or member functions called free).
|
||||
|
||||
/**
|
||||
* enable or disable manual and automatic heap validity checking.
|
||||
* (enabled by default during critical_init.)
|
||||
**/
|
||||
LIB_API void wdbg_heap_Enable(bool);
|
||||
|
||||
/**
|
||||
* check heap integrity.
|
||||
* errors are reported by the CRT or via debug_display_error.
|
||||
* no effect if called between wdbg_heap_Enable(false) and the next
|
||||
* wdbg_heap_Enable(true).
|
||||
**/
|
||||
LIB_API void wdbg_heap_Validate(void);
|
||||
|
||||
#endif // #ifndef INCLUDED_WDBG_HEAP
|
@ -373,7 +373,7 @@ void CProfileNode::Frame()
|
||||
}
|
||||
|
||||
// TODO: these should probably only count allocations that occur in the thread being profiled
|
||||
#if HAVE_VC_DEBUG_ALLOC
|
||||
#if MSC_VERSION
|
||||
static intptr_t memory_alloc_count = 0;
|
||||
static int (*old_alloc_hook) (int, void*, size_t, int, long, const unsigned char*, int);
|
||||
static int alloc_hook(int allocType, void* userData, size_t size, int blockType,
|
||||
|
@ -9,6 +9,10 @@
|
||||
|
||||
#include <cxxtest/GlobalFixture.h>
|
||||
|
||||
#if OS_WIN
|
||||
#include "lib/sysdep/win/wdbg_heap.h"
|
||||
#endif
|
||||
|
||||
class LeakReporter : public CxxTest::GlobalFixture
|
||||
{
|
||||
virtual bool tearDownWorld()
|
||||
@ -16,19 +20,9 @@ class LeakReporter : public CxxTest::GlobalFixture
|
||||
// Enable leak reporting on exit.
|
||||
// (This is done in tearDownWorld so that it doesn't report 'leaks'
|
||||
// if the program is aborted before finishing cleanly.)
|
||||
|
||||
#if HAVE_VC_DEBUG_ALLOC
|
||||
int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
||||
flags |= _CRTDBG_LEAK_CHECK_DF; // check for memory leaks
|
||||
flags |= _CRTDBG_ALLOC_MEM_DF; // also check allocs using the non-debug version of new
|
||||
_CrtSetDbgFlag(flags);
|
||||
|
||||
// Send output to stdout as well as the debug window, so it works during
|
||||
// the normal build process as well as when debugging the test .exe
|
||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
|
||||
#if OS_WIN
|
||||
wdbg_heap_Enable(true);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user