From ca3830fac2271fe81e5eac068e7bad20cf9ac9cc Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 26 Aug 2021 13:35:01 -0700 Subject: [PATCH] [uptime] new feature to track OT instance uptime (in msec) (#6968) This commit adds a new module `Uptime` which tracks the number of milliseconds since OpenThread stack initialization as an `uint64_t` value. It also adds public OT APIs to get the current uptime value (either as the number of milliseconds or in human-readable string format like "2 days 12:45:12.762"). A CLI `uptime` command is also added. This feature can be enabled using the newly added config option `OPENTHREAD_CONFIG_UPTIME_ENABLE` (or the related CMake `OT_UPTIME` option). --- Android.mk | 1 + etc/cmake/options.cmake | 5 + examples/README.md | 1 + examples/common-switches.mk | 5 + include/openthread/instance.h | 36 ++++- script/check-size | 1 + script/cmake-build | 1 + script/make-pretty | 1 + script/test | 2 + src/cli/README.md | 27 ++++ src/cli/cli.cpp | 25 +++ src/cli/cli.hpp | 6 + src/core/BUILD.gn | 3 + src/core/CMakeLists.txt | 1 + src/core/Makefile.am | 3 + src/core/api/instance_api.cpp | 16 ++ src/core/common/instance.cpp | 3 + src/core/common/instance.hpp | 11 ++ src/core/common/uptime.cpp | 142 ++++++++++++++++++ src/core/common/uptime.hpp | 122 +++++++++++++++ .../config/openthread-core-default-config.h | 10 ++ src/core/radio.cmake | 1 + src/posix/Makefile-posix | 1 + tests/fuzz/oss-fuzz-build | 1 + tests/toranj/openthread-core-toranj-config.h | 8 + 25 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 src/core/common/uptime.cpp create mode 100644 src/core/common/uptime.hpp diff --git a/Android.mk b/Android.mk index 3bb987ba9..7fd5c1bf4 100644 --- a/Android.mk +++ b/Android.mk @@ -234,6 +234,7 @@ LOCAL_SRC_FILES := \ src/core/common/timer.cpp \ src/core/common/tlvs.cpp \ src/core/common/trickle_timer.cpp \ + src/core/common/uptime.cpp \ src/core/crypto/aes_ccm.cpp \ src/core/crypto/aes_ecb.cpp \ src/core/crypto/ecdsa.cpp \ diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 1d6230a17..268594d92 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -350,6 +350,11 @@ if(OT_UDP_FORWARD) target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1") endif() +option(OT_UPTIME "enable support for tracking OpenThread instance's uptime") +if(OT_UPTIME) + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_UPTIME_ENABLE=1") +endif() + option(OT_FULL_LOGS "enable full logs") if(OT_FULL_LOGS) if(NOT OT_LOG_LEVEL) diff --git a/examples/README.md b/examples/README.md index 87b3d55c5..cff5d4c5d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -65,3 +65,4 @@ This page lists the available common switches with description. Unless stated ot | TIME_SYNC | OT_TIME_SYNC | Enables the time synchronization service feature. **Note: Enabling this feature breaks conformance to the Thread Specification.** | | | TREL | OT_TREL | Enables TREL radio link for Thread over Infrastructure feature. | | UDP_FORWARD | OT_UDP_FORWARD | Enables support for UDP forward. | Enable this switch on the Border Router device (running on the NCP design) with External Commissioning support to service Thread Commissioner packets on the NCP side. | +| UPTIME | OT_UPTIME | Enables support for tracking OpenThread instance's uptime. | diff --git a/examples/common-switches.mk b/examples/common-switches.mk index a3ae15b47..6fbb4f8e0 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -87,6 +87,7 @@ THREAD_VERSION ?= 1.2 TIME_SYNC ?= 0 TREL ?= 0 UDP_FORWARD ?= 0 +UPTIME ?= 0 RCP_RESTORATION_MAX_COUNT ?= 0 @@ -332,6 +333,10 @@ ifeq ($(UDP_FORWARD),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1 endif +ifeq ($(UPTIME),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_UPTIME_ENABLE=1 +endif + ifeq ($(DISABLE_BUILTIN_MBEDTLS),1) configure_OPTIONS += --disable-builtin-mbedtls endif diff --git a/include/openthread/instance.h b/include/openthread/instance.h index bdb8394dc..786e807b8 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (158) +#define OPENTHREAD_API_VERSION (159) /** * @addtogroup api-instance @@ -126,6 +126,40 @@ bool otInstanceIsInitialized(otInstance *aInstance); */ void otInstanceFinalize(otInstance *aInstance); +/** + * This function returns the current instance uptime (in msec). + * + * This function requires `OPENTHREAD_CONFIG_UPTIME_ENABLE` to be enabled. + * + * The uptime is given as number of milliseconds since OpenThread instance was initialized. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The uptime (number of milliseconds). + * + */ +uint64_t otInstanceGetUptime(otInstance *aInstance); + +#define OT_UPTIME_STRING_SIZE 24 ///< Recommended size for string representation of uptime. + +/** + * This function returns the current instance uptime as a human-readable string. + * + * This function requires `OPENTHREAD_CONFIG_UPTIME_ENABLE` to be enabled. + * + * The string follows the format "::." for hours, minutes, seconds and millisecond (if uptime is + * shorter than one day) or "
d.::." (if longer than a day). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be truncated + * but the outputted string is always null-terminated. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aBuffer A pointer to a char array to output the string. + * @param[in] aSize The size of @p aBuffer (in bytes). Recommended to use `OT_UPTIME_STRING_SIZE`. + * + */ +void otInstanceGetUptimeAsString(otInstance *aInstance, char *aBuffer, uint16_t aSize); + /** * This enumeration defines flags that are passed as part of `otStateChangedCallback`. * diff --git a/script/check-size b/script/check-size index e24b53815..3c4cb7715 100755 --- a/script/check-size +++ b/script/check-size @@ -145,6 +145,7 @@ size_nrf52840_version() "-DOT_SRP_SERVER=ON" "-DOT_TIME_SYNC=ON" "-DOT_UDP_FORWARD=ON" + "-DOT_UPTIME=ON" ) local thread_version=$1 diff --git a/script/cmake-build b/script/cmake-build index 8e1401f94..1f3b65d45 100755 --- a/script/cmake-build +++ b/script/cmake-build @@ -100,6 +100,7 @@ readonly OT_POSIX_SIM_COMMON_OPTIONS=( "-DOT_LOG_LEVEL_DYNAMIC=ON" "-DOT_COMPILE_WARNING_AS_ERROR=ON" "-DOT_RCP_RESTORATION_MAX_COUNT=2" + "-DOT_UPTIME=ON" ) die() diff --git a/script/make-pretty b/script/make-pretty index 9c07fcf42..ad3a7b1b2 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -122,6 +122,7 @@ readonly OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_COVERAGE=ON' '-DOT_LOG_LEVEL_DYNAMIC=ON' '-DOT_COMPILE_WARNING_AS_ERROR=ON' + '-DOT_UPTIME=ON' ) readonly OT_CLANG_TIDY_CHECKS="\ diff --git a/script/test b/script/test index 3f042be2a..a82eb3e37 100755 --- a/script/test +++ b/script/test @@ -67,6 +67,7 @@ build_simulation() "-DOT_SERVICE=ON" "-DOT_SRP_CLIENT=ON" "-DOT_SRP_SERVER=ON" + "-DOT_UPTIME=ON" "-DOT_THREAD_VERSION=${version}" ) @@ -275,6 +276,7 @@ do_build_otbr_docker() "-DOT_SLAAC=ON" "-DOT_SRP_CLIENT=ON" "-DOT_TREL=OFF" + "-DOT_UPTIME=ON" "-DOTBR_DUA_ROUTING=ON" "-DCMAKE_CXX_FLAGS='-DOPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF=1'" ) diff --git a/src/cli/README.md b/src/cli/README.md index f157f18f3..5661411c3 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -110,6 +110,7 @@ Done - [txpower](#txpower) - [udp](README_UDP.md) - [unsecureport](#unsecureport-add-port) +- [uptime](#uptime) - [version](#version) ## OpenThread Command Details @@ -2594,6 +2595,32 @@ Print all ports from the allowed unsecured port list. Done ``` +### uptime + +This command requires `OPENTHREAD_CONFIG_UPTIME_ENABLE` to be enabled. + +Print the OpenThread stack uptime (duration since OpenThread stack initialization). + +```bash +> uptime +12:46:35.469 +Done +> +``` + +### uptime ms + +This command requires `OPENTHREAD_CONFIG_UPTIME_ENABLE` to be enabled. + +Print the OpenThread stack uptime in msec. + +```bash +> uptime ms +426238 +Done +> +``` + ### version Print the build version information. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 58aa7a11f..9485d2d2f 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -4168,6 +4168,31 @@ exit: return error; } +#if OPENTHREAD_CONFIG_UPTIME_ENABLE +otError Interpreter::ProcessUptime(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgs[0].IsEmpty()) + { + char string[OT_UPTIME_STRING_SIZE]; + + otInstanceGetUptimeAsString(mInstance, string, sizeof(string)); + OutputLine("%s", string); + } + else if (aArgs[0] == "ms") + { + OutputLine("%lu", otInstanceGetUptime(mInstance)); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + + return error; +} +#endif + otError Interpreter::ProcessVersion(Arg aArgs[]) { otError error = OT_ERROR_NONE; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 0651dc982..b422b21a6 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -655,6 +655,9 @@ private: #endif otError ProcessUdp(Arg aArgs[]); otError ProcessUnsecurePort(Arg aArgs[]); +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + otError ProcessUptime(Arg aArgs[]); +#endif otError ProcessVersion(Arg aArgs[]); #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE otError ProcessMacFilter(Arg aArgs[]); @@ -940,6 +943,9 @@ private: {"txpower", &Interpreter::ProcessTxPower}, {"udp", &Interpreter::ProcessUdp}, {"unsecureport", &Interpreter::ProcessUnsecurePort}, +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + {"uptime", &Interpreter::ProcessUptime}, +#endif {"version", &Interpreter::ProcessVersion}, }; diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 28e19964e..a18656ca3 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -419,6 +419,8 @@ openthread_core_files = [ "common/trickle_timer.cpp", "common/trickle_timer.hpp", "common/type_traits.hpp", + "common/uptime.cpp", + "common/uptime.hpp", "crypto/aes_ccm.cpp", "crypto/aes_ccm.hpp", "crypto/aes_ecb.cpp", @@ -673,6 +675,7 @@ openthread_radio_sources = [ "common/string.cpp", "common/tasklet.cpp", "common/timer.cpp", + "common/uptime.cpp", "crypto/aes_ccm.cpp", "crypto/aes_ecb.cpp", "diags/factory_diags.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 60204d07f..c83b04854 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -107,6 +107,7 @@ set(COMMON_SOURCES common/timer.cpp common/tlvs.cpp common/trickle_timer.cpp + common/uptime.cpp crypto/aes_ccm.cpp crypto/aes_ecb.cpp crypto/ecdsa.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index a4da793ce..22d48dc2d 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -184,6 +184,7 @@ SOURCES_COMMON = \ common/timer.cpp \ common/tlvs.cpp \ common/trickle_timer.cpp \ + common/uptime.cpp \ crypto/aes_ccm.cpp \ crypto/aes_ecb.cpp \ crypto/ecdsa.cpp \ @@ -323,6 +324,7 @@ libopenthread_radio_a_SOURCES = \ common/string.cpp \ common/tasklet.cpp \ common/timer.cpp \ + common/uptime.cpp \ crypto/aes_ccm.cpp \ crypto/aes_ecb.cpp \ diags/factory_diags.cpp \ @@ -418,6 +420,7 @@ HEADERS_COMMON = \ common/tlvs.hpp \ common/trickle_timer.hpp \ common/type_traits.hpp \ + common/uptime.hpp \ config/announce_sender.h \ config/backbone_router.h \ config/border_router.h \ diff --git a/src/core/api/instance_api.cpp b/src/core/api/instance_api.cpp index b81e2f616..dda0de060 100644 --- a/src/core/api/instance_api.cpp +++ b/src/core/api/instance_api.cpp @@ -100,6 +100,22 @@ void otInstanceReset(otInstance *aInstance) instance.Reset(); } +#if OPENTHREAD_CONFIG_UPTIME_ENABLE +uint64_t otInstanceGetUptime(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.Get().GetUptime(); +} + +void otInstanceGetUptimeAsString(otInstance *aInstance, char *aBuffer, uint16_t aSize) +{ + Instance &instance = *static_cast(aInstance); + + instance.Get().GetUptime(aBuffer, aSize); +} +#endif + #if OPENTHREAD_MTD || OPENTHREAD_FTD otError otSetStateChangedCallback(otInstance *aInstance, otStateChangedCallback aCallback, void *aContext) { diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index 0d3842cb2..c0a7e1b1c 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -64,6 +64,9 @@ Instance::Instance(void) , mTimerMicroScheduler(*this) #endif , mRadio(*this) +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + , mUptime(*this) +#endif #if OPENTHREAD_MTD || OPENTHREAD_FTD , mNotifier(*this) , mTimeTicker(*this) diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 8d056c6cd..5ead9914e 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -51,6 +51,7 @@ #include "common/tasklet.hpp" #include "common/time_ticker.hpp" #include "common/timer.hpp" +#include "common/uptime.hpp" #include "diags/factory_diags.hpp" #include "radio/radio.hpp" @@ -357,6 +358,9 @@ private: // (particularly, SubMac and Mac) to allow them to use its methods // from their constructor. Radio mRadio; +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + Uptime mUptime; +#endif #if OPENTHREAD_MTD || OPENTHREAD_FTD // Notifier, TimeTicker, Settings, and MessagePool are initialized @@ -444,6 +448,13 @@ template <> inline Radio::Callbacks &Instance::Get(void) return mRadio.mCallbacks; } +#if OPENTHREAD_CONFIG_UPTIME_ENABLE +template <> inline Uptime &Instance::Get(void) +{ + return mUptime; +} +#endif + #if OPENTHREAD_MTD || OPENTHREAD_FTD template <> inline Notifier &Instance::Get(void) { diff --git a/src/core/common/uptime.cpp b/src/core/common/uptime.cpp new file mode 100644 index 000000000..9bac0f3b5 --- /dev/null +++ b/src/core/common/uptime.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements mechanism to track device's uptime. + */ + +#include "uptime.hpp" + +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "common/locator_getters.hpp" + +namespace ot { + +Uptime::Uptime(Instance &aInstance) + : InstanceLocator(aInstance) + , mStartTime(TimerMilli::GetNow()) + , mOverflowCount(0) + , mTimer(aInstance, HandleTimer) +{ + mTimer.FireAt(mStartTime + kTimerInterval); +} + +uint64_t Uptime::GetUptime(void) const +{ + TimeMilli now = TimerMilli::GetNow(); + uint32_t overflowCount = mOverflowCount; + + // `mTimer` is scheduled with duration `kTimerInterval = (1 << 30)` + // so it takes four timer fires for us to go over the entire 32-bit + // range and get back to `mStartTime` (at which point we increment + // the `mOverflowCount` in `HandleTimer()`). + // + // Here, we handle the corner case where we may have reached or + // passed beyond the `mStartTime` but the `mTimer` is not yet + // handled. In this case we increment `overflowCount` (which is + // our copy of the current `mOverflowCount` value). We use the + // check `(mTimer.GetFireTime() == mStartTime)` to determine if + // we are in the last of the four timer fires before overflow. + + if ((mTimer.GetFireTime() == mStartTime) && (now >= mStartTime)) + { + overflowCount++; + } + + // The uptime is returned as a `uint64_t`. The `overflowCount` + // gives the higher 32 bits, and `(now - mStartTime)` gives the + // lower 32 bits (which is always correct even under the corner + // case where the `HandleTimer()` is not yet handled). + + return (static_cast(overflowCount) << 32) + (now - mStartTime); +} + +void Uptime::GetUptime(char *aBuffer, uint16_t aSize) const +{ + StringWriter writer(aBuffer, aSize); + + UptimeToString(GetUptime(), writer); +} + +void Uptime::HandleTimer(Timer &aTimer) +{ + aTimer.Get().HandleTimer(); +} + +void Uptime::HandleTimer(void) +{ + if (mTimer.GetFireTime() == mStartTime) + { + mOverflowCount++; + } + + mTimer.FireAt(mTimer.GetFireTime() + kTimerInterval); +} + +static uint32_t DivideAndGetRemainder(uint32_t &aDividend, uint32_t aDivisor) +{ + // Returns the quotient of division `aDividend / aDivisor` and updates + // `aDividend` to returns the remainder + + uint32_t quotient = aDividend / aDivisor; + + aDividend -= quotient * aDivisor; + + return quotient; +} + +void Uptime::UptimeToString(uint64_t aUptime, StringWriter &aWriter) +{ + uint64_t days = aUptime / kOneDayInMsec; + uint32_t remainder; + uint32_t hours; + uint32_t minutes; + uint32_t seconds; + + if (days > 0) + { + aWriter.Append("%lud.", days); + aUptime -= days * kOneDayInMsec; + } + + remainder = static_cast(aUptime); + hours = DivideAndGetRemainder(remainder, kOneHourInMsec); + minutes = DivideAndGetRemainder(remainder, kOneMinuteInMsec); + seconds = DivideAndGetRemainder(remainder, kOneSecondInMsec); + + aWriter.Append("%02u:%02u:%02u.%03u", hours, minutes, seconds, remainder); +} + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_UPTIME_ENABLE diff --git a/src/core/common/uptime.hpp b/src/core/common/uptime.hpp new file mode 100644 index 000000000..35a5c91a4 --- /dev/null +++ b/src/core/common/uptime.hpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for tracking device's uptime. + */ + +#ifndef UPTIME_HPP_ +#define UPTIME_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + +#include "common/locator.hpp" +#include "common/non_copyable.hpp" +#include "common/string.hpp" +#include "common/time.hpp" +#include "common/timer.hpp" + +namespace ot { + +/** + * This class implements tracking of device uptime (in msec). + * + */ +class Uptime : public InstanceLocator, private NonCopyable +{ +public: + /** + * This constructor initializes an `Uptime` instance. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit Uptime(Instance &aInstance); + + /** + * This method returns the current device uptime (in msec). + * + * The uptime is maintained as number of milliseconds since OpenThread instance was initialized. + * + * @returns The uptime (number of milliseconds). + * + */ + uint64_t GetUptime(void) const; + + /** + * This method gets the current uptime as a human-readable string. + * + * The string follows the format "::." for hours, minutes, seconds and millisecond (if uptime is + * shorter than one day) or "
d.::." (if longer than a day). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[out] aBuffer A pointer to a char array to output the string. + * @param[in] aSize The size of @p aBuffer (in bytes). Recommended to use `OT_UPTIME_STRING_SIZE`. + * + */ + void GetUptime(char *aBuffer, uint16_t aSize) const; + + /** + * This method converts an uptime value (number of milliseconds) to a human-readable string. + * + * The string follows the format "::." for hours, minutes, seconds and millisecond (if uptime is + * shorter than one day) or "
d.::." (if longer than a day). + * + * @param[in] aUptime The uptime to convert. + * @param[inout] aWriter A `StringWriter` to append the converted string to. + * + */ + static void UptimeToString(uint64_t aUptime, StringWriter &aWriter); + +private: + static constexpr uint32_t kTimerInterval = (1 << 30); + static constexpr uint32_t kOneSecondInMsec = 1000; + static constexpr uint32_t kOneMinuteInMsec = 60 * kOneSecondInMsec; + static constexpr uint32_t kOneHourInMsec = 60 * kOneMinuteInMsec; + static constexpr uint32_t kOneDayInMsec = 24 * kOneHourInMsec; + + static_assert(static_cast(4 * kTimerInterval) == 0, "kTimerInterval is not correct"); + + static void HandleTimer(Timer &aTimer); + void HandleTimer(void); + + TimeMilli mStartTime; + uint32_t mOverflowCount; + TimerMilli mTimer; +}; + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_UPTIME_ENABLE + +#endif // UPTIME_HPP_ diff --git a/src/core/config/openthread-core-default-config.h b/src/core/config/openthread-core-default-config.h index 60e518a48..c73c7f69a 100644 --- a/src/core/config/openthread-core-default-config.h +++ b/src/core/config/openthread-core-default-config.h @@ -88,6 +88,16 @@ #define OPENTHREAD_CONFIG_ECDSA_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_UPTIME_ENABLE + * + * Define to 1 to enable tracking the uptime of OpenThread instance. + * + */ +#ifndef OPENTHREAD_CONFIG_UPTIME_ENABLE +#define OPENTHREAD_CONFIG_UPTIME_ENABLE 0 +#endif + /** * @def OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE * diff --git a/src/core/radio.cmake b/src/core/radio.cmake index 880dd9c74..79e501fd2 100644 --- a/src/core/radio.cmake +++ b/src/core/radio.cmake @@ -53,6 +53,7 @@ target_sources(openthread-radio PRIVATE common/string.cpp common/tasklet.cpp common/timer.cpp + common/uptime.cpp crypto/aes_ccm.cpp crypto/aes_ecb.cpp diags/factory_diags.cpp diff --git a/src/posix/Makefile-posix b/src/posix/Makefile-posix index 11a3ed6fb..9740197eb 100644 --- a/src/posix/Makefile-posix +++ b/src/posix/Makefile-posix @@ -75,6 +75,7 @@ SRP_SERVER ?= 1 ifneq ($(DAEMON),1) UDP_FORWARD ?= 1 endif +UPTIME ?= 1 COMMONCFLAGS := \ -g \ diff --git a/tests/fuzz/oss-fuzz-build b/tests/fuzz/oss-fuzz-build index 7b2103671..31373c415 100755 --- a/tests/fuzz/oss-fuzz-build +++ b/tests/fuzz/oss-fuzz-build @@ -70,6 +70,7 @@ -DOT_SRP_CLIENT=ON \ -DOT_SRP_SERVER=ON \ -DOT_THREAD_VERSION=1.2 \ + -DOT_UPTIME=ON \ .. ninja ) diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index 00f5dd8a4..1c15a6c8f 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -47,6 +47,14 @@ */ #define OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_UPTIME_ENABLE + * + * Define to 1 to enable tracking the uptime of OpenThread instance. + * + */ +#define OPENTHREAD_CONFIG_UPTIME_ENABLE 1 + /** * @def OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE *