OSDev.org
https://forum.osdev.org/

Implementation of assert() in kernel space
https://forum.osdev.org/viewtopic.php?f=1&t=10140
Page 1 of 1

Author:  Colonel Kernel [ Sat Jan 01, 2005 6:44 pm ]
Post subject:  Implementation of assert() in kernel space

Quote:
this thread is referenced in the FAQ


I want to add some debugging & error-handling facilities to my kernel, and I'm curious about how other people have tackled assert().

For example, let's say some code in a low-level part of the kernel (like the kernel-mode C run-time library) detects a logic error in a debug build and wants to report it via assert(). Here are the choices as I see them (assuming the assertion failed):
  • 1. assert() prints out the file and line number, then panics.
  • 2. assert() tries to capture the current register contents and a kernel stack dump itself, then prints out the file and line number, then panics.
  • 3. assert() prints out the file and line number, then triggers one of the kernel's exception handlers (by executing int 3 maybe), which in turn dumps the machine state that it has already collected, and then panics.

Option 1 is easy to do, but doesn't include machine state in the output. Option 2 seems hard to do (at least to me... maybe I'm missing something?). Option 3 leverages the existing code I have for capturing the context of the current task, but are there any risks associated with this approach?

As an aside, how important is machine state for debugging anyway? My feeling is that it's less important for assert(), since you have file & line numbers available, but it's important for all other cases... If I could probably do without it, I may just go for option 1.
Quote:

Author:  Pype.Clicker [ Sun Jan 02, 2005 6:17 am ]
Post subject:  Re:Implementation of assert() in kernel space

register state may help you figuring out the value of some variable. Probably the result of the assert() computation makes more sense, though.

Eg:
Code:
kassert(ptr->next==p2->next,4,ptr,p2,ptr->next,p2->next);

that would output
"assertion failed at file.c:line X, 8004260, 8004280, 0, 8004280"

Author:  Colonel Kernel [ Sun Jan 02, 2005 12:40 pm ]
Post subject:  Re:Implementation of assert() in kernel space

That makes sense, and should be easy to do...

I had another thought about making assert trigger a breakpoint exception -- would it be the right way to allow future integration with a remote kernel debugger?

Author:  Colonel Kernel [ Tue Jan 04, 2005 12:57 am ]
Post subject:  Re:Implementation of assert() in kernel space

I did a bit of research... In case anyone is interested, Linux takes the approach I mentioned in its BUG() macro (which is called from at least one version of assert()). It uses the reserved opcode ud2 to defer panicking to the central trap-dispatching mechanism.

Author:  dh [ Wed Jan 05, 2005 10:36 am ]
Post subject:  Re:Implementation of assert() in kernel space

i did a VERY basic assert():
Code:
#define assert(check, condition) if (check != condition) kSystem_Halt();


I'll put the full thing once it is tested ;P

Author:  proxy [ Wed Jan 05, 2005 2:52 pm ]
Post subject:  Re:Implementation of assert() in kernel space

your assert has a problem (even as simple as it is...) in some cases it can break scoping.. like this:

Code:
   if (foo)
      assert( yabba, dabba );
   else
      something_else( );


this will expand to..

Code:
   if (foo)
      if(yabba != dabba)
         kSystem_Halt;;
      else
         something_else( );


and thus you have a dangling else problem...

to fix this is simple define your assert liek this:

Code:
#define assert(check, condition) do { if(check != condition) kSystem_Halt(); } while(0)


also.... another issue

you shoudl ALWAYS put params of a macro in parethesis to make sure it expands properly for example..suppose i do:

Code:
assert(a & 3, 0);


this will expand to:

Code:
if(a & 3 != 0) kSystem_Halt();


well != has higher priority than & so this acts like this...

Code:
if(a & (3 != 0)) kSystem_Halt();


no bueno...

best is like this:

Code:
#define assert(check, condition) do { if((check) != (condition)) kSystem_Halt(); } while(0)


if EVEN better :) is like this..

