revert between 56095 -> 55830 in arch
[AROS.git] / workbench / classes / zune / texteditor / mcp / Debug.c
blobe458a8d748810a19df801558c4aff220be6dc71c
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
19 $Id$
21 ***************************************************************************/
23 #ifdef DEBUG
25 #include <stdio.h> // vsnprintf
26 #include <string.h>
27 #include <stdarg.h>
29 #include <proto/exec.h>
30 #include <proto/dos.h>
31 #include <clib/alib_protos.h>
33 #include "SDI_compiler.h"
35 #include "private.h"
37 #define DEBUG_USE_MALLOC_REDEFINE 1
38 #include "Debug.h"
39 #include "version.h"
41 #if defined(__MORPHOS__)
42 #include <exec/rawfmt.h>
43 #elif defined(__AROS__)
44 #include <proto/arossupport.h>
45 #else
46 #include <clib/debug_protos.h>
47 #endif
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, ...)
62 va_list args;
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);
75 #else
76 KPutFmt(format, args);
77 #endif
80 va_end(args);
83 /****************************************************************************/
85 void SetupDebug(void)
87 char var[256];
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)
95 char *s = var;
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 },
109 { "all", DBC_ALL },
110 { NULL, 0 }
113 static const struct { const char *token; unsigned long flag; } dbflags[] =
115 { "always", DBF_ALWAYS },
116 { "startup", DBF_STARTUP },
117 { "all", DBF_ALL },
118 { NULL, 0 }
121 // we parse the env variable token-wise
122 while(*s)
124 ULONG i;
125 char *e;
127 if((e = strpbrk(s, " ,;")) == NULL)
128 e = s+strlen(s);
130 // check if the token is class definition or
131 // just a flag definition
132 if(s[0] == '@')
134 // check if this call is a negation or not
135 if(s[1] == '!')
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);
147 else
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);
160 else
162 // check if this call is a negation or not
163 if(s[0] == '!')
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);
174 else
176 // check if the token was "ansi" and if so enable the ANSI color
177 // output
178 if(strnicmp(s, "ansi", 4) == 0)
180 _DBPRINTF("ansi output enabled\n");
181 ansi_output = TRUE;
183 else
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
198 if(*e)
199 s = ++e;
200 else
201 break;
205 _DBPRINTF("set debug classes/flags (env:texteditor.mcp.debug): %08lx/%08lx\n", debug_classes, debug_flags);
206 _DBPRINTF("** Normal processing follows ***************************************\n");
208 SetupDbgMalloc();
211 /****************************************************************************/
213 void CleanupDebug(void)
215 CleanupDbgMalloc();
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)
259 int i;
260 for(i=0; i < indent_level; i++)
261 _DBPRINTF(" ");
264 /****************************************************************************/
266 void _ENTER(unsigned long dclass, const char *file, int line, const char *function)
268 if(isFlagSet(debug_classes, dclass))
270 _INDENT();
271 if(ansi_output)
272 _DBPRINTF("%s%s:%ld:Entering %s%s\n", ANSI_ESC_FG_BROWN, file, line, function, ANSI_ESC_CLR);
273 else
274 _DBPRINTF("%s:%ld:Entering %s\n", file, line, function);
277 indent_level++;
280 void _LEAVE(unsigned long dclass, const char *file, int line, const char *function)
282 indent_level--;
284 if(isFlagSet(debug_classes, dclass))
286 _INDENT();
287 if(ansi_output)
288 _DBPRINTF("%s%s:%ld:Leaving %s%s\n", ANSI_ESC_FG_BROWN, file, line, function, ANSI_ESC_CLR);
289 else
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)
296 indent_level--;
298 if(isFlagSet(debug_classes, dclass))
300 _INDENT();
301 if(ansi_output)
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);
303 else
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))
315 const char *fmt;
317 switch(size)
319 case 1:
320 fmt = "%s:%ld:%s = %ld, 0x%02lx";
321 break;
323 case 2:
324 fmt = "%s:%ld:%s = %ld, 0x%04lx";
325 break;
327 default:
328 fmt = "%s:%ld:%s = %ld, 0x%08lx";
329 break;
332 _INDENT();
334 if(ansi_output)
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);
343 else
344 _DBPRINTF(", '%lc'", value);
347 if(ansi_output)
348 _DBPRINTF("%s\n", ANSI_ESC_CLR);
349 else
350 _DBPRINTF("\n");
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))
361 const char *fmt;
363 _INDENT();
365 if(p != NULL)
366 fmt = "%s:%ld:%s = 0x%08lx\n";
367 else
368 fmt = "%s:%ld:%s = NULL\n";
370 if(ansi_output)
372 _DBPRINTF(ANSI_ESC_FG_GREEN);
373 _DBPRINTF(fmt, file, line, name, p);
374 _DBPRINTF(ANSI_ESC_CLR);
376 else
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))
388 _INDENT();
390 if(ansi_output)
391 _DBPRINTF("%s%s:%ld:%s = 0x%08lx \"%s\"%s\n", ANSI_ESC_FG_GREEN, file, line, name, string, string, ANSI_ESC_CLR);
392 else
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))
404 _INDENT();
406 if(ansi_output)
407 _DBPRINTF("%s%s:%ld:%s%s\n", ANSI_ESC_FG_GREEN, file, line, msg, ANSI_ESC_CLR);
408 else
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)))
420 va_list args;
422 va_start(args, format);
423 _VDPRINTF(dclass, dflags, file, line, format, args);
424 va_end(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];
437 _INDENT();
439 vsnprintf(buf, 1024, format, args);
441 if(ansi_output)
443 const char *highlight = ANSI_ESC_FG_GREEN;
445 switch(dclass)
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);
458 else
459 _DBPRINTF("%s:%ld:%s\n", file, line, buf);
463 /****************************************************************************/
465 struct DbgMallocNode
467 struct MinNode node;
468 void *memory;
469 size_t size;
470 const char *file;
471 const char *func;
472 int line;
475 static struct MinList DbgMallocList[256];
476 static struct SignalSemaphore DbgMallocListSema;
477 static ULONG DbgMallocCount;
478 static ULONG DbgUnsuitableFreeCount;
480 #ifndef ARRAY_SIZE
481 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
482 #endif
484 #ifndef NewList
485 #endif
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)
506 result = dmn;
507 break;
511 return result;
515 /// matchAllocFunc
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)
520 BOOL match = FALSE;
521 const char *p = freeFunc;
522 const char *q;
524 while((q = strchr(p, '|')) != NULL)
526 char tmp[16];
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)
532 match = TRUE;
533 break;
535 p = q+1;
538 if(match == FALSE)
540 // compare the last or only function name
541 match = (strcmp(allocFunc, p) == 0);
544 return match;
548 /// _MEMTRACK
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)
560 dmn->memory = ptr;
561 dmn->size = size;
562 dmn->file = file;
563 dmn->line = line;
564 dmn->func = func;
566 ObtainSemaphore(&DbgMallocListSema);
568 AddTail((struct List *)&DbgMallocList[ptr2hash(ptr)], (struct Node *)&dmn->node);
569 DbgMallocCount++;
571 ReleaseSemaphore(&DbgMallocListSema);
574 else
575 _DPRINTF(DBC_WARNING, DBF_ALWAYS, file, line, "potential invalid %s call with return (0x%08lx, 0x%08lx)", func, ptr, size);
580 /// _UNMEMTRACK
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++;
601 FreeVec(dmn);
603 DbgMallocCount--;
605 success = TRUE;
608 if(success == FALSE)
609 _DPRINTF(DBC_WARNING, DBF_ALWAYS, file, line, "free of untracked memory area 0x%08lx attempted", ptr);
611 ReleaseSemaphore(&DbgMallocListSema);
616 /// SetupDbgMalloc
617 // initialize the memory tracking framework
618 static void SetupDbgMalloc(void)
620 ENTER();
622 if(isFlagSet(debug_classes, DBC_MTRACK))
624 ULONG i;
626 for(i = 0; i < ARRAY_SIZE(DbgMallocList); i++)
627 NewList((struct List *)&DbgMallocList[i]);
629 DbgMallocCount = 0;
630 DbgUnsuitableFreeCount = 0;
632 memset(&DbgMallocListSema, 0, sizeof(DbgMallocListSema));
633 InitSemaphore(&DbgMallocListSema);
636 LEAVE();
640 /// CleanupDbgMalloc
641 // cleanup the memory tracking framework and output possibly pending allocations
642 static void CleanupDbgMalloc(void)
644 ENTER();
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)
656 ULONG i;
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.
672 FreeVec(dmn);
676 if(DbgUnsuitableFreeCount != 0)
678 E(DBF_ALWAYS, "there were %ld unsuitable freeing calls", DbgUnsuitableFreeCount);
681 else
682 D(DBF_ALWAYS, "all memory trackings have been free()'d correctly");
684 ReleaseSemaphore(&DbgMallocListSema);
687 LEAVE();
691 /// DumpDbgMalloc
692 // output all current allocations
693 void DumpDbgMalloc(void)
695 ENTER();
697 if(isFlagSet(debug_classes, DBC_MTRACK))
699 ULONG i;
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);
719 LEAVE();
724 #endif /* DEBUG */