Skip to content

Commit 02da069

Browse files
authored
Log EValue tag names instead of numeric values
Differential Revision: D67888756 Pull Request resolved: #7538
1 parent 39aa22d commit 02da069

File tree

7 files changed

+258
-26
lines changed

7 files changed

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

runtime/core/tag.h

Lines changed: 23 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,27 @@ 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+
*/
60+
constexpr size_t kTagNameBufferSize = 19;
61+
3962
} // namespace runtime
4063
} // namespace executorch
4164

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: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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::kTagNameBufferSize;
16+
using executorch::runtime::Tag;
17+
using executorch::runtime::tag_to_string;
18+
19+
// The behavior of tag_to_string depends on the value of ET_ENABLE_ENUM_STRINGS.
20+
// If it is not set, tag_to_string will return a string representation of the
21+
// enum index value. As this behavior is compile-time gated, tests must also
22+
// be compile-time gated.
23+
#if ET_ENABLE_ENUM_STRINGS
24+
TEST(TagToString, TagValues) {
25+
std::array<char, 16> name;
26+
27+
tag_to_string(Tag::Tensor, name.data(), name.size());
28+
EXPECT_STREQ("Tensor", name.data());
29+
30+
tag_to_string(Tag::Int, name.data(), name.size());
31+
EXPECT_STREQ("Int", name.data());
32+
33+
tag_to_string(Tag::Double, name.data(), name.size());
34+
EXPECT_STREQ("Double", name.data());
35+
36+
tag_to_string(Tag::Bool, name.data(), name.size());
37+
EXPECT_STREQ("Bool", name.data());
38+
}
39+
40+
TEST(TagToString, TagNameBufferSize) {
41+
// Validate that kTagNameBufferSize is large enough to hold the all tag
42+
// strings without truncation.
43+
std::array<char, kTagNameBufferSize> name;
44+
45+
// Note that the return value of tag_to_string does not include the null
46+
// terminator.
47+
size_t longest = 0;
48+
49+
#define TEST_CASE(tag) \
50+
auto tag##_len = tag_to_string(Tag::tag, name.data(), name.size()); \
51+
EXPECT_LT(tag##_len, kTagNameBufferSize) \
52+
<< "kTagNameBufferSize is too small to hold " #tag; \
53+
longest = std::max(longest, tag##_len);
54+
55+
EXECUTORCH_FORALL_TAGS(TEST_CASE)
56+
#undef TEST_CASE
57+
58+
EXPECT_EQ(longest + 1, kTagNameBufferSize)
59+
<< "kTagNameBufferSize has incorrect value, expected " << longest + 1;
60+
}
61+
62+
TEST(TagToString, FitsExact) {
63+
std::array<char, 4> name;
64+
65+
auto ret = tag_to_string(Tag::Int, name.data(), name.size());
66+
67+
EXPECT_EQ(3, ret);
68+
EXPECT_STREQ("Int", name.data());
69+
}
70+
71+
TEST(TagToString, Truncate) {
72+
std::array<char, 6> name;
73+
std::fill(name.begin(), name.end(), '-');
74+
75+
auto ret = tag_to_string(Tag::Double, name.data(), name.size());
76+
EXPECT_EQ(6, ret);
77+
EXPECT_TRUE(name[name.size() - 1] == 0);
78+
EXPECT_STREQ("Doubl", name.data());
79+
}
80+
#endif // ET_ENABLE_ENUM_STRINGS

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: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <executorch/runtime/executor/method.h>
1010

11+
#include <array>
1112
#include <cinttypes> // @donotremove
1213
#include <cstdint>
1314
#include <cstdio>
@@ -823,26 +824,43 @@ Method::set_input(const EValue& input_evalue, size_t input_idx) {
823824
ET_CHECK_OR_RETURN_ERROR(
824825
input_idx < inputs_size(),
825826
InvalidArgument,
826-
"Given input index must be less than the number of inputs in method, but got %zu and %zu",
827+
"Input index (%zu) must be less than the number of inputs in method (%zu).",
827828
input_idx,
828829
inputs_size());
829830

830831
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));
837832

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,
843-
input_idx,
844-
static_cast<uint32_t>(e.tag),
845-
static_cast<uint32_t>(input_evalue.tag));
833+
if (!e.isTensor() && !e.isScalar()) {
834+
#if ET_LOG_ENABLED
835+
std::array<char, kTagNameBufferSize> tag_name;
836+
tag_to_string(e.tag, tag_name.data(), tag_name.size());
837+
ET_LOG(
838+
Error,
839+
"Input %zu was expected to be a Tensor or primitive but was %s.",
840+
input_idx,
841+
tag_name.data());
842+
#endif
843+
844+
return Error::InvalidArgument;
845+
}
846+
847+
if (e.tag != input_evalue.tag) {
848+
#if ET_LOG_ENABLED
849+
std::array<char, kTagNameBufferSize> e_tag_name;
850+
std::array<char, kTagNameBufferSize> input_tag_name;
851+
tag_to_string(e.tag, e_tag_name.data(), e_tag_name.size());
852+
tag_to_string(
853+
input_evalue.tag, input_tag_name.data(), input_tag_name.size());
854+
ET_LOG(
855+
Error,
856+
"Input %zu was expected to have type %s but was %s.",
857+
input_idx,
858+
e_tag_name.data(),
859+
input_tag_name.data());
860+
#endif
861+
862+
return Error::InvalidArgument;
863+
}
846864

