From 61c0637ef60bd88f3f7e8156ec5b6a70bdb0a776 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 18 Mar 2020 08:03:10 +0100 Subject: [PATCH 1/4] [architecture] New assertion interface and implementation --- src/modm/architecture/interface/assert.h | 73 ----- src/modm/architecture/interface/assert.h.in | 87 ++++++ src/modm/architecture/interface/assert.hpp | 152 ---------- src/modm/architecture/interface/assert.hpp.in | 177 +++++++++++ src/modm/architecture/interface/assert.md | 283 +++++++++++++++--- src/modm/architecture/module.lb | 22 +- src/modm/architecture/utils.hpp | 2 + src/modm/platform/core/avr/assert.cpp | 56 ---- .../core/avr/linkerscript/linkerscript.ld | 4 +- src/modm/platform/core/avr/module.lb | 9 +- src/modm/platform/core/cortex/assert.cpp | 66 ---- src/modm/platform/core/cortex/assert.cpp.in | 98 ++++++ src/modm/platform/core/cortex/assert.hpp.in | 24 ++ src/modm/platform/core/cortex/delay.cpp.in | 4 +- src/modm/platform/core/cortex/module.lb | 10 +- src/modm/platform/core/cortex/startup.c.in | 4 +- src/modm/platform/core/hosted/assert.cpp.in | 84 ------ src/modm/platform/core/hosted/module.lb | 10 +- 18 files changed, 668 insertions(+), 497 deletions(-) delete mode 100644 src/modm/architecture/interface/assert.h create mode 100644 src/modm/architecture/interface/assert.h.in delete mode 100644 src/modm/architecture/interface/assert.hpp create mode 100644 src/modm/architecture/interface/assert.hpp.in delete mode 100644 src/modm/platform/core/avr/assert.cpp delete mode 100644 src/modm/platform/core/cortex/assert.cpp create mode 100644 src/modm/platform/core/cortex/assert.cpp.in create mode 100644 src/modm/platform/core/cortex/assert.hpp.in delete mode 100644 src/modm/platform/core/hosted/assert.cpp.in diff --git a/src/modm/architecture/interface/assert.h b/src/modm/architecture/interface/assert.h deleted file mode 100644 index cadaba34ae..0000000000 --- a/src/modm/architecture/interface/assert.h +++ /dev/null @@ -1,73 +0,0 @@ -// coding: utf-8 -/* - * Copyright (c) 2017, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_ASSERT_H -#define MODM_ASSERT_H - -#include -#include -#include - -#ifndef __DOXYGEN__ - -// FIXME: is not C compatible! -#ifdef MODM_CPU_AVR -# include -# define modm_ifss(s) PSTR(s) -#else -# define modm_ifss(s) ((const char *)(s)) -#endif - -#define modm_assert4(condition, module, location, failure) \ - ({ \ - bool modm_assert_evaluated_condition = (bool) (condition); \ - if (!modm_assert_evaluated_condition) { \ - modm_assert_fail(modm_ifss(module "\0" location "\0" failure)); } \ - modm_assert_evaluated_condition; \ - }) - - -#define modm_assert5(condition, module, location, failure, context) \ - ({ \ - bool modm_assert_evaluated_condition = (bool) (condition); \ - if (!modm_assert_evaluated_condition) { \ - modm_assert_fail_context(modm_ifss(module "\0" location "\0" failure), (uintptr_t) context); } \ - modm_assert_evaluated_condition; \ - }) - -#define modm_assert_get_macro(_1,_2,_3,_4,_5,foo,...) foo -#define modm_assert(...) \ - modm_assert_get_macro(__VA_ARGS__, modm_assert5, modm_assert4)(__VA_ARGS__) - -#ifdef MODM_DEBUG_BUILD -#define modm_assert_debug(...) \ - modm_assert_get_macro(__VA_ARGS__, modm_assert5, modm_assert4)(__VA_ARGS__) -#else -# define modm_assert_debug(condition, ...) \ - ({ \ - bool modm_unused modm_assert_evaluated_condition = (bool) (condition); \ - modm_assert_evaluated_condition; \ - }) -#endif - -modm_extern_c void -modm_assert_fail_context(const char * identifier, uintptr_t context); - -modm_extern_c void -modm_assert_fail(const char * identifier); - -modm_extern_c void -modm_abandon(const char * module, const char * location, const char * failure, uintptr_t context); - -#endif // __DOXYGEN__ - -#endif // MODM_ASSERT_H diff --git a/src/modm/architecture/interface/assert.h.in b/src/modm/architecture/interface/assert.h.in new file mode 100644 index 0000000000..5d7a670050 --- /dev/null +++ b/src/modm/architecture/interface/assert.h.in @@ -0,0 +1,87 @@ +// coding: utf-8 +/* + * Copyright (c) 2017, 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include + +/// @cond +#define MODM_ASSERTION_INFO_HAS_DESCRIPTION {{ 1 if options.with_description else 0 }} + +// C-type information struct +typedef struct modm_packed +{ + const char *name; +%% if options.with_description + const char *description; +%% endif + uintptr_t context; + uint8_t behavior; +} _modm_assertion_info; + +// modm_assert* helper macros +%# FIXME: is not C compatible! +%% if core.startswith("avr") +#include +#define _modm_assert_ifss(s) PSTR((s)) +%% else +#define _modm_assert_ifss(s) ((const char *)(s)) +%% endif + +#define _modm_assert5(behavior, condition, name, description, context) \ + ({ \ + const bool evaluated_condition = (bool) (condition); \ + if (!evaluated_condition) { \ + _modm_assertion_info info = { \ + _modm_assert_ifss(name),{% if options.with_description %} _modm_assert_ifss(description),{% endif %} \ + (uintptr_t)(context), (uint8_t)(behavior)}; \ + modm_assert_report(&info); \ + if (behavior & 4) __builtin_unreachable(); \ + } \ + evaluated_condition; \ + }) +#define _modm_assert4(behavior, condition, name, description) \ + _modm_assert5(behavior, condition, name, description, -1) + +#define _modm_assert_get_macro(_1,_2,_3,_4,_modm_assertN,...) _modm_assertN + + +// modm_assert* definitions +#define modm_assert(...) \ + _modm_assert_get_macro(__VA_ARGS__, _modm_assert5, _modm_assert4)(0x04, __VA_ARGS__) +#define modm_assert_continue_ignore(...) \ + _modm_assert_get_macro(__VA_ARGS__, _modm_assert5, _modm_assert4)(0x02, __VA_ARGS__) +#define modm_assert_continue_fail(...) \ + _modm_assert_get_macro(__VA_ARGS__, _modm_assert5, _modm_assert4)(0x01, __VA_ARGS__) + +#ifdef MODM_DEBUG_BUILD + +#define modm_assert_continue_ignore_debug(...) \ + _modm_assert_get_macro(__VA_ARGS__, _modm_assert5, _modm_assert4)(0x82, __VA_ARGS__) +#define modm_assert_continue_fail_debug(...) \ + _modm_assert_get_macro(__VA_ARGS__, _modm_assert5, _modm_assert4)(0x81, __VA_ARGS__) + +#else + +#define modm_assert_continue_ignore_debug(condition, ...) \ + ({ bool modm_unused evaluated_condition = (bool)(condition); evaluated_condition; }) +#define modm_assert_continue_fail_debug(condition, ...) \ + modm_assert_continue_ignore_debug(condition) + +#endif + +// required runtime implementation +modm_extern_c void +modm_assert_report(_modm_assertion_info *info); +/// @endcond diff --git a/src/modm/architecture/interface/assert.hpp b/src/modm/architecture/interface/assert.hpp deleted file mode 100644 index 4f6a663b21..0000000000 --- a/src/modm/architecture/interface/assert.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2016-2018, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_ASSERT_HPP -#define MODM_ASSERT_HPP - -#include -#include -#include -#include - - -namespace modm -{ - -/// Describes abandonment type of assertions. -/// @ingroup modm_architecture_assert -enum class -Abandonment : uint8_t -{ - DontCare = Bit0, ///< Do not care about failure. - Ignore = Bit1, ///< Safe to ignore this failure. - Fail = Bit2 ///< This failure is reason for abandonment. -}; - -/// Signature of the assertion handlers -/// @ingroup modm_architecture_assert -using AssertionHandler = Abandonment (*)(const char * module, - const char * location, - const char * failure, - uintptr_t context); - -} // namespace modm - -#ifdef __DOXYGEN__ - -/** - * This adds a function to the list of assertion handlers to execute on - * assertion failure. Note that this macro does not give you any influence - * over the order of handler execution on assertion failure. - * Do not write assertion handlers that depend on any ordered execution! - * - * @warning On AVR targets the failure identifier resides in Flash memory! - * - * @param handler A function of signature `AssertionHandler`. - * - * @ingroup modm_architecture_assert - */ -#define MODM_ASSERTION_HANDLER(handler) - -/** - * 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 modm_architecture_assert - */ -modm_extern_c bool -modm_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 modm_architecture_assert - */ -modm_extern_c bool -modm_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 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 modm_architecture_assert - */ -modm_extern_c bool -modm_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 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 modm_architecture_assert - */ -modm_extern_c bool -modm_assert_debug(bool condition, const char * module, const char * location, const char * failure, uintptr_t context); - -/** - * Overwriteable abandonment handler for all targets. - * - * You should overwrite this handler for custom failure behaviour like blinking - * LEDs and outputting the failure string via a serial connection. - * - * @ingroup modm_architecture_assert - */ -modm_extern_c void -modm_abandon(const char * module, - const char * location, - const char * failure, - uintptr_t context) modm_weak; - -#else - -#if defined MODM_CPU_ARM || defined MODM_CPU_AVR -# define MODM_ASSERTION_LINKER_SECTION ".assertion" -#elif defined MODM_OS_OSX -# define MODM_ASSERTION_LINKER_SECTION "__DATA,modm_assertion" -#elif defined MODM_OS_LINUX -# define MODM_ASSERTION_LINKER_SECTION "modm_assertion" -#endif - -#ifdef MODM_ASSERTION_LINKER_SECTION -# define MODM_ASSERTION_HANDLER(handler) \ - __attribute__((section(MODM_ASSERTION_LINKER_SECTION), used)) \ - const modm::AssertionHandler \ - handler ## _assertion_handler_ptr = handler -#else -# warning "MODM_ASSERTION_HANDLER(handler) ignored, due to missing linker section definition!" -# define MODM_ASSERTION_HANDLER(handler) -#endif - -#ifdef MODM_DEBUG_BUILD -# define MODM_ASSERTION_HANDLER_DEBUG(handler) \ - MODM_ASSERTION_HANDLER(handler) -#else -# define MODM_ASSERTION_HANDLER_DEBUG(handler) -#endif - -#include "assert.h" - -#endif // __DOXYGEN__ - -#endif // MODM_ASSERT_HPP diff --git a/src/modm/architecture/interface/assert.hpp.in b/src/modm/architecture/interface/assert.hpp.in new file mode 100644 index 0000000000..a8256224c5 --- /dev/null +++ b/src/modm/architecture/interface/assert.hpp.in @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2016-2018, 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include "assert.h" +#include +%% if core.startswith("avr") +#include +%% endif +/// @endcond + +namespace modm +{ + +/// Describes abandonment type of assertions. +/// @ingroup modm_architecture_assert +enum class +Abandonment : uint8_t +{ + DontCare = Bit0, ///< Do not care about failure. + Ignore = Bit1, ///< Ignore this failure. + Fail = Bit2, ///< This failure is reason for abandonment. + Debug = Bit7 ///< Only set for a debug-only failure. +}; +/// @ingroup modm_architecture_assert +using AbandonmentBehavior = Flags8; +/// @cond +MODM_TYPE_FLAGS(AbandonmentBehavior); +/// @endcond + +/** + * Contains information about the failed assertion. + * + * @ingroup modm_architecture_assert + */ +struct modm_packed +AssertionInfo +{ + const char *name; ///< Can be used to recognise the assertion in code +%% if options.with_description + const char *description; ///< Detailed failure description +%% endif + uintptr_t context; ///< Optional context depends on assertion + AbandonmentBehavior behavior; ///< Can this assertion be ignored? +}; + +/// Signature of the assertion handlers +/// @ingroup modm_architecture_assert +using AssertionHandler = Abandonment (*)(const AssertionInfo &info); + +} // namespace modm + +#ifdef __DOXYGEN__ + +/** + * Declares whether or not AssertionInfo has a `description` field. + * + * @ingroup modm_architecture_assert + */ +#define MODM_ASSERTION_INFO_HAS_DESCRIPTION {{ 1 if options.with_description else 0 }} + +/** + * This adds a function to the list of assertion handlers to execute on + * assertion failure. Note that this macro does not give you any influence + * over the order of handler execution on assertion failure. + * Do not write assertion handlers that depend on any ordered execution! + * + * @param handler A function of signature `AssertionHandler`. + * + * @ingroup modm_architecture_assert + */ +#define MODM_ASSERTION_HANDLER(handler) + +/** + * This adds an assertion handler only for debug mode. + * + * @param handler A function of signature `AssertionHandler`. + * + * @ingroup modm_architecture_assert + */ +#define MODM_ASSERTION_HANDLER_DEBUG(handler) + +/** + * Always abandons execution + * + * @note This assert is included in all builds! + * @ingroup modm_architecture_assert + */ +modm_noreturn void +modm_assert(bool condition, + const char *name, const char *description, + uintptr_t context=uintptr_t(-1)); + +/** + * Abandons execution, unless overwritten by assertion handlers to resume. + * + * @returns result of condition evaluation + * @note This assert is included in all builds! + * @ingroup modm_architecture_assert + */ +bool +modm_assert_continue_fail(bool condition, + const char *name, const char *description, + uintptr_t context=uintptr_t(-1)); + +/** + * Resumes execution, unless overwritten by assertion handlers to abandon. + * + * @returns result of condition evaluation + * @note This assert is included in all builds! + * @ingroup modm_architecture_assert + */ +bool +modm_assert_continue_ignore(bool condition, + const char *name, const char *description, + uintptr_t context=uintptr_t(-1)); + +/** + * Abandons execution, unless overwritten by assertion handlers to resume. + * + * @note This assert is only included debug builds! + * @returns result of condition evaluation + * @ingroup modm_architecture_assert + */ +bool +modm_assert_continue_fail_debug(bool condition, + const char *name, const char *description, + uintptr_t context=uintptr_t(-1)); + +/** + * Resumes execution, unless overwritten by assertion handlers to abandon. + * + * @note This assert is only included debug builds! + * @returns result of condition evaluation + * @ingroup modm_architecture_assert + */ +bool +modm_assert_continue_ignore_debug(bool condition, + const char *name, const char *description, + uintptr_t context=uintptr_t(-1)); + +#else + +#ifdef MODM_DEBUG_BUILD +#define MODM_ASSERTION_HANDLER_DEBUG(handler) \ + MODM_ASSERTION_HANDLER(handler) +#else +#define MODM_ASSERTION_HANDLER_DEBUG(handler) \ + static const modm::AssertionHandler modm_unused \ + handler ## _assertion_handler_ptr = handler +#endif + +#endif // __DOXYGEN__ + +/** + * Overwriteable abandonment handler for all targets. + * + * You should overwrite this handler for custom failure behaviour like blinking + * LEDs and logging the failure via a serial connection. + * + * @ingroup modm_architecture_assert + */ +modm_extern_c void +modm_abandon(const modm::AssertionInfo &info) modm_weak; + + +// Core must implement MODM_ASSERTION_HANDLER(handler) +#include diff --git a/src/modm/architecture/interface/assert.md b/src/modm/architecture/interface/assert.md index 094020656b..61ae62f014 100644 --- a/src/modm/architecture/interface/assert.md +++ b/src/modm/architecture/interface/assert.md @@ -1,49 +1,236 @@ -# Assertions - -These functions allow you to assert a condition at runtime and define -failure handlers in your application that can decide what to do with this -assertion and provide custom functionality. - -Each assertion has the form `modm_assert(condition, module, location, failure)`, -where the condition is a boolean and rest are strings, so that a simple -string compare can be used to match for module, location or failure. -For example, the identifier `"can", "init", "timeout"` describes a timeout -failure in the CAN initializer function. -The assert `modm_assert_debug(condition, module, location, failure)` is only -available on debug builds and is removed from the code for a release build. - -The user can define one or multiple assertion handlers in any part of the -application using the `MODM_ASSERTION_HANDLER(function)` macro. -All assertion handlers will be executed when an assertion fails anywhere in -the code and get passed the identifier string. - -!!!note - The order of assertion handler execution is undefined and must not been - relied upon for any functionality! - -!!!warning - Assertion handlers may be executed in interrupt context! - -Depending on the information in the failure identifier, the assertion handler -returns `Abandonment::DontCare` if the failure is not of interest, or -`Abandonment::Ignore` for recoverable failures, or `Abandonment::Fail` for -failures that do not allow normal program continuation. -The program is aborted, if any assertion handler returns `Abandonment::Fail`, -all assertion handlers return `Abandonment::DontCare` or no assertion -handlers have been defined in the application. -Only if one or many assertion handlers return `Abandonment::Ignore` and the -remainder returns `Abandonment::DontCare`, only then is the assertion ignored. - -!!!note - It is intended that the assertion handlers do not block (forever), so that - all assertion handlers can get called. - -On program abandonment `modm_abandon(module, location, failure)` is called, -which exits the program silently by default. -Only on hosted an formatted error string is output by default. -It is therefore recommended to overwrite this function on embedded targets -for custom behavior like blinking an LED and printing to a serial connection. - -!!!warning - The abandonment handler may also be executed in interrupt context! +# Assertions and Abandonment +This module provides a way to define and report assertions, that act as a +low-cost replacement for C++ exceptions and as a low-cost customization point +for errors raised in asynchronous code. + +Assertions are called with or without a context: + +- `modm_assert(condition, name, descr);` +- `modm_assert(condition, name, descr, context);` + +They have the following arguments: + +1. `bool condition`: The assertion fails when this condition evaluated to false. +2. `const char *name`: A short and unique assertion name. +3. `const char *description`: A detailed description of the failure. +4. `uintptr_t context = -1`: Optional context. + +The condition is evaluated at most once by a (C-style) cast to bool. + +The name format is not enforced, however, it is recommended to either use `what` +for top-level failures, like `malloc` for heap troubles, or `scope.what` for +failures that may not be unique, like `can.rx` vs. `uart.rx` for when their +respective receive buffers overflow. + +The description can be as detailed as necessary, since it is only included in +the firmware if the `with_description` option is set to true, which also defines +`MODM_ASSERTION_INFO_HAS_DESCRIPTION` to 1 or 0. You can either find the +detailed description in your code via its name, or if you prefer a stand-alone +solution and your target has enough memory, include all strings in your binary. + +The context is of pointer size, and anything passed to it is cast to +`uintptr_t`. Otherwise all bits are set via `uintptr_t(-1)`. + +Assertions are implemented as macros and expand to this pseudo-code equivalent: + +```cpp +void modm_assert(bool condition, const char *name, const char *description, + uintptr_t context=uintptr_t(-1)) +{ + if (not condition) + { + modm::AssertionInfo info{name, description, context}; + modm_assert_report(info); + // Unreachable code + } +} +``` + +!!! note "Assertions can be used in both C and C++ code." + +If you like to know the technical details, you can [read here about the original +assertions in xpcc](https://blog.salkinium.com/xpccs-error-model). + + +## Assertion Handlers + +Assertions may also be recoverable if the call site allows for it. For example +if the CAN receive buffer overflows, you may want to simply discard the input. +If malloc fails to allocate it just returns NULL and the caller is responsible +to deal with that. But maybe you want to enable an additional assertion in debug +mode just to double-check. + +When an assertion fails, the runtime calls any number of user-defined handlers, +registered using `MODM_ASSERTION_HANDLER(handler)`. The handlers must return a +`modm::Abandonment` value, specifing whether they want to continue with the +execution with `Abandonment::Ignore`, or abandon execution with +`Abandonment::Fail` leading to a call to `modm_abandon(info)`, or delegate the +decision with `Abandonment::DontCare`. + +For example, this neutral handler logs the failed assertion's name, but +delegates all further decisions to others: + +```cpp +static modm::Abandonment +log_assertion(const modm::AssertionInfo &info) +{ + MODM_LOG_ERROR.printf("Assertion '%s' raised!\n", info.name); + return Abandonment::DontCare; +} +// Register handler for all builds +MODM_ASSERTION_HANDLER(log_assertion); +// Or register only for debug builds +MODM_ASSERTION_HANDLER_DEBUG(log_assertion); +``` + +You may register specialized handlers anywhere in your code, for example for +ignoring the mentioned CAN receive buffer overflow in your connectivity code: + +```cpp +static modm::Abandonment +ignore_can_rx_handler(const modm::AssertionInfo &info) +{ + if (info.name == "can.rx"s) { + // Only silently ignore this specific assertion! + return Abandonment::Ignore; + } + return Abandonment::DontCare; +} +MODM_ASSERTION_HANDLER(ignore_can_rx_handler); +``` + +You may define any number of handlers, which are called in random order by the +runtime and their results are accumulated as follows: + +1. If at least one handler returns `Abandonment::Fail` execution abandons. +2. Otherwise if any handler returns `Abandonment::Ignore` execution resumes. +3. Otherwise if no handlers are registered or all handlers return + `Abandonment::DontCare`, the assertion type determines the outcome. + +!!! note "Handler execution order is undefined." + The order of handler execution is undefined and must not be relied upon for + any functionality! + +!!! warning "Assertions and handlers are not reentrant!" + Assertions may fail in an interrupt context, thus calling handlers in that + context too. Since handlers may make use of interrupts themselves (for + logging via UART) assertions are not atomically scoped by default. You may + however use a `modm::atomic::Lock` inside your assertion handler. + +!!! warning "Handlers may be called inside high-priority interrupts!" + This is problematic when relying on interrupts still working inside handlers + for example for logging the failure via UART. Be aware of this and make sure + you do not inadvertantly block inside handlers. + + +## Assertion Types + +The call site of the assertion decides whether an assertion can be recovered +from or not. For example, if the CAN receive buffer has overflowed, but +execution continues, then code to discard the message must be in place. + +In case no handlers are registered or they all delegate the abandoment decision +away, the call site must decide what the default behavior is. For this purpose +the following assertions are available: + +1. `void modm_assert()`: Always abandons execution when failed. +2. `bool modm_assert_continue_fail()`: Abandons execution unless overwritten. +3. `bool modm_assert_continue_ignore()`: Resumes execution unless overwritten. + +Assertions that can resume execution return the evaluated boolean condition to +be used to branch to cleanup code: + +```cpp +// Can be done inline in any flow control statement +if (not modm_assert_continue_fail(condition, ...)) { + // cleanup code +} + +// Or saved in a variable and queried multiple times. +const bool cleanup = not modm_assert_continue_ignore(condition, ...); +if (cleanup) { + // cleanup code part 1 +} +// other code +if (cleanup) { + // cleanup code part 1 +} + +// or if cleanup is require *before* assertion is called +const bool is_ok = (condition); +if (not is_ok) { + // pre-assert cleanup +} +modm_assert_continue_fail(is_ok, ...); +if (not is_ok) { + // post-assert cleanup +} +``` + +Additionally, these assertions are only active in debug mode. Of course they +still evaluate and return the condition in release mode, so you can use them +just as above: + +4. `bool modm_assert_continue_fail_debug()` +5. `bool modm_assert_continue_ignore_debug()` + + +### When to use what? + +Here are some guidelines for choosing the best assertion type: + +1. Prefer to report errors via return types whenever possible! +2. If no sane recovery is possible, use `modm_assert()`. +3. If there is a (sensible) fallback for the failure, use + `modm_assert_continue_{fail|ignore}()`: + a. Abort by default, if the failure runs contrary to expected behavior. + b. Resume by default, if the failure is expected and its behavior is well + documented. +4. If the failure is expected *and* communicated via the normal API, or it + only occurs rarely or through a clear misuse of the API, use + `modm_assert_continue_{fail|ignore}_debug()`. + +Let's illustrate these with a few examples: + +1. libc `exit()` is called. There is no sensible fallback, since there is no + operating system to return control back to, so use `modm_assert()`. +2. An interrupt without a user-defined handler is triggered. The developer most + likely enabled the wrong interrupt or provided the wrong handler. A sensible + fallback is to disable the interrupt from triggering again and to alert the + developer with `modm_assert_continue_fail(..., irq_number)`. +3. The CAN receive buffer overflows. A sensible fallback is to discard the + message, which is documented as the expected behavior. Since this occurs + asynchronously inside the CAN RX interrupt, there is no way to return an + error code, so call `modm_assert_continue_ignore(..., &message)` with a + pointer to the message to inform the developer. +4. malloc fails to due to heap exhaustion and returns NULL, delegating the + fallback implementation to the caller. Since the typical callers of malloc + are known for not checking for NULL, using + `modm_assert_continue_fail_debug()` here is warranted, helping the + developer find potential issues faster, and then ignoring this assert for + debug builds by registering a handler via `MODM_ASSERTION_HANDLER_DEBUG()`. +5. An I2C transfer failure is detected inside an interrupt. Such failures are + expected on busses and typically the transfers are simply retried. You can + use `modm_assert_continue_ignore_debug()` to give the developer a way to + log the failure frequency without having to provide a special API. This can + help diagnose a problem perhaps with the bus connection faster. + + +## Abandoning execution + +If execution is abandoned `modm_abandon(const AssertionInfo &info)` is called, +which is a weak and empty function by default. + +The function is meant to be overwritten by the application on embedded targets +for example to disable relevant hardware for safety, log the failure via UART +and perhaps blink some LEDs wildly to get the user's attention. + +After returning from that function, the runtime resets the chip on Cortex-M, or +loops forever on AVR, or calls `abort()` on hosted. You may of course, implement +your own abandoning behavior instead of returning from `modm_abandon()`. + +!!! warning "`modm_abandon()` may be called inside high-priority interrupts!" + You can try to lower the active IRQ priority to let UART work, however, in + the worst case you're called from within the HardFault or even NMI handlers + which have the highest fixed priority. diff --git a/src/modm/architecture/module.lb b/src/modm/architecture/module.lb index 09de2610a4..c73c8b28c9 100644 --- a/src/modm/architecture/module.lb +++ b/src/modm/architecture/module.lb @@ -48,13 +48,29 @@ class Assert(Module): module.description = FileReader("interface/assert.md") def prepare(self, module, options): - module.depends(":math:utils", ":architecture:accessor") + module.add_option( + BooleanOption(name="with_description", + default=options[":target"].identifier.platform in ["hosted"], + description=Assert.with_description_descr)) + + module.depends(":architecture:register") + if options[":target"].identifier.platform in ["avr"]: + module.depends(":architecture:accessor") return True def build(self, env): env.outbasepath = "modm/src/modm/architecture" - env.copy("interface/assert.hpp") - env.copy("interface/assert.h") + env.substitutions = {"core": env[":target"].get_driver("core")["type"]} + env.template("interface/assert.hpp.in") + env.template("interface/assert.h.in") + + with_description_descr = """# Include assertion description + +Places the full description of a `modm_assert()` into the firmware image instead +of only into the ELF file. This makes printing assertion information a simple +standalone feature, fully independent of any additional script for decoding +logging output, however, it may increase binary size considerably! +""" # ----------------------------------------------------------------------------- class Atomic(Module): diff --git a/src/modm/architecture/utils.hpp b/src/modm/architecture/utils.hpp index 05d08aca86..6929ff7b56 100644 --- a/src/modm/architecture/utils.hpp +++ b/src/modm/architecture/utils.hpp @@ -120,6 +120,8 @@ #define modm_unlikely(x) __builtin_expect(!!(x), 0) #define modm_section(s) __attribute__((section(s))) #define modm_fallthrough __attribute__((fallthrough)) + #define modm_noreturn __attribute__((noreturn)) + #define modm_warn_unused_result __attribute__((warn_unused_result)) #ifdef MODM_COMPILER_MINGW // FIXME: Windows Object Format PE does not support weak symbols diff --git a/src/modm/platform/core/avr/assert.cpp b/src/modm/platform/core/avr/assert.cpp deleted file mode 100644 index 8e7b69d753..0000000000 --- a/src/modm/platform/core/avr/assert.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2016, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include -#include - -using modm::AssertionHandler; -using modm::Abandonment; - -extern AssertionHandler __assertion_table_start; -extern AssertionHandler __assertion_table_end; - -extern "C" -{ - -void -modm_assert_fail(const char * identifier) -{ - // just forward this call - modm_assert_fail_context(identifier, 0); -} - -void modm_assert_fail_context(const char * identifier, uintptr_t context) -{ - uint8_t state(uint8_t(Abandonment::DontCare)); - const char * module = identifier; - const char * location = module + strlen_P(module) + 1; - const char * failure = location + strlen_P(location) + 1; - - AssertionHandler * table_addr = &__assertion_table_start; - for (; table_addr < &__assertion_table_end; table_addr++) - { - AssertionHandler handler = (AssertionHandler) pgm_read_word(table_addr); - state |= (uint8_t) handler(module, location, failure, context); - } - - if (state == (uint8_t) Abandonment::DontCare or - state & (uint8_t) Abandonment::Fail) - { - modm_abandon(module, location, failure, context); - for (;;) {} - } -} - -modm_weak -void modm_abandon(const char *, const char *, const char *, uintptr_t) {} - -} diff --git a/src/modm/platform/core/avr/linkerscript/linkerscript.ld b/src/modm/platform/core/avr/linkerscript/linkerscript.ld index 2fc4e7dee0..fda6ccfdbd 100644 --- a/src/modm/platform/core/avr/linkerscript/linkerscript.ld +++ b/src/modm/platform/core/avr/linkerscript/linkerscript.ld @@ -15,11 +15,11 @@ SECTIONS .modm_assertion : AT( ADDR(.text) + SIZEOF(.text) + SIZEOF(.data)) { . = ALIGN(2); - __assertion_table_start = . + SIZEOF(.data); + __assertion_table_start = .; KEEP(*(.assertion)) - __assertion_table_end = . + SIZEOF(.data); + __assertion_table_end = .; } } INSERT AFTER .data diff --git a/src/modm/platform/core/avr/module.lb b/src/modm/platform/core/avr/module.lb index 3870306d56..c90172d5e5 100644 --- a/src/modm/platform/core/avr/module.lb +++ b/src/modm/platform/core/avr/module.lb @@ -56,7 +56,10 @@ def build(env): env.copy("linkerscript/linkerscript.ld", "linkerscript.ld") env.collect(":build:linkflags", "-L{linkdir}", "-Tlinkerscript.ld") - env.substitutions = {"target": target.identifier} + env.substitutions = { + "target": target.identifier, + "core": target.get_driver("core")["type"] + } env.outbasepath = "modm/src/modm/platform/core" env.copy("main.hpp") @@ -64,7 +67,9 @@ def build(env): env.template("ram.cpp.in") # dealing with runtime assertions - env.copy("assert.cpp") + if env.has_module(":architecture:assert"): + env.template("../cortex/assert.cpp.in", "assert.cpp") + env.template("../cortex/assert.hpp.in", "assert.hpp") if env.has_module(":architecture:atomic"): env.copy("atomic_lock.hpp") diff --git a/src/modm/platform/core/cortex/assert.cpp b/src/modm/platform/core/cortex/assert.cpp deleted file mode 100644 index 569d906ad4..0000000000 --- a/src/modm/platform/core/cortex/assert.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2016, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include -#include -#include - -using modm::AssertionHandler; -using modm::Abandonment; - -extern AssertionHandler __assertion_table_start; -extern AssertionHandler __assertion_table_end; - -extern "C" -{ - -modm_weak void -modm_undefined_handler(int32_t irqn) -{ - // Set the currently executing interrupt to the lowest priority to allow - // reporting of the assertion failure and disable it from firing again. - NVIC_SetPriority(IRQn_Type(irqn), (1ul << __NVIC_PRIO_BITS) - 1ul); - NVIC_DisableIRQ(IRQn_Type(irqn)); - modm_assert(0, "core", "nvic", "undefined", irqn); -} - -void -modm_assert_fail(const char * identifier) -{ - // just forward this call - modm_assert_fail_context(identifier, 0); -} - -void modm_assert_fail_context(const char * identifier, uintptr_t context) -{ - uint8_t state(uint8_t(Abandonment::DontCare)); - const char * module = identifier; - const char * location = module + strlen(module) + 1; - const char * failure = location + strlen(location) + 1; - - AssertionHandler * handler = &__assertion_table_start; - for (; handler < &__assertion_table_end; handler++) - { - state |= (uint8_t) (*handler)(module, location, failure, context); - } - - if (state == (uint8_t) Abandonment::DontCare or - state & (uint8_t) Abandonment::Fail) - { - modm_abandon(module, location, failure, context); - NVIC_SystemReset(); - } -} - -modm_weak -void modm_abandon(const char *, const char *, const char *, uintptr_t) {} - -} diff --git a/src/modm/platform/core/cortex/assert.cpp.in b/src/modm/platform/core/cortex/assert.cpp.in new file mode 100644 index 0000000000..54f2920312 --- /dev/null +++ b/src/modm/platform/core/cortex/assert.cpp.in @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016-2020, Niklas Hauser + * Copyright (c) 2017, Sascha Schade + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +%% if target.platform in ["hosted"] +#include +#include +%% elif core.startswith("avr") +#include +%% else +#include +%% endif +#include + +using modm::AssertionHandler; +using modm::Abandonment; +using modm::AbandonmentBehavior; + +%% if target.family == "darwin" +extern AssertionHandler __assertion_table_start __asm("section$start$__DATA$modm_assertion"); +extern AssertionHandler __assertion_table_end __asm("section$end$__DATA$modm_assertion"); +%% elif target.family == "linux" +extern AssertionHandler __assertion_table_start __asm("__start_modm_assertion"); +extern AssertionHandler __assertion_table_end __asm("__stop_modm_assertion"); +%% else +extern AssertionHandler __assertion_table_start; +extern AssertionHandler __assertion_table_end; +%% endif + +%% if target.platform in ["hosted"] +// Since we use the default linker script on hosted, the above linker section are +// only included if something is put into these sections. Therefore we are placing +// an empty assertion handler here, which does not influence assertion handling. +Abandonment _modm_empty_assertion_handler(const modm::AssertionInfo &) +{ return Abandonment::DontCare; } +MODM_ASSERTION_HANDLER(_modm_empty_assertion_handler); +%% endif + +extern "C" +{ + +void +modm_assert_report(_modm_assertion_info *cinfo) +{ + auto info = reinterpret_cast(cinfo); + AbandonmentBehavior behavior(info->behavior); + + for (const AssertionHandler *handler = &__assertion_table_start; + handler < &__assertion_table_end; handler++) + { +%% if core.startswith("avr") + behavior |= ((AssertionHandler)pgm_read_ptr(handler))(*info); +%% else + behavior |= (*handler)(*info); +%% endif + } + + info->behavior = behavior; + behavior.reset(Abandonment::Debug); + if ((behavior == Abandonment::DontCare) or + (behavior & Abandonment::Fail)) + { + modm_abandon(*info); +%% if core.startswith("cortex-m") + NVIC_SystemReset(); +%% elif core.startswith("avr") + while(1) ; +%% else + abort(); +%% endif + } +} + +modm_weak +void modm_abandon(const modm::AssertionInfo &info) +{ +%% if target.platform in ["hosted"] + MODM_LOG_ERROR.printf("Assertion '%s'", info.name); + if (info.context != uintptr_t(-1)) { MODM_LOG_ERROR.printf(" @ %p (%lu)", (void *)info.context, info.context); } + %% if options.get(":architecture:assert:with_description", False) + MODM_LOG_ERROR.printf(" failed!\n %s\nAbandoning...\n", info.description) << modm::flush; + %% else + MODM_LOG_ERROR.printf(" failed!\nAbandoning...\n") << modm::flush; + %% endif +%% else + (void)info; +%% endif +} + +} diff --git a/src/modm/platform/core/cortex/assert.hpp.in b/src/modm/platform/core/cortex/assert.hpp.in new file mode 100644 index 0000000000..627577977a --- /dev/null +++ b/src/modm/platform/core/cortex/assert.hpp.in @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#define MODM_ASSERTION_HANDLER(handler) \ +%% if target.family == "darwin" + __attribute__((section("__DATA,modm_assertion"), used)) \ +%% elif target.family == "linux" + __attribute__((section("modm_assertion"), used)) \ +%% else + __attribute__((section(".assertion"), used)) \ +%% endif + const modm::AssertionHandler \ + handler ## _assertion_handler_ptr = handler + diff --git a/src/modm/platform/core/cortex/delay.cpp.in b/src/modm/platform/core/cortex/delay.cpp.in index f53b9624d5..9e21b21882 100644 --- a/src/modm/platform/core/cortex/delay.cpp.in +++ b/src/modm/platform/core/cortex/delay.cpp.in @@ -26,9 +26,9 @@ %# FIXME: Move out of this modules, maybe remove completely %# TODO: These numbers were measured from a few example applications. %# A statistical analysis of the average overhead cycles is required! -%% if partname.startswith("stm32f4") +%% if target.family in ["f4"] %% set overhead = 25 -%% elif partname.startswith("stm32f1") +%% elif target.family in ["f1"] %% set overhead = 12 %% else %% set overhead = 30 diff --git a/src/modm/platform/core/cortex/module.lb b/src/modm/platform/core/cortex/module.lb index 472a5541a3..efa0efd808 100644 --- a/src/modm/platform/core/cortex/module.lb +++ b/src/modm/platform/core/cortex/module.lb @@ -253,8 +253,10 @@ def validate(env): def build(env): env.substitutions = env.query("vector_table") - env.substitutions["partname"] = env[":target"].partname - env.substitutions["with_fault_storage"] = env.has_module(":platform:fault") + env.substitutions.update({ + "target": env[":target"].identifier, + "with_fault_storage": env.has_module(":platform:fault"), + }) env.outbasepath = "modm/src/modm/platform/core" # startup script @@ -264,7 +266,9 @@ def build(env): env.collect(":build:linkflags", "-nostartfiles") # dealing with runtime assertions - env.copy("assert.cpp") + if env.has_module(":architecture:assert"): + env.template("assert.cpp.in") + env.template("assert.hpp.in") # hardware init section env.copy("hardware_init.hpp") diff --git a/src/modm/platform/core/cortex/startup.c.in b/src/modm/platform/core/cortex/startup.c.in index 8cf726cb49..e3b8a3c728 100644 --- a/src/modm/platform/core/cortex/startup.c.in +++ b/src/modm/platform/core/cortex/startup.c.in @@ -127,8 +127,8 @@ void __modm_startup(void) main(); // If main exits, assert here in debug mode - modm_assert_debug(0, "core", "main", "exit"); - + (void) modm_assert_continue_fail_debug(0, + "main.exit", "The main() function returned!"); // Otherwise reboot NVIC_SystemReset(); } diff --git a/src/modm/platform/core/hosted/assert.cpp.in b/src/modm/platform/core/hosted/assert.cpp.in deleted file mode 100644 index e8a58dc332..0000000000 --- a/src/modm/platform/core/hosted/assert.cpp.in +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2016-2018, Niklas Hauser - * Copyright (c) 2017, Sascha Schade - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include -#include -#include - -using modm::AssertionHandler; -using modm::Abandonment; - -%% if target.family == "darwin" -extern AssertionHandler __assertion_table_start __asm("section$start$__DATA$modm_assertion"); -extern AssertionHandler __assertion_table_end __asm("section$end$__DATA$modm_assertion"); -%% else -extern AssertionHandler __assertion_table_start __asm("__start_modm_assertion"); -extern AssertionHandler __assertion_table_end __asm("__stop_modm_assertion"); -%% endif - -// Since we use the default linker script on hosted, the above linker section are -// only included if something is put into these sections. Therefore we are placing -// an empty assertion handler here, which does not influence assertion handling. - -Abandonment -empty_assertion_handler(const char *, const char *, const char *, uintptr_t) -{ - return Abandonment::DontCare; -} -MODM_ASSERTION_HANDLER(empty_assertion_handler); - -extern "C" -{ - -void -modm_assert_fail(const char * identifier) -{ - // just forward this call - modm_assert_fail_context(identifier, 0); -} - -void modm_assert_fail_context(const char * identifier, uintptr_t context) -{ - const char * module = identifier; - const char * location = module + strlen(module) + 1; - const char * failure = location + strlen(location) + 1; -%% if target.family == "windows" - // __assertion_table is not implemented - modm_abandon(module, location, failure, context); - exit(1); -%% else - uint8_t state((uint8_t) Abandonment::DontCare); - - AssertionHandler * handler = &__assertion_table_start; - for (; handler < &__assertion_table_end; handler++) - { - state |= (uint8_t) (*handler)(module, location, failure, context); - } - - if (state == (uint8_t) Abandonment::DontCare or - state & (uint8_t) Abandonment::Fail) - { - modm_abandon(module, location, failure, context); - exit(1); - } -%% endif -} - -modm_weak -void modm_abandon(const char * module, const char * location, const char * failure, uintptr_t context) -{ - MODM_LOG_ERROR.printf("Assertion '%s.%s.%s'", module, location, failure); - if (context) { MODM_LOG_ERROR.printf(" @ %p (%lu)", (void *) context, context); } - MODM_LOG_ERROR.printf(" failed! Abandoning...\n"); -} - -} diff --git a/src/modm/platform/core/hosted/module.lb b/src/modm/platform/core/hosted/module.lb index f794ee3e9d..6af622ba31 100644 --- a/src/modm/platform/core/hosted/module.lb +++ b/src/modm/platform/core/hosted/module.lb @@ -20,7 +20,6 @@ def prepare(module, options): return False module.depends( - ":architecture:assert", ":architecture:memory", ":debug") @@ -28,7 +27,7 @@ def prepare(module, options): def build(env): target = env[":target"].identifier - env.substitutions = {"target": target} + env.substitutions = {"target": target, "core": "hosted"} env.outbasepath = "modm/src/modm/platform/core" env.copy("memory.cpp") @@ -48,10 +47,13 @@ def build(env): env.template("clock.cpp.in") if target.family == "windows": - env.log.error("Assertions are not implemented!") + if env.has_module(":architecture:assert"): + env.log.error("Assertions are not implemented!") if env.has_module(":architecture:delay"): env.log.error("Delay functions are not implemented!") else: - env.template("assert.cpp.in") + if env.has_module(":architecture:assert"): + env.template("../cortex/assert.cpp.in", "assert.cpp") + env.template("../cortex/assert.hpp.in", "assert.hpp") if env.has_module(":architecture:delay"): env.template("delay.hpp.in") From cbc5c8e137df014efc24f5b072b700cf5768cf4b Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 18 Mar 2020 08:05:57 +0100 Subject: [PATCH 2/4] [src] Update all asserts to new interface --- ext/aws/FreeRTOSConfig.h.in | 2 +- ext/aws/modm_port.cpp | 2 +- ext/gcc/assert.cpp.in | 2 +- ext/gcc/cabi_cortex.c | 4 +- ext/gcc/cxxabi.cpp.in | 8 +-- ext/gcc/newdelete_avr.cpp | 25 ++++---- ext/gcc/newdelete_cortex.cpp | 19 ++++-- src/modm/debug/debug.hpp | 1 - src/modm/debug/error_report.cpp | 35 ----------- src/modm/debug/error_report.hpp | 62 ------------------- src/modm/driver/can/mcp2515_impl.hpp | 4 +- src/modm/platform/can/stm32/can.cpp.in | 36 +++++------ src/modm/platform/can/stm32/can.hpp.in | 5 +- src/modm/platform/can/stm32/error_code.hpp | 53 ---------------- src/modm/platform/can/stm32/module.lb | 1 - src/modm/platform/core/avr/ram.cpp.in | 8 ++- .../core/cortex/heap_block_allocator.cpp | 14 +++-- src/modm/platform/core/cortex/heap_newlib.cpp | 14 +++-- src/modm/platform/core/cortex/heap_tlsf.cpp | 9 ++- .../platform/core/cortex/reset_handler.sx | 2 +- src/modm/platform/core/cortex/vectors.c.in | 28 ++++++--- src/modm/processing/resumable/macros.hpp | 3 +- .../resumable/nested_resumable.hpp.in | 3 +- src/modm/processing/resumable/resumable.hpp | 2 - .../processing/resumable/resumable_test.cpp | 9 +-- 25 files changed, 113 insertions(+), 238 deletions(-) delete mode 100644 src/modm/debug/error_report.cpp delete mode 100644 src/modm/debug/error_report.hpp delete mode 100644 src/modm/platform/can/stm32/error_code.hpp diff --git a/ext/aws/FreeRTOSConfig.h.in b/ext/aws/FreeRTOSConfig.h.in index 6f8a8c5892..7114022b75 100644 --- a/ext/aws/FreeRTOSConfig.h.in +++ b/ext/aws/FreeRTOSConfig.h.in @@ -90,7 +90,7 @@ modm_extern_c uint32_t _ZN4modm5clock4fcpuE; #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE /* Define to trap errors during development. */ -#define configASSERT( x ) { modm_assert((x), "freertos", __FILE__, MODM_STRINGIFY(__LINE__)); } +#define configASSERT( x ) { modm_assert((x), "freertos.assert", __FILE__, MODM_STRINGIFY(__LINE__)); } /* FreeRTOS MPU specific definitions. */ #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 diff --git a/ext/aws/modm_port.cpp b/ext/aws/modm_port.cpp index 099adfd3a4..2354753357 100644 --- a/ext/aws/modm_port.cpp +++ b/ext/aws/modm_port.cpp @@ -8,5 +8,5 @@ extern "C" void vApplicationStackOverflowHook(TaskHandle_t, const char *); void vApplicationStackOverflowHook(TaskHandle_t /*pxTask*/, const char *pcTaskName) { - modm_assert(false, "freertos", "stack", "overflow", pcTaskName); + modm_assert(false, "freertos.stack", "FreeRTOS detected a stack overflow!", pcTaskName); } diff --git a/ext/gcc/assert.cpp.in b/ext/gcc/assert.cpp.in index d24b9f81d8..49a2e1d272 100644 --- a/ext/gcc/assert.cpp.in +++ b/ext/gcc/assert.cpp.in @@ -16,7 +16,7 @@ %% if options["assert_on_exception"] #include -#define __modm_stdcpp_failure(failure) modm_assert(false, "stdc++", "stdc++", failure);__builtin_abort(); +#define __modm_stdcpp_failure(failure) modm_assert(false, "c++." failure, "C++ Exception 'std::" failure "' was raised!"); %% else #define __modm_stdcpp_failure(failure) __builtin_abort(); %% endif diff --git a/ext/gcc/cabi_cortex.c b/ext/gcc/cabi_cortex.c index 39daf74e65..51d41fadce 100644 --- a/ext/gcc/cabi_cortex.c +++ b/ext/gcc/cabi_cortex.c @@ -16,6 +16,6 @@ extern void _exit(int); void _exit(int status) { - modm_assert(false, "libc", "libc", "exit", status); - __builtin_trap(); + modm_assert(false, "libc.exit", + "The libc exit(status) function was called!", status); } diff --git a/ext/gcc/cxxabi.cpp.in b/ext/gcc/cxxabi.cpp.in index 12a2cf7815..ffabad0c9a 100644 --- a/ext/gcc/cxxabi.cpp.in +++ b/ext/gcc/cxxabi.cpp.in @@ -18,9 +18,9 @@ extern "C" { void __cxa_pure_virtual() -{ modm_assert_debug(0, "cxa", "virtual", "pure"); __builtin_trap(); } +{ modm_assert(0, "virt.pure", "A pure virtual function was called!"); } void __cxa_deleted_virtual() -{ modm_assert_debug(0, "cxa", "virtual", "deleted"); __builtin_trap(); } +{ modm_assert(0, "virt.del", "A deleted virtual function was called!"); } void* __dso_handle = (void*) &__dso_handle; %% if core.startswith("avr") @@ -52,8 +52,8 @@ extern "C" int __cxa_guard_acquire(int *guard) std::atomic_int *atomic_guard = reinterpret_cast(guard); if (atomic_guard->exchange(INITIALIZING) == INITIALIZING) { - modm_assert_debug(0, "cxa", "guard", "recursion", guard); - return 0; + modm_assert(0, "stat.rec", + "Recursive initialization of a function static!", guard); } return 1; } diff --git a/ext/gcc/newdelete_avr.cpp b/ext/gcc/newdelete_avr.cpp index d6552ba8ab..ee1b1fe679 100644 --- a/ext/gcc/newdelete_avr.cpp +++ b/ext/gcc/newdelete_avr.cpp @@ -17,36 +17,37 @@ #include #include -void * -operator new(size_t size) +static inline void * +modm_new(size_t size) { void * ptr = modm::platform::allocateMemory(size); - modm_assert(ptr, "core", "heap", "new", size); + modm_assert_continue_fail(ptr, "new", + "C++ new() operator failed to allocate!", size); return ptr; } +void * +operator new(size_t size) +{ + return modm_new(size); +} + void * operator new[](size_t size) { - void * ptr = modm::platform::allocateMemory(size); - modm_assert(ptr, "core", "heap", "new", size); - return ptr; + return modm_new(size); } void * operator new(size_t size, modm::MemoryTraits) { - void * ptr = modm::platform::allocateMemory(size); - modm_assert(ptr, "core", "heap", "new", size); - return ptr; + return modm_new(size); } void * operator new[](size_t size, modm::MemoryTraits) { - void * ptr = modm::platform::allocateMemory(size); - modm_assert(ptr, "core", "heap", "new", size); - return ptr; + return modm_new(size); } void* diff --git a/ext/gcc/newdelete_cortex.cpp b/ext/gcc/newdelete_cortex.cpp index 58a23a9133..b53c05ef4e 100644 --- a/ext/gcc/newdelete_cortex.cpp +++ b/ext/gcc/newdelete_cortex.cpp @@ -20,12 +20,19 @@ extern "C" void * malloc_tr(size_t, uint32_t); +static inline void +assert_ptr(void *ptr, size_t size) +{ + modm_assert(ptr, "new", + "C++ new() operator failed to allocate!", size); +} + // ---------------------------------------------------------------------------- void * operator new(size_t size) throw () { void * ptr = malloc(size); - modm_assert(ptr, "core", "heap", "new", size); + assert_ptr(ptr, size); return ptr; } @@ -33,18 +40,18 @@ void * operator new[](size_t size) throw () { void * ptr = malloc(size); - modm_assert(ptr, "core", "heap", "new", size); + assert_ptr(ptr, size); return ptr; } void * -operator new(size_t size, std::nothrow_t) noexcept +operator new(size_t size, const std::nothrow_t&) noexcept { return malloc(size); } void * -operator new[](size_t size, std::nothrow_t) noexcept +operator new[](size_t size, const std::nothrow_t&) noexcept { return malloc(size); } @@ -53,7 +60,7 @@ void * operator new(size_t size, modm::MemoryTraits traits) noexcept { void * ptr = malloc_tr(size, traits.value); - modm_assert(ptr, "core", "heap", "new", size); + assert_ptr(ptr, size); return ptr; } @@ -61,7 +68,7 @@ void * operator new[](size_t size, modm::MemoryTraits traits) noexcept { void * ptr = malloc_tr(size, traits.value); - modm_assert(ptr, "core", "heap", "new", size); + assert_ptr(ptr, size); return ptr; } diff --git a/src/modm/debug/debug.hpp b/src/modm/debug/debug.hpp index 9f285a30f7..1a292a884e 100644 --- a/src/modm/debug/debug.hpp +++ b/src/modm/debug/debug.hpp @@ -16,6 +16,5 @@ #define MODM_DEBUG_HPP #include "debug/logger.hpp" -#include "debug/error_report.hpp" #endif // MODM_DEBUG_HPP diff --git a/src/modm/debug/error_report.cpp b/src/modm/debug/error_report.cpp deleted file mode 100644 index e6f4214fb0..0000000000 --- a/src/modm/debug/error_report.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2011-2012, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#include "error_report.hpp" - -static void -dummyErrorHandler(uint16_t) -{ -} - -// ---------------------------------------------------------------------------- -modm::ErrorReport::Handler modm::ErrorReport::globalErrorHandler = &dummyErrorHandler; - -// ---------------------------------------------------------------------------- -void -modm::ErrorReport::attach(Handler handler) -{ - globalErrorHandler = handler; -} - -void -modm::ErrorReport::detach() -{ - globalErrorHandler = &dummyErrorHandler; -} diff --git a/src/modm/debug/error_report.hpp b/src/modm/debug/error_report.hpp deleted file mode 100644 index aa2bed0560..0000000000 --- a/src/modm/debug/error_report.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2009-2011, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2012, Niklas Hauser - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_ERROR_REPORT_HPP -#define MODM_ERROR_REPORT_HPP - -#include - -namespace modm -{ - /** - * Global error reporter - * - * Used to report severe errors at one place. - * - * @ingroup modm_debug - * @author Fabian Greif - */ - class ErrorReport - { - public: - typedef void (*Handler)(uint16_t errorCode); - - /** - * Attach a Global Error Handler Function. - * - * This handler is used to report severe errors (Buffer Overflows in - * Peripheral Drivers etc.). The error code is architecture dependent. - */ - static void - attach(Handler handler); - - /** - * Remove Error Handler. - */ - static void - detach(); - - static inline void - report(uint16_t errorCode) - { - globalErrorHandler(errorCode); - } - - private: - ErrorReport(); - - static Handler globalErrorHandler; - }; -} - -#endif // MODM_ERROR_REPORT_HPP diff --git a/src/modm/driver/can/mcp2515_impl.hpp b/src/modm/driver/can/mcp2515_impl.hpp index fea4d4add2..ff41dd77b4 100644 --- a/src/modm/driver/can/mcp2515_impl.hpp +++ b/src/modm/driver/can/mcp2515_impl.hpp @@ -97,7 +97,9 @@ modm::Mcp2515::initializeWithPrescaler( // check if we could read back some of the values uint8_t readback = readRegister(CNF2); - modm_assert(readback == cnf[CNF2_idx], "mcp2515", "init", "readback"); + if (not modm_assert_continue_fail_debug(readback == cnf[CNF2_idx], "mcp2515.init", + "Cannot read the CNF2 register of the MCP2515!", readback)) + return false; // reset device to normal mode and disable the clkout pin and // wait until the new mode is active diff --git a/src/modm/platform/can/stm32/can.cpp.in b/src/modm/platform/can/stm32/can.cpp.in index cb679eba16..36f999fd12 100644 --- a/src/modm/platform/can/stm32/can.cpp.in +++ b/src/modm/platform/can/stm32/can.cpp.in @@ -14,7 +14,6 @@ */ // ---------------------------------------------------------------------------- -#include #include #include #include @@ -49,7 +48,7 @@ static modm::atomic::Queue rxQueue; // ---------------------------------------------------------------------------- -void +bool modm::platform::Can{{ id }}::initializeWithPrescaler( uint16_t prescaler, uint8_t bs1, uint8_t bs2, uint32_t interruptPriority, Mode startupMode, bool overwriteOnOverrun) @@ -102,7 +101,9 @@ modm::platform::Can{{ id }}::initializeWithPrescaler( // The CAN hardware waits until the current CAN activity (transmission // or reception) is completed before entering the initialization mode. } - modm_assert(deadlockPreventer > 0, "can", "init", "timeout", {{ 0 if id == '' else id }}); + if (not modm_assert_continue_fail_debug(deadlockPreventer > 0, "can.init", + "CAN::initialize() timed out on entering initialization!", {{ 0 if id == '' else id }})) + return false; // Enable Interrupts: // FIFO1 Overrun, FIFO0 Overrun @@ -152,7 +153,8 @@ modm::platform::Can{{ id }}::initializeWithPrescaler( while ((({{ reg }}->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) and (deadlockPreventer-- > 0)) { // wait for the normal mode } - modm_assert(deadlockPreventer > 0, "can", "init", "timeout2", {{ 0 if id == '' else id }}); + return modm_assert_continue_fail_debug(deadlockPreventer > 0, "can.init2", + "CAN::initialize() timed out on leaving initialization!", {{ 0 if id == '' else id }}); } // ---------------------------------------------------------------------------- @@ -255,9 +257,9 @@ MODM_ISR({{ reg }}_TX) */ MODM_ISR({{ reg }}_RX0) { - if ({{ reg }}->RF0R & CAN_RF0R_FOVR0) { - modm::ErrorReport::report(modm::platform::CAN{{ id }}_FIFO0_OVERFLOW); - + if (not modm_assert_continue_ignore(not ({{ reg }}->RF0R & CAN_RF0R_FOVR0), + "can.rx.hw0", "CAN receive hardware buffer overflowed!", {{ 0 if id == '' else id }})) + { // release overrun flag & access the next message {{ reg }}->RF0R = CAN_RF0R_FOVR0 | CAN_RF0R_RFOM0; } @@ -269,9 +271,8 @@ MODM_ISR({{ reg }}_RX0) // Release FIFO (access the next message) {{ reg }}->RF0R = CAN_RF0R_RFOM0; - if (!rxQueue.push(rxMessage)) { - modm::ErrorReport::report(modm::platform::CAN{{ id }}_FIFO0_OVERFLOW); - } + modm_assert_continue_ignore(rxQueue.push(rxMessage), "can.rx.sw0", + "CAN receive software buffer overflowed!", {{ 0 if id == '' else id }}); %% endif } @@ -282,9 +283,9 @@ MODM_ISR({{ reg }}_RX0) */ MODM_ISR({{ reg }}_RX1) { - if ({{ reg }}->RF1R & CAN_RF1R_FOVR1) { - modm::ErrorReport::report(modm::platform::CAN{{ id }}_FIFO1_OVERFLOW); - + if (not modm_assert_continue_ignore(not ({{ reg }}->RF1R & CAN_RF1R_FOVR1), + "can.rx.hw1", "CAN receive hardware buffer overflowed!", {{ 0 if id == '' else id }})) + { // release overrun flag & access the next message {{ reg }}->RF1R = CAN_RF1R_FOVR1 | CAN_RF1R_RFOM1; } @@ -296,9 +297,8 @@ MODM_ISR({{ reg }}_RX1) // Release FIFO (access the next message) {{ reg }}->RF1R = CAN_RF1R_RFOM1; - if (!rxQueue.push(rxMessage)) { - modm::ErrorReport::report(modm::platform::CAN{{ id }}_FIFO1_OVERFLOW); - } + modm_assert_continue_ignore(rxQueue.push(rxMessage), "can.rx.sw1", + "CAN receive software buffer overflowed!", {{ 0 if id == '' else id }}); %% endif } @@ -430,8 +430,8 @@ modm::platform::Can{{ id }}::sendMessage(const can::Message& message) { // All mailboxes used at the moment %% if options["buffer.tx"] > 0 - if (!txQueue.push(message)) { - modm::ErrorReport::report(modm::platform::CAN{{ id }}_TX_OVERFLOW); + if (not modm_assert_continue_ignore(txQueue.push(message), "can.tx", + "CAN transmit software buffer overflowed!", {{ 0 if id == '' else id }})) { return false; } return true; diff --git a/src/modm/platform/can/stm32/can.hpp.in b/src/modm/platform/can/stm32/can.hpp.in index ecbe1d1bd3..c41b137b49 100644 --- a/src/modm/platform/can/stm32/can.hpp.in +++ b/src/modm/platform/can/stm32/can.hpp.in @@ -21,7 +21,6 @@ #include "../device.hpp" #include "can_bit_timings.hpp" -#include "error_code.hpp" #include "can_filter.hpp" @@ -71,7 +70,7 @@ public: private: /// Private Initializer with computed prescaler and timing constants - static void + static bool initializeWithPrescaler(uint16_t prescaler, uint8_t bs1, uint8_t bs2, uint32_t interruptPriority, Mode startupMode, bool overwriteOnOverrun); public: @@ -107,7 +106,7 @@ public: * other function from this class! */ template< class SystemClock, bitrate_t bitrate=kbps(125), percent_t tolerance=pct(1) > - static inline void + static inline bool initialize( uint32_t interruptPriority, Mode startupMode = Mode::Normal, bool overwriteOnOverrun = true) { diff --git a/src/modm/platform/can/stm32/error_code.hpp b/src/modm/platform/can/stm32/error_code.hpp deleted file mode 100644 index 2999c1b6e9..0000000000 --- a/src/modm/platform/can/stm32/error_code.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2009, Georgi Grinshpun - * Copyright (c) 2009-2011, 2017, Fabian Greif - * Copyright (c) 2010, Martin Rosekeit - * Copyright (c) 2011-2012, 2014-2017, Niklas Hauser - * Copyright (c) 2013, Kevin Läufer - * - * This file is part of the modm project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -// ---------------------------------------------------------------------------- - -#ifndef MODM_STM32_ERROR_HPP -#define MODM_STM32_ERROR_HPP - -namespace modm -{ - namespace platform - { - /// @ingroup modm_platform_can - enum ErrorCode - { - CAN_ERROR_PASSIVE_MODE = 0x0010, - CAN_BUS_OFF_MODE = 0x0011, - CAN_FIFO0_OVERFLOW = 0x0012, - CAN_FIFO1_OVERFLOW = 0x0013, - CAN_TX_OVERFLOW = 0x0014, - - CAN1_ERROR_PASSIVE_MODE = 0x0010, - CAN1_BUS_OFF_MODE = 0x0011, - CAN1_FIFO0_OVERFLOW = 0x0012, - CAN1_FIFO1_OVERFLOW = 0x0013, - CAN1_TX_OVERFLOW = 0x0014, - - CAN2_ERROR_PASSIVE_MODE = 0x0010, - CAN2_BUS_OFF_MODE = 0x0011, - CAN2_FIFO0_OVERFLOW = 0x0012, - CAN2_FIFO1_OVERFLOW = 0x0013, - CAN2_TX_OVERFLOW = 0x0014, - - CAN3_ERROR_PASSIVE_MODE = 0x0010, - CAN3_BUS_OFF_MODE = 0x0011, - CAN3_FIFO0_OVERFLOW = 0x0012, - CAN3_FIFO1_OVERFLOW = 0x0013, - CAN3_TX_OVERFLOW = 0x0014, - }; - } -} - -#endif diff --git a/src/modm/platform/can/stm32/module.lb b/src/modm/platform/can/stm32/module.lb index 2e4ba55407..6c07ffdcf8 100644 --- a/src/modm/platform/can/stm32/module.lb +++ b/src/modm/platform/can/stm32/module.lb @@ -174,7 +174,6 @@ def build(env): env.template("can_filter.hpp.in") env.template("can_filter.cpp.in") - env.copy("error_code.hpp") if "instance" not in driver: env.template("can.hpp.in") diff --git a/src/modm/platform/core/avr/ram.cpp.in b/src/modm/platform/core/avr/ram.cpp.in index 4e5af2e898..17898876c9 100644 --- a/src/modm/platform/core/avr/ram.cpp.in +++ b/src/modm/platform/core/avr/ram.cpp.in @@ -82,8 +82,9 @@ initializeMemory(void) void * modm::platform::allocateMemory(std::size_t requestedSize) { - if (!modm_assert_debug(requestedSize > 0 && requestedSize <= MAX_BLOCK_PARTS * BLOCK_SIZE, - "core", "heap", "malloc", requestedSize)) + if (!modm_assert_continue_fail_debug( + (requestedSize > 0 && requestedSize <= MAX_BLOCK_PARTS * BLOCK_SIZE), + "malloc", "No memory left in heap!", requestedSize)) return 0; requestedSize += 2; // bytes needed for the mangement @@ -143,7 +144,8 @@ modm::platform::allocateMemory(std::size_t requestedSize) avail = cp - __brkval; */ - if (!modm_assert_debug(slotsAvailable >= neededSlots, "core", "heap", "malloc", requestedSize)) + if (!modm_assert_continue_fail_debug(slotsAvailable >= neededSlots, "malloc2", + "No memory left in heap! Cannot enlarge heap!", requestedSize)) return 0; *p = neededSlots; diff --git a/src/modm/platform/core/cortex/heap_block_allocator.cpp b/src/modm/platform/core/cortex/heap_block_allocator.cpp index fc99eedf23..3cf8f3cba7 100644 --- a/src/modm/platform/core/cortex/heap_block_allocator.cpp +++ b/src/modm/platform/core/cortex/heap_block_allocator.cpp @@ -39,10 +39,10 @@ extern "C" { void __modm_initialize_memory(void) { - const uint32_t *heap_start, *heap_end; + const uint32_t *heap_start{nullptr}, *heap_end; // find the largest heap that is DMA-able and S-Bus accessible modm::platform::HeapTable::find_largest(&heap_start, &heap_end); - modm_assert(heap_start, "core", "heap", "init"); + modm_assert(heap_start, "heap.init", "Could not find main heap memory!"); // clamp the heap size to the maximum if ((size_t) heap_end - (size_t) heap_start > max_heap_size) { heap_end = (const uint32_t *) ((char *) heap_start + max_heap_size); @@ -55,14 +55,17 @@ void *__wrap__malloc_r(struct _reent *r, size_t size) { (void) r; void *ptr = allocator.allocate(size); - modm_assert_debug(ptr, "core", "heap", "malloc", size); + modm_assert_continue_fail_debug(ptr, "malloc", + "No memory left in Block heap!", size); return ptr; } void *__wrap__calloc_r(struct _reent *r, size_t size) { void *ptr = __wrap__malloc_r(r, size); - if (modm_assert_debug(ptr, "core", "heap", "calloc", size)) { + + if (modm_assert_continue_fail_debug(ptr, "calloc", + "No memory left in Block heap!", size)) { memset(ptr, 0, size); } return ptr; @@ -74,7 +77,8 @@ void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) (void) p; (void) size; // NOT IMPLEMENTED! - modm_assert_debug(0, "core", "heap", "realloc", size); + modm_assert_continue_fail_debug(0, "realloc", + "Not implemented for Block heap!", size); return NULL; } diff --git a/src/modm/platform/core/cortex/heap_newlib.cpp b/src/modm/platform/core/cortex/heap_newlib.cpp index 0146aee9c0..4d79a904b9 100644 --- a/src/modm/platform/core/cortex/heap_newlib.cpp +++ b/src/modm/platform/core/cortex/heap_newlib.cpp @@ -29,7 +29,7 @@ void __modm_initialize_memory(void) // find the largest heap that is DMA-able and S-Bus accessible modm::platform::HeapTable::find_largest( (const uint32_t **) &__brkval, (const uint32_t **) &heap_end); - modm_assert(__brkval, "core", "heap", "init"); + modm_assert(__brkval, "heap.init", "Could not find main heap memory!"); } /* Support function. Adjusts end of heap to provide more memory to @@ -53,7 +53,8 @@ _sbrk_r(struct _reent *r, ptrdiff_t size) const uint8_t *heap = __brkval; __brkval += size; - modm_assert(__brkval < heap_end, "core", "heap", "sbrk", size); + modm_assert(__brkval < heap_end, "heap.sbrk", + "Heap could not be enlarged!", size); // Return pointer to start of new heap area. return (void*)heap; @@ -61,19 +62,22 @@ _sbrk_r(struct _reent *r, ptrdiff_t size) void *__real__malloc_r(struct _reent *r, size_t size); void *__wrap__malloc_r(struct _reent *r, size_t size) { void * ptr = __real__malloc_r(r, size); - modm_assert_debug(ptr, "core", "heap", "malloc", size); + modm_assert_continue_fail_debug(ptr, "malloc", + "No memory left in Newlib heap!", size); return ptr; } void *__real__calloc_r(struct _reent *r, size_t size); void *__wrap__calloc_r(struct _reent *r, size_t size) { void * ptr = __real__calloc_r(r, size); - modm_assert_debug(ptr, "core", "heap", "calloc", size); + modm_assert_continue_fail_debug(ptr, "calloc", + "No memory left in Newlib heap!", size); return ptr; } void *__real__realloc_r(struct _reent *r, void *p, size_t size); void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) { void * ptr = __real__realloc_r(r, p, size); - modm_assert_debug(ptr, "core", "heap", "realloc", size); + modm_assert_continue_fail_debug(ptr, "realloc", + "No memory left in Newlib heap!", size); return ptr; } void __real__free_r(struct _reent *r, void *p); diff --git a/src/modm/platform/core/cortex/heap_tlsf.cpp b/src/modm/platform/core/cortex/heap_tlsf.cpp index 27241d3e45..1d0daecdcb 100644 --- a/src/modm/platform/core/cortex/heap_tlsf.cpp +++ b/src/modm/platform/core/cortex/heap_tlsf.cpp @@ -82,7 +82,8 @@ get_tlsf_for_ptr(void *p) return pool->tlsf; } } - modm_assert_debug(0, "core", "heap", "pool"); + modm_assert_continue_fail_debug(0, "tlsf.pool", + "Cannot find the right TLSF pool for pointer!", p); return NULL; } @@ -113,7 +114,8 @@ void * malloc_tr(size_t size, uint32_t traits) goto try_again; } // there is no memory left even after fallback. - modm_assert_debug(0, "core", "heap", "malloc", size); + modm_assert_continue_fail_debug(0, "malloc", + "No memory left in any TLSF pools!", size); return NULL; } @@ -140,7 +142,8 @@ void *__wrap__realloc_r(struct _reent *r, void *p, size_t size) if (!pool) return NULL; void *ptr = tlsf_realloc(pool, p, size); - modm_assert_debug(p, "core", "heap", "realloc", size); + modm_assert_continue_fail_debug(0, "realloc", + "Unable to realloc in TLSF pool!", size); return ptr; } diff --git a/src/modm/platform/core/cortex/reset_handler.sx b/src/modm/platform/core/cortex/reset_handler.sx index 51591df3d3..25fd6e7774 100644 --- a/src/modm/platform/core/cortex/reset_handler.sx +++ b/src/modm/platform/core/cortex/reset_handler.sx @@ -17,5 +17,5 @@ .func Reset_Handler Reset_Handler: bl __modm_initialize_platform - bl __modm_startup + b __modm_startup .endfunc diff --git a/src/modm/platform/core/cortex/vectors.c.in b/src/modm/platform/core/cortex/vectors.c.in index 649d0b499a..63458e94c2 100644 --- a/src/modm/platform/core/cortex/vectors.c.in +++ b/src/modm/platform/core/cortex/vectors.c.in @@ -14,13 +14,7 @@ #include // ---------------------------------------------------------------------------- -extern void modm_undefined_handler(int32_t); -void Undefined_Handler(void) -{ - int32_t irqn; - asm volatile("mrs %[irqn], ipsr" :[irqn] "=r" (irqn)); - modm_undefined_handler(irqn - 16); -} +void Undefined_Handler(void); /* Provide weak aliases for each Exception handler to Undefined_Handler. * As they are weak aliases, any function with the same name will override * this definition. */ @@ -60,4 +54,22 @@ FunctionPointer vectorsRom[] = // reserve space for the remapped vector table in RAM modm_section(".vector_ram") FunctionPointer vectorsRam[sizeof(vectorsRom) / sizeof(FunctionPointer)]; -%% endif \ No newline at end of file +%% endif + +// ---------------------------------------------------------------------------- +// Explicitly include this BELOW the vector table to *not deal* with potential +// re-#defines of interrupt vector names! Bad vendors!! BAD!!! +#include + +void Undefined_Handler(void) +{ + int32_t irqn; + asm volatile("mrs %[irqn], ipsr" :[irqn] "=r" (irqn)); + irqn -= 16; + // Set the currently executing interrupt to the lowest priority to allow + // reporting of the assertion failure and disable it from firing again. + NVIC_SetPriority((IRQn_Type)irqn, (1ul << __NVIC_PRIO_BITS) - 1ul); + NVIC_DisableIRQ((IRQn_Type)irqn); + modm_assert_continue_fail(0, "nvic.undef", + "An undefined NVIC interrupt was raised!", irqn); +} diff --git a/src/modm/processing/resumable/macros.hpp b/src/modm/processing/resumable/macros.hpp index 0c05d9124d..bbe9453eb9 100644 --- a/src/modm/processing/resumable/macros.hpp +++ b/src/modm/processing/resumable/macros.hpp @@ -193,8 +193,7 @@ constexpr uint16_t rfCounter = __COUNTER__; \ this->template checkRfType(); \ constexpr uint8_t rfIndex = 0; \ - if (!this->nestingOkRf()) { \ - modm_assert(false, MODM_RESUMABLE_MODULE_NAME, "begin", "nesting", this); \ + if (not this->nestingOkRf()) { \ return {modm::rf::NestingError}; \ } \ switch (this->pushRf(0)) { \ diff --git a/src/modm/processing/resumable/nested_resumable.hpp.in b/src/modm/processing/resumable/nested_resumable.hpp.in index e3d77d1b48..e19be75bf8 100644 --- a/src/modm/processing/resumable/nested_resumable.hpp.in +++ b/src/modm/processing/resumable/nested_resumable.hpp.in @@ -121,7 +121,8 @@ protected: nestingOkRf() const { %% if options["check_nesting_depth"] - return (rfLevel < Levels); + return modm_assert_continue_fail_debug(rfLevel < Levels, "rf.nest", + "Called too many nested ResumableFunctions of the same class!", this); %% else return true; %% endif diff --git a/src/modm/processing/resumable/resumable.hpp b/src/modm/processing/resumable/resumable.hpp index 292e68f859..dac4b7a857 100644 --- a/src/modm/processing/resumable/resumable.hpp +++ b/src/modm/processing/resumable/resumable.hpp @@ -18,8 +18,6 @@ #include #include -#define MODM_RESUMABLE_MODULE_NAME "resumable" - namespace modm { diff --git a/test/modm/processing/resumable/resumable_test.cpp b/test/modm/processing/resumable/resumable_test.cpp index 6904906786..ff4af16dfd 100644 --- a/test/modm/processing/resumable/resumable_test.cpp +++ b/test/modm/processing/resumable/resumable_test.cpp @@ -348,15 +348,10 @@ class TestingNestedThread : public modm::NestedResumable<3> static bool testing_nesting_assertion(false); modm::Abandonment -resumable_test_nesting_handler(const char * module, - const char * location, - const char * function, - uintptr_t) +resumable_test_nesting_handler(const modm::AssertionInfo &info) { if (testing_nesting_assertion) { - TEST_ASSERT_EQUALS_STRING(module, MODM_RESUMABLE_MODULE_NAME); - TEST_ASSERT_EQUALS_STRING(location, "begin"); - TEST_ASSERT_EQUALS_STRING(function, "nesting"); + TEST_ASSERT_EQUALS_STRING(info.name, "rf.nesting"); return modm::Abandonment::Ignore; } return modm::Abandonment::DontCare; From a5726c29470d1372b987a54238b08f121f58edf8 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 18 Mar 2020 08:06:17 +0100 Subject: [PATCH 3/4] [board] Update all BSPs to new assert interface --- src/modm/board/black_pill/module.lb | 5 ++- src/modm/board/blue_pill/module.lb | 5 ++- src/modm/board/board.cpp.in | 40 +++++++++++++-------- src/modm/board/disco_f051r8/module.lb | 5 ++- src/modm/board/disco_f072rb/module.lb | 5 ++- src/modm/board/disco_f100rb/module.lb | 5 ++- src/modm/board/disco_f303vc/module.lb | 5 ++- src/modm/board/disco_f407vg/module.lb | 5 ++- src/modm/board/disco_f429zi/module.lb | 5 ++- src/modm/board/disco_f469ni/module.lb | 5 ++- src/modm/board/disco_f746ng/module.lb | 5 ++- src/modm/board/disco_f769ni/module.lb | 5 ++- src/modm/board/disco_l152rc/module.lb | 5 ++- src/modm/board/disco_l476vg/module.lb | 5 ++- src/modm/board/mini_f401/module.lb | 5 ++- src/modm/board/mini_f411/module.lb | 5 ++- src/modm/board/nucleo_f031k6/module.lb | 5 ++- src/modm/board/nucleo_f042k6/module.lb | 5 ++- src/modm/board/nucleo_f103rb/module.lb | 5 ++- src/modm/board/nucleo_f303k8/module.lb | 5 ++- src/modm/board/nucleo_f401re/module.lb | 5 ++- src/modm/board/nucleo_f411re/module.lb | 5 ++- src/modm/board/nucleo_f429zi/module.lb | 5 ++- src/modm/board/nucleo_f446re/module.lb | 5 ++- src/modm/board/nucleo_g071rb/module.lb | 5 ++- src/modm/board/nucleo_g474re/module.lb | 5 ++- src/modm/board/nucleo_l152re/module.lb | 5 ++- src/modm/board/nucleo_l432kc/module.lb | 5 ++- src/modm/board/nucleo_l476rg/module.lb | 5 ++- src/modm/board/olimexino_stm32/module.lb | 5 ++- src/modm/board/stm32f030f4p6_demo/module.lb | 5 +++ 31 files changed, 147 insertions(+), 43 deletions(-) diff --git a/src/modm/board/black_pill/module.lb b/src/modm/board/black_pill/module.lb index 4d47393243..289161895f 100644 --- a/src/modm/board/black_pill/module.lb +++ b/src/modm/board/black_pill/module.lb @@ -67,7 +67,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') diff --git a/src/modm/board/blue_pill/module.lb b/src/modm/board/blue_pill/module.lb index 5de4f23df4..303c309cac 100644 --- a/src/modm/board/blue_pill/module.lb +++ b/src/modm/board/blue_pill/module.lb @@ -67,7 +67,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') diff --git a/src/modm/board/board.cpp.in b/src/modm/board/board.cpp.in index dfa159bcf0..e457d71b40 100644 --- a/src/modm/board/board.cpp.in +++ b/src/modm/board/board.cpp.in @@ -10,8 +10,11 @@ // ---------------------------------------------------------------------------- #include "board.hpp" +%% if with_assert +#include +%% endif -%% if board_has_logger +%% if with_logger Board::LoggerDevice loggerDevice; // Set all four logger streams to use the UART @@ -19,23 +22,25 @@ modm::log::Logger modm::log::debug(loggerDevice); modm::log::Logger modm::log::info(loggerDevice); modm::log::Logger modm::log::warning(loggerDevice); modm::log::Logger modm::log::error(loggerDevice); +%% endif +%% if with_assert modm_extern_c void -modm_abandon(const char * module, - const char * location, - const char * failure, - uintptr_t context) +modm_abandon(const modm::AssertionInfo &info) { - MODM_LOG_ERROR << "Assertion '" << module << "." << location << "." << failure << "'"; - if (context) { MODM_LOG_ERROR << " @ " << (void *) context << " (" << (uint32_t) context << ")"; } - MODM_LOG_ERROR << " failed! Abandoning..." << modm::endl; +%% if with_logger + MODM_LOG_ERROR << "Assertion '" << info.name << "'"; + if (info.context != uintptr_t(-1)) { + MODM_LOG_ERROR << " @ " << (void *) info.context << + " (" << (uint32_t) info.context << ")"; + } + %% if options.get(":architecture:assert:with_description", False) + MODM_LOG_ERROR << " failed!\n " << info.description << "\nAbandoning...\n"; + %% else + MODM_LOG_ERROR << " failed!\nAbandoning...\n"; + %% endif %% else -modm_extern_c void -modm_abandon(const char *, - const char *, - const char *, - uintptr_t) -{ + (void)info; %% endif Board::Leds::setOutput(); @@ -46,4 +51,11 @@ modm_abandon(const char *, Board::Leds::write(0); modm::delayMilliseconds(180); } +%% if with_logger + // Do not flush here otherwise you may deadlock due to waiting on the UART + // interrupt which may never be executed when abandoning in a higher + // priority Interrupt!!! + // MODM_LOG_ERROR << modm::flush; +%% endif } +%% endif diff --git a/src/modm/board/disco_f051r8/module.lb b/src/modm/board/disco_f051r8/module.lb index 1256f8e88b..3ff74ca3e4 100644 --- a/src/modm/board/disco_f051r8/module.lb +++ b/src/modm/board/disco_f051r8/module.lb @@ -33,7 +33,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32f0discovery.cfg"); diff --git a/src/modm/board/disco_f072rb/module.lb b/src/modm/board/disco_f072rb/module.lb index 4ffdc99d93..450ac411f4 100644 --- a/src/modm/board/disco_f072rb/module.lb +++ b/src/modm/board/disco_f072rb/module.lb @@ -34,7 +34,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32f0discovery.cfg"); diff --git a/src/modm/board/disco_f100rb/module.lb b/src/modm/board/disco_f100rb/module.lb index 87e86895a4..73dc20ea09 100644 --- a/src/modm/board/disco_f100rb/module.lb +++ b/src/modm/board/disco_f100rb/module.lb @@ -32,7 +32,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32vldiscovery.cfg"); diff --git a/src/modm/board/disco_f303vc/module.lb b/src/modm/board/disco_f303vc/module.lb index f4ce536fb4..6f33a2afcb 100644 --- a/src/modm/board/disco_f303vc/module.lb +++ b/src/modm/board/disco_f303vc/module.lb @@ -35,7 +35,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32f3discovery.cfg"); diff --git a/src/modm/board/disco_f407vg/module.lb b/src/modm/board/disco_f407vg/module.lb index d2810e225f..aea2bd53a5 100644 --- a/src/modm/board/disco_f407vg/module.lb +++ b/src/modm/board/disco_f407vg/module.lb @@ -35,7 +35,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32f4discovery.cfg"); diff --git a/src/modm/board/disco_f429zi/module.lb b/src/modm/board/disco_f429zi/module.lb index bf42c1f120..969231efc3 100644 --- a/src/modm/board/disco_f429zi/module.lb +++ b/src/modm/board/disco_f429zi/module.lb @@ -34,7 +34,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32f429discovery.cfg"); diff --git a/src/modm/board/disco_f469ni/module.lb b/src/modm/board/disco_f469ni/module.lb index 289bcc6630..f86563e070 100644 --- a/src/modm/board/disco_f469ni/module.lb +++ b/src/modm/board/disco_f469ni/module.lb @@ -36,7 +36,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32f469discovery.cfg") diff --git a/src/modm/board/disco_f746ng/module.lb b/src/modm/board/disco_f746ng/module.lb index ff80279a43..0499aa4d0b 100644 --- a/src/modm/board/disco_f746ng/module.lb +++ b/src/modm/board/disco_f746ng/module.lb @@ -34,7 +34,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32f7discovery.cfg"); diff --git a/src/modm/board/disco_f769ni/module.lb b/src/modm/board/disco_f769ni/module.lb index 65dca2b0a2..734cd5455e 100644 --- a/src/modm/board/disco_f769ni/module.lb +++ b/src/modm/board/disco_f769ni/module.lb @@ -34,7 +34,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32f7discovery.cfg"); diff --git a/src/modm/board/disco_l152rc/module.lb b/src/modm/board/disco_l152rc/module.lb index a2711b862b..906b7428aa 100644 --- a/src/modm/board/disco_l152rc/module.lb +++ b/src/modm/board/disco_l152rc/module.lb @@ -28,7 +28,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32ldiscovery.cfg"); diff --git a/src/modm/board/disco_l476vg/module.lb b/src/modm/board/disco_l476vg/module.lb index 7be00eb433..03090d0981 100644 --- a/src/modm/board/disco_l476vg/module.lb +++ b/src/modm/board/disco_l476vg/module.lb @@ -28,7 +28,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32l4discovery.cfg"); diff --git a/src/modm/board/mini_f401/module.lb b/src/modm/board/mini_f401/module.lb index eaef1967cc..ac9a00b912 100644 --- a/src/modm/board/mini_f401/module.lb +++ b/src/modm/board/mini_f401/module.lb @@ -68,7 +68,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('../mini_f411/board.hpp', 'board.hpp') env.copy('board.xml') diff --git a/src/modm/board/mini_f411/module.lb b/src/modm/board/mini_f411/module.lb index af38aedc73..26757ada92 100644 --- a/src/modm/board/mini_f411/module.lb +++ b/src/modm/board/mini_f411/module.lb @@ -68,7 +68,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": False} + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') diff --git a/src/modm/board/nucleo_f031k6/module.lb b/src/modm/board/nucleo_f031k6/module.lb index 100db03f15..2b04182bf6 100644 --- a/src/modm/board/nucleo_f031k6/module.lb +++ b/src/modm/board/nucleo_f031k6/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo32_arduino.hpp", "nucleo32_arduino.hpp") diff --git a/src/modm/board/nucleo_f042k6/module.lb b/src/modm/board/nucleo_f042k6/module.lb index ef9fe4d6bc..2d81cf9123 100644 --- a/src/modm/board/nucleo_f042k6/module.lb +++ b/src/modm/board/nucleo_f042k6/module.lb @@ -30,7 +30,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo32_arduino.hpp", "nucleo32_arduino.hpp") diff --git a/src/modm/board/nucleo_f103rb/module.lb b/src/modm/board/nucleo_f103rb/module.lb index 2d915e7d22..f6add3f678 100644 --- a/src/modm/board/nucleo_f103rb/module.lb +++ b/src/modm/board/nucleo_f103rb/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo64_arduino.hpp", "nucleo64_arduino.hpp") diff --git a/src/modm/board/nucleo_f303k8/module.lb b/src/modm/board/nucleo_f303k8/module.lb index b8409cb719..09cd48e36a 100644 --- a/src/modm/board/nucleo_f303k8/module.lb +++ b/src/modm/board/nucleo_f303k8/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo32_arduino.hpp", "nucleo32_arduino.hpp") diff --git a/src/modm/board/nucleo_f401re/module.lb b/src/modm/board/nucleo_f401re/module.lb index e862156395..ceaac660a7 100644 --- a/src/modm/board/nucleo_f401re/module.lb +++ b/src/modm/board/nucleo_f401re/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo64_arduino.hpp", "nucleo64_arduino.hpp") diff --git a/src/modm/board/nucleo_f411re/module.lb b/src/modm/board/nucleo_f411re/module.lb index 3beca27c96..9236e5cd4a 100644 --- a/src/modm/board/nucleo_f411re/module.lb +++ b/src/modm/board/nucleo_f411re/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo64_arduino.hpp", "nucleo64_arduino.hpp") diff --git a/src/modm/board/nucleo_f429zi/module.lb b/src/modm/board/nucleo_f429zi/module.lb index fa110ab366..875ed25377 100644 --- a/src/modm/board/nucleo_f429zi/module.lb +++ b/src/modm/board/nucleo_f429zi/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo144_arduino.hpp", "nucleo144_arduino.hpp") diff --git a/src/modm/board/nucleo_f446re/module.lb b/src/modm/board/nucleo_f446re/module.lb index 17e76819ac..f720d20f75 100644 --- a/src/modm/board/nucleo_f446re/module.lb +++ b/src/modm/board/nucleo_f446re/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo64_arduino.hpp", "nucleo64_arduino.hpp") diff --git a/src/modm/board/nucleo_g071rb/module.lb b/src/modm/board/nucleo_g071rb/module.lb index da40bcefa1..f3ab5dda1d 100644 --- a/src/modm/board/nucleo_g071rb/module.lb +++ b/src/modm/board/nucleo_g071rb/module.lb @@ -28,7 +28,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') diff --git a/src/modm/board/nucleo_g474re/module.lb b/src/modm/board/nucleo_g474re/module.lb index 5ccaf40715..0a2527b508 100644 --- a/src/modm/board/nucleo_g474re/module.lb +++ b/src/modm/board/nucleo_g474re/module.lb @@ -28,7 +28,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo64_arduino.hpp", "nucleo64_arduino.hpp") diff --git a/src/modm/board/nucleo_l152re/module.lb b/src/modm/board/nucleo_l152re/module.lb index 1b3f0e7838..1bd2b6a476 100644 --- a/src/modm/board/nucleo_l152re/module.lb +++ b/src/modm/board/nucleo_l152re/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo64_arduino.hpp", "nucleo64_arduino.hpp") diff --git a/src/modm/board/nucleo_l432kc/module.lb b/src/modm/board/nucleo_l432kc/module.lb index 48c8d24433..f87a49ae87 100644 --- a/src/modm/board/nucleo_l432kc/module.lb +++ b/src/modm/board/nucleo_l432kc/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/stm32l4discovery.cfg"); diff --git a/src/modm/board/nucleo_l476rg/module.lb b/src/modm/board/nucleo_l476rg/module.lb index e343e9050c..a9575a31d9 100644 --- a/src/modm/board/nucleo_l476rg/module.lb +++ b/src/modm/board/nucleo_l476rg/module.lb @@ -29,7 +29,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.copy("../nucleo64_arduino.hpp", "nucleo64_arduino.hpp") diff --git a/src/modm/board/olimexino_stm32/module.lb b/src/modm/board/olimexino_stm32/module.lb index 493812c167..6a99d67ada 100644 --- a/src/modm/board/olimexino_stm32/module.lb +++ b/src/modm/board/olimexino_stm32/module.lb @@ -30,7 +30,10 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" - env.substitutions = {"board_has_logger": True} + env.substitutions = { + "with_logger": True, + "with_assert": env.has_module(":architecture:assert") + } env.template("../board.cpp.in", "board.cpp") env.copy('.') env.collect(":build:openocd.source", "board/st_nucleo_f103rb.cfg"); diff --git a/src/modm/board/stm32f030f4p6_demo/module.lb b/src/modm/board/stm32f030f4p6_demo/module.lb index 6b4cdf09a3..4751a3b7bf 100644 --- a/src/modm/board/stm32f030f4p6_demo/module.lb +++ b/src/modm/board/stm32f030f4p6_demo/module.lb @@ -66,6 +66,11 @@ def prepare(module, options): def build(env): env.outbasepath = "modm/src/modm/board" + env.substitutions = { + "with_logger": False, + "with_assert": env.has_module(":architecture:assert") + } + env.template("../board.cpp.in", "board.cpp") env.copy('.') env.outbasepath = "modm/openocd/modm/board/" From c35d52cecd8b65a00c1bcd78d4962a31d0763d90 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Wed, 18 Mar 2020 08:06:31 +0100 Subject: [PATCH 4/4] [example] Update all examples to new assert interface --- examples/avr/assert/main.cpp | 62 +++++++++----------- examples/avr/assert/project.xml | 2 +- examples/linux/assert/main.cpp | 28 +++++---- examples/linux/assert/project.xml | 1 + examples/stm32f469_discovery/assert/main.cpp | 40 +++++++------ 5 files changed, 66 insertions(+), 67 deletions(-) diff --git a/examples/avr/assert/main.cpp b/examples/avr/assert/main.cpp index b4f0f8bd46..39fc6798c0 100644 --- a/examples/avr/assert/main.cpp +++ b/examples/avr/assert/main.cpp @@ -11,29 +11,23 @@ #include #include -#include using modm::accessor::asFlash; // Flash support on avr-gcc is so horribly broken. #define IFS(s) asFlash(PSTR(s)) -#define MODM_CAN_MODULE_NAME "can" -#define MODM_IOBUFFER_MODULE_NAME "iobuffer" -#define MODM_UART_MODULE_NAME "uart" - extern "C" void -modm_abandon(const char * module, - const char * location, - const char * failure, - uintptr_t context) +modm_abandon(const modm::AssertionInfo &info) { - serialStream << IFS("Assertion '") - << asFlash(module) << '.' - << asFlash(location) << '.' - << asFlash(failure) - << IFS("' @ ") << (void *) context - << IFS(" failed! Abandoning.") << modm::endl; + serialStream << IFS("Assertion '") << asFlash(info.name) << '\''; + if (info.context != uintptr_t(-1)) + serialStream << IFS(" @ ") << (void *)info.context << IFS(" (") << (uint32_t)info.context << IFS(")"); +#if MODM_ASSERTION_INFO_HAS_DESCRIPTION + serialStream << IFS(" failed!\n ") << asFlash(info.description) << IFS("\nAbandoning...\n"); +#else + serialStream << IFS(" failed!\nAbandoning...\n"); +#endif Leds::setOutput(); while (true) { @@ -44,57 +38,55 @@ modm_abandon(const char * module, } } + static modm::Abandonment -test_assertion_handler(const char * module, - const char * /* location */, - const char * /* failure */, - uintptr_t /* context */) +test_assertion_handler(const modm::AssertionInfo &info) { - serialStream << IFS("#1: '") << asFlash(module) << IFS("'!") << modm::endl; + serialStream << IFS("#1: '") << asFlash(info.name) << IFS("'!") << modm::endl; // The strings are located in FLASH!!! - if (strcmp_P(module, PSTR(MODM_IOBUFFER_MODULE_NAME)) == 0) + if (strncmp_P("io.", info.name, 3) == 0) { + serialStream << IFS("Ignoring assertion!") << modm::endl; return modm::Abandonment::Ignore; + } return modm::Abandonment::DontCare; } MODM_ASSERTION_HANDLER(test_assertion_handler); static modm::Abandonment -test_assertion_handler2(const char * /* module */, - const char * location, - const char * /* failure */, - uintptr_t /* context */) +test_assertion_handler2(const modm::AssertionInfo &info) { - serialStream << IFS("#2: '") << asFlash(location) << IFS("'!") << modm::endl; + serialStream << IFS("#2: '") << asFlash(info.name) << IFS("'!") << modm::endl; return modm::Abandonment::DontCare; } MODM_ASSERTION_HANDLER(test_assertion_handler2); static modm::Abandonment -test_assertion_handler3(const char * /* module */, - const char * /* location */, - const char * failure, - uintptr_t /* context */) +test_assertion_handler3(const modm::AssertionInfo &info) { - serialStream << IFS("#3: '") << asFlash(failure) << IFS("'!") << modm::endl; + serialStream << IFS("#3: '") << asFlash(info.name) << IFS("'!") << modm::endl; return modm::Abandonment::DontCare; } MODM_ASSERTION_HANDLER(test_assertion_handler3); + // ---------------------------------------------------------------------------- int main() { Board::initialize(); Leds::setOutput(); + serialStream << IFS("Starting test...\n"); - modm_assert(true, MODM_CAN_MODULE_NAME, "init", "timeout"); + // only fails for debug builds, but is ignored anyways + modm_assert_continue_fail(false, "io.tx", + "The IO transmit buffer is full!"); - modm_assert_debug(false, MODM_IOBUFFER_MODULE_NAME, "tx", "full"); + modm_assert_continue_fail_debug(false, "uart.init", "UART init failed!"); - modm_assert(false, MODM_UART_MODULE_NAME, "init", "mode"); + modm_assert(false, "can.init", "CAN init timed out!"); while (true) { - Led3::toggle(); + LedD13::toggle(); modm::delayMilliseconds(500); } } diff --git a/examples/avr/assert/project.xml b/examples/avr/assert/project.xml index 6a2e6c25e9..3d1e400663 100644 --- a/examples/avr/assert/project.xml +++ b/examples/avr/assert/project.xml @@ -1,5 +1,5 @@ - modm:al-avreb-can + modm:arduino-nano diff --git a/examples/linux/assert/main.cpp b/examples/linux/assert/main.cpp index 3a5b92978e..3fdbf044e0 100644 --- a/examples/linux/assert/main.cpp +++ b/examples/linux/assert/main.cpp @@ -11,32 +11,36 @@ #include #include - -#define MODM_CAN_MODULE_NAME "can" -#define MODM_IOBUFFER_MODULE_NAME "iobuffer" -#define MODM_UART_MODULE_NAME "uart" +#include static modm::Abandonment -test_assertion_handler(const char * module, - const char * /* location */, - const char * /* failure */, - uintptr_t /* context */) +test_assertion_handler(const modm::AssertionInfo &info) { - if (strcmp(module, MODM_IOBUFFER_MODULE_NAME) == 0) + if (strncmp(info.name, "io.", 3) == 0) { + MODM_LOG_WARNING << "Ignoring all 'io.*' assertions!" << modm::endl; return modm::Abandonment::Ignore; + } return modm::Abandonment::DontCare; } MODM_ASSERTION_HANDLER(test_assertion_handler); +static modm::Abandonment +log_assertion_handler(const modm::AssertionInfo &info) +{ + MODM_LOG_DEBUG.printf("Assertion '%s' failed!\n", info.name); + return modm::Abandonment::DontCare; +} +MODM_ASSERTION_HANDLER_DEBUG(log_assertion_handler); + // ---------------------------------------------------------------------------- int main() { - modm_assert(true, MODM_CAN_MODULE_NAME, "init", "timeout"); + modm_assert_continue_fail_debug(false, "io.tx", "IO transmit buffer is full!"); - modm_assert_debug(false, MODM_IOBUFFER_MODULE_NAME, "tx", "full"); + modm_assert_continue_fail_debug(false, "uart.init", "UART init failed!"); - modm_assert(false, MODM_UART_MODULE_NAME, "init", "mode"); + modm_assert(false, "can.init", "CAN init timed out!"); return 0; } diff --git a/examples/linux/assert/project.xml b/examples/linux/assert/project.xml index 2f8f9fc288..19f1facaf9 100644 --- a/examples/linux/assert/project.xml +++ b/examples/linux/assert/project.xml @@ -5,6 +5,7 @@ modm:platform:core + modm:architecture:assert modm:debug modm:build:scons modm:build:cmake diff --git a/examples/stm32f469_discovery/assert/main.cpp b/examples/stm32f469_discovery/assert/main.cpp index c0b6c5e6e0..7316890818 100644 --- a/examples/stm32f469_discovery/assert/main.cpp +++ b/examples/stm32f469_discovery/assert/main.cpp @@ -11,17 +11,17 @@ // ---------------------------------------------------------------------------- #include +#include +#include +using namespace std::string_literals; using namespace Board; static modm::Abandonment -test_assertion_handler(const char * module, - const char * /* location */, - const char * /* failure */, - uintptr_t /* context */) +test_assertion_handler(const modm::AssertionInfo &info) { - if (not strcmp(module, "iobuffer")) { - MODM_LOG_ERROR << "Ignoring iobuffer full!" << modm::endl; + if (not strncmp(info.name, "io.", 3)) { + MODM_LOG_ERROR << "Ignoring all io.* assertions!" << modm::endl; return modm::Abandonment::Ignore; } return modm::Abandonment::DontCare; @@ -29,17 +29,14 @@ test_assertion_handler(const char * module, MODM_ASSERTION_HANDLER(test_assertion_handler); static modm::Abandonment -core_assertion_handler(const char * module, - const char * /* location */, - const char * failure, - uintptr_t context) +core_assertion_handler(const modm::AssertionInfo &info) { - if (not memcmp(module, "core\0nvic\0undefined", 19)) { - MODM_LOG_ERROR.printf("Ignoring undefined IRQ handler %d!\n", context); + if (info.name == "nvic.undef"s) { + MODM_LOG_ERROR.printf("Ignoring undefined IRQ handler %d!\n", info.context); return modm::Abandonment::Ignore; } - if (not memcmp(module, "core\0heap", 9)) { - MODM_LOG_ERROR.printf("Ignoring 'core.heap.%s' of size 0x%x!\n", failure, context); + if ((info.name == "new"s) or (info.name == "malloc"s)) { + MODM_LOG_ERROR.printf("Ignoring '%s' of size 0x%x!\n", info.name, info.context); return modm::Abandonment::Ignore; } return modm::Abandonment::DontCare; @@ -51,10 +48,12 @@ int main() { Board::initialize(); + MODM_LOG_INFO << "\n=== RESTART ===\n" << modm::flush; // trigger an IRQ with undefined handler - NVIC_EnableIRQ(RTC_Alarm_IRQn); - NVIC_SetPendingIRQ(RTC_Alarm_IRQn); + NVIC_EnableIRQ(OTG_FS_WKUP_IRQn); + NVIC_SetPendingIRQ(OTG_FS_WKUP_IRQn); + // trigger an out of memory // we definitely don't have 32MB RAM on this board @@ -68,13 +67,16 @@ main() // does not fail, should not be optimized away volatile bool true_condition = true; - modm_assert(true_condition, "can", "init", "timeout"); + modm_assert(true_condition, "can.init", + "Can::init() function has timed out!"); // only fails for debug builds, but is ignored anyways - modm_assert_debug(false, "iobuffer", "tx", "full"); + modm_assert_continue_fail_debug(false, "io.tx", + "The IO transmit buffer is full!"); + MODM_LOG_ERROR << modm::flush; // "accidentally" return from main, without even returning properly! - // This should be caught by the debug assert core.main.exit! + // This should be caught by the debug assert main.exit! // while (true) // {}; // return 0;