Skip to content

Commit 2640a86

Browse files
authored
Fix message truncating logic to respect UTF8 encoding. (#14394)
1 parent c00612f commit 2640a86

File tree

4 files changed

+115
-17
lines changed

4 files changed

+115
-17
lines changed

runtime/platform/log.cpp

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,38 @@ static_assert(
5959
kLevelToPal[size_t(LogLevel::Fatal)] == et_pal_log_level_t::kFatal,
6060
"");
6161

62+
#if ET_LOG_ENABLED
63+
static size_t get_valid_utf8_prefix_length(const char* bytes, size_t length) {
64+
if (!bytes || length == 0) {
65+
return 0;
66+
}
67+
const auto* data = reinterpret_cast<const unsigned char*>(bytes);
68+
size_t i = length;
69+
while (i > 0 && (data[i - 1] & 0xC0) == 0x80) {
70+
--i;
71+
}
72+
if (i == 0) {
73+
return 0;
74+
}
75+
const size_t lead_pos = i - 1;
76+
const unsigned char lead = data[lead_pos];
77+
size_t need = 0;
78+
79+
if (lead < 0x80) {
80+
need = 1;
81+
} else if ((lead & 0xE0) == 0xC0) {
82+
need = 2;
83+
} else if ((lead & 0xF0) == 0xE0) {
84+
need = 3;
85+
} else if ((lead & 0xF8) == 0xF0) {
86+
need = 4;
87+
} else {
88+
return lead_pos;
89+
}
90+
return length - lead_pos == need ? length : lead_pos;
91+
}
92+
#endif // ET_LOG_ENABLED
93+
6294
/**
6395
* Log a string message.
6496
*
@@ -84,20 +116,24 @@ void vlogf(
84116

85117
// Maximum length of a log message.
86118
static constexpr size_t kMaxLogMessageLength = 256;
87-
char buf[kMaxLogMessageLength];
88-
size_t len = vsnprintf(buf, kMaxLogMessageLength, format, args);
89-
if (len >= kMaxLogMessageLength - 1) {
90-
buf[kMaxLogMessageLength - 2] = '$';
91-
len = kMaxLogMessageLength - 1;
92-
}
93-
buf[kMaxLogMessageLength - 1] = 0;
119+
char buffer[kMaxLogMessageLength];
120+
121+
const auto write_count =
122+
vsnprintf(buffer, kMaxLogMessageLength, format, args);
123+
const size_t used_length = (write_count < 0)
124+
? 0
125+
: (write_count >= static_cast<int>(kMaxLogMessageLength)
126+
? kMaxLogMessageLength - 1
127+
: static_cast<size_t>(write_count));
128+
const auto valid_length = get_valid_utf8_prefix_length(buffer, used_length);
129+
buffer[valid_length] = '\0';
94130

95-
et_pal_log_level_t pal_level = (level < LogLevel::NumLevels)
131+
const auto pal_level = (level < LogLevel::NumLevels)
96132
? kLevelToPal[size_t(level)]
97133
: et_pal_log_level_t::kUnknown;
98134

99135
pal_emit_log_message(
100-
timestamp, pal_level, filename, function, line, buf, len);
136+
timestamp, pal_level, filename, function, line, buffer, valid_length);
101137

102138
#endif // ET_LOG_ENABLED
103139
}

runtime/platform/test/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ et_cxx_test(
3333
#
3434
# et_cxx_test(platform_death_test SOURCES executor_pal_death_test.cpp)
3535

36-
et_cxx_test(logging_test SOURCES logging_test.cpp)
36+
# No weak function symbols Windows/MSVC, thus PAL intercept is not supported.
37+
if(NOT WIN32)
38+
et_cxx_test(logging_test SOURCES logging_test.cpp stub_platform.cpp)
39+
set_source_files_properties(
40+
logging_test.cpp PROPERTIES COMPILE_DEFINITIONS "ET_MIN_LOG_LEVEL=Debug"
41+
)
42+
endif()
3743

3844
# TODO: Re-enable this test on OSS
3945
#

runtime/platform/test/logging_test.cpp

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,79 @@
1010

1111
#include <executorch/runtime/platform/log.h>
1212
#include <executorch/runtime/platform/runtime.h>
13+
#include <executorch/runtime/platform/test/pal_spy.h>
14+
#include <executorch/runtime/platform/test/stub_platform.h>
1315

1416
using namespace executorch::runtime;
1517

16-
class LoggingTest : public ::testing::Test {
17-
public:
18-
static void SetUpTestSuite() {
19-
// Initialize runtime.
20-
runtime_init();
21-
}
22-
};
18+
class LoggingTest : public ::testing::Test {};
2319

2420
TEST_F(LoggingTest, LogLevels) {
21+
PalSpy spy;
22+
InterceptWith iw(spy);
23+
2524
ET_LOG(Debug, "Debug log.");
25+
EXPECT_EQ(spy.last_log_message_args.message, "Debug log.");
26+
2627
ET_LOG(Info, "Info log.");
28+
EXPECT_EQ(spy.last_log_message_args.message, "Info log.");
29+
2730
ET_LOG(Error, "Error log.");
31+
EXPECT_EQ(spy.last_log_message_args.message, "Error log.");
32+
2833
ET_LOG(Fatal, "Fatal log.");
34+
EXPECT_EQ(spy.last_log_message_args.message, "Fatal log.");
2935
}
3036

3137
TEST_F(LoggingTest, LogFormatting) {
38+
PalSpy spy;
39+
InterceptWith iw(spy);
40+
3241
ET_LOG(Info, "Sample log with integer: %u", 100);
42+
EXPECT_EQ(spy.last_log_message_args.message, "Sample log with integer: 100");
43+
}
44+
45+
static std::string get_prefix(std::size_t length, bool use_multibyte) {
46+
if (!use_multibyte) {
47+
return std::string(length, 'A');
48+
}
49+
std::ostringstream result;
50+
result << std::string(length % 4, 'A');
51+
std::size_t remaining = length - (length % 4);
52+
while (remaining > 0) {
53+
result << "\xF0\x9F\x91\x8D";
54+
remaining -= 4;
55+
}
56+
return result.str();
57+
}
58+
59+
TEST_F(LoggingTest, Utf8Truncation) {
60+
PalSpy spy;
61+
InterceptWith iw(spy);
62+
63+
const char euro[] = "\xE2\x82\xAC";
64+
const char thumbs_up[] = "\xF0\x9F\x91\x8D";
65+
const char e_acute[] = "\xC3\xA9";
66+
const char capital_a_tilde[] = "\xC3\x83";
67+
68+
struct TruncCase {
69+
size_t prefix_length;
70+
const char* codepoint;
71+
};
72+
const TruncCase cases[] = {
73+
{253, euro},
74+
{252, thumbs_up},
75+
{254, e_acute},
76+
{254, capital_a_tilde},
77+
};
78+
for (bool use_multibyte_prefix : {false, true}) {
79+
for (const auto& c : cases) {
80+
const std::string prefix =
81+
get_prefix(c.prefix_length, use_multibyte_prefix);
82+
const std::string suffix = "_SHOULD_BE_CUT";
83+
ET_LOG(Info, "%s%s%s", prefix.c_str(), c.codepoint, suffix.c_str());
84+
EXPECT_EQ(spy.last_log_message_args.message, prefix);
85+
EXPECT_EQ(spy.last_log_message_args.length, prefix.size());
86+
}
87+
}
3388
}

runtime/platform/test/targets.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def define_common_targets():
8484
"logging_test.cpp",
8585
],
8686
deps = [
87+
":stub_platform",
8788
"//executorch/runtime/platform:platform",
8889
],
8990
compiler_flags = [

0 commit comments

Comments
 (0)