Skip to content

Commit f61aaee

Browse files
shoumikhinfacebook-github-bot
authored andcommitted
Fix message truncating logic to respect UTF8 encoding. (#14394)
Summary: . Reviewed By: swolchok, metascroy Differential Revision: D82681904
1 parent 654e722 commit f61aaee

File tree

4 files changed

+97
-17
lines changed

4 files changed

+97
-17
lines changed

runtime/platform/log.cpp

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

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

85119
// Maximum length of a log message.
86120
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;
121+
char buffer[kMaxLogMessageLength];
122+
123+
const auto write_count =
124+
vsnprintf(buffer, kMaxLogMessageLength, format, args);
125+
const size_t used_length = (write_count < 0)
126+
? 0
127+
: (write_count >= static_cast<int>(kMaxLogMessageLength)
128+
? kMaxLogMessageLength - 1
129+
: static_cast<size_t>(write_count));
130+
const auto valid_length = get_valid_utf8_prefix_length(buffer, used_length);
131+
buffer[valid_length] = '\0';
94132

95-
et_pal_log_level_t pal_level = (level < LogLevel::NumLevels)
133+
const auto pal_level = (level < LogLevel::NumLevels)
96134
? kLevelToPal[size_t(level)]
97135
: et_pal_log_level_t::kUnknown;
98136

99137
pal_emit_log_message(
100-
timestamp, pal_level, filename, function, line, buf, len);
138+
timestamp, pal_level, filename, function, line, buffer, valid_length);
101139

102140
#endif // ET_LOG_ENABLED
103141
}

runtime/platform/test/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ 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+
et_cxx_test(logging_test SOURCES logging_test.cpp stub_platform.cpp)
37+
target_compile_definitions(logging_test PRIVATE ET_MIN_LOG_LEVEL=Debug)
3738

3839
# TODO: Re-enable this test on OSS
3940
#

runtime/platform/test/logging_test.cpp

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,64 @@
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+
TEST_F(LoggingTest, Utf8Truncation) {
46+
PalSpy spy;
47+
InterceptWith iw(spy);
48+
49+
{
50+
std::string prefix(253, 'A');
51+
const char euro[] = "\xE2\x82\xAC";
52+
std::string suffix = "_SHOULD_BE_CUT";
53+
ET_LOG(Info, "%s%s%s", prefix.c_str(), euro, suffix.c_str());
54+
EXPECT_EQ(spy.last_log_message_args.message, prefix);
55+
EXPECT_EQ(spy.last_log_message_args.length, prefix.size());
56+
}
57+
{
58+
std::string prefix(252, 'B');
59+
const char thumbs_up[] = "\xF0\x9F\x91\x8D";
60+
std::string suffix = "_SHOULD_BE_CUT";
61+
ET_LOG(Info, "%s%s%s", prefix.c_str(), thumbs_up, suffix.c_str());
62+
EXPECT_EQ(spy.last_log_message_args.message, prefix);
63+
EXPECT_EQ(spy.last_log_message_args.length, prefix.size());
64+
}
65+
{
66+
std::string prefix(254, 'C');
67+
const char e_acute[] = "\xC3\xA9";
68+
std::string suffix = "_SHOULD_BE_CUT";
69+
ET_LOG(Info, "%s%s%s", prefix.c_str(), e_acute, suffix.c_str());
70+
EXPECT_EQ(spy.last_log_message_args.message, prefix);
71+
EXPECT_EQ(spy.last_log_message_args.length, prefix.size());
72+
}
3373
}

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)