1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "logging_splitters.h"
18
19 #include <string>
20 #include <vector>
21
22 #include <android-base/silent_death_test.h>
23 #include <android-base/strings.h>
24 #include <gtest/gtest.h>
25
26 using logging_splitters_DeathTest = SilentDeathTest;
27
28 namespace android {
29 namespace base {
30
TestNewlineSplitter(const std::string & input,const std::vector<std::string> & expected_output)31 void TestNewlineSplitter(const std::string& input,
32 const std::vector<std::string>& expected_output) {
33 std::vector<std::string> output;
34 auto logger_function = [&](const char* msg, int length) {
35 if (length == -1) {
36 output.push_back(msg);
37 } else {
38 output.push_back(std::string(msg, length));
39 }
40 };
41 SplitByLines(input.c_str(), logger_function);
42
43 EXPECT_EQ(expected_output, output);
44 }
45
TEST(logging_splitters,NewlineSplitter_EmptyString)46 TEST(logging_splitters, NewlineSplitter_EmptyString) {
47 TestNewlineSplitter("", std::vector<std::string>{""});
48 }
49
TEST(logging_splitters,NewlineSplitter_BasicString)50 TEST(logging_splitters, NewlineSplitter_BasicString) {
51 TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
52 }
53
TEST(logging_splitters,NewlineSplitter_ormalBasicStringTrailingNewline)54 TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
55 TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
56 }
57
TEST(logging_splitters,NewlineSplitter_MultilineTrailing)58 TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
59 TestNewlineSplitter("normal string\nsecond string\nthirdstring",
60 std::vector<std::string>{"normal string", "second string", "thirdstring"});
61 }
62
TEST(logging_splitters,NewlineSplitter_MultilineTrailingNewline)63 TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
64 TestNewlineSplitter(
65 "normal string\nsecond string\nthirdstring\n",
66 std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
67 }
68
TEST(logging_splitters,NewlineSplitter_MultilineEmbeddedNewlines)69 TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
70 TestNewlineSplitter(
71 "normal string\n\n\nsecond string\n\nthirdstring\n",
72 std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
73 }
74
TestLogdChunkSplitter(const std::string & tag,const std::string & file,const std::string & input,const std::vector<std::string> & expected_output)75 void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
76 const std::string& input,
77 const std::vector<std::string>& expected_output) {
78 std::vector<std::string> output;
79 auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
80 output.push_back(msg);
81 };
82
83 SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
84 input.c_str(), logger_function);
85
86 auto return_lengths = [&] {
87 std::string sizes;
88 sizes += "expected_output sizes:";
89 for (const auto& string : expected_output) {
90 sizes += " " + std::to_string(string.size());
91 }
92 sizes += "\noutput sizes:";
93 for (const auto& string : output) {
94 sizes += " " + std::to_string(string.size());
95 }
96 return sizes;
97 };
98
99 EXPECT_EQ(expected_output, output) << return_lengths();
100 }
101
TEST(logging_splitters,LogdChunkSplitter_EmptyString)102 TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
103 TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
104 }
105
TEST(logging_splitters,LogdChunkSplitter_BasicString)106 TEST(logging_splitters, LogdChunkSplitter_BasicString) {
107 TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
108 }
109
TEST(logging_splitters,LogdChunkSplitter_NormalBasicStringTrailingNewline)110 TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
111 TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
112 }
113
TEST(logging_splitters,LogdChunkSplitter_MultilineTrailing)114 TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
115 TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
116 std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
117 }
118
TEST(logging_splitters,LogdChunkSplitter_MultilineTrailingNewline)119 TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
120 TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
121 std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
122 }
123
TEST(logging_splitters,LogdChunkSplitter_MultilineEmbeddedNewlines)124 TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
125 TestLogdChunkSplitter(
126 "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
127 std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
128 }
129
130 // This test should return the same string, the logd logger itself will truncate down to size.
131 // This has historically been the behavior both in libbase and liblog.
TEST(logging_splitters,LogdChunkSplitter_HugeLineNoNewline)132 TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
133 auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
134 ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
135
136 TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
137 }
138
ReduceToMaxSize(const std::string & tag,const std::string & string)139 std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
140 return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
141 }
142
TEST(logging_splitters,LogdChunkSplitter_MultipleHugeLineNoNewline)143 TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
144 auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
145 auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
146 auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
147
148 auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
149
150 std::string tag = "tag";
151 std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
152 long_string_z};
153
154 TestLogdChunkSplitter(tag, "", long_strings, expected);
155 }
156
157 // With a ~4k buffer, we should print 2 long strings per logger call.
TEST(logging_splitters,LogdChunkSplitter_Multiple2kLines)158 TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
159 std::vector expected = {
160 std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
161 std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
162 std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
163 };
164
165 auto long_strings = Join(expected, '\n');
166
167 TestLogdChunkSplitter("tag", "", long_strings, expected);
168 }
169
TEST(logging_splitters,LogdChunkSplitter_ExactSizedLines)170 TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
171 const char* tag = "tag";
172 ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
173 auto long_string_a = std::string(max_size, 'a');
174 auto long_string_b = std::string(max_size, 'b');
175 auto long_string_c = std::string(max_size, 'c');
176
177 auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
178
179 TestLogdChunkSplitter(tag, "", long_strings,
180 std::vector{long_string_a, long_string_b, long_string_c});
181 }
182
TEST(logging_splitters,LogdChunkSplitter_UnderEqualOver)183 TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
184 std::string tag = "tag";
185 ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
186
187 auto first_string_size = 1000;
188 auto first_string = std::string(first_string_size, 'a');
189 auto second_string_size = max_size - first_string_size - 1;
190 auto second_string = std::string(second_string_size, 'b');
191
192 auto exact_string = std::string(max_size, 'c');
193
194 auto large_string = std::string(max_size + 50, 'd');
195
196 auto final_string = std::string("final string!\n\nfinal \n \n final \n");
197
198 std::vector expected = {first_string + '\n' + second_string, exact_string,
199 ReduceToMaxSize(tag, large_string), final_string};
200
201 std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
202 final_string};
203 auto long_strings = Join(input_strings, '\n');
204
205 TestLogdChunkSplitter(tag, "", long_strings, expected);
206 }
207
TEST(logging_splitters,LogdChunkSplitter_WithFile)208 TEST(logging_splitters, LogdChunkSplitter_WithFile) {
209 std::string tag = "tag";
210 std::string file = "/path/to/myfile.cpp";
211 int line = 1000;
212 auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
213 ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
214
215 auto first_string_size = 1000;
216 auto first_string = std::string(first_string_size, 'a');
217 auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
218 auto second_string = std::string(second_string_size, 'b');
219
220 auto exact_string = std::string(max_size - file_header.size(), 'c');
221
222 auto large_string = std::string(max_size + 50, 'd');
223
224 auto final_string = std::string("final string!");
225
226 std::vector expected = {
227 file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
228 file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
229
230 std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
231 final_string};
232 auto long_strings = Join(input_strings, '\n');
233
234 TestLogdChunkSplitter(tag, file, long_strings, expected);
235 }
236
237 // We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
238 // We could recover from this, but it's certainly an error for someone to attempt to use a tag this
239 // large, so we abort instead.
TEST_F(logging_splitters_DeathTest,LogdChunkSplitter_TooLongTag)240 TEST_F(logging_splitters_DeathTest, LogdChunkSplitter_TooLongTag) {
241 auto long_tag = std::string(5000, 'x');
242 auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
243 ASSERT_DEATH(
244 SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
245 }
246
247 // We do handle excessively large file names correctly however.
TEST(logging_splitters,LogdChunkSplitter_TooLongFile)248 TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
249 auto long_file = std::string(5000, 'x');
250 std::string tag = "tag";
251
252 std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
253
254 TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
255 }
256
TestStderrOutputGenerator(const char * tag,const char * file,int line,const char * message,const std::string & expected)257 void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
258 const std::string& expected) {
259 // All log messages will show "01-01 00:00:00.000"...
260 struct timespec ts = {.tv_sec = 0, .tv_nsec = 0};
261 // ...provided we're in the right time zone.
262 putenv(const_cast<char*>("TZ=UTC"));
263 tzset();
264
265 int pid = 1234; // All log messages will have 1234 for their PID.
266 uint64_t tid = 4321; // All log messages will have 4321 for their TID.
267
268 auto result = StderrOutputGenerator(ts, pid, tid, ERROR, tag, file, line, message);
269 EXPECT_EQ(expected, result);
270 }
271
TEST(logging_splitters,StderrOutputGenerator_Basic)272 TEST(logging_splitters, StderrOutputGenerator_Basic) {
273 TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
274 "01-01 00:00:00.000 1234 4321 E nullptr : simple message\n");
275 TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
276 "01-01 00:00:00.000 1234 4321 E tag : simple message\n");
277 TestStderrOutputGenerator(
278 "tag", "/path/to/some/file", 0, "simple message",
279 "01-01 00:00:00.000 1234 4321 E tag : /path/to/some/file:0 simple message\n");
280 }
281
TEST(logging_splitters,StderrOutputGenerator_NewlineTagAndFile)282 TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
283 TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
284 "01-01 00:00:00.000 1234 4321 E tag\n\n : simple message\n");
285 TestStderrOutputGenerator(
286 "tag", "/path/to/some/file\n\n", 0, "simple message",
287 "01-01 00:00:00.000 1234 4321 E tag : /path/to/some/file\n\n:0 simple message\n");
288 }
289
TEST(logging_splitters,StderrOutputGenerator_TrailingNewLine)290 TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
291 TestStderrOutputGenerator("tag", nullptr, 0, "simple message\n",
292 "01-01 00:00:00.000 1234 4321 E tag : simple message\n"
293 "01-01 00:00:00.000 1234 4321 E tag : \n");
294 }
295
TEST(logging_splitters,StderrOutputGenerator_MultiLine)296 TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
297 const char* expected_result =
298 "01-01 00:00:00.000 1234 4321 E tag : simple message\n"
299 "01-01 00:00:00.000 1234 4321 E tag : \n"
300 "01-01 00:00:00.000 1234 4321 E tag : \n"
301 "01-01 00:00:00.000 1234 4321 E tag : another message \n"
302 "01-01 00:00:00.000 1234 4321 E tag : \n"
303 "01-01 00:00:00.000 1234 4321 E tag : final message \n"
304 "01-01 00:00:00.000 1234 4321 E tag : \n"
305 "01-01 00:00:00.000 1234 4321 E tag : \n"
306 "01-01 00:00:00.000 1234 4321 E tag : \n";
307
308 TestStderrOutputGenerator("tag", nullptr, 0,
309 "simple message\n\n\nanother message \n\n final message \n\n\n",
310 expected_result);
311 }
312
TEST(logging_splitters,StderrOutputGenerator_MultiLineLong)313 TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
314 auto long_string_a = std::string(4000, 'a');
315 auto long_string_b = std::string(4000, 'b');
316
317 auto message = long_string_a + '\n' + long_string_b;
318 auto expected_result = "01-01 00:00:00.000 1234 4321 E tag : " + long_string_a + '\n' +
319 "01-01 00:00:00.000 1234 4321 E tag : " + long_string_b + '\n';
320 TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
321 }
322
323 } // namespace base
324 } // namespace android
325