1 /***************************************************************************
3 TextEditor.mcc - Textediting MUI Custom Class
4 Copyright (C) 1997-2000 Allan Odgaard
5 Copyright (C) 2005-2014 TextEditor.mcc Open Source Team
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 TextEditor class Support Site: http://www.sf.net/projects/texteditor-mcc
21 ***************************************************************************/
25 #include <stdio.h> // vsnprintf
29 #include <proto/exec.h>
30 #include <proto/dos.h>
31 #include <clib/alib_protos.h>
33 #include "SDI_compiler.h"
37 #define DEBUG_USE_MALLOC_REDEFINE 1
41 #if defined(__MORPHOS__)
42 #include <exec/rawfmt.h>
43 #elif defined(__AROS__)
44 #include <proto/arossupport.h>
46 #include <clib/debug_protos.h>
49 // our static variables with default values
50 static int indent_level
= 0;
51 static BOOL ansi_output
= FALSE
;
52 static ULONG debug_flags
= DBF_ALWAYS
| DBF_STARTUP
; // default debug flags
53 static ULONG debug_classes
= DBC_ERROR
| DBC_DEBUG
| DBC_WARNING
| DBC_ASSERT
| DBC_REPORT
| DBC_MTRACK
; // default debug classes
55 static void SetupDbgMalloc(void);
56 static void CleanupDbgMalloc(void);
58 /****************************************************************************/
60 void _DBPRINTF(const char *format
, ...)
64 va_start(args
, format
);
67 #if defined(__MORPHOS__)
68 VNewRawDoFmt(format
, (APTR
)RAWFMTFUNC_SERIAL
, NULL
, args
);
69 #elif defined(__amigaos4__)
70 static char buf
[1024];
71 vsnprintf(buf
, 1024, format
, args
);
72 DebugPrintF("%s", buf
);
73 #elif defined(__AROS__)
74 vkprintf(format
, args
);
76 KPutFmt(format
, args
);
83 /****************************************************************************/
89 _DBPRINTF("** TextEditor.mcp v" LIB_REV_STRING
" startup **********************\n");
90 _DBPRINTF("Exec version: v%ld.%ld\n", ((struct Library
*)SysBase
)->lib_Version
, ((struct Library
*)SysBase
)->lib_Revision
);
91 _DBPRINTF("Initializing runtime debugging:\n");
93 if(GetVar("texteditor.mcp.debug", var
, sizeof(var
), 0) > 0)
97 // static list of our debugging classes tokens.
98 // in the texteditor.mcp.debug variable these classes always start with a @
99 static const struct { const char *token
; unsigned long flag
; } dbclasses
[] =
101 { "ctrace", DBC_CTRACE
},
102 { "report", DBC_REPORT
},
103 { "assert", DBC_ASSERT
},
104 { "timeval", DBC_TIMEVAL
},
105 { "debug", DBC_DEBUG
},
106 { "error", DBC_ERROR
},
107 { "warning", DBC_WARNING
},
108 { "mtrack", DBC_MTRACK
},
113 static const struct { const char *token
; unsigned long flag
; } dbflags
[] =
115 { "always", DBF_ALWAYS
},
116 { "startup", DBF_STARTUP
},
121 // we parse the env variable token-wise
127 if((e
= strpbrk(s
, " ,;")) == NULL
)
130 // check if the token is class definition or
131 // just a flag definition
134 // check if this call is a negation or not
137 // search for the token and clear the flag
138 for(i
=0; dbclasses
[i
].token
; i
++)
140 if(strnicmp(&s
[2], dbclasses
[i
].token
, strlen(dbclasses
[i
].token
)) == 0)
142 _DBPRINTF("clear '%s' debug class flag.\n", dbclasses
[i
].token
);
143 clearFlag(debug_classes
, dbclasses
[i
].flag
);
149 // search for the token and set the flag
150 for(i
=0; dbclasses
[i
].token
; i
++)
152 if(strnicmp(&s
[1], dbclasses
[i
].token
, strlen(dbclasses
[i
].token
)) == 0)
154 _DBPRINTF("set '%s' debug class flag\n", dbclasses
[i
].token
);
155 setFlag(debug_classes
, dbclasses
[i
].flag
);
162 // check if this call is a negation or not
165 for(i
=0; dbflags
[i
].token
; i
++)
167 if(strnicmp(&s
[1], dbflags
[i
].token
, strlen(dbflags
[i
].token
)) == 0)
169 _DBPRINTF("clear '%s' debug flag\n", dbflags
[i
].token
);
170 clearFlag(debug_flags
, dbflags
[i
].flag
);
176 // check if the token was "ansi" and if so enable the ANSI color
178 if(strnicmp(s
, "ansi", 4) == 0)
180 _DBPRINTF("ansi output enabled\n");
185 for(i
=0; dbflags
[i
].token
; i
++)
187 if(strnicmp(s
, dbflags
[i
].token
, strlen(dbflags
[i
].token
)) == 0)
189 _DBPRINTF("set '%s' debug flag\n", dbflags
[i
].token
);
190 setFlag(debug_flags
, dbflags
[i
].flag
);
197 // set the next start to our last search
205 _DBPRINTF("set debug classes/flags (env:texteditor.mcp.debug): %08lx/%08lx\n", debug_classes
, debug_flags
);
206 _DBPRINTF("** Normal processing follows ***************************************\n");
211 /****************************************************************************/
213 void CleanupDebug(void)
217 _DBPRINTF("** Cleaned up debugging ********************************************\n");
220 /****************************************************************************/
222 // define variables for using ANSI colors in our debugging scheme
223 #define ANSI_ESC_CLR "\033[0m"
224 #define ANSI_ESC_BOLD "\033[1m"
225 #define ANSI_ESC_UNDERLINE "\033[4m"
226 #define ANSI_ESC_BLINK "\033[5m"
227 #define ANSI_ESC_REVERSE "\033[7m"
228 #define ANSI_ESC_INVISIBLE "\033[8m"
229 #define ANSI_ESC_FG_BLACK "\033[0;30m"
230 #define ANSI_ESC_FG_RED "\033[0;31m"
231 #define ANSI_ESC_FG_GREEN "\033[0;32m"
232 #define ANSI_ESC_FG_BROWN "\033[0;33m"
233 #define ANSI_ESC_FG_BLUE "\033[0;34m"
234 #define ANSI_ESC_FG_PURPLE "\033[0;35m"
235 #define ANSI_ESC_FG_CYAN "\033[0;36m"
236 #define ANSI_ESC_FG_LGRAY "\033[0;37m"
237 #define ANSI_ESC_FG_DGRAY "\033[1;30m"
238 #define ANSI_ESC_FG_LRED "\033[1;31m"
239 #define ANSI_ESC_FG_LGREEN "\033[1;32m"
240 #define ANSI_ESC_FG_YELLOW "\033[1;33m"
241 #define ANSI_ESC_FG_LBLUE "\033[1;34m"
242 #define ANSI_ESC_FG_LPURPLE "\033[1;35m"
243 #define ANSI_ESC_FG_LCYAN "\033[1;36m"
244 #define ANSI_ESC_FG_WHITE "\033[1;37m"
245 #define ANSI_ESC_BG "\033[0;4" // background esc-squ start with 4x
246 #define ANSI_ESC_BG_BLACK "\033[0;40m"
247 #define ANSI_ESC_BG_RED "\033[0;41m"
248 #define ANSI_ESC_BG_GREEN "\033[0;42m"
249 #define ANSI_ESC_BG_BROWN "\033[0;43m"
250 #define ANSI_ESC_BG_BLUE "\033[0;44m"
251 #define ANSI_ESC_BG_PURPLE "\033[0;45m"
252 #define ANSI_ESC_BG_CYAN "\033[0;46m"
253 #define ANSI_ESC_BG_LGRAY "\033[0;47m"
255 /****************************************************************************/
257 INLINE
void _INDENT(void)
260 for(i
=0; i
< indent_level
; i
++)
264 /****************************************************************************/
266 void _ENTER(unsigned long dclass
, const char *file
, int line
, const char *function
)
268 if(isFlagSet(debug_classes
, dclass
))
272 _DBPRINTF("%s%s:%ld:Entering %s%s\n", ANSI_ESC_FG_BROWN
, file
, line
, function
, ANSI_ESC_CLR
);
274 _DBPRINTF("%s:%ld:Entering %s\n", file
, line
, function
);
280 void _LEAVE(unsigned long dclass
, const char *file
, int line
, const char *function
)
284 if(isFlagSet(debug_classes
, dclass
))
288 _DBPRINTF("%s%s:%ld:Leaving %s%s\n", ANSI_ESC_FG_BROWN
, file
, line
, function
, ANSI_ESC_CLR
);
290 _DBPRINTF("%s:%ld:Leaving %s\n", file
, line
, function
);
294 void _RETURN(unsigned long dclass
, const char *file
, int line
, const char *function
, unsigned long result
)
298 if(isFlagSet(debug_classes
, dclass
))
302 _DBPRINTF("%s%s:%ld:Leaving %s (result 0x%08lx, %ld)%s\n", ANSI_ESC_FG_BROWN
, file
, line
, function
, result
, result
, ANSI_ESC_CLR
);
304 _DBPRINTF("%s:%ld:Leaving %s (result 0x%08lx, %ld)\n", file
, line
, function
, result
, result
);
308 /****************************************************************************/
310 void _SHOWVALUE(unsigned long dclass
, unsigned long dflags
, unsigned long value
, int size
, const char *name
, const char *file
, int line
)
312 if(isFlagSet(debug_classes
, dclass
) &&
313 isFlagSet(debug_flags
, dflags
))
320 fmt
= "%s:%ld:%s = %ld, 0x%02lx";
324 fmt
= "%s:%ld:%s = %ld, 0x%04lx";
328 fmt
= "%s:%ld:%s = %ld, 0x%08lx";
335 _DBPRINTF(ANSI_ESC_FG_GREEN
);
337 _DBPRINTF(fmt
, file
, line
, name
, value
, value
);
339 if(size
== 1 && value
< 256)
341 if(value
< ' ' || (value
>= 127 && value
< 160))
342 _DBPRINTF(", '\\x%02lx'", value
);
344 _DBPRINTF(", '%lc'", value
);
348 _DBPRINTF("%s\n", ANSI_ESC_CLR
);
354 /****************************************************************************/
356 void _SHOWPOINTER(unsigned long dclass
, unsigned long dflags
, const void *p
, const char *name
, const char *file
, int line
)
358 if(isFlagSet(debug_classes
, dclass
) &&
359 isFlagSet(debug_flags
, dflags
))
366 fmt
= "%s:%ld:%s = 0x%08lx\n";
368 fmt
= "%s:%ld:%s = NULL\n";
372 _DBPRINTF(ANSI_ESC_FG_GREEN
);
373 _DBPRINTF(fmt
, file
, line
, name
, p
);
374 _DBPRINTF(ANSI_ESC_CLR
);
377 _DBPRINTF(fmt
, file
, line
, name
, p
);
381 /****************************************************************************/
383 void _SHOWSTRING(unsigned long dclass
, unsigned long dflags
, const char *string
, const char *name
, const char *file
, int line
)
385 if(isFlagSet(debug_classes
, dclass
) &&
386 isFlagSet(debug_flags
, dflags
))
391 _DBPRINTF("%s%s:%ld:%s = 0x%08lx \"%s\"%s\n", ANSI_ESC_FG_GREEN
, file
, line
, name
, string
, string
, ANSI_ESC_CLR
);
393 _DBPRINTF("%s:%ld:%s = 0x%08lx \"%s\"\n", file
, line
, name
, string
, string
);
397 /****************************************************************************/
399 void _SHOWMSG(unsigned long dclass
, unsigned long dflags
, const char *msg
, const char *file
, int line
)
401 if(isFlagSet(debug_classes
, dclass
) &&
402 isFlagSet(debug_flags
, dflags
))
407 _DBPRINTF("%s%s:%ld:%s%s\n", ANSI_ESC_FG_GREEN
, file
, line
, msg
, ANSI_ESC_CLR
);
409 _DBPRINTF("%s:%ld:%s\n", file
, line
, msg
);
413 /****************************************************************************/
415 void _DPRINTF(unsigned long dclass
, unsigned long dflags
, const char *file
, unsigned long line
, const char *format
, ...)
417 if((isFlagSet(debug_classes
, dclass
) && isFlagSet(debug_flags
, dflags
)) ||
418 (isFlagSet(dclass
, DBC_ERROR
) || isFlagSet(dclass
, DBC_WARNING
)))
422 va_start(args
, format
);
423 _VDPRINTF(dclass
, dflags
, file
, line
, format
, args
);
428 /****************************************************************************/
430 void _VDPRINTF(unsigned long dclass
, unsigned long dflags
, const char *file
, unsigned long line
, const char *format
, va_list args
)
432 if((isFlagSet(debug_classes
, dclass
) && isFlagSet(debug_flags
, dflags
)) ||
433 (isFlagSet(dclass
, DBC_ERROR
) || isFlagSet(dclass
, DBC_WARNING
)))
435 static char buf
[1024];
439 vsnprintf(buf
, 1024, format
, args
);
443 const char *highlight
= ANSI_ESC_FG_GREEN
;
447 case DBC_CTRACE
: highlight
= ANSI_ESC_FG_BROWN
; break;
448 case DBC_REPORT
: highlight
= ANSI_ESC_FG_GREEN
; break;
449 case DBC_ASSERT
: highlight
= ANSI_ESC_FG_RED
; break;
450 case DBC_TIMEVAL
: highlight
= ANSI_ESC_FG_GREEN
; break;
451 case DBC_DEBUG
: highlight
= ANSI_ESC_FG_GREEN
; break;
452 case DBC_ERROR
: highlight
= ANSI_ESC_FG_RED
; break;
453 case DBC_WARNING
: highlight
= ANSI_ESC_FG_PURPLE
;break;
456 _DBPRINTF("%s%s:%ld:%s%s\n", highlight
, file
, line
, buf
, ANSI_ESC_CLR
);
459 _DBPRINTF("%s:%ld:%s\n", file
, line
, buf
);
463 /****************************************************************************/
475 static struct MinList DbgMallocList
[256];
476 static struct SignalSemaphore DbgMallocListSema
;
477 static ULONG DbgMallocCount
;
478 static ULONG DbgUnsuitableFreeCount
;
481 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
487 // a very simple hashing function to spread the allocations across the lists
488 // Since AmigaOS memory allocation has a granularity of at least 8 bytes we simple ignore the
489 // lower 4 bits (=16 Bytes) and take the next 8 bits as hash value. Not very sophisticated, but
490 // it does the job quite good.
491 #define ptr2hash(p) ((((ULONG)(p)) >> 4) & 0xff)
493 /// findDbgMallocNode
494 // find a given pointer in the tracking lists
495 static struct DbgMallocNode
*findDbgMallocNode(const void *ptr
)
497 struct DbgMallocNode
*result
= NULL
;
498 struct Node
*curNode
;
500 for(curNode
= GetHead((struct List
*)&DbgMallocList
[ptr2hash(ptr
)]); curNode
!= NULL
; curNode
= GetSucc(curNode
))
502 struct DbgMallocNode
*dmn
= (struct DbgMallocNode
*)curNode
;
504 if(dmn
->memory
== ptr
)
516 // check wether the used allocation function matches a set of given function names
517 // separated by '|', i.e. "malloc|calloc|strdup"
518 BOOL
matchAllocFunc(const char *allocFunc
, const char *freeFunc
)
521 const char *p
= freeFunc
;
524 while((q
= strchr(p
, '|')) != NULL
)
528 // we have to handle more than one possible function name
529 strlcpy(tmp
, p
, ((size_t)(q
-p
)+1 < sizeof(tmp
)) ? (size_t)(q
-p
)+1 : sizeof(tmp
));
530 if(strcmp(allocFunc
, tmp
) == 0)
540 // compare the last or only function name
541 match
= (strcmp(allocFunc
, p
) == 0);
549 // add a new node to the memory tracking lists
550 void _MEMTRACK(const char *file
, const int line
, const char *func
, void *ptr
, size_t size
)
552 if(isFlagSet(debug_classes
, DBC_MTRACK
))
554 if(ptr
!= NULL
&& size
!= 0)
556 struct DbgMallocNode
*dmn
;
558 if((dmn
= AllocVec(sizeof(*dmn
), MEMF_ANY
)) != NULL
)
566 ObtainSemaphore(&DbgMallocListSema
);
568 AddTail((struct List
*)&DbgMallocList
[ptr2hash(ptr
)], (struct Node
*)&dmn
->node
);
571 ReleaseSemaphore(&DbgMallocListSema
);
575 _DPRINTF(DBC_WARNING
, DBF_ALWAYS
, file
, line
, "potential invalid %s call with return (0x%08lx, 0x%08lx)", func
, ptr
, size
);
581 // remove a node from the memory tracking lists
582 void _UNMEMTRACK(const char *file
, const int line
, const char *func
, const void *ptr
)
584 if(isFlagSet(debug_classes
, DBC_MTRACK
) && ptr
!= NULL
)
586 BOOL success
= FALSE
;
587 struct DbgMallocNode
*dmn
;
589 ObtainSemaphore(&DbgMallocListSema
);
591 if((dmn
= findDbgMallocNode(ptr
)) != NULL
)
593 Remove((struct Node
*)dmn
);
595 if(matchAllocFunc(dmn
->func
, func
) == FALSE
)
597 _DPRINTF(DBC_WARNING
, DBF_ALWAYS
, file
, line
, "free of tracked memory area 0x%08lx with unsuitable function (allocated with %s, freed with %s counterpart)", ptr
, dmn
->func
, func
);
598 DbgUnsuitableFreeCount
++;
609 _DPRINTF(DBC_WARNING
, DBF_ALWAYS
, file
, line
, "free of untracked memory area 0x%08lx attempted", ptr
);
611 ReleaseSemaphore(&DbgMallocListSema
);
617 // initialize the memory tracking framework
618 static void SetupDbgMalloc(void)
622 if(isFlagSet(debug_classes
, DBC_MTRACK
))
626 for(i
= 0; i
< ARRAY_SIZE(DbgMallocList
); i
++)
627 NewList((struct List
*)&DbgMallocList
[i
]);
630 DbgUnsuitableFreeCount
= 0;
632 memset(&DbgMallocListSema
, 0, sizeof(DbgMallocListSema
));
633 InitSemaphore(&DbgMallocListSema
);
641 // cleanup the memory tracking framework and output possibly pending allocations
642 static void CleanupDbgMalloc(void)
646 if(isFlagSet(debug_classes
, DBC_MTRACK
))
648 _DBPRINTF("** Cleaning up memory tracking *************************************\n");
650 ObtainSemaphore(&DbgMallocListSema
);
652 if(DbgMallocCount
!= 0 || DbgUnsuitableFreeCount
!= 0)
654 if(DbgMallocCount
!= 0)
658 E(DBF_ALWAYS
, "there are still %ld unfreed memory trackings", DbgMallocCount
);
659 for(i
= 0; i
< ARRAY_SIZE(DbgMallocList
); i
++)
661 struct DbgMallocNode
*dmn
;
663 while((dmn
= (struct DbgMallocNode
*)RemHead((struct List
*)&DbgMallocList
[i
])) != NULL
)
665 _DPRINTF(DBC_ERROR
, DBF_ALWAYS
, dmn
->file
, dmn
->line
, "unfreed memory tracking: 0x%08lx, size/type %ld, func (%s)", dmn
->memory
, dmn
->size
, dmn
->func
);
667 // We only free the node structure here but not dmn->memory itself.
668 // First of all, this is because the allocation could have been done
669 // by other functions than malloc() and calling free() for these will
670 // cause havoc. And second the c-library's startup code will/should
671 // free all further pending allocations upon program termination.
676 if(DbgUnsuitableFreeCount
!= 0)
678 E(DBF_ALWAYS
, "there were %ld unsuitable freeing calls", DbgUnsuitableFreeCount
);
682 D(DBF_ALWAYS
, "all memory trackings have been free()'d correctly");
684 ReleaseSemaphore(&DbgMallocListSema
);
692 // output all current allocations
693 void DumpDbgMalloc(void)
697 if(isFlagSet(debug_classes
, DBC_MTRACK
))
701 ObtainSemaphore(&DbgMallocListSema
);
703 D(DBF_ALWAYS
, "%ld memory areas tracked", DbgMallocCount
);
704 for(i
= 0; i
< ARRAY_SIZE(DbgMallocList
); i
++)
706 struct Node
*curNode
;
708 for(curNode
= GetHead((struct List
*)&DbgMallocList
[i
]); curNode
!= NULL
; curNode
= GetSucc(curNode
))
710 struct DbgMallocNode
*dmn
= (struct DbgMallocNode
*)curNode
;
712 _DPRINTF(DBC_MTRACK
, DBF_ALWAYS
, dmn
->file
, dmn
->line
, "memarea 0x%08lx, size/type %ld, func (%s)", dmn
->memory
, dmn
->size
, dmn
->func
);
716 ReleaseSemaphore(&DbgMallocListSema
);