Add tensor name to some of the error messages produced by tflite::Verify.

PiperOrigin-RevId: 233640378
This commit is contained in:
A. Unique TensorFlower 2019-02-12 11:06:25 -08:00 committed by TensorFlower Gardener
parent ba941c212d
commit 5f5088767d
2 changed files with 48 additions and 42 deletions

View File

@ -54,7 +54,7 @@ const uint32_t kMaxNumString = UINT_MAX / sizeof(int32_t) - 2;
// Verifies string tensor has legit buffer contents that follow the schema // Verifies string tensor has legit buffer contents that follow the schema
// defined in lite/string_util.h // defined in lite/string_util.h
bool VerifyStringTensorBuffer(const Buffer& buffer, bool VerifyStringTensorBuffer(const Tensor& tensor, const Buffer& buffer,
ErrorReporter* error_reporter) { ErrorReporter* error_reporter) {
uint32_t buffer_size = buffer.data()->size(); uint32_t buffer_size = buffer.data()->size();
const char* buffer_ptr = reinterpret_cast<const char*>(buffer.data()->data()); const char* buffer_ptr = reinterpret_cast<const char*>(buffer.data()->data());
@ -62,7 +62,8 @@ bool VerifyStringTensorBuffer(const Buffer& buffer,
uint32_t num_strings = *GetIntPtr(buffer_ptr); uint32_t num_strings = *GetIntPtr(buffer_ptr);
if (num_strings > kMaxNumString) { if (num_strings > kMaxNumString) {
ReportError(error_reporter, ReportError(error_reporter,
"String tensor has invalid num of string set: %d", num_strings); "String tensor %s has invalid num of string set: %d",
tensor.name()->c_str(), num_strings);
return false; return false;
} }
uint32_t header_offsets = uint32_t header_offsets =
@ -70,9 +71,9 @@ bool VerifyStringTensorBuffer(const Buffer& buffer,
if (buffer_size < header_offsets) { if (buffer_size < header_offsets) {
ReportError(error_reporter, ReportError(error_reporter,
"String tensor buffer requires at least %d bytes, but is " "String tensor %s buffer requires at least %d bytes, but is "
"allocated with %d bytes", "allocated with %d bytes",
header_offsets, buffer_size); tensor.name()->c_str(), header_offsets, buffer_size);
return false; return false;
} }
@ -81,22 +82,24 @@ bool VerifyStringTensorBuffer(const Buffer& buffer,
if (*GetIntPtr(buffer_ptr + offset) != header_offsets) { if (*GetIntPtr(buffer_ptr + offset) != header_offsets) {
ReportError(error_reporter, ReportError(error_reporter,
"String tensor buffer initial offset must be: %d", "String tensor %s buffer initial offset must be: %d",
header_offsets); tensor.name()->c_str(), header_offsets);
return false; return false;
} }
offset += sizeof(int32_t); offset += sizeof(int32_t);
for (int i = 1; i <= num_strings; i++, offset += sizeof(int32_t)) { for (int i = 1; i <= num_strings; i++, offset += sizeof(int32_t)) {
int string_offset = *GetIntPtr(buffer_ptr + offset); int string_offset = *GetIntPtr(buffer_ptr + offset);
if (string_offset < prev_ptr || string_offset > buffer_size) { if (string_offset < prev_ptr || string_offset > buffer_size) {
ReportError(error_reporter, "String tensor buffer is invalid: index %d", ReportError(error_reporter,
i); "String tensor %s buffer is invalid: index %d",
tensor.name()->c_str(), i);
return false; return false;
} }
} }
if (*GetIntPtr(buffer_ptr + offset - sizeof(int32_t)) != buffer_size) { if (*GetIntPtr(buffer_ptr + offset - sizeof(int32_t)) != buffer_size) {
ReportError(error_reporter, "String tensor buffer last offset must be %d", ReportError(error_reporter,
buffer_size); "String tensor %s buffer last offset must be %d",
tensor.name()->c_str(), buffer_size);
return false; return false;
} }
return true; return true;
@ -107,13 +110,15 @@ bool VerifyNumericTensorBuffer(const Tensor& tensor, const Buffer& buffer,
ErrorReporter* error_reporter) { ErrorReporter* error_reporter) {
uint64_t bytes_required = 1; uint64_t bytes_required = 1;
if (!tensor.shape()) { if (!tensor.shape()) {
ReportError(error_reporter, "Tensor shape is empty"); ReportError(error_reporter, "Tensor %s shape is empty",
tensor.name()->c_str());
return false; return false;
} }
for (int dim : *tensor.shape()) { for (int dim : *tensor.shape()) {
bytes_required *= dim; bytes_required *= dim;
if (bytes_required > UINT_MAX) { if (bytes_required > UINT_MAX) {
ReportError(error_reporter, "Tensor dimension overflow"); ReportError(error_reporter, "Tensor %s dimension overflow",
tensor.name()->c_str());
return false; return false;
} }
} }
@ -133,19 +138,21 @@ bool VerifyNumericTensorBuffer(const Tensor& tensor, const Buffer& buffer,
case TensorType_FLOAT16: case TensorType_FLOAT16:
// FALLTHROUGH_INTENDED; // FALLTHROUGH_INTENDED;
default: default:
ReportError(error_reporter, "Invalid tensor type: %d", tensor.type()); ReportError(error_reporter, "Tensor %s invalid type: %d",
tensor.name()->c_str(), tensor.type());
return false; return false;
} }
if (bytes_required > UINT_MAX) { if (bytes_required > UINT_MAX) {
ReportError(error_reporter, "Tensor dimension overflow"); ReportError(error_reporter, "Tensor %s dimension overflow",
tensor.name()->c_str());
return false; return false;
} }
if (bytes_required != buffer.data()->size()) { if (bytes_required != buffer.data()->size()) {
ReportError( ReportError(
error_reporter, error_reporter,
"Tensor requires %d bytes, but is allocated with %d bytes buffer", "Tensor %s requires %d bytes, but is allocated with %d bytes buffer",
bytes_required, buffer.data()->size()); tensor.name()->c_str(), bytes_required, buffer.data()->size());
return false; return false;
} }
return true; return true;
@ -299,14 +306,14 @@ bool VerifyTensors(const Model& model, ErrorReporter* error_reporter) {
continue; continue;
} }
if (tensor->buffer() >= model.buffers()->size()) { if (tensor->buffer() >= model.buffers()->size()) {
ReportError(error_reporter, "Invalid tensor buffer index: %d", ReportError(error_reporter, "Tensor %s invalid buffer index: %d",
tensor->buffer()); tensor->name(), tensor->buffer());
return false; return false;
} }
auto* buffer = model.buffers()->Get(tensor->buffer()); auto* buffer = model.buffers()->Get(tensor->buffer());
if (!buffer) { if (!buffer) {
ReportError(error_reporter, "Tensor buffer %d not set", ReportError(error_reporter, "Tensor %s buffer %d not set",
tensor->buffer()); tensor->name(), tensor->buffer());
return false; return false;
} }
@ -314,7 +321,7 @@ bool VerifyTensors(const Model& model, ErrorReporter* error_reporter) {
// buffers will be allocated by the interpreter at run-time. // buffers will be allocated by the interpreter at run-time.
if (buffer->data()) { if (buffer->data()) {
if (tensor->type() == TensorType_STRING) { if (tensor->type() == TensorType_STRING) {
if (!VerifyStringTensorBuffer(*buffer, error_reporter)) { if (!VerifyStringTensorBuffer(*tensor, *buffer, error_reporter)) {
return false; return false;
} }
} else { } else {

View File

@ -161,7 +161,7 @@ TEST(VerifyModel, TestEmptyShape) {
builder.FinishModel({0, 1}, {3}); builder.FinishModel({0, 1}, {3});
ASSERT_FALSE(builder.Verify()); ASSERT_FALSE(builder.Verify());
EXPECT_THAT(builder.GetErrorString(), EXPECT_THAT(builder.GetErrorString(),
::testing::ContainsRegex("Tensor shape is empty")); ::testing::ContainsRegex("Tensor inputtwo shape is empty"));
} }
TEST(VerifyModel, TestSimpleModel) { TEST(VerifyModel, TestSimpleModel) {
@ -221,10 +221,9 @@ TEST(VerifyModel, TestIntTensorShapeIsGreaterThanBuffer) {
builder.AddTensor({2, 3}, TensorType_UINT8, {1, 2, 3, 4}, "input"); builder.AddTensor({2, 3}, TensorType_UINT8, {1, 2, 3, 4}, "input");
builder.FinishModel({}, {}); builder.FinishModel({}, {});
ASSERT_FALSE(builder.Verify()); ASSERT_FALSE(builder.Verify());
EXPECT_THAT( EXPECT_THAT(builder.GetErrorString(),
builder.GetErrorString(), ::testing::ContainsRegex("Tensor input requires 6 bytes, but is "
::testing::ContainsRegex( "allocated with 4 bytes buffer"));
"Tensor requires 6 bytes, but is allocated with 4 bytes buffer"));
} }
TEST(VerifyModel, TestIntTensorShapeIsSmallerThanBuffer) { TEST(VerifyModel, TestIntTensorShapeIsSmallerThanBuffer) {
@ -232,10 +231,9 @@ TEST(VerifyModel, TestIntTensorShapeIsSmallerThanBuffer) {
builder.AddTensor({2, 1}, TensorType_UINT8, {1, 2, 3, 4}, "input"); builder.AddTensor({2, 1}, TensorType_UINT8, {1, 2, 3, 4}, "input");
builder.FinishModel({}, {}); builder.FinishModel({}, {});
ASSERT_FALSE(builder.Verify()); ASSERT_FALSE(builder.Verify());
EXPECT_THAT( EXPECT_THAT(builder.GetErrorString(),
builder.GetErrorString(), ::testing::ContainsRegex("Tensor input requires 2 bytes, but is "
::testing::ContainsRegex( "allocated with 4 bytes buffer"));
"Tensor requires 2 bytes, but is allocated with 4 bytes buffer"));
} }
TEST(VerifyModel, TestIntTensorShapeOverflow) { TEST(VerifyModel, TestIntTensorShapeOverflow) {
@ -245,7 +243,7 @@ TEST(VerifyModel, TestIntTensorShapeOverflow) {
builder.FinishModel({}, {}); builder.FinishModel({}, {});
ASSERT_FALSE(builder.Verify()); ASSERT_FALSE(builder.Verify());
EXPECT_THAT(builder.GetErrorString(), EXPECT_THAT(builder.GetErrorString(),
::testing::ContainsRegex("Tensor dimension overflow")); ::testing::ContainsRegex("Tensor input dimension overflow"));
} }
TEST(VerifyModel, TensorBufferIsNotValid) { TEST(VerifyModel, TensorBufferIsNotValid) {
@ -284,9 +282,10 @@ TEST(VerifyModel, StringTensorHasInvalidNumString) {
"input"); "input");
builder.FinishModel({}, {}); builder.FinishModel({}, {});
ASSERT_FALSE(builder.Verify()); ASSERT_FALSE(builder.Verify());
EXPECT_THAT(builder.GetErrorString(), EXPECT_THAT(
builder.GetErrorString(),
::testing::ContainsRegex( ::testing::ContainsRegex(
"String tensor buffer requires at least -2147483640 bytes, " "String tensor input buffer requires at least -2147483640 bytes, "
"but is allocated with 18 bytes")); "but is allocated with 18 bytes"));
} }
@ -299,7 +298,7 @@ TEST(VerifyModel, StringTensorOffsetTooSmall) {
ASSERT_FALSE(builder.Verify()); ASSERT_FALSE(builder.Verify());
EXPECT_THAT(builder.GetErrorString(), EXPECT_THAT(builder.GetErrorString(),
::testing::ContainsRegex( ::testing::ContainsRegex(
"String tensor buffer initial offset must be: 16")); "String tensor input buffer initial offset must be: 16"));
} }
TEST(VerifyModel, StringTensorOffsetOutOfRange) { TEST(VerifyModel, StringTensorOffsetOutOfRange) {
@ -309,9 +308,9 @@ TEST(VerifyModel, StringTensorOffsetOutOfRange) {
{2, 0, 0, 0, 16, 0, 0, 0, 17, 0, 0, 0, 22, 0, 0, 0, 'A', 'B'}, "input"); {2, 0, 0, 0, 16, 0, 0, 0, 17, 0, 0, 0, 22, 0, 0, 0, 'A', 'B'}, "input");
builder.FinishModel({}, {}); builder.FinishModel({}, {});
ASSERT_FALSE(builder.Verify()); ASSERT_FALSE(builder.Verify());
EXPECT_THAT( EXPECT_THAT(builder.GetErrorString(),
builder.GetErrorString(), ::testing::ContainsRegex(
::testing::ContainsRegex("String tensor buffer is invalid: index 2")); "String tensor input buffer is invalid: index 2"));
} }
TEST(VerifyModel, StringTensorIsLargerThanRequired) { TEST(VerifyModel, StringTensorIsLargerThanRequired) {
@ -322,9 +321,9 @@ TEST(VerifyModel, StringTensorIsLargerThanRequired) {
"input"); "input");
builder.FinishModel({}, {}); builder.FinishModel({}, {});
ASSERT_FALSE(builder.Verify()); ASSERT_FALSE(builder.Verify());
EXPECT_THAT( EXPECT_THAT(builder.GetErrorString(),
builder.GetErrorString(), ::testing::ContainsRegex(
::testing::ContainsRegex("String tensor buffer last offset must be 19")); "String tensor input buffer last offset must be 19"));
} }
TEST(VerifyModel, AllOpsAreSupported) { TEST(VerifyModel, AllOpsAreSupported) {