Skip to content

crtdbg: fix MSVC CRTDBG memory leak reporting #1460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -3246,6 +3246,68 @@ static void adjust_symlink_flags(void)

#ifdef _DEBUG
#include <crtdbg.h>

#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

/*
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions config.mak.uname
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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

Expand Down
23 changes: 23 additions & 0 deletions git-compat-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdlib.h>
#include <crtdbg.h>
#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
Expand Down Expand Up @@ -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);
Expand All @@ -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, ...);
Expand Down
8 changes: 8 additions & 0 deletions kwset.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

#define U(c) ((unsigned char) (c))

/* Balanced tree of edges and labels leaving a given trie node. */
Expand Down
53 changes: 43 additions & 10 deletions wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

#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)
{
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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;

Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
}
Expand All @@ -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;

Expand All @@ -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;

Expand Down