/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * |_o_o|\\ Copyright (c) 1989 The Software Distillery. * * |. o.| || All Rights Reserved * * | . | || Written by Doug Walker * * | o | || The Software Distillery * * | . |// 235 Trillingham Lane * * ====== Cary, NC 27513 * * BBS:(919)-471-6436 * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* This file contains the routines that must be linked in to every pgm */ /* that wants to use MemWatchIII - MWInit, MWTerm, MWCheck, MWSend, */ /* MWHold, MWPurge, MWSend */ #include "mempriv.h" struct MWGlobal mwg = {0L}; /* Make sure the 'flags' field is 0's */ /* Initialization routine - should be called once before any memory */ /* is allocated */ void MWInit(dbfh, flags) BPTR dbfh; LONG flags; { if(mwg.flags & MWF_ACTIVE) { if(!dbfh && mwg.dbfh) dbfh = mwg.dbfh; if(dbfh) { MSG(dbfh, "MemWatch ERROR: MWInit called twice\n"); } return; } memset((char *)&mwg, 0, sizeof(struct MWGlobal)); mwg.flags = flags|MWF_ACTIVE; if(!(flags & MWF_NOLOG)) mwg.dbfh = (dbfh ? dbfh : Output()); MWSend(MWM_INIT); mwg.lim[MWT_CHIP] = mwg.lim[MWT_FAST] = 0x7fffffff; mwg.task = FindTask(0L); } /* Termination routine - should be called after all memory usage is done */ void MWTerm() { int msgdone = 0; MWCheck(); MWSend(MWM_TERM); /* no need to trash the mem we may be about to free for good */ mwg.flags |= MWF_NOFTRASH; if(!(mwg.flags & MWF_NOFREE)) { while(mwg.first) { if(mwg.dbfh) { if(!msgdone) { MSG(mwg.dbfh, "MemWatch ERROR: The following allocations were not freed:\n"); msgdone = 1; } MWPrint(mwg.first, 0, 0, 0); } MWFreeMem(mwg.first->memory, mwg.first->size, 0); } } MWPurge(); /* Really free all mem on the 'free' chain */ memset((char *)&mwg, 0, sizeof(struct MWGlobal)); } /* Validity check routine - checks all known allocations for overwrites */ /* Called from every alloc and free routine, plus when specifically */ /* invoked */ void MWCheck() { struct MWAlc *mwa; char *tmpchar; int error, header, trailer; if(mwg.flags & MWF_ERROR) { /* Error already found by external process */ if(mwg.dbfh) MSG(mwg.dbfh, "MemWatch ERROR: External process found memory error\n"); MWHold(); return; } error = 0; for(mwa=mwg.first; mwa; mwa=mwa->next) { if( (mwa->myflags & MWF_REPMASK) == MWF_REPORTED) continue; if(header=memcmp((char *)&mwa->header, MWHEADER, 4)) { if(mwg.dbfh) MSG(mwg.dbfh, "MemWatch ERROR: Header trashed\n"); } if(trailer=memcmp(mwa->memory+mwa->size, MWTRAILER, 4)) { if(mwg.dbfh) MSG(mwg.dbfh, "MemWatch ERROR: Trailer trashed\n"); } if(header || trailer) { error = 1; mwa->myflags |= MWF_REPORTED; MWPrint(mwa, 0, 0, 0); } } for(mwa=mwg.freed; mwa; mwa=mwa->next) { if( (mwa->myflags & MWF_REPMASK) == MWF_REPORTED) continue; for(header=0, tmpchar=mwa->memory; headersize; header++, tmpchar++) { if(*tmpchar != MWFTRASH) { mwa->myflags |= MWF_REPORTED; if(mwg.dbfh) { error = 1; MSG(mwg.dbfh, "MemWatch ERROR: Freed memory modified\n"); MWPrint(mwa, 0, 0, 0); break; } } } } if(error) MWHold(); } void MWHold() { struct MWAlc *mwa; if(mwg.flags & MWF_EXT) { if(mwg.dbfh) MSG(mwg.dbfh, "***Process Held By MemWatch***\n"); MWSend(MWM_KILLME); /* Should never return... */ } mwg.flags &= ~MWF_ERROR; /* But if it does, clear the error flag */ /* If we're attempting to go on, make all the sentinals correct */ for(mwa=mwg.first; mwa; mwa=mwa->next) { mwa->myflags = ~MWF_REPMASK; memcpy((char *)&mwa->header, MWHEADER, 4); memcpy((mwa->memory + mwa->size), MWTRAILER, 4); } for(mwa=mwg.freed; mwa; mwa=mwa->next) { mwa->myflags = ~MWF_REPMASK; memset(mwa->memory, MWFTRASH, mwa->size); } } /* MWPurge really frees all memory placed on the 'freed' chain by */ /* FreeMem() or free() */ void MWPurge() { struct MWAlc *cur, *next; for(cur=mwg.freed; cur; cur=next) { next = cur->next; FreeMem(cur, cur->size + sizeof(struct MWAlc)); } mwg.freed = NULL; } /* MWSend communicates with the external MemWatch process */ int MWSend(type) int type; { struct MsgPort *extport; struct MWMsg mwmsg; if(!(mwg.flags & MWF_ACTIVE) || !(mwg.flags & MWF_EXT)) return(1); if(!(extport = FindPort(MWPORTNAME)) || !(mwmsg.msgpart.mn_ReplyPort = CreatePort(NULL, 0))) goto exterror; mwmsg.msgpart.mn_Length = sizeof(struct MWMsg); mwmsg.type = type|MWM_REPLY; mwmsg.mwg = &mwg; PutMsg(extport, &mwmsg.msgpart); WaitPort(mwmsg.msgpart.mn_ReplyPort); GetMsg(mwmsg.msgpart.mn_ReplyPort); DeletePort(mwmsg.msgpart.mn_ReplyPort); if(mwmsg.type != MWM_OK) { goto exterror; } return(0); exterror: if(mwg.dbfh) MSG(mwg.dbfh, "MemWatch ERROR: Can't communicate with external process\n"); mwg.flags &= ~MWF_EXT; return(1); } #define ALCMSG \ "0x00000000 length 00000000 allocated line 00000 file xxxxxxxxxxxxxxx\n" /*0 1 2 3 4 5 6*/ /*0123456789012345678901234567890123456789012345678901234567890*/ #define ALC_MEM 2 #define ALC_LEN 18 #define ALC_LIN 42 #define ALC_FIL 53 #define FREMSG \ "Invalid FreeMem call, addr 0x00000000 length 00000000\n" /*0 1 2 3 4 5 6*/ /*0123456789012345678901234567890123456789012345678901234567890*/ #define FRE_MEM 29 #define FRE_LEN 45 #define LENMSG \ "FreeMem called with length 00000000 on the following allocation:\n" /*0 1 2 3 4 5 6*/ /*0123456789012345678901234567890123456789012345678901234567890*/ #define LEN_LEN 27 #define USEMSG \ "00000000 bytes in 00000000 allocations\n" /*0 1 2 3 4 5 6*/ /*0123456789012345678901234567890123456789012345678901234567890*/ #define USE_SIZ 0 #define USE_ALC 18 #define MAXMSG \ "Max chip usage = 00000000 bytes; Max fast usage = 00000000 bytes\n\n" /*0 1 2 3 4 5 6*/ /*0123456789012345678901234567890123456789012345678901234567890*/ #define MAX_CHP 17 #define MAX_FST 50 #define DIGITS "0123456789ABCDEF" static void fmtdec(char *, long, LONG); static void fmthex(char *, LONG); static char *msgs[] = { ALCMSG, FREMSG, LENMSG, USEMSG, MAXMSG }; void MWPrint(mwa, msgnum, val1, val2) struct MWAlc *mwa; int msgnum; LONG val1, val2; { char *buffer; int i; if(!(mwg.flags & MWF_ACTIVE) || !mwg.dbfh) return; buffer = msgs[msgnum]; switch(msgnum) { case 0: fmthex(msgs[0]+ALC_MEM, (LONG)mwa->memory); fmtdec(msgs[0]+ALC_LEN, 8, mwa->size); fmtdec(msgs[0]+ALC_LIN, 5, mwa->line); if( (i = strlen(mwa->file)) > 15) i=15; memcpy(msgs[0]+ALC_FIL, mwa->file, i); msgs[0][ALC_FIL+i] = '\n'; msgs[0][ALC_FIL+i+1] = '\0'; break; case 1: fmthex(msgs[1]+FRE_MEM, val1); fmtdec(msgs[1]+FRE_LEN, 8, val2); break; case 2: fmtdec(msgs[2]+LEN_LEN, 8, val1); break; case 3: fmtdec(msgs[3]+USE_SIZ, 8, val1); fmtdec(msgs[3]+USE_ALC, 8, val2); break; case 4: fmtdec(msgs[4]+MAX_CHP, 8, val1); fmtdec(msgs[4]+MAX_FST, 8, val2); break; default: /* What do we do here? Give up, I guess*/ return; } MSG(mwg.dbfh, buffer); } static void fmtdec(buf, len, val) char *buf; long len; LONG val; { int i; for(i=1; i<=len; i++, val/=10) buf[len-i] = (val ? DIGITS[val%10] : ' '); } static void fmthex(buf, val) char *buf; LONG val; { int i, j; union { LONG l; char c[4]; } u; u.l = val; for(i=j=0; i<4; i++) { buf[j++] = DIGITS[(u.c[i]&0xf0)>>4]; buf[j++] = DIGITS[u.c[i]&0x0f]; } }