Skip to content

Commit

Permalink
ledger_assert: Add support for printf like message and add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Xavier Chapron authored and xchapron-ledger committed Jan 24, 2024
1 parent d345ef6 commit 9ba66ce
Show file tree
Hide file tree
Showing 3 changed files with 351 additions and 198 deletions.
249 changes: 92 additions & 157 deletions include/ledger_assert.h
Original file line number Diff line number Diff line change
@@ -1,160 +1,95 @@
#pragma once

#include <stdbool.h>

#ifdef HAVE_PRINTF
void assert_print_failed(void);
#endif

#ifdef LEDGER_ASSERT_CONFIG_FILE_INFO
#define LEDGER_ASSERT_CONFIG_MESSAGE_INFO 1
#define LEDGER_ASSERT_CONFIG_LR_AND_PC_INFO 1
#endif

#ifdef LEDGER_ASSERT_CONFIG_MESSAGE_INFO
#define LEDGER_ASSERT_CONFIG_LR_AND_PC_INFO 1
#endif

#if defined(LEDGER_ASSERT_CONFIG_LR_AND_PC_INFO) && defined(HAVE_LEDGER_ASSERT_DISPLAY)
#define LR_AND_PC_SIZE 30
void assert_display_lr_and_pc(int lr, int pc);
#define ASSERT_DISPLAY_LR_AND_PC(lr, pc) assert_display_lr_and_pc(lr, pc)
#else
#define LR_AND_PC_SIZE 0
#define ASSERT_DISPLAY_LR_AND_PC(lr, pc) \
do { \
} while (0)
#endif

#if defined(LEDGER_ASSERT_CONFIG_MESSAGE_INFO) && defined(HAVE_LEDGER_ASSERT_DISPLAY)
#define MESSAGE_SIZE 20
void assert_display_message(const char *message);
#define ASSERT_DISPLAY_MESSAGE(message) assert_display_message(message)
#else
#define MESSAGE_SIZE 0
#define ASSERT_DISPLAY_MESSAGE(message) \
do { \
} while (0)
#endif

#if defined(LEDGER_ASSERT_CONFIG_FILE_INFO) && defined(HAVE_LEDGER_ASSERT_DISPLAY)
#define FILE_SIZE 50
void assert_display_file_info(const char *file, unsigned int line);
#define ASSERT_DISPLAY_FILE_INFO(file, line) assert_display_file_info(file, line)
#else
#define FILE_SIZE 0
#define ASSERT_DISPLAY_FILE_INFO(file, line) \
do { \
} while (0)
#endif

#ifdef HAVE_LEDGER_ASSERT_DISPLAY
#define ASSERT_BUFFER_LEN LR_AND_PC_SIZE + MESSAGE_SIZE + FILE_SIZE
void __attribute__((noreturn)) assert_display_exit(void);

#define LEDGER_ASSERT_EXIT() assert_display_exit()
#else
void assert_exit(bool confirm);
#define LEDGER_ASSERT_EXIT() assert_exit(true)
#endif

#if defined(LEDGER_ASSERT_CONFIG_LR_AND_PC_INFO) && defined(HAVE_PRINTF)
void assert_print_lr_and_pc(int lr, int pc);
#define ASSERT_PRINT_LR_AND_PC(lr, pc) assert_print_lr_and_pc(lr, pc)
#else
#define ASSERT_PRINT_LR_AND_PC(lr, pc) \
do { \
} while (0)
#endif

#if defined(LEDGER_ASSERT_CONFIG_MESSAGE_INFO) && defined(HAVE_PRINTF)
void assert_print_message(const char *message);
#define ASSERT_PRINT_MESSAGE(message) assert_print_message(message)
#else
#define ASSERT_PRINT_MESSAGE(message) \
do { \
} while (0)
#endif

#if defined(LEDGER_ASSERT_CONFIG_FILE_INFO) && defined(HAVE_PRINTF)
void assert_print_file_info(const char *file, int line);
#define ASSERT_PRINT_FILE_INFO(file, line) assert_print_file_info(file, line)
#else
#define ASSERT_PRINT_FILE_INFO(file, line) \
do { \
} while (0)
#endif

#ifdef LEDGER_ASSERT_CONFIG_LR_AND_PC_INFO
#define LEDGER_ASSERT_LR_AND_PC() \
do { \
int _lr_address = 0; \
int _pc_address = 0; \
\
__asm volatile("mov %0, lr" : "=r"(_lr_address)); \
__asm volatile("mov %0, pc" : "=r"(_pc_address)); \
ASSERT_PRINT_LR_AND_PC(_lr_address, _pc_address); \
ASSERT_DISPLAY_LR_AND_PC(_lr_address, _pc_address); \
} while (0)
#elif defined(HAVE_PRINTF)
#define LEDGER_ASSERT_LR_AND_PC() assert_print_failed()
#else
#define LEDGER_ASSERT_LR_AND_PC() \
do { \
} while (0)
#endif

#ifdef LEDGER_ASSERT_CONFIG_MESSAGE_INFO
#define LEDGER_ASSERT_MESSAGE(message) \
do { \
ASSERT_PRINT_MESSAGE(message); \
ASSERT_DISPLAY_MESSAGE(message); \
} while (0)
#else
#define LEDGER_ASSERT_MESSAGE(message) \
do { \
} while (0)
#endif