847865
if (e.isTensor()) {
848866
const auto& t_dst = e.toTensor();
@@ -932,7 +950,12 @@ Method::set_input(const EValue& input_evalue, size_t input_idx) {
932950
e.toString().data(),
933951
input_evalue.toString().data());
934952
} else {
935-
ET_LOG(Error, "Unsupported input type: %d", (int32_t)e.tag);
953+
#if ET_LOG_ENABLED
954+
std::array<char, kTagNameBufferSize> tag_name;
955+
tag_to_string(e.tag, tag_name.data(), tag_name.size());
956+
ET_LOG(Error, "Unsupported input type: %s", tag_name.data());
957+
#endif
958+
936959
return Error::InvalidArgument;
937960
}
938961
return Error::Ok;
@@ -984,11 +1007,15 @@ Method::set_output_data_ptr(void* buffer, size_t size, size_t output_idx) {
9841007
outputs_size());
9851008

9861009
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);
1010+
if (!output.isTensor()) {
1011+
#if ET_LOG_ENABLED
1012+
std::array<char, kTagNameBufferSize> tag_name;
1013+
tag_to_string(output.tag, tag_name.data(), tag_name.size());
1014+
ET_LOG(Error, "Output type: %s is not a tensor.", tag_name.data());
1015+
#endif
1016+
1017+
return Error::InvalidArgument;
1018+
}
9921019

9931020
auto tensor_meta = this->method_meta().output_tensor_meta(output_idx);
9941021
if (tensor_meta->is_memory_planned()) {
@@ -1001,11 +1028,16 @@ Method::set_output_data_ptr(void* buffer, size_t size, size_t output_idx) {
10011028
}
10021029

10031030
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);
1031+
if (!output.isTensor()) {
1032+
#if ET_LOG_ENABLED
1033+
std::array<char, kTagNameBufferSize> tag_name;
1034+
tag_to_string(output.tag, tag_name.data(), tag_name.size());
1035+
ET_LOG(Error, "output type: %s is not a tensor.", tag_name.data());
1036+
#endif
1037+
1038+
return Error::InvalidArgument;
1039+
}
1040+
10091041
ET_CHECK_OR_RETURN_ERROR(
10101042
t.nbytes() <= size,
10111043
InvalidArgument,

0 commit comments

Comments
 (0)