diff --git a/compat/mingw.c b/compat/mingw.c index cccb19a69a5231..b7ab9b59d01608 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -3246,6 +3246,68 @@ static void adjust_symlink_flags(void) #ifdef _DEBUG #include + +#ifdef USE_MSVC_CRTDBG +static struct trace_key crtdbg_key = TRACE_KEY_INIT(CRTDBG); + +static void _crtdbg_atexit(void) +{ + if (trace_want(&crtdbg_key)) { + int leaks_found; + int mode = _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE; + HANDLE h = (HANDLE)_get_osfhandle(crtdbg_key.fd); + + /* + * Route the memory leak dump to the trace file *AND* to the VS + * debugger Output window. + */ + _CrtSetReportMode(_CRT_ASSERT, mode); + _CrtSetReportMode(_CRT_WARN, mode); + _CrtSetReportMode(_CRT_ERROR, mode); + + _CrtSetReportFile(_CRT_ASSERT, h); + _CrtSetReportFile(_CRT_WARN, h); + _CrtSetReportFile(_CRT_ERROR, h); + + leaks_found = _CrtDumpMemoryLeaks(); + + trace_printf_key(&crtdbg_key, "_crtdbg_atexit [%d]", leaks_found); + } else { + /* + * Route the memory leak dump to just the VS debugger Output + * window. + */ + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); + + _CrtDumpMemoryLeaks(); + } +} + +static void _crtdbg_setup(void) +{ + /* + * Enable leak detection in the CRT. + * Enable delayed freeing by the CRT (overwriting to-be-freed memory + * with 0xDD and not let memory be re-used for a while). + * + * But don't force an automatic dump, because we want to control it + * in our atexit routine. + */ + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF); + + atexit(_crtdbg_atexit); + + /* + * If they also want to dump to a file, force a real trace message + * to the dump file as a delimiter. This should help when runing + * the test suites. + */ + if (trace_want(&crtdbg_key)) + trace_printf_key(&crtdbg_key, "_crtdbg_setup"); +} +#endif #endif /* @@ -3274,10 +3336,9 @@ int msc_startup(int argc, wchar_t **w_argv, wchar_t **w_env) #ifdef _DEBUG _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); -#endif - #ifdef USE_MSVC_CRTDBG - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + _crtdbg_setup(); +#endif #endif fsync_object_files = 1; diff --git a/config.mak.uname b/config.mak.uname index aadf7a28d48b35..d241a74dd41707 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -428,8 +428,6 @@ else endif BASIC_CFLAGS += $(sdk_libs) $(msvc_libs) - # Optionally enable memory leak reporting. - # BASIC_CLFAGS += -DUSE_MSVC_CRTDBG BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1 # Always give "-Zi" to the compiler and "-debug" to linker (even in # release mode) to force a PDB to be generated (like RelWithDebInfo). @@ -446,6 +444,11 @@ ifndef DEBUG AR += -LTCG else BASIC_CFLAGS += -MDd -DDEBUG -D_DEBUG +ifdef CRTDBG + # Optionally enable memory leak reporting using: + # make MSVC=1 DEBUG=1 CRTDBG=1 + BASIC_CFLAGS += -DUSE_MSVC_CRTDBG=1 +endif endif X = .exe diff --git a/git-compat-util.h b/git-compat-util.h index aebb3e2b1cec4a..f414749d175427 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -6,8 +6,29 @@ * For these to work they must appear very early in each * file -- before most of the standard header files. */ +#define _CRTDBG_MAP_ALLOC #include #include +#undef getcwd /* don't complain about mapping to mingw_getcwd() */ +#undef free /* don't collide with structure fields named "free" */ + +#define xstrdup(s) crtdbg_xstrdup((s), __FILE__, __LINE__) +#define xmalloc(s) crtdbg_xmalloc((s), __FILE__, __LINE__) +#define xmallocz(s) crtdbg_xmallocz((s), __FILE__, __LINE__) +#define xmallocz_gently(s) crtdbg_xmallocz_gently((s), __FILE__, __LINE__) +#define xmemdupz(s, l) crtdbg_xmemdupz((s), (l), __FILE__, __LINE__) +#define xstrndup(s, l) crtdbg_xstrndup((s), (l), __FILE__, __LINE__) +#define xrealloc(p, s) crtdbg_xrealloc((p), (s), __FILE__, __LINE__) +#define xcalloc(n, s) crtdbg_xcalloc((n), (s), __FILE__, __LINE__) + +extern char *crtdbg_xstrdup(const char *str, const char *fn, int ln); +extern void *crtdbg_xmalloc(size_t size, const char *fn, int ln); +extern void *crtdbg_xmallocz(size_t size, const char *fn, int ln); +extern void *crtdbg_xmallocz_gently(size_t size, const char *fn, int ln); +extern void *crtdbg_xmemdupz(const void *data, size_t len, const char *fn, int ln); +extern char *crtdbg_xstrndup(const char *str, size_t len, const char *fn, int ln); +extern void *crtdbg_xrealloc(void *ptr, size_t size, const char *fn, int ln); +extern void *crtdbg_xcalloc(size_t nmemb, size_t size, const char *fn, int ln); #endif #define _FILE_OFFSET_BITS 64 @@ -880,6 +901,7 @@ static inline size_t st_sub(size_t a, size_t b) # define xalloca(size) (xmalloc(size)) # define xalloca_free(p) (free(p)) #endif +#ifndef USE_MSVC_CRTDBG extern char *xstrdup(const char *str); extern void *xmalloc(size_t size); extern void *xmallocz(size_t size); @@ -888,6 +910,7 @@ extern void *xmemdupz(const void *data, size_t len); extern char *xstrndup(const char *str, size_t len); extern void *xrealloc(void *ptr, size_t size); extern void *xcalloc(size_t nmemb, size_t size); +#endif extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); extern void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset); extern int xopen(const char *path, int flags, ...); diff --git a/kwset.c b/kwset.c index 4fb6455acaf129..2b989e2dd1a5fd 100644 --- a/kwset.c +++ b/kwset.c @@ -41,6 +41,14 @@ #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free +#ifdef USE_MSVC_CRTDBG +#undef xmalloc +static void *xmalloc(size_t size) +{ + return crtdbg_xmalloc(size, "kwset.c", -1); +} +#endif + #define U(c) ((unsigned char) (c)) /* Balanced tree of edges and labels leaving a given trie node. */ diff --git a/wrapper.c b/wrapper.c index d20356a776bc63..63b3a0cd325750 100644 --- a/wrapper.c +++ b/wrapper.c @@ -4,6 +4,39 @@ #include "cache.h" #include "config.h" +#ifdef USE_MSVC_CRTDBG +#undef strdup /* cancel definitions from crtdbg.h because they hard */ +#undef calloc /* code __FILE__ and __LINE__ */ +#undef malloc +#undef realloc + +#define strdup(s) (_strdup_dbg((s), _NORMAL_BLOCK, caller_fn, caller_ln)) +#define calloc(n, s) (_calloc_dbg((n), (s), _NORMAL_BLOCK, caller_fn, caller_ln)) +#define malloc(s) (_malloc_dbg((s), _NORMAL_BLOCK, caller_fn, caller_ln)) +#define realloc(p, s) (_realloc_dbg((p), (s), _NORMAL_BLOCK, caller_fn, caller_ln)) + +#define do_xmalloc(s, g) (crtdbg_do_xmalloc((s), (g), caller_fn, caller_ln)) +#define do_xmallocz(s, g) (crtdbg_do_xmallocz((s), (g), caller_fn, caller_ln)) + +#define DCL_1(f, arg1) crtdbg_##f(arg1, const char *caller_fn, int caller_ln) +#define DCL_2(f, arg1, arg2) crtdbg_##f(arg1, arg2, const char *caller_fn, int caller_ln) +#else +#define DCL_1(f, arg1) f(arg1) +#define DCL_2(f, arg1, arg2) f(arg1, arg2) +#endif + +#define DCL_XSTRDUP(arg1) DCL_1(xstrdup, arg1) +#define DCL_XMALLOC(arg1) DCL_1(xmalloc, arg1) +#define DCL_XMALLOCZ(arg1) DCL_1(xmallocz, arg1) +#define DCL_XMALLOCZ_GENTLY(arg1) DCL_1(xmallocz_gently, arg1) +#define DCL_XMEMDUPZ(arg1, arg2) DCL_2(xmemdupz, arg1, arg2) +#define DCL_XSTRNDUP(arg1, arg2) DCL_2(xstrndup, arg1, arg2) +#define DCL_XREALLOC(arg1, arg2) DCL_2(xrealloc, arg1, arg2) +#define DCL_XCALLOC(arg1, arg2) DCL_2(xcalloc, arg1, arg2) + +#define DCL_DO_XMALLOC(arg1, arg2) DCL_2(do_xmalloc, arg1, arg2) +#define DCL_DO_XMALLOCZ(arg1, arg2) DCL_2(do_xmallocz, arg1, arg2) + static void do_nothing(size_t size) { } @@ -39,7 +72,7 @@ try_to_free_t set_try_to_free_routine(try_to_free_t routine) return old; } -char *xstrdup(const char *str) +char *DCL_XSTRDUP(const char *str) { char *ret = strdup(str); if (!ret) { @@ -51,7 +84,7 @@ char *xstrdup(const char *str) return ret; } -static void *do_xmalloc(size_t size, int gentle) +static void *DCL_DO_XMALLOC(size_t size, int gentle) { void *ret; @@ -82,12 +115,12 @@ static void *do_xmalloc(size_t size, int gentle) return ret; } -void *xmalloc(size_t size) +void *DCL_XMALLOC(size_t size) { return do_xmalloc(size, 0); } -static void *do_xmallocz(size_t size, int gentle) +static void *DCL_DO_XMALLOCZ(size_t size, int gentle) { void *ret; if (unsigned_add_overflows(size, 1)) { @@ -103,12 +136,12 @@ static void *do_xmallocz(size_t size, int gentle) return ret; } -void *xmallocz(size_t size) +void *DCL_XMALLOCZ(size_t size) { return do_xmallocz(size, 0); } -void *xmallocz_gently(size_t size) +void *DCL_XMALLOCZ_GENTLY(size_t size) { return do_xmallocz(size, 1); } @@ -119,18 +152,18 @@ void *xmallocz_gently(size_t size) * and returns a pointer to the allocated memory. If the allocation fails, * the program dies. */ -void *xmemdupz(const void *data, size_t len) +void *DCL_XMEMDUPZ(const void *data, size_t len) { return memcpy(xmallocz(len), data, len); } -char *xstrndup(const char *str, size_t len) +char *DCL_XSTRNDUP(const char *str, size_t len) { char *p = memchr(str, '\0', len); return xmemdupz(str, p ? p - str : len); } -void *xrealloc(void *ptr, size_t size) +void *DCL_XREALLOC(void *ptr, size_t size) { void *ret; @@ -149,7 +182,7 @@ void *xrealloc(void *ptr, size_t size) return ret; } -void *xcalloc(size_t nmemb, size_t size) +void *DCL_XCALLOC(size_t nmemb, size_t size) { void *ret;