BOSS 7.0.7
BESIII Offline Software System
Loading...
Searching...
No Matches
KalFitMemLeak.c
Go to the documentation of this file.
1/*
2** MEMWATCH.C
3** Nonintrusive ANSI C memory leak / overwrite detection
4** Copyright (C) 1992-2003 Johan Lindh
5** All rights reserved.
6** Version 2.71
7
8 This file is part of MEMWATCH.
9
10 MEMWATCH is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 MEMWATCH is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with MEMWATCH; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
24**
25** 920810 JLI [1.00]
26** 920830 JLI [1.10 double-free detection]
27** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit]
28** 921022 JLI [1.20 ASSERT and VERIFY]
29** 921105 JLI [1.30 C++ support and TRACE]
30** 921116 JLI [1.40 mwSetOutFunc]
31** 930215 JLI [1.50 modified ASSERT/VERIFY]
32** 930327 JLI [1.51 better auto-init & PC-lint support]
33** 930506 JLI [1.55 MemWatch class, improved C++ support]
34** 930507 JLI [1.60 mwTest & CHECK()]
35** 930809 JLI [1.65 Abort/Retry/Ignore]
36** 930820 JLI [1.70 data dump when unfreed]
37** 931016 JLI [1.72 modified C++ new/delete handling]
38** 931108 JLI [1.77 mwSetAssertAction() & some small changes]
39** 940110 JLI [1.80 no-mans-land alloc/checking]
40** 940328 JLI [2.00 version 2.0 rewrite]
41** Improved NML (no-mans-land) support.
42** Improved performance (especially for free()ing!).
43** Support for 'read-only' buffers (checksums)
44** ^^ NOTE: I never did this... maybe I should?
45** FBI (free'd block info) tagged before freed blocks
46** Exporting of the mwCounter variable
47** mwBreakOut() localizes debugger support
48** Allocation statistics (global, per-module, per-line)
49** Self-repair ability with relinking
50** 950913 JLI [2.10 improved garbage handling]
51** 951201 JLI [2.11 improved auto-free in emergencies]
52** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()]
53** 960514 JLI [2.12 undefining of existing macros]
54** 960515 JLI [2.13 possibility to use default new() & delete()]
55** 960516 JLI [2.20 suppression of file flushing on unfreed msgs]
56** 960516 JLI [2.21 better support for using MEMWATCH with DLL's]
57** 960710 JLI [X.02 multiple logs and mwFlushNow()]
58** 960801 JLI [2.22 merged X.01 version with current]
59** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's]
60** 960805 JLI [2.31 merged X.02 version with current]
61** 961002 JLI [2.32 support for realloc() + fixed STDERR bug]
62** 961222 JLI [2.40 added mwMark() & mwUnmark()]
63** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY]
64** 970113 JLI [2.42 added support for PC-Lint 7.00g]
65** 970207 JLI [2.43 added support for strdup()]
66** 970209 JLI [2.44 changed default filename to lowercase]
67** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers]
68** 970723 JLI [2.46 added MW_ARI_NULLREAD flag]
69** 970813 JLI [2.47 stabilized marker handling]
70** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway]
71** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support]
72** 980417 JLI [2.51 more checks for invalid addresses]
73** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting]
74** 990112 JLI [2.53 added check for empty heap to mwIsOwned]
75** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML]
76** 990224 JLI [2.56 changed ordering of members in structures]
77** 990303 JLI [2.57 first maybe-fixit-for-hpux test]
78** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit]
79** 990517 JLI [2.59 fixed some high-sensitivity warnings]
80** 990610 JLI [2.60 fixed some more high-sensitivity warnings]
81** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names]
82** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()]
83** 991007 JLI [2.63 first shot at a 64-bit compatible version]
84** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const]
85** 000704 JLI [2.65 added some more detection for 64-bits]
86** 010502 JLI [2.66 incorporated some user fixes]
87** [mwRelink() could print out garbage pointer (thanks [email protected])]
88** [added array destructor for C++ (thanks [email protected])]
89** [added mutex support (thanks [email protected])]
90** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined]
91** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked]
92** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen]
93** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)]
94** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)]
95*/
96
97#define __MEMWATCH_C 1
98
99#ifdef MW_NOCPP
100#define MEMWATCH_NOCPP
101#endif
102#ifdef MW_STDIO
103#define MEMWATCH_STDIO
104#endif
105
106/***********************************************************************
107** Include files
108***********************************************************************/
109
110#include <stdio.h>
111#include <stdlib.h>
112#include <stdarg.h>
113#include <string.h>
114#include <signal.h>
115#include <setjmp.h>
116#include <time.h>
117#include <limits.h>
118#include "memwatch.h"
119
120#ifndef toupper
121#include <ctype.h>
122#endif
123
124#if defined(WIN32) || defined(__WIN32__)
125#define MW_HAVE_MUTEX 1
126#include <windows.h>
127#endif
128
129#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
130#define MW_HAVE_MUTEX 1
131#include <pthread.h>
132#endif
133
134/***********************************************************************
135** Defines & other weird stuff
136***********************************************************************/
137
138/*lint -save -e767 */
139#define VERSION "2.71" /* the current version number */
140#define CHKVAL(mw) (0xFE0180L^(long)mw->count^(long)mw->size^(long)mw->line)
141#define FLUSH() mwFlush()
142#define TESTS(f,l) if(mwTestAlways) (void)mwTestNow(f,l,1)
143#define PRECHK 0x01234567L
144#define POSTCHK 0x76543210L
145#define mwBUFFER_TO_MW(p) ( (mwData*) (void*) ( ((char*)p)-mwDataSize-mwOverflowZoneSize ) )
146/*lint -restore */
147
148#define MW_NML 0x0001
149
150#ifdef _MSC_VER
151#define COMMIT "c" /* Microsoft C requires the 'c' to perform as desired */
152#else
153#define COMMIT "" /* Normal ANSI */
154#endif /* _MSC_VER */
155
156#ifdef __cplusplus
157#define CPPTEXT "++"
158#else
159#define CPPTEXT ""
160#endif /* __cplusplus */
161
162#ifdef MEMWATCH_STDIO
163#define mwSTDERR stderr
164#else
165#define mwSTDERR mwLog
166#endif
167
168#ifdef MW_HAVE_MUTEX
169#define MW_MUTEX_INIT() mwMutexInit()
170#define MW_MUTEX_TERM() mwMutexTerm()
171#define MW_MUTEX_LOCK() mwMutexLock()
172#define MW_MUTEX_UNLOCK() mwMutexUnlock()
173#else
174#define MW_MUTEX_INIT()
175#define MW_MUTEX_TERM()
176#define MW_MUTEX_LOCK()
177#define MW_MUTEX_UNLOCK()
178#endif
179
180/***********************************************************************
181** If you really, really know what you're doing,
182** you can predefine these things yourself.
183***********************************************************************/
184
185#ifndef mwBYTE_DEFINED
186# if CHAR_BIT != 8
187# error need CHAR_BIT to be 8!
188# else
189typedef unsigned char mwBYTE;
190# define mwBYTE_DEFINED 1
191# endif
192#endif
193
194#if defined(ULONGLONG_MAX) || defined(ULLONG_MAX) || defined(_UI64_MAX) || defined(ULONG_LONG_MAX)
195# define mw64BIT 1
196# define mwROUNDALLOC_DEFAULT 8
197#else
198# if UINT_MAX <= 0xFFFFUL
199# define mw16BIT 1
200# define mwROUNDALLOC_DEFAULT 2
201# else
202# if ULONG_MAX > 0xFFFFFFFFUL
203# define mw64BIT 1
204# define mwROUNDALLOC_DEFAULT 8
205# else
206# define mw32BIT 1
207# define mwROUNDALLOC_DEFAULT 4
208# endif
209# endif
210#endif
211
212/* mwROUNDALLOC is the number of bytes to */
213/* round up to, to ensure that the end of */
214/* the buffer is suitable for storage of */
215/* any kind of object */
216#ifndef mwROUNDALLOC
217# define mwROUNDALLOC mwROUNDALLOC_DEFAULT
218#endif
219
220#ifndef mwDWORD_DEFINED
221#if ULONG_MAX == 0xFFFFFFFFUL
222typedef unsigned long mwDWORD;
223#define mwDWORD_DEFINED "unsigned long"
224#endif
225#endif
226
227#ifndef mwDWORD_DEFINED
228#if UINT_MAX == 0xFFFFFFFFUL
229typedef unsigned int mwDWORD;
230#define mwDWORD_DEFINED "unsigned int"
231#endif
232#endif
233
234#ifndef mwDWORD_DEFINED
235#if USHRT_MAX == 0xFFFFFFFFUL
236typedef unsigned short mwDWORD;
237#define mwDWORD_DEFINED "unsigned short"
238#endif
239#endif
240
241#ifndef mwBYTE_DEFINED
242#error "can't find out the correct type for a 8 bit scalar"
243#endif
244
245#ifndef mwDWORD_DEFINED
246#error "can't find out the correct type for a 32 bit scalar"
247#endif
248
249/***********************************************************************
250** Typedefs & structures
251***********************************************************************/
252
253/* main data holding area, precedes actual allocation */
254typedef struct mwData_ mwData;
255struct mwData_ {
256 mwData* prev; /* previous allocation in chain */
257 mwData* next; /* next allocation in chain */
258 const char* file; /* file name where allocated */
259 long count; /* action count */
260 long check; /* integrity check value */
261#if 0
262 long crc; /* data crc value */
263#endif
264 size_t size; /* size of allocation */
265 int line; /* line number where allocated */
266 unsigned flag; /* flag word */
267 };
268
269/* statistics structure */
270typedef struct mwStat_ mwStat;
271struct mwStat_ {
272 mwStat* next; /* next statistic buffer */
273 const char* file;
274 long total; /* total bytes allocated */
275 long num; /* total number of allocations */
276 long max; /* max allocated at one time */
277 long curr; /* current allocations */
278 int line;
279 };
280
281/* grabbing structure, 1K in size */
282typedef struct mwGrabData_ mwGrabData;
285 int type;
286 char blob[ 1024 - sizeof(mwGrabData*) - sizeof(int) ];
287 };
288
289typedef struct mwMarker_ mwMarker;
290struct mwMarker_ {
291 void *host;
292 char *text;
294 int level;
295 };
296
297#if defined(WIN32) || defined(__WIN32__)
298typedef HANDLE mwMutex;
299#endif
300
301#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
302typedef pthread_mutex_t mwMutex;
303#endif
304
305/***********************************************************************
306** Static variables
307***********************************************************************/
308
309static int mwInited = 0;
310static int mwInfoWritten = 0;
311static int mwUseAtexit = 0;
312static FILE* mwLog = NULL;
313static int mwFlushing = 0;
314static int mwStatLevel = MW_STAT_DEFAULT;
315static int mwNML = MW_NML_DEFAULT;
316static int mwFBI = 0;
317static long mwAllocLimit = 0L;
318static int mwUseLimit = 0;
319
320static long mwNumCurAlloc = 0L;
321static mwData* mwHead = NULL;
322static mwData* mwTail = NULL;
323static int mwDataSize = 0;
324static unsigned char mwOverflowZoneTemplate[] = "mEmwAtch";
325static int mwOverflowZoneSize = mwROUNDALLOC;
326
327static void (*mwOutFunction)(int) = NULL;
328static int (*mwAriFunction)(const char*) = NULL;
329static int mwAriAction = MW_ARI_ABORT;
330
331static char mwPrintBuf[MW_TRACE_BUFFER+8];
332
333static unsigned long mwCounter = 0L;
334static long mwErrors = 0L;
335
336static int mwTestFlags = 0;
337static int mwTestAlways = 0;
338
339static FILE* mwLogB1 = NULL;
340static int mwFlushingB1 = 0;
341
342static mwStat* mwStatList = NULL;
343static long mwStatTotAlloc = 0L;
344static long mwStatMaxAlloc = 0L;
345static long mwStatNumAlloc = 0L;
346static long mwStatCurAlloc = 0L;
347static long mwNmlNumAlloc = 0L;
348static long mwNmlCurAlloc = 0L;
349
350static mwGrabData* mwGrabList = NULL;
351static long mwGrabSize = 0L;
352
353static void * mwLastFree[MW_FREE_LIST];
354static const char *mwLFfile[MW_FREE_LIST];
355static int mwLFline[MW_FREE_LIST];
356static int mwLFcur = 0;
357
358static mwMarker* mwFirstMark = NULL;
359
360static FILE* mwLogB2 = NULL;
361static int mwFlushingB2 = 0;
362
363#ifdef MW_HAVE_MUTEX
364static mwMutex mwGlobalMutex;
365#endif
366
367/***********************************************************************
368** Static function declarations
369***********************************************************************/
370
371static void mwAutoInit( void );
372static FILE* mwLogR( void );
373static void mwLogW( FILE* );
374static int mwFlushR( void );
375static void mwFlushW( int );
376static void mwFlush( void );
377static void mwIncErr( void );
378static void mwUnlink( mwData*, const char* file, int line );
379static int mwRelink( mwData*, const char* file, int line );
380static int mwIsHeapOK( mwData *mw );
381static int mwIsOwned( mwData* mw, const char* file, int line );
382static int mwTestBuf( mwData* mw, const char* file, int line );
383static void mwDefaultOutFunc( int );
384static void mwWrite( const char* format, ... );
385static void mwLogFile( const char* name );
386static size_t mwFreeUp( size_t, int );
387static const void *mwTestMem( const void *, unsigned, int );
388static int mwStrCmpI( const char *s1, const char *s2 );
389static int mwTestNow( const char *file, int line, int always_invoked );
390static void mwDropAll( void );
391static const char *mwGrabType( int type );
392static unsigned mwGrab_( unsigned kb, int type, int silent );
393static unsigned mwDrop_( unsigned kb, int type, int silent );
394static int mwARI( const char* text );
395static void mwStatReport( void );
396static mwStat* mwStatGet( const char*, int, int );
397static void mwStatAlloc( size_t, const char*, int );
398static void mwStatFree( size_t, const char*, int );
399static int mwCheckOF( const void * p );
400static void mwWriteOF( void * p );
401static char mwDummy( char c );
402#ifdef MW_HAVE_MUTEX
403static void mwMutexInit( void );
404static void mwMutexTerm( void );
405static void mwMutexLock( void );
406static void mwMutexUnlock( void );
407#endif
408
409/***********************************************************************
410** System functions
411***********************************************************************/
412
413void mwInit( void ) {
414 time_t tid;
415
416 if( mwInited++ > 0 ) return;
417
419
420 /* start a log if none is running */
421 if( mwLogR() == NULL ) mwLogFile( "memwatch.log" );
422 if( mwLogR() == NULL ) {
423 int i;
424 char buf[32];
425 /* oops, could not open it! */
426 /* probably because it's already open */
427 /* so we try some other names */
428 for( i=1; i<100; i++ ) {
429 sprintf( buf, "memwat%02d.log", i );
430 mwLogFile( buf );
431 if( mwLogR() != NULL ) break;
432 }
433 }
434
435 /* initialize the statistics */
436 mwStatList = NULL;
437 mwStatTotAlloc = 0L;
438 mwStatCurAlloc = 0L;
439 mwStatMaxAlloc = 0L;
440 mwStatNumAlloc = 0L;
441 mwNmlCurAlloc = 0L;
442 mwNmlNumAlloc = 0L;
443
444 /* calculate the buffer size to use for a mwData */
445 mwDataSize = sizeof(mwData);
446 while( mwDataSize % mwROUNDALLOC ) mwDataSize ++;
447
448 /* write informational header if needed */
449 if( !mwInfoWritten ) {
450 mwInfoWritten = 1;
451 (void) time( &tid );
452 mwWrite(
453 "\n============="
454 " MEMWATCH " VERSION " Copyright (C) 1992-1999 Johan Lindh "
455 "=============\n");
456 mwWrite( "\nStarted at %s\n", ctime( &tid ) );
457
458/**************************************************************** Generic */
459 mwWrite( "Modes: " );
460#ifdef mwNew
461 mwWrite( "C++ " );
462#endif /* mwNew */
463#ifdef __STDC__
464 mwWrite( "__STDC__ " );
465#endif /* __STDC__ */
466#ifdef mw16BIT
467 mwWrite( "16-bit " );
468#endif
469#ifdef mw32BIT
470 mwWrite( "32-bit " );
471#endif
472#ifdef mw64BIT
473 mwWrite( "64-bit " );
474#endif
475 mwWrite( "mwDWORD==(" mwDWORD_DEFINED ")\n" );
476 mwWrite( "mwROUNDALLOC==%d sizeof(mwData)==%d mwDataSize==%d\n",
477 mwROUNDALLOC, sizeof(mwData), mwDataSize );
478/**************************************************************** Generic */
479
480/************************************************************ Microsoft C */
481#ifdef _MSC_VER
482 mwWrite( "Compiled using Microsoft C" CPPTEXT
483 " %d.%02d\n", _MSC_VER / 100, _MSC_VER % 100 );
484#endif /* _MSC_VER */
485/************************************************************ Microsoft C */
486
487/************************************************************** Borland C */
488#ifdef __BORLANDC__
489 mwWrite( "Compiled using Borland C"
490#ifdef __cplusplus
491 "++ %d.%01d\n", __BCPLUSPLUS__/0x100, (__BCPLUSPLUS__%0x100)/0x10 );
492#else
493 " %d.%01d\n", __BORLANDC__/0x100, (__BORLANDC__%0x100)/0x10 );
494#endif /* __cplusplus */
495#endif /* __BORLANDC__ */
496/************************************************************** Borland C */
497
498/************************************************************** Watcom C */
499#ifdef __WATCOMC__
500 mwWrite( "Compiled using Watcom C %d.%02d ",
501 __WATCOMC__/100, __WATCOMC__%100 );
502#ifdef __FLAT__
503 mwWrite( "(32-bit flat model)" );
504#endif /* __FLAT__ */
505 mwWrite( "\n" );
506#endif /* __WATCOMC__ */
507/************************************************************** Watcom C */
508
509 mwWrite( "\n" );
510 FLUSH();
511 }
512
513 if( mwUseAtexit ) (void) atexit( mwAbort );
514 return;
515 }
516
517void mwAbort( void ) {
518 mwData *mw;
519 mwMarker *mrk;
520 char *data;
521 time_t tid;
522 int c, i, j;
523 int errors;
524
525 tid = time( NULL );
526 mwWrite( "\nStopped at %s\n", ctime( &tid) );
527
528 if( !mwInited )
529 mwWrite( "internal: mwAbort(): MEMWATCH not initialized!\n" );
530
531 /* release the grab list */
532 mwDropAll();
533
534 /* report mwMarked items */
535 while( mwFirstMark ) {
536 mrk = mwFirstMark->next;
537 mwWrite( "mark: %p: %s\n", mwFirstMark->host, mwFirstMark->text );
538 free( mwFirstMark->text );
539 free( mwFirstMark );
540 mwFirstMark = mrk;
541 mwErrors ++;
542 }
543
544 /* release all still allocated memory */
545 errors = 0;
546 while( mwHead != NULL && errors < 3 ) {
547 if( !mwIsOwned(mwHead, __FILE__, __LINE__ ) ) {
548 if( errors < 3 )
549 {
550 errors ++;
551 mwWrite( "internal: NML/unfreed scan restarting\n" );
552 FLUSH();
553 mwHead = mwHead;
554 continue;
555 }
556 mwWrite( "internal: NML/unfreed scan aborted, heap too damaged\n" );
557 FLUSH();
558 break;
559 }
560 mwFlushW(0);
561 if( !(mwHead->flag & MW_NML) ) {
562 mwErrors++;
563 data = ((char*)mwHead)+mwDataSize;
564 mwWrite( "unfreed: <%ld> %s(%d), %ld bytes at %p ",
565 mwHead->count, mwHead->file, mwHead->line, (long)mwHead->size, data+mwOverflowZoneSize );
566 if( mwCheckOF( data ) ) {
567 mwWrite( "[underflowed] ");
568 FLUSH();
569 }
570 if( mwCheckOF( (data+mwOverflowZoneSize+mwHead->size) ) ) {
571 mwWrite( "[overflowed] ");
572 FLUSH();
573 }
574 mwWrite( " \t{" );
575 j = 16; if( mwHead->size < 16 ) j = (int) mwHead->size;
576 for( i=0;i<16;i++ ) {
577 if( i<j ) mwWrite( "%02X ",
578 (unsigned char) *(data+mwOverflowZoneSize+i) );
579 else mwWrite( ".. " );
580 }
581 for( i=0;i<j;i++ ) {
582 c = *(data+mwOverflowZoneSize+i);
583 if( c < 32 || c > 126 ) c = '.';
584 mwWrite( "%c", c );
585 }
586 mwWrite( "}\n" );
587 mw = mwHead;
588 mwUnlink( mw, __FILE__, __LINE__ );
589 free( mw );
590 }
591 else {
592 data = ((char*)mwHead) + mwDataSize + mwOverflowZoneSize;
593 if( mwTestMem( data, mwHead->size, MW_VAL_NML ) ) {
594 mwErrors++;
595 mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
596 mwHead->count, data + mwOverflowZoneSize, mwHead->file, mwHead->line );
597 FLUSH();
598 }
599 mwNmlNumAlloc --;
600 mwNmlCurAlloc -= mwHead->size;
601 mw = mwHead;
602 mwUnlink( mw, __FILE__, __LINE__ );
603 free( mw );
604 }
605 }
606
607 if( mwNmlNumAlloc ) mwWrite("internal: NoMansLand block counter %ld, not zero\n", mwNmlNumAlloc );
608 if( mwNmlCurAlloc ) mwWrite("internal: NoMansLand byte counter %ld, not zero\n", mwNmlCurAlloc );
609
610 /* report statistics */
611 mwStatReport();
612 FLUSH();
613
614 mwInited = 0;
615 mwHead = mwTail = NULL;
616 if( mwErrors )
617 fprintf(mwSTDERR,"MEMWATCH detected %ld anomalies\n",mwErrors);
618 mwLogFile( NULL );
619 mwErrors = 0;
620
622
623 }
624
625void mwTerm( void ) {
626 if( mwInited == 1 )
627 {
628 mwAbort();
629 return;
630 }
631 if( !mwInited )
632 mwWrite("internal: mwTerm(): MEMWATCH has not been started!\n");
633 else
634 mwInited --;
635 }
636
637void mwStatistics( int level )
638{
639 mwAutoInit();
640 if( level<0 ) level=0;
641 if( mwStatLevel != level )
642 {
643 mwWrite( "statistics: now collecting on a %s basis\n",
644 level<1?"global":(level<2?"module":"line") );
645 mwStatLevel = level;
646 }
647}
648
649void mwAutoCheck( int onoff ) {
650 mwAutoInit();
651 mwTestAlways = onoff;
652 if( onoff ) mwTestFlags = MW_TEST_ALL;
653 }
654
655void mwSetOutFunc( void (*func)(int) ) {
656 mwAutoInit();
657 mwOutFunction = func;
658 }
659
660static void mwWriteOF( void *p )
661{
662 int i;
663 unsigned char *ptr;
664 ptr = (unsigned char*) p;
665 for( i=0; i<mwOverflowZoneSize; i++ )
666 {
667 *(ptr+i) = mwOverflowZoneTemplate[i%8];
668 }
669 return;
670}
671
672static int mwCheckOF( const void *p )
673{
674 int i;
675 const unsigned char *ptr;
676 ptr = (const unsigned char *) p;
677 for( i=0; i<mwOverflowZoneSize; i++ )
678 {
679 if( *(ptr+i) != mwOverflowZoneTemplate[i%8] )
680 return 1; /* errors found */
681 }
682 return 0; /* no errors */
683}
684
685int mwTest( const char *file, int line, int items ) {
686 mwAutoInit();
687 mwTestFlags = items;
688 return mwTestNow( file, line, 0 );
689 }
690
691/*
692** Returns zero if there are no errors.
693** Returns nonzero if there are errors.
694*/
695int mwTestBuffer( const char *file, int line, void *p ) {
696 mwData* mw;
697
698 mwAutoInit();
699
700 /* do the quick ownership test */
701 mw = (mwData*) mwBUFFER_TO_MW( p );
702
703 if( mwIsOwned( mw, file, line ) ) {
704 return mwTestBuf( mw, file, line );
705 }
706 return 1;
707 }
708
709void mwBreakOut( const char* cause ) {
710 fprintf(mwSTDERR, "breakout: %s\n", cause);
711 mwWrite("breakout: %s\n", cause );
712 return;
713 }
714
715/*
716** 981217 JLI: is it possible that ->next is not always set?
717*/
718void * mwMark( void *p, const char *desc, const char *file, unsigned line ) {
719 mwMarker *mrk;
720 unsigned n, isnew;
721 char *buf;
722 int tot, oflow = 0;
723 char wherebuf[128];
724
725 mwAutoInit();
726 TESTS(NULL,0);
727
728 if( desc == NULL ) desc = "unknown";
729 if( file == NULL ) file = "unknown";
730
731 tot = sprintf( wherebuf, "%.48s called from %s(%d)", desc, file, line );
732 if( tot >= (int)sizeof(wherebuf) ) { wherebuf[sizeof(wherebuf)-1] = 0; oflow = 1; }
733
734 if( p == NULL ) {
735 mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc );
736 return p;
737 }
738
739 if( mwFirstMark != NULL && !mwIsReadAddr( mwFirstMark, sizeof( mwMarker ) ) )
740 {
741 mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n",
742 file, line, mwFirstMark, desc );
743 return p;
744 }
745
746 for( mrk=mwFirstMark; mrk; mrk=mrk->next )
747 {
748 if( mrk->next != NULL && !mwIsReadAddr( mrk->next, sizeof( mwMarker ) ) )
749 {
750 mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n",
751 file, line, mrk, mrk->next, desc );
752 return p;
753 }
754 if( mrk->host == p ) break;
755 }
756
757 if( mrk == NULL ) {
758 isnew = 1;
759 mrk = (mwMarker*) malloc( sizeof( mwMarker ) );
760 if( mrk == NULL ) {
761 mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
762 return p;
763 }
764 mrk->next = NULL;
765 n = 0;
766 }
767 else {
768 isnew = 0;
769 n = strlen( mrk->text );
770 }
771
772 n += strlen( wherebuf );
773 buf = (char*) malloc( n+3 );
774 if( buf == NULL ) {
775 if( isnew ) free( mrk );
776 mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
777 return p;
778 }
779
780 if( isnew ) {
781 memcpy( buf, wherebuf, n+1 );
782 mrk->next = mwFirstMark;
783 mrk->host = p;
784 mrk->text = buf;
785 mrk->level = 1;
786 mwFirstMark = mrk;
787 }
788 else {
789 strcpy( buf, mrk->text );
790 strcat( buf, ", " );
791 strcat( buf, wherebuf );
792 free( mrk->text );
793 mrk->text = buf;
794 mrk->level ++;
795 }
796
797 if( oflow ) {
798 mwIncErr();
799 mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
800 }
801 return p;
802 }
803
804void* mwUnmark( void *p, const char *file, unsigned line ) {
805 mwMarker *mrk, *prv;
806 mrk = mwFirstMark;
807 prv = NULL;
808 while( mrk ) {
809 if( mrk->host == p ) {
810 if( mrk->level < 2 ) {
811 if( prv ) prv->next = mrk->next;
812 else mwFirstMark = mrk->next;
813 free( mrk->text );
814 free( mrk );
815 return p;
816 }
817 mrk->level --;
818 return p;
819 }
820 prv = mrk;
821 mrk = mrk->next;
822 }
823 mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p );
824 return p;
825 }
826
827
828/***********************************************************************
829** Abort/Retry/Ignore handlers
830***********************************************************************/
831
832static int mwARI( const char *estr ) {
833 char inbuf[81];
834 int c;
835 fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr);
836 (void) fgets(inbuf,sizeof(inbuf),stdin);
837 for( c=0; inbuf[c] && inbuf[c] <= ' '; c++ ) ;
838 c = inbuf[c];
839 if( c == 'R' || c == 'r' ) {
840 mwBreakOut( estr );
841 return MW_ARI_RETRY;
842 }
843 if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE;
844 return MW_ARI_ABORT;
845 }
846
847/* standard ARI handler (exported) */
848int mwAriHandler( const char *estr ) {
849 mwAutoInit();
850 return mwARI( estr );
851 }
852
853/* used to set the ARI function */
854void mwSetAriFunc( int (*func)(const char *) ) {
855 mwAutoInit();
856 mwAriFunction = func;
857 }
858
859/***********************************************************************
860** Allocation handlers
861***********************************************************************/
862
863void* mwMalloc( size_t size, const char* file, int line) {
864 size_t needed;
865 mwData *mw;
866 char *ptr;
867 void *p;
868
869 mwAutoInit();
870
872
873 TESTS(file,line);
874
875 mwCounter ++;
876 needed = mwDataSize + mwOverflowZoneSize*2 + size;
877 if( needed < size )
878 {
879 /* theoretical case: req size + mw overhead exceeded size_t limits */
880 return NULL;
881 }
882
883 /* if this allocation would violate the limit, fail it */
884 if( mwUseLimit && ((long)size + mwStatCurAlloc > mwAllocLimit) ) {
885 mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
886 mwCounter, file, line, (long)size, mwAllocLimit - mwStatCurAlloc );
887 mwIncErr();
888 FLUSH();
890 return NULL;
891 }
892
893 mw = (mwData*) malloc( needed );
894 if( mw == NULL ) {
895 if( mwFreeUp(needed,0) >= needed ) {
896 mw = (mwData*) malloc(needed);
897 if( mw == NULL ) {
898 mwWrite( "internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed );
899 mwIncErr();
900 FLUSH();
901 }
902 }
903 if( mw == NULL ) {
904 mwWrite( "fail: <%ld> %s(%d), %ld wanted %ld allocated\n",
905 mwCounter, file, line, (long)size, mwStatCurAlloc );
906 mwIncErr();
907 FLUSH();
909 return NULL;
910 }
911 }
912
913 mw->count = mwCounter;
914 mw->prev = NULL;
915 mw->next = mwHead;
916 mw->file = file;
917 mw->size = size;
918 mw->line = line;
919 mw->flag = 0;
920 mw->check = CHKVAL(mw);
921
922 if( mwHead ) mwHead->prev = mw;
923 mwHead = mw;
924 if( mwTail == NULL ) mwTail = mw;
925
926 ptr = ((char*)mw) + mwDataSize;
927 mwWriteOF( ptr ); /* '*(long*)ptr = PRECHK;' */
928 ptr += mwOverflowZoneSize;
929 p = ptr;
930 memset( ptr, MW_VAL_NEW, size );
931 ptr += size;
932 mwWriteOF( ptr ); /* '*(long*)ptr = POSTCHK;' */
933
934 mwNumCurAlloc ++;
935 mwStatCurAlloc += (long) size;
936 mwStatTotAlloc += (long) size;
937 if( mwStatCurAlloc > mwStatMaxAlloc )
938 mwStatMaxAlloc = mwStatCurAlloc;
939 mwStatNumAlloc ++;
940
941 if( mwStatLevel ) mwStatAlloc( size, file, line );
942
944 return p;
945 }
946
947void* mwRealloc( void *p, size_t size, const char* file, int line) {
948 int oldUseLimit, i;
949 mwData *mw;
950 char *ptr;
951
952 mwAutoInit();
953
954 if( p == NULL ) return mwMalloc( size, file, line );
955 if( size == 0 ) { mwFree( p, file, line ); return NULL; }
956
958
959 /* do the quick ownership test */
960 mw = (mwData*) mwBUFFER_TO_MW( p );
961 if( mwIsOwned( mw, file, line ) ) {
962
963 /* if the buffer is an NML, treat this as a double-free */
964 if( mw->flag & MW_NML )
965 {
966 mwIncErr();
967 if( *((unsigned char*)(mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
968 {
969 mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
970 mwCounter, file, line, mw );
971 }
972 goto check_dbl_free;
973 }
974
975 /* if this allocation would violate the limit, fail it */
976 if( mwUseLimit && ((long)size + mwStatCurAlloc - (long)mw->size > mwAllocLimit) ) {
977 TESTS(file,line);
978 mwCounter ++;
979 mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
980 mwCounter, file, line, (unsigned long)size - mw->size, mwAllocLimit - mwStatCurAlloc );
981 mwIncErr();
982 FLUSH();
984 return NULL;
985 }
986
987 /* fake realloc operation */
988 oldUseLimit = mwUseLimit;
989 mwUseLimit = 0;
990 ptr = (char*) mwMalloc( size, file, line );
991 if( ptr != NULL ) {
992 if( size < mw->size )
993 memcpy( ptr, p, size );
994 else
995 memcpy( ptr, p, mw->size );
996 mwFree( p, file, line );
997 }
998 mwUseLimit = oldUseLimit;
1000 return (void*) ptr;
1001 }
1002
1003 /* Unknown pointer! */
1004
1005 /* using free'd pointer? */
1006check_dbl_free:
1007 for(i=0;i<MW_FREE_LIST;i++) {
1008 if( mwLastFree[i] == p ) {
1009 mwIncErr();
1010 mwWrite( "realloc: <%ld> %s(%d), %p was"
1011 " freed from %s(%d)\n",
1012 mwCounter, file, line, p,
1013 mwLFfile[i], mwLFline[i] );
1014 FLUSH();
1016 return NULL;
1017 }
1018 }
1019
1020 /* some weird pointer */
1021 mwIncErr();
1022 mwWrite( "realloc: <%ld> %s(%d), unknown pointer %p\n",
1023 mwCounter, file, line, p );
1024 FLUSH();
1026 return NULL;
1027 }
1028
1029char *mwStrdup( const char* str, const char* file, int line ) {
1030 size_t len;
1031 char *newstring;
1032
1033 MW_MUTEX_LOCK();
1034
1035 if( str == NULL ) {
1036 mwIncErr();
1037 mwWrite( "strdup: <%ld> %s(%d), strdup(NULL) called\n",
1038 mwCounter, file, line );
1039 FLUSH();
1041 return NULL;
1042 }
1043
1044 len = strlen( str ) + 1;
1045 newstring = (char*) mwMalloc( len, file, line );
1046 if( newstring != NULL ) memcpy( newstring, str, len );
1048 return newstring;
1049 }
1050
1051void mwFree( void* p, const char* file, int line ) {
1052 int i;
1053 mwData* mw;
1054 char buffer[ sizeof(mwData) + (mwROUNDALLOC*3) + 64 ];
1055
1056 /* this code is in support of C++ delete */
1057 if( file == NULL ) {
1058 mwFree_( p );
1060 return;
1061 }
1062
1063 mwAutoInit();
1064
1065 MW_MUTEX_LOCK();
1066 TESTS(file,line);
1067 mwCounter ++;
1068
1069 /* on NULL free, write a warning and return */
1070 if( p == NULL ) {
1071 mwWrite( "NULL free: <%ld> %s(%d), NULL pointer free'd\n",
1072 mwCounter, file, line );
1073 FLUSH();
1075 return;
1076 }
1077
1078 /* do the quick ownership test */
1079 mw = (mwData*) mwBUFFER_TO_MW( p );
1080
1081 if( mwIsOwned( mw, file, line ) ) {
1082 (void) mwTestBuf( mw, file, line );
1083
1084 /* if the buffer is an NML, treat this as a double-free */
1085 if( mw->flag & MW_NML )
1086 {
1087 if( *(((unsigned char*)mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
1088 {
1089 mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
1090 mwCounter, file, line, mw );
1091 }
1092 goto check_dbl_free;
1093 }
1094
1095 /* update the statistics */
1096 mwNumCurAlloc --;
1097 mwStatCurAlloc -= (long) mw->size;
1098 if( mwStatLevel ) mwStatFree( mw->size, mw->file, mw->line );
1099
1100 /* we should either free the allocation or keep it as NML */
1101 if( mwNML ) {
1102 mw->flag |= MW_NML;
1103 mwNmlNumAlloc ++;
1104 mwNmlCurAlloc += (long) mw->size;
1105 memset( ((char*)mw)+mwDataSize+mwOverflowZoneSize, MW_VAL_NML, mw->size );
1106 }
1107 else {
1108 /* unlink the allocation, and enter the post-free data */
1109 mwUnlink( mw, file, line );
1110 memset( mw, MW_VAL_DEL,
1111 mw->size + mwDataSize+mwOverflowZoneSize+mwOverflowZoneSize );
1112 if( mwFBI ) {
1113 memset( mw, '.', mwDataSize + mwOverflowZoneSize );
1114 sprintf( buffer, "FBI<%ld>%s(%d)", mwCounter, file, line );
1115 strncpy( (char*)(void*)mw, buffer, mwDataSize + mwOverflowZoneSize );
1116 }
1117 free( mw );
1118 }
1119
1120 /* add the pointer to the last-free track */
1121 mwLFfile[ mwLFcur ] = file;
1122 mwLFline[ mwLFcur ] = line;
1123 mwLastFree[ mwLFcur++ ] = p;
1124 if( mwLFcur == MW_FREE_LIST ) mwLFcur = 0;
1125
1127 return;
1128 }
1129
1130 /* check for double-freeing */
1131check_dbl_free:
1132 for(i=0;i<MW_FREE_LIST;i++) {
1133 if( mwLastFree[i] == p ) {
1134 mwIncErr();
1135 mwWrite( "double-free: <%ld> %s(%d), %p was"
1136 " freed from %s(%d)\n",
1137 mwCounter, file, line, p,
1138 mwLFfile[i], mwLFline[i] );
1139 FLUSH();
1141 return;
1142 }
1143 }
1144
1145 /* some weird pointer... block the free */
1146 mwIncErr();
1147 mwWrite( "WILD free: <%ld> %s(%d), unknown pointer %p\n",
1148 mwCounter, file, line, p );
1149 FLUSH();
1151 return;
1152 }
1153
1154void* mwCalloc( size_t a, size_t b, const char *file, int line ) {
1155 void *p;
1156 size_t size = a * b;
1157 p = mwMalloc( size, file, line );
1158 if( p == NULL ) return NULL;
1159 memset( p, 0, size );
1160 return p;
1161 }
1162
1163void mwFree_( void *p ) {
1164 MW_MUTEX_LOCK();
1165 TESTS(NULL,0);
1167 free(p);
1168 }
1169
1170void* mwMalloc_( size_t size ) {
1171 MW_MUTEX_LOCK();
1172 TESTS(NULL,0);
1174 return malloc( size );
1175 }
1176
1177void* mwRealloc_( void *p, size_t size ) {
1178 MW_MUTEX_LOCK();
1179 TESTS(NULL,0);
1181 return realloc( p, size );
1182 }
1183
1184void* mwCalloc_( size_t a, size_t b ) {
1185 MW_MUTEX_LOCK();
1186 TESTS(NULL,0);
1188 return calloc( a, b );
1189 }
1190
1191void mwFlushNow( void ) {
1192 if( mwLogR() ) fflush( mwLogR() );
1193 return;
1194 }
1195
1196void mwDoFlush( int onoff ) {
1197 mwFlushW( onoff<1?0:onoff );
1198 if( onoff ) if( mwLogR() ) fflush( mwLogR() );
1199 return;
1200 }
1201
1202void mwLimit( long lim ) {
1203 TESTS(NULL,0);
1204 mwWrite("limit: old limit = ");
1205 if( !mwAllocLimit ) mwWrite( "none" );
1206 else mwWrite( "%ld bytes", mwAllocLimit );
1207 mwWrite( ", new limit = ");
1208 if( !lim ) {
1209 mwWrite( "none\n" );
1210 mwUseLimit = 0;
1211 }
1212 else {
1213 mwWrite( "%ld bytes\n", lim );
1214 mwUseLimit = 1;
1215 }
1216 mwAllocLimit = lim;
1217 FLUSH();
1218 }
1219
1220void mwSetAriAction( int action ) {
1221 MW_MUTEX_LOCK();
1222 TESTS(NULL,0);
1223 mwAriAction = action;
1225 return;
1226 }
1227
1228int mwAssert( int exp, const char *exps, const char *fn, int ln ) {
1229 int i;
1230 char buffer[MW_TRACE_BUFFER+8];
1231 if( exp ) {
1232 return 0;
1233 }
1234 mwAutoInit();
1235 MW_MUTEX_LOCK();
1236 TESTS(fn,ln);
1237 mwIncErr();
1238 mwCounter++;
1239 mwWrite( "assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
1240 if( mwAriFunction != NULL ) {
1241 sprintf( buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps );
1242 i = (*mwAriFunction)(buffer);
1243 switch( i ) {
1244 case MW_ARI_IGNORE:
1245 mwWrite( "assert trap: <%ld> IGNORED - execution continues\n", mwCounter );
1247 return 0;
1248 case MW_ARI_RETRY:
1249 mwWrite( "assert trap: <%ld> RETRY - executing again\n", mwCounter );
1251 return 1;
1252 }
1253 }
1254 else {
1255 if( mwAriAction & MW_ARI_IGNORE ) {
1256 mwWrite( "assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
1258 return 0;
1259 }
1260 fprintf(mwSTDERR,"\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps );
1261 }
1262
1263 FLUSH();
1264 (void) mwTestNow( fn, ln, 1 );
1265 FLUSH();
1266
1267 if( mwAriAction & MW_ARI_NULLREAD ) {
1268 /* This is made in an attempt to kick in */
1269 /* any debuggers or OS stack traces */
1270 FLUSH();
1271 /*lint -save -e413 */
1272 i = *((int*)NULL);
1273 mwDummy( (char)i );
1274 /*lint -restore */
1275 }
1276
1278 exit(255);
1279 /* NOT REACHED - the return statement is in to keep */
1280 /* stupid compilers from squeaking about differing return modes. */
1281 /* Smart compilers instead say 'code unreachable...' */
1282 /*lint -save -e527 */
1283 return 0;
1284 /*lint -restore */
1285 }
1286
1287int mwVerify( int exp, const char *exps, const char *fn, int ln ) {
1288 int i;
1289 char buffer[MW_TRACE_BUFFER+8];
1290 if( exp ) {
1291 return 0;
1292 }
1293 mwAutoInit();
1294 MW_MUTEX_LOCK();
1295 TESTS(fn,ln);
1296 mwIncErr();
1297 mwCounter++;
1298 mwWrite( "verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
1299 if( mwAriFunction != NULL ) {
1300 sprintf( buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps );
1301 i = (*mwAriFunction)(buffer);
1302 if( i == 0 ) {
1303 mwWrite( "verify trap: <%ld> IGNORED - execution continues\n", mwCounter );
1305 return 0;
1306 }
1307 if( i == 1 ) {
1308 mwWrite( "verify trap: <%ld> RETRY - executing again\n", mwCounter );
1310 return 1;
1311 }
1312 }
1313 else {
1314 if( mwAriAction & MW_ARI_NULLREAD ) {
1315 /* This is made in an attempt to kick in */
1316 /* any debuggers or OS stack traces */
1317 FLUSH();
1318 /*lint -save -e413 */
1319 i = *((int*)NULL);
1320 mwDummy( (char)i );
1321 /*lint -restore */
1322 }
1323 if( mwAriAction & MW_ARI_IGNORE ) {
1324 mwWrite( "verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
1326 return 0;
1327 }
1328 fprintf(mwSTDERR,"\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps );
1329 }
1330 FLUSH();
1331 (void) mwTestNow( fn, ln, 1 );
1332 FLUSH();
1334 exit(255);
1335 /* NOT REACHED - the return statement is in to keep */
1336 /* stupid compilers from squeaking about differing return modes. */
1337 /* Smart compilers instead say 'code unreachable...' */
1338 /*lint -save -e527 */
1339 return 0;
1340 /*lint -restore */
1341 }
1342
1343void mwTrace( const char *format, ... ) {
1344 int tot, oflow = 0;
1345 va_list mark;
1346
1347 mwAutoInit();
1348 MW_MUTEX_LOCK();
1349 TESTS(NULL,0);
1350 if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc;
1351
1352 va_start( mark, format );
1353 tot = vsprintf( mwPrintBuf, format, mark );
1354 va_end( mark );
1355 if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; }
1356 for(tot=0;mwPrintBuf[tot];tot++)
1357 (*mwOutFunction)( mwPrintBuf[tot] );
1358 if( oflow ) {
1359 mwIncErr();
1360 mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
1361 }
1362
1363 FLUSH();
1365 }
1366
1367
1368/***********************************************************************
1369** Grab & Drop
1370***********************************************************************/
1371
1372unsigned mwGrab( unsigned kb ) {
1373 TESTS(NULL,0);
1374 return mwGrab_( kb, MW_VAL_GRB, 0 );
1375 }
1376
1377unsigned mwDrop( unsigned kb ) {
1378 TESTS(NULL,0);
1379 return mwDrop_( kb, MW_VAL_GRB, 0 );
1380 }
1381
1382static void mwDropAll() {
1383 TESTS(NULL,0);
1384 (void) mwDrop_( 0, MW_VAL_GRB, 0 );
1385 (void) mwDrop_( 0, MW_VAL_NML, 0 );
1386 if( mwGrabList != NULL )
1387 mwWrite( "internal: the grab list is not empty after mwDropAll()\n");
1388 }
1389
1390static const char *mwGrabType( int type ) {
1391 switch( type ) {
1392 case MW_VAL_GRB:
1393 return "grabbed";
1394 case MW_VAL_NML:
1395 return "no-mans-land";
1396 default:
1397 /* do nothing */
1398 ;
1399 }
1400 return "<unknown type>";
1401 }
1402
1403static unsigned mwGrab_( unsigned kb, int type, int silent ) {
1404 unsigned i = kb;
1405 mwGrabData *gd;
1406 if( !kb ) i = kb = 65000U;
1407
1408 for(;kb;kb--) {
1409 if( mwUseLimit &&
1410 (mwStatCurAlloc + mwGrabSize + (long)sizeof(mwGrabData) > mwAllocLimit) ) {
1411 if( !silent ) {
1412 mwWrite("grabbed: all allowed memory to %s (%u kb)\n",
1413 mwGrabType(type), i-kb);
1414 FLUSH();
1415 }
1416 return i-kb;
1417 }
1418 gd = (mwGrabData*) malloc( sizeof(mwGrabData) );
1419 if( gd == NULL ) {
1420 if( !silent ) {
1421 mwWrite("grabbed: all available memory to %s (%u kb)\n",
1422 mwGrabType(type), i-kb);
1423 FLUSH();
1424 }
1425 return i-kb;
1426 }
1427 mwGrabSize += (long) sizeof(mwGrabData);
1428 gd->next = mwGrabList;
1429 memset( gd->blob, type, sizeof(gd->blob) );
1430 gd->type = type;
1431 mwGrabList = gd;
1432 }
1433 if( !silent ) {
1434 mwWrite("grabbed: %u kilobytes of %s memory\n", i, mwGrabType(type) );
1435 FLUSH();
1436 }
1437 return i;
1438 }
1439
1440static unsigned mwDrop_( unsigned kb, int type, int silent ) {
1441 unsigned i = kb;
1442 mwGrabData *gd,*tmp,*pr;
1443 const void *p;
1444
1445 if( mwGrabList == NULL && kb == 0 ) return 0;
1446 if( !kb ) i = kb = 60000U;
1447
1448 pr = NULL;
1449 gd = mwGrabList;
1450 for(;kb;) {
1451 if( gd == NULL ) {
1452 if( i-kb > 0 && !silent ) {
1453 mwWrite("dropped: all %s memory (%u kb)\n", mwGrabType(type), i-kb);
1454 FLUSH();
1455 }
1456 return i-kb;
1457 }
1458 if( gd->type == type ) {
1459 if( pr ) pr->next = gd->next;
1460 kb --;
1461 tmp = gd;
1462 if( mwGrabList == gd ) mwGrabList = gd->next;
1463 gd = gd->next;
1464 p = mwTestMem( tmp->blob, sizeof( tmp->blob ), type );
1465 if( p != NULL ) {
1466 mwWrite( "wild pointer: <%ld> %s memory hit at %p\n",
1467 mwCounter, mwGrabType(type), p );
1468 FLUSH();
1469 }
1470 mwGrabSize -= (long) sizeof(mwGrabData);
1471 free( tmp );
1472 }
1473 else {
1474 pr = gd;
1475 gd = gd->next;
1476 }
1477 }
1478 if( !silent ) {
1479 mwWrite("dropped: %u kilobytes of %s memory\n", i, mwGrabType(type) );
1480 FLUSH();
1481 }
1482 return i;
1483 }
1484
1485/***********************************************************************
1486** No-Mans-Land
1487***********************************************************************/
1488
1489void mwNoMansLand( int level ) {
1490 mwAutoInit();
1491 TESTS(NULL,0);
1492 switch( level ) {
1493 case MW_NML_NONE:
1494 (void) mwDrop_( 0, MW_VAL_NML, 0 );
1495 break;
1496 case MW_NML_FREE:
1497 break;
1498 case MW_NML_ALL:
1499 (void) mwGrab_( 0, MW_VAL_NML, 0 );
1500 break;
1501 default:
1502 return;
1503 }
1504 mwNML = level;
1505 }
1506
1507/***********************************************************************
1508** Static functions
1509***********************************************************************/
1510
1511static void mwAutoInit( void )
1512{
1513 if( mwInited ) return;
1514 mwUseAtexit = 1;
1515 mwInit();
1516 return;
1517}
1518
1519static FILE *mwLogR() {
1520 if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) return mwLog;
1521 if( mwLog == mwLogB1 ) mwLogB2 = mwLog;
1522 if( mwLog == mwLogB2 ) mwLogB1 = mwLog;
1523 if( mwLogB1 == mwLogB2 ) mwLog = mwLogB1;
1524 if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) {
1525 mwWrite("internal: log file handle damaged and recovered\n");
1526 FLUSH();
1527 return mwLog;
1528 }
1529 fprintf(mwSTDERR,"\nMEMWATCH: log file handle destroyed, using mwSTDERR\n" );
1530 mwLog = mwLogB1 = mwLogB2 = mwSTDERR;
1531 return mwSTDERR;
1532 }
1533
1534static void mwLogW( FILE *p ) {
1535 mwLog = mwLogB1 = mwLogB2 = p;
1536 }
1537
1538static int mwFlushR() {
1539 if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) return mwFlushing;
1540 if( mwFlushing == mwFlushingB1 ) mwFlushingB2 = mwFlushing;
1541 if( mwFlushing == mwFlushingB2 ) mwFlushingB1 = mwFlushing;
1542 if( mwFlushingB1 == mwFlushingB2 ) mwFlushing = mwFlushingB1;
1543 if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) {
1544 mwWrite("internal: flushing flag damaged and recovered\n");
1545 FLUSH();
1546 return mwFlushing;
1547 }
1548 mwWrite("internal: flushing flag destroyed, so set to true\n");
1549 mwFlushing = mwFlushingB1 = mwFlushingB2 = 1;
1550 return 1;
1551 }
1552
1553static void mwFlushW( int n ) {
1554 mwFlushing = mwFlushingB1 = mwFlushingB2 = n;
1555 }
1556
1557static void mwIncErr() {
1558 mwErrors++;
1559 mwFlushW( mwFlushR()+1 );
1560 FLUSH();
1561 }
1562
1563static void mwFlush() {
1564 if( mwLogR() == NULL ) return;
1565#ifdef MW_FLUSH
1566 fflush( mwLogR() );
1567#else
1568 if( mwFlushR() ) fflush( mwLogR() );
1569#endif
1570 return;
1571 }
1572
1573static void mwUnlink( mwData* mw, const char* file, int line ) {
1574 if( mw->prev == NULL ) {
1575 if( mwHead != mw )
1576 mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 NULL, but not head\n",
1577 mwCounter, file, line, mw );
1578 mwHead = mw->next;
1579 }
1580 else {
1581 if( mw->prev->next != mw )
1582 mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 failure\n",
1583 mwCounter, file, line, mw );
1584 else mw->prev->next = mw->next;
1585 }
1586 if( mw->next == NULL ) {
1587 if( mwTail != mw )
1588 mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 NULL, but not tail\n",
1589 mwCounter, file, line, mw );
1590 mwTail = mw->prev;
1591 }
1592 else {
1593 if( mw->next->prev != mw )
1594 mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 failure\n",
1595 mwCounter, file, line, mw );
1596 else mw->next->prev = mw->prev;
1597 }
1598 }
1599
1600/*
1601** Relinking tries to repair a damaged mw block.
1602** Returns nonzero if it thinks it successfully
1603** repaired the heap chain.
1604*/
1605static int mwRelink( mwData* mw, const char* file, int line ) {
1606 int fails;
1607 mwData *mw1, *mw2;
1608 long count, size;
1609 mwStat *ms;
1610
1611 if( file == NULL ) file = "unknown";
1612
1613 if( mw == NULL ) {
1614 mwWrite("relink: cannot repair MW at NULL\n");
1615 FLUSH();
1616 goto emergency;
1617 }
1618
1619 if( !mwIsSafeAddr(mw, mwDataSize) ) {
1620 mwWrite("relink: MW-%p is a garbage pointer\n", mw);
1621 FLUSH();
1622 goto emergency;
1623 }
1624
1625 mwWrite("relink: <%ld> %s(%d) attempting to repair MW-%p...\n", mwCounter, file, line, mw );
1626 FLUSH();
1627 fails = 0;
1628
1629 /* Repair from head */
1630 if( mwHead != mw ) {
1631 if( !mwIsSafeAddr( mwHead, mwDataSize ) ) {
1632 mwWrite("relink: failed for MW-%p; head pointer destroyed\n", mw );
1633 FLUSH();
1634 goto emergency;
1635 }
1636 for( mw1=mwHead; mw1; mw1=mw1->next ) {
1637 if( mw1->next == mw ) {
1638 mw->prev = mw1;
1639 break;
1640 }
1641 if( mw1->next &&
1642 ( !mwIsSafeAddr(mw1->next, mwDataSize ) || mw1->next->prev != mw1) ) {
1643 mwWrite("relink: failed for MW-%p; forward chain fragmented at MW-%p: 'next' is %p\n", mw, mw1, mw1->next );
1644 FLUSH();
1645 goto emergency;
1646 }
1647 }
1648 if( mw1 == NULL ) {
1649 mwWrite("relink: MW-%p not found in forward chain search\n", mw );
1650 FLUSH();
1651 fails ++;
1652 }
1653 }
1654 else
1655 {
1656 mwWrite( "relink: MW-%p is the head (first) allocation\n", mw );
1657 if( mw->prev != NULL )
1658 {
1659 mwWrite( "relink: MW-%p prev pointer is non-NULL, you have a wild pointer\n", mw );
1660 mw->prev = NULL;
1661 }
1662 }
1663
1664 /* Repair from tail */
1665 if( mwTail != mw ) {
1666 if( !mwIsSafeAddr( mwTail, mwDataSize ) ) {
1667 mwWrite("relink: failed for MW-%p; tail pointer destroyed\n", mw );
1668 FLUSH();
1669 goto emergency;
1670 }
1671 for( mw1=mwTail; mw1; mw1=mw1->prev ) {
1672 if( mw1->prev == mw ) {
1673 mw->next = mw1;
1674 break;
1675 }
1676 if( mw1->prev && (!mwIsSafeAddr(mw1->prev, mwDataSize ) || mw1->prev->next != mw1) ) {
1677 mwWrite("relink: failed for MW-%p; reverse chain fragmented at MW-%p, 'prev' is %p\n", mw, mw1, mw1->prev );
1678 FLUSH();
1679 goto emergency;
1680 }
1681 }
1682 if( mw1 == NULL ) {
1683 mwWrite("relink: MW-%p not found in reverse chain search\n", mw );
1684 FLUSH();
1685 fails ++;
1686 }
1687 }
1688 else
1689 {
1690 mwWrite( "relink: MW-%p is the tail (last) allocation\n", mw );
1691 if( mw->next != NULL )
1692 {
1693 mwWrite( "relink: MW-%p next pointer is non-NULL, you have a wild pointer\n", mw );
1694 mw->next = NULL;
1695 }
1696 }
1697
1698 if( fails > 1 ) {
1699 mwWrite("relink: heap appears intact, MW-%p probably garbage pointer\n", mw );
1700 FLUSH();
1701 goto verifyok;
1702 }
1703
1704 /* restore MW info where possible */
1705 if( mwIsReadAddr( mw->file, 1 ) ) {
1706 ms = mwStatGet( mw->file, -1, 0 );
1707 if( ms == NULL ) mw->file = "<relinked>";
1708 }
1709 mw->check = CHKVAL(mw);
1710 goto verifyok;
1711
1712 /* Emergency repair */
1713 emergency:
1714
1715 if( mwHead == NULL && mwTail == NULL )
1716 {
1717 if( mwStatCurAlloc == 0 )
1718 mwWrite("relink: <%ld> %s(%d) heap is empty, nothing to repair\n", mwCounter, file, line );
1719 else
1720 mwWrite("relink: <%ld> %s(%d) heap damaged beyond repair\n", mwCounter, file, line );
1721 FLUSH();
1722 return 0;
1723 }
1724
1725 mwWrite("relink: <%ld> %s(%d) attempting emergency repairs...\n", mwCounter, file, line );
1726 FLUSH();
1727
1728 if( mwHead == NULL || mwTail == NULL )
1729 {
1730 if( mwHead == NULL ) mwWrite("relink: mwHead is NULL, but mwTail is %p\n", mwTail );
1731 else mwWrite("relink: mwTail is NULL, but mwHead is %p\n", mwHead );
1732 }
1733
1734 mw1=NULL;
1735 if( mwHead != NULL )
1736 {
1737 if( !mwIsReadAddr( mwHead, mwDataSize ) || mwHead->check != CHKVAL(mwHead) )
1738 {
1739 mwWrite("relink: mwHead (MW-%p) is damaged, skipping forward scan\n", mwHead );
1740 mwHead = NULL;
1741 goto scan_reverse;
1742 }
1743 if( mwHead->prev != NULL )
1744 {
1745 mwWrite("relink: the mwHead pointer's 'prev' member is %p, not NULL\n", mwHead->prev );
1746 }
1747 for( mw1=mwHead; mw1; mw1=mw1->next )
1748 {
1749 if( mw1->next )
1750 {
1751 if( !mwIsReadAddr(mw1->next,mwDataSize) ||
1752 !mw1->next->check != CHKVAL(mw1) ||
1753 mw1->next->prev != mw1 )
1754 {
1755 mwWrite("relink: forward chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
1756 mw1, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw1->file, mw1->line );
1757 if( mwIsReadAddr(mw1->next,mwDataSize ) )
1758 {
1759 mwWrite("relink: forward chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
1760 mw1->next, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"",
1761 mwIsReadAddr(mw1->file,16)?mw1->file:"<garbage-pointer>", mw1->line );
1762 }
1763 else
1764 {
1765 mwWrite("relink: the 'next' pointer of this MW points to %p, which is out-of-legal-access\n",
1766 mw1->next );
1767 }
1768 break;
1769 }
1770 }
1771 }
1772 }
1773
1774
1775scan_reverse:
1776 mw2=NULL;
1777 if( mwTail != NULL )
1778 {
1779 if( !mwIsReadAddr(mwTail,mwDataSize) || mwTail->check != CHKVAL(mwTail) )
1780 {
1781 mwWrite("relink: mwTail (%p) is damaged, skipping reverse scan\n", mwTail );
1782 mwTail = NULL;
1783 goto analyze;
1784 }
1785 if( mwTail->next != NULL )
1786 {
1787 mwWrite("relink: the mwTail pointer's 'next' member is %p, not NULL\n", mwTail->next );
1788 }
1789 for( mw2=mwTail; mw2; mw2=mw2->prev )
1790 {
1791 if( mw2->prev )
1792 {
1793 if( !mwIsReadAddr(mw2->prev,mwDataSize) ||
1794 !mw2->prev->check != CHKVAL(mw2) ||
1795 mw2->prev->next != mw2 )
1796 {
1797 mwWrite("relink: reverse chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n",
1798 mw2, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw2->file, mw2->line );
1799 if( mwIsReadAddr(mw2->prev,mwDataSize ) )
1800 {
1801 mwWrite("relink: reverse chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n",
1802 mw2->prev, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"",
1803 mwIsReadAddr(mw2->file,16)?mw2->file:"<garbage-pointer>", mw2->line );
1804 }
1805 else
1806 {
1807 mwWrite("relink: the 'prev' pointer of this MW points to %p, which is out-of-legal-access\n",
1808 mw2->prev );
1809 }
1810 break;
1811 }
1812 }
1813 }
1814 }
1815
1816analyze:
1817 if( mwHead == NULL && mwTail == NULL )
1818 {
1819 mwWrite("relink: both head and tail pointers damaged, aborting program\n");
1820 mwFlushW(1);
1821 FLUSH();
1822 abort();
1823 }
1824 if( mwHead == NULL )
1825 {
1826 mwHead = mw2;
1827 mwWrite("relink: heap truncated, MW-%p designated as new mwHead\n", mw2 );
1828 mw2->prev = NULL;
1829 mw1 = mw2 = NULL;
1830 }
1831 if( mwTail == NULL )
1832 {
1833 mwTail = mw1;
1834 mwWrite("relink: heap truncated, MW-%p designated as new mwTail\n", mw1 );
1835 mw1->next = NULL;
1836 mw1 = mw2 = NULL;
1837 }
1838 if( mw1 == NULL && mw2 == NULL &&
1839 mwHead->prev == NULL && mwTail->next == NULL ) {
1840 mwWrite("relink: verifying heap integrity...\n" );
1841 FLUSH();
1842 goto verifyok;
1843 }
1844 if( mw1 && mw2 && mw1 != mw2 ) {
1845 mw1->next = mw2;
1846 mw2->prev = mw1;
1847 mwWrite("relink: emergency repairs successful, assessing damage...\n");
1848 FLUSH();
1849 }
1850 else {
1851 mwWrite("relink: heap totally destroyed, aborting program\n");
1852 mwFlushW(1);
1853 FLUSH();
1854 abort();
1855 }
1856
1857 /* Verify by checking that the number of active allocations */
1858 /* match the number of entries in the chain */
1859verifyok:
1860 if( !mwIsHeapOK( NULL ) ) {
1861 mwWrite("relink: heap verification FAILS - aborting program\n");
1862 mwFlushW(1);
1863 FLUSH();
1864 abort();
1865 }
1866 for( size=count=0, mw1=mwHead; mw1; mw1=mw1->next ) {
1867 count ++;
1868 size += (long) mw1->size;
1869 }
1870 if( count == mwNumCurAlloc ) {
1871 mwWrite("relink: successful, ");
1872 if( size == mwStatCurAlloc ) {
1873 mwWrite("no allocations lost\n");
1874 }
1875 else {
1876 if( mw != NULL ) {
1877 mwWrite("size information lost for MW-%p\n", mw);
1878 mw->size = 0;
1879 }
1880 }
1881 }
1882 else {
1883 mwWrite("relink: partial, %ld MW-blocks of %ld bytes lost\n",
1884 mwNmlNumAlloc+mwNumCurAlloc-count, mwNmlCurAlloc+mwStatCurAlloc-size );
1885 return 0;
1886 }
1887
1888 return 1;
1889 }
1890
1891/*
1892** If mwData* is NULL:
1893** Returns 0 if heap chain is broken.
1894** Returns 1 if heap chain is intact.
1895** If mwData* is not NULL:
1896** Returns 0 if mwData* is missing or if chain is broken.
1897** Returns 1 if chain is intact and mwData* is found.
1898*/
1899static int mwIsHeapOK( mwData *includes_mw ) {
1900 int found = 0;
1901 mwData *mw;
1902
1903 for( mw = mwHead; mw; mw=mw->next ) {
1904 if( includes_mw == mw ) found++;
1905 if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0;
1906 if( mw->prev ) {
1907 if( !mwIsSafeAddr( mw->prev, mwDataSize ) ) return 0;
1908 if( mw==mwHead || mw->prev->next != mw ) return 0;
1909 }
1910 if( mw->next ) {
1911 if( !mwIsSafeAddr( mw->next, mwDataSize ) ) return 0;
1912 if( mw==mwTail || mw->next->prev != mw ) return 0;
1913 }
1914 else if( mw!=mwTail ) return 0;
1915 }
1916
1917 if( includes_mw != NULL && !found ) return 0;
1918
1919 return 1;
1920 }
1921
1922static int mwIsOwned( mwData* mw, const char *file, int line ) {
1923 int retv;
1924 mwStat *ms;
1925
1926 /* see if the address is legal according to OS */
1927 if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0;
1928
1929 /* make sure we have _anything_ allocated */
1930 if( mwHead == NULL && mwTail == NULL && mwStatCurAlloc == 0 )
1931 return 0;
1932
1933 /* calculate checksum */
1934 if( mw->check != CHKVAL(mw) ) {
1935 /* may be damaged checksum, see if block is in heap */
1936 if( mwIsHeapOK( mw ) ) {
1937 /* damaged checksum, repair it */
1938 mwWrite( "internal: <%ld> %s(%d), checksum for MW-%p is incorrect\n",
1939 mwCounter, file, line, mw );
1940 mwIncErr();
1941 if( mwIsReadAddr( mw->file, 1 ) ) {
1942 ms = mwStatGet( mw->file, -1, 0 );
1943 if( ms == NULL ) mw->file = "<relinked>";
1944 }
1945 else mw->file = "<unknown>";
1946 mw->size = 0;
1947 mw->check = CHKVAL(mw);
1948 return 1;
1949 }
1950 /* no, it's just some garbage data */
1951 return 0;
1952 }
1953
1954 /* check that the non-NULL pointers are safe */
1955 if( mw->prev && !mwIsSafeAddr( mw->prev, mwDataSize ) ) mwRelink( mw, file, line );
1956 if( mw->next && !mwIsSafeAddr( mw->next, mwDataSize ) ) mwRelink( mw, file, line );
1957
1958 /* safe address, checksum OK, proceed with heap checks */
1959
1960 /* see if the block is in the heap */
1961 retv = 0;
1962 if( mw->prev ) { if( mw->prev->next == mw ) retv ++; }
1963 else { if( mwHead == mw ) retv++; }
1964 if( mw->next ) { if( mw->next->prev == mw ) retv ++; }
1965 else { if( mwTail == mw ) retv++; }
1966 if( mw->check == CHKVAL(mw) ) retv ++;
1967 if( retv > 2 ) return 1;
1968
1969 /* block not in heap, check heap for corruption */
1970
1971 if( !mwIsHeapOK( mw ) ) {
1972 if( mwRelink( mw, file, line ) )
1973 return 1;
1974 }
1975
1976 /* unable to repair */
1977 mwWrite( "internal: <%ld> %s(%d), mwIsOwned fails for MW-%p\n",
1978 mwCounter, file, line, mw );
1979 mwIncErr();
1980
1981 return 0;
1982 }
1983
1984/*
1985** mwTestBuf:
1986** Checks a buffers links and pre/postfixes.
1987** Writes errors found to the log.
1988** Returns zero if no errors found.
1989*/
1990static int mwTestBuf( mwData* mw, const char* file, int line ) {
1991 int retv = 0;
1992 char *p;
1993
1994 if( file == NULL ) file = "unknown";
1995
1996 if( !mwIsSafeAddr( mw, mwDataSize + mwOverflowZoneSize ) ) {
1997 mwWrite( "internal: <%ld> %s(%d): pointer MW-%p is invalid\n",
1998 mwCounter, file, line, mw );
1999 mwIncErr();
2000 return 2;
2001 }
2002
2003 if( mw->check != CHKVAL(mw) ) {
2004 mwWrite( "internal: <%ld> %s(%d), info trashed; relinking\n",
2005 mwCounter, file, line );
2006 mwIncErr();
2007 if( !mwRelink( mw, file, line ) ) return 2;
2008 }
2009
2010 if( mw->prev && mw->prev->next != mw ) {
2011 mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link1 broken\n",
2012 mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
2013 mwIncErr();
2014 if( !mwRelink( mw, file, line ) ) retv = 2;
2015 }
2016 if( mw->next && mw->next->prev != mw ) {
2017 mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link2 broken\n",
2018 mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
2019 mwIncErr();
2020 if( !mwRelink( mw, file, line ) ) retv = 2;
2021 }
2022
2023 p = ((char*)mw) + mwDataSize;
2024 if( mwCheckOF( p ) ) {
2025 mwWrite( "underflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
2026 mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
2027 mwIncErr();
2028 retv = 1;
2029 }
2030 p += mwOverflowZoneSize + mw->size;
2031 if( mwIsReadAddr( p, mwOverflowZoneSize ) && mwCheckOF( p ) ) {
2032 mwWrite( "overflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n",
2033 mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line );
2034 mwIncErr();
2035 retv = 1;
2036 }
2037
2038 return retv;
2039 }
2040
2041static void mwDefaultOutFunc( int c ) {
2042 if( mwLogR() ) fputc( c, mwLogR() );
2043 }
2044
2045static void mwWrite( const char *format, ... ) {
2046 int tot, oflow = 0;
2047 va_list mark;
2048 mwAutoInit();
2049 if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc;
2050 va_start( mark, format );
2051 tot = vsprintf( mwPrintBuf, format, mark );
2052 va_end( mark );
2053 if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; }
2054 for(tot=0;mwPrintBuf[tot];tot++)
2055 (*mwOutFunction)( mwPrintBuf[tot] );
2056 if( oflow ) {
2057 mwWrite( "\ninternal: mwWrite(): WARNING! OUTPUT EXCEEDED %u CHARS: SYSTEM UNSTABLE\n", MW_TRACE_BUFFER-1 );
2058 FLUSH();
2059 }
2060 return;
2061 }
2062
2063static void mwLogFile( const char *name ) {
2064 time_t tid;
2065 (void) time( &tid );
2066 if( mwLogR() != NULL ) {
2067 fclose( mwLogR() );
2068 mwLogW( NULL );
2069 }
2070 if( name == NULL ) return;
2071 mwLogW( fopen( name, "a" COMMIT ) );
2072 if( mwLogR() == NULL )
2073 mwWrite( "logfile: failed to open/create file '%s'\n", name );
2074 }
2075
2076/*
2077** Try to free NML memory until a contiguous allocation of
2078** 'needed' bytes can be satisfied. If this is not enough
2079** and the 'urgent' parameter is nonzero, grabbed memory is
2080** also freed.
2081*/
2082static size_t mwFreeUp( size_t needed, int urgent ) {
2083 void *p;
2084 mwData *mw, *mw2;
2085 char *data;
2086
2087 /* free grabbed NML memory */
2088 for(;;) {
2089 if( mwDrop_( 1, MW_VAL_NML, 1 ) == 0 ) break;
2090 p = malloc( needed );
2091 if( p == NULL ) continue;
2092 free( p );
2093 return needed;
2094 }
2095
2096 /* free normal NML memory */
2097 mw = mwHead;
2098 while( mw != NULL ) {
2099 if( !(mw->flag & MW_NML) ) mw = mw->next;
2100 else {
2101 data = ((char*)mw)+mwDataSize+mwOverflowZoneSize;
2102 if( mwTestMem( data, mw->size, MW_VAL_NML ) ) {
2103 mwIncErr();
2104 mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
2105 mw->count, data + mwOverflowZoneSize, mw->file, mw->line );
2106 }
2107 mw2 = mw->next;
2108 mwUnlink( mw, "mwFreeUp", 0 );
2109 free( mw );
2110 mw = mw2;
2111 p = malloc( needed );
2112 if( p == NULL ) continue;
2113 free( p );
2114 return needed;
2115 }
2116 }
2117
2118 /* if not urgent (for internal purposes), fail */
2119 if( !urgent ) return 0;
2120
2121 /* free grabbed memory */
2122 for(;;) {
2123 if( mwDrop_( 1, MW_VAL_GRB, 1 ) == 0 ) break;
2124 p = malloc( needed );
2125 if( p == NULL ) continue;
2126 free( p );
2127 return needed;
2128 }
2129
2130 return 0;
2131 }
2132
2133static const void * mwTestMem( const void *p, unsigned len, int c ) {
2134 const unsigned char *ptr;
2135 ptr = (const unsigned char *) p;
2136 while( len-- ) {
2137 if( *ptr != (unsigned char)c ) return (const void*)ptr;
2138 ptr ++;
2139 }
2140 return NULL;
2141 }
2142
2143static int mwStrCmpI( const char *s1, const char *s2 ) {
2144 if( s1 == NULL || s2 == NULL ) return 0;
2145 while( *s1 ) {
2146 if( toupper(*s2) == toupper(*s1) ) { s1++; s2++; continue; }
2147 return 1;
2148 }
2149 return 0;
2150 }
2151
2152#define AIPH() if( always_invoked ) { mwWrite("autocheck: <%ld> %s(%d) ", mwCounter, file, line ); always_invoked = 0; }
2153
2154static int mwTestNow( const char *file, int line, int always_invoked ) {
2155 int retv = 0;
2156 mwData *mw;
2157 char *data;
2158
2159 if( file && !always_invoked )
2160 mwWrite("check: <%ld> %s(%d), checking %s%s%s\n",
2161 mwCounter, file, line,
2162 (mwTestFlags & MW_TEST_CHAIN) ? "chain ": "",
2163 (mwTestFlags & MW_TEST_ALLOC) ? "alloc ": "",
2164 (mwTestFlags & MW_TEST_NML) ? "nomansland ": ""
2165 );
2166
2167 if( mwTestFlags & MW_TEST_CHAIN ) {
2168 for( mw = mwHead; mw; mw=mw->next ) {
2169 if( !mwIsSafeAddr(mw, mwDataSize) ) {
2170 AIPH();
2171 mwWrite("check: heap corruption detected\n");
2172 mwIncErr();
2173 return retv + 1;
2174 }
2175 if( mw->prev ) {
2176 if( !mwIsSafeAddr(mw->prev, mwDataSize) ) {
2177 AIPH();
2178 mwWrite("check: heap corruption detected\n");
2179 mwIncErr();
2180 return retv + 1;
2181 }
2182 if( mw==mwHead || mw->prev->next != mw ) {
2183 AIPH();
2184 mwWrite("check: heap chain broken, prev link incorrect\n");
2185 mwIncErr();
2186 retv ++;
2187 }
2188 }
2189 if( mw->next ) {
2190 if( !mwIsSafeAddr(mw->next, mwDataSize) ) {
2191 AIPH();
2192 mwWrite("check: heap corruption detected\n");
2193 mwIncErr();
2194 return retv + 1;
2195 }
2196 if( mw==mwTail || mw->next->prev != mw ) {
2197 AIPH();
2198 mwWrite("check: heap chain broken, next link incorrect\n");
2199 mwIncErr();
2200 retv ++;
2201 }
2202 }
2203 else if( mw!=mwTail ) {
2204 AIPH();
2205 mwWrite("check: heap chain broken, tail incorrect\n");
2206 mwIncErr();
2207 retv ++;
2208 }
2209 }
2210 }
2211 if( mwTestFlags & MW_TEST_ALLOC ) {
2212 for( mw = mwHead; mw; mw=mw->next ) {
2213 if( mwTestBuf( mw, file, line ) ) retv ++;
2214 }
2215 }
2216 if( mwTestFlags & MW_TEST_NML ) {
2217 for( mw = mwHead; mw; mw=mw->next ) {
2218 if( (mw->flag & MW_NML) ) {
2219 data = ((char*)mw)+mwDataSize+mwOverflowZoneSize;
2220 if( mwTestMem( data, mw->size, MW_VAL_NML ) ) {
2221 mwIncErr();
2222 mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n",
2223 mw->count, data + mwOverflowZoneSize, mw->file, mw->line );
2224 }
2225 }
2226 }
2227 }
2228
2229
2230 if( file && !always_invoked && !retv )
2231 mwWrite("check: <%ld> %s(%d), complete; no errors\n",
2232 mwCounter, file, line );
2233 return retv;
2234 }
2235
2236/**********************************************************************
2237** Statistics
2238**********************************************************************/
2239
2240static void mwStatReport()
2241{
2242 mwStat* ms, *ms2;
2243 const char *modname;
2244 int modnamelen;
2245
2246 /* global statistics report */
2247 mwWrite( "\nMemory usage statistics (global):\n" );
2248 mwWrite( " N)umber of allocations made: %ld\n", mwStatNumAlloc );
2249 mwWrite( " L)argest memory usage : %ld\n", mwStatMaxAlloc );
2250 mwWrite( " T)otal of all alloc() calls: %ld\n", mwStatTotAlloc );
2251 mwWrite( " U)nfreed bytes totals : %ld\n", mwStatCurAlloc );
2252 FLUSH();
2253
2254 if( mwStatLevel < 1 ) return;
2255
2256 /* on a per-module basis */
2257 mwWrite( "\nMemory usage statistics (detailed):\n");
2258 mwWrite( " Module/Line Number Largest Total Unfreed \n");
2259 for( ms=mwStatList; ms; ms=ms->next )
2260 {
2261 if( ms->line == -1 )
2262 {
2263 if( ms->file == NULL || !mwIsReadAddr(ms->file,22) ) modname = "<unknown>";
2264 else modname = ms->file;
2265 modnamelen = strlen(modname);
2266 if( modnamelen > 42 )
2267 {
2268 modname = modname + modnamelen - 42;
2269 }
2270
2271 mwWrite(" %-42s %-8ld %-8ld %-8ld %-8ld\n",
2272 modname, ms->num, ms->max, ms->total, ms->curr );
2273 if( ms->file && mwStatLevel > 1 )
2274 {
2275 for( ms2=mwStatList; ms2; ms2=ms2->next )
2276 {
2277 if( ms2->line!=-1 && ms2->file!=NULL && !mwStrCmpI( ms2->file, ms->file ) )
2278 {
2279 mwWrite( " %-8d %-8ld %-8ld %-8ld %-8ld\n",
2280 ms2->line, ms2->num, ms2->max, ms2->total, ms2->curr );
2281 }
2282 }
2283 }
2284 }
2285 }
2286}
2287
2288static mwStat* mwStatGet( const char *file, int line, int makenew ) {
2289 mwStat* ms;
2290
2291 if( mwStatLevel < 2 ) line = -1;
2292
2293 for( ms=mwStatList; ms!=NULL; ms=ms->next ) {
2294 if( line != ms->line ) continue;
2295 if( file==NULL ) {
2296 if( ms->file == NULL ) break;
2297 continue;
2298 }
2299 if( ms->file == NULL ) continue;
2300 if( !strcmp( ms->file, file ) ) break;
2301 }
2302
2303 if( ms != NULL ) return ms;
2304
2305 if( !makenew ) return NULL;
2306
2307 ms = (mwStat*) malloc( sizeof(mwStat) );
2308 if( ms == NULL ) {
2309 if( mwFreeUp( sizeof(mwStat), 0 ) < sizeof(mwStat) ||
2310 (ms=(mwStat*)malloc(sizeof(mwStat))) == NULL ) {
2311 mwWrite("internal: memory low, statistics incomplete for '%s'\n", file );
2312 return NULL;
2313 }
2314 }
2315 ms->file = file;
2316 ms->line = line;
2317 ms->total = 0L;
2318 ms->max = 0L;
2319 ms->num = 0L;
2320 ms->curr = 0L;
2321 ms->next = mwStatList;
2322 mwStatList = ms;
2323 return ms;
2324 }
2325
2326static void mwStatAlloc( size_t size, const char* file, int line ) {
2327 mwStat* ms;
2328
2329 /* update the module statistics */
2330 ms = mwStatGet( file, -1, 1 );
2331 if( ms != NULL ) {
2332 ms->total += (long) size;
2333 ms->curr += (long) size;
2334 ms->num ++;
2335 if( ms->curr > ms->max ) ms->max = ms->curr;
2336 }
2337
2338 /* update the line statistics */
2339 if( mwStatLevel > 1 && line != -1 && file ) {
2340 ms = mwStatGet( file, line, 1 );
2341 if( ms != NULL ) {
2342 ms->total += (long) size;
2343 ms->curr += (long) size;
2344 ms->num ++;
2345 if( ms->curr > ms->max ) ms->max = ms->curr;
2346 }
2347 }
2348
2349 }
2350
2351static void mwStatFree( size_t size, const char* file, int line ) {
2352 mwStat* ms;
2353
2354 /* update the module statistics */
2355 ms = mwStatGet( file, -1, 1 );
2356 if( ms != NULL ) ms->curr -= (long) size;
2357
2358 /* update the line statistics */
2359 if( mwStatLevel > 1 && line != -1 && file ) {
2360 ms = mwStatGet( file, line, 1 );
2361 if( ms != NULL ) ms->curr -= (long) size;
2362 }
2363 }
2364
2365/***********************************************************************
2366** Safe memory checkers
2367**
2368** Using ifdefs, implement the operating-system specific mechanism
2369** of identifying a piece of memory as legal to access with read
2370** and write priviliges. Default: return nonzero for non-NULL pointers.
2371***********************************************************************/
2372
2373static char mwDummy( char c )
2374{
2375 return c;
2376}
2377
2378#ifndef MW_SAFEADDR
2379#ifdef WIN32
2380#define MW_SAFEADDR
2381#define WIN32_LEAN_AND_MEAN
2382#include <windows.h>
2383int mwIsReadAddr( const void *p, unsigned len )
2384{
2385 if( p == NULL ) return 0;
2386 if( IsBadReadPtr(p,len) ) return 0;
2387 return 1;
2388}
2389int mwIsSafeAddr( void *p, unsigned len )
2390{
2391 /* NOTE: For some reason, under Win95 the IsBad... */
2392 /* can return false for invalid pointers. */
2393 if( p == NULL ) return 0;
2394 if( IsBadReadPtr(p,len) || IsBadWritePtr(p,len) ) return 0;
2395 return 1;
2396}
2397#endif /* WIN32 */
2398#endif /* MW_SAFEADDR */
2399
2400#ifndef MW_SAFEADDR
2401#ifdef SIGSEGV
2402#define MW_SAFEADDR
2403
2404typedef void (*mwSignalHandlerPtr)( int );
2405mwSignalHandlerPtr mwOldSIGSEGV = (mwSignalHandlerPtr) 0;
2406jmp_buf mwSIGSEGVjump;
2407static void mwSIGSEGV( int n );
2408
2409static void mwSIGSEGV( int n )
2410{
2411 n = n;
2412 longjmp( mwSIGSEGVjump, 1 );
2413}
2414
2415int mwIsReadAddr( const void *p, unsigned len )
2416{
2417 const char *ptr;
2418
2419 if( p == NULL ) return 0;
2420 if( !len ) return 1;
2421
2422 /* set up to catch the SIGSEGV signal */
2423 mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV );
2424
2425 if( setjmp( mwSIGSEGVjump ) )
2426 {
2427 signal( SIGSEGV, mwOldSIGSEGV );
2428 return 0;
2429 }
2430
2431 /* read all the bytes in the range */
2432 ptr = (const char *)p;
2433 ptr += len;
2434
2435 /* the reason for this rather strange construct is that */
2436 /* we want to keep the number of used parameters and locals */
2437 /* to a minimum. if we use len for a counter gcc will complain */
2438 /* it may get clobbered by longjmp() at high warning levels. */
2439 /* it's a harmless warning, but this way we don't have to see it. */
2440 do
2441 {
2442 ptr --;
2443 if( *ptr == 0x7C ) (void) mwDummy( (char)0 );
2444 } while( (const void*) ptr != p );
2445
2446 /* remove the handler */
2447 signal( SIGSEGV, mwOldSIGSEGV );
2448
2449 return 1;
2450}
2451int mwIsSafeAddr( void *p, unsigned len )
2452{
2453 char *ptr;
2454
2455 if( p == NULL ) return 0;
2456 if( !len ) return 1;
2457
2458 /* set up to catch the SIGSEGV signal */
2459 mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV );
2460
2461 if( setjmp( mwSIGSEGVjump ) )
2462 {
2463 signal( SIGSEGV, mwOldSIGSEGV );
2464 return 0;
2465 }
2466
2467 /* read and write-back all the bytes in the range */
2468 ptr = (char *)p;
2469 ptr += len;
2470
2471 /* the reason for this rather strange construct is that */
2472 /* we want to keep the number of used parameters and locals */
2473 /* to a minimum. if we use len for a counter gcc will complain */
2474 /* it may get clobbered by longjmp() at high warning levels. */
2475 /* it's a harmless warning, but this way we don't have to see it. */
2476 do
2477 {
2478 ptr --;
2479 *ptr = mwDummy( *ptr );
2480 } while( (void*) ptr != p );
2481
2482 /* remove the handler */
2483 signal( SIGSEGV, mwOldSIGSEGV );
2484
2485 return 1;
2486}
2487#endif /* SIGSEGV */
2488#endif /* MW_SAFEADDR */
2489
2490#ifndef MW_SAFEADDR
2491int mwIsReadAddr( const void *p, unsigned len )
2492{
2493 if( p == NULL ) return 0;
2494 if( len == 0 ) return 1;
2495 return 1;
2496}
2497int mwIsSafeAddr( void *p, unsigned len )
2498{
2499 if( p == NULL ) return 0;
2500 if( len == 0 ) return 1;
2501 return 1;
2502}
2503#endif
2504
2505/**********************************************************************
2506** Mutex handling
2507**********************************************************************/
2508
2509#if defined(WIN32) || defined(__WIN32__)
2510
2511static void mwMutexInit( void )
2512{
2513 mwGlobalMutex = CreateMutex( NULL, FALSE, NULL);
2514 return;
2515}
2516
2517static void mwMutexTerm( void )
2518{
2519 CloseHandle( mwGlobalMutex );
2520 return;
2521}
2522
2523static void mwMutexLock( void )
2524{
2525 if( WaitForSingleObject(mwGlobalMutex, 1000 ) == WAIT_TIMEOUT )
2526 {
2527 mwWrite( "mwMutexLock: timed out, possible deadlock\n" );
2528 }
2529 return;
2530}
2531
2532static void mwMutexUnlock( void )
2533{
2534 ReleaseMutex( mwGlobalMutex );
2535 return;
2536}
2537
2538#endif
2539
2540#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H)
2541
2542static void mwMutexInit( void )
2543{
2544 pthread_mutex_init( &mwGlobalMutex, NULL );
2545 return;
2546}
2547
2548static void mwMutexTerm( void )
2549{
2550 pthread_mutex_destroy( &mwGlobalMutex );
2551 return;
2552}
2553
2554static void mwMutexLock( void )
2555{
2556 pthread_mutex_lock(&mwGlobalMutex);
2557 return;
2558}
2559
2560static void mwMutexUnlock( void )
2561{
2562 pthread_mutex_unlock(&mwGlobalMutex);
2563 return;
2564}
2565
2566#endif
2567
2568/**********************************************************************
2569** C++ new & delete
2570**********************************************************************/
2571
2572#if 0 /* 980317: disabled C++ */
2573
2574#ifdef __cplusplus
2575#ifndef MEMWATCH_NOCPP
2576
2577int mwNCur = 0;
2578const char *mwNFile = NULL;
2579int mwNLine = 0;
2580
2581class MemWatch {
2582public:
2583 MemWatch();
2584 ~MemWatch();
2585 };
2586
2587MemWatch::MemWatch() {
2588 if( mwInited ) return;
2589 mwUseAtexit = 0;
2590 mwInit();
2591 }
2592
2593MemWatch::~MemWatch() {
2594 if( mwUseAtexit ) return;
2595 mwTerm();
2596 }
2597
2598/*
2599** This global new will catch all 'new' calls where MEMWATCH is
2600** not active.
2601*/
2602void* operator new( unsigned size ) {
2603 mwNCur = 0;
2604 return mwMalloc( size, "<unknown>", 0 );
2605 }
2606
2607/*
2608** This is the new operator that's called when a module uses mwNew.
2609*/
2610void* operator new( unsigned size, const char *file, int line ) {
2611 mwNCur = 0;
2612 return mwMalloc( size, file, line );
2613 }
2614
2615/*
2616** This is the new operator that's called when a module uses mwNew[].
2617** -- hjc 07/16/02
2618*/
2619void* operator new[] ( unsigned size, const char *file, int line ) {
2620 mwNCur = 0;
2621 return mwMalloc( size, file, line );
2622 }
2623
2624/*
2625** Since this delete operator will recieve ALL delete's
2626** even those from within libraries, we must accept
2627** delete's before we've been initialized. Nor can we
2628** reliably check for wild free's if the mwNCur variable
2629** is not set.
2630*/
2631void operator delete( void *p ) {
2632 if( p == NULL ) return;
2633 if( !mwInited ) {
2634 free( p );
2635 return;
2636 }
2637 if( mwNCur ) {
2638 mwFree( p, mwNFile, mwNLine );
2639 mwNCur = 0;
2640 return;
2641 }
2642 mwFree_( p );
2643 }
2644
2645void operator delete[]( void *p ) {
2646 if( p == NULL ) return;
2647 if( !mwInited ) {
2648 free( p );
2649 return;
2650 }
2651 if( mwNCur ) {
2652 mwFree( p, mwNFile, mwNLine );
2653 mwNCur = 0;
2654 return;
2655 }
2656 mwFree_( p );
2657 }
2658
2659#endif /* MEMWATCH_NOCPP */
2660#endif /* __cplusplus */
2661
2662#endif /* 980317: disabled C++ */
2663
2664/* MEMWATCH.C */
char * file
Definition: DQA_TO_DB.cxx:15
EvtComplex exp(const EvtComplex &c)
Definition: EvtComplex.hh:252
DOUBLE_PRECISION count[3]
void mwFlushNow(void)
void mwNoMansLand(int level)
#define VERSION
#define COMMIT
#define MW_MUTEX_UNLOCK()
#define MW_MUTEX_INIT()
struct mwStat_ mwStat
void mwAbort(void)
#define TESTS(f, l)
void mwAutoCheck(int onoff)
void mwInit(void)
#define mwBUFFER_TO_MW(p)
void mwBreakOut(const char *cause)
int mwIsReadAddr(const void *p, unsigned len)
void mwTerm(void)
#define MW_NML
#define mwROUNDALLOC
#define MW_MUTEX_TERM()
#define CPPTEXT
#define CHKVAL(mw)
#define AIPH()
int mwIsSafeAddr(void *p, unsigned len)
void mwSetAriAction(int action)
#define mwSTDERR
int mwAriHandler(const char *estr)
#define FLUSH()
struct mwData_ mwData
#define MW_MUTEX_LOCK()
#define mwMark(p, t, f, n)
#define mwTest(f, l)
#define mwFree_(p)
#define MW_ARI_RETRY
#define mwGrab(n)
#define MW_TEST_ALLOC
#define MW_TEST_CHAIN
#define MW_ARI_IGNORE
#define mwAssert(e, es, f, l)
#define mwMalloc_(n)
#define mwVerify(e, es, f, l)
#define MW_ARI_ABORT
#define mwFree(p)
#define MW_NML_NONE
#define mwUnmark(p, f, n)
#define MW_NML_DEFAULT
#define MW_TRACE_BUFFER
#define mwMalloc(n, f, l)
#define MW_VAL_GRB
#define mwSetOutFunc(f)
#define mwRealloc(p, n, f, l)
#define mwTestBuffer(f, l, b)
#define MW_ARI_NULLREAD
const unsigned long mwCounter
#define MW_TEST_ALL
#define mwCalloc(n, m, f, l)
#define mwLimit(n)
#define MW_VAL_NEW
#define mwStatistics(f)
#define MW_FREE_LIST
#define MW_VAL_NML
#define mwSetAriFunc(f)
#define MW_STAT_DEFAULT
#define MW_NML_ALL
#define MW_VAL_DEL
#define mwDrop(n)
#define MW_NML_FREE
#define mwRealloc_(p, n)
#define mwCalloc_(n, m)
#define mwStrdup(p, f, l)
#define mwDoFlush(n)
#define MW_TEST_NML
#define mwTrace
sprintf(cut,"kal_costheta0_em>-0.93&&kal_costheta0_em<0.93&&kal_pxy0_em>=0.05+%d*0.1&&kal_pxy0_em<0.15+%d*0.1&&NGch>=2", j, j)
const double b
Definition: slope.cxx:9
long check
mwData * prev
size_t size
const char * file
unsigned flag
long count
mwData * next
char blob[1024 - sizeof(mwGrabData *) - sizeof(int)]
mwGrabData * next
mwMarker * next
void * host
char * text
const char * file
mwStat * next
long total