1
0
mirror of https://github.com/deadw00d/AROS.git synced 2025-11-22 11:49:05 +00:00
Files
AROS-v0/compiler/arossupport/include/debug.h
2025-11-09 17:01:04 +01:00

409 lines
12 KiB
C

/*
Copyright (C) 1995-2025, The AROS Development Team. All rights reserved.
Debugging macros.
This include file can be included several times!
*/
#ifndef CLIB_AROSSUPPORT_PROTOS_H
# include <proto/arossupport.h>
#endif
#ifndef PROTO_EXEC_H
# include <proto/exec.h> /* For FindTask() */
#endif
#ifndef EXEC_TASKS_H
# include <exec/tasks.h>
#endif
#ifndef EXEC_ALERTS_H
# include <exec/alerts.h>
#endif
#ifndef DEBUG
# define DEBUG 0
#endif
#ifndef SDEBUG
# define SDEBUG 0
#endif
#ifndef ADEBUG
# define ADEBUG 0
#endif
#ifndef MDEBUG
# define MDEBUG 0
#endif
/* Remove all macros. They get new values each time this file is
included */
#undef D
#undef DB2
#undef ReturnVoid
#undef ReturnPtr
#undef ReturnStr
#undef ReturnInt
#undef ReturnXInt
#undef ReturnFloat
#undef ReturnSpecial
#undef ReturnBool
/* Macros for "stair debugging" */
#undef SDInit
#undef EnterFunc
#undef Indent
#undef ExitFunc
/* StegerG */
#undef SDEBUG
#define SDEBUG 0
#if SDEBUG
# ifndef SDEBUG_INDENT
# define SDEBUG_INDENT 2
# endif
/* This is some new macros for making debug output more readable,
** by indenting for each functioncall made.
** Usage: Call the SDInit() macro before anything else in your main().
** Start the functions you want to debug with EnterFunc(bug("something"))
** and ALWAYS match these with a Returnxxxx type macro
** at the end of the func.
** Inside the func you can use the normal D(bug()) macro.
**
** To enable the macros, just add a #define SDEBUG 1
*/
/* User macro */
#define EnterFunc(x) do { \
struct Task *sd_task = FindTask(NULL); \
int sd_spaceswritten; \
for (sd_spaceswritten = 0; sd_spaceswritten < (ULONG)sd_task->tc_UserData; sd_spaceswritten ++) kprintf(" "); \
((ULONG)sd_task->tc_UserData) += SDEBUG_INDENT; } while(0); \
x
/* User macro. Add into start of your main() routine */
# define SDInit() \
do { FindTask(NULL)->tc_UserData = NULL; } while(0)
/* Internal */
# define Indent do { \
struct Task *sd_task = FindTask(NULL); \
int sd_spaceswritten; \
for (sd_spaceswritten = 0; sd_spaceswritten < (ULONG)sd_task->tc_UserData; sd_spaceswritten ++) kprintf(" "); } while(0)
/* Internal */
#define ExitFunc do { \
struct Task *sd_task = FindTask(NULL); \
int sd_spaceswritten; \
((ULONG)sd_task->tc_UserData) -= SDEBUG_INDENT; \
for (sd_spaceswritten = 0; sd_spaceswritten < (ULONG)sd_task->tc_UserData; sd_spaceswritten ++) kprintf(" "); } while(0)
#else
# define SDInit()
# define Indent
# define EnterFunc(x...) D(x)
# define ExitFunc
#endif /* SDEBUG */
/* Sanity check macros
*
* ASSERT(x)
* Do nothing if the expression <x> evalutates to a
* non-zero value, output a debug message otherwise.
*
* ASSERT_VALID_PTR(x)
* Checks that the expression <x> points to a valid memory location, and
* outputs a debug message otherwise. A NULL pointer is considered INVALID.
*
* ASSERT_VALID_PTR_OR_NULL(x)
* Checks that the expression <x> points to a valid memory location or that
* it is NULL, and outputs a debug message otherwise. A NULL pointer is
* considered VALID.
*
* ASSERT_VALID_TASK(t)
* Checks that the pointer <t> points to a valid Task
* structure and outputs a debug message otherwise.
*
* ASSERT_VALID_PROCESS(p)
* Checks that the pointer <p> points to a valid Process
* structure and outputs a debug message otherwise.
*
* KASSERT(x)
* Do nothing if the expression <x> evalutates to a
* non-zero value, output a debug message, and cause an
* Alert() otherwise. This should only be used in kernel code.
*
*/
#undef DBPRINTF
#undef THIS_FILE
#undef ASSERT
#undef ASSERT_VALID_PTR
#undef ASSERT_VALID_PTR_OR_NULL
#undef ASSERT_VALID_TASK
#undef ASSERT_VALID_PROCESS
#if ADEBUG
#define DBPRINTF kprintf
/* The trick with THIS_FILE allows us to reuse the same static string
* instead of allocating a new copy for each invocation of these macros.
*/
#ifndef __SRCFILENAME__
#define THIS_FILE __FILE__
#else
#define THIS_FILE __SRCFILENAME__
#endif
#define ASSERT(x) do { \
if (!(x)) { \
DBPRINTF("\x07%s:%u: assertion failed: %s\n", \
THIS_FILE, __LINE__, #x); \
Alert(AG_BadParm); \
} \
} while(0)
#define ASSERT_VALID_PTR(x) do { \
if (((IPTR)(x) < 1024) || !TypeOfMem((APTR)(x))) { \
DBPRINTF("\x07%s, %u: bad pointer: %s = $%p\n", \
THIS_FILE, __LINE__, #x, x); \
Alert(AG_BadParm); \
} \
} while(0)
#define ASSERT_VALID_PTR_OR_NULL(x) do { \
if (((x) != NULL) && \
(((IPTR)(x) < 1024) || !TypeOfMem((APTR)(x)))) { \
DBPRINTF("\x07%s, %u: bad pointer: %s = $%p\n", \
THIS_FILE, __LINE__, #x, x); \
Alert(AG_BadParm); \
} \
} while(0)
#define ASSERT_VALID_TASK(t) do { \
ASSERT_VALID_PTR(t); \
ASSERT((((t)->tc_Node.ln_Type == NT_TASK) || \
(t)->tc_Node.ln_Type == NT_PROCESS)); \
} while(0)
#define ASSERT_VALID_PROCESS(p) do { \
ASSERT_VALID_PTR(p); \
ASSERT((p)->pr_Task.tc_Node.ln_Type == NT_PROCESS); \
} while(0)
#define KASSERT(x) do { \
if (!(x)) { \
DBPRINTF("\x07%s:%u: assertion failed: %s\n", \
THIS_FILE, __LINE__, #x); \
Alert(AG_BadParm); \
} \
} while(0)
#else /* !ADEBUG */
# define ASSERT(x)
# define ASSERT_VALID_PTR(x)
# define ASSERT_VALID_PTR_OR_NULL(x)
# define ASSERT_VALID_TASK(t)
# define ASSERT_VALID_PROCESS(p)
# define KASSERT(x)
#endif /* ADEBUG */
/* Memory munging macros
*/
/* MUNGWALL_SIZE must be a multiple of MEMCHUNK_TOTAL, otherwise for example
rom/exec/allocate complains, because the return value (memory pointer) of
AllocMem would not be a multiple of MEMCHUNK_TOTAL anymore.
See rom/exec/allocmem and rom/exec/freemem for more info.
The "32 *" probably makes sure that you get a multiple of MEMCHUNK_TOTAL on
every machine, because on 32 bit machines MEMCHUNK_TOTAL will be 8 and
on 64 bit machines it will be 16, and it would even work on 128 bit machines
because I guess there MEMCHUNK_TOTAL will be 32 */
#define MUNGWALL_SIZE (32 * 1)
#define MEMFILL_FREE 0xDEADBEEFL
#define MEMFILL_ALLOC 0xC0DEDBADL
#define MEMFILL_WALL 0xABADC0DEL
#undef MUNGE_BLOCK
#undef BUILD_WALL
#undef CHECK_WALL
#undef MungWallCheck
#if MDEBUG
/* Fill the memory block pointed by <ptr> of size <size> with <fill>
*/
# define MUNGE_BLOCK(ptr, fill, size) do { \
ULONG *__p = (ULONG *)(ptr); \
ULONG __s = (size) / sizeof(ULONG); \
while (__s--) *__p++ = (fill); \
} while(0)
/* Build a wall over the memory block <ptr> of size <size> with <fill> bricks.
*/
# define BUILD_WALL(ptr, fill, size) do { \
UBYTE *__p = (UBYTE *)(ptr); \
ULONG __s = (size); \
while (__s--) *__p++ = (fill); \
} while(0)
/* Check the integrity of the wall <ptr> of size <size> bytes containing <fill>.
*/
# define CHECK_WALL(ptr, fill, size) do { \
UBYTE *__p = (UBYTE *)(ptr); \
size_t __s = (size); \
while (__s--) \
{ \
if(*__p != (fill)) \
{ \
struct Task *__t = FindTask(NULL); \
kprintf("\x07" "Broken wall detected at %s:%u at 0x%p, " \
"Task: 0x%p, Name: %s\n", \
__FUNCTION__, __LINE__, \
__p, __t, __t->tc_Node.ln_Name);\
} \
__p++; \
} \
} while(0)
# define MungWallCheck() AvailMem(MEMF_CLEAR)
#else
# define MUNGE_BLOCK(ptr, size, fill)
# define BUILD_WALL(ptr, fill, size)
# define CHECK_WALL(ptr, fill, size)
# define MungWallCheck()
#endif /* MDEBUG */
#ifdef __mc68000__
# define M68K_RETURN_INREG(val, reg) asm volatile("move.l %0, %%" reg :: "r"(val) : reg)
#else
# define M68K_RETURN_INREG(val, reg)
#endif
#if DEBUG
# define D(x...) Indent x
# if DEBUG > 1
# define DB2(x...) x
# else
# define DB2(x...) /* eps */
# endif
/* return-macros. NOTE: I make a copy of the value in __aros_val, because
the return-value might have side effects (like return x++;). */
# define ReturnVoid(name) { ExitFunc kprintf ("Exit " name "()\n"); return; }
# define ReturnPtr(name,type,val) { type __aros_val = (type)val; \
ExitFunc kprintf ("Exit " name "=%p\n", \
(APTR)__aros_val); return __aros_val; }
# define ReturnPtrReg(name,type,val,reg) { \
type __aros_val = (type)(val); \
ExitFunc kprintf("Exit " name "=%p (" reg ")\n", (APTR)__aros_val); \
M68K_RETURN_INREG(__aros_val, reg); \
return __aros_val; \
}
# define ReturnStr(name,type,val) { type __aros_val = (type)val; \
ExitFunc kprintf ("Exit " name "=\"%s\"\n", \
__aros_val); return __aros_val; }
# define ReturnInt(name,type,val) { type __aros_val = (type)val; \
ExitFunc kprintf ("Exit " name "=%ld\n", \
(ULONG)__aros_val); return __aros_val; }
# define ReturnXInt(name,type,val) { type __aros_val = (type)val; \
ExitFunc kprintf ("Exit " name "=%lx\n", \
(ULONG)__aros_val); return __aros_val; }
# define ReturnFloat(name,type,val) { type __aros_val = (type)val; \
ExitFunc kprintf ("Exit " name "=%g\n", \
(ULONG)__aros_val); return __aros_val; }
# define ReturnSpecial(name,type,val,fmt) { type __aros_val = (type)val; \
ExitFunc kprintf ("Exit " name "=" fmt "\n", \
(ULONG)__aros_val); return __aros_val; }
# define ReturnBool(name,val) { BOOL __aros_val = (val != 0); \
ExitFunc kprintf ("Exit " name "=%s\n", \
__aros_val ? "TRUE" : "FALSE"); \
return __aros_val; }
#else /* !DEBUG */
# define D(x...) /* eps */
# define DB2(x...) /* eps */
# define ReturnVoid(name) return
# define ReturnPtr(name,type,val) return val
# define ReturnPtrReg(name,type,val,reg) { \
type __aros_val = (type)(val); \
M68K_RETURN_INREG(__aros_val, reg); \
return __aros_val; \
}
# define ReturnStr(name,type,val) return val
# define ReturnInt(name,type,val) return val
# define ReturnXInt(name,type,val) return val
# define ReturnFloat(name,type,val) return val
# define ReturnSpecial(name,type,val,fmt) return val
# define ReturnBool(name,val) return val
#endif /* DEBUG */
#undef CHECK_STACK
#if AROS_STACK_DEBUG
/*
* This works if stack snooping is turned on. One way to do it is to boot AROS
* with "stacksnoop" command line argument.
* I don't want to care about word length here because ULONG is a part of IPTR and
* ULONG test will do here on 64-bit machines too.
*
* TODO: This doesn't work for 'Reversed' stack (growing upwards). If someone will ever
* work on such an architecture (SPARC ???) he'll have to fix it.
*/
#define CHECK_STACK \
{ \
struct Task *me = FindTask(NULL); \
if (me->tc_Flags & TF_STACKCHK) \
{ \
ULONG *stktop = me->tc_SPLower; \
\
if (stktop && (*stktop != 0xE1E1E1E1)) \
bug("STACK OVERFLOW in %s, line %u\n", __FILE__, __LINE__); \
} \
}
#else
#define CHECK_STACK
#endif
#ifndef AROS_DEBUG_H
#define AROS_DEBUG_H
#define bug kprintf
#define rbug(main,sub,lvl,fmt,args...) \
rkprintf (DBG_MAINSYSTEM_ ## main, \
DBG_ ## main ## _SUBSYSTEM_ ## sub, \
lvl, fmt, ##args)
/* Debugging constants. These should be defined outside and this
part should be generated. */
#define DBG_MAINSYSTEM_INTUITION "intuition"
#define DBG_INTUITION_SUBSYSTEM_INPUTHANDLER "inputhandler"
#define AROS_FUNCTION_NOT_IMPLEMENTED(library) \
kprintf("The function %s/%s() is not implemented.\n", (library), __FUNCTION__)
#define AROS_METHOD_NOT_IMPLEMENTED(CLASS, name) \
kprintf("The method %s::%s() is not implemented.\n", (CLASS), (name))
#define aros_print_not_implemented(name) \
kprintf("The function %s() is not implemented.\n", (name))
#define ALIVE kprintf("%s - %s line %u\n",__FILE__,__FUNCTION__,__LINE__);
#endif /* AROS_DEBUG_H */