Skip to content
This repository has been archived by the owner on Jan 7, 2019. It is now read-only.

Commit

Permalink
[assert] Improve implementation, add asserts.
Browse files Browse the repository at this point in the history
- Make header C-compatible so asserts can be called from C.
- Specialize `assert_fail` function for context value.
- Return condition from `xpcc_assert` for error handling.
- Remove `exit()` calls from implementation.
- Add assertions to core.
- Update F469-DISCO assert example.
  • Loading branch information
salkinium committed May 7, 2017
2 parents e9591d5 + 0ce8983 commit 3992534
Show file tree
Hide file tree
Showing 24 changed files with 490 additions and 173 deletions.
75 changes: 42 additions & 33 deletions examples/stm32f469_discovery/assert/main.cpp
Original file line number Diff line number Diff line change
@@ -1,59 +1,68 @@
#include <xpcc/architecture/platform.hpp>

#define XPCC_CAN_MODULE_NAME "can"
#define XPCC_IOBUFFER_MODULE_NAME "iobuffer"
#define XPCC_UART_MODULE_NAME "uart"

using namespace Board;

extern "C" void xpcc_abandon(const char * module,
const char * location,
const char * failure,
uintptr_t context)
{
XPCC_LOG_ERROR << "Assertion '"
<< module << "." << location << "." << failure
<< "' @ " << (void *) context
<< " failed! Abandoning..." << xpcc::endl;

LedGreen::setOutput();
while(1) {
LedBlue::set();
xpcc::delayMilliseconds(20);
LedBlue::reset();
xpcc::delayMilliseconds(180);
}
}

static xpcc::Abandonment
test_assertion_handler(const char * module,
const char * /* location */,
const char * /* failure */,
uintptr_t /* context */)
{
if (strcmp(module, XPCC_IOBUFFER_MODULE_NAME) == 0)
if (!strcmp(module, "iobuffer")) {
XPCC_LOG_ERROR << "Ignoring iobuffer full!" << xpcc::endl;
return xpcc::Abandonment::Ignore;
}
return xpcc::Abandonment::DontCare;
}
XPCC_ASSERTION_HANDLER(test_assertion_handler);

static xpcc::Abandonment
core_assertion_handler(const char * module,
const char * /* location */,
const char * failure,
uintptr_t context)
{
if (!memcmp(module, "core\0nvic\0undefined", 19)) {
XPCC_LOG_ERROR.printf("Ignoring undefined IRQ handler %d!\n", context);
return xpcc::Abandonment::Ignore;
}
if (!memcmp(module, "core\0heap", 9)) {
XPCC_LOG_ERROR.printf("Ignoring 'core.heap.%s' of size 0x%x!\n", failure, context);
return xpcc::Abandonment::Ignore;
}
return xpcc::Abandonment::DontCare;
}
XPCC_ASSERTION_HANDLER(core_assertion_handler);

// ----------------------------------------------------------------------------
int
main()
{
Board::initialize();

xpcc_assert(true, XPCC_CAN_MODULE_NAME, "init", "timeout");
// trigger an IRQ with undefined handler
NVIC_EnableIRQ(RTC_Alarm_IRQn);
NVIC_SetPendingIRQ(RTC_Alarm_IRQn);

xpcc_assert_debug(false, XPCC_IOBUFFER_MODULE_NAME, "tx", "full");
// trigger an out of memory
// we definitely don't have 32MB RAM on this board
// returns NULL, asserts in debug mode
volatile void * ptr = malloc(1 << 25);
// returns NULL, asserts in debug mode
ptr = new (std::nothrow) uint8_t[1 << 25];
// always asserts
ptr = new uint8_t[1 << 25];
(void) ptr;

xpcc_assert(false, XPCC_UART_MODULE_NAME, "init", "mode");
// does not fail, should not be optimized away
volatile bool true_condition = true;
xpcc_assert(true_condition, "can", "init", "timeout");

while(1)
{
LedRed::toggle();
xpcc::delayMilliseconds(500);
}
// only fails for debug builds, but is ignored anyways
xpcc_assert_debug(false, "iobuffer", "tx", "full");

return 0;
// "accidentally" return from main, without even returning properly!
// This should be cought by the debug assert core.main.exit!
// while(1) ;
// return 0;
}
3 changes: 3 additions & 0 deletions examples/stm32f469_discovery/assert/project.cfg
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[build]
board = stm32f469_discovery
buildpath = ${xpccpath}/build/stm32f469_discovery/${name}

[defines]
XPCC_DEBUG_BUILD=1
74 changes: 74 additions & 0 deletions src/xpcc/architecture/interface/assert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// coding: utf-8
/* Copyright (c) 2016-2017, Niklas Hauser
* All Rights Reserved.
*
* The file is part of the xpcc library and is released under the 3-clause BSD
* license. See the file `LICENSE` for the full license governing this code.
*/
// ----------------------------------------------------------------------------

#ifndef XPCC_ASSERT_H
#define XPCC_ASSERT_H

#if defined(__cplusplus) && !defined(XPCC_ASSERT_HPP)
# error "Don't include this file directly, use 'assert.hpp' instead!"
#endif

