Skip to content

Commit 7e46386

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. ``` 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 fedb035 commit 7e46386

File tree

7 files changed

+248
-22
lines changed

7 files changed

+248
-22
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: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
#include <executorch/runtime/core/defines.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, "Tensor").
20+
* Otherwise, it will return a string representation of the index value ("1").
21+
*
22+
* If the user buffer is not large enough to hold the string representation, the string
23+
* will be truncated.
24+
*
25+
* The return value is the number of characters written, or in the case of truncation,
26+
* the number of characters that would be written if the buffer was large enough.
27+
*/
28+
size_t tag_to_string(Tag tag, char* buffer, size_t buffer_size) {
29+
#if ET_ENABLE_ENUM_STRINGS
30+
const char* name_str;
31+
#define DEFINE_CASE(name) \
32+
case Tag::name: \
33+
name_str = #name; \
34+
break;
35+
36+
switch (tag) {
37+
EXECUTORCH_FORALL_TAGS(DEFINE_CASE)
38+
default:
39+
name_str = "Unknown";
40+
break;
41+
}
42+
43+
return snprintf(buffer, buffer_size, "%s", name_str);
44+
#undef DEFINE_CASE
45+
#else
46+
return snprintf(buffer, buffer_size, "%d", static_cast<int>(tag));
47+
#endif // ET_ENABLE_ENUM_TO_STRING
48+
}
49+
50+
} // namespace runtime
51+
} // namespace executorch

runtime/core/tag.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#pragma once
1010

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

1315
namespace executorch {
1416
namespace runtime {
@@ -36,6 +38,24 @@ 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 bby default), this will return a string name (for example, "Tensor").
44+
* Otherwise, it will return a string representation of the index value ("1").
45+
*
46+
* If the user buffer is not large enough to hold the string representation, the string
47+
* will be truncated.
48+
*
49+
* The return value is the number of characters written, or in the case of truncation,
50+
* the number of characters that would be written if the buffer was large enough.
51+
*/
52+
size_t tag_to_string(Tag tag, char* buffer, size_t buffer_size);
53+
54+
// The size of the buffer needed to hold the longest tag string, including the null
55+
// terminator. This value is expected to be updated manually, but it checked in
56+
// test_tag.cpp.
57+
#define TAG_NAME_BUFFER_SIZE 19
58+
3959
} // namespace runtime
4060
} // namespace executorch
4161

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: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 <array>
12+
#include <gtest/gtest.h>
13+
14+
using namespace ::testing;
15+
using executorch::runtime::tag_to_string;
16+
using executorch::runtime::Tag;
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(SpanToString, 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(SpanToString, 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) << "TAG_NAME_BUFFER_SIZE is too small to hold " #tag; \
51+
longest = std::max(longest, tag##_len);
52+
53+
EXECUTORCH_FORALL_TAGS(TEST_CASE)
54+
#undef TEST_CASE
55+
56+
EXPECT_EQ(longest + 1, TAG_NAME_BUFFER_SIZE) << "TAG_NAME_BUFFER_SIZE has incorrect value, expected " << longest + 1;
57+
}
58+
59+
TEST(SpanToString, FitsExact) {
60+
std::array<char, 4> name;
61+
62+
auto ret = tag_to_string(Tag::Int, name.data(), name.size());
63+
64+
EXPECT_EQ(3, ret);
65+
EXPECT_STREQ("Int", name.data());
66+
}
67+
68+
TEST(SpanToString, Truncate) {
69+
std::array<char, 6> name;
70+
std::fill(name.begin(), name.end(), '-');
71+
72+
auto ret = tag_to_string(Tag::Double, name.data(), name.size());
73+
EXPECT_EQ(6, ret);
74+
EXPECT_TRUE(name[name.size() - 1] == 0);
75+
EXPECT_STREQ("Doubl", name.data());
76+
}
77+
#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: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -816,26 +816,41 @@ Method::set_input(const EValue& input_evalue, size_t input_idx) {
816816
ET_CHECK_OR_RETURN_ERROR(
817817
input_idx < inputs_size(),
818818
InvalidArgument,
819-
"Given input index must be less than the number of inputs in method, but got %zu and %zu",
819+
"Input index (%zu) must be less than the number of inputs in method (%zu).",
820820
input_idx,
821821
inputs_size());
822822

823823
const auto& e = get_value(get_input_index(input_idx));
824-
ET_CHECK_OR_RETURN_ERROR(
825-
e.isTensor() || e.isScalar(),
826-
InvalidArgument,
827-
"The %zu-th input in method is expected Tensor or prim, but received %" PRIu32,
828-
input_idx,
829-
static_cast<uint32_t>(e.tag));
824+
825+
if (!e.isTensor() && !e.isScalar()) {
826+
#if ET_LOG_ENABLED
827+
char tag_name[TAG_NAME_BUFFER_SIZE];
828+
tag_to_string(e.tag, tag_name, sizeof(tag_name));
829+
#endif
830830

831+
ET_CHECK_OR_RETURN_ERROR(
832+
e.isTensor() || e.isScalar(),
833+
InvalidArgument,
834+
"Input %zu was expected to be a Tensor or primitive but was %s.",
835+
input_idx,
836+
tag_name);
837+
}
838+
839+
if (e.tag != input_evalue.tag) {
840+
#if ET_LOG_ENABLED
841+
char e_tag_name[TAG_NAME_BUFFER_SIZE];
842+
char input_tag_name[TAG_NAME_BUFFER_SIZE];
843+
tag_to_string(e.tag, e_tag_name, sizeof(e_tag_name));
844+
tag_to_string(input_evalue.tag, input_tag_name, sizeof(input_tag_name));
845+
#endif
831846
ET_CHECK_OR_RETURN_ERROR(
832847
e.tag == input_evalue.tag,
833848
InvalidArgument,
834-
"The %zu-th input of method should have the same type as the input_evalue, but get tag %" PRIu32
835-
" and tag %" PRIu32,
849+
"Input %zu was expected to have type %s but was %s.",
836850
input_idx,
837-
static_cast<uint32_t>(e.tag),
838-
static_cast<uint32_t>(input_evalue.tag));
851+
e_tag_name,
852+
input_tag_name);
853+
}
839854

840855
if (e.isTensor()) {
841856
const auto& t_dst = e.toTensor();
@@ -925,7 +940,11 @@ Method::set_input(const EValue& input_evalue, size_t input_idx) {
925940
e.toString().data(),
926941
input_evalue.toString().data());
927942
} else {
928-
ET_LOG(Error, "Unsupported input type: %d", (int32_t)e.tag);
943+
char tag_name[16];
944+
#if ET_LOG_ENABLED
945+
tag_to_string(e.tag, tag_name, sizeof(tag_name));
946+
#endif
947+
ET_LOG(Error, "Unsupported input type: %s", tag_name);
929948
return Error::InvalidArgument;
930949
}
931950
return Error::Ok;
@@ -977,11 +996,18 @@ Method::set_output_data_ptr(void* buffer, size_t size, size_t output_idx) {
977996
outputs_size());
978997

979998
auto& output = mutable_value(get_output_index(output_idx));
980-
ET_CHECK_OR_RETURN_ERROR(
981-
output.isTensor(),
982-
InvalidArgument,
983-
"output type: %zu is not tensor",
984-
(size_t)output.tag);
999+
if (!output.isTensor()) {
1000+
#if ET_LOG_ENABLED
1001+
char tag_name[TAG_NAME_BUFFER_SIZE];
1002+
tag_to_string(output.tag, tag_name, sizeof(tag_name));
1003+
#endif
1004+
1005+
ET_CHECK_OR_RETURN_ERROR(
1006+
output.isTensor(),
1007+
InvalidArgument,
1008+
"output type: %s is not tensor",
1009+
tag_name);
1010+
}
9851011

9861012
auto tensor_meta = this->method_meta().output_tensor_meta(output_idx);
9871013
if (tensor_meta->is_memory_planned()) {
@@ -994,11 +1020,18 @@ Method::set_output_data_ptr(void* buffer, size_t size, size_t output_idx) {
9941020
}
9951021

9961022
auto& t = output.toTensor();
997-
ET_CHECK_OR_RETURN_ERROR(
998-
output.isTensor(),
999-
InvalidArgument,
1000-
"output type: %zu is not tensor",
1001-
(size_t)output.tag);
1023+
if (!output.isTensor()) {
1024+
#if ET_LOG_ENABLED
1025+
char tag_name[TAG_NAME_BUFFER_SIZE];
1026+
tag_to_string(output.tag, tag_name, sizeof(tag_name));
1027+
#endif
1028+
1029+
ET_CHECK_OR_RETURN_ERROR(
1030+
output.isTensor(),
1031+
InvalidArgument,
1032+
"output type: %s is not tensor",
1033+
tag_name);
1034+
}
10021035
ET_CHECK_OR_RETURN_ERROR(
10031036
t.nbytes() <= size,
10041037
InvalidArgument,

0 commit comments

Comments
 (0)