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__ */


Reply via email to