#include <stdint.h>
#include <stdbool.h>
#include <xpcc/architecture/utils.hpp>

#ifndef __DOXYGEN__

// FIXME: <xpcc/architecture/driver/flash.hpp> is not C compatible!
#ifdef XPCC__CPU_AVR
# include <avr/pgmspace.h>
# define xpcc_ifss(s) PSTR(s)
#else
# define xpcc_ifss(s) ((const char *)(s))
#endif

#define xpcc_assert4(condition, module, location, failure) \
({ \
bool xpcc_assert_evaluated_condition = (bool) (condition); \
if (!xpcc_assert_evaluated_condition) { \
xpcc_assert_fail(xpcc_ifss(module "\0" location "\0" failure)); } \
xpcc_assert_evaluated_condition; \
})


#define xpcc_assert5(condition, module, location, failure, context) \
({ \
bool xpcc_assert_evaluated_condition = (bool) (condition); \
if (!xpcc_assert_evaluated_condition) { \
xpcc_assert_fail_context(xpcc_ifss(module "\0" location "\0" failure), (uintptr_t) context); } \
xpcc_assert_evaluated_condition; \
})

#define xpcc_assert_get_macro(_1,_2,_3,_4,_5,foo,...) foo
#define xpcc_assert(...) \
xpcc_assert_get_macro(__VA_ARGS__, xpcc_assert5, xpcc_assert4)(__VA_ARGS__)

#ifdef XPCC_DEBUG_BUILD
#define xpcc_assert_debug(...) \
xpcc_assert_get_macro(__VA_ARGS__, xpcc_assert5, xpcc_assert4)(__VA_ARGS__)
#else
# define xpcc_assert_debug(condition, ...) \
({ \
bool xpcc_unused xpcc_assert_evaluated_condition = (bool) (condition); \
xpcc_assert_evaluated_condition; \
})
#endif

xpcc_extern_c void
xpcc_assert_fail_context(const char * identifier, uintptr_t context);

xpcc_extern_c void
xpcc_assert_fail(const char * identifier);

xpcc_extern_c void
xpcc_abandon(const char * module, const char * location, const char * failure, uintptr_t context);

#endif // __DOXYGEN__

#endif // XPCC_ASSERT_H
50 changes: 19 additions & 31 deletions src/xpcc/architecture/interface/assert.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// coding: utf-8
/* Copyright (c) 2016, Roboterclub Aachen e.V.
/* Copyright (c) 2016-2017, Niklas Hauser
* All Rights Reserved.
*
* The file is part of the xpcc library and is released under the 3-clause BSD
Expand All @@ -13,7 +13,6 @@
#include <stdint.h>
#include <xpcc/architecture/utils.hpp>
#include <xpcc/utils/bit_constants.hpp>
#include <xpcc/architecture/driver/accessor/flash.hpp>

/**
* @ingroup interface
Expand Down Expand Up @@ -106,42 +105,50 @@ using AssertionHandler = Abandonment (*)(const char * module,
/**
* Assert a condition to be true with failure identifier.
* This assert is always included in the source code.
* @returns result of condition evaluation
*
* @note On AVR targets the failure identifier string is placed in Flash memory!
*
* @ingroup assert
*/
#define xpcc_assert(condition, module, location, failure)
xpcc_extern_c bool
xpcc_assert(bool condition, const char * module, const char * location, const char * failure);

/**
* Assert a condition to be true with failure identifier and context.
* This assert is always included in the source code.
* @returns result of condition evaluation
*
* @note On AVR targets the failure identifier string is placed in Flash memory!
*
* @ingroup assert
*/
#define xpcc_assert(condition, module, location, failure, context)
xpcc_extern_c bool
xpcc_assert(bool condition, const char * module, const char * location, const char * failure, uintptr_t context);

/**
* Assert a condition to be true with failure identifier.
* This assert is only included in the source code on debug builds!
* This assert is only triggered in the source code on debug builds!
* @returns result of condition evaluation
*
* @note On AVR targets the strings are placed in Flash memory!
*
* @ingroup assert
*/
#define xpcc_assert_debug(condition, module, location, failure)
xpcc_extern_c bool
xpcc_assert_debug(bool condition, const char * module, const char * location, const char * failure);

/**
* Assert a condition to be true with failure identifier and context.
* This assert is only included in the source code on debug builds!
* This assert is only triggered in the source code on debug builds!
* @returns result of condition evaluation
*
* @note On AVR targets the strings are placed in Flash memory!
*
* @ingroup assert
*/
#define xpcc_assert_debug(condition, module, location, failure, context)
xpcc_extern_c bool
xpcc_assert_debug(bool condition, const char * module, const char * location, const char * failure, uintptr_t context);

