[log] add otLogGenerateNextHexDumpLine() to generate hex dump (#9555)

This commit adds a public OT API to generate hex dump output line by
line. This function is then used for both `LogDump{}()` and frame
capture output by CLI `promiscuous` command (removing repeated
similar code) and harmonize the hex dump outputs.
This commit is contained in:
Abtin Keshavarzian
2023-10-25 14:54:33 -07:00
committed by GitHub
parent 9106817c62
commit 91b7c3ff3f
10 changed files with 203 additions and 142 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (368)
#define OPENTHREAD_API_VERSION (369)
/**
* @addtogroup api-instance
+39
View File
@@ -253,6 +253,45 @@ void otLogPlatArgs(otLogLevel aLogLevel, const char *aPlatModuleName, const char
*/
void otLogCli(otLogLevel aLogLevel, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
#define OT_LOG_HEX_DUMP_LINE_SIZE 73 ///< Hex dump line string size.
/**
* Represents information used for generating hex dump output.
*
*/
typedef struct
{
const uint8_t *mDataBytes; ///< The data byes.
uint16_t mDataLength; ///< The data length (number of bytes in @p mDataBytes)
const char *mTitle; ///< Title string to add table header (MUST NOT be `NULL`).
char mLine[OT_LOG_HEX_DUMP_LINE_SIZE]; ///< Buffer to output one line of generated hex dump.
uint16_t mIterator; ///< Iterator used by OT stack. MUST be initialized to zero.
} otLogHexDumpInfo;
/**
* Generates the next hex dump line.
*
* Can call this method back-to-back to generate the hex dump output line by line. On the first call the `mIterator`
* field in @p aInfo MUST be set to zero.
*
* Here is an example of the generated hex dump output:
*
* "==========================[{mTitle} len=070]============================"
* "| 41 D8 87 34 12 FF FF 25 | 4C 57 DA F2 FB 2F 62 7F | A..4...%LW.../b. |"
* "| 3B 01 F0 4D 4C 4D 4C 54 | 4F 00 15 15 00 00 00 00 | ;..MLMLTO....... |"
* "| 00 00 00 01 80 DB 60 82 | 7E 33 72 3B CC B3 A1 84 | ......`.~3r;.... |"
* "| 3B E6 AD B2 0B 45 E7 45 | C5 B9 00 1A CB 2D 6D 1C | ;....E.E.....-m. |"
* "| 10 3E 3C F5 D3 70 | | .><..p |"
* "------------------------------------------------------------------------"
*
* @param[in,out] aInfo A pointer to `otLogHexDumpInfo` to use to generate hex dump.
*
* @retval OT_ERROR_NONE Successfully generated the next line, `mLine` field in @p aInfo is updated.
* @retval OT_ERROR_NOT_FOUND Reached the end and no more line to generate.
*
*/
otError otLogGenerateNextHexDumpLine(otLogHexDumpInfo *aInfo);
/**
* @}
*
+8 -59
View File
@@ -6239,70 +6239,19 @@ void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx,
void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx)
{
OT_UNUSED_VARIABLE(aIsTx);
otLogHexDumpInfo info;
info.mDataBytes = aFrame->mPsdu;
info.mDataLength = aFrame->mLength;
info.mTitle = (aIsTx) ? "TX" : "RX";
info.mIterator = 0;
OutputNewLine();
for (size_t i = 0; i < 44; i++)
while (otLogGenerateNextHexDumpLine(&info) == OT_ERROR_NONE)
{
OutputFormat("=");
OutputLine("%s", info.mLine);
}
OutputFormat("[len = %3u]", aFrame->mLength);
for (size_t i = 0; i < 28; i++)
{
OutputFormat("=");
}
OutputNewLine();
for (size_t i = 0; i < aFrame->mLength; i += 16)
{
OutputFormat("|");
for (size_t j = 0; j < 16; j++)
{
if (i + j < aFrame->mLength)
{
OutputFormat(" %02X", aFrame->mPsdu[i + j]);
}
else
{
OutputFormat(" ..");
}
}
OutputFormat("|");
for (size_t j = 0; j < 16; j++)
{
if (i + j < aFrame->mLength)
{
if (31 < aFrame->mPsdu[i + j] && aFrame->mPsdu[i + j] < 127)
{
OutputFormat(" %c", aFrame->mPsdu[i + j]);
}
else
{
OutputFormat(" ?");
}
}
else
{
OutputFormat(" .");
}
}
OutputLine("|");
}
for (size_t i = 0; i < 83; i++)
{
OutputFormat("-");
}
OutputNewLine();
}
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
+7
View File
@@ -232,3 +232,10 @@ exit:
#endif
return;
}
otError otLogGenerateNextHexDumpLine(otLogHexDumpInfo *aInfo)
{
AssertPointerIsNotNull(aInfo);
return GenerateNextHexDumpLine(*aInfo);
}
+98 -76
View File
@@ -40,6 +40,7 @@
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/num_utils.hpp"
#include "common/numeric_limits.hpp"
#include "common/string.hpp"
/*
@@ -169,99 +170,120 @@ template void Logger::DumpAtLevel<kLogLevelDebg>(const char *aModuleName,
const void *aData,
uint16_t aDataLength);
void Logger::DumpLine(const char *aModuleName, LogLevel aLogLevel, const uint8_t *aData, const uint16_t aDataLength)
{
ot::String<kStringLineLength> string;
string.Append("|");
for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
{
if (i < aDataLength)
{
string.Append(" %02X", aData[i]);
}
else
{
string.Append(" ..");
}
if (!((i + 1) % 8))
{
string.Append(" |");
}
}
string.Append(" ");
for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
{
char c = '.';
if (i < aDataLength)
{
char byteAsChar = static_cast<char>(0x7f & aData[i]);
if (isprint(byteAsChar))
{
c = byteAsChar;
}
}
string.Append("%c", c);
}
LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
}
void Logger::DumpInModule(const char *aModuleName,
LogLevel aLogLevel,
const char *aText,
const void *aData,
uint16_t aDataLength)
{
constexpr uint16_t kWidth = 72;
constexpr uint16_t kTextSuffixLen = sizeof("[ len=000]") - 1;
uint16_t txtLen = StringLength(aText, kWidth - kTextSuffixLen) + kTextSuffixLen;
ot::String<kStringLineLength> string;
HexDumpInfo info;
VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
for (uint16_t i = 0; i < static_cast<uint16_t>((kWidth - txtLen) / 2); i++)
info.mDataBytes = reinterpret_cast<const uint8_t *>(aData);
info.mDataLength = aDataLength;
info.mTitle = aText;
info.mIterator = 0;
while (GenerateNextHexDumpLine(info) == kErrorNone)
{
string.Append("=");
LogInModule(aModuleName, aLogLevel, "%s", info.mLine);
}
string.Append("[%s len=%03u]", aText, aDataLength);
for (uint16_t i = 0; i < static_cast<uint16_t>(kWidth - txtLen - (kWidth - txtLen) / 2); i++)
{
string.Append("=");
}
LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
for (uint16_t i = 0; i < aDataLength; i += kDumpBytesPerLine)
{
DumpLine(aModuleName, aLogLevel, static_cast<const uint8_t *>(aData) + i,
Min(static_cast<uint8_t>(aDataLength - i), kDumpBytesPerLine));
}
string.Clear();
for (uint16_t i = 0; i < kWidth; i++)
{
string.Append("-");
}
LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
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
+28 -2
View File
@@ -40,6 +40,8 @@
#include <openthread/platform/logging.h>
#include <openthread/platform/toolchain.h>
#include "common/error.hpp"
namespace ot {
/**
@@ -332,8 +334,6 @@ public:
template <LogLevel kLogLevel>
static void DumpAtLevel(const char *aModuleName, const char *aText, const void *aData, uint16_t aDataLength);
static void DumpLine(const char *aModuleName, LogLevel aLogLevel, const uint8_t *aData, uint16_t aDataLength);
#endif
};
@@ -372,6 +372,32 @@ extern template void Logger::DumpAtLevel<kLogLevelDebg>(const char *aModuleName,
#endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
#endif // OT_SHOULD_LOG
typedef otLogHexDumpInfo HexDumpInfo; ///< Represents the hex dump info.
/**
* Generates the next hex dump line.
*
* Can call this method back-to-back to generate the hex dump output line by line. On the first call the `mIterator`
* field in @p aInfo MUST be set to zero.
*
* Here is an example of the generated hex dump output:
*
* "==========================[{mTitle} len=070]============================"
* "| 41 D8 87 34 12 FF FF 25 | 4C 57 DA F2 FB 2F 62 7F | A..4...%LW.../b. |"
* "| 3B 01 F0 4D 4C 4D 4C 54 | 4F 00 15 15 00 00 00 00 | ;..MLMLTO....... |"
* "| 00 00 00 01 80 DB 60 82 | 7E 33 72 3B CC B3 A1 84 | ......`.~3r;.... |"
* "| 3B E6 AD B2 0B 45 E7 45 | C5 B9 00 1A CB 2D 6D 1C | ;....E.E.....-m. |"
* "| 10 3E 3C F5 D3 70 | | .><..p |"
* "------------------------------------------------------------------------"
*
* @param[in,out] aInfo A reference to a `LogHexDumpInfo` to use to generate hex dump.
*
* @retval kErrorNone Successfully generated the next line, `mLine` field in @p aInfo is updated.
* @retval kErrorNotFound Reached the end and no more line to generate.
*
*/
Error GenerateNextHexDumpLine(HexDumpInfo &aInfo);
} // namespace ot
#endif // LOG_HPP_
+10
View File
@@ -310,6 +310,16 @@ StringWriter &StringWriter::AppendHexBytes(const uint8_t *aBytes, uint16_t aLeng
return *this;
}
StringWriter &StringWriter::AppendCharMultipleTimes(char aChar, uint16_t aCount)
{
while (aCount--)
{
Append("%c", aChar);
}
return *this;
}
bool IsValidUtf8String(const char *aString) { return IsValidUtf8String(aString, strlen(aString)); }
bool IsValidUtf8String(const char *aString, size_t aLength)
+9
View File
@@ -423,6 +423,15 @@ public:
*/
StringWriter &AppendHexBytes(const uint8_t *aBytes, uint16_t aLength);
/**
* Appends a given character a given number of times.
*
* @param[in] aChar The character to append.
* @param[in] aCount Number of times to append @p aChar.
*
*/
StringWriter &AppendCharMultipleTimes(char aChar, uint16_t aCount);
/**
* Converts all uppercase letter characters in the string to lowercase.
*
+2 -3
View File
@@ -76,9 +76,8 @@ expect "16 bytes from $addr: icmp_seq=1"
expect_line "Done"
switch_node 3
expect -re {============================================\[len = +\d+]============================}
expect -re {\|( ([0-9A-Z]{2}|\.\.)){16}\|( .){16}\|}
expect -- "-----------------------------------------------------------------------------------"
expect -re {==============================\[RX len=\d+\]==============================}
expect -- "------------------------------------------------------------------------"
send "promiscuous disable\n"
expect_line "Done"
+1 -1
View File
@@ -111,7 +111,7 @@
#define OPENTHREAD_CONFIG_MAC_FILTER_SIZE 80
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_DEBG
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 1