mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[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:
committed by
GitHub
parent
9106817c62
commit
91b7c3ff3f
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -232,3 +232,10 @@ exit:
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
otError otLogGenerateNextHexDumpLine(otLogHexDumpInfo *aInfo)
|
||||
{
|
||||
AssertPointerIsNotNull(aInfo);
|
||||
|
||||
return GenerateNextHexDumpLine(*aInfo);
|
||||
}
|
||||
|
||||
+98
-76
@@ -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
@@ -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_
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user