| /* | |
| * Copyright (c) 2017-2022, 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 the logging related functions. | |
| */ | |
| #include "log.hpp" | |
| #include <ctype.h> | |
| #include <openthread/platform/logging.h> | |
| #include "common/code_utils.hpp" | |
| #include "common/num_utils.hpp" | |
| #include "common/numeric_limits.hpp" | |
| #include "common/string.hpp" | |
| #include "instance/instance.hpp" | |
| /* | |
| * Verify debug UART dependency. | |
| * | |
| * It is reasonable to only enable the debug UART and not enable logs to the DEBUG UART. | |
| */ | |
| #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && (!OPENTHREAD_CONFIG_ENABLE_DEBUG_UART) | |
| #error "OPENTHREAD_CONFIG_ENABLE_DEBUG_UART_LOG requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART" | |
| #endif | |
| #if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && !OPENTHREAD_CONFIG_UPTIME_ENABLE | |
| #error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME requires OPENTHREAD_CONFIG_UPTIME_ENABLE" | |
| #endif | |
| #if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE | |
| #error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME is not supported under OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE" | |
| #endif | |
| namespace ot { | |
| #if OT_SHOULD_LOG | |
| template <LogLevel kLogLevel> void Logger::LogAtLevel(const char *aModuleName, const char *aFormat, ...) | |
| { | |
| va_list args; | |
| va_start(args, aFormat); | |
| LogVarArgs(aModuleName, kLogLevel, aFormat, args); | |
| va_end(args); | |
| } | |
| // Explicit instantiations | |
| template void Logger::LogAtLevel<kLogLevelNone>(const char *aModuleName, const char *aFormat, ...); | |
| template void Logger::LogAtLevel<kLogLevelCrit>(const char *aModuleName, const char *aFormat, ...); | |
| template void Logger::LogAtLevel<kLogLevelWarn>(const char *aModuleName, const char *aFormat, ...); | |
| template void Logger::LogAtLevel<kLogLevelNote>(const char *aModuleName, const char *aFormat, ...); | |
| template void Logger::LogAtLevel<kLogLevelInfo>(const char *aModuleName, const char *aFormat, ...); | |
| template void Logger::LogAtLevel<kLogLevelDebg>(const char *aModuleName, const char *aFormat, ...); | |
| void Logger::LogInModule(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, ...) | |
| { | |
| va_list args; | |
| va_start(args, aFormat); | |
| LogVarArgs(aModuleName, aLogLevel, aFormat, args); | |
| va_end(args); | |
| } | |
| void Logger::LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs) | |
| { | |
| static const char kModuleNamePadding[] = "--------------"; | |
| ot::String<OPENTHREAD_CONFIG_LOG_MAX_SIZE> logString; | |
| static_assert(sizeof(kModuleNamePadding) == kMaxLogModuleNameLength + 1, "Padding string is not correct"); | |
| #if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME | |
| ot::Uptime::UptimeToString(ot::Instance::Get().Get<ot::Uptime>().GetUptime(), logString, /* aInlcudeMsec */ true); | |
| logString.Append(" "); | |
| #endif | |
| #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE | |
| VerifyOrExit(Instance::GetLogLevel() >= aLogLevel); | |
| #endif | |
| #if OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL | |
| { | |
| static const char kLevelChars[] = { | |
| '-', /* kLogLevelNone */ | |
| 'C', /* kLogLevelCrit */ | |
| 'W', /* kLogLevelWarn */ | |
| 'N', /* kLogLevelNote */ | |
| 'I', /* kLogLevelInfo */ | |
| 'D', /* kLogLevelDebg */ | |
| }; | |
| logString.Append("[%c] ", kLevelChars[aLogLevel]); | |
| } | |
| #endif | |
| logString.Append("%.*s%s: ", kMaxLogModuleNameLength, aModuleName, | |
| &kModuleNamePadding[StringLength(aModuleName, kMaxLogModuleNameLength)]); | |
| logString.AppendVarArgs(aFormat, aArgs); | |
| logString.Append("%s", OPENTHREAD_CONFIG_LOG_SUFFIX); | |
| otPlatLog(aLogLevel, OT_LOG_REGION_CORE, "%s", logString.AsCString()); | |
| ExitNow(); | |
| exit: | |
| return; | |
| } | |
| #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) | |
| void Logger::LogOnError(const char *aModuleName, Error aError, const char *aText) | |
| { | |
| if (aError != kErrorNone) | |
| { | |
| LogAtLevel<kLogLevelWarn>(aModuleName, "Failed to %s: %s", aText, ErrorToString(aError)); | |
| } | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_LOG_PKT_DUMP | |
| template <LogLevel kLogLevel> | |
| void Logger::DumpAtLevel(const char *aModuleName, const char *aText, const void *aData, uint16_t aDataLength) | |
| { | |
| DumpInModule(aModuleName, kLogLevel, aText, aData, aDataLength); | |
| } | |
| // Explicit instantiations | |
| template void Logger::DumpAtLevel<kLogLevelNone>(const char *aModuleName, | |
| const char *aText, | |
| const void *aData, | |
| uint16_t aDataLength); | |
| template void Logger::DumpAtLevel<kLogLevelCrit>(const char *aModuleName, | |
| const char *aText, | |
| const void *aData, | |
| uint16_t aDataLength); | |
| template void Logger::DumpAtLevel<kLogLevelWarn>(const char *aModuleName, | |
| const char *aText, | |
| const void *aData, | |
| uint16_t aDataLength); | |
| template void Logger::DumpAtLevel<kLogLevelNote>(const char *aModuleName, | |
| const char *aText, | |
| const void *aData, | |
| uint16_t aDataLength); | |
| template void Logger::DumpAtLevel<kLogLevelInfo>(const char *aModuleName, | |
| const char *aText, | |
| const void *aData, | |
| uint16_t aDataLength); | |
| template void Logger::DumpAtLevel<kLogLevelDebg>(const char *aModuleName, | |
| const char *aText, | |
| const void *aData, | |
| uint16_t aDataLength); | |
| void Logger::DumpInModule(const char *aModuleName, | |
| LogLevel aLogLevel, | |
| const char *aText, | |
| const void *aData, | |
| uint16_t aDataLength) | |
| { | |
| HexDumpInfo info; | |
| VerifyOrExit(otLoggingGetLevel() >= aLogLevel); | |
| info.mDataBytes = reinterpret_cast<const uint8_t *>(aData); | |
| info.mDataLength = aDataLength; | |
| info.mTitle = aText; | |
| info.mIterator = 0; | |
| while (GenerateNextHexDumpLine(info) == kErrorNone) | |
| { | |
| LogInModule(aModuleName, aLogLevel, "%s", info.mLine); | |
| } | |
| exit: | |
| return; | |
| } | |
| #endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP | |
| #endif // OT_SHOULD_LOG | |
| Error GenerateNextHexDumpLine(HexDumpInfo &aInfo) | |
| { | |
| constexpr uint16_t kIterTableStartLine = 0; | |
| constexpr uint16_t kIterFirstDataLine = NumericLimits<uint16_t>::kMax - 2; | |
| constexpr uint16_t kIterTableEndLine = NumericLimits<uint16_t>::kMax - 1; | |
| constexpr uint16_t kIterFinished = NumericLimits<uint16_t>::kMax; | |
| constexpr uint16_t kWidth = 72; | |
| constexpr uint16_t kTitleSuffixLen = sizeof("[ len=000]") - 1; | |
| constexpr uint16_t kDumpBytesPerLine = 16; | |
| Error error = kErrorNone; | |
| StringWriter writer(aInfo.mLine, sizeof(aInfo.mLine)); | |
| switch (aInfo.mIterator) | |
| { | |
| case kIterTableStartLine: | |
| { | |
| uint16_t txtLen = StringLength(aInfo.mTitle, kWidth - kTitleSuffixLen) + kTitleSuffixLen; | |
| writer.AppendCharMultipleTimes('=', static_cast<uint16_t>((kWidth - txtLen) / 2)); | |
| writer.Append("[%s len=%03u]", aInfo.mTitle, aInfo.mDataLength); | |
| writer.AppendCharMultipleTimes('=', static_cast<uint16_t>(kWidth - txtLen - (kWidth - txtLen) / 2)); | |
| aInfo.mIterator = kIterFirstDataLine; | |
| break; | |
| } | |
| case kIterTableEndLine: | |
| writer.AppendCharMultipleTimes('-', kWidth); | |
| aInfo.mIterator = kIterFinished; | |
| break; | |
| case kIterFinished: | |
| error = kErrorNotFound; | |
| break; | |
| case kIterFirstDataLine: | |
| aInfo.mIterator = 0; | |
| OT_FALL_THROUGH; | |
| default: | |
| { | |
| uint16_t startIndex = aInfo.mIterator; | |
| uint16_t endIndex = aInfo.mIterator + kDumpBytesPerLine; | |
| writer.Append("|"); | |
| for (uint16_t i = startIndex; i < endIndex; i++) | |
| { | |
| (i < aInfo.mDataLength) ? writer.Append(" %02X", aInfo.mDataBytes[i]) : writer.Append(" "); | |
| if ((i % 8) == 7) | |
| { | |
| writer.Append(" |"); | |
| } | |
| } | |
| writer.Append(" "); | |
| for (uint16_t i = startIndex; i < endIndex; i++) | |
| { | |
| char c = ' '; | |
| if (i < aInfo.mDataLength) | |
| { | |
| uint8_t byte = aInfo.mDataBytes[i]; | |
| c = ((byte < 127) && isprint(static_cast<char>(byte))) ? static_cast<char>(byte) : '.'; | |
| } | |
| writer.Append("%c", c); | |
| } | |
| writer.Append(" |"); | |
| aInfo.mIterator = endIndex; | |
| if (aInfo.mIterator >= aInfo.mDataLength) | |
| { | |
| aInfo.mIterator = kIterTableEndLine; | |
| } | |
| break; | |
| } | |
| } | |
| return error; | |
| } | |
| } // namespace ot |