Extract DebugLog calls to a single location in MicroErrorReporter. Expose MicroSnprintf method for use elsewhere in the micro codebase.
PiperOrigin-RevId: 297743314 Change-Id: I235d065d0ee754c108108c14b3b0f632b8d78614
This commit is contained in:
parent
3be1f72f52
commit
02fac26517
@ -26,7 +26,6 @@ cc_library(
|
||||
name = "micro_framework",
|
||||
srcs = [
|
||||
"debug_log.cc",
|
||||
"debug_log_numbers.cc",
|
||||
"memory_helpers.cc",
|
||||
"micro_allocator.cc",
|
||||
"micro_error_reporter.cc",
|
||||
@ -37,7 +36,6 @@ cc_library(
|
||||
],
|
||||
hdrs = [
|
||||
"debug_log.h",
|
||||
"debug_log_numbers.h",
|
||||
"memory_helpers.h",
|
||||
"micro_allocator.h",
|
||||
"micro_error_reporter.h",
|
||||
@ -55,6 +53,7 @@ cc_library(
|
||||
],
|
||||
deps = [
|
||||
":micro_compatibility",
|
||||
":micro_string",
|
||||
":micro_utils",
|
||||
"//tensorflow/lite:type_to_tflitetype",
|
||||
"//tensorflow/lite/c:common",
|
||||
@ -65,6 +64,23 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "micro_string",
|
||||
srcs = [
|
||||
"micro_string.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"micro_string.h",
|
||||
],
|
||||
build_for_embedded = True,
|
||||
copts = micro_copts(),
|
||||
deps = [
|
||||
"//tensorflow/lite/c:common",
|
||||
"//tensorflow/lite/core/api",
|
||||
"//tensorflow/lite/kernels/internal:compatibility",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "micro_utils",
|
||||
srcs = [
|
||||
@ -168,3 +184,14 @@ tflite_micro_cc_test(
|
||||
"//tensorflow/lite/micro/testing:micro_test",
|
||||
],
|
||||
)
|
||||
|
||||
tflite_micro_cc_test(
|
||||
name = "micro_string_test",
|
||||
srcs = [
|
||||
"micro_string_test.cc",
|
||||
],
|
||||
deps = [
|
||||
":micro_string",
|
||||
"//tensorflow/lite/micro/testing:micro_test",
|
||||
],
|
||||
)
|
||||
|
@ -1,28 +0,0 @@
|
||||
/* Copyright 2018 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_DEBUG_LOG_NUMBERS_H_
|
||||
#define TENSORFLOW_LITE_MICRO_DEBUG_LOG_NUMBERS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Output numbers to the debug logging stream.
|
||||
extern "C" {
|
||||
void DebugLogInt32(int32_t i);
|
||||
void DebugLogUInt32(uint32_t i);
|
||||
void DebugLogHex(uint32_t i);
|
||||
void DebugLogFloat(float i);
|
||||
}
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_DEBUG_LOG_NUMBERS_H_
|
@ -15,53 +15,21 @@ limitations under the License.
|
||||
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
#include "tensorflow/lite/micro/micro_string.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
void DebugLogPrintf(const char* format, va_list args) {
|
||||
const int output_cache_size = 64;
|
||||
char output_cache[output_cache_size + 1];
|
||||
int output_cache_index = 0;
|
||||
const char* current = format;
|
||||
while (*current != 0) {
|
||||
if (*current == '%') {
|
||||
const char next = *(current + 1);
|
||||
if ((next == 'd') || (next == 's') || (next == 'f')) {
|
||||
current += 1;
|
||||
if (output_cache_index > 0) {
|
||||
output_cache[output_cache_index] = 0;
|
||||
DebugLog(output_cache);
|
||||
output_cache_index = 0;
|
||||
}
|
||||
if (next == 'd') {
|
||||
DebugLogInt32(va_arg(args, int));
|
||||
} else if (next == 's') {
|
||||
DebugLog(va_arg(args, char*));
|
||||
} else if (next == 'f') {
|
||||
DebugLogFloat(va_arg(args, double));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
output_cache[output_cache_index] = *current;
|
||||
output_cache_index += 1;
|
||||
}
|
||||
if (output_cache_index >= output_cache_size) {
|
||||
output_cache[output_cache_index] = 0;
|
||||
DebugLog(output_cache);
|
||||
output_cache_index = 0;
|
||||
}
|
||||
current += 1;
|
||||
}
|
||||
if (output_cache_index > 0) {
|
||||
output_cache[output_cache_index] = 0;
|
||||
DebugLog(output_cache);
|
||||
output_cache_index = 0;
|
||||
}
|
||||
DebugLog("\r\n");
|
||||
}
|
||||
|
||||
constexpr int kMaxLogLen = 256;
|
||||
|
||||
} // namespace
|
||||
|
||||
int MicroErrorReporter::Report(const char* format, va_list args) {
|
||||
DebugLogPrintf(format, args);
|
||||
char log_buffer[kMaxLogLen];
|
||||
MicroVsnprintf(log_buffer, kMaxLogLen, format, args);
|
||||
DebugLog(log_buffer);
|
||||
DebugLog("\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/micro/compatibility.h"
|
||||
#include "tensorflow/lite/micro/debug_log.h"
|
||||
#include "tensorflow/lite/micro/debug_log_numbers.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
|
@ -19,14 +19,22 @@ limitations under the License.
|
||||
// of DebugLog() and then get the numerical variations without requiring any
|
||||
// more code.
|
||||
|
||||
#include "tensorflow/lite/micro/debug_log_numbers.h"
|
||||
|
||||
#include "tensorflow/lite/micro/debug_log.h"
|
||||
#include "tensorflow/lite/micro/micro_string.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Int formats can need up to 10 bytes for the value plus a single byte for the
|
||||
// sign.
|
||||
constexpr int kMaxIntCharsNeeded = 10 + 1;
|
||||
// Hex formats can need up to 8 bytes for the value plus two bytes for the "0x".
|
||||
constexpr int kMaxHexCharsNeeded = 8 + 2;
|
||||
|
||||
// Float formats can need up to 7 bytes for the fraction plus 3 bytes for "x2^"
|
||||
// plus 3 bytes for the exponent and a single sign bit.
|
||||
constexpr float kMaxFloatCharsNeeded = 7 + 3 + 3 + 1;
|
||||
|
||||
// All input buffers to the number conversion functions must be this long.
|
||||
static const int kFastToBufferSize = 48;
|
||||
const int kFastToBufferSize = 48;
|
||||
|
||||
// Reverses a zero-terminated string in-place.
|
||||
char* ReverseStringInPlace(char* start, char* end) {
|
||||
@ -158,28 +166,97 @@ char* FastFloatToBufferLeft(float f, char* buffer) {
|
||||
return current;
|
||||
}
|
||||
|
||||
int FormatInt32(char* output, int32_t i) {
|
||||
return static_cast<int>(FastInt32ToBufferLeft(i, output) - output);
|
||||
}
|
||||
|
||||
int FormatUInt32(char* output, uint32_t i) {
|
||||
return static_cast<int>(FastUInt32ToBufferLeft(i, output, 10) - output);
|
||||
}
|
||||
|
||||
int FormatHex(char* output, uint32_t i) {
|
||||
return static_cast<int>(FastUInt32ToBufferLeft(i, output, 16) - output);
|
||||
}
|
||||
|
||||
int FormatFloat(char* output, float i) {
|
||||
return static_cast<int>(FastFloatToBufferLeft(i, output) - output);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" void DebugLogInt32(int32_t i) {
|
||||
char number_string[kFastToBufferSize];
|
||||
FastInt32ToBufferLeft(i, number_string);
|
||||
DebugLog(number_string);
|
||||
extern "C" int MicroVsnprintf(char* output, int len, const char* format,
|
||||
va_list args) {
|
||||
int output_index = 0;
|
||||
const char* current = format;
|
||||
// One extra character must be left for the null terminator.
|
||||
const int usable_length = len - 1;
|
||||
while (*current != '\0' && output_index < usable_length) {
|
||||
if (*current == '%') {
|
||||
current++;
|
||||
switch (*current) {
|
||||
case 'd':
|
||||
// Cut off log message if format could exceed log buffer length.
|
||||
if (usable_length - output_index < kMaxIntCharsNeeded) {
|
||||
output[output_index++] = '\0';
|
||||
return output_index;
|
||||
}
|
||||
output_index +=
|
||||
FormatInt32(&output[output_index], va_arg(args, int32_t));
|
||||
current++;
|
||||
break;
|
||||
case 'u':
|
||||
if (usable_length - output_index < kMaxIntCharsNeeded) {
|
||||
output[output_index++] = '\0';
|
||||
return output_index;
|
||||
}
|
||||
output_index +=
|
||||
FormatUInt32(&output[output_index], va_arg(args, uint32_t));
|
||||
current++;
|
||||
break;
|
||||
case 'x':
|
||||
if (usable_length - output_index < kMaxHexCharsNeeded) {
|
||||
output[output_index++] = '\0';
|
||||
return output_index;
|
||||
}
|
||||
output[output_index++] = '0';
|
||||
output[output_index++] = 'x';
|
||||
output_index +=
|
||||
FormatHex(&output[output_index], va_arg(args, uint32_t));
|
||||
current++;
|
||||
break;
|
||||
case 'f':
|
||||
if (usable_length - output_index < kMaxFloatCharsNeeded) {
|
||||
output[output_index++] = '\0';
|
||||
return output_index;
|
||||
}
|
||||
output_index +=
|
||||
FormatFloat(&output[output_index], va_arg(args, double));
|
||||
current++;
|
||||
break;
|
||||
case '%':
|
||||
output[output_index++] = *current++;
|
||||
break;
|
||||
case 's':
|
||||
char* string = va_arg(args, char*);
|
||||
int string_idx = 0;
|
||||
while (string_idx + output_index < usable_length &&
|
||||
string[string_idx] != '\0') {
|
||||
output[output_index++] = string[string_idx++];
|
||||
}
|
||||
current++;
|
||||
}
|
||||
} else {
|
||||
output[output_index++] = *current++;
|
||||
}
|
||||
}
|
||||
output[output_index++] = '\0';
|
||||
return output_index;
|
||||
}
|
||||
|
||||
extern "C" void DebugLogUInt32(uint32_t i) {
|
||||
char number_string[kFastToBufferSize];
|
||||
FastUInt32ToBufferLeft(i, number_string, 10);
|
||||
DebugLog(number_string);
|
||||
}
|
||||
|
||||
extern "C" void DebugLogHex(uint32_t i) {
|
||||
char number_string[kFastToBufferSize];
|
||||
FastUInt32ToBufferLeft(i, number_string, 16);
|
||||
DebugLog(number_string);
|
||||
}
|
||||
|
||||
extern "C" void DebugLogFloat(float i) {
|
||||
char number_string[kFastToBufferSize];
|
||||
FastFloatToBufferLeft(i, number_string);
|
||||
DebugLog(number_string);
|
||||
extern "C" int MicroSnprintf(char* output, int len, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int bytes_written = MicroVsnprintf(output, len, format, args);
|
||||
va_end(args);
|
||||
return bytes_written;
|
||||
}
|
36
tensorflow/lite/micro/micro_string.h
Normal file
36
tensorflow/lite/micro/micro_string.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* Copyright 2018 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_MICRO_STRING_H_
|
||||
#define TENSORFLOW_LITE_MICRO_MICRO_STRING_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/micro/micro_string.h"
|
||||
|
||||
// Implements simple string formatting for numeric types. Returns the number of
|
||||
// bytes written to output.
|
||||
extern "C" {
|
||||
// Functionally equivalent to vsnprintf, trimmed down for TFLite Micro.
|
||||
// MicroSnprintf() is implemented using MicroVsnprintf().
|
||||
int MicroVsnprintf(char* output, int len, const char* format, va_list args);
|
||||
// Functionally equavalent to snprintf, trimmed down for TFLite Micro.
|
||||
// For example, MicroSnprintf(buffer, 10, "int %d", 10) will put the string
|
||||
// "int 10" in the buffer.
|
||||
// Floating point values are logged in exponent notation (1.XXX*2^N).
|
||||
int MicroSnprintf(char* output, int len, const char* format, ...);
|
||||
}
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_MICRO_STRING_H_
|
132
tensorflow/lite/micro/micro_string_test.cc
Normal file
132
tensorflow/lite/micro/micro_string_test.cc
Normal file
@ -0,0 +1,132 @@
|
||||
/* Copyright 2020 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/micro_string.h"
|
||||
|
||||
#include "tensorflow/lite/micro/testing/micro_test.h"
|
||||
|
||||
TF_LITE_MICRO_TESTS_BEGIN
|
||||
|
||||
TF_LITE_MICRO_TEST(FormatPositiveIntShouldMatchExpected) {
|
||||
const int kBufferLen = 32;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "Int: 55";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "Int: %d", 55);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(FormatNegativeIntShouldMatchExpected) {
|
||||
const int kBufferLen = 32;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "Int: -55";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "Int: %d", -55);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(FormatUnsignedIntShouldMatchExpected) {
|
||||
const int kBufferLen = 32;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "UInt: 12345";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "UInt: %u", 12345);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(FormatHexShouldMatchExpected) {
|
||||
const int kBufferLen = 32;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "Hex: 0x12345";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "Hex: %x", 0x12345);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(FormatFloatShouldMatchExpected) {
|
||||
const int kBufferLen = 32;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "Float: 1.0*2^4";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "Float: %f", 16.f);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(BadlyFormattedStringShouldProduceReasonableString) {
|
||||
const int kBufferLen = 32;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "Test Badly % formated % string";
|
||||
int bytes_written =
|
||||
MicroSnprintf(buffer, kBufferLen, "Test Badly %% formated %% string%");
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(IntFormatOverrunShouldTruncate) {
|
||||
const int kBufferLen = 8;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "Int: ";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "Int: %d", 12345);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(UnsignedIntFormatOverrunShouldTruncate) {
|
||||
const int kBufferLen = 8;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "UInt: ";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "UInt: %u", 12345);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(HexFormatOverrunShouldTruncate) {
|
||||
const int kBufferLen = 8;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "Hex: ";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "Hex: %x", 0x12345);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(FloatFormatOverrunShouldTruncate) {
|
||||
const int kBufferLen = 12;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "Float: ";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "Float: %x", 12345.f);
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(StringFormatOverrunShouldTruncate) {
|
||||
const int kBufferLen = 10;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "String: h";
|
||||
int bytes_written =
|
||||
MicroSnprintf(buffer, kBufferLen, "String: %s", "hello world");
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TEST(StringFormatWithExactOutputSizeOverrunShouldTruncate) {
|
||||
const int kBufferLen = 10;
|
||||
char buffer[kBufferLen];
|
||||
const char golden[] = "format st";
|
||||
int bytes_written = MicroSnprintf(buffer, kBufferLen, "format str");
|
||||
TF_LITE_MICRO_EXPECT_EQ(sizeof(golden), bytes_written);
|
||||
TF_LITE_MICRO_EXPECT_STRING_EQ(golden, buffer);
|
||||
}
|
||||
|
||||
TF_LITE_MICRO_TESTS_END
|
@ -213,4 +213,15 @@ extern tflite::ErrorReporter* reporter;
|
||||
micro_test::did_test_fail = true; \
|
||||
} while (false)
|
||||
|
||||
#define TF_LITE_MICRO_EXPECT_STRING_EQ(string1, string2) \
|
||||
do { \
|
||||
for (int i = 0; string1[i] != '\0' && string2[i] != '\0'; i++) { \
|
||||
if (string1[i] != string2[i]) { \
|
||||
micro_test::reporter->Report("FAIL: %s did not match %s", string1, \
|
||||
string2, __FILE__, __LINE__); \
|
||||
micro_test::did_test_fail = true; \
|
||||
} \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_TESTING_MICRO_TEST_H_
|
||||
|
Loading…
Reference in New Issue
Block a user