Skip to Content
DocumentationAPICall Stack Management

Call Stack Management

In order to obtain the traceback in run time, we need to actively manage the call stack. Unfortunately, C doesn’t provide an easy way to do this. Therefore, we will have to wrap function calls or code blocks with a macro. While this approach can be quite verbose, it is what makes our library possible.

Do not use jump statements to jump outside macro, including break, continue, return and goto.

API

ctb_clear_context Function

Clear the C Traceback context (including errors and call stack).

void ctb_clear_context(void);
If you are writing a library, you can call this at API entry to clear any remaining context.

TRACE Macro

Wrapper macro for expression to automatically manage call stack frames without checking for errors.

void TRACE(expr)

Always use TRY instead of TRACE when error handling matters. It is easy to miss errors as TRACE are not checking for errors stored in context.

Parameters

NameTypeDescription
exprN/AThe expression to be traced.

Expands to

do \ { \ ctb_push_call_stack_frame(__FILE__, __func__, __LINE__, #expr); \ (expr); \ ctb_pop_call_stack_frame(__FILE__, __func__, __LINE__, #expr); \ } while (0)

Usage

int function_a(void) { TRACE(function_b()); // Trace a function call } int function_b(void) { int i = 0; TRACE(i = 2); // Trace any expression }

TRACE_BLOCK Macro

Wrapper macro for a code block to automatically manage call stack frames without checking for errors.

void TRACE_BLOCK(...)

Always call ctb_check_error after TRACE_BLOCK when error handling matters. It is easy to miss errors as TRACE_BLOCK are not checking for errors stored in context.

Parameters

NameTypeDescription
...N/AThe block of code to be traced.

Expands to

do \ { \ ctb_push_call_stack_frame(__FILE__, __func__, __LINE__, #__VA_ARGS__); \ __VA_ARGS__ \ ctb_pop_call_stack_frame(__FILE__, __func__, __LINE__, #__VA_ARGS__); \ } while (0)

Usage

int function_a(void) { /* Trace a code block */ TRACE_BLOCK( int *ptr; printf("%d", *ptr); // Oops, segmentation fault ); // <-- Don't forget the semi-colon // Note: i is not accessible here // printf("%d", i); <-- Illegal! }

TRY Macro

Wrapper macro for expression to automatically manage call stack frames and check for errors.

bool TRY(expr)

Parameters

NameTypeDescription
exprN/AThe expression to be traced.

Returns

NameTypeDescription
successboolWhether the expression executed without error.

Expands to

(ctb_push_call_stack_frame(__FILE__, __func__, __LINE__, #expr), \ (expr), \ ctb_pop_call_stack_frame(__FILE__, __func__, __LINE__, #expr), \ !ctb_check_error())

Usage

int function_a(void) { bool success = TRY(function_b()); // Try a function call } int function_b(void) { int i = 0; (void) TRY(i = 2); // Try any expression }

TRY_GOTO Macro

Wrapper for an expression. If an error occurs after the expression, jump to label.

void TRY_GOTO(expr, label)

Parameters

NameTypeDescription
exprN/AThe expression to be traced.
labelN/AThe label to jump to on error.

Expands to

do \ { \ ctb_push_call_stack_frame(__FILE__, __func__, __LINE__, #expr); \ (expr); \ ctb_pop_call_stack_frame(__FILE__, __func__, __LINE__, #expr); \ if (ctb_check_error()) \ { \ goto label; \ } \ } while (0)

Usage

int function_a(void) { TRY_GOTO(function_b(), error); // Trace a function call /* Do something */ return 0; error: return 1; }

TRY_BLOCK_GOTO Macro

Wrapper for a block of code. If an error occurs after the block executes, jump to label.

void TRY_BLOCK_GOTO(label, ...)

Parameters

NameTypeDescription
labelN/AThe label to jump to on error.
...N/AThe block of code to be traced.

Expands to

do \ { \ ctb_push_call_stack_frame(__FILE__, __func__, __LINE__, #__VA_ARGS__); \ __VA_ARGS__ \ ctb_pop_call_stack_frame(__FILE__, __func__, __LINE__, #__VA_ARGS__); \ if (ctb_check_error()) \ { \ goto label; \ } \ } while (0)

Usage

int function_a(void) { /* Trace a code block */ TRY_BLOCK_GOTO( error, int *ptr; printf("%d", *ptr); // Oops, segmentation fault ); // <-- Don't forget the semi-colon // Note: i is not accessible here // printf("%d", i); <-- Illegal! return 0; error: return 1; }
Last updated on