Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

debug_irq_disable: add module to debug time spent in irq_disable #18795

Merged
merged 3 commits into from
Nov 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/lib/include/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extern "C" {
#define MAYBE_INLINE
#endif /* IRQ_API_INLINED */

#ifndef IRQ_API_INLINED
/**
* @brief This function sets the IRQ disable bit in the status register
*
Expand Down Expand Up @@ -89,7 +90,7 @@ MAYBE_INLINE bool irq_is_enabled(void);
*/
MAYBE_INLINE bool irq_is_in(void);

#ifdef IRQ_API_INLINED
#else
#include "irq_arch.h"
#endif /* IRQ_API_INLINED */

Expand Down
61 changes: 54 additions & 7 deletions cpu/cortexm_common/include/irq_arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,54 @@
#include <stdbool.h>
#include <stdint.h>
#include "cpu_conf.h"
#include "kernel_defines.h"
#include "debug_irq_disable.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Start SysTick timer to measure time spent with IRQ disabled
*/
static inline void _irq_debug_start_count(void)
{
SysTick->VAL = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this work better if we keep the systick running and store the lock time

and than return reload_mask & ( unlock_time -locktime)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you think that's better? We'd need an extra variable and start / stop would no longer by symmetric, we'd need an extra function to start the SysTick.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way this PR does it is: ether use systick for this xor for something else (e.g.: your countdowntimer)
The way would allow you to have any Initialisation on that systick (counting down or systicking around) and do this and all that needs is static uint32_t

i am not sure if it also would be quicker since these are 3 memory write accesses and mine has 1 read and 1 write for start and this has 1 read and 1 write for stop vs mine has 2 reads.

Copy link
Contributor Author

@benpicco benpicco Nov 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Countdown timer wouldn't run the SysTick continuously either, only when there is a countdown scheduled.
It would be incompatible with this either way, if merged.

And I'm not that concerned about a few instructions here - the UART printouts will ensure that this is purely a debug feature and can't be used for anything performance critical 😉

SysTick->LOAD = SysTick_LOAD_RELOAD_Msk;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
}

/**
* @brief Stop SysTick timer, return time spent with IRQ disabled
*/
static inline uint32_t _irq_debug_stop_count(void)
{
uint32_t ticks = SysTick_LOAD_RELOAD_Msk - SysTick->VAL;
SysTick->CTRL = 0;
return ticks;
}

/**
* @brief Disable all maskable interrupts
*/
static inline __attribute__((always_inline)) unsigned int irq_disable(void)
static inline __attribute__((always_inline))
unsigned int irq_disable(void)
{
uint32_t mask = __get_PRIMASK();

if ((mask == 0) && IS_USED(MODULE_DEBUG_IRQ_DISABLE)) {
_irq_debug_start_count();
}

__disable_irq();
return mask;
}

/**
* @brief Enable all maskable interrupts
*/
static inline __attribute__((always_inline)) __attribute__((used)) unsigned int
irq_enable(void)
static inline __attribute__((always_inline)) __attribute__((used))
unsigned int irq_enable(void)
{
unsigned result = __get_PRIMASK();

Expand All @@ -53,16 +80,35 @@ irq_enable(void)
/**
* @brief Restore the state of the IRQ flags
*/
static inline __attribute__((always_inline)) void irq_restore(
unsigned int state)
static inline __attribute__((always_inline))
#if !IS_USED(MODULE_DEBUG_IRQ_DISABLE)
void irq_restore(unsigned int state)
{
__set_PRIMASK(state);
}
#else
void _irq_restore(unsigned int state, const char *file, unsigned line)
{
uint32_t ticks = 0;

if (state == 0) {
ticks = _irq_debug_stop_count();
}

__set_PRIMASK(state);

if (ticks) {
debug_irq_disable_print(file, line, ticks);
}
}
#define irq_restore(state) _irq_restore(state, __FILE__, __LINE__);
#endif /* MODULE_DEBUG_IRQ_DISABLE */

/**
* @brief See if IRQs are currently enabled
*/
static inline __attribute__((always_inline)) bool irq_is_enabled(void)
static inline __attribute__((always_inline))
bool irq_is_enabled(void)
{
/* so far, all existing Cortex-M are only using the least significant bit
* in the PRIMARK register. If ever any other bit is used for different
Expand All @@ -73,7 +119,8 @@ static inline __attribute__((always_inline)) bool irq_is_enabled(void)
/**
* @brief See if the current context is inside an ISR
*/
static inline __attribute__((always_inline)) bool irq_is_in(void)
static inline __attribute__((always_inline))
bool irq_is_in(void)
{
return (__get_IPSR() & 0xFF);
}
Expand Down
1 change: 1 addition & 0 deletions sys/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ rsource "congure/Kconfig"
rsource "cpp11-compat/Kconfig"
rsource "cpp_new_delete/Kconfig"
rsource "cxx_ctor_guards/Kconfig"
rsource "debug_irq_disable/Kconfig"
rsource "div/Kconfig"
rsource "embunit/Kconfig"
rsource "entropy_source/Kconfig"
Expand Down
4 changes: 4 additions & 0 deletions sys/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ ifneq (,$(filter crc32_fast,$(USEMODULE)))
USEMODULE += checksum
endif

ifneq (,$(filter debug_irq_disable,$(USEMODULE)))
USEMODULE += fmt
endif

ifneq (,$(filter eepreg,$(USEMODULE)))
FEATURES_REQUIRED += periph_eeprom
endif
Expand Down
21 changes: 21 additions & 0 deletions sys/debug_irq_disable/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (C) 2022 Benjamin Valentin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#

menuconfig MODULE_DEBUG_IRQ_DISABLE
bool "Measure IRQ disable durations"
depends on TEST_KCONFIG
depends on CPU_CORE_CORTEX_M
help
Print time spent with IRQs disabled

config DEBUG_IRQ_DISABLE_THRESHOLD
int "Suppress Threshold"
default 1
depends on MODULE_DEBUG_IRQ_DISABLE
help
Threshold (in CPU ticks) below which periods with IRQs disabled are not printed.
Use this to prevent *a lot* of output when debugging.
1 change: 1 addition & 0 deletions sys/debug_irq_disable/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base
54 changes: 54 additions & 0 deletions sys/debug_irq_disable/debug_irq_disable.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2022 ML!PA Consulting GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup debug_irq_disable
* @{
*
* @file
* @brief Helper for debug_irq_disable
*
* @author Benjamin Valentin <[email protected]>
* @}
*/

#include <stdbool.h>
#include "fmt.h"
#include "debug_irq_disable.h"

void debug_irq_disable_print(const char *file, unsigned line, uint32_t ticks)
{
static unsigned is_printing;
static unsigned init_skip = 10;

/* if we try to print before libc is initialized, we will hard fault */
if (init_skip && --init_skip) {
return;
}

if (is_printing) {
return;
}

if (ticks < CONFIG_DEBUG_IRQ_DISABLE_THRESHOLD) {
return;
}

/* prevent infinite recursion if stdio driver uses irq_disable() */
++is_printing;

print_str("irq disabled for ");
print_u32_dec(ticks);
print_str(" ticks in ");
print_str(file);
print_str(":");
print_u32_dec(line);
print_str("\n");

--is_printing;
}
54 changes: 54 additions & 0 deletions sys/include/debug_irq_disable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2022 ML!PA Consulting GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @defgroup debug_irq_disable IRQ Disable Debug helper
* @ingroup sys
* @brief Debug time spent with IRQ disabled
* @{
*
* @file
*
* @author Benjamin Valentin <[email protected]>
*/

#ifndef DEBUG_IRQ_DISABLE_H
#define DEBUG_IRQ_DISABLE_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Threshold (in CPU ticks) below which periods with IRQs
* disabled are not printed.
*
* Use this to prevent *a lot* of output when debugging.
*/
#ifndef CONFIG_DEBUG_IRQ_DISABLE_THRESHOLD
#define CONFIG_DEBUG_IRQ_DISABLE_THRESHOLD (1)
#endif

/**
* @brief Print time spent with IRQ disabled
* @internal
*
* @param[in] file file where irq_restore() was called
* @param[in] line line where irq_restore() was called
* @param[in] ticks CPU ticks spent with IRQ disabled
*/
void debug_irq_disable_print(const char *file, unsigned line, uint32_t ticks);

#ifdef __cplusplus
}
#endif

/** @} */
#endif /* DEBUG_IRQ_DISABLE_H */