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.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)
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
},
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
},
131 // we parse the env variable token-wise
137 if((e
= strpbrk(s
, " ,;")) == NULL
)
140 // check if the token is class definition or
141 // just a flag definition
144 // check if this call is a negation or not
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
);
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
);
172 // check if this call is a negation or not
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
);
186 // check if the token was "ansi" and if so enable the ANSI color
188 if(strnicmp(s
, "ansi", 4) == 0)
190 _DBPRINTF("ansi output enabled\n");
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
215 _DBPRINTF("set debug classes/flags (env:texteditor.mcc.debug): %08lx/%08lx\n", debug_classes
, debug_flags
);
216 _DBPRINTF("** Normal processing follows ***************************************\n");
221 /****************************************************************************/
223 void CleanupDebug(void)
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)
270 for(i
=0; i
< indent_level
; i
++)
274 /****************************************************************************/
276 void _ENTER(unsigned long dclass
, const char *file
, int line
, const char *function
)
278 if(isFlagSet(debug_classes
, dclass
))
282 _DBPRINTF("%s%s:%ld:Entering %s%s\n", ANSI_ESC_FG_BROWN
, file
, line
, function
, ANSI_ESC_CLR
);
284 _DBPRINTF("%s:%ld:Entering %s\n", file
, line
, function
);
290 void _LEAVE(unsigned long dclass
, const char *file
, int line
, const char *function
)
294 if(isFlagSet(debug_classes
, dclass
))
298 _DBPRINTF("%s%s:%ld:Leaving %s%s\n", ANSI_ESC_FG_BROWN
, file
, line
, function
, ANSI_ESC_CLR
);
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
)
308 if(isFlagSet(debug_classes
, dclass
))
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
);
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
))
330 fmt
= "%s:%ld:%s = %ld, 0x%02lx";
334 fmt
= "%s:%ld:%s = %ld, 0x%04lx";
338 fmt
= "%s:%ld:%s = %ld, 0x%08lx";
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
);
354 _DBPRINTF(", '%lc'", value
);
358 _DBPRINTF("%s\n", ANSI_ESC_CLR
);
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
))
376 fmt
= "%s:%ld:%s = 0x%08lx\n";
378 fmt
= "%s:%ld:%s = NULL\n";
382 _DBPRINTF(ANSI_ESC_FG_GREEN
);
383 _DBPRINTF(fmt
, file
, line
, name
, p
);
384 _DBPRINTF(ANSI_ESC_CLR
);
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
))
401 _DBPRINTF("%s%s:%ld:%s = 0x%08lx \"%s\"%s\n", ANSI_ESC_FG_GREEN
, file
, line
, name
, string
, string
, ANSI_ESC_CLR
);
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
))
417 _DBPRINTF("%s%s:%ld:%s%s\n", ANSI_ESC_FG_GREEN
, file
, line
, msg
, ANSI_ESC_CLR
);
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
)))
432 va_start(args
, format
);
433 _VDPRINTF(dclass
, dflags
, file
, line
, format
, 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];
449 vsnprintf(buf
, 1024, format
, args
);
453 const char *highlight
= ANSI_ESC_FG_GREEN
;
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
);
469 _DBPRINTF("%s:%ld:%s\n", file
, line
, buf
);
473 /****************************************************************************/
485 static struct MinList DbgMallocList
[256];
486 static struct SignalSemaphore DbgMallocListSema
;
487 static ULONG DbgMallocCount
;
488 static ULONG DbgUnsuitableFreeCount
;
491 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
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
)
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
)
531 const char *p
= freeFunc
;
534 while((q
= strchr(p
, '|')) != NULL
)
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)
550 // compare the last or only function name
551 match
= (strcmp(allocFunc
, p
) == 0);
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
)
576 ObtainSemaphore(&DbgMallocListSema
);
578 AddTail((struct List
*)&DbgMallocList
[ptr2hash(ptr
)], (struct Node
*)&dmn
->node
);
581 ReleaseSemaphore(&DbgMallocListSema
);
585 _DPRINTF(DBC_WARNING
, DBF_ALWAYS
, file
, line
, "potential invalid %s call with return (0x%08lx, 0x%08lx)", func
, ptr
, size
);
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
++;
619 _DPRINTF(DBC_WARNING
, DBF_ALWAYS
, file
, line
, "free of untracked memory area 0x%08lx attempted", ptr
);
621 ReleaseSemaphore(&DbgMallocListSema
);
627 // initialize the memory tracking framework
628 static void SetupDbgMalloc(void)
632 if(isFlagSet(debug_classes
, DBC_MTRACK
))
636 for(i
= 0; i
< ARRAY_SIZE(DbgMallocList
); i
++)
637 NewList((struct List
*)&DbgMallocList
[i
]);
640 DbgUnsuitableFreeCount
= 0;
642 memset(&DbgMallocListSema
, 0, sizeof(DbgMallocListSema
));
643 InitSemaphore(&DbgMallocListSema
);
651 // cleanup the memory tracking framework and output possibly pending allocations
652 static void CleanupDbgMalloc(void)
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)
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.
686 if(DbgUnsuitableFreeCount
!= 0)
688 E(DBF_ALWAYS
, "there were %ld unsuitable freeing calls", DbgUnsuitableFreeCount
);
692 D(DBF_ALWAYS
, "all memory trackings have been free()'d correctly");
694 ReleaseSemaphore(&DbgMallocListSema
);
702 // output all current allocations
703 void DumpDbgMalloc(void)
707 if(isFlagSet(debug_classes
, DBC_MTRACK
))
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
);