From 33283874ca650f9c3543ab5cdd412c4247c97bc0 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 10 Oct 2023 08:35:07 -0400 Subject: [PATCH] Split apart text-only logging and binary logging. (#29661) Text-only logging does not depend on Span, but binary logging (LogByteSpan) does. And Span.h depends on text-only logging. We were manually breaking the cycle caused by having both logging types in CHIPLogging.h by not including Span.h from CHIPLogging.h and forward-declaring Span instead. This fixes things so that we have a TextOnlyLogging.h (which is used by Span.h, via CodeUtils.h, and does not use Span at all), and a BinaryLogging.h which uses Span and includes Span.h. --- examples/common/pigweed/RpcService.cpp | 2 +- examples/platform/ameba/PigweedLogger.cpp | 2 +- .../silabs/SilabsDeviceAttestationCreds.cpp | 1 + src/lib/support/BUILD.gn | 5 +- src/lib/support/CodeUtils.h | 2 +- src/lib/support/logging/BinaryLogging.cpp | 59 +++ src/lib/support/logging/BinaryLogging.h | 79 +++ src/lib/support/logging/CHIPLogging.h | 450 +----------------- .../{CHIPLogging.cpp => TextOnlyLogging.cpp} | 37 +- src/lib/support/logging/TextOnlyLogging.h | 432 +++++++++++++++++ 10 files changed, 583 insertions(+), 486 deletions(-) create mode 100644 src/lib/support/logging/BinaryLogging.cpp create mode 100644 src/lib/support/logging/BinaryLogging.h rename src/lib/support/logging/{CHIPLogging.cpp => TextOnlyLogging.cpp} (86%) create mode 100644 src/lib/support/logging/TextOnlyLogging.h diff --git a/examples/common/pigweed/RpcService.cpp b/examples/common/pigweed/RpcService.cpp index 0b049c0b2ab8cc..e36ab54afe1eb4 100644 --- a/examples/common/pigweed/RpcService.cpp +++ b/examples/common/pigweed/RpcService.cpp @@ -30,7 +30,7 @@ #include "pw_stream/sys_io_stream.h" #include "pw_sys_io/sys_io.h" -#include +#include #include diff --git a/examples/platform/ameba/PigweedLogger.cpp b/examples/platform/ameba/PigweedLogger.cpp index 5713cec0920354..effc173d953e38 100644 --- a/examples/platform/ameba/PigweedLogger.cpp +++ b/examples/platform/ameba/PigweedLogger.cpp @@ -18,7 +18,7 @@ #include "FreeRTOS.h" #include "pw_sys_io_ameba/init.h" #include "semphr.h" -#include +#include #include #include diff --git a/examples/platform/silabs/SilabsDeviceAttestationCreds.cpp b/examples/platform/silabs/SilabsDeviceAttestationCreds.cpp index dac4081ff35e12..d442f520debcbc 100644 --- a/examples/platform/silabs/SilabsDeviceAttestationCreds.cpp +++ b/examples/platform/silabs/SilabsDeviceAttestationCreds.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/src/lib/support/BUILD.gn b/src/lib/support/BUILD.gn index cb6055f33376c6..ae745b52b8ef28 100644 --- a/src/lib/support/BUILD.gn +++ b/src/lib/support/BUILD.gn @@ -140,8 +140,11 @@ static_library("support") { "Variant.h", "ZclString.cpp", "ZclString.h", - "logging/CHIPLogging.cpp", + "logging/BinaryLogging.cpp", + "logging/BinaryLogging.h", "logging/CHIPLogging.h", + "logging/TextOnlyLogging.cpp", + "logging/TextOnlyLogging.h", "verhoeff/Verhoeff.cpp", "verhoeff/Verhoeff.h", "verhoeff/Verhoeff10.cpp", diff --git a/src/lib/support/CodeUtils.h b/src/lib/support/CodeUtils.h index e117f1bbaa0906..60780a689ef82a 100644 --- a/src/lib/support/CodeUtils.h +++ b/src/lib/support/CodeUtils.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include /** * Base-level abnormal termination. diff --git a/src/lib/support/logging/BinaryLogging.cpp b/src/lib/support/logging/BinaryLogging.cpp new file mode 100644 index 00000000000000..ca570a0b168336 --- /dev/null +++ b/src/lib/support/logging/BinaryLogging.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2013-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BinaryLogging.h" + +namespace chip { +namespace Logging { + +#if _CHIP_USE_LOGGING + +void LogByteSpan(uint8_t module, uint8_t category, const ByteSpan & span) +{ + // Maximum number of characters needed to print 8 byte buffer including formatting (0x) + // 8 bytes * (2 nibbles per byte + 4 character for ", 0x") + null termination. + // Rounding up to 50 bytes. + char output[50]; + size_t offset = 0; + for (unsigned int i = 0; i < span.size(); i++) + { + if (i % 8 == 0 && offset != 0) + { + Log(module, category, "%s", output); + offset = 0; + } + int result = snprintf(&output[offset], sizeof(output) - offset, "0x%02x, ", (unsigned char) span.data()[i]); + if (result > 0) + { + offset += static_cast(result); + } + else + { + Log(module, Logging::kLogCategory_Error, "Failed to print ByteSpan buffer"); + return; + } + } + if (offset != 0) + { + Log(module, category, "%s", output); + } +} + +#endif // _CHIP_USE_LOGGING + +} // namespace Logging +} // namespace chip diff --git a/src/lib/support/logging/BinaryLogging.h b/src/lib/support/logging/BinaryLogging.h new file mode 100644 index 00000000000000..93cca57cae0381 --- /dev/null +++ b/src/lib/support/logging/BinaryLogging.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2013-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines macros, constants, and interfaces for logging binary + * data (represented by ByteSpan). + */ + +#pragma once + +#include + +#include +#include + +namespace chip { +namespace Logging { + +#if CHIP_DETAIL_LOGGING + +/** + * @def ChipLogByteSpan(MOD, DATA) + * + * @brief + * Log a byte span for the specified module in the 'Detail' category. + * + */ +#define ChipLogByteSpan(MOD, DATA) ChipInternalLogByteSpan(MOD, DETAIL, DATA) + +#else // CHIP_DETAIL_LOGGING +#define ChipLogByteSpan(MOD, DATA) ((void) 0) +#endif // CHIP_DETAIL_LOGGING + +// _CHIP_USE_LOGGING is defined in TextOnlyLogging.h +#if _CHIP_USE_LOGGING + +void LogByteSpan(uint8_t module, uint8_t category, const ByteSpan & span); + +#if CHIP_SYSTEM_CONFIG_PLATFORM_LOG +#ifndef ChipPlatformLogByteSpan +#error "CHIP_SYSTEM_CONFIG_PLATFORM_LOG is enabled but ChipPlatformLogByteSpan() is not defined" +#endif +#define ChipInternalLogByteSpan(...) ChipPlatformLogByteSpan(__VA_ARGS__) +#else // CHIP_SYSTEM_CONFIG_PLATFORM_LOG +#define ChipInternalLogByteSpan(MOD, CAT, DATA) \ + if (CHIP_CONFIG_LOG_MODULE_##MOD && IsModuleCategoryEnabled(MOD, CAT)) \ + { \ + ChipInternalLogByteSpanImpl(MOD, CHIP_LOG_CATEGORY_##CAT, DATA); \ + } +#endif // CHIP_SYSTEM_CONFIG_PLATFORM_LOG + +#define ChipInternalLogByteSpanImpl(MOD, CAT, DATA) \ + do \ + { \ + if (chip::Logging::IsCategoryEnabled(CAT)) \ + { \ + chip::Logging::LogByteSpan(chip::Logging::kLogModule_##MOD, CAT, DATA); \ + } \ + } while (0) + +#endif // _CHIP_USE_LOGGING + +} // namespace Logging +} // namespace chip diff --git a/src/lib/support/logging/CHIPLogging.h b/src/lib/support/logging/CHIPLogging.h index 12b102f94c8f93..a827a5f31a1784 100644 --- a/src/lib/support/logging/CHIPLogging.h +++ b/src/lib/support/logging/CHIPLogging.h @@ -1,6 +1,5 @@ /* - * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2023 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,450 +15,7 @@ * limitations under the License. */ -/** - * @file - * This file defines macros, constants, and interfaces for a - * platform-independent logging interface for the chip SDK. - * - * chip SDK clients may choose, at compile time, among Android, - * C Standard I/O, or external (platform- and integrator-defined) - * logging style implementations that will be invoked when any of - * the following preprocessor symbols are asserted: - * - * - #CHIP_ERROR_LOGGING - * - #CHIP_PROGRESS_LOGGING - * - #CHIP_DETAIL_LOGGING - * - #CHIP_AUTOMATION_LOGGING - * - */ - #pragma once -#include - -#include -#include -#include - -#include -#include -#include - -#if CHIP_SYSTEM_CONFIG_PLATFORM_LOG && defined(CHIP_SYSTEM_CONFIG_PLATFORM_LOG_INCLUDE) -#include CHIP_SYSTEM_CONFIG_PLATFORM_LOG_INCLUDE -#endif - -#if CHIP_PW_TOKENIZER_LOGGING -#include "pw_tokenizer/tokenize.h" -#endif - -/** - * @namespace chip::Logging - * - * @brief - * This namespace includes all interfaces within chip for shared - * logging support. - * - * The interfaces include macros, constants, and functions for a - * platform-independent logging interface for the chip SDK. - * - * chip SDK clients may choose, at compile time, among Android, - * C Standard I/O, or external (platform- and integrator-defined) - * logging style implementations that will be invoked when any of - * the following preprocessor symbols are asserted: - * - * - #CHIP_ERROR_LOGGING - * - #CHIP_PROGRESS_LOGGING - * - #CHIP_DETAIL_LOGGING - * - #CHIP_AUTOMATION_LOGGING - * - */ - -namespace chip { - -template -class Span; -using ByteSpan = Span; - -namespace Logging { - -// Get the module name associated with a LogModule, or "-" on invalid value. -const char * GetModuleName(LogModule module); - -// Log redirection -using LogRedirectCallback_t = void (*)(const char * module, uint8_t category, const char * msg, va_list args); -DLL_EXPORT void SetLogRedirectCallback(LogRedirectCallback_t callback); - -// Log filtering (no-op unless CHIP_LOG_FILTERING is enabled) -DLL_EXPORT uint8_t GetLogFilter(); -DLL_EXPORT void SetLogFilter(uint8_t category); - -#if CHIP_ERROR_LOGGING -/** - * @def ChipLogError(MOD, MSG, ...) - * - * @brief - * Log a chip message for the specified module in the 'Error' - * category. - * - */ -#define ChipLogError(MOD, MSG, ...) ChipInternalLog(MOD, ERROR, MSG, ##__VA_ARGS__) -#else // CHIP_ERROR_LOGGING -#define ChipLogError(MOD, MSG, ...) ((void) 0) -#endif // CHIP_ERROR_LOGGING - -#if CHIP_PROGRESS_LOGGING -/** - * @def ChipLogProgress(MOD, MSG, ...) - * - * @brief - * Log a chip message for the specified module in the 'Progress' - * category. - * - */ -#define ChipLogProgress(MOD, MSG, ...) ChipInternalLog(MOD, PROGRESS, MSG, ##__VA_ARGS__) -#else // CHIP_PROGRESS_LOGGING -#define ChipLogProgress(MOD, MSG, ...) ((void) 0) -#endif // CHIP_PROGRESS_LOGGING - -#if CHIP_DETAIL_LOGGING -/** - * @def ChipLogDetail(MOD, MSG, ...) - * - * @brief - * Log a chip message for the specified module in the 'Detail' - * category. - * - */ -#define ChipLogDetail(MOD, MSG, ...) ChipInternalLog(MOD, DETAIL, MSG, ##__VA_ARGS__) - -/** - * @def ChipLogByteSpan(MOD, DATA) - * - * @brief - * Log a byte span for the specified module in the 'Detail' category. - * - */ -#define ChipLogByteSpan(MOD, DATA) ChipInternalLogByteSpan(MOD, DETAIL, DATA) -#else // CHP_DETAIL_LOGGING -#define ChipLogDetail(MOD, MSG, ...) ((void) 0) -#define ChipLogByteSpan(MOD, DATA) ((void) 0) -#endif // CHIP_DETAIL_LOGGING - -#if CHIP_AUTOMATION_LOGGING -/** - * @def ChipLogAutomation(MSG, ...) - * - * @brief - * Log a chip message for the specified module in the 'Automation' - * category. - * - */ -#define ChipLogAutomation(MSG, ...) ChipInternalLog(Automation, AUTOMATION, MSG, ##__VA_ARGS__) -#else // CHIP_AUTOMATION_LOGGING -#define ChipLogAutomation(MOD, MSG, ...) ((void) 0) -#endif // CHIP_AUTOMATION_LOGGING - -/** - * @def ChipLogIfFalse(aCondition) - * - * @brief - * This checks for the specified condition, which is expected to - * commonly be true and emits some log, based on configuration, if - * the condition is false. - * - * @note - * Evaluation of @a aCondition is always done, but logging is only enabled when - * #CHIP_CONFIG_ENABLE_CONDITION_LOGGING is enabled. This can be turned on or - * off for each compilation unit by enabling or disabling, as desired, - * #CHIP_CONFIG_ENABLE_CONDITION_LOGGING before ChipLogging.h is included by - * the preprocessor. - * - * Example Usage: - * - * @code - * #define CHIP_CONFIG_ENABLE_CONDITION_LOGGING 1 - * - * #include - * - * ... - * - * void foo(void) - * { - * CHIP_ERROR err = CHIP_NO_ERROR; - * - * ... - * - * exit: - * ChipLogIfFalse(CHIP_END_OF_TLV == err); - * } - * @endcode - * - * @param[in] aCondition A Boolean expression to be evaluated. - * - * @sa CHIP_CONFIG_ENABLE_TRACE_ON_CHECK_FAILURE - * - */ - -#if CHIP_CONFIG_ENABLE_CONDITION_LOGGING && !defined(ChipLogIfFalse) - -#define ChipLogIfFalse(aCondition) \ - do \ - { \ - if (!(aCondition)) \ - { \ - ChipLogError(NotSpecified, "Condition Failed (%s) at %s:%d", #aCondition, __FILE__, __LINE__); \ - } \ - } while (0) - -#else // CHIP_CONFIG_ENABLE_CONDITION_LOGGING - -#define ChipLogIfFalse(aCondition) IgnoreUnusedVariable(aCondition) - -#endif // CHIP_CONFIG_ENABLE_CONDITION_LOGGING - -/* - * @brief - * Macro for use in a string formatter for a 64-bit hex print. - * Will split into 2x 32-bit prints to support small printf libraries - * - * Example Usage: - * - * @code - * void foo() { - * uint64_t value = 0x1122334455667788; - * ChipLogProgress(Foo, "A 64-bit value: 0x" ChipLogFormatX64, ChipLogValueX64(value)); - * } - * @endcode - * - */ -#define ChipLogFormatX64 "%08" PRIX32 "%08" PRIX32 - -/* - * @brief - * Macro for use in a printf parameter list for a 64-bit value. - * Will split into MSB/LSB 32-bit values to use only 32-bit formatting. - * - * Example Usage: - * - * @code - * void foo() { - * uint64_t value = 0x1122334455667788; - * ChipLogProgress(Foo, "A 64-bit value: 0x" ChipLogFormatX64, ChipLogValueX64(value)); - * } - * @endcode - * - * @param[in] aValue 64-bit value that will be split in 32-bit MSB/LSB part - */ -#define ChipLogValueX64(aValue) static_cast(aValue >> 32), static_cast(aValue) - -/* - * @brief - * Macro for use in a string formatter for a MEI hex print. - * Will split into 2x 16-bit prints to display both the MEI prefix/suffix - * - * Example Usage: - * - * @code - * void foo() { - * chip::CommandId value = 0x12340001; - * ChipLogProgress(Foo, "A MEI value: " ChipLogFormatMEI, ChipLogValueMEI(value)); - * } - * @endcode - * - */ -#define ChipLogFormatMEI "0x%04X_%04X" - -/* - * @brief - * Macro for use in a printf parameter list for MEI value. - * Will split into MSB/LSB 16-bit values to separate prefix/suffix. - * - * Example Usage: - * - * @code - * void foo() { - * chip::CommandId value = 0x12340001; - * ChipLogProgress(Foo, "A MEI value: " ChipLogFormatMEI, ChipLogValueMEI(value)); - * } - * @endcode - * - * @param[in] aValue "32-bit value that will be split in 16-bit MSB/LSB part - */ -#define ChipLogValueMEI(aValue) static_cast(aValue >> 16), static_cast(aValue) - -/** - * Logging helpers for exchanges. For now just log the exchange id and whether - * it's an initiator or responder, but eventually we may want to log the peer - * node id as well (especially for the responder case). Some callsites only - * have the exchange id and initiator/responder boolean, not an actual exchange, - * so we want to have a helper for that case too. - */ -#define ChipLogFormatExchangeId "%u%c" -#define ChipLogValueExchangeId(id, isInitiator) id, ((isInitiator) ? 'i' : 'r') -#define ChipLogFormatExchange ChipLogFormatExchangeId -#define ChipLogValueExchange(ec) ChipLogValueExchangeId((ec)->GetExchangeId(), (ec)->IsInitiator()) -#define ChipLogValueExchangeIdFromSentHeader(payloadHeader) \ - ChipLogValueExchangeId((payloadHeader).GetExchangeID(), (payloadHeader).IsInitiator()) -// A received header's initiator boolean is the inverse of the exchange's. -#define ChipLogValueExchangeIdFromReceivedHeader(payloadHeader) \ - ChipLogValueExchangeId((payloadHeader).GetExchangeID(), !(payloadHeader).IsInitiator()) - -/** - * Logging helpers for protocol ids. A protocol id is a (vendor-id, - * protocol-id) pair. - */ -#define ChipLogFormatProtocolId "(%u, %u)" -#define ChipLogValueProtocolId(id) (id).GetVendorId(), (id).GetProtocolId() - -/** - * Logging helpers for message counters, so we format them consistently. - */ -#define ChipLogFormatMessageCounter "%" PRIu32 - -/** - * Logging helpers for message types, so we format them consistently. - */ -#define ChipLogFormatMessageType "0x%x" - -/** Logging helpers for scoped node ids, which is a tuple of */ -#define ChipLogFormatScopedNodeId "<" ChipLogFormatX64 ", %d>" -#define ChipLogValueScopedNodeId(id) ChipLogValueX64((id).GetNodeId()), (id).GetFabricIndex() - -/** - * CHIP Logging Implementation internals. - */ - -#if CHIP_ERROR_LOGGING || CHIP_PROGRESS_LOGGING || CHIP_DETAIL_LOGGING || CHIP_AUTOMATION_LOGGING -#define _CHIP_USE_LOGGING 1 -#else -#define _CHIP_USE_LOGGING 0 -#endif // CHIP_ERROR_LOGGING || CHIP_PROGRESS_LOGGING || CHIP_DETAIL_LOGGING || CHIP_AUTOMATION_LOGGING - -#if _CHIP_USE_LOGGING - -static constexpr uint16_t kMaxModuleNameLen = 3; - -#if CHIP_LOG_FILTERING -bool IsCategoryEnabled(uint8_t category); -#else // CHIP_LOG_FILTERING -inline bool IsCategoryEnabled(uint8_t category) -{ - return true; -} -#endif // CHIP_LOG_FILTERING - -/* Internal macros mapping upper case definitions to camel case category constants*/ -#define CHIP_LOG_CATEGORY_DETAIL chip::Logging::kLogCategory_Detail -#define CHIP_LOG_CATEGORY_PROGRESS chip::Logging::kLogCategory_Progress -#define CHIP_LOG_CATEGORY_ERROR chip::Logging::kLogCategory_Error -#define CHIP_LOG_CATEGORY_AUTOMATION chip::Logging::kLogCategory_Automation - -/* - * CHIP Logging Modules Categories filtering implementation. - * - * @brief - * Macro for use to check if given category is enabled for given log module. - * - * Example Usage: - * Let's assume PROGRESS category control for DeviceLayer log module. - * - * Default behavior - category is not modified, so macro returns global category value: - * IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns CHIP_PROGRESS_LOGGING - * - * Enabling category - category is enabled for module, the category value is ignored and the global value is used: - * #define CHIP_CONFIG_LOG_MODULE_DeviceLayer_PROGRESS 1 - * IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns CHIP_PROGRESS_LOGGING - * - * Disabling category - category is disabled for module, ignoring global category value: - * #define CHIP_CONFIG_LOG_MODULE_DeviceLayer_PROGRESS 0 - * IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns 0 - * - * Algorithm flow: - * 1. IsModuleCategoryEnabled(MOD, CAT) uses MOD and CAT to create strings for category module macro and global category macro, - * and invokes _IsModuleCategoryEnabled1(). - * 2. _IsModuleCategoryEnabled1(MOD_CAT, GLOB_CAT) invokes _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) to extract macros - * values. - * 3. _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) uses MOD_CAT to create string for helper macro. - * - If MOD_CAT is 0 the helper macro containing dummy arg is used. - * - If MOD_CAT doesn't exist, empty is used. - * 4. _IsModuleCategoryEnabled3(DUMMY_ARG, GLOB_CAT) invokes _IsModuleCategoryEnabled4() using different number of arguments - * depending on DUMMY_ARG. - * 5. _IsModuleCategoryEnabled4 output: - * - If category for module was not defined or define 1, the DUMMY_ARG was empty, so returning ARG2 is GLOB_CAT. - * - If category for module was defined 0, the DUMMY_ARG had one argument, so returning ARG2 is 0. - * - */ -#define IsModuleCategoryEnabled(MOD, CAT) _IsModuleCategoryEnabled1(CHIP_CONFIG_LOG_MODULE_##MOD##_##CAT, CHIP_##CAT##_LOGGING) -#define _IsModuleCategoryEnabled1(MOD_CAT, GLOB_CAT) _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) -#define _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) \ - _IsModuleCategoryEnabled3(_IsModuleCategoryEnabled3_DummyArg##MOD_CAT, GLOB_CAT) -#define _IsModuleCategoryEnabled3_DummyArg0 dummyArg, -#define _IsModuleCategoryEnabled3(DUMMY_ARG, GLOB_CAT) _IsModuleCategoryEnabled4(DUMMY_ARG 0, GLOB_CAT) -#define _IsModuleCategoryEnabled4(ARG1, ARG2, ...) (ARG2) - -void Log(uint8_t module, uint8_t category, const char * msg, ...) ENFORCE_FORMAT(3, 4); -void LogByteSpan(uint8_t module, uint8_t category, const ByteSpan & span); -void LogV(uint8_t module, uint8_t category, const char * msg, va_list args) ENFORCE_FORMAT(3, 0); - -#if CHIP_SYSTEM_CONFIG_PLATFORM_LOG -#ifndef ChipPlatformLog -#error "CHIP_SYSTEM_CONFIG_PLATFORM_LOG is enabled but ChipPlatformLog() is not defined" -#endif -#ifndef ChipPlatformLogByteSpan -#error "CHIP_SYSTEM_CONFIG_PLATFORM_LOG is enabled but ChipPlatformLogByteSpan() is not defined" -#endif -#define ChipInternalLog(...) ChipPlatformLog(__VA_ARGS__) -#define ChipInternalLogByteSpan(...) ChipPlatformLogByteSpan(__VA_ARGS__) -#else // CHIP_SYSTEM_CONFIG_PLATFORM_LOG -#define ChipInternalLog(MOD, CAT, MSG, ...) \ - if (CHIP_CONFIG_LOG_MODULE_##MOD && IsModuleCategoryEnabled(MOD, CAT)) \ - { \ - ChipInternalLogImpl(MOD, CHIP_LOG_CATEGORY_##CAT, MSG, ##__VA_ARGS__); \ - } - -#define ChipInternalLogByteSpan(MOD, CAT, DATA) \ - if (CHIP_CONFIG_LOG_MODULE_##MOD && IsModuleCategoryEnabled(MOD, CAT)) \ - { \ - ChipInternalLogByteSpanImpl(MOD, CHIP_LOG_CATEGORY_##CAT, DATA); \ - } -#endif // CHIP_SYSTEM_CONFIG_PLATFORM_LOG - -#if CHIP_PW_TOKENIZER_LOGGING - -void HandleTokenizedLog(uint32_t levels, pw_tokenizer_Token token, pw_tokenizer_ArgTypes, ...); - -#define ChipInternalLogImpl(MOD, CAT, MSG, ...) \ - do \ - { \ - if (chip::Logging::IsCategoryEnabled(CAT)) \ - { \ - PW_TOKENIZE_FORMAT_STRING(PW_TOKENIZER_DEFAULT_DOMAIN, UINT32_MAX, MSG, __VA_ARGS__); \ - ::chip::Logging::HandleTokenizedLog((uint32_t) ((CAT << 8) | chip::Logging::kLogModule_##MOD), _pw_tokenizer_token, \ - PW_TOKENIZER_ARG_TYPES(__VA_ARGS__) PW_COMMA_ARGS(__VA_ARGS__)); \ - } \ - } while (0) -#else // CHIP_PW_TOKENIZER_LOGGING -#define ChipInternalLogImpl(MOD, CAT, MSG, ...) \ - do \ - { \ - if (chip::Logging::IsCategoryEnabled(CAT)) \ - { \ - chip::Logging::Log(chip::Logging::kLogModule_##MOD, CAT, MSG, ##__VA_ARGS__); \ - } \ - } while (0) -#endif // CHIP_PW_TOKENIZER_LOGGING - -#define ChipInternalLogByteSpanImpl(MOD, CAT, DATA) \ - do \ - { \ - if (chip::Logging::IsCategoryEnabled(CAT)) \ - { \ - chip::Logging::LogByteSpan(chip::Logging::kLogModule_##MOD, CAT, DATA); \ - } \ - } while (0) - -#endif // _CHIP_USE_LOGGING - -} // namespace Logging -} // namespace chip +#include +#include diff --git a/src/lib/support/logging/CHIPLogging.cpp b/src/lib/support/logging/TextOnlyLogging.cpp similarity index 86% rename from src/lib/support/logging/CHIPLogging.cpp rename to src/lib/support/logging/TextOnlyLogging.cpp index fd8d782efb2d08..04ac20f351acab 100644 --- a/src/lib/support/logging/CHIPLogging.cpp +++ b/src/lib/support/logging/TextOnlyLogging.cpp @@ -1,6 +1,5 @@ /* - * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2023 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,12 +22,11 @@ * */ -#include "CHIPLogging.h" +#include "TextOnlyLogging.h" #include #include #include -#include #include @@ -197,37 +195,6 @@ void Log(uint8_t module, uint8_t category, const char * msg, ...) va_end(v); } -void LogByteSpan(uint8_t module, uint8_t category, const chip::ByteSpan & span) -{ - // Maximum number of characters needed to print 8 byte buffer including formatting (0x) - // 8 bytes * (2 nibbles per byte + 4 character for ", 0x") + null termination. - // Rounding up to 50 bytes. - char output[50]; - size_t offset = 0; - for (unsigned int i = 0; i < span.size(); i++) - { - if (i % 8 == 0 && offset != 0) - { - Log(module, category, "%s", output); - offset = 0; - } - int result = snprintf(&output[offset], sizeof(output) - offset, "0x%02x, ", (unsigned char) span.data()[i]); - if (result > 0) - { - offset += static_cast(result); - } - else - { - Log(module, chip::Logging::kLogCategory_Error, "Failed to print ByteSpan buffer"); - return; - } - } - if (offset != 0) - { - Log(module, category, "%s", output); - } -} - void LogV(uint8_t module, uint8_t category, const char * msg, va_list args) { const char * moduleName = GetModuleName(static_cast(module)); diff --git a/src/lib/support/logging/TextOnlyLogging.h b/src/lib/support/logging/TextOnlyLogging.h new file mode 100644 index 00000000000000..a754106935ea9d --- /dev/null +++ b/src/lib/support/logging/TextOnlyLogging.h @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2020-2023 Project CHIP Authors + * Copyright (c) 2013-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file defines macros, constants, and interfaces for a + * platform-independent logging interface for the chip SDK. + * + * chip SDK clients may choose, at compile time, among Android, + * C Standard I/O, or external (platform- and integrator-defined) + * logging style implementations that will be invoked when any of + * the following preprocessor symbols are asserted: + * + * - #CHIP_ERROR_LOGGING + * - #CHIP_PROGRESS_LOGGING + * - #CHIP_DETAIL_LOGGING + * - #CHIP_AUTOMATION_LOGGING + * + */ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include + +#if CHIP_SYSTEM_CONFIG_PLATFORM_LOG && defined(CHIP_SYSTEM_CONFIG_PLATFORM_LOG_INCLUDE) +#include CHIP_SYSTEM_CONFIG_PLATFORM_LOG_INCLUDE +#endif + +#if CHIP_PW_TOKENIZER_LOGGING +#include "pw_tokenizer/tokenize.h" +#endif + +/** + * @namespace chip::Logging + * + * @brief + * This namespace includes all interfaces within chip for shared + * logging support. + * + * The interfaces include macros, constants, and functions for a + * platform-independent logging interface for the chip SDK. + * + * chip SDK clients may choose, at compile time, among Android, + * C Standard I/O, or external (platform- and integrator-defined) + * logging style implementations that will be invoked when any of + * the following preprocessor symbols are asserted: + * + * - #CHIP_ERROR_LOGGING + * - #CHIP_PROGRESS_LOGGING + * - #CHIP_DETAIL_LOGGING + * - #CHIP_AUTOMATION_LOGGING + * + */ + +namespace chip { + +namespace Logging { + +// Get the module name associated with a LogModule, or "-" on invalid value. +const char * GetModuleName(LogModule module); + +// Log redirection +using LogRedirectCallback_t = void (*)(const char * module, uint8_t category, const char * msg, va_list args); +DLL_EXPORT void SetLogRedirectCallback(LogRedirectCallback_t callback); + +// Log filtering (no-op unless CHIP_LOG_FILTERING is enabled) +DLL_EXPORT uint8_t GetLogFilter(); +DLL_EXPORT void SetLogFilter(uint8_t category); + +#if CHIP_ERROR_LOGGING +/** + * @def ChipLogError(MOD, MSG, ...) + * + * @brief + * Log a chip message for the specified module in the 'Error' + * category. + * + */ +#define ChipLogError(MOD, MSG, ...) ChipInternalLog(MOD, ERROR, MSG, ##__VA_ARGS__) +#else // CHIP_ERROR_LOGGING +#define ChipLogError(MOD, MSG, ...) ((void) 0) +#endif // CHIP_ERROR_LOGGING + +#if CHIP_PROGRESS_LOGGING +/** + * @def ChipLogProgress(MOD, MSG, ...) + * + * @brief + * Log a chip message for the specified module in the 'Progress' + * category. + * + */ +#define ChipLogProgress(MOD, MSG, ...) ChipInternalLog(MOD, PROGRESS, MSG, ##__VA_ARGS__) +#else // CHIP_PROGRESS_LOGGING +#define ChipLogProgress(MOD, MSG, ...) ((void) 0) +#endif // CHIP_PROGRESS_LOGGING + +#if CHIP_DETAIL_LOGGING +/** + * @def ChipLogDetail(MOD, MSG, ...) + * + * @brief + * Log a chip message for the specified module in the 'Detail' + * category. + * + */ +#define ChipLogDetail(MOD, MSG, ...) ChipInternalLog(MOD, DETAIL, MSG, ##__VA_ARGS__) + +#else // CHP_DETAIL_LOGGING +#define ChipLogDetail(MOD, MSG, ...) ((void) 0) +#endif // CHIP_DETAIL_LOGGING + +#if CHIP_AUTOMATION_LOGGING +/** + * @def ChipLogAutomation(MSG, ...) + * + * @brief + * Log a chip message for the specified module in the 'Automation' + * category. + * + */ +#define ChipLogAutomation(MSG, ...) ChipInternalLog(Automation, AUTOMATION, MSG, ##__VA_ARGS__) +#else // CHIP_AUTOMATION_LOGGING +#define ChipLogAutomation(MOD, MSG, ...) ((void) 0) +#endif // CHIP_AUTOMATION_LOGGING + +/** + * @def ChipLogIfFalse(aCondition) + * + * @brief + * This checks for the specified condition, which is expected to + * commonly be true and emits some log, based on configuration, if + * the condition is false. + * + * @note + * Evaluation of @a aCondition is always done, but logging is only enabled when + * #CHIP_CONFIG_ENABLE_CONDITION_LOGGING is enabled. This can be turned on or + * off for each compilation unit by enabling or disabling, as desired, + * #CHIP_CONFIG_ENABLE_CONDITION_LOGGING before ChipLogging.h is included by + * the preprocessor. + * + * Example Usage: + * + * @code + * #define CHIP_CONFIG_ENABLE_CONDITION_LOGGING 1 + * + * #include + * + * ... + * + * void foo(void) + * { + * CHIP_ERROR err = CHIP_NO_ERROR; + * + * ... + * + * exit: + * ChipLogIfFalse(CHIP_END_OF_TLV == err); + * } + * @endcode + * + * @param[in] aCondition A Boolean expression to be evaluated. + * + * @sa CHIP_CONFIG_ENABLE_TRACE_ON_CHECK_FAILURE + * + */ + +#if CHIP_CONFIG_ENABLE_CONDITION_LOGGING && !defined(ChipLogIfFalse) + +#define ChipLogIfFalse(aCondition) \ + do \ + { \ + if (!(aCondition)) \ + { \ + ChipLogError(NotSpecified, "Condition Failed (%s) at %s:%d", #aCondition, __FILE__, __LINE__); \ + } \ + } while (0) + +#else // CHIP_CONFIG_ENABLE_CONDITION_LOGGING + +#define ChipLogIfFalse(aCondition) IgnoreUnusedVariable(aCondition) + +#endif // CHIP_CONFIG_ENABLE_CONDITION_LOGGING + +/* + * @brief + * Macro for use in a string formatter for a 64-bit hex print. + * Will split into 2x 32-bit prints to support small printf libraries + * + * Example Usage: + * + * @code + * void foo() { + * uint64_t value = 0x1122334455667788; + * ChipLogProgress(Foo, "A 64-bit value: 0x" ChipLogFormatX64, ChipLogValueX64(value)); + * } + * @endcode + * + */ +#define ChipLogFormatX64 "%08" PRIX32 "%08" PRIX32 + +/* + * @brief + * Macro for use in a printf parameter list for a 64-bit value. + * Will split into MSB/LSB 32-bit values to use only 32-bit formatting. + * + * Example Usage: + * + * @code + * void foo() { + * uint64_t value = 0x1122334455667788; + * ChipLogProgress(Foo, "A 64-bit value: 0x" ChipLogFormatX64, ChipLogValueX64(value)); + * } + * @endcode + * + * @param[in] aValue 64-bit value that will be split in 32-bit MSB/LSB part + */ +#define ChipLogValueX64(aValue) static_cast(aValue >> 32), static_cast(aValue) + +/* + * @brief + * Macro for use in a string formatter for a MEI hex print. + * Will split into 2x 16-bit prints to display both the MEI prefix/suffix + * + * Example Usage: + * + * @code + * void foo() { + * chip::CommandId value = 0x12340001; + * ChipLogProgress(Foo, "A MEI value: " ChipLogFormatMEI, ChipLogValueMEI(value)); + * } + * @endcode + * + */ +#define ChipLogFormatMEI "0x%04X_%04X" + +/* + * @brief + * Macro for use in a printf parameter list for MEI value. + * Will split into MSB/LSB 16-bit values to separate prefix/suffix. + * + * Example Usage: + * + * @code + * void foo() { + * chip::CommandId value = 0x12340001; + * ChipLogProgress(Foo, "A MEI value: " ChipLogFormatMEI, ChipLogValueMEI(value)); + * } + * @endcode + * + * @param[in] aValue "32-bit value that will be split in 16-bit MSB/LSB part + */ +#define ChipLogValueMEI(aValue) static_cast(aValue >> 16), static_cast(aValue) + +/** + * Logging helpers for exchanges. For now just log the exchange id and whether + * it's an initiator or responder, but eventually we may want to log the peer + * node id as well (especially for the responder case). Some callsites only + * have the exchange id and initiator/responder boolean, not an actual exchange, + * so we want to have a helper for that case too. + */ +#define ChipLogFormatExchangeId "%u%c" +#define ChipLogValueExchangeId(id, isInitiator) id, ((isInitiator) ? 'i' : 'r') +#define ChipLogFormatExchange ChipLogFormatExchangeId +#define ChipLogValueExchange(ec) ChipLogValueExchangeId((ec)->GetExchangeId(), (ec)->IsInitiator()) +#define ChipLogValueExchangeIdFromSentHeader(payloadHeader) \ + ChipLogValueExchangeId((payloadHeader).GetExchangeID(), (payloadHeader).IsInitiator()) +// A received header's initiator boolean is the inverse of the exchange's. +#define ChipLogValueExchangeIdFromReceivedHeader(payloadHeader) \ + ChipLogValueExchangeId((payloadHeader).GetExchangeID(), !(payloadHeader).IsInitiator()) + +/** + * Logging helpers for protocol ids. A protocol id is a (vendor-id, + * protocol-id) pair. + */ +#define ChipLogFormatProtocolId "(%u, %u)" +#define ChipLogValueProtocolId(id) (id).GetVendorId(), (id).GetProtocolId() + +/** + * Logging helpers for message counters, so we format them consistently. + */ +#define ChipLogFormatMessageCounter "%" PRIu32 + +/** + * Logging helpers for message types, so we format them consistently. + */ +#define ChipLogFormatMessageType "0x%x" + +/** Logging helpers for scoped node ids, which is a tuple of */ +#define ChipLogFormatScopedNodeId "<" ChipLogFormatX64 ", %d>" +#define ChipLogValueScopedNodeId(id) ChipLogValueX64((id).GetNodeId()), (id).GetFabricIndex() + +/** + * CHIP Logging Implementation internals. + */ + +#if CHIP_ERROR_LOGGING || CHIP_PROGRESS_LOGGING || CHIP_DETAIL_LOGGING || CHIP_AUTOMATION_LOGGING +#define _CHIP_USE_LOGGING 1 +#else +#define _CHIP_USE_LOGGING 0 +#endif // CHIP_ERROR_LOGGING || CHIP_PROGRESS_LOGGING || CHIP_DETAIL_LOGGING || CHIP_AUTOMATION_LOGGING + +#if _CHIP_USE_LOGGING + +static constexpr uint16_t kMaxModuleNameLen = 3; + +#if CHIP_LOG_FILTERING +bool IsCategoryEnabled(uint8_t category); +#else // CHIP_LOG_FILTERING +inline bool IsCategoryEnabled(uint8_t category) +{ + return true; +} +#endif // CHIP_LOG_FILTERING + +/* Internal macros mapping upper case definitions to camel case category constants*/ +#define CHIP_LOG_CATEGORY_DETAIL chip::Logging::kLogCategory_Detail +#define CHIP_LOG_CATEGORY_PROGRESS chip::Logging::kLogCategory_Progress +#define CHIP_LOG_CATEGORY_ERROR chip::Logging::kLogCategory_Error +#define CHIP_LOG_CATEGORY_AUTOMATION chip::Logging::kLogCategory_Automation + +/* + * CHIP Logging Modules Categories filtering implementation. + * + * @brief + * Macro for use to check if given category is enabled for given log module. + * + * Example Usage: + * Let's assume PROGRESS category control for DeviceLayer log module. + * + * Default behavior - category is not modified, so macro returns global category value: + * IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns CHIP_PROGRESS_LOGGING + * + * Enabling category - category is enabled for module, the category value is ignored and the global value is used: + * #define CHIP_CONFIG_LOG_MODULE_DeviceLayer_PROGRESS 1 + * IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns CHIP_PROGRESS_LOGGING + * + * Disabling category - category is disabled for module, ignoring global category value: + * #define CHIP_CONFIG_LOG_MODULE_DeviceLayer_PROGRESS 0 + * IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns 0 + * + * Algorithm flow: + * 1. IsModuleCategoryEnabled(MOD, CAT) uses MOD and CAT to create strings for category module macro and global category macro, + * and invokes _IsModuleCategoryEnabled1(). + * 2. _IsModuleCategoryEnabled1(MOD_CAT, GLOB_CAT) invokes _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) to extract macros + * values. + * 3. _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) uses MOD_CAT to create string for helper macro. + * - If MOD_CAT is 0 the helper macro containing dummy arg is used. + * - If MOD_CAT doesn't exist, empty is used. + * 4. _IsModuleCategoryEnabled3(DUMMY_ARG, GLOB_CAT) invokes _IsModuleCategoryEnabled4() using different number of arguments + * depending on DUMMY_ARG. + * 5. _IsModuleCategoryEnabled4 output: + * - If category for module was not defined or define 1, the DUMMY_ARG was empty, so returning ARG2 is GLOB_CAT. + * - If category for module was defined 0, the DUMMY_ARG had one argument, so returning ARG2 is 0. + * + */ +#define IsModuleCategoryEnabled(MOD, CAT) _IsModuleCategoryEnabled1(CHIP_CONFIG_LOG_MODULE_##MOD##_##CAT, CHIP_##CAT##_LOGGING) +#define _IsModuleCategoryEnabled1(MOD_CAT, GLOB_CAT) _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) +#define _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) \ + _IsModuleCategoryEnabled3(_IsModuleCategoryEnabled3_DummyArg##MOD_CAT, GLOB_CAT) +#define _IsModuleCategoryEnabled3_DummyArg0 dummyArg, +#define _IsModuleCategoryEnabled3(DUMMY_ARG, GLOB_CAT) _IsModuleCategoryEnabled4(DUMMY_ARG 0, GLOB_CAT) +#define _IsModuleCategoryEnabled4(ARG1, ARG2, ...) (ARG2) + +void Log(uint8_t module, uint8_t category, const char * msg, ...) ENFORCE_FORMAT(3, 4); +void LogV(uint8_t module, uint8_t category, const char * msg, va_list args) ENFORCE_FORMAT(3, 0); + +#if CHIP_SYSTEM_CONFIG_PLATFORM_LOG +#ifndef ChipPlatformLog +#error "CHIP_SYSTEM_CONFIG_PLATFORM_LOG is enabled but ChipPlatformLog() is not defined" +#endif +#define ChipInternalLog(...) ChipPlatformLog(__VA_ARGS__) +#else // CHIP_SYSTEM_CONFIG_PLATFORM_LOG +#define ChipInternalLog(MOD, CAT, MSG, ...) \ + if (CHIP_CONFIG_LOG_MODULE_##MOD && IsModuleCategoryEnabled(MOD, CAT)) \ + { \ + ChipInternalLogImpl(MOD, CHIP_LOG_CATEGORY_##CAT, MSG, ##__VA_ARGS__); \ + } + +#endif // CHIP_SYSTEM_CONFIG_PLATFORM_LOG + +#if CHIP_PW_TOKENIZER_LOGGING + +void HandleTokenizedLog(uint32_t levels, pw_tokenizer_Token token, pw_tokenizer_ArgTypes, ...); + +#define ChipInternalLogImpl(MOD, CAT, MSG, ...) \ + do \ + { \ + if (chip::Logging::IsCategoryEnabled(CAT)) \ + { \ + PW_TOKENIZE_FORMAT_STRING(PW_TOKENIZER_DEFAULT_DOMAIN, UINT32_MAX, MSG, __VA_ARGS__); \ + ::chip::Logging::HandleTokenizedLog((uint32_t) ((CAT << 8) | chip::Logging::kLogModule_##MOD), _pw_tokenizer_token, \ + PW_TOKENIZER_ARG_TYPES(__VA_ARGS__) PW_COMMA_ARGS(__VA_ARGS__)); \ + } \ + } while (0) +#else // CHIP_PW_TOKENIZER_LOGGING +#define ChipInternalLogImpl(MOD, CAT, MSG, ...) \ + do \ + { \ + if (chip::Logging::IsCategoryEnabled(CAT)) \ + { \ + chip::Logging::Log(chip::Logging::kLogModule_##MOD, CAT, MSG, ##__VA_ARGS__); \ + } \ + } while (0) +#endif // CHIP_PW_TOKENIZER_LOGGING + +#endif // _CHIP_USE_LOGGING + +} // namespace Logging +} // namespace chip