| 1 |  | 
|---|
| 2 | /* | 
|---|
| 3 | *@@sourcefile memdebug.c: | 
|---|
| 4 | *      memory debugging helpers. | 
|---|
| 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 | *         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 | * | 
|---|
| 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 | 
|---|
| 34 | *         function, and DO NOT call malloc() or other memory | 
|---|
| 35 | *         functions in there. | 
|---|
| 36 | * | 
|---|
| 37 | *         These debug functions have been added with V0.9.3 | 
|---|
| 38 | *         and should now be compiler-independent. | 
|---|
| 39 | * | 
|---|
| 40 | *         V0.9.6 added realloc() support and fixed a few bugs. | 
|---|
| 41 | * | 
|---|
| 42 | *         With V0.9.16, most of this was rewritten to be much | 
|---|
| 43 | *         faster. This no longer slows down the system enormously. | 
|---|
| 44 | * | 
|---|
| 45 | *      -- A PM heap debugging window which shows the status | 
|---|
| 46 | *         of the heap logging list. See memdCreateMemDebugWindow | 
|---|
| 47 | *         (memdebug_win.c) for details. | 
|---|
| 48 | * | 
|---|
| 49 | *      To enable memory debugging, do the following in each (!) | 
|---|
| 50 | *      of your code modules: | 
|---|
| 51 | * | 
|---|
| 52 | *      1) Include at least <stdlib.h> and <string.h>. | 
|---|
| 53 | * | 
|---|
| 54 | *      2) Include memdebug.h AFTER those two. This will remap | 
|---|
| 55 | *         the malloc() etc. calls to the debug functions in | 
|---|
| 56 | *         this file by defining macros for them. | 
|---|
| 57 | * | 
|---|
| 58 | *         If you don't want those replaced, add | 
|---|
| 59 | +              #define DONT_REPLACE_MALLOC | 
|---|
| 60 | *         before including memdebug.h. | 
|---|
| 61 | * | 
|---|
| 62 | *         To avoid calling a debug function for a single call, | 
|---|
| 63 | *         place the malloc call (or whatever) in brackets. | 
|---|
| 64 | * | 
|---|
| 65 | *      That's all. XWorkplace's setup.h does this automatically | 
|---|
| 66 | *      if XWorkplace is compiled with debug code. | 
|---|
| 67 | * | 
|---|
| 68 | *      A couple of WARNINGS: | 
|---|
| 69 | * | 
|---|
| 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. | 
|---|
| 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. | 
|---|
| 83 | *          The pointer returned is _not_ identical to the one | 
|---|
| 84 | *          that was internally allocated. | 
|---|
| 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 | * | 
|---|
| 96 | *@@added V0.9.1 (2000-02-12) [umoeller] | 
|---|
| 97 | */ | 
|---|
| 98 |  | 
|---|
| 99 | /* | 
|---|
| 100 | *      Copyright (C) 2000-2001 Ulrich Mller. | 
|---|
| 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 |  | 
|---|
| 128 | #include "helpers\tree.h" | 
|---|
| 129 |  | 
|---|
| 130 | #define DONT_REPLACE_MALLOC             // never do debug memory for this | 
|---|
| 131 | #define MEMDEBUG_PRIVATE | 
|---|
| 132 | #include "setup.h" | 
|---|
| 133 |  | 
|---|
| 134 | #ifdef __XWPMEMDEBUG__ | 
|---|
| 135 |  | 
|---|
| 136 | #include "helpers\dosh.h" | 
|---|
| 137 | #include "helpers\except.h" | 
|---|
| 138 |  | 
|---|
| 139 | #include "helpers\memdebug.h"        // included by setup.h already | 
|---|
| 140 | #include "helpers\stringh.h" | 
|---|
| 141 |  | 
|---|
| 142 | /* | 
|---|
| 143 | *@@category: Helpers\C helpers\Heap debugging | 
|---|
| 144 | *      See memdebug.c. | 
|---|
| 145 | */ | 
|---|
| 146 |  | 
|---|
| 147 | /* ****************************************************************** | 
|---|
| 148 | * | 
|---|
| 149 | *   Global variables | 
|---|
| 150 | * | 
|---|
| 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 |  | 
|---|
| 159 | HMTX                G_hmtxMallocList = NULLHANDLE; | 
|---|
| 160 |  | 
|---|
| 161 | extern TREE         *G_pHeapItemsRoot = NULL; | 
|---|
| 162 | extern LONG         G_cHeapItems = 0; | 
|---|
| 163 |  | 
|---|
| 164 | PFNCBMEMDLOG        G_pMemdLogFunc = NULL; | 
|---|
| 165 |  | 
|---|
| 166 | extern ULONG        G_ulItemsReleased = 0; | 
|---|
| 167 | extern ULONG        G_ulBytesReleased = 0; | 
|---|
| 168 |  | 
|---|
| 169 | /* ****************************************************************** | 
|---|
| 170 | * | 
|---|
| 171 | *   Debug heap management | 
|---|
| 172 | * | 
|---|
| 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 | { | 
|---|
| 188 | if (!G_hmtxMallocList) | 
|---|
| 189 | { | 
|---|
| 190 | // first call: | 
|---|
| 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 | } | 
|---|
| 200 | else | 
|---|
| 201 | return !DosRequestMutexSem(G_hmtxMallocList, | 
|---|
| 202 | SEM_INDEFINITE_WAIT); | 
|---|
| 203 |  | 
|---|
| 204 | return FALSE; | 
|---|
| 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 | /* | 
|---|
| 220 | *@@ LogError: | 
|---|
| 221 | * | 
|---|
| 222 | *@@added V0.9.16 (2001-12-08) [umoeller] | 
|---|
| 223 | */ | 
|---|
| 224 |  | 
|---|
| 225 | STATIC VOID LogError(const char *pcszFormat,     // in: format string (like with printf) | 
|---|
| 226 | ...)                        // in: additional stuff (like with printf) | 
|---|
| 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 |  | 
|---|
| 246 | STATIC PHEAPITEM FindHeapItem(void *p) | 
|---|
| 247 | { | 
|---|
| 248 | return (PHEAPITEM)treeFind(G_pHeapItemsRoot, | 
|---|
| 249 | (ULONG)p, | 
|---|
| 250 | treeCompareKeys); | 
|---|
| 251 | } | 
|---|
| 252 |  | 
|---|
| 253 | /* | 
|---|
| 254 | *@@ FillHeapItem: | 
|---|
| 255 | * | 
|---|
| 256 | *@@added V0.9.16 (2001-12-08) [umoeller] | 
|---|
| 257 | */ | 
|---|
| 258 |  | 
|---|
| 259 | STATIC VOID FillHeapItem(PHEAPITEM pHeapItem, | 
|---|
| 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 | 
|---|
| 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 |  | 
|---|
| 289 | STATIC VOID CheckMagics(const char *pcszParentFunc, | 
|---|
| 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 | 
|---|
| 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 | /* | 
|---|
| 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] | 
|---|
| 342 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster | 
|---|
| 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 | 
|---|
| 354 | LogError(__FUNCTION__ ": Function %s (%s, line %d) called malloc(0).", | 
|---|
| 355 | pcszFunction, | 
|---|
| 356 | pcszSourceFile, | 
|---|
| 357 | ulLine); | 
|---|
| 358 | else | 
|---|
| 359 | if (memdLock()) | 
|---|
| 360 | { | 
|---|
| 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; | 
|---|
| 366 |  | 
|---|
| 367 | if (pObj = malloc(   sizeof(MEMBLOCKMAGIC_HEAD) | 
|---|
| 368 | + stSize | 
|---|
| 369 | + sizeof(MEMBLOCKMAGIC_TAIL))) | 
|---|
| 370 | { | 
|---|
| 371 | PHEAPITEM pHeapItem; | 
|---|
| 372 | BOOL fInsert = TRUE; | 
|---|
| 373 |  | 
|---|
| 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)); | 
|---|
| 385 |  | 
|---|
| 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; | 
|---|
| 392 |  | 
|---|
| 393 | FillHeapItem(pHeapItem, | 
|---|
| 394 | prc, | 
|---|
| 395 | stSize, | 
|---|
| 396 | pcszSourceFile, | 
|---|
| 397 | ulLine, | 
|---|
| 398 | pcszFunction); | 
|---|
| 399 |  | 
|---|
| 400 | if (fInsert) | 
|---|
| 401 | // append heap item to linked list | 
|---|
| 402 | if (treeInsert(&G_pHeapItemsRoot, | 
|---|
| 403 | &G_cHeapItems, | 
|---|
| 404 | (TREE*)pHeapItem, | 
|---|
| 405 | treeCompareKeys)) | 
|---|
| 406 | { | 
|---|
| 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); | 
|---|
| 413 | } | 
|---|
| 414 | } | 
|---|
| 415 |  | 
|---|
| 416 | memdUnlock(); | 
|---|
| 417 | } | 
|---|
| 418 |  | 
|---|
| 419 | return prc; | 
|---|
| 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); | 
|---|
| 442 | return p; | 
|---|
| 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] | 
|---|
| 464 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster | 
|---|
| 465 | */ | 
|---|
| 466 |  | 
|---|
| 467 | void memdFree(void *p, | 
|---|
| 468 | const char *pcszSourceFile, | 
|---|
| 469 | unsigned long ulLine, | 
|---|
| 470 | const char *pcszFunction) | 
|---|
| 471 | { | 
|---|
| 472 | if (memdLock()) | 
|---|
| 473 | { | 
|---|
| 474 | PHEAPITEM pHeapItem; | 
|---|
| 475 |  | 
|---|
| 476 | // search the list with the pointer which was | 
|---|
| 477 | // really returned by the original malloc(), | 
|---|
| 478 | // that is, the byte after the magic string | 
|---|
| 479 | if (pHeapItem = FindHeapItem(p)) | 
|---|
| 480 | { | 
|---|
| 481 | // the same address may be allocated and freed | 
|---|
| 482 | // several times, so check | 
|---|
| 483 | if (!pHeapItem->fFreed) | 
|---|
| 484 | { | 
|---|
| 485 | // found: | 
|---|
| 486 | void    *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD); | 
|---|
| 487 |  | 
|---|
| 488 | CheckMagics(__FUNCTION__, | 
|---|
| 489 | pHeapItem, | 
|---|
| 490 | (PBYTE)p, | 
|---|
| 491 | pcszSourceFile, | 
|---|
| 492 | ulLine, | 
|---|
| 493 | pcszFunction); | 
|---|
| 494 |  | 
|---|
| 495 | // free the real memory item | 
|---|
| 496 | free(pBeforeMagic); | 
|---|
| 497 |  | 
|---|
| 498 | // mark the heap item as freed, but | 
|---|
| 499 | // keep it in the list | 
|---|
| 500 | pHeapItem->fFreed = TRUE; | 
|---|
| 501 |  | 
|---|
| 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); | 
|---|
| 515 | } | 
|---|
| 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); | 
|---|
| 523 |  | 
|---|
| 524 | memdUnlock(); | 
|---|
| 525 | } | 
|---|
| 526 | } | 
|---|
| 527 |  | 
|---|
| 528 | /* | 
|---|
| 529 | *@@ memdRealloc: | 
|---|
| 530 | *      wrapper function for realloc(). See memdMalloc for | 
|---|
| 531 | *      details. | 
|---|
| 532 | * | 
|---|
| 533 | *@@added V0.9.6 (2000-11-12) [umoeller] | 
|---|
| 534 | *@@changed V0.9.12 (2001-05-21) [umoeller]: this reported errors on realloc(0), which is a valid call, fixed | 
|---|
| 535 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster | 
|---|
| 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 |  | 
|---|
| 546 | if (!p) | 
|---|
| 547 | // p == NULL: this is valid, use malloc() instead | 
|---|
| 548 | // V0.9.12 (2001-05-21) [umoeller] | 
|---|
| 549 | return memdMalloc(stSize, pcszSourceFile, ulLine, pcszFunction); | 
|---|
| 550 |  | 
|---|
| 551 | if (memdLock()) | 
|---|
| 552 | { | 
|---|
| 553 | // search the list with the pointer which was | 
|---|
| 554 | // really returned by the original malloc(), | 
|---|
| 555 | // that is, the byte after the magic string | 
|---|
| 556 | PHEAPITEM pHeapItem, pExisting; | 
|---|
| 557 | if (pHeapItem = FindHeapItem(p)) | 
|---|
| 558 | { | 
|---|
| 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; | 
|---|
| 580 |  | 
|---|
| 581 | CheckMagics(__FUNCTION__, | 
|---|
| 582 | pHeapItem, | 
|---|
| 583 | (PBYTE)p, | 
|---|
| 584 | pcszSourceFile, | 
|---|
| 585 | ulLine, | 
|---|
| 586 | pcszFunction); | 
|---|
| 587 |  | 
|---|
| 588 | // now reallocate! | 
|---|
| 589 | pObjNew = malloc(   sizeof(MEMBLOCKMAGIC_HEAD) | 
|---|
| 590 | + stSize   // new size | 
|---|
| 591 | + sizeof(MEMBLOCKMAGIC_TAIL)); | 
|---|
| 592 |  | 
|---|
| 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); | 
|---|
| 599 |  | 
|---|
| 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; | 
|---|
| 604 |  | 
|---|
| 605 | // copy buffer from old memory object | 
|---|
| 606 | memcpy(prc,         // after "front" magic | 
|---|
| 607 | p, | 
|---|
| 608 | cbCopy); | 
|---|
| 609 |  | 
|---|
| 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)); | 
|---|
| 615 |  | 
|---|
| 616 | // free the old buffer | 
|---|
| 617 | free(pBeforeMagic); | 
|---|
| 618 |  | 
|---|
| 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 | } | 
|---|
| 632 |  | 
|---|
| 633 | FillHeapItem(pHeapItem, | 
|---|
| 634 | prc, | 
|---|
| 635 | stSize, | 
|---|
| 636 | pcszSourceFile, | 
|---|
| 637 | ulLine, | 
|---|
| 638 | pcszFunction); | 
|---|
| 639 |  | 
|---|
| 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 | } | 
|---|
| 654 |  | 
|---|
| 655 | } // if (!pHeapItem->fFreed) | 
|---|
| 656 | } | 
|---|
| 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); | 
|---|
| 663 |  | 
|---|
| 664 | memdUnlock(); | 
|---|
| 665 | } | 
|---|
| 666 |  | 
|---|
| 667 | return prc; | 
|---|
| 668 | } | 
|---|
| 669 |  | 
|---|
| 670 | /* | 
|---|
| 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 | { | 
|---|
| 689 | /* PHEAPITEM pHeapItem = treeFirst(G_pHeapItemsRoot); | 
|---|
| 690 |  | 
|---|
| 691 | while (pHeapItem) | 
|---|
| 692 | { | 
|---|
| 693 | // store next first, because we can change the "next" pointer | 
|---|
| 694 | PHEAPITEM   pNext = treeNext(pHeapItem); | 
|---|
| 695 |  | 
|---|
| 696 | if (pHeapItem->fFreed) | 
|---|
| 697 | { | 
|---|
| 698 | // item was freed: | 
|---|
| 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 |  | 
|---|
| 710 | if (pHeapItem == G_pHeapItemsLast) | 
|---|
| 711 | // reset "last item" cache | 
|---|
| 712 | G_pHeapItemsLast = NULL; | 
|---|
| 713 |  | 
|---|
| 714 | free(pHeapItem); | 
|---|
| 715 | } | 
|---|
| 716 | else | 
|---|
| 717 | // item still valid: | 
|---|
| 718 | pPrevious = pHeapItem; | 
|---|
| 719 |  | 
|---|
| 720 | pHeapItem = pNext; | 
|---|
| 721 | } | 
|---|
| 722 | */ | 
|---|
| 723 | G_ulItemsReleased += ulItemsReleased; | 
|---|
| 724 | G_ulBytesReleased += ulBytesReleased; | 
|---|
| 725 |  | 
|---|
| 726 | memdUnlock(); | 
|---|
| 727 | } | 
|---|
| 728 |  | 
|---|
| 729 | return ulItemsReleased; | 
|---|
| 730 | } | 
|---|
| 731 |  | 
|---|
| 732 | /* ****************************************************************** | 
|---|
| 733 | * | 
|---|
| 734 | *   XFolder debugging helpers | 
|---|
| 735 | * | 
|---|
| 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 |  | 
|---|
| 750 | /* | 
|---|
| 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 | { | 
|---|
| 756 | TRY_QUIET(excpt1) | 
|---|
| 757 | { | 
|---|
| 758 | PSZ psz; | 
|---|
| 759 | if (psz = strhCreateDump(pb, ulSize, ulIndent)) | 
|---|
| 760 | { | 
|---|
| 761 | _Pmpf(("\n%s", psz)); | 
|---|
| 762 | free(psz); | 
|---|
| 763 | } | 
|---|
| 764 | CATCH(excpt1) | 
|---|
| 765 | { | 
|---|
| 766 | _Pmpf(("Crash in " __FUNCTION__ )); | 
|---|
| 767 | } END_CATCH(); | 
|---|
| 768 | } | 
|---|
| 769 | */ | 
|---|
| 770 | #endif | 
|---|
| 771 |  | 
|---|
| 772 | #endif | 
|---|
| 773 |  | 
|---|