/**
* Overwriteable abandonment handler for all targets.
Expand All @@ -151,7 +158,7 @@ using AssertionHandler = Abandonment (*)(const char * module,
*
* @ingroup assert
*/
extern "C" void
xpcc_extern_c void
xpcc_abandon(const char * module,
const char * location,
const char * failure,
Expand All @@ -173,37 +180,18 @@ xpcc_abandon(const char * module,
const xpcc::AssertionHandler \
handler ## _assertion_handler_ptr = handler
#else
# warning "XPCC_ASSERTION_HANDLER(handler) ignored, due to missing linker section definition!"
# define XPCC_ASSERTION_HANDLER(handler)
#endif

#define xpcc_assert4(condition, module, location, failure) \
xpcc_assert5(condition, module, location, failure, 0)

#define xpcc_assert5(condition, module, location, failure, context) \
if ((bool) (condition)) {} else { \
xpcc_assert_fail(INLINE_FLASH_STORAGE_STRING(module "\0" location "\0" failure), (uintptr_t) context); }

#define xpcc_assert_get_macro(_1,_2,_3,_4,_5,foo,...) foo
#define xpcc_assert(...) \
xpcc_assert_get_macro(__VA_ARGS__, xpcc_assert5, xpcc_assert4)(__VA_ARGS__)

#ifndef NDEBUG
#define xpcc_assert_debug(...) \
xpcc_assert_get_macro(__VA_ARGS__, xpcc_assert5, xpcc_assert4)(__VA_ARGS__)
#ifdef XPCC_DEBUG_BUILD
# define XPCC_ASSERTION_HANDLER_DEBUG(handler) \
XPCC_ASSERTION_HANDLER(handler)
#else
# define xpcc_assert_debug(...)
# define XPCC_ASSERTION_HANDLER_DEBUG(handler)
#endif

extern "C" {

void xpcc_assert_fail(const char * identifier, uintptr_t context);

void xpcc_abandon(const char * module, const char * location, const char * failure, uintptr_t context);

}
#include "assert.h"

#endif // __DOXYGEN__

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,26 @@ xpcc::log::Logger xpcc::log::debug(loggerDevice);
xpcc::log::Logger xpcc::log::info(loggerDevice);
xpcc::log::Logger xpcc::log::warning(loggerDevice);
xpcc::log::Logger xpcc::log::error(loggerDevice);

xpcc_extern_c void
xpcc_abandon(const char * module,
const char * location,
const char * failure,
uintptr_t context)
{
XPCC_LOG_ERROR << "Assertion '" << module << "." << location << "." << failure << "'";
if (context) { XPCC_LOG_ERROR << " @ " << (void *) context << " (" << (uint32_t) context << ")"; }
XPCC_LOG_ERROR << " failed! Abandoning..." << xpcc::endl;

// Since LedD13 is also a GPIO pin, we don't force this pin to output,
// in case something sensitive is connected to this pin.
// The user must "enable" the use of this pin as an LED output, by
// explicitly setting the pin to output in the application.
// Board::LedD13::setOutput();
while(1) {
Board::LedD13::set();
xpcc::delayMilliseconds(20);
Board::LedD13::reset();
xpcc::delayMilliseconds(180);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,26 @@ xpcc::log::Logger xpcc::log::debug(loggerDevice);
xpcc::log::Logger xpcc::log::info(loggerDevice);
xpcc::log::Logger xpcc::log::warning(loggerDevice);
xpcc::log::Logger xpcc::log::error(loggerDevice);

xpcc_extern_c void
xpcc_abandon(const char * module,
const char * location,
const char * failure,
uintptr_t context)
{
XPCC_LOG_ERROR << "Assertion '" << module << "." << location << "." << failure << "'";
if (context) { XPCC_LOG_ERROR << " @ " << (void *) context << " (" << (uint32_t) context << ")"; }
XPCC_LOG_ERROR << " failed! Abandoning..." << xpcc::endl;

// Since LedD13 is also a GPIO pin, we don't force this pin to output,
// in case something sensitive is connected to this pin.
// The user must "enable" the use of this pin as an LED output, by
// explicitly setting the pin to output in the application.
// Board::LedD13::setOutput();
while(1) {
Board::LedD13::set();
xpcc::delayMilliseconds(20);
Board::LedD13::reset();
xpcc::delayMilliseconds(180);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,26 @@ xpcc::log::Logger xpcc::log::debug(loggerDevice);
xpcc::log::Logger xpcc::log::info(loggerDevice);
xpcc::log::Logger xpcc::log::warning(loggerDevice);
xpcc::log::Logger xpcc::log::error(loggerDevice);

xpcc_extern_c void
xpcc_abandon(const char * module,
const char * location,
const char * failure,
uintptr_t context)
{
XPCC_LOG_ERROR << "Assertion '" << module << "." << location << "." << failure << "'";
if (context) { XPCC_LOG_ERROR << " @ " << (void *) context << " (" << (uint32_t) context << ")"; }
XPCC_LOG_ERROR << " failed! Abandoning..." << xpcc::endl;

// Since LedD13 is also a GPIO pin, we don't force this pin to output,
// in case something sensitive is connected to this pin.
// The user must "enable" the use of this pin as an LED output, by
// explicitly setting the pin to output in the application.
// Board::LedD13::setOutput();
while(1) {
Board::LedD13::set();
xpcc::delayMilliseconds(20);
Board::LedD13::reset();
xpcc::delayMilliseconds(180);
}
}
Loading

0 comments on commit 3992534

Please sign in to comment.