|
| 1 | +#pragma once |
| 2 | + |
| 3 | +//! @file |
| 4 | +//! |
| 5 | +//! Copyright (c) Memfault, Inc. |
| 6 | +//! See License.txt for details |
| 7 | +//! |
| 8 | +//! @brief |
| 9 | +//! Task watchdog API. |
| 10 | +//! |
| 11 | +//! This module implements a task watchdog that can be used to detect stuck RTOS |
| 12 | +//! tasks. |
| 13 | +//! |
| 14 | +//! Example usage: |
| 15 | +//! |
| 16 | +//! // Initialize the task watchdog system on system start |
| 17 | +//! memfault_task_watchdog_init(); |
| 18 | +//! |
| 19 | +//! // In a task loop |
| 20 | +//! void example_task_loop(void) { |
| 21 | +//! while(1) { |
| 22 | +//! rtos_wait_for_task_to_wake_up(); |
| 23 | +//! |
| 24 | +//! // Example: Use the task watchdog to monitor a block of work |
| 25 | +//! MEMFAULT_TASK_WATCHDOG_START(example_task_loop_channel_id); |
| 26 | +//! // do work that should be monitored by the watchdog |
| 27 | +//! MEMFAULT_TASK_WATCHDOG_STOP(example_task_loop_channel_id); |
| 28 | +//! |
| 29 | +//! // Example: Use the task watchdog to monitor a repeated operation |
| 30 | +//! MEMFAULT_TASK_WATCHDOG_START(example_task_loop_channel_id); |
| 31 | +//! // feeding the watchdog is only necessary if in some long-running task |
| 32 | +//! // that runs the risk of expiring the timeout, but is not expected to be |
| 33 | +//! // stuck in between calls |
| 34 | +//! for (size_t i = 0; i < 100; i++) { |
| 35 | +//! MEMFAULT_TASK_WATCHDOG_FEED(example_task_loop_channel_id); |
| 36 | +//! run_slow_repeated_task(); |
| 37 | +//! } |
| 38 | +//! MEMFAULT_TASK_WATCHDOG_STOP(example_task_loop_channel_id); |
| 39 | +//! |
| 40 | +//! rtos_put_task_to_sleep(); |
| 41 | +//! } |
| 42 | +//! } |
| 43 | +//! |
| 44 | +//! // In either a standalone timer, or a periodic background task, call the |
| 45 | +//! // memfault_task_watchdog_check_all function |
| 46 | +//! void example_task_watchdog_timer_cb(void) { |
| 47 | +//! memfault_task_watchdog_check_all(); |
| 48 | +//! } |
| 49 | +//! |
| 50 | +//! // Call memfault_task_watchdog_bookkeep() in |
| 51 | +//! // memfault_platform_coredump_save_begin()- this updates all the task |
| 52 | +//! // channel timeouts, in the event that the task monitor wasn't able to run |
| 53 | +//! // for some reason. Also might be appropriate to refresh other system |
| 54 | +//! // watchdogs prior to executing the coredump save. |
| 55 | +//! bool memfault_platform_coredump_save_begin(void) { |
| 56 | +//! memfault_task_watchdog_bookkeep(); |
| 57 | +//! // may also need to refresh other watchdogs here, for example: |
| 58 | +//! memfault_task_watchdog_platform_refresh_callback(); |
| 59 | +//! return true; |
| 60 | +//! } |
| 61 | + |
| 62 | +#include <stddef.h> |
| 63 | + |
| 64 | +#include "memfault/config.h" |
| 65 | + |
| 66 | +#ifdef __cplusplus |
| 67 | +extern "C" { |
| 68 | +#endif |
| 69 | + |
| 70 | +//! Declare the task watchdog channel IDs. These shouldn't be used directly, but |
| 71 | +//! should be passed to the MEMFAULT_TASK_WATCHDOG_START etc. functions with the |
| 72 | +//! name declared in the memfault_task_watchdog_config.def file. |
| 73 | +#define MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_) kMemfaultTaskWatchdogChannel_##channel_, |
| 74 | +typedef enum { |
| 75 | +#if MEMFAULT_TASK_WATCHDOG_ENABLE |
| 76 | +#include "memfault_task_watchdog_config.def" |
| 77 | +#else |
| 78 | + // define one channel to prevent the compiler from complaining about a zero-length array |
| 79 | + MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(placeholder_) |
| 80 | +#endif |
| 81 | + kMemfaultTaskWatchdogChannel_NumChannels, |
| 82 | +} eMemfaultTaskWatchdogChannel; |
| 83 | +#undef MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE |
| 84 | +#define MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_) kMemfaultTaskWatchdogChannel_##channel_ |
| 85 | + |
| 86 | +//! Initialize (or reset) the task watchdog system. This will stop and reset all |
| 87 | +//! internal bookkeeping for all channels. |
| 88 | +void memfault_task_watchdog_init(void); |
| 89 | + |
| 90 | +//! This function should be called periodically (for example, around the |
| 91 | +//! MEMFAULT_TASK_WATCHDOG_TIMEOUT_INTERVAL_MS period). It checks if any task |
| 92 | +//! watchdog channel has reached the timeout interval. If so, it will trigger a |
| 93 | +//! task watchdog assert. |
| 94 | +void memfault_task_watchdog_check_all(void); |
| 95 | + |
| 96 | +//! As in `memfault_task_watchdog_check_all`, but only updates the internal |
| 97 | +//! bookkeeping, and does not trigger any callbacks or asserts. |
| 98 | +//! |
| 99 | +//! Intended to be called just prior to coredump capture when the system is in |
| 100 | +//! the fault_handler. |
| 101 | +void memfault_task_watchdog_bookkeep(void); |
| 102 | + |
| 103 | +//! Start a task watchdog channel. After being started, a channel will now be |
| 104 | +//! eligible for expiration. Also resets the timeout interval for the channel. |
| 105 | +#define MEMFAULT_TASK_WATCHDOG_START(channel_id_) \ |
| 106 | + memfault_task_watchdog_start(MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_id_)) |
| 107 | + |
| 108 | +//! Reset the timeout interval for a task watchdog channel. |
| 109 | +#define MEMFAULT_TASK_WATCHDOG_FEED(channel_id_) \ |
| 110 | + memfault_task_watchdog_feed(MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_id_)) |
| 111 | + |
| 112 | +//! Stop a task watchdog channel. After being stopped, a channel will no longer |
| 113 | +//! be eligible for expiration and is reset |
| 114 | +#define MEMFAULT_TASK_WATCHDOG_STOP(channel_id_) \ |
| 115 | + memfault_task_watchdog_stop(MEMFAULT_TASK_WATCHDOG_CHANNEL_DEFINE(channel_id_)) |
| 116 | + |
| 117 | +//! These functions should not be used directly, but instead through the macros |
| 118 | +//! above. |
| 119 | +void memfault_task_watchdog_start(eMemfaultTaskWatchdogChannel channel_id); |
| 120 | +void memfault_task_watchdog_feed(eMemfaultTaskWatchdogChannel channel_id); |
| 121 | +void memfault_task_watchdog_stop(eMemfaultTaskWatchdogChannel channel_id); |
| 122 | + |
| 123 | +//! Optional weakly defined function to perform any additional actions during |
| 124 | +//! `memfault_task_watchdog_check_all` when no channels have expired |
| 125 | +void memfault_task_watchdog_platform_refresh_callback(void); |
| 126 | + |
| 127 | +#ifdef __cplusplus |
| 128 | +} |
| 129 | +#endif |
0 commit comments