Hi,
This patch is written for MSI, but could be adapted to other code. It keeps a linked list of allocated memory and removes free'd memory from the list.
It requires that you change all the HeapAlloc's or mallocs in a dll to msi_alloc (for example), but can detect unfree'd memory and invalid free's.
I have discovered quite a few problems in my own code with this patch, so hopefully somebody else will find it useful too.
Mike
Index: dlls/msi/msi.c =================================================================== RCS file: /home/mike/src/wine-cvs/wine/dlls/msi/msi.c,v retrieving revision 1.102 diff -u -p -r1.102 msi.c --- dlls/msi/msi.c 20 Sep 2005 11:59:14 -0000 1.102 +++ dlls/msi/msi.c 21 Sep 2005 15:02:32 -0000 @@ -1273,6 +1273,31 @@ end: } +struct list msi_memory_list; +CRITICAL_SECTION msi_mem_cs; + +CRITICAL_SECTION msi_mem_cs; +static CRITICAL_SECTION_DEBUG msi_mem_cs_debug = +{ + 0, 0, &msi_mem_cs, + { &msi_mem_cs_debug.ProcessLocksList, + &msi_mem_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": msi_mem_cs") } +}; +CRITICAL_SECTION msi_mem_cs = { &msi_mem_cs_debug, -1, 0, 0, 0, 0 }; + +static void msi_dump_allocated_memory(void) +{ + struct msi_mem_list *mem; + + EnterCriticalSection( &msi_mem_cs ); + LIST_FOR_EACH_ENTRY( mem, &msi_memory_list, struct msi_mem_list, entry ) + ERR("unfreed memory %p line %d file %s\n", mem, mem->line, mem->file); + LeaveCriticalSection( &msi_mem_cs ); +} + +extern void msi_close_handles(void); + /****************************************************************** * DllMain */ @@ -1281,11 +1306,13 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, switch(fdwReason) { case DLL_PROCESS_ATTACH: + list_init( &msi_memory_list ); msi_hInstance = hinstDLL; DisableThreadLibraryCalls(hinstDLL); msi_dialog_register_class(); break; case DLL_PROCESS_DETACH: + msi_dump_allocated_memory(); msi_dialog_unregister_class(); /* FIXME: Cleanup */ break; Index: dlls/msi/msipriv.h =================================================================== RCS file: /home/mike/src/wine-cvs/wine/dlls/msi/msipriv.h,v retrieving revision 1.96 diff -u -p -r1.96 msipriv.h --- dlls/msi/msipriv.h 21 Sep 2005 10:55:23 -0000 1.96 +++ dlls/msi/msipriv.h 21 Sep 2005 15:02:32 -0000 @@ -31,6 +31,7 @@ #include "objidl.h" #include "winnls.h" #include "wine/list.h" +#include "wine/debug.h" #define MSI_DATASIZEMASK 0x00ff #define MSITYPE_VALID 0x0100 @@ -418,66 +419,136 @@ extern DWORD gUIFilter; extern LPVOID gUIContext; extern WCHAR gszLogFile[MAX_PATH]; -/* memory allocation macro functions */ -static inline void *msi_alloc( size_t len ) -{ - return HeapAlloc( GetProcessHeap(), 0, len ); -} +extern struct list msi_memory_list; +extern CRITICAL_SECTION msi_mem_cs; -static inline void *msi_alloc_zero( size_t len ) -{ - return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len ); -} +#define MSI_MEM_MAGIC 0x04250919 -static inline void *msi_realloc( void *mem, size_t len ) +struct msi_mem_list { + int magic; + struct list entry; + int line; + const char *file; +}; + +#define MSI_MEMORY_DEBUG + +static inline void *__msi_alloc( int flags, size_t len, int line, const char *file ) { - return HeapReAlloc( GetProcessHeap(), 0, mem, len ); +#ifdef MSI_MEMORY_DEBUG + struct msi_mem_list *elem; + EnterCriticalSection( &msi_mem_cs ); + elem = HeapAlloc( GetProcessHeap(), flags, sizeof *elem + len ); + if (elem) + { + elem->magic = MSI_MEM_MAGIC; + elem->line = line; + elem->file = file; + list_add_tail( &msi_memory_list, &elem->entry ); + elem++; + } + LeaveCriticalSection( &msi_mem_cs ); + return elem; +#else + return HeapAlloc( GetProcessHeap(), flags, len ); +#endif } -static inline void *msi_realloc_zero( void *mem, size_t len ) +static inline void *__msi_realloc( void *mem, int flags, size_t len, int line, const char *file ) { - return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, mem, len ); -} +#ifdef MSI_MEMORY_DEBUG + struct msi_mem_list *elem = mem; + if (!elem--) return NULL; -static inline BOOL msi_free( void *mem ) + if (elem->magic != MSI_MEM_MAGIC) + { + MESSAGE("msi: bad realloc %s # %d\n", file, line); + return NULL; + } + EnterCriticalSection( &msi_mem_cs ); + list_remove( &elem->entry ); + elem = HeapReAlloc( GetProcessHeap(), flags, elem, sizeof *elem + len ); + if (elem) + { + list_add_tail( &msi_memory_list, &elem->entry ); + elem++; + } + LeaveCriticalSection( &msi_mem_cs ); + + return elem; +#else + return HeapReAlloc( GetProcessHeap(), flags, mem, len ); +#endif +} +static inline BOOL __msi_free( void *mem, int line, const char *file ) { +#ifdef MSI_MEMORY_DEBUG + struct msi_mem_list *elem = mem; + if (!elem--) return TRUE; + if (elem->magic != MSI_MEM_MAGIC) + { + MESSAGE("msi: bad free %s # %d\n", file, line); + (*(int*)0) = 0; + return FALSE; + } + elem->magic = 0; + EnterCriticalSection( &msi_mem_cs ); + list_remove( &elem->entry ); + LeaveCriticalSection( &msi_mem_cs ); + return HeapFree( GetProcessHeap(), 0, elem ); +#else return HeapFree( GetProcessHeap(), 0, mem ); +#endif } -inline static char *strdupWtoA( LPCWSTR str ) +#define msi_alloc( len ) __msi_alloc( 0, len, __LINE__, __FILE__ ) +#define msi_alloc_zero( len ) __msi_alloc( HEAP_ZERO_MEMORY, len, __LINE__, __FILE__ ) +#define msi_realloc( mem, len ) __msi_realloc( mem, 0, len, __LINE__, __FILE__ ) +#define msi_realloc_zero( mem, len ) __msi_realloc( mem, HEAP_ZERO_MEMORY, len, __LINE__, __FILE__ ) +#define msi_free( mem ) __msi_free( mem, __LINE__, __FILE__ ) + +inline static char *__strdupWtoA( LPCWSTR str, int line, const char *file ) { LPSTR ret = NULL; DWORD len; - - if (!str) return ret; + if (!str) + return ret; + len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); - ret = msi_alloc( len ); + ret = __msi_alloc( 0, len, line, file ); if (ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); return ret; } -inline static LPWSTR strdupAtoW( LPCSTR str ) +#define strdupWtoA( str ) __strdupWtoA( str, __LINE__, __FILE__ ) + +inline static LPWSTR __strdupAtoW( LPCSTR str, int line, const char *file ) { LPWSTR ret = NULL; DWORD len; - if (!str) return ret; + if (!str) + return ret; len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); - ret = msi_alloc( len * sizeof(WCHAR) ); + ret = __msi_alloc( 0, len * sizeof(WCHAR), line, file ); if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); return ret; } +#define strdupAtoW( str ) __strdupAtoW( str, __LINE__, __FILE__ ) -inline static LPWSTR strdupW( LPCWSTR src ) +inline static LPWSTR __strdupW( LPCWSTR src, int line, const char *file ) { LPWSTR dest; if (!src) return NULL; - dest = msi_alloc( (lstrlenW(src)+1)*sizeof(WCHAR) ); - if (dest) - lstrcpyW(dest, src); + dest = __msi_alloc( 0, (lstrlenW(src)+1)*sizeof(WCHAR), line, file ); + if (!dest) + return NULL; + lstrcpyW(dest, src); return dest; } +#define strdupW( str ) __strdupW( str, __LINE__, __FILE__ ) + #endif /* __WINE_MSI_PRIVATE__ */