diff --git a/hal/mbed_sleep_manager.c b/hal/mbed_sleep_manager.c index 8efd7bcc8e..79fbc58d52 100644 --- a/hal/mbed_sleep_manager.c +++ b/hal/mbed_sleep_manager.c @@ -14,18 +14,140 @@ * limitations under the License. */ +#include "mbed_assert.h" #include "mbed_power_mgmt.h" #include "mbed_critical.h" #include "sleep_api.h" #include "mbed_error.h" +#include "mbed_debug.h" #include +#include #if DEVICE_SLEEP // deep sleep locking counter. A target is allowed to deep sleep if counter == 0 static uint16_t deep_sleep_lock = 0U; -void sleep_manager_lock_deep_sleep(void) +#ifdef MBED_SLEEP_TRACING_ENABLED + +// Length of the identifier extracted from the driver name to store for logging. +#define IDENTIFIER_WIDTH 15 +// Number of drivers that can be stored in the structure +#define STATISTIC_COUNT 10 + +typedef struct sleep_statistic { + char identifier[IDENTIFIER_WIDTH]; + uint8_t count; +} sleep_statistic_t; + +static sleep_statistic_t sleep_stats[STATISTIC_COUNT]; + +static const char* strip_path(const char* const filename) +{ + char *output = strrchr(filename, '/'); + + if (output != NULL) { + return output + 1; + } + + output = strrchr(filename, '\\'); + + if (output != NULL) { + return output + 1; + } + + return filename; +} + +static sleep_statistic_t* sleep_tracker_find(const char *const filename) +{ + char temp[IDENTIFIER_WIDTH]; + strncpy(temp, filename, IDENTIFIER_WIDTH); + temp[IDENTIFIER_WIDTH - 1] = '\0'; + + // Search for the a driver matching the current name and return it's index + for (int i = 0; i < STATISTIC_COUNT; ++i) { + if (strcmp(sleep_stats[i].identifier, temp) == 0) { + return &sleep_stats[i]; + } + } + + return NULL; +} + +static sleep_statistic_t* sleep_tracker_add(const char* const filename) +{ + char temp[IDENTIFIER_WIDTH]; + strncpy(temp, filename, IDENTIFIER_WIDTH); + temp[IDENTIFIER_WIDTH - 1] = '\0'; + + for (int i = 0; i < STATISTIC_COUNT; ++i) { + if (sleep_stats[i].identifier[0] == '\0') { + core_util_critical_section_enter(); + strncpy(sleep_stats[i].identifier, temp, sizeof(temp)); + core_util_critical_section_exit(); + + return &sleep_stats[i]; + } + } + + debug("No free indexes left to use in mbed sleep tracker.\r\n"); + + return NULL; +} + +static void sleep_tracker_print_stats(void) +{ + debug("Sleep locks held:\r\n"); + for (int i = 0; i < STATISTIC_COUNT; ++i) { + if (sleep_stats[i].count == 0) { + continue; + } + + if (sleep_stats[i].identifier[0] == '\0') { + return; + } + + debug("[id: %s, count: %u]\r\n", sleep_stats[i].identifier, + sleep_stats[i].count); + } +} + +void sleep_tracker_lock(const char* const filename, int line) +{ + const char* const stripped_path = strip_path(filename); + + sleep_statistic_t* stat = sleep_tracker_find(stripped_path); + + // Entry for this driver does not exist, create one. + if (stat == NULL) { + stat = sleep_tracker_add(stripped_path); + } + + core_util_atomic_incr_u8(&stat->count, 1); + + debug("LOCK: %s, ln: %i, lock count: %u\r\n", stripped_path, line, deep_sleep_lock); +} + +void sleep_tracker_unlock(const char* const filename, int line) +{ + const char* const stripped_path = strip_path(filename); + sleep_statistic_t* stat = sleep_tracker_find(stripped_path); + + // Entry for this driver does not exist, something went wrong. + if (stat == NULL) { + debug("Unlocking sleep for driver that was not previously locked: %s, ln: %i\r\n", stripped_path, line); + return; + } + + core_util_atomic_decr_u8(&stat->count, 1); + + debug("UNLOCK: %s, ln: %i, lock count: %u\r\n", stripped_path, line, deep_sleep_lock); +} + +#endif // MBED_SLEEP_TRACING_ENABLED + +void sleep_manager_lock_deep_sleep_internal(void) { core_util_critical_section_enter(); if (deep_sleep_lock == USHRT_MAX) { @@ -36,7 +158,7 @@ void sleep_manager_lock_deep_sleep(void) core_util_critical_section_exit(); } -void sleep_manager_unlock_deep_sleep(void) +void sleep_manager_unlock_deep_sleep_internal(void) { core_util_critical_section_enter(); if (deep_sleep_lock == 0) { @@ -54,6 +176,9 @@ bool sleep_manager_can_deep_sleep(void) void sleep_manager_sleep_auto(void) { +#ifdef MBED_SLEEP_TRACING_ENABLED + sleep_tracker_print_stats(); +#endif core_util_critical_section_enter(); // debug profile should keep debuggers attached, no deep sleep allowed #ifdef MBED_DEBUG @@ -73,12 +198,12 @@ void sleep_manager_sleep_auto(void) // locking is valid only if DEVICE_SLEEP is defined // we provide empty implementation -void sleep_manager_lock_deep_sleep(void) +void sleep_manager_lock_deep_sleep_internal(void) { } -void sleep_manager_unlock_deep_sleep(void) +void sleep_manager_unlock_deep_sleep_internal(void) { } diff --git a/platform/mbed_power_mgmt.h b/platform/mbed_power_mgmt.h index 9efbaca411..c70fb515f9 100644 --- a/platform/mbed_power_mgmt.h +++ b/platform/mbed_power_mgmt.h @@ -23,7 +23,7 @@ #ifndef MBED_POWER_MGMT_H #define MBED_POWER_MGMT_H -#include "hal/sleep_api.h" +#include "sleep_api.h" #include "mbed_toolchain.h" #include @@ -63,6 +63,34 @@ extern "C" { * } * @endcode */ +#ifdef MBED_SLEEP_TRACING_ENABLED + +void sleep_tracker_lock(const char *const filename, int line); +void sleep_tracker_unlock(const char *const filename, int line); + +#define sleep_manager_lock_deep_sleep() \ + do \ + { \ + sleep_manager_lock_deep_sleep_internal(); \ + sleep_tracker_lock(__FILE__, __LINE__); \ + } while (0); + +#define sleep_manager_unlock_deep_sleep() \ + do \ + { \ + sleep_manager_unlock_deep_sleep_internal(); \ + sleep_tracker_unlock(__FILE__, __LINE__); \ + } while (0); + +#else + +#define sleep_manager_lock_deep_sleep() \ + sleep_manager_lock_deep_sleep_internal() + +#define sleep_manager_unlock_deep_sleep() \ + sleep_manager_unlock_deep_sleep_internal() + +#endif // MBED_SLEEP_TRACING_ENABLED /** Lock the deep sleep mode * @@ -76,7 +104,7 @@ extern "C" { * The lock is a counter, can be locked up to USHRT_MAX * This function is IRQ and thread safe */ -void sleep_manager_lock_deep_sleep(void); +void sleep_manager_lock_deep_sleep_internal(void); /** Unlock the deep sleep mode * @@ -85,7 +113,7 @@ void sleep_manager_lock_deep_sleep(void); * The lock is a counter, should be equally unlocked as locked * This function is IRQ and thread safe */ -void sleep_manager_unlock_deep_sleep(void); +void sleep_manager_unlock_deep_sleep_internal(void); /** Get the status of deep sleep allowance for a target * @@ -93,6 +121,7 @@ void sleep_manager_unlock_deep_sleep(void); */ bool sleep_manager_can_deep_sleep(void); + /** Enter auto selected sleep mode. It chooses the sleep or deeepsleep modes based * on the deepsleep locking counter * diff --git a/platform/mbed_sleep.h b/platform/mbed_sleep.h index c29549f331..89b8b4ec3f 100644 --- a/platform/mbed_sleep.h +++ b/platform/mbed_sleep.h @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifndef MBED_MBED_SLEEP_H #define MBED_MBED_SLEEP_H