Skip to content

Commit 32632c3

Browse files
GregoryComerfacebook-github-bot
authored andcommitted
Log EValue tag names instead of numeric values (#7538)
Summary: Update error log messages that include EValue tags to use a string representation of the tag rather than the numerical index. This improves readability for users. Example Old Message: ``` [method.cpp:814] The 0-th input of method should have the same type as the input_evalue, but get tag 1 and tag 4 ``` Example New Message: ``` [method.cpp:813] Input 0 was expected to be Tensor but was Int. ``` Pull Request resolved: #7538 Test Plan: Locally built the executorch bento kernel and tested with an invalid EValue input to capture the example new message above. Repeated with "-c executorch.enable_enum_strings=false" to verify fallback behavior. Added tests for `tag_to_string` to tag_test.cpp, runnable via ``` buck test executorch/runtime/core/test:tag_test ``` Reviewed By: digantdesai, manuelcandales Differential Revision: D67888756 Pulled By: GregoryComer
1 parent 4655202 commit 32632c3

File tree

7 files changed

+258
-25
lines changed

7 files changed

+258
-25
lines changed

runtime/core/defines.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
/**
10+
* @file
11+
* Contains preprocessor definitions used by ExecuTorch core.
12+
*/
13+
14+
#pragma once
15+
16+
// Enable ET_ENABLE_ENUM_STRINGS by default. This option gates inclusion of
17+
// enum string names and can be disabled by explicitly setting it to 0.
18+
#ifndef ET_ENABLE_ENUM_STRINGS
19+
#define ET_ENABLE_ENUM_STRINGS 1
20+
#endif

runtime/core/tag.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <executorch/runtime/core/defines.h>
10+
#include <executorch/runtime/core/tag.h>
11+
12+
#include <cstdio>
13+
14+
namespace executorch {
15+
namespace runtime {
16+
17+
/**
18+
* Convert a tag value to a string representation. If ET_ENABLE_ENUM_STRINGS is
19+
* set (it is on by default), this will return a string name (for example,
20+
* "Tensor"). Otherwise, it will return a string representation of the index
21+
* value ("1").
22+
*
23+
* If the user buffer is not large enough to hold the string representation, the
24+
* string will be truncated.
25+
*
26+
* The return value is the number of characters written, or in the case of
27+
* truncation, the number of characters that would be written if the buffer was
28+
* large enough.
29+
*/
30+
size_t tag_to_string(Tag tag, char* buffer, size_t buffer_size) {
31+
#if ET_ENABLE_ENUM_STRINGS
32+
const char* name_str;
33+
#define DEFINE_CASE(name) \
34+
case Tag::name: \
35+
name_str = #name; \
36+
break;
37+
38+
switch (tag) {
39+
EXECUTORCH_FORALL_TAGS(DEFINE_CASE)
40+
default:
41+
name_str = "Unknown";
42+
break;
43+
}
44+
45+
return snprintf(buffer, buffer_size, "%s", name_str);
46+
#undef DEFINE_CASE
47+
#else
48+
return snprintf(buffer, buffer_size, "%d", static_cast<int>(tag));
49+
#endif // ET_ENABLE_ENUM_TO_STRING
50+
}
51+
52+
} // namespace runtime
53+
} // namespace executorch

runtime/core/tag.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#pragma once
1010

11+
#include <executorch/runtime/core/defines.h>
12+
#include <executorch/runtime/platform/compiler.h>
1113
#include <cstdint>
1214

1315
namespace executorch {
@@ -36,6 +38,26 @@ enum class Tag : uint32_t {
3638
#undef DEFINE_TAG
3739
};
3840

41+
/**
42+
* Convert a tag value to a string representation. If ET_ENABLE_ENUM_STRINGS is
43+
* set (it is on by default), this will return a string name (for example,
44+
* "Tensor"). Otherwise, it will return a string representation of the index
45+
* value ("1").
46+
*
47+
* If the user buffer is not large enough to hold the string representation, the
48+
* string will be truncated.
49+
*
50+
* The return value is the number of characters written, or in the case of
51+
* truncation, the number of characters that would be written if the buffer was
52+
* large enough.
53+
*/
54+
size_t tag_to_string(Tag tag, char* buffer, size_t buffer_size);
55+
56+
// The size of the buffer needed to hold the longest tag string, including the
57+
// null terminator. This value is expected to be updated manually, but it
58+
// checked in test_tag.cpp.
59+
#define TAG_NAME_BUFFER_SIZE 19
60+
3961
} // namespace runtime
4062
} // namespace executorch
4163

runtime/core/targets.bzl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ def get_sdk_flags():
1818
sdk_flags += ["-DEXECUTORCH_BUILD_DEVTOOLS"]
1919
return sdk_flags
2020

