revert between 56095 -> 55830 in arch
[AROS.git] / workbench / classes / zune / texteditor / mcc / Debug.c
blob2ea2b47076ee54f10674101231ebab1cb5b15fdb
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.mcc 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.mcc.debug", var, sizeof(var), 0) > 0)
95 char *s = var;
97 // static list of our debugging classes tokens.
98 // in the texteditor.mcc.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 { "input", DBF_INPUT },
118 { "rexx", DBF_REXX },
119 { "clipboard", DBF_CLIPBOARD },
120 { "undo", DBF_UNDO },
121 { "dump", DBF_DUMP },
122 { "style", DBF_STYLE },
123 { "spell", DBF_SPELL },
124 { "block", DBF_BLOCK },
125 { "import", DBF_IMPORT },
126 { "export", DBF_IMPORT },
127 { "all", DBF_ALL },
128 { NULL, 0 }
131 // we parse the env variable token-wise
132 while(*s)
134 ULONG i;
135 char *e;
137 if((e = strpbrk(s, " ,;")) == NULL)
138 e = s+strlen(s);
140 // check if the token is class definition or
141 // just a flag definition
142 if(s[0] == '@')
144 // check if this call is a negation or not
145 if(s[1] == '!')
147 // search for the token and clear the flag
148 for(i=0; dbclasses[i].token; i++)
150 if(strnicmp(&s[2], dbclasses[i].token, strlen(dbclasses[i].token)) == 0)
152 _DBPRINTF("clear '%s' debug class flag.\n", dbclasses[i].token);
153 clearFlag(debug_classes, dbclasses[i].flag);
157 else
159 // search for the token and set the flag
160 for(i=0; dbclasses[i].token; i++)
162 if(strnicmp(&s[1], dbclasses[i].token, strlen(dbclasses[i].token)) == 0)
164 _DBPRINTF("set '%s' debug class flag\n", dbclasses[i].token);
165 setFlag(debug_classes, dbclasses[i].flag);
170 else
172 // check if this call is a negation or not
173 if(s[0] == '!')
175 for(i=0; dbflags[i].token; i++)
177 if(strnicmp(&s[1], dbflags[i].token, strlen(dbflags[i].token)) == 0)
179 _DBPRINTF("clear '%s' debug flag\n", dbflags[i].token);
180 clearFlag(debug_flags, dbflags[i].flag);
184 else
186 // check if the token was "ansi" and if so enable the ANSI color
187 // output
188 if(strnicmp(s, "ansi", 4) == 0)
190 _DBPRINTF("ansi output enabled\n");
191 ansi_output = TRUE;
193 else
195 for(i=0; dbflags[i].token; i++)
197 if(strnicmp(s, dbflags[i].token, strlen(dbflags[i].token)) == 0)
199 _DBPRINTF("set '%s' debug flag\n", dbflags[i].token);
200 setFlag(debug_flags, dbflags[i].flag);
207 // set the next start to our last search
208 if(*e)
209 s = ++e;
210 else
211 break;
215 _DBPRINTF("set debug classes/flags (env:texteditor.mcc.debug): %08lx/%08lx\n", debug_classes, debug_flags);
216 _DBPRINTF("** Normal processing follows ***************************************\n");
218 SetupDbgMalloc();
221 /****************************************************************************/
223 void CleanupDebug(void)
225 CleanupDbgMalloc();
227 _DBPRINTF("** Cleaned up debugging ********************************************\n");
230 /****************************************************************************/
232 // define variables for using ANSI colors in our debugging scheme
233 #define ANSI_ESC_CLR "\033[0m"
234 #define ANSI_ESC_BOLD "\033[1m"
235 #define ANSI_ESC_UNDERLINE "\033[4m"
236 #define ANSI_ESC_BLINK "\033[5m"
237 #define ANSI_ESC_REVERSE "\033[7m"
238 #define ANSI_ESC_INVISIBLE "\033[8m"
239 #define ANSI_ESC_FG_BLACK "\033[0;30m"
240 #define ANSI_ESC_FG_RED "\033[0;31m"
241 #define ANSI_ESC_FG_GREEN "\033[0;32m"
242 #define ANSI_ESC_FG_BROWN "\033[0;33m"
243 #define ANSI_ESC_FG_BLUE "\033[0;34m"
244 #define ANSI_ESC_FG_PURPLE "\033[0;35m"
245 #define ANSI_ESC_FG_CYAN "\033[0;36m"
246 #define ANSI_ESC_FG_LGRAY "\033[0;37m"
247 #define ANSI_ESC_FG_DGRAY "\033[1;30m"
248 #define ANSI_ESC_FG_LRED "\033[1;31m"
249 #define ANSI_ESC_FG_LGREEN "\033[1;32m"
250 #define ANSI_ESC_FG_YELLOW "\033[1;33m"
251 #define ANSI_ESC_FG_LBLUE "\033[1;34m"
252 #define ANSI_ESC_FG_LPURPLE "\033[1;35m"
253 #define ANSI_ESC_FG_LCYAN "\033[1;36m"
254 #define ANSI_ESC_FG_WHITE "\033[1;37m"
255 #define ANSI_ESC_BG "\033[0;4" // background esc-squ start with 4x
256 #define ANSI_ESC_BG_BLACK "\033[0;40m"
257 #define ANSI_ESC_BG_RED "\033[0;41m"
258 #define ANSI_ESC_BG_GREEN "\033[0;42m"
259 #define ANSI_ESC_BG_BROWN "\033[0;43m"
260 #define ANSI_ESC_BG_BLUE "\033[0;44m"
261 #define ANSI_ESC_BG_PURPLE "\033[0;45m"
262 #define ANSI_ESC_BG_CYAN "\033[0;46m"
263 #define ANSI_ESC_BG_LGRAY "\033[0;47m"
265 /****************************************************************************/
267 INLINE void _INDENT(void)
269 int i;
270 for(i=0; i < indent_level; i++)
271 _DBPRINTF(" ");
274 /****************************************************************************/
276 void _ENTER(unsigned long dclass, const char *file, int line, const char *function)
278 if(isFlagSet(debug_classes, dclass))
280 _INDENT();
281 if(ansi_output)
282 _DBPRINTF("%s%s:%ld:Entering %s%s\n", ANSI_ESC_FG_BROWN, file, line, function, ANSI_ESC_CLR);
283 else
284 _DBPRINTF("%s:%ld:Entering %s\n", file, line, function);
287 indent_level++;
290 void _LEAVE(unsigned long dclass, const char *file, int line, const char *function)
292 indent_level--;
294 if(isFlagSet(debug_classes, dclass))
296 _INDENT();
297 if(ansi_output)
298 _DBPRINTF("%s%s:%ld:Leaving %s%s\n", ANSI_ESC_FG_BROWN, file, line, function, ANSI_ESC_CLR);
299 else
300 _DBPRINTF("%s:%ld:Leaving %s\n", file, line, function);
304 void _RETURN(unsigned long dclass, const char *file, int line, const char *function, unsigned long result)
306 indent_level--;
308 if(isFlagSet(debug_classes, dclass))
310 _INDENT();
311 if(ansi_output)
312 _DBPRINTF("%s%s:%ld:Leaving %s (result 0x%08lx, %ld)%s\n", ANSI_ESC_FG_BROWN, file, line, function, result, result, ANSI_ESC_CLR);
313 else
314 _DBPRINTF("%s:%ld:Leaving %s (result 0x%08lx, %ld)\n", file, line, function, result, result);
318 /****************************************************************************/
320 void _SHOWVALUE(unsigned long dclass, unsigned long dflags, unsigned long value, int size, const char *name, const char *file, int line)
322 if(isFlagSet(debug_classes, dclass) &&
323 isFlagSet(debug_flags, dflags))
325 const char *fmt;
327 switch(size)
329 case 1:
330 fmt = "%s:%ld:%s = %ld, 0x%02lx";
331 break;
333 case 2:
334 fmt = "%s:%ld:%s = %ld, 0x%04lx";
335 break;
337 default:
338 fmt = "%s:%ld:%s = %ld, 0x%08lx";
339 break;
342 _INDENT();
344 if(ansi_output)
345 _DBPRINTF(ANSI_ESC_FG_GREEN);
347 _DBPRINTF(fmt, file, line, name, value, value);
349 if(size == 1 && value < 256)
351 if(value < ' ' || (value >= 127 && value < 160))
352 _DBPRINTF(", '\\x%02lx'", value);
353 else
354 _DBPRINTF(", '%lc'", value);
357 if(ansi_output)
358 _DBPRINTF("%s\n", ANSI_ESC_CLR);
359 else
360 _DBPRINTF("\n");
364 /****************************************************************************/
366 void _SHOWPOINTER(unsigned long dclass, unsigned long dflags, const void *p, const char *name, const char *file, int line)
368 if(isFlagSet(debug_classes, dclass) &&
369 isFlagSet(debug_flags, dflags))
371 const char *fmt;
373 _INDENT();
375 if(p != NULL)
376 fmt = "%s:%ld:%s = 0x%08lx\n";
377 else
378 fmt = "%s:%ld:%s = NULL\n";
380 if(ansi_output)
382 _DBPRINTF(ANSI_ESC_FG_GREEN);
383 _DBPRINTF(fmt, file, line, name, p);
384 _DBPRINTF(ANSI_ESC_CLR);
386 else
387 _DBPRINTF(fmt, file, line, name, p);
391 /****************************************************************************/
393 void _SHOWSTRING(unsigned long dclass, unsigned long dflags, const char *string, const char *name, const char *file, int line)
395 if(isFlagSet(debug_classes, dclass) &&
396 isFlagSet(debug_flags, dflags))
398 _INDENT();
400 if(ansi_output)
401 _DBPRINTF("%s%s:%ld:%s = 0x%08lx \"%s\"%s\n", ANSI_ESC_FG_GREEN, file, line, name, string, string, ANSI_ESC_CLR);
402 else
403 _DBPRINTF("%s:%ld:%s = 0x%08lx \"%s\"\n", file, line, name, string, string);
407 /****************************************************************************/
409 void _SHOWMSG(unsigned long dclass, unsigned long dflags, const char *msg, const char *file, int line)
411 if(isFlagSet(debug_classes, dclass) &&
412 isFlagSet(debug_flags, dflags))
414 _INDENT();
416 if(ansi_output)
417 _DBPRINTF("%s%s:%ld:%s%s\n", ANSI_ESC_FG_GREEN, file, line, msg, ANSI_ESC_CLR);
418 else
419 _DBPRINTF("%s:%ld:%s\n", file, line, msg);
423 /****************************************************************************/
425 void _DPRINTF(unsigned long dclass, unsigned long dflags, const char *file, unsigned long line, const char *format, ...)
427 if((isFlagSet(debug_classes, dclass) && isFlagSet(debug_flags, dflags)) ||
428 (isFlagSet(dclass, DBC_ERROR) || isFlagSet(dclass, DBC_WARNING)))
430 va_list args;
432 va_start(args, format);
433 _VDPRINTF(dclass, dflags, file, line, format, args);
434 va_end(args);
438 /****************************************************************************/
440 void _VDPRINTF(unsigned long dclass, unsigned long dflags, const char *file, unsigned long line, const char *format, va_list args)
442 if((isFlagSet(debug_classes, dclass) && isFlagSet(debug_flags, dflags)) ||
443 (isFlagSet(dclass, DBC_ERROR) || isFlagSet(dclass, DBC_WARNING)))
445 static char buf[1024];
447 _INDENT();
449 vsnprintf(buf, 1024, format, args);
451 if(ansi_output)
453 const char *highlight = ANSI_ESC_FG_GREEN;
455 switch(dclass)
457 case DBC_CTRACE: highlight = ANSI_ESC_FG_BROWN; break;
458 case DBC_REPORT: highlight = ANSI_ESC_FG_GREEN; break;
459 case DBC_ASSERT: highlight = ANSI_ESC_FG_RED; break;
460 case DBC_TIMEVAL: highlight = ANSI_ESC_FG_GREEN; break;
461 case DBC_DEBUG: highlight = ANSI_ESC_FG_GREEN; break;
462 case DBC_ERROR: highlight = ANSI_ESC_FG_RED; break;
463 case DBC_WARNING: highlight = ANSI_ESC_FG_PURPLE;break;
466 _DBPRINTF("%s%s:%ld:%s%s\n", highlight, file, line, buf, ANSI_ESC_CLR);
468 else
469 _DBPRINTF("%s:%ld:%s\n", file, line, buf);
473 /****************************************************************************/
475 struct DbgMallocNode
477 struct MinNode node;
478 void *memory;
479 size_t size;
480 const char *file;
481 const char *func;
482 int line;
485 static struct MinList DbgMallocList[256];
486 static struct SignalSemaphore DbgMallocListSema;
487 static ULONG DbgMallocCount;
488 static ULONG DbgUnsuitableFreeCount;
490 #ifndef ARRAY_SIZE
491 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
492 #endif
494 #ifndef NewList
495 #endif
497 // a very simple hashing function to spread the allocations across the lists
498 // Since AmigaOS memory allocation has a granularity of at least 8 bytes we simple ignore the
499 // lower 4 bits (=16 Bytes) and take the next 8 bits as hash value. Not very sophisticated, but
500 // it does the job quite good.
501 #define ptr2hash(p) ((((ULONG)(p)) >> 4) & 0xff)
503 /// findDbgMallocNode
504 // find a given pointer in the tracking lists
505 static struct DbgMallocNode *findDbgMallocNode(const void *ptr)
507 struct DbgMallocNode *result = NULL;
508 struct Node *curNode;
510 for(curNode = GetHead((struct List *)&DbgMallocList[ptr2hash(ptr)]); curNode != NULL; curNode = GetSucc(curNode))
512 struct DbgMallocNode *dmn = (struct DbgMallocNode *)curNode;
514 if(dmn->memory == ptr)
516 result = dmn;
517 break;
521 return result;
525 /// matchAllocFunc
526 // check wether the used allocation function matches a set of given function names
527 // separated by '|', i.e. "malloc|calloc|strdup"
528 BOOL matchAllocFunc(const char *allocFunc, const char *freeFunc)
530 BOOL match = FALSE;
531 const char *p = freeFunc;
532 const char *q;
534 while((q = strchr(p, '|')) != NULL)
536 char tmp[16];
538 // we have to handle more than one possible function name
539 strlcpy(tmp, p, ((size_t)(q-p)+1 < sizeof(tmp)) ? (size_t)(q-p)+1 : sizeof(tmp));
540 if(strcmp(allocFunc, tmp) == 0)
542 match = TRUE;
543 break;
545 p = q+1;
548 if(match == FALSE)
550 // compare the last or only function name
551 match = (strcmp(allocFunc, p) == 0);
554 return match;
558 /// _MEMTRACK
559 // add a new node to the memory tracking lists
560 void _MEMTRACK(const char *file, const int line, const char *func, void *ptr, size_t size)
562 if(isFlagSet(debug_classes, DBC_MTRACK))
564 if(ptr != NULL && size != 0)
566 struct DbgMallocNode *dmn;
568 if((dmn = AllocVec(sizeof(*dmn), MEMF_ANY)) != NULL)
570 dmn->memory = ptr;
571 dmn->size = size;
572 dmn->file = file;
573 dmn->line = line;
574 dmn->func = func;
576 ObtainSemaphore(&DbgMallocListSema);
578 AddTail((struct List *)&DbgMallocList[ptr2hash(ptr)], (struct Node *)&dmn->node);
579 DbgMallocCount++;
581 ReleaseSemaphore(&DbgMallocListSema);
584 else
585 _DPRINTF(DBC_WARNING, DBF_ALWAYS, file, line, "potential invalid %s call with return (0x%08lx, 0x%08lx)", func, ptr, size);
590 /// _UNMEMTRACK
591 // remove a node from the memory tracking lists
592 void _UNMEMTRACK(const char *file, const int line, const char *func, const void *ptr)
594 if(isFlagSet(debug_classes, DBC_MTRACK) && ptr != NULL)
596 BOOL success = FALSE;
597 struct DbgMallocNode *dmn;
599 ObtainSemaphore(&DbgMallocListSema);
601 if((dmn = findDbgMallocNode(ptr)) != NULL)
603 Remove((struct Node *)dmn);
605 if(matchAllocFunc(dmn->func, func) == FALSE)
607 _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);
608 DbgUnsuitableFreeCount++;
611 FreeVec(dmn);
613 DbgMallocCount--;
615 success = TRUE;
618 if(success == FALSE)
619 _DPRINTF(DBC_WARNING, DBF_ALWAYS, file, line, "free of untracked memory area 0x%08lx attempted", ptr);
621 ReleaseSemaphore(&DbgMallocListSema);
626 /// SetupDbgMalloc
627 // initialize the memory tracking framework
628 static void SetupDbgMalloc(void)
630 ENTER();
632 if(isFlagSet(debug_classes, DBC_MTRACK))
634 ULONG i;
636 for(i = 0; i < ARRAY_SIZE(DbgMallocList); i++)
637 NewList((struct List *)&DbgMallocList[i]);
639 DbgMallocCount = 0;
640 DbgUnsuitableFreeCount = 0;
642 memset(&DbgMallocListSema, 0, sizeof(DbgMallocListSema));
643 InitSemaphore(&DbgMallocListSema);
646 LEAVE();
650 /// CleanupDbgMalloc
651 // cleanup the memory tracking framework and output possibly pending allocations
652 static void CleanupDbgMalloc(void)
654 ENTER();
656 if(isFlagSet(debug_classes, DBC_MTRACK))
658 _DBPRINTF("** Cleaning up memory tracking *************************************\n");
660 ObtainSemaphore(&DbgMallocListSema);
662 if(DbgMallocCount != 0 || DbgUnsuitableFreeCount != 0)
664 if(DbgMallocCount != 0)
666 ULONG i;
668 E(DBF_ALWAYS, "there are still %ld unfreed memory trackings", DbgMallocCount);
669 for(i = 0; i < ARRAY_SIZE(DbgMallocList); i++)
671 struct DbgMallocNode *dmn;
673 while((dmn = (struct DbgMallocNode *)RemHead((struct List *)&DbgMallocList[i])) != NULL)
675 _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);
677 // We only free the node structure here but not dmn->memory itself.
678 // First of all, this is because the allocation could have been done
679 // by other functions than malloc() and calling free() for these will
680 // cause havoc. And second the c-library's startup code will/should
681 // free all further pending allocations upon program termination.
682 FreeVec(dmn);
686 if(DbgUnsuitableFreeCount != 0)
688 E(DBF_ALWAYS, "there were %ld unsuitable freeing calls", DbgUnsuitableFreeCount);
691 else
692 D(DBF_ALWAYS, "all memory trackings have been free()'d correctly");
694 ReleaseSemaphore(&DbgMallocListSema);
697 LEAVE();
701 /// DumpDbgMalloc
702 // output all current allocations
703 void DumpDbgMalloc(void)
705 ENTER();
707 if(isFlagSet(debug_classes, DBC_MTRACK))
709 ULONG i;
711 ObtainSemaphore(&DbgMallocListSema);
713 D(DBF_ALWAYS, "%ld memory areas tracked", DbgMallocCount);
714 for(i = 0; i < ARRAY_SIZE(DbgMallocList); i++)
716 struct Node *curNode;
718 for(curNode = GetHead((struct List *)&DbgMallocList[i]); curNode != NULL; curNode = GetSucc(curNode))
720 struct DbgMallocNode *dmn = (struct DbgMallocNode *)curNode;
722 _DPRINTF(DBC_MTRACK, DBF_ALWAYS, dmn->file, dmn->line, "memarea 0x%08lx, size/type %ld, func (%s)", dmn->memory, dmn->size, dmn->func);
726 ReleaseSemaphore(&DbgMallocListSema);
729 LEAVE();
734 #endif /* DEBUG */