-
Notifications
You must be signed in to change notification settings - Fork 821
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add a tflite::hexdump() for printing raw memory
Add tflite::hexdump() for printing raw memory to output streams. Copy the output format of Python's hexdump module.
- Loading branch information
Showing
4 changed files
with
222 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// Copyright 2024 The TensorFlow Authors. All Rights Reserved. | ||
// | ||
// 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 "tensorflow/lite/micro/hexdump.h" | ||
|
||
#include <algorithm> | ||
#include <cctype> | ||
|
||
#include "tensorflow/lite/micro/debug_log.h" | ||
#include "tensorflow/lite/micro/static_vector.h" | ||
|
||
namespace { | ||
|
||
tflite::Span<char> output(const tflite::Span<char>& buf, const char* format, | ||
...) { | ||
// Writes formatted output, printf-style, to either a buffer or DebugLog. | ||
// Writes to DebugLog if the buffer data pointer is null. Does not exceed | ||
// the size of the buffer. Returns the unused remainder of the buffer, or a | ||
// buffer with a null data pointer in the case of printing to DebugLog. | ||
|
||
tflite::Span<char> result{nullptr, 0}; | ||
|
||
va_list args; | ||
va_start(args, format); | ||
|
||
if (buf.data() == nullptr) { | ||
DebugLog(format, args); | ||
result = {nullptr, 0}; | ||
} else { | ||
size_t len = DebugVsnprintf(buf.data(), buf.size(), format, args); | ||
// Returns the number of characters that would have been written if | ||
// there were enough room, so cap it at the size of the buffer in order to | ||
// know how much was actually written. | ||
size_t consumed = std::min(len, buf.size()); | ||
result = {buf.data() + consumed, buf.size() - consumed}; | ||
} | ||
|
||
va_end(args); | ||
return result; | ||
} | ||
|
||
} // end anonymous namespace | ||
|
||
tflite::Span<char> tflite::hexdump(const tflite::Span<const std::byte> region, | ||
const tflite::Span<char> out) { | ||
tflite::Span<char> buffer{out}; | ||
std::size_t byte_nr = 0; | ||
constexpr int per_line = 16; | ||
const int lines = (region.size() + per_line - 1) / per_line; // round up | ||
|
||
for (int line = 0; line < lines; ++line) { | ||
tflite::StaticVector<char, per_line> ascii; | ||
|
||
// print address | ||
buffer = output(buffer, "%08X:", line); | ||
|
||
for (int pos = 0; pos < per_line; ++pos) { | ||
if (byte_nr < region.size()) { | ||
// print byte | ||
int as_int = static_cast<int>(region[byte_nr++]); | ||
buffer = output(buffer, " %02X", as_int); | ||
|
||
// buffer an ascii printable value | ||
char c{'.'}; | ||
if (std::isprint(as_int)) { | ||
c = static_cast<char>(as_int); | ||
} | ||
ascii.push_back(c); | ||
} else { | ||
buffer = output(buffer, " "); | ||
} | ||
|
||
// print extra space in middle of the line | ||
if (pos == per_line / 2 - 1) { | ||
buffer = output(buffer, " "); | ||
} | ||
} | ||
|
||
// print the ascii value | ||
buffer = output(buffer, " "); | ||
for (const auto& c : ascii) { | ||
buffer = output(buffer, "%c", c); | ||
} | ||
buffer = output(buffer, "%c", '\n'); | ||
} | ||
|
||
return {out.data(), out.size() - buffer.size()}; | ||
} | ||
|
||
void tflite::hexdump(const tflite::Span<const std::byte> region) { | ||
hexdump(region, {nullptr, 0}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright 2024 The TensorFlow Authors. All Rights Reserved. | ||
// | ||
// 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. | ||
|
||
#ifndef TENSORFLOW_LITE_MICRO_HEXDUMP_H_ | ||
#define TENSORFLOW_LITE_MICRO_HEXDUMP_H_ | ||
|
||
#include <cstddef> | ||
|
||
#include "tensorflow/lite/micro/span.h" | ||
|
||
namespace tflite { | ||
|
||
// Displays the contents of a memory region, formatted in hexadecimal and ASCII | ||
// in a style matching Python's hexdump module, using DebugLog(). | ||
void hexdump(Span<const std::byte> region); | ||
|
||
// Writes the contents of a memory region, formatted in hexadecimal and ASCII | ||
// in a style matching Python's hexdump module, to a buffer. Returns the portion | ||
// of the buffer written. | ||
Span<char> hexdump(Span<const std::byte> region, Span<char> buffer); | ||
|
||
} // end namespace tflite | ||
|
||
#endif // TENSORFLOW_LITE_MICRO_HEXDUMP_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright 2024 The TensorFlow Authors. All Rights Reserved. | ||
// | ||
// 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 "tensorflow/lite/micro/hexdump.h" | ||
|
||
#include <array> | ||
|
||
#include "tensorflow/lite/micro/span.h" | ||
#include "tensorflow/lite/micro/testing/micro_test.h" | ||
|
||
constexpr tflite::Span<const char> input{ | ||
"This is an input string for testing."}; | ||
|
||
const tflite::Span<const std::byte> region{ | ||
reinterpret_cast<const std::byte*>(input.data()), input.size()}; | ||
|
||
// clang-format off | ||
constexpr tflite::Span<const char> expected{ | ||
"00000000: 54 68 69 73 20 69 73 20 61 6E 20 69 6E 70 75 74 This is an input\n" | ||
"00000001: 20 73 74 72 69 6E 67 20 66 6F 72 20 74 65 73 74 string for test\n" | ||
"00000002: 69 6E 67 2E 00 ing..\n"}; | ||
// clang-format on | ||
|
||
// String literals have null terminators, but don't expect a null terminator | ||
// in the hexdump output. | ||
constexpr tflite::Span<const char> expected_no_null{expected.data(), | ||
expected.size() - 1}; | ||
|
||
TF_LITE_MICRO_TESTS_BEGIN | ||
|
||
TF_LITE_MICRO_TEST(TestOutputToBuffer) { | ||
// Allocate a buffer with an arbitrary amount of extra room so the test has | ||
// the possibility of failing if hexdump mishandles the extra space. | ||
std::array<char, expected.size() + 10> buffer; | ||
|
||
tflite::Span<char> output = tflite::hexdump(region, buffer); | ||
TF_LITE_MICRO_EXPECT(output == expected_no_null); | ||
} | ||
|
||
TF_LITE_MICRO_TEST(TestOutputToDebugLog) { | ||
// There's no easy way to verify DebugLog output; however, test it anyhow to | ||
// catch an outright crash, and so the output appears in the log should | ||
// someone wish to examine it. | ||
tflite::hexdump(region); | ||
} | ||
|
||
TF_LITE_MICRO_TESTS_END |