#ifdef LEDGER_ASSERT_CONFIG_FILE_INFO
#define LEDGER_ASSERT_FILE_INFO() \
do { \
ASSERT_PRINT_FILE_INFO(__FILE__, __LINE__); \
ASSERT_DISPLAY_FILE_INFO(__FILE__, __LINE__); \
} while (0)
#else
#define LEDGER_ASSERT_FILE_INFO() \
do { \
} while (0)
#endif

#define LEDGER_ASSERT(test, message) \
do { \
if (!(test)) { \
LEDGER_ASSERT_LR_AND_PC(); \
LEDGER_ASSERT_MESSAGE(message); \
LEDGER_ASSERT_FILE_INFO(); \
LEDGER_ASSERT_EXIT(); \
} \
} while (0)

#if defined(HAVE_DEBUG_THROWS) && defined(HAVE_PRINTF)
void throw_print_lr(int e, int lr);
#define THROW_PRINT_LR(e, lr_val) throw_print_lr(e, lr_val)
#else
#define THROW_PRINT_LR(e, lr_val) \
do { \
} while (0)
#endif

#if defined(HAVE_DEBUG_THROWS)
void __attribute__((noreturn)) assert_display_exit(void);
void throw_display_lr(int e, int lr);
#define DEBUG_THROW(e) \
do { \
unsigned int lr_val; \
__asm volatile("mov %0, lr" : "=r"(lr_val)); \
throw_display_lr(e, lr_val); \
THROW_PRINT_LR(e, lr_val); \
#include "ledger_assert_internals.h"

/*****************
* LEDGER_ASSERT *
****************/

/**
* LEDGER_ASSERT - exit the app if assertion is false.
*
* Description:
* This is a C macro that can be used similarly of the libc `assert` macro.
* Therefore, it can help programmers find bugs in their programs, or handle
* exceptional cases via a crash that will produce limited debugging output.
*
*
* Important note:
* Note that contrary to the libc `assert`, the `LEDGER_ASSERT` macro will
* still end the app execution even if build with DEBUG=0 or NDEBUG.
* However, there are configurations explained below that shall be used to
* reduce the code size impact of the `LEDGER_ASSERT` macro to the bare minimum.
*
*
* Examples of usage:
*
* 1/ Simple example always raising with simple message:
* - `LEDGER_ASSERT(false, "Always assert");`
*
* 2/ More complex examples:
* - `LEDGER_ASSERT(len <= sizeof(buf), "Len too long");`
* - `LEDGER_ASSERT(check_value(value) == 0, "check_value failed");`
*
* 3/ Examples showcasing advance message format:
* - `LEDGER_ASSERT(len <= sizeof(buf), "Len too long %d <= %d", len, sizeof(buf));`
* - `int err = check_value(value); LEDGER_ASSERT(err == 0, "check_value failed (%d)", err);`
*
*
* Configuration:
*
* I/ Type of info to expose in PRINTF and screen if enabled.
*
* There are 4 types of information that can be displayed:
*
* 1/ A simple info displaying "LEDGER_ASSERT FAILED"
* => This is always enabled
*
* 2/ An info containing the PC and LR of the instruction that failed.
* This can be used to locate the failing code and flow using the following
* command: `arm-none-eabi-addr2line --exe bin/app.elf -pf 0xC0DE586B`
* => This is enabled when `LEDGER_ASSERT_CONFIG_LR_AND_PC_INFO` is defined.
*
* 3/ An info displaying the `LEDGER_ASSERT` message passed as parameter.
* => This is enabled when `LEDGER_ASSERT_CONFIG_MESSAGE_INFO` is defined.
*
* 4/ An info displaying the file and line position of the failing
* `LEDGER_ASSERT`.
* => This is enabled when `LEDGER_ASSERT_CONFIG_FILE_INFO` is defined.
*
* By default, when an info level X is enabled, all info level below are enabled.
*
* When using `Makefile.standard_app` and building with `DEBUG=1` all info are
* enabled.
*
* Note that enabling these info increase the app code size and is only
* recommended to ease the debugging.
*
*
* II/ Display info on device screen.
*
* Control whether or not a specific screen displaying assert info is shown
* to the app user before exiting the app due to an assert failure.
*
* To enable it, add `DEFINES+=HAVE_LEDGER_ASSERT_DISPLAY` in your app Makefile.
*
* This is enabled by default when using `Makefile.standard_app` and building
* with `DEBUG=1`. It can still be disabled with using
* `DISABLE_DEBUG_LEDGER_ASSERT=1`.
*
* Note that this is increasing the app code size and exposes a non-user
* friendly screen in case of assert. therefore this should not be enabled in
* app release build but only kept for debug purposes.
*
*/
#define LEDGER_ASSERT(test, format, ...) \
do { \
if (!(test)) { \
LEDGER_ASSERT_LR_AND_PC_PREAMBLE(); \
PRINTF("LEDGER_ASSERT FAILED\n"); \
LEDGER_ASSERT_MESSAGE(format, ##__VA_ARGS__); \
LEDGER_ASSERT_FILE_INFO(); \
LEDGER_ASSERT_LR_AND_PC(); \
LEDGER_ASSERT_EXIT(); \
} \
} while (0)
#endif
Loading

0 comments on commit 9ba66ce

Please sign in to comment.