[8] | 1 |
|
---|
| 2 | /*
|
---|
[13] | 3 | *@@sourcefile memdebug.c:
|
---|
| 4 | * memory debugging helpers.
|
---|
[8] | 5 | *
|
---|
| 6 | * Several things are in here which might turn out to
|
---|
| 7 | * be useful:
|
---|
| 8 | *
|
---|
| 9 | * -- Memory block dumping (memdDumpMemoryBlock).
|
---|
| 10 | *
|
---|
| 11 | * -- Sophisticated heap debugging functions, which
|
---|
| 12 | * automatically replace malloc() and free() etc.
|
---|
[13] | 13 | * If the __XWPMEMDEBUG__ #define is set before including
|
---|
| 14 | * memdebug.h, all those standard library calls
|
---|
| 15 | * are remapped to use the functions in this file
|
---|
| 16 | * instead.
|
---|
| 17 | *
|
---|
| 18 | * At present, malloc(), calloc(), realloc(), strdup()
|
---|
| 19 | * and free() are supported.
|
---|
| 20 | *
|
---|
[8] | 21 | * These log every memory allocation made and log
|
---|
| 22 | * much more data compared to the VAC++ memory
|
---|
| 23 | * debugging functions. See HEAPITEM for details.
|
---|
| 24 | * Automatic heap checking is also included, using
|
---|
| 25 | * special "magic string" which are checked to
|
---|
| 26 | * detect overwrites.
|
---|
| 27 | *
|
---|
| 28 | * To be able to trace memory errors, set the global
|
---|
| 29 | * variable G_pMemdLogFunc to a function which can
|
---|
| 30 | * write an error string to a meaningful place (the
|
---|
| 31 | * screen or a file). WARNING: While that error function
|
---|
| 32 | * is executed, the system might be in a global memory
|
---|
| 33 | * lock, so DON'T display a message box while in that
|
---|
[123] | 34 | * function, and DO NOT call malloc() or other memory
|
---|
| 35 | * functions in there.
|
---|
[8] | 36 | *
|
---|
| 37 | * These debug functions have been added with V0.9.3
|
---|
| 38 | * and should now be compiler-independent.
|
---|
| 39 | *
|
---|
[13] | 40 | * V0.9.6 added realloc() support and fixed a few bugs.
|
---|
| 41 | *
|
---|
[123] | 42 | * With V0.9.16, most of this was rewritten to be much
|
---|
| 43 | * faster. This no longer slows down the system enormously.
|
---|
| 44 | *
|
---|
[13] | 45 | * -- A PM heap debugging window which shows the status
|
---|
[8] | 46 | * of the heap logging list. See memdCreateMemDebugWindow
|
---|
[13] | 47 | * (memdebug_win.c) for details.
|
---|
[8] | 48 | *
|
---|
[13] | 49 | * To enable memory debugging, do the following in each (!)
|
---|
| 50 | * of your code modules:
|
---|
[8] | 51 | *
|
---|
| 52 | * 1) Include at least <stdlib.h> and <string.h>.
|
---|
| 53 | *
|
---|
| 54 | * 2) Include memdebug.h AFTER those two. This will remap
|
---|
[123] | 55 | * the malloc() etc. calls to the debug functions in
|
---|
| 56 | * this file by defining macros for them.
|
---|
[8] | 57 | *
|
---|
| 58 | * If you don't want those replaced, add
|
---|
| 59 | + #define DONT_REPLACE_MALLOC
|
---|
| 60 | * before including memdebug.h.
|
---|
| 61 | *
|
---|
[123] | 62 | * To avoid calling a debug function for a single call,
|
---|
| 63 | * place the malloc call (or whatever) in brackets.
|
---|
| 64 | *
|
---|
[8] | 65 | * That's all. XWorkplace's setup.h does this automatically
|
---|
| 66 | * if XWorkplace is compiled with debug code.
|
---|
| 67 | *
|
---|
[13] | 68 | * A couple of WARNINGS:
|
---|
| 69 | *
|
---|
[123] | 70 | * 1) When free() is invoked, the memory that was allocated
|
---|
| 71 | * is freed, but not the memory log entry (the HEAPITEM)
|
---|
| 72 | * to allow tracing what was freed. As a result, the tree
|
---|
| 73 | * of memory items keeps growing longer. Do not expect
|
---|
| 74 | * this to work forever, even though things have greatly
|
---|
| 75 | * improved with V0.9.16.
|
---|
[13] | 76 | *
|
---|
| 77 | * 2) The replacement functions in this file allocate
|
---|
| 78 | * extra memory for the magic strings. For example, if
|
---|
| 79 | * you call malloc(100), more than 100 bytes get allocated
|
---|
| 80 | * to allow for storing the magic strings to detect
|
---|
| 81 | * memory overwrites. Two magic strings are allocated,
|
---|
| 82 | * one before the actual buffer, and one behind it.
|
---|
[123] | 83 | * The pointer returned is _not_ identical to the one
|
---|
| 84 | * that was internally allocated.
|
---|
[13] | 85 | *
|
---|
| 86 | * As a result, YOU MUST NOT confuse the replacement
|
---|
| 87 | * memory functions with the original ones. If you
|
---|
| 88 | * use malloc() in one source file and free() the
|
---|
| 89 | * buffer in another one where debug memory has not
|
---|
| 90 | * been enabled, you'll get crashes.
|
---|
| 91 | *
|
---|
| 92 | * As a rule of thumb, enable memory debugging for all
|
---|
| 93 | * your source files or for none. And make sure everything
|
---|
| 94 | * is recompiled when you change your mind.
|
---|
| 95 | *
|
---|
[8] | 96 | *@@added V0.9.1 (2000-02-12) [umoeller]
|
---|
| 97 | */
|
---|
| 98 |
|
---|
| 99 | /*
|
---|
[93] | 100 | * Copyright (C) 2000-2001 Ulrich Mller.
|
---|
[8] | 101 | * This program is part of the XWorkplace package.
|
---|
| 102 | * This program is free software; you can redistribute it and/or modify
|
---|
| 103 | * it under the terms of the GNU General Public License as published by
|
---|
| 104 | * the Free Software Foundation, in version 2 as it comes in the COPYING
|
---|
| 105 | * file of the XWorkplace main distribution.
|
---|
| 106 | * This program is distributed in the hope that it will be useful,
|
---|
| 107 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 108 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 109 | * GNU General Public License for more details.
|
---|
| 110 | */
|
---|
| 111 |
|
---|
| 112 | #define OS2EMX_PLAIN_CHAR
|
---|
| 113 | // this is needed for "os2emx.h"; if this is defined,
|
---|
| 114 | // emx will define PSZ as _signed_ char, otherwise
|
---|
| 115 | // as unsigned char
|
---|
| 116 |
|
---|
| 117 | #define INCL_DOSSEMAPHORES
|
---|
| 118 | #define INCL_DOSEXCEPTIONS
|
---|
| 119 | #define INCL_DOSPROCESS
|
---|
| 120 | #define INCL_DOSERRORS
|
---|
| 121 |
|
---|
| 122 | #include <os2.h>
|
---|
| 123 |
|
---|
| 124 | #include <stdio.h>
|
---|
| 125 | #include <string.h>
|
---|
| 126 | #include <setjmp.h>
|
---|
| 127 |
|
---|
[123] | 128 | #include "helpers\tree.h"
|
---|
| 129 |
|
---|
[13] | 130 | #define DONT_REPLACE_MALLOC // never do debug memory for this
|
---|
| 131 | #define MEMDEBUG_PRIVATE
|
---|
[8] | 132 | #include "setup.h"
|
---|
| 133 |
|
---|
| 134 | #ifdef __XWPMEMDEBUG__
|
---|
| 135 |
|
---|
[123] | 136 | #include "helpers\dosh.h"
|
---|
[8] | 137 | #include "helpers\except.h"
|
---|
[123] | 138 |
|
---|
[13] | 139 | #include "helpers\memdebug.h" // included by setup.h already
|
---|
[8] | 140 | #include "helpers\stringh.h"
|
---|
| 141 |
|
---|
| 142 | /*
|
---|
| 143 | *@@category: Helpers\C helpers\Heap debugging
|
---|
[21] | 144 | * See memdebug.c.
|
---|
[8] | 145 | */
|
---|
| 146 |
|
---|
| 147 | /* ******************************************************************
|
---|
[14] | 148 | *
|
---|
| 149 | * Global variables
|
---|
| 150 | *
|
---|
[8] | 151 | ********************************************************************/
|
---|
| 152 |
|
---|
| 153 | #define MEMBLOCKMAGIC_HEAD "\210\203`H&cx$&%\254"
|
---|
| 154 | // size must be a multiple of 4 or dword-alignment will fail;
|
---|
| 155 | // there's an extra 0 byte at the end, so we have a size of 12
|
---|
| 156 | // V0.9.3 (2000-04-17) [umoeller]
|
---|
| 157 | #define MEMBLOCKMAGIC_TAIL "\250\210&%/dfjsk%#,dlhf\223"
|
---|
| 158 |
|
---|
[123] | 159 | HMTX G_hmtxMallocList = NULLHANDLE;
|
---|
[8] | 160 |
|
---|
[123] | 161 | extern TREE *G_pHeapItemsRoot = NULL;
|
---|
| 162 | extern LONG G_cHeapItems = 0;
|
---|
[8] | 163 |
|
---|
[123] | 164 | PFNCBMEMDLOG G_pMemdLogFunc = NULL;
|
---|
[8] | 165 |
|
---|
[123] | 166 | extern ULONG G_ulItemsReleased = 0;
|
---|
| 167 | extern ULONG G_ulBytesReleased = 0;
|
---|
[8] | 168 |
|
---|
| 169 | /* ******************************************************************
|
---|
[14] | 170 | *
|
---|
| 171 | * Debug heap management
|
---|
| 172 | *
|
---|
[8] | 173 | ********************************************************************/
|
---|
| 174 |
|
---|
| 175 | /*
|
---|
| 176 | *@@ memdLock:
|
---|
| 177 | * enables the global memory lock to protect
|
---|
| 178 | * the global variables here. Use memdUnlock
|
---|
| 179 | * to unlock again, and lock only for the shortest
|
---|
| 180 | * possible time. This is only used by the memdebug.c
|
---|
| 181 | * functions.
|
---|
| 182 | *
|
---|
| 183 | *@@added V0.9.3 (2000-04-10) [umoeller]
|
---|
| 184 | */
|
---|
| 185 |
|
---|
| 186 | BOOL memdLock(VOID)
|
---|
| 187 | {
|
---|
[123] | 188 | if (!G_hmtxMallocList)
|
---|
| 189 | {
|
---|
[8] | 190 | // first call:
|
---|
[123] | 191 | if (!DosCreateMutexSem(NULL,
|
---|
| 192 | &G_hmtxMallocList,
|
---|
| 193 | 0, // unshared
|
---|
| 194 | TRUE)) // request now!
|
---|
| 195 | {
|
---|
| 196 | treeInit(&G_pHeapItemsRoot, &G_cHeapItems);
|
---|
| 197 | return TRUE;
|
---|
| 198 | }
|
---|
| 199 | }
|
---|
[13] | 200 | else
|
---|
[238] | 201 | return !DosRequestMutexSem(G_hmtxMallocList,
|
---|
| 202 | SEM_INDEFINITE_WAIT);
|
---|
[8] | 203 |
|
---|
[238] | 204 | return FALSE;
|
---|
[8] | 205 | }
|
---|
| 206 |
|
---|
| 207 | /*
|
---|
| 208 | *@@ memdUnlock:
|
---|
| 209 | * the reverse to memdLock.
|
---|
| 210 | *
|
---|
| 211 | *@@added V0.9.3 (2000-04-10) [umoeller]
|
---|
| 212 | */
|
---|
| 213 |
|
---|
| 214 | VOID memdUnlock(VOID)
|
---|
| 215 | {
|
---|
| 216 | DosReleaseMutexSem(G_hmtxMallocList);
|
---|
| 217 | }
|
---|
| 218 |
|
---|
| 219 | /*
|
---|
[123] | 220 | *@@ LogError:
|
---|
| 221 | *
|
---|
| 222 | *@@added V0.9.16 (2001-12-08) [umoeller]
|
---|
| 223 | */
|
---|
| 224 |
|
---|
[222] | 225 | STATIC VOID LogError(const char *pcszFormat, // in: format string (like with printf)
|
---|
[142] | 226 | ...) // in: additional stuff (like with printf)
|
---|
[123] | 227 | {
|
---|
| 228 | if (G_pMemdLogFunc)
|
---|
| 229 | {
|
---|
| 230 | CHAR szMsg[1000];
|
---|
| 231 | va_list args;
|
---|
| 232 |
|
---|
| 233 | va_start(args, pcszFormat);
|
---|
| 234 | vsprintf(szMsg, pcszFormat, args);
|
---|
| 235 | va_end(args);
|
---|
| 236 | G_pMemdLogFunc(szMsg);
|
---|
| 237 | }
|
---|
| 238 | }
|
---|
| 239 |
|
---|
| 240 | /*
|
---|
| 241 | *@@ FindHeapItem:
|
---|
| 242 | *
|
---|
| 243 | *@@added V0.9.16 (2001-12-08) [umoeller]
|
---|
| 244 | */
|
---|
| 245 |
|
---|
[222] | 246 | STATIC PHEAPITEM FindHeapItem(void *p)
|
---|
[123] | 247 | {
|
---|
[238] | 248 | return (PHEAPITEM)treeFind(G_pHeapItemsRoot,
|
---|
| 249 | (ULONG)p,
|
---|
| 250 | treeCompareKeys);
|
---|
[123] | 251 | }
|
---|
| 252 |
|
---|
| 253 | /*
|
---|
| 254 | *@@ FillHeapItem:
|
---|
| 255 | *
|
---|
| 256 | *@@added V0.9.16 (2001-12-08) [umoeller]
|
---|
| 257 | */
|
---|
| 258 |
|
---|
[222] | 259 | STATIC VOID FillHeapItem(PHEAPITEM pHeapItem,
|
---|
[142] | 260 | void *prc,
|
---|
| 261 | size_t stSize,
|
---|
| 262 | const char *pcszSourceFile, // in: source file name
|
---|
| 263 | unsigned long ulLine, // in: source line
|
---|
| 264 | const char *pcszFunction) // in: function name
|
---|
[123] | 265 | {
|
---|
| 266 | pHeapItem->ulSize = stSize;
|
---|
| 267 |
|
---|
| 268 | pHeapItem->pcszSourceFile = pcszSourceFile;
|
---|
| 269 | pHeapItem->ulLine = ulLine;
|
---|
| 270 | pHeapItem->pcszFunction = pcszFunction;
|
---|
| 271 |
|
---|
| 272 | DosGetDateTime(&pHeapItem->dtAllocated);
|
---|
| 273 |
|
---|
| 274 | pHeapItem->ulTID = doshMyTID();
|
---|
| 275 |
|
---|
| 276 | pHeapItem->fFreed = FALSE;
|
---|
| 277 |
|
---|
| 278 | // use the return pointer as the tree sort key
|
---|
| 279 | // V0.9.16 (2001-12-08) [umoeller]
|
---|
| 280 | pHeapItem->Tree.ulKey = (ULONG)prc;
|
---|
| 281 | }
|
---|
| 282 |
|
---|
| 283 | /*
|
---|
| 284 | *@@ CheckMagics:
|
---|
| 285 | *
|
---|
| 286 | *@@added V0.9.16 (2001-12-08) [umoeller]
|
---|
| 287 | */
|
---|
| 288 |
|
---|
[222] | 289 | STATIC VOID CheckMagics(const char *pcszParentFunc,
|
---|
[142] | 290 | PHEAPITEM pHeapItem,
|
---|
| 291 | PBYTE p,
|
---|
| 292 | const char *pcszSourceFile, // in: source file name
|
---|
| 293 | unsigned long ulLine, // in: source line
|
---|
| 294 | const char *pcszFunction) // in: function name
|
---|
[123] | 295 | {
|
---|
| 296 | void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
| 297 | ULONG ulError = 0;
|
---|
| 298 |
|
---|
| 299 | // check magic string
|
---|
| 300 | if (memcmp(pBeforeMagic,
|
---|
| 301 | MEMBLOCKMAGIC_HEAD,
|
---|
| 302 | sizeof(MEMBLOCKMAGIC_HEAD)))
|
---|
| 303 | ulError = 1;
|
---|
| 304 | else if (memcmp(((PBYTE)p) + pHeapItem->ulSize,
|
---|
| 305 | MEMBLOCKMAGIC_TAIL,
|
---|
| 306 | sizeof(MEMBLOCKMAGIC_TAIL)))
|
---|
| 307 | ulError = 2;
|
---|
| 308 |
|
---|
| 309 | if (ulError)
|
---|
| 310 | {
|
---|
| 311 | LogError("%s: Magic string %s memory block at 0x%lX has been overwritten.\n"
|
---|
| 312 | "This was detected by the free() call at %s (%s, line %d).\n"
|
---|
| 313 | "The block was allocated by %s (%s, line %d).",
|
---|
| 314 | pcszParentFunc,
|
---|
| 315 | (ulError == 1) ? "before" : "after",
|
---|
| 316 | p,
|
---|
| 317 | pcszFunction,
|
---|
| 318 | pcszSourceFile,
|
---|
| 319 | ulLine, // free
|
---|
| 320 | pHeapItem->pcszFunction,
|
---|
| 321 | pHeapItem->pcszSourceFile,
|
---|
| 322 | pHeapItem->ulLine);
|
---|
| 323 | }
|
---|
| 324 | }
|
---|
| 325 |
|
---|
| 326 | /*
|
---|
[8] | 327 | *@@ memdMalloc:
|
---|
| 328 | * wrapper function for malloc() to trace malloc()
|
---|
| 329 | * calls more precisely.
|
---|
| 330 | *
|
---|
| 331 | * If XWorkplace is compiled with debug code, setup.h
|
---|
| 332 | * automatically #includes memdebug.h, which maps
|
---|
| 333 | * malloc to this function so that the source file
|
---|
| 334 | * etc. parameters automatically get passed.
|
---|
| 335 | *
|
---|
| 336 | * For each call, we call the default malloc(), whose
|
---|
| 337 | * return value is returned, and create a HEAPITEM
|
---|
| 338 | * for remembering the call, which is stored in a global
|
---|
| 339 | * linked list.
|
---|
| 340 | *
|
---|
| 341 | *@@added V0.9.3 (2000-04-11) [umoeller]
|
---|
[123] | 342 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster
|
---|
[8] | 343 | */
|
---|
| 344 |
|
---|
| 345 | void* memdMalloc(size_t stSize,
|
---|
| 346 | const char *pcszSourceFile, // in: source file name
|
---|
| 347 | unsigned long ulLine, // in: source line
|
---|
| 348 | const char *pcszFunction) // in: function name
|
---|
| 349 | {
|
---|
| 350 | void *prc = NULL;
|
---|
| 351 |
|
---|
| 352 | if (stSize == 0)
|
---|
| 353 | // malloc(0) called: report error
|
---|
[123] | 354 | LogError(__FUNCTION__ ": Function %s (%s, line %d) called malloc(0).",
|
---|
| 355 | pcszFunction,
|
---|
| 356 | pcszSourceFile,
|
---|
| 357 | ulLine);
|
---|
| 358 | else
|
---|
| 359 | if (memdLock())
|
---|
[8] | 360 | {
|
---|
[123] | 361 | // call default malloc(), but with the additional
|
---|
| 362 | // size of our MEMBLOCKMAGIC strings; we'll return
|
---|
| 363 | // the first byte after the "front" string so we can
|
---|
| 364 | // check for string overwrites
|
---|
| 365 | void *pObj;
|
---|
[8] | 366 |
|
---|
[123] | 367 | if (pObj = malloc( sizeof(MEMBLOCKMAGIC_HEAD)
|
---|
| 368 | + stSize
|
---|
| 369 | + sizeof(MEMBLOCKMAGIC_TAIL)))
|
---|
[8] | 370 | {
|
---|
[123] | 371 | PHEAPITEM pHeapItem;
|
---|
| 372 | BOOL fInsert = TRUE;
|
---|
[8] | 373 |
|
---|
[123] | 374 | // store "front" magic string
|
---|
| 375 | memcpy(pObj,
|
---|
| 376 | MEMBLOCKMAGIC_HEAD,
|
---|
| 377 | sizeof(MEMBLOCKMAGIC_HEAD));
|
---|
| 378 | // return address: first byte after "front" magic string
|
---|
| 379 | prc = ((PBYTE)pObj) + sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
| 380 | // store "tail" magic string to block which
|
---|
| 381 | // will be returned plus the size which was requested
|
---|
| 382 | memcpy(((PBYTE)prc) + stSize,
|
---|
| 383 | MEMBLOCKMAGIC_TAIL,
|
---|
| 384 | sizeof(MEMBLOCKMAGIC_TAIL));
|
---|
[8] | 385 |
|
---|
[123] | 386 | if (!(pHeapItem = FindHeapItem(prc)))
|
---|
| 387 | // not re-using old address:
|
---|
| 388 | // create a new heap item
|
---|
| 389 | pHeapItem = (PHEAPITEM)malloc(sizeof(HEAPITEM));
|
---|
| 390 | else
|
---|
| 391 | fInsert = FALSE;
|
---|
[8] | 392 |
|
---|
[123] | 393 | FillHeapItem(pHeapItem,
|
---|
| 394 | prc,
|
---|
| 395 | stSize,
|
---|
| 396 | pcszSourceFile,
|
---|
| 397 | ulLine,
|
---|
| 398 | pcszFunction);
|
---|
[8] | 399 |
|
---|
[123] | 400 | if (fInsert)
|
---|
| 401 | // append heap item to linked list
|
---|
| 402 | if (treeInsert(&G_pHeapItemsRoot,
|
---|
| 403 | &G_cHeapItems,
|
---|
| 404 | (TREE*)pHeapItem,
|
---|
| 405 | treeCompareKeys))
|
---|
[8] | 406 | {
|
---|
[123] | 407 | LogError(__FUNCTION__ ": treeInsert failed for memory block at 0x%lX.\n"
|
---|
| 408 | "The block was allocated by %s (%s, line %d).",
|
---|
| 409 | prc,
|
---|
| 410 | pcszFunction,
|
---|
| 411 | pcszSourceFile,
|
---|
| 412 | ulLine);
|
---|
[8] | 413 | }
|
---|
[123] | 414 | }
|
---|
[8] | 415 |
|
---|
[123] | 416 | memdUnlock();
|
---|
[8] | 417 | }
|
---|
| 418 |
|
---|
[238] | 419 | return prc;
|
---|
[8] | 420 | }
|
---|
| 421 |
|
---|
| 422 | /*
|
---|
| 423 | *@@ memdCalloc:
|
---|
| 424 | * similar to memdMalloc; this is the wrapper for
|
---|
| 425 | * the calloc() call. This is automatically
|
---|
| 426 | * remapped also.
|
---|
| 427 | *
|
---|
| 428 | *@@added V0.9.3 (2000-04-11) [umoeller]
|
---|
| 429 | */
|
---|
| 430 |
|
---|
| 431 | void* memdCalloc(size_t num,
|
---|
| 432 | size_t stSize,
|
---|
| 433 | const char *pcszSourceFile,
|
---|
| 434 | unsigned long ulLine,
|
---|
| 435 | const char *pcszFunction)
|
---|
| 436 | {
|
---|
| 437 | void *p = memdMalloc(num * stSize,
|
---|
| 438 | pcszSourceFile,
|
---|
| 439 | ulLine,
|
---|
| 440 | pcszFunction);
|
---|
| 441 | memset(p, 0, num * stSize);
|
---|
[238] | 442 | return p;
|
---|
[8] | 443 | }
|
---|
| 444 |
|
---|
| 445 | /*
|
---|
| 446 | *@@ memdFree:
|
---|
| 447 | * wrapper for the free() call, which is remapped
|
---|
| 448 | * by setup.h and memdebug.h like memdMalloc
|
---|
| 449 | * and memdCalloc. This searches the memory object
|
---|
| 450 | * (p) which was previously allocated on the linked
|
---|
| 451 | * list of HEAPITEM's and frees it then by calling
|
---|
| 452 | * the default free().
|
---|
| 453 | *
|
---|
| 454 | * The HEAPITEM itself is not freed, but only marked
|
---|
| 455 | * as freed. As a result, the linked list can grow
|
---|
| 456 | * REALLY large. While memdMalloc does not become
|
---|
| 457 | * slower with large HEAPITEM lists because it only
|
---|
| 458 | * appends to the end of the list, which is remembered,
|
---|
| 459 | * memdFree can become extremely slow because the entire
|
---|
| 460 | * list needs to be searched with each call.
|
---|
| 461 | * So call memdReleaseFreed from time to time.
|
---|
| 462 | *
|
---|
| 463 | *@@added V0.9.3 (2000-04-10) [umoeller]
|
---|
[123] | 464 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster
|
---|
[8] | 465 | */
|
---|
| 466 |
|
---|
| 467 | void memdFree(void *p,
|
---|
| 468 | const char *pcszSourceFile,
|
---|
| 469 | unsigned long ulLine,
|
---|
| 470 | const char *pcszFunction)
|
---|
| 471 | {
|
---|
| 472 | if (memdLock())
|
---|
| 473 | {
|
---|
[123] | 474 | PHEAPITEM pHeapItem;
|
---|
[8] | 475 |
|
---|
| 476 | // search the list with the pointer which was
|
---|
| 477 | // really returned by the original malloc(),
|
---|
[123] | 478 | // that is, the byte after the magic string
|
---|
| 479 | if (pHeapItem = FindHeapItem(p))
|
---|
[8] | 480 | {
|
---|
[123] | 481 | // the same address may be allocated and freed
|
---|
| 482 | // several times, so check
|
---|
| 483 | if (!pHeapItem->fFreed)
|
---|
[91] | 484 | {
|
---|
[123] | 485 | // found:
|
---|
| 486 | void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
[8] | 487 |
|
---|
[123] | 488 | CheckMagics(__FUNCTION__,
|
---|
| 489 | pHeapItem,
|
---|
[164] | 490 | (PBYTE)p,
|
---|
[123] | 491 | pcszSourceFile,
|
---|
| 492 | ulLine,
|
---|
| 493 | pcszFunction);
|
---|
[8] | 494 |
|
---|
[123] | 495 | // free the real memory item
|
---|
| 496 | free(pBeforeMagic);
|
---|
[8] | 497 |
|
---|
[123] | 498 | // mark the heap item as freed, but
|
---|
| 499 | // keep it in the list
|
---|
| 500 | pHeapItem->fFreed = TRUE;
|
---|
[8] | 501 |
|
---|
[123] | 502 | } // if (!pHeapItem->fFreed)
|
---|
| 503 | else
|
---|
| 504 | // memory block has been freed twice:
|
---|
| 505 | LogError(__FUNCTION__ ": Memory block at 0x%lX has been freed twice.\n"
|
---|
| 506 | "This was detected by the free() call at %s (%s, line %d).\n"
|
---|
| 507 | "The block was originally allocated by %s (%s, line %d).",
|
---|
| 508 | p,
|
---|
| 509 | pcszFunction,
|
---|
| 510 | pcszSourceFile,
|
---|
| 511 | ulLine, // free
|
---|
| 512 | pHeapItem->pcszFunction,
|
---|
| 513 | pHeapItem->pcszSourceFile,
|
---|
| 514 | pHeapItem->ulLine);
|
---|
[8] | 515 | }
|
---|
[123] | 516 | else
|
---|
| 517 | // not found:
|
---|
| 518 | LogError(__FUNCTION__ ": free() called with invalid object 0x%lX from %s (%s, line %d).",
|
---|
| 519 | p,
|
---|
| 520 | pcszFunction,
|
---|
| 521 | pcszSourceFile,
|
---|
| 522 | ulLine);
|
---|
[8] | 523 |
|
---|
| 524 | memdUnlock();
|
---|
| 525 | }
|
---|
| 526 | }
|
---|
| 527 |
|
---|
| 528 | /*
|
---|
[13] | 529 | *@@ memdRealloc:
|
---|
| 530 | * wrapper function for realloc(). See memdMalloc for
|
---|
| 531 | * details.
|
---|
| 532 | *
|
---|
| 533 | *@@added V0.9.6 (2000-11-12) [umoeller]
|
---|
[71] | 534 | *@@changed V0.9.12 (2001-05-21) [umoeller]: this reported errors on realloc(0), which is a valid call, fixed
|
---|
[123] | 535 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster
|
---|
[13] | 536 | */
|
---|
| 537 |
|
---|
| 538 | void* memdRealloc(void *p,
|
---|
| 539 | size_t stSize,
|
---|
| 540 | const char *pcszSourceFile, // in: source file name
|
---|
| 541 | unsigned long ulLine, // in: source line
|
---|
| 542 | const char *pcszFunction) // in: function name
|
---|
| 543 | {
|
---|
| 544 | void *prc = NULL;
|
---|
| 545 |
|
---|
[71] | 546 | if (!p)
|
---|
| 547 | // p == NULL: this is valid, use malloc() instead
|
---|
| 548 | // V0.9.12 (2001-05-21) [umoeller]
|
---|
[238] | 549 | return memdMalloc(stSize, pcszSourceFile, ulLine, pcszFunction);
|
---|
[71] | 550 |
|
---|
[13] | 551 | if (memdLock())
|
---|
| 552 | {
|
---|
| 553 | // search the list with the pointer which was
|
---|
| 554 | // really returned by the original malloc(),
|
---|
[123] | 555 | // that is, the byte after the magic string
|
---|
| 556 | PHEAPITEM pHeapItem, pExisting;
|
---|
| 557 | if (pHeapItem = FindHeapItem(p))
|
---|
[13] | 558 | {
|
---|
[123] | 559 | // found:
|
---|
| 560 | if (pHeapItem->fFreed)
|
---|
| 561 | {
|
---|
| 562 | LogError(__FUNCTION__ ": realloc() called with memory block at 0x%lX that was already freed.\n"
|
---|
| 563 | "This was detected by the realloc() call at %s (%s, line %d).\n"
|
---|
| 564 | "The block was originally allocated by %s (%s, line %d).",
|
---|
| 565 | p,
|
---|
| 566 | pcszFunction,
|
---|
| 567 | pcszSourceFile,
|
---|
| 568 | ulLine, // free
|
---|
| 569 | pHeapItem->pcszFunction,
|
---|
| 570 | pHeapItem->pcszSourceFile,
|
---|
| 571 | pHeapItem->ulLine);
|
---|
| 572 | }
|
---|
| 573 | else
|
---|
| 574 | {
|
---|
| 575 | // block is valid:
|
---|
| 576 | void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
| 577 | PVOID pObjNew = 0;
|
---|
| 578 | ULONG ulError = 0;
|
---|
| 579 | ULONG cbCopy = 0;
|
---|
[13] | 580 |
|
---|
[123] | 581 | CheckMagics(__FUNCTION__,
|
---|
| 582 | pHeapItem,
|
---|
[164] | 583 | (PBYTE)p,
|
---|
[123] | 584 | pcszSourceFile,
|
---|
| 585 | ulLine,
|
---|
| 586 | pcszFunction);
|
---|
[13] | 587 |
|
---|
[123] | 588 | // now reallocate!
|
---|
| 589 | pObjNew = malloc( sizeof(MEMBLOCKMAGIC_HEAD)
|
---|
| 590 | + stSize // new size
|
---|
| 591 | + sizeof(MEMBLOCKMAGIC_TAIL));
|
---|
[13] | 592 |
|
---|
[123] | 593 | // store "front" magic string
|
---|
| 594 | memcpy(pObjNew,
|
---|
| 595 | MEMBLOCKMAGIC_HEAD,
|
---|
| 596 | sizeof(MEMBLOCKMAGIC_HEAD));
|
---|
| 597 | // return address: first byte after "front" magic string
|
---|
| 598 | prc = ((PBYTE)pObjNew) + sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
[13] | 599 |
|
---|
[123] | 600 | // bytes to copy: the smaller of the old and the new size
|
---|
| 601 | cbCopy = pHeapItem->ulSize;
|
---|
| 602 | if (stSize < pHeapItem->ulSize)
|
---|
| 603 | cbCopy = stSize;
|
---|
[13] | 604 |
|
---|
[123] | 605 | // copy buffer from old memory object
|
---|
| 606 | memcpy(prc, // after "front" magic
|
---|
| 607 | p,
|
---|
| 608 | cbCopy);
|
---|
[13] | 609 |
|
---|
[123] | 610 | // store "tail" magic string to block which
|
---|
| 611 | // will be returned plus the size which was requested
|
---|
| 612 | memcpy(((PBYTE)prc) + stSize,
|
---|
| 613 | MEMBLOCKMAGIC_TAIL,
|
---|
| 614 | sizeof(MEMBLOCKMAGIC_TAIL));
|
---|
[13] | 615 |
|
---|
[123] | 616 | // free the old buffer
|
---|
| 617 | free(pBeforeMagic);
|
---|
[13] | 618 |
|
---|
[123] | 619 | // update the tree, since prc has changed
|
---|
| 620 | treeDelete(&G_pHeapItemsRoot,
|
---|
| 621 | &G_cHeapItems,
|
---|
| 622 | (TREE*)pHeapItem);
|
---|
| 623 | // append heap item to linked list
|
---|
| 624 | if (pExisting = FindHeapItem(prc))
|
---|
| 625 | {
|
---|
| 626 | // a different heap item exists for this address:
|
---|
| 627 | // delete this one and use that instead; there's
|
---|
| 628 | // no need to re-insert either
|
---|
| 629 | free(pHeapItem);
|
---|
| 630 | pHeapItem = pExisting;
|
---|
| 631 | }
|
---|
[13] | 632 |
|
---|
[123] | 633 | FillHeapItem(pHeapItem,
|
---|
| 634 | prc,
|
---|
| 635 | stSize,
|
---|
| 636 | pcszSourceFile,
|
---|
| 637 | ulLine,
|
---|
| 638 | pcszFunction);
|
---|
[13] | 639 |
|
---|
[123] | 640 | // insert only if we didn't use an existing item
|
---|
| 641 | if (!pExisting)
|
---|
| 642 | if (treeInsert(&G_pHeapItemsRoot,
|
---|
| 643 | &G_cHeapItems,
|
---|
| 644 | (TREE*)pHeapItem,
|
---|
| 645 | treeCompareKeys))
|
---|
| 646 | {
|
---|
| 647 | LogError(__FUNCTION__ ": treeInsert failed for memory block at 0x%lX.\n"
|
---|
| 648 | "The block was allocated by %s (%s, line %d).",
|
---|
| 649 | prc,
|
---|
| 650 | pcszFunction,
|
---|
| 651 | pcszSourceFile,
|
---|
| 652 | ulLine);
|
---|
| 653 | }
|
---|
[13] | 654 |
|
---|
[123] | 655 | } // if (!pHeapItem->fFreed)
|
---|
[13] | 656 | }
|
---|
[123] | 657 | else
|
---|
| 658 | LogError(__FUNCTION__ ": realloc() called with invalid object from %s (%s, line %d) for object 0x%lX.",
|
---|
| 659 | pcszFunction,
|
---|
| 660 | pcszSourceFile,
|
---|
| 661 | ulLine,
|
---|
| 662 | p);
|
---|
[13] | 663 |
|
---|
| 664 | memdUnlock();
|
---|
| 665 | }
|
---|
| 666 |
|
---|
[238] | 667 | return prc;
|
---|
[13] | 668 | }
|
---|
| 669 |
|
---|
| 670 | /*
|
---|
[8] | 671 | *@@ memdReleaseFreed:
|
---|
| 672 | * goes thru the entire global HEAPITEM's list
|
---|
| 673 | * and throws out all items which have been freed.
|
---|
| 674 | * Call this from time to time in order to keep
|
---|
| 675 | * the system usable. See memdFree() for details.
|
---|
| 676 | *
|
---|
| 677 | * Returns the no. of HEAPITEM's that have been
|
---|
| 678 | * released.
|
---|
| 679 | *
|
---|
| 680 | *@@added V0.9.3 (2000-04-11) [umoeller]
|
---|
| 681 | */
|
---|
| 682 |
|
---|
| 683 | unsigned long memdReleaseFreed(void)
|
---|
| 684 | {
|
---|
| 685 | BOOL ulItemsReleased = 0,
|
---|
| 686 | ulBytesReleased = 0;
|
---|
| 687 | if (memdLock())
|
---|
| 688 | {
|
---|
[123] | 689 | /* PHEAPITEM pHeapItem = treeFirst(G_pHeapItemsRoot);
|
---|
[8] | 690 |
|
---|
| 691 | while (pHeapItem)
|
---|
| 692 | {
|
---|
[13] | 693 | // store next first, because we can change the "next" pointer
|
---|
[123] | 694 | PHEAPITEM pNext = treeNext(pHeapItem);
|
---|
[13] | 695 |
|
---|
[8] | 696 | if (pHeapItem->fFreed)
|
---|
| 697 | {
|
---|
[13] | 698 | // item was freed:
|
---|
[8] | 699 | if (pPrevious == NULL)
|
---|
| 700 | // head of list:
|
---|
| 701 | G_pHeapItemsRoot = pNext; // can be NULL
|
---|
| 702 | else
|
---|
| 703 | // somewhere later:
|
---|
| 704 | // link next to previous to skip current
|
---|
| 705 | pPrevious->pNext = pNext; // can be NULL
|
---|
| 706 |
|
---|
| 707 | ulItemsReleased++;
|
---|
| 708 | ulBytesReleased += pHeapItem->ulSize;
|
---|
| 709 |
|
---|
[13] | 710 | if (pHeapItem == G_pHeapItemsLast)
|
---|
| 711 | // reset "last item" cache
|
---|
| 712 | G_pHeapItemsLast = NULL;
|
---|
| 713 |
|
---|
[8] | 714 | free(pHeapItem);
|
---|
| 715 | }
|
---|
| 716 | else
|
---|
| 717 | // item still valid:
|
---|
| 718 | pPrevious = pHeapItem;
|
---|
| 719 |
|
---|
| 720 | pHeapItem = pNext;
|
---|
| 721 | }
|
---|
[123] | 722 | */
|
---|
[8] | 723 | G_ulItemsReleased += ulItemsReleased;
|
---|
| 724 | G_ulBytesReleased += ulBytesReleased;
|
---|
| 725 |
|
---|
| 726 | memdUnlock();
|
---|
| 727 | }
|
---|
| 728 |
|
---|
[238] | 729 | return ulItemsReleased;
|
---|
[8] | 730 | }
|
---|
| 731 |
|
---|
| 732 | /* ******************************************************************
|
---|
[14] | 733 | *
|
---|
| 734 | * XFolder debugging helpers
|
---|
| 735 | *
|
---|
[8] | 736 | ********************************************************************/
|
---|
| 737 |
|
---|
| 738 | #ifdef _PMPRINTF_
|
---|
| 739 | /*
|
---|
| 740 | *@@ memdDumpMemoryBlock:
|
---|
| 741 | * if _PMPRINTF_ has been #define'd before including
|
---|
| 742 | * memdebug.h,
|
---|
| 743 | * this will dump a block of memory to the PMPRINTF
|
---|
| 744 | * output window. Useful for debugging internal
|
---|
| 745 | * structures.
|
---|
| 746 | * If _PMPRINTF_ has been NOT #define'd,
|
---|
| 747 | * no code will be produced at all. :-)
|
---|
| 748 | */
|
---|
| 749 |
|
---|
[164] | 750 | /*
|
---|
[8] | 751 | void memdDumpMemoryBlock(PBYTE pb, // in: start address
|
---|
| 752 | ULONG ulSize, // in: size of block
|
---|
| 753 | ULONG ulIndent) // in: how many spaces to put
|
---|
| 754 | // before each output line
|
---|
| 755 | {
|
---|
[149] | 756 | TRY_QUIET(excpt1)
|
---|
[8] | 757 | {
|
---|
[149] | 758 | PSZ psz;
|
---|
| 759 | if (psz = strhCreateDump(pb, ulSize, ulIndent))
|
---|
| 760 | {
|
---|
| 761 | _Pmpf(("\n%s", psz));
|
---|
| 762 | free(psz);
|
---|
| 763 | }
|
---|
[471] | 764 | }
|
---|
[149] | 765 | CATCH(excpt1)
|
---|
| 766 | {
|
---|
| 767 | _Pmpf(("Crash in " __FUNCTION__ ));
|
---|
| 768 | } END_CATCH();
|
---|
[8] | 769 | }
|
---|
[164] | 770 | */
|
---|
[8] | 771 | #endif
|
---|
| 772 |
|
---|
| 773 | #endif
|
---|
| 774 |
|
---|