From 502797c0b3e1820be1144daa45e9df6d1e563891 Mon Sep 17 00:00:00 2001 From: Mason Tran Date: Mon, 29 Apr 2024 12:09:54 -0400 Subject: [PATCH] [spinel] add support for logging crash dumps (#10061) This commit adds a new feature that allows platforms to log crash logs. ### Additions - `void otPlatLogCrashDump(void)` - API that logs a crash dump using OpenThread Logging APIs - `SPINEL_PROP_RCP_LOG_CRASH_DUMP` - spinel prop that calls `otPlatLogCrashDump()` when `Set` - `SPINEL_CAP_RCP_LOG_CRASH_DUMP` - spinel capability denoting that a RCP supports logging crash dumps ### Usage - For `ot-cli-ftd|mtd`, `otPlatLogCrashDump()` is called at the end of app initialization, before the main loop. See [main.c] - For Host/RCP setups, during initialization, the Host gets the RCP capabilities. If the RCP has the `SPINEL_CAP_RCP_LOG_CRASH_DUMP` capability, the Host will `Set` the `SPINEL_PROP_RCP_LOG_CRASH_DUMP` property, triggering the RCP to call `otPlatLogCrashDump()`. - If RCP Recovery is enabled, the this will also happen once the RCP has been restored --- etc/cmake/options.cmake | 1 + examples/apps/cli/main.c | 5 +++++ examples/platforms/simulation/misc.c | 11 +++++++++++ include/openthread/platform/misc.h | 10 ++++++++++ script/test | 2 ++ src/core/config/misc.h | 14 ++++++++++++++ src/lib/spinel/logger.cpp | 9 +++++++++ src/lib/spinel/radio_spinel.cpp | 14 +++++++++++++- src/lib/spinel/radio_spinel.hpp | 1 + src/lib/spinel/spinel.c | 2 ++ src/lib/spinel/spinel.h | 11 +++++++++++ src/ncp/ncp_base.cpp | 4 ++++ src/ncp/ncp_base_dispatcher.cpp | 8 ++++++++ src/ncp/ncp_base_radio.cpp | 5 +++++ tests/unit/test_platform.cpp | 4 ++++ 15 files changed, 100 insertions(+), 1 deletion(-) diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index eb41ec875..f1b1eb0cd 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -233,6 +233,7 @@ ot_option(OT_OTNS OPENTHREAD_CONFIG_OTNS_ENABLE "OTNS") ot_option(OT_PING_SENDER OPENTHREAD_CONFIG_PING_SENDER_ENABLE "ping sender" ${OT_APP_CLI}) ot_option(OT_PLATFORM_BOOTLOADER_MODE OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE "platform bootloader mode") ot_option(OT_PLATFORM_KEY_REF OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE "platform key reference secure storage") +ot_option(OT_PLATFORM_LOG_CRASH_DUMP OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE "platform log crash dump") ot_option(OT_PLATFORM_NETIF OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE "platform netif") ot_option(OT_PLATFORM_POWER_CALIBRATION OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE "power calibration") ot_option(OT_PLATFORM_UDP OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE "platform UDP") diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c index 62f7e3f1b..d0c6be2dd 100644 --- a/examples/apps/cli/main.c +++ b/examples/apps/cli/main.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "openthread-system.h" #include "cli/cli_config.h" @@ -140,6 +141,10 @@ int main(int argc, char *argv[]) IgnoreError(otCliSetUserCommands(kCommands, OT_ARRAY_LENGTH(kCommands), instance)); #endif +#if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE + otPlatLogCrashDump(); +#endif + while (!otSysPseudoResetWasRequested()) { otTaskletsProcess(instance); diff --git a/examples/platforms/simulation/misc.c b/examples/platforms/simulation/misc.c index 07739a2fe..a6d73370c 100644 --- a/examples/platforms/simulation/misc.c +++ b/examples/platforms/simulation/misc.c @@ -31,6 +31,7 @@ #include #include +#include #include #include "openthread-system.h" @@ -108,3 +109,13 @@ otPlatMcuPowerState otPlatGetMcuPowerState(otInstance *aInstance) return gPlatMcuPowerState; } + +#if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE +otError otPlatLogCrashDump(void) +{ + otLogCritPlat("LOGGING SIMULATED CRASH DUMP"); + otLogCritPlat("Reset Reason: %d", sPlatResetReason); + + return OT_ERROR_NONE; +} +#endif diff --git a/include/openthread/platform/misc.h b/include/openthread/platform/misc.h index b1b202111..ecde9bae6 100644 --- a/include/openthread/platform/misc.h +++ b/include/openthread/platform/misc.h @@ -207,6 +207,16 @@ otError otPlatSetMcuPowerState(otInstance *aInstance, otPlatMcuPowerState aState */ otPlatMcuPowerState otPlatGetMcuPowerState(otInstance *aInstance); +/** + * Logs a crash dump using OpenThread logging APIs + * + * @note This API is an optional logging platform API. It's up to the platform layer to implement it. + * + * @retval OT_ERROR_NONE Crash dump was logged successfully + * @retval OT_ERROR_NOT_CAPABLE Platform is not capable of logging a crash dump + */ +otError otPlatLogCrashDump(void); + /** * @} * diff --git a/script/test b/script/test index a9a71b3b5..ceab700ef 100755 --- a/script/test +++ b/script/test @@ -104,6 +104,7 @@ build_simulation() "-DOT_MESSAGE_USE_HEAP=OFF" "-DOT_NETDATA_PUBLISHER=ON" "-DOT_PING_SENDER=ON" + "-DOT_PLATFORM_LOG_CRASH_DUMP=ON" "-DOT_REFERENCE_DEVICE=ON" "-DOT_SERVICE=ON" "-DOT_SRP_CLIENT=ON" @@ -173,6 +174,7 @@ build_posix() "-DBUILD_TESTING=ON" "-DOT_MESSAGE_USE_HEAP=ON" "-DOT_PLATFORM_BOOTLOADER_MODE=ON" + "-DOT_PLATFORM_LOG_CRASH_DUMP=ON" "-DOT_THREAD_VERSION=${version}" ) diff --git a/src/core/config/misc.h b/src/core/config/misc.h index e764b8872..56dd92bed 100644 --- a/src/core/config/misc.h +++ b/src/core/config/misc.h @@ -609,6 +609,20 @@ #define OPENTHREAD_CONFIG_BLE_TCAT_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE + * + * Define to 1 to enable crash dump logging. + * + * On platforms that support crash dump logging, this feature will log a crash dump using the OT Debug Log service. + * + * Logging a crash dump requires the platform to implement the `otPlatLogCrashDump()` function. + * + */ +#ifndef OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE +#define OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE 0 +#endif + /** * @} * diff --git a/src/lib/spinel/logger.cpp b/src/lib/spinel/logger.cpp index 46636c7f1..c01761343 100644 --- a/src/lib/spinel/logger.cpp +++ b/src/lib/spinel/logger.cpp @@ -340,6 +340,15 @@ void Logger::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx) } break; + case SPINEL_PROP_RCP_LOG_CRASH_DUMP: + { + const char *name; + name = "log-crash-dump"; + + start += Snprintf(start, static_cast(end - start), ", %s", name); + } + break; + case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT: case SPINEL_PROP_PHY_CHAN_MAX_POWER: { diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 87abaef93..65d2123f7 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -64,6 +64,8 @@ bool RadioSpinel::sSupportsLogStream = bool RadioSpinel::sSupportsResetToBootloader = false; ///< RCP supports resetting into bootloader mode. +bool RadioSpinel::sSupportsLogCrashDump = false; ///< RCP supports logging a crash dump. + otRadioCaps RadioSpinel::sRadioCaps = OT_RADIO_CAPS_NONE; RadioSpinel::RadioSpinel(void) @@ -147,6 +149,12 @@ void RadioSpinel::Init(SpinelInterface &aSpinelInterface, SuccessOrExit(error = Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, sIeeeEui64.m8)); InitializeCaps(supportsRcpApiVersion, supportsRcpMinHostApiVersion); + if (sSupportsLogCrashDump) + { + LogDebg("RCP supports crash dump logging. Requesting crash dump."); + SuccessOrExit(error = Set(SPINEL_PROP_RCP_LOG_CRASH_DUMP, nullptr)); + } + if (!aSkipRcpCompatibilityCheck) { SuccessOrDie(CheckRcpApiVersion(supportsRcpApiVersion, supportsRcpMinHostApiVersion)); @@ -212,8 +220,9 @@ void RadioSpinel::InitializeCaps(bool &aSupportsRcpApiVersion, bool &aSupportsRc sSupportsLogStream = mSpinelDriver.CoprocessorHasCap(SPINEL_CAP_OPENTHREAD_LOG_METADATA); aSupportsRcpApiVersion = mSpinelDriver.CoprocessorHasCap(SPINEL_CAP_RCP_API_VERSION); - sSupportsResetToBootloader = mSpinelDriver.CoprocessorHasCap(SPINEL_CAP_RCP_RESET_TO_BOOTLOADER); aSupportsRcpMinHostApiVersion = mSpinelDriver.CoprocessorHasCap(SPINEL_CAP_RCP_MIN_HOST_API_VERSION); + sSupportsResetToBootloader = mSpinelDriver.CoprocessorHasCap(SPINEL_CAP_RCP_RESET_TO_BOOTLOADER); + sSupportsLogCrashDump = mSpinelDriver.CoprocessorHasCap(SPINEL_CAP_RCP_LOG_CRASH_DUMP); } otError RadioSpinel::CheckRadioCapabilities(void) @@ -2001,6 +2010,9 @@ void RadioSpinel::RecoverFromRcpFailure(void) } --mRcpFailureCount; + + SuccessOrDie(Set(SPINEL_PROP_RCP_LOG_CRASH_DUMP, nullptr)); + LogNote("RCP recovery is done"); exit: diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 1117cfb58..6eb5e4e4c 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -1247,6 +1247,7 @@ class RadioSpinel : private Logger static bool sIsReady; ///< NCP ready. static bool sSupportsLogStream; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format. static bool sSupportsResetToBootloader; ///< RCP supports resetting into bootloader mode. + static bool sSupportsLogCrashDump; ///< RCP supports logging a crash dump. #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index c85d2444c..683e58fdf 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -1427,6 +1427,7 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_SERVER_LEADER_SERVICES, "SERVER_LEADER_SERVICES"}, {SPINEL_PROP_RCP_API_VERSION, "RCP_API_VERSION"}, {SPINEL_PROP_RCP_MIN_HOST_API_VERSION, "RCP_MIN_HOST_API_VERSION"}, + {SPINEL_PROP_RCP_LOG_CRASH_DUMP, "RCP_LOG_CRASH_DUMP"}, {SPINEL_PROP_UART_BITRATE, "UART_BITRATE"}, {SPINEL_PROP_UART_XON_XOFF, "UART_XON_XOFF"}, {SPINEL_PROP_15_4_PIB_PHY_CHANNELS_SUPPORTED, "15_4_PIB_PHY_CHANNELS_SUPPORTED"}, @@ -1608,6 +1609,7 @@ const char *spinel_capability_to_cstr(spinel_capability_t capability) {SPINEL_CAP_RCP_API_VERSION, "RCP_API_VERSION"}, {SPINEL_CAP_RCP_MIN_HOST_API_VERSION, "RCP_MIN_HOST_API_VERSION"}, {SPINEL_CAP_RCP_RESET_TO_BOOTLOADER, "RCP_RESET_TO_BOOTLOADER"}, + {SPINEL_CAP_RCP_LOG_CRASH_DUMP, "RCP_LOG_CRASH_DUMP"}, {SPINEL_CAP_MAC_ALLOWLIST, "MAC_ALLOWLIST"}, {SPINEL_CAP_MAC_RAW, "MAC_RAW"}, {SPINEL_CAP_OOB_STEERING_DATA, "OOB_STEERING_DATA"}, diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index c1865f373..4d5fc28d9 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -1296,6 +1296,7 @@ enum SPINEL_CAP_RCP_API_VERSION = (SPINEL_CAP_RCP__BEGIN + 0), SPINEL_CAP_RCP_MIN_HOST_API_VERSION = (SPINEL_CAP_RCP__BEGIN + 1), SPINEL_CAP_RCP_RESET_TO_BOOTLOADER = (SPINEL_CAP_RCP__BEGIN + 2), + SPINEL_CAP_RCP_LOG_CRASH_DUMP = (SPINEL_CAP_RCP__BEGIN + 3), SPINEL_CAP_RCP__END = 80, SPINEL_CAP_OPENTHREAD__BEGIN = 512, @@ -4399,6 +4400,16 @@ enum */ SPINEL_PROP_RCP_MIN_HOST_API_VERSION = SPINEL_PROP_RCP__BEGIN + 1, + /// Crash Dump + /** Format: Empty : Write only + * + * Required capability: SPINEL_CAP_RADIO and SPINEL_CAP_RCP_LOG_CRASH_DUMP. + * + * Writing to this property instructs the RCP to log a crash dump if available. + * + */ + SPINEL_PROP_RCP_LOG_CRASH_DUMP = SPINEL_PROP_RCP__BEGIN + 2, + SPINEL_PROP_RCP__END = 0xFF, SPINEL_PROP_INTERFACE__BEGIN = 0x100, diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 96e362575..6b99b4d4a 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -1947,6 +1947,10 @@ template <> otError NcpBase::HandlePropertyGet(void) SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RCP_RESET_TO_BOOTLOADER)); #endif +#if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE + SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RCP_LOG_CRASH_DUMP)); +#endif + #if OPENTHREAD_PLATFORM_POSIX SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_POSIX)); #endif diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 48bd97e82..112f5022f 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -479,6 +479,13 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SERVER_ALLOW_LOCAL_DATA_CHANGE), #endif +#endif // OPENTHREAD_MTD || OPENTHREAD_FTD + +#if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE && OPENTHREAD_RADIO + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_LOG_CRASH_DUMP), +#endif + +#if OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_CNTR_RESET), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_CNTR_ALL_MAC_COUNTERS), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_CNTR_MLE_COUNTERS), @@ -487,6 +494,7 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_CNTR_MAC_RETRY_HISTOGRAM), #endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD + #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_MAC_KEY), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_MAC_FRAME_COUNTER), diff --git a/src/ncp/ncp_base_radio.cpp b/src/ncp/ncp_base_radio.cpp index cad3ef52d..299f34100 100644 --- a/src/ncp/ncp_base_radio.cpp +++ b/src/ncp/ncp_base_radio.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -614,6 +615,10 @@ template <> otError NcpBase::HandlePropertySet( } #endif +#if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE +template <> otError NcpBase::HandlePropertySet(void) { return otPlatLogCrashDump(); } +#endif + } // namespace Ncp } // namespace ot diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 496dc4979..c30c68ebc 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -844,4 +844,8 @@ OT_TOOL_WEAK void otPlatDnssdUnregisterKey(otInstance *aInstance #endif // OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE +#if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE +OT_TOOL_WEAK otError otPlatLogCrashDump(void) { return OT_ERROR_NONE; } +#endif + } // extern "C"