2

I am reading "No Bugs!" by David Thielen and chapter 3 discusses a way to determine if a given code path has been hit. It suggests having a macro that check an argument, and, if it is true, executes an assembly instruction that generates a specific interrupt (0x3) to create a debugger breakpoint. If the argument to the macro is false, it simply does nothing.

The macro, then, look like this:

#ifdef DEBUG
#define Trap(t)     ( (t) ? __asm__("int $0x3") : )
#endif

This code, however, causes a compile error for gcc:

int_test.c:16:35: error: expected expression before ‘__asm__’

I learned from here that because gcc's asm is a block statement and not an expression, I must use statement expressions in order to use the asm in this way. So, now it becomes:

#define Trap(t)     ( (t) ? ({ __asm__("int $0x3"); }) : )

The compiler still complains:

int_test.c:16:64: error: expected expression before ‘)’ token

Okay, so now I have to do this?

#define Trap(t)     ( (t) ? ({ __asm__("int $0x3"); }) : ({ ; }) )

That seems really dumb. Can't I just have the preprocessor insert nothing if t is false without using this annoying syntax?

Note: I have left out a few other similar macros for simplicity and I have adapted the syntax from the book to work for gcc (such as replacing the book's _asm with asm as well as using AT&T syntax and surrounding the assmembly in "")

Community
  • 1
  • 1
master_latch
  • 434
  • 5
  • 12
  • 3
    If you only need side effects and not values, why use the ternary operator instead of an if statement? – Jim Lewis Dec 13 '13 at 22:27
  • 2
    +1 to using `if` instead of ternary operator. **HOWEVER** I have to ask, why in the world are you using assembly instructions to trigger breakpoints instead of just using a real debugger and setting breakpoints at places of interest? This seems like a very, very primitive, unnecessary, low level and downright *ancient* debugging method... – TypeIA Dec 13 '13 at 22:30
  • Traditionally this is done through the comma operator. E.g. cond && (function_with_side_effects(), 1) though I'm not certain whether this will work for GCC inline assembly statements, so you may need to place the actual breakpoint in a separate function. Any particular reason you don't want to make the whole thing an (inline) function? – doynax Dec 13 '13 at 22:34
  • @David Norris: It is not an alternative to normal interactive breakpoints but rather a useful complement. They are typically used more-or-less as assertions except for conditions which aren't necessarily errors (to highlight rarely exercised code paths, say) – doynax Dec 13 '13 at 22:40
  • @DavidNorris If I just used the debugger, I would have to set the breakpoints by hand each time I run gdb, right? Unless there is a way to save a gdb config file or something. In either case, I'm just trying to learn from this book, but it was published in '92 so I guess it is dated. – master_latch Dec 13 '13 at 22:45

2 Answers2

4

The advice you're getting from the book is essentially to create your own assert() functionality.

On most systems, you should just use the assert() macro that's provided in assert.h. If the assertion is triggered and you're running under a debugger the assert will trigger a breakpoint (again - on most systems).

If for some reason you can't use the standard assert.h functionality (for example, maybe some sort of bare-bones embedded system toolchain that doesn't provide it), you can do something like the following:

#ifdef DEBUG_MODE
    void DoAssert(void);
    #define ASSERT(expr) ((expr) ? (void) 0 : DoAssert())
#else
    #define ASSERT(expr) ((void) 0)
#endif

And put whatever you need to have to trap into the debugger in the DoAssert() function - in your case that would be the inline assembly for int 3.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Ah, I see. This book was published in '92 so perhaps the assert() library was not introduced until after it was published. – master_latch Dec 13 '13 at 22:48
  • assert() was certainly around at the time. The real difference is that this type of trap() is non-fatal. This way you can place a permanent breakpoint in a failure which your program knows how to handle (say a missing file) or some other edge case you'd like to know about – doynax Dec 13 '13 at 22:58
1

Since you're using an instruction inside an expression anyway, use the conditional construct with instruction syntax instead of the conditional construct with expression syntax.

#define Trap(t)     ({ if (t) __asm__("int $0x3"); })

Alternatively, make it a function.

static inline void Trap(long t) {
    if (t) __asm__("int $0x3");
}

Though since you won't be using Trap as an expression much, given that it returns void (so you can only use it on the left-hand side of , or as a top-level expression), you might as well make the whole thing an instruction.

#define Trap(t)     do {if (t) __asm__("int $0x3");} while (0)
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254