21+
def enable_enum_strings():
22+
return native.read_config("executorch", "enable_enum_strings", "true") == "true"
23+
24+
def get_core_flags():
25+
core_flags = []
26+
core_flags += ["-DET_ENABLE_ENUM_STRINGS=" + ("1" if enable_enum_strings() else "0")]
27+
return core_flags
28+
2129
def define_common_targets():
2230
"""Defines targets that should be shared between fbcode and xplat.
2331
@@ -30,6 +38,7 @@ def define_common_targets():
3038
exported_headers = [
3139
"array_ref.h", # TODO(T157717874): Migrate all users to span and then move this to portable_type
3240
"data_loader.h",
41+
"defines.h",
3342
"error.h",
3443
"freeable_buffer.h",
3544
"result.h",
@@ -39,6 +48,7 @@ def define_common_targets():
3948
"//executorch/...",
4049
"@EXECUTORCH_CLIENTS",
4150
],
51+
exported_preprocessor_flags = get_core_flags(),
4252
exported_deps = [
4353
"//executorch/runtime/platform:platform",
4454
],
@@ -109,9 +119,14 @@ def define_common_targets():
109119

110120
runtime.cxx_library(
111121
name = "tag",
122+
srcs = ["tag.cpp"],
112123
exported_headers = [
113124
"tag.h",
114125
],
126+
exported_deps = [
127+
":core",
128+
"//executorch/runtime/platform:compiler",
129+
],
115130
visibility = [
116131
"//executorch/...",
117132
],

runtime/core/test/tag_test.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <executorch/runtime/core/tag.h>
10+
11+
#include <gtest/gtest.h>
12+
#include <array>
13+
14+
using namespace ::testing;
15+
using executorch::runtime::Tag;
16+
using executorch::runtime::tag_to_string;
17+
18+
// The behavior of tag_to_string depends on the value of ET_ENABLE_ENUM_STRINGS.
19+
// If it is not set, tag_to_string will return a string representation of the
20+
// enum index value. As this behavior is compile-time gated, tests must also
21+
// be compile-time gated.
22+
#if ET_ENABLE_ENUM_STRINGS
23+
TEST(TagToString, TagValues) {
24+
std::array<char, 16> name;
25+
26+
tag_to_string(Tag::Tensor, name.data(), name.size());
27+
EXPECT_STREQ("Tensor", name.data());
28+
29+
tag_to_string(Tag::Int, name.data(), name.size());
30+
EXPECT_STREQ("Int", name.data());
31+
32+
tag_to_string(Tag::Double, name.data(), name.size());
33+
EXPECT_STREQ("Double", name.data());
34+
35+
tag_to_string(Tag::Bool, name.data(), name.size());
36+
EXPECT_STREQ("Bool", name.data());
37+
}
38+
39+
TEST(TagToString, TagNameBufferSize) {
40+
// Validate that TAG_NAME_BUFFER_SIZE is large enough to hold the all tag
41+
// strings without truncation.
42+
std::array<char, TAG_NAME_BUFFER_SIZE> name;
43+
44+
// Note that the return value of tag_to_string does not include the null
45+
// terminator.
46+
size_t longest = 0;
47+
48+
#define TEST_CASE(tag) \
49+
auto tag##_len = tag_to_string(Tag::tag, name.data(), name.size()); \
50+
EXPECT_LT(tag##_len, TAG_NAME_BUFFER_SIZE) \
51+
<< "TAG_NAME_BUFFER_SIZE is too small to hold " #tag; \
52+
longest = std::max(longest, tag##_len);
53+
54+
EXECUTORCH_FORALL_TAGS(TEST_CASE)
55+
#undef TEST_CASE
56+
57+
EXPECT_EQ(longest + 1, TAG_NAME_BUFFER_SIZE)
58+
<< "TAG_NAME_BUFFER_SIZE has incorrect value, expected " << longest + 1;
59+
}
60+
61+
TEST(TagToString, FitsExact) {
62+
std::array<char, 4> name;
63+
64+
auto ret = tag_to_string(Tag::Int, name.data(), name.size());
65+
66+
EXPECT_EQ(3, ret);
67+
EXPECT_STREQ("Int", name.data());
68+
}
69+
70+
TEST(TagToString, Truncate) {
71+
std::array<char, 6> name;
72+
std::fill(name.begin(), name.end(), '-');
73+
74+
auto ret = tag_to_string(Tag::Double, name.data(), name.size());
75+
EXPECT_EQ(6, ret);
76+
EXPECT_TRUE(name[name.size() - 1] == 0);
77+
EXPECT_STREQ("Doubl", name.data());
78+
}
79+
#endif

runtime/core/test/targets.bzl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ def define_common_targets():
7373
],
7474
)
7575

76+
runtime.cxx_test(
77+
name = "tag_test",
78+
srcs = [
79+
"tag_test.cpp",
80+
],
81+
deps = [
82+
"//executorch/runtime/core:tag",
83+
],
84+
)
85+
7686
runtime.cxx_test(
7787
name = "tensor_shape_dynamism_test_aten",
7888
srcs = ["tensor_shape_dynamism_test_aten.cpp"],

runtime/executor/method.cpp

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -823,26 +823,41 @@ Method::set_input(const EValue& input_evalue, size_t input_idx) {
823823
ET_CHECK_OR_RETURN_ERROR(
824824
input_idx < inputs_size(),
825825
InvalidArgument,
826-
"Given input index must be less than the number of inputs in method, but got %zu and %zu",
826+
"Input index (%zu) must be less than the number of inputs in method (%zu).",
827827
input_idx,
828828
inputs_size());
829829

830830
const auto& e = get_value(get_input_index(input_idx));
831-
ET_CHECK_OR_RETURN_ERROR(
832-
e.isTensor() || e.isScalar(),
833-
InvalidArgument,
834-
"The %zu-th input in method is expected Tensor or prim, but received %" PRIu32,
835-
input_idx,
836-
static_cast<uint32_t>(e.tag));
837831

838-
ET_CHECK_OR_RETURN_ERROR(
839-
e.tag == input_evalue.tag,
840-
InvalidArgument,
841-
"The %zu-th input of method should have the same type as the input_evalue, but get tag %" PRIu32
842-
" and tag %" PRIu32,
832+
if (!e.isTensor() && !e.isScalar()) {
833+
#if ET_LOG_ENABLED
834+
char tag_name[TAG_NAME_BUFFER_SIZE];
835+
tag_to_string(e.tag, tag_name, sizeof(tag_name));
836+
#endif
837+
838+
ET_LOG(
839+
Error,
840+
"Input %zu was expected to be a Tensor or primitive but was %s.",
843841
input_idx,
844-
static_cast<uint32_t>(e.tag),
845-
static_cast<uint32_t>(input_evalue.tag));
842+
tag_name);
843+
return Error::InvalidArgument;
844+
}
845+
846+
if (e.tag != input_evalue.tag) {
847+
#if ET_LOG_ENABLED
848+
char e_tag_name[TAG_NAME_BUFFER_SIZE];
849+
char input_tag_name[TAG_NAME_BUFFER_SIZE];
850+
tag_to_string(e.tag, e_tag_name, sizeof(e_tag_name));
851+
tag_to_string(input_evalue.tag, input_tag_name, sizeof(input_tag_name));
852+
#endif
853+
ET_LOG(
854+
Error,
855+
"Input %zu was expected to have type %s but was %s.",
856+
input_idx,
857+
e_tag_name,
858+
input_tag_name);
859+
return Error::InvalidArgument;
860+
}
846861

847862
if (e.isTensor()) {
848863
const auto& t_dst = e.toTensor();
@@ -932,7 +947,11 @@ Method::set_input(const EValue& input_evalue, size_t input_idx) {
932947
e.toString().data(),
933948
input_evalue.toString().data());
934949
} else {
935-
ET_LOG(Error, "Unsupported input type: %d", (int32_t)e.tag);
950+
char tag_name[16];
951+
#if ET_LOG_ENABLED
952+
tag_to_string(e.tag, tag_name, sizeof(tag_name));
953+
#endif
954+
ET_LOG(Error, "Unsupported input type: %s", tag_name);
936955
return Error::InvalidArgument;
937956
}
938957
return Error::Ok;
@@ -984,11 +1003,18 @@ Method::set_output_data_ptr(void* buffer, size_t size, size_t output_idx) {
9841003
outputs_size());
9851004

9861005
auto& output = mutable_value(get_output_index(output_idx));
987-
ET_CHECK_OR_RETURN_ERROR(
988-
output.isTensor(),
989-
InvalidArgument,
990-
"output type: %zu is not tensor",
991-
(size_t)output.tag);
1006+
if (!output.isTensor()) {
1007+
#if ET_LOG_ENABLED
1008+
char tag_name[TAG_NAME_BUFFER_SIZE];
1009+
tag_to_string(output.tag, tag_name, sizeof(tag_name));
1010+
#endif
1011+
1012+
ET_LOG(
1013+
Error,
1014+
"Output type: %s is not a tensor.",
1015+
tag_name);
1016+
return Error::InvalidArgument;
1017+
}
9921018

9931019
auto tensor_meta = this->method_meta().output_tensor_meta(output_idx);
9941020
if (tensor_meta->is_memory_planned()) {
@@ -1001,11 +1027,19 @@ Method::set_output_data_ptr(void* buffer, size_t size, size_t output_idx) {
10011027
}
10021028

10031029
auto& t = output.toTensor();
1004-
ET_CHECK_OR_RETURN_ERROR(
1005-
output.isTensor(),
1006-
InvalidArgument,
1007-
"output type: %zu is not tensor",
1008-
(size_t)output.tag);
1030+
if (!output.isTensor()) {
1031+
#if ET_LOG_ENABLED
1032+
char tag_name[TAG_NAME_BUFFER_SIZE];
1033+
tag_to_string(output.tag, tag_name, sizeof(tag_name));
1034+
#endif
1035+
1036+
ET_LOG(
1037+
Error,
1038+
"output type: %s is not a tensor.",
1039+
tag_name);
1040+
return Error::InvalidArgument;
1041+
}
1042+
10091043
ET_CHECK_OR_RETURN_ERROR(
10101044
t.nbytes() <= size,
10111045
InvalidArgument,

0 commit comments

Comments
 (0)