Code:
#define assert(check) do { if(!(check)) kSystem_Halt(); } while(0)


this way you can make any comparison you want (not just != ....)

good luck :)

proxy

Author:  dh [ Sun Jan 09, 2005 3:46 pm ]
Post subject:  Re:Implementation of assert() in kernel space

oh ya? I'll study that. Thanks.

Author:  mystran [ Sun Jan 09, 2005 6:18 pm ]
Post subject:  Re:Implementation of assert() in kernel space

I think it's better to avoid do{...}while() blocks when you can avoid them. You can write a conditional in a macro in a way that it can be used inside anything, as long as you don't need loops. In fact, you can also do sequence of things if you want too. You can't loop without a block, but then again I don't think looping in macros is that good of a thing anyway, and you can always call a function that does loop.

Code:
#define assert(test) \
  ((test) \
    ? (kprint("assertion failed, " __FILE__ ":" __LINE__ ": " #test), panic()) \
    : 0)

Author:  Colonel Kernel [ Mon Jan 10, 2005 12:48 am ]
Post subject:  Re:Implementation of assert() in kernel space

I think the loop in this case is necessary though -- as a safeguard in case kSystem_halt fails for some reason and doesn't actually halt the CPU.

Author:  Solar [ Mon Jan 10, 2005 1:44 am ]
Post subject:  Re:Implementation of assert() in kernel space

Erm... do { } while ( 0 ) does not loop!

The assert() implementation recommended by P.J. Plauger (author of the Dinkumware library and member of the C committee) is indeed the trinary operator though.

What follows is a ripaway from how I implemented assert() in the PDCLib, provided as Public Domain.

assert.h:

Code:
#ifndef _KERNEL_ASSERT
#define _KERNEL_ASSERT
#if __STDC_VERSION__ == 199901L
void _Kassert_99( char const * const, char const * const, char const * const );
#else
void _Kassert( char const * const );
#endif
#define __symbol2value( x ) #x
#define __symbol2string( x ) __symbol2value( x )
#endif

/* re-including assert.h results in assert() being redefined */
#undef assert

#ifdef NDEBUG
#define assert( ignore ) ( (void) 0 )
#else
#if __STDC_VERSION__ == 199901L
#define assert( expression ) ( ( expression ) ? (void) 0 \
        : _Kassert( "Assertion failed: " #expression \
                          ", function ", __func__, \
                          ", file " __FILE__ \
                          ", line " __symbol2string( __LINE__ ) \
                          "." ) )
#else
#define assert( expression ) ( ( expression ) ? (void) 0 \
        : _Kassert( "Assertion failed: " #expression \
                          ", file " __FILE__ \
                          ", line " __symbol2string( __LINE__ ) \
                          "." ) )
#endif
#endif


Implement _Kassert() and _Kassert_99() to do whatever you like. The char* contain e.g.:

"Assertion failed: x == 0, file mykernel.c, line 385." for _Kassert(), and

"Assertion failed: x == 0, function "
"mykernel_map_table"
", file mykernel.c, line 285." for _Kassert_99(), respectively.

Only C99 does have __func__ specified, hence the __STDC_VERSION__ check. Above implementation allows you to use this assert() in exactly the way described by the C standard (i.e., you may include assert.h multiple times, the macro is defined to nothing when NDEBUG is set etc.)

Hope this helps.

Author:  proxy [ Mon Jan 10, 2005 9:52 am ]
Post subject:  Re:Implementation of assert() in kernel space

as solar mentioned the "do { } while(0)" is not ment to be a loop at all, it is ment to avoid scope issues (which i gave an example of...) keep in mind that the compiler (even older ones) is smart enough to know not to emit loop related code for this one. it is a VERY common way to properly implement assert.

proxy

Author:  Colonel Kernel [ Mon Jan 10, 2005 2:06 pm ]
Post subject:  Re:Implementation of assert() in kernel space

0... 1... Damn those off-by-one errors. ;)

Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/