1 /* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 /**
24 @file
25 Unit test of the Optimizer trace API (WL#5257)
26 */
27
28 // First include (the generated) my_config.h, to get correct platform defines.
29 #include "my_config.h"
30
31 #include <gtest/gtest.h>
32 #include <sys/types.h>
33
34 #include "my_inttypes.h"
35 #include "my_macros.h"
36
37 #include "m_string.h" // llstr
38 #include "mysys_err.h" // for testing of OOM
39 #include "sql/mysqld.h" // system_charset_info
40 #include "sql/opt_trace.h"
41 #ifdef HAVE_SYS_WAIT_H
42 #include <sys/wait.h> // for WEXITSTATUS
43 #endif
44
45 namespace opt_trace_unittest {
46
47 const ulonglong all_features = Opt_trace_context::default_features;
48
49 /**
50 @note It is a macro, for proper reporting of line numbers in case of
51 assertion failure. SCOPED_TRACE will report line number at the
52 macro expansion site.
53 */
54 #define check_json_compliance(str, length) \
55 { \
56 SCOPED_TRACE(""); \
57 do_check_json_compliance(str, length); \
58 }
59
60 /**
61 Checks compliance of a trace with JSON syntax rules.
62 This is a helper which has interest only when developing the test; once you
63 know that the produced trace is compliant and has expected content, just
64 set "expected" to it, add a comparison with "expected", and don't use this
65 function.
66 @param str pointer to trace
67 @param length trace's length
68 */
do_check_json_compliance(const char * str,size_t length)69 static void do_check_json_compliance(const char *str, size_t length) {
70 return;
71 /*
72 Read from stdin, eliminate comments, parse as JSON. If invalid, an
73 exception is thrown by Python, uncaught, which produces a non-zero error
74 code.
75 */
76 #ifndef _WIN32
77 const char python_cmd[] =
78 "python -c \""
79 "import json, re, sys;"
80 "s= sys.stdin.read();"
81 "s= re.sub('/\\\\*[ A-Za-z_]* \\\\*/', '', s);"
82 "json.loads(s, 'utf-8')\"";
83 // Send the trace to this new process' stdin:
84 FILE *fd = popen(python_cmd, "w");
85 ASSERT_TRUE(nullptr != fd);
86 ASSERT_NE(0U, length); // empty is not compliant
87 ASSERT_EQ(1U, fwrite(str, length, 1, fd));
88 int rc = pclose(fd);
89 rc = WEXITSTATUS(rc);
90 EXPECT_EQ(0, rc);
91 #endif
92 }
93
94 extern "C" void my_error_handler(uint error, const char *str, myf MyFlags);
95
96 class TraceContentTest : public ::testing::Test {
97 public:
98 Opt_trace_context trace;
99 static bool oom; ///< whether we got an OOM error from opt trace
100 protected:
SetUpTestCase()101 static void SetUpTestCase() {
102 system_charset_info = &my_charset_utf8_general_ci;
103 }
SetUp()104 virtual void SetUp() {
105 /* Save original and install our custom error hook. */
106 m_old_error_handler_hook = error_handler_hook;
107 error_handler_hook = my_error_handler;
108 oom = false;
109 // Setting debug flags triggers enter/exit trace, so redirect to /dev/null
110 DBUG_SET("o," IF_WIN("NUL", "/dev/null"));
111 }
TearDown()112 virtual void TearDown() { error_handler_hook = m_old_error_handler_hook; }
113
114 static void (*m_old_error_handler_hook)(uint, const char *, myf);
115 };
116 bool TraceContentTest::oom;
117 void (*TraceContentTest::m_old_error_handler_hook)(uint, const char *, myf);
118
my_error_handler(uint error,const char *,myf)119 void my_error_handler(uint error, const char *, myf) {
120 const uint EE = static_cast<uint>(EE_OUTOFMEMORY);
121 EXPECT_EQ(EE, error);
122 if (error == EE) TraceContentTest::oom = true;
123 }
124
TEST_F(TraceContentTest,ConstructAndDestruct)125 TEST_F(TraceContentTest, ConstructAndDestruct) {}
126
127 /** Test empty trace */
TEST_F(TraceContentTest,Empty)128 TEST_F(TraceContentTest, Empty) {
129 ASSERT_FALSE(
130 trace.start(true, false, false, false, -1, 1, ULONG_MAX, all_features));
131 EXPECT_TRUE(trace.is_started());
132 EXPECT_TRUE(trace.support_I_S());
133 /*
134 Add at least an object to it. A really empty trace ("") is not
135 JSON-compliant, at least Python's JSON module raises an exception.
136 */
137 { Opt_trace_object oto(&trace); }
138 /* End trace */
139 trace.end();
140 /* And verify trace's content */
141 Opt_trace_iterator it(&trace);
142 /*
143 ASSERT here, because a failing EXPECT_FALSE would continue into
144 it.get_value() and segfault.
145 */
146 ASSERT_FALSE(it.at_end());
147 Opt_trace_info info;
148 it.get_value(&info);
149 const char expected[] = "{\n}";
150 EXPECT_STREQ(expected, info.trace_ptr);
151 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
152 check_json_compliance(info.trace_ptr, info.trace_length);
153 EXPECT_EQ(0U, info.missing_bytes);
154 EXPECT_FALSE(info.missing_priv);
155 EXPECT_FALSE(oom);
156 /* Should be no more traces */
157 it.next();
158 ASSERT_TRUE(it.at_end());
159 }
160
161 /** Test normal usage */
TEST_F(TraceContentTest,NormalUsage)162 TEST_F(TraceContentTest, NormalUsage) {
163 ASSERT_FALSE(
164 trace.start(true, false, true, false, -1, 1, ULONG_MAX, all_features));
165 {
166 Opt_trace_object oto(&trace);
167 oto.add_select_number(123456);
168 {
169 Opt_trace_array ota(&trace, "one array");
170 ota.add(200.4);
171 {
172 Opt_trace_object oto1(&trace);
173 oto1.add_alnum("one key", "one value").add("another key", 100U);
174 }
175 ota.add_alnum("one string element");
176 ota.add(true);
177 ota.add_hex(12318421343459ULL);
178 }
179 oto.add("yet another key", -1000LL);
180 {
181 Opt_trace_array ota(&trace, "another array");
182 ota.add(1LL).add(2).add(3LL).add(4LL);
183 }
184 }
185 trace.end();
186 Opt_trace_iterator it(&trace);
187 ASSERT_FALSE(it.at_end());
188 Opt_trace_info info;
189 it.get_value(&info);
190 const char expected[] =
191 "{\n"
192 " \"select#\": 123456,\n"
193 " \"one array\": [\n"
194 " 200.4,\n"
195 " {\n"
196 " \"one key\": \"one value\",\n"
197 " \"another key\": 100\n"
198 " },\n"
199 " \"one string element\",\n"
200 " true,\n"
201 " 0x0b341b20dce3\n"
202 " ] /* one array */,\n"
203 " \"yet another key\": -1000,\n"
204 " \"another array\": [\n"
205 " 1,\n"
206 " 2,\n"
207 " 3,\n"
208 " 4\n"
209 " ] /* another array */\n"
210 "}";
211 EXPECT_STREQ(expected, info.trace_ptr);
212 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
213 check_json_compliance(info.trace_ptr, info.trace_length);
214 EXPECT_EQ(0U, info.missing_bytes);
215 EXPECT_FALSE(info.missing_priv);
216 EXPECT_FALSE(oom);
217 it.next();
218 ASSERT_TRUE(it.at_end());
219 }
220
221 /** Test reaction to malformed JSON (object with value without key) */
TEST_F(TraceContentTest,BuggyObject)222 TEST_F(TraceContentTest, BuggyObject) {
223 ASSERT_FALSE(
224 trace.start(true, false, true, false, -1, 1, ULONG_MAX, all_features));
225 {
226 Opt_trace_object oto(&trace);
227 {
228 Opt_trace_array ota(&trace, "one array");
229 ota.add(200.4);
230 {
231 Opt_trace_object oto1(&trace);
232 oto1.add_alnum("one value"); // no key, which is wrong
233 oto1.add(326); // same
234 Opt_trace_object oto2(&trace); // same
235 }
236 ota.add_alnum("one string element");
237 ota.add(true);
238 }
239 oto.add("yet another key", -1000LL);
240 {
241 Opt_trace_array ota(&trace, "another array");
242 ota.add(1LL).add(2LL).add(3LL).add(4LL);
243 }
244 }
245 trace.end();
246 Opt_trace_iterator it(&trace);
247 ASSERT_FALSE(it.at_end());
248 Opt_trace_info info;
249 it.get_value(&info);
250 const char expected[] =
251 "{\n"
252 " \"one array\": [\n"
253 " 200.4,\n"
254 " {\n"
255 " \"unknown_key_1\": \"one value\",\n"
256 " \"unknown_key_2\": 326,\n"
257 " \"unknown_key_3\": {\n"
258 " }\n"
259 " },\n"
260 " \"one string element\",\n"
261 " true\n"
262 " ] /* one array */,\n"
263 " \"yet another key\": -1000,\n"
264 " \"another array\": [\n"
265 " 1,\n"
266 " 2,\n"
267 " 3,\n"
268 " 4\n"
269 " ] /* another array */\n"
270 "}";
271 EXPECT_STREQ(expected, info.trace_ptr);
272 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
273 check_json_compliance(info.trace_ptr, info.trace_length);
274 EXPECT_EQ(0U, info.missing_bytes);
275 EXPECT_FALSE(info.missing_priv);
276 EXPECT_FALSE(oom);
277 it.next();
278 ASSERT_TRUE(it.at_end());
279 }
280
281 /** Test reaction to malformed JSON (array with value with key) */
TEST_F(TraceContentTest,BuggyArray)282 TEST_F(TraceContentTest, BuggyArray) {
283 ASSERT_FALSE(
284 trace.start(true, false, true, false, -1, 1, ULONG_MAX, all_features));
285 {
286 Opt_trace_object oto(&trace);
287 {
288 Opt_trace_array ota(&trace, "one array");
289 ota.add("superfluous key", 200.4); // key, which is wrong
290 ota.add("not necessary", 326); // same
291 Opt_trace_object oto2(&trace, "not needed"); // same
292 }
293 oto.add("yet another key", -1000LL);
294 {
295 Opt_trace_array ota(&trace, "another array");
296 ota.add(1LL).add(2LL).add(3LL).add(4LL);
297 }
298 }
299 trace.end();
300 Opt_trace_iterator it(&trace);
301 ASSERT_FALSE(it.at_end());
302 Opt_trace_info info;
303 it.get_value(&info);
304 const char expected[] =
305 "{\n"
306 " \"one array\": [\n"
307 " 200.4,\n"
308 " 326,\n"
309 " {\n"
310 " } /* not needed */\n"
311 " ] /* one array */,\n"
312 " \"yet another key\": -1000,\n"
313 " \"another array\": [\n"
314 " 1,\n"
315 " 2,\n"
316 " 3,\n"
317 " 4\n"
318 " ] /* another array */\n"
319 "}";
320 EXPECT_STREQ(expected, info.trace_ptr);
321 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
322 check_json_compliance(info.trace_ptr, info.trace_length);
323 EXPECT_EQ(0U, info.missing_bytes);
324 EXPECT_FALSE(info.missing_priv);
325 EXPECT_FALSE(oom);
326 it.next();
327 ASSERT_TRUE(it.at_end());
328 }
329
330 /** Test Opt_trace_disable_I_S */
TEST_F(TraceContentTest,DisableISWithObject)331 TEST_F(TraceContentTest, DisableISWithObject) {
332 ASSERT_FALSE(
333 trace.start(true, false, true, false, -1, 1, ULONG_MAX, all_features));
334 {
335 Opt_trace_object oto(&trace);
336 {
337 Opt_trace_array ota(&trace, "one array");
338 ota.add(200.4);
339 {
340 Opt_trace_object oto1(&trace);
341 oto1.add_alnum("one key", "one value").add("another key", 100LL);
342 Opt_trace_disable_I_S otd(&trace, true);
343 oto1.add("a third key", false);
344 Opt_trace_object oto2(&trace, "a fourth key");
345 oto2.add("key inside", 1LL);
346 /* don't disable... but above layer is stronger */
347 Opt_trace_disable_I_S otd2(&trace, false);
348 oto2.add("another key inside", 5LL);
349 // disabling should apply to substatements too:
350 ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
351 all_features));
352 { Opt_trace_object oto3(&trace); }
353 trace.end();
354 }
355 ota.add_alnum("one string element");
356 ota.add(true);
357 }
358 Opt_trace_disable_I_S otd2(&trace, false); // don't disable
359 oto.add("yet another key", -1000LL);
360 {
361 Opt_trace_array ota(&trace, "another array");
362 ota.add(1LL).add(2LL).add(3LL).add(4LL);
363 }
364 }
365 trace.end();
366 Opt_trace_iterator it(&trace);
367 ASSERT_FALSE(it.at_end());
368 Opt_trace_info info;
369 it.get_value(&info);
370 const char expected[] =
371 "{\n"
372 " \"one array\": [\n"
373 " 200.4,\n"
374 " {\n"
375 " \"one key\": \"one value\",\n"
376 " \"another key\": 100\n"
377 " },\n"
378 " \"one string element\",\n"
379 " true\n"
380 " ] /* one array */,\n"
381 " \"yet another key\": -1000,\n"
382 " \"another array\": [\n"
383 " 1,\n"
384 " 2,\n"
385 " 3,\n"
386 " 4\n"
387 " ] /* another array */\n"
388 "}";
389 EXPECT_STREQ(expected, info.trace_ptr);
390 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
391 check_json_compliance(info.trace_ptr, info.trace_length);
392 EXPECT_EQ(0U, info.missing_bytes);
393 EXPECT_FALSE(info.missing_priv);
394 EXPECT_FALSE(oom);
395 it.next();
396 ASSERT_TRUE(it.at_end());
397 }
398
399 /** Test Opt_trace_context::disable_I_S_for_this_and_children */
TEST_F(TraceContentTest,DisableISWithCall)400 TEST_F(TraceContentTest, DisableISWithCall) {
401 // Test that it disables even before any start()
402 trace.disable_I_S_for_this_and_children();
403 ASSERT_FALSE(
404 trace.start(true, false, true, false, -1, 1, ULONG_MAX, all_features));
405 {
406 Opt_trace_object oto(&trace);
407 {
408 Opt_trace_array ota(&trace, "one array");
409 ota.add(200.4);
410 {
411 Opt_trace_object oto1(&trace);
412 oto1.add_alnum("one key", "one value").add("another key", 100LL);
413 oto1.add("a third key", false);
414 Opt_trace_object oto2(&trace, "a fourth key");
415 oto2.add("key inside", 1LL);
416 // disabling should apply to substatements too:
417 ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
418 all_features));
419 { Opt_trace_object oto3(&trace); }
420 trace.end();
421 /* don't disable... but above layer is stronger */
422 Opt_trace_disable_I_S otd2(&trace, false);
423 oto2.add("another key inside", 5LL);
424 // disabling should apply to substatements too:
425 ASSERT_FALSE(trace.start(true, false, true, false, -1, 1, ULONG_MAX,
426 all_features));
427 { Opt_trace_object oto4(&trace); }
428 trace.end();
429 }
430 ota.add_alnum("one string element");
431 ota.add(true);
432 }
433 oto.add("yet another key", -1000LL);
434 {
435 Opt_trace_array ota(&trace, "another array");
436 ota.add(1LL).add(2LL).add(3LL).add(4LL);
437 }
438 }
439 trace.end();
440 trace.restore_I_S();
441 Opt_trace_iterator it(&trace);
442 ASSERT_TRUE(it.at_end());
443 }
444
445 /** Helper for Trace_settings_test.offset */
make_one_trace(Opt_trace_context * trace,const char * name,long offset,long limit)446 void make_one_trace(Opt_trace_context *trace, const char *name, long offset,
447 long limit) {
448 ASSERT_FALSE(trace->start(true, false, true, false, offset, limit, ULONG_MAX,
449 all_features));
450 {
451 Opt_trace_object oto(trace);
452 oto.add(name, 0LL);
453 }
454 trace->end();
455 }
456
457 /**
458 Helper for Trace_settings_test.offset
459
460 @param trace The trace context.
461 @param names A NULL-terminated array of "names".
462
463 Checks that the list of traces is as expected.
464 This macro checks that the first trace contains names[0], that the second
465 trace contains names[1], etc. That the number of traces is the same as
466 the number of elements in "names".
467
468 @note It is a macro, for proper reporting of line numbers in case of
469 assertion failure. SCOPED_TRACE will report line number at the
470 macro expansion site.
471 */
472 #define check(trace, names) \
473 { \
474 SCOPED_TRACE(""); \
475 do_check(&trace, names); \
476 }
477
do_check(Opt_trace_context * trace,const char ** names)478 void do_check(Opt_trace_context *trace, const char **names) {
479 Opt_trace_iterator it(trace);
480 Opt_trace_info info;
481 for (const char **name = names; *name != nullptr; name++) {
482 ASSERT_FALSE(it.at_end());
483 it.get_value(&info);
484 const size_t name_len = strlen(*name);
485 EXPECT_EQ(name_len + 11, info.trace_length);
486 EXPECT_EQ(0, strncmp(info.trace_ptr + 5, *name, name_len));
487 EXPECT_EQ(0U, info.missing_bytes);
488 it.next();
489 }
490 ASSERT_TRUE(it.at_end());
491 }
492
493 /** Test offset/limit variables */
TEST_F(TraceContentTest,Offset)494 TEST_F(TraceContentTest, Offset) {
495 make_one_trace(&trace, "100", -1 /* offset */, 1 /* limit */);
496 const char *expected_traces0[] = {"100", nullptr};
497 check(trace, expected_traces0);
498 make_one_trace(&trace, "101", -1, 1);
499 /* 101 should have overwritten 100 */
500 const char *expected_traces1[] = {"101", nullptr};
501 check(trace, expected_traces1);
502 make_one_trace(&trace, "102", -1, 1);
503 const char *expected_traces2[] = {"102", nullptr};
504 check(trace, expected_traces2);
505 trace.reset();
506 const char *expected_traces_empty[] = {nullptr};
507 check(trace, expected_traces_empty);
508 make_one_trace(&trace, "103", -3, 2);
509 make_one_trace(&trace, "104", -3, 2);
510 make_one_trace(&trace, "105", -3, 2);
511 make_one_trace(&trace, "106", -3, 2);
512 make_one_trace(&trace, "107", -3, 2);
513 make_one_trace(&trace, "108", -3, 2);
514 make_one_trace(&trace, "109", -3, 2);
515 const char *expected_traces3[] = {"107", "108", nullptr};
516 check(trace, expected_traces3);
517 trace.reset();
518 check(trace, expected_traces_empty);
519 make_one_trace(&trace, "110", 3, 2);
520 make_one_trace(&trace, "111", 3, 2);
521 make_one_trace(&trace, "112", 3, 2);
522 make_one_trace(&trace, "113", 3, 2);
523 make_one_trace(&trace, "114", 3, 2);
524 make_one_trace(&trace, "115", 3, 2);
525 make_one_trace(&trace, "116", 3, 2);
526 const char *expected_traces10[] = {"113", "114", nullptr};
527 check(trace, expected_traces10);
528 trace.reset();
529 check(trace, expected_traces_empty);
530 make_one_trace(&trace, "117", 0, 1);
531 make_one_trace(&trace, "118", 0, 1);
532 make_one_trace(&trace, "119", 0, 1);
533 const char *expected_traces17[] = {"117", nullptr};
534 check(trace, expected_traces17);
535 trace.reset();
536 make_one_trace(&trace, "120", 0, 0);
537 make_one_trace(&trace, "121", 0, 0);
538 make_one_trace(&trace, "122", 0, 0);
539 const char *expected_traces20[] = {nullptr};
540 check(trace, expected_traces20);
541 EXPECT_FALSE(oom);
542 }
543
544 /** Test truncation by max_mem_size */
TEST_F(TraceContentTest,MaxMemSize)545 TEST_F(TraceContentTest, MaxMemSize) {
546 ASSERT_FALSE(trace.start(true, false, false, false, -1, 1,
547 1000 /* max_mem_size */, all_features));
548 /* make a "long" trace */
549 {
550 Opt_trace_object oto(&trace);
551 Opt_trace_array ota(&trace, "one array");
552 for (int i = 0; i < 100; i++) {
553 ota.add_alnum("make it long");
554 }
555 }
556 trace.end();
557 Opt_trace_iterator it(&trace);
558 ASSERT_FALSE(it.at_end());
559 Opt_trace_info info;
560 it.get_value(&info);
561 const char expected[] =
562 "{\n"
563 " \"one array\": [\n"
564 " \"make it long\",\n"
565 " \"make it long\",\n";
566 /*
567 Without truncation the trace would take:
568 2+17+3+1+20*100 = 2023
569 */
570 EXPECT_EQ(996U, info.trace_length);
571 EXPECT_EQ(1027U, info.missing_bytes); // 996+1027=2023
572 EXPECT_FALSE(info.missing_priv);
573 EXPECT_FALSE(oom);
574 EXPECT_EQ(0, strncmp(expected, info.trace_ptr, sizeof(expected) - 1));
575 it.next();
576 ASSERT_TRUE(it.at_end());
577 }
578
579 /** Test how truncation by max_mem_size affects next traces */
TEST_F(TraceContentTest,MaxMemSize2)580 TEST_F(TraceContentTest, MaxMemSize2) {
581 ASSERT_FALSE(trace.start(true, false, false, false, -2, 2,
582 21 /* max_mem_size */, all_features));
583 /* make a "long" trace */
584 {
585 Opt_trace_object oto(&trace);
586 oto.add_alnum("some key1", "make it long");
587 }
588 trace.end();
589 /* A second similar trace */
590 ASSERT_FALSE(trace.start(true, false, false, false, -2, 2, 21, all_features));
591 {
592 Opt_trace_object oto(&trace);
593 oto.add_alnum("some key2", "make it long");
594 }
595 trace.end();
596 Opt_trace_iterator it(&trace);
597 ASSERT_FALSE(it.at_end());
598 Opt_trace_info info;
599 it.get_value(&info);
600 EXPECT_EQ(17U, info.trace_length);
601 EXPECT_EQ(16U, info.missing_bytes);
602 EXPECT_FALSE(info.missing_priv);
603 EXPECT_FALSE(oom);
604 it.next();
605 ASSERT_FALSE(it.at_end());
606 it.get_value(&info);
607 /* 2nd trace completely empty as first trace left no room */
608 EXPECT_EQ(0U, info.trace_length);
609 EXPECT_EQ(33U, info.missing_bytes);
610 it.next();
611 ASSERT_TRUE(it.at_end());
612 /*
613 3rd trace; the first one should automatically be purged, thus the 3rd
614 should have a bit of room.
615 */
616 ASSERT_FALSE(trace.start(true, false, false, false, -2, 2, 21, all_features));
617 {
618 Opt_trace_object oto(&trace);
619 oto.add_alnum("some key3", "make it long");
620 }
621 trace.end();
622 Opt_trace_iterator it2(&trace);
623 ASSERT_FALSE(it2.at_end());
624 it2.get_value(&info);
625 EXPECT_EQ(0U, info.trace_length);
626 EXPECT_EQ(33U, info.missing_bytes);
627 EXPECT_FALSE(info.missing_priv);
628 EXPECT_FALSE(oom);
629 it2.next();
630 it2.get_value(&info);
631 /*
632 3rd one had room. A bit less than first, because just reading the second
633 with the iterator has reallocated the second from 0 to 8 bytes...
634 */
635 EXPECT_EQ(14U, info.trace_length);
636 EXPECT_EQ(19U, info.missing_bytes);
637 EXPECT_FALSE(info.missing_priv);
638 it2.next();
639 ASSERT_TRUE(it2.at_end());
640 }
641
open_object(uint count,Opt_trace_context * trace,bool simulate_oom)642 void open_object(uint count, Opt_trace_context *trace, bool simulate_oom) {
643 if (count == 0) return;
644 count--;
645 char key[4];
646 /*
647 Add 100 to always have a key of length 3, this matters to
648 TraceContentTest.Indent.
649 We could use just a fixed string, but it would cause an assertion failure
650 (due to invalid JSON, which itself is conceivable in case of OOM).
651 */
652 llstr(100 + count, key);
653 if (simulate_oom) {
654 if (count == 90) DBUG_SET("+d,opt_trace_oom_in_open_struct");
655 /*
656 Now we let 80 objects be created, so that one of them surely hits
657 re-allocation and OOM failure.
658 */
659 if (count == 10) DBUG_SET("-d,opt_trace_oom_in_open_struct");
660 }
661 Opt_trace_object oto(trace, key);
662 open_object(count, trace, simulate_oom);
663 }
664
665 #ifndef DBUG_OFF
666
667 /// Test reaction to out-of-memory condition in trace buffer
TEST_F(TraceContentTest,OOMinBuffer)668 TEST_F(TraceContentTest, OOMinBuffer) {
669 ASSERT_FALSE(
670 trace.start(true, false, false, false, -1, 1, ULONG_MAX, all_features));
671 {
672 Opt_trace_object oto(&trace);
673 {
674 Opt_trace_array ota(&trace, "one array");
675 DBUG_SET("+d,opt_trace_oom_in_buffers");
676 for (int i = 0; i < 30; i++)
677 ota.add_alnum("_______________________________________________");
678 DBUG_SET("-d,opt_trace_oom_in_buffers");
679 }
680 }
681 trace.end();
682 Opt_trace_iterator it(&trace);
683 ASSERT_FALSE(it.at_end());
684 Opt_trace_info info;
685 it.get_value(&info);
686 EXPECT_EQ(0U, info.missing_bytes);
687 EXPECT_FALSE(info.missing_priv);
688 it.next();
689 ASSERT_TRUE(it.at_end());
690 EXPECT_TRUE(oom);
691 }
692
693 /// Test reaction to out-of-memory condition in book-keeping data structures
TEST_F(TraceContentTest,OOMinBookKeeping)694 TEST_F(TraceContentTest, OOMinBookKeeping) {
695 ASSERT_FALSE(
696 trace.start(true, false, false, false, -1, 1, ULONG_MAX, all_features));
697 {
698 Opt_trace_object oto(&trace);
699 open_object(100, &trace, true);
700 }
701 trace.end();
702 Opt_trace_iterator it(&trace);
703 ASSERT_FALSE(it.at_end());
704 Opt_trace_info info;
705 it.get_value(&info);
706 EXPECT_EQ(0U, info.missing_bytes);
707 EXPECT_FALSE(info.missing_priv);
708 it.next();
709 ASSERT_TRUE(it.at_end());
710 EXPECT_TRUE(oom);
711 }
712
713 /// Test reaction to OOM when purging traces
TEST_F(TraceContentTest,OOMinPurge)714 TEST_F(TraceContentTest, OOMinPurge) {
715 make_one_trace(&trace, "103", -3, 2);
716 make_one_trace(&trace, "104", -3, 2);
717 DBUG_SET("+d,opt_trace_oom_in_purge");
718 make_one_trace(&trace, "105", -3, 2);
719 make_one_trace(&trace, "106", -3, 2);
720 make_one_trace(&trace, "107", -3, 2);
721 make_one_trace(&trace, "108", -3, 2);
722 make_one_trace(&trace, "109", -3, 2);
723 make_one_trace(&trace, "110", -3, 2);
724 make_one_trace(&trace, "111", -3, 2);
725 make_one_trace(&trace, "112", -3, 2);
726 make_one_trace(&trace, "113", -3, 2);
727 make_one_trace(&trace, "114", -3, 2);
728 make_one_trace(&trace, "115", -3, 2);
729 make_one_trace(&trace, "116", -3, 2);
730 make_one_trace(&trace, "117", -3, 2);
731 make_one_trace(&trace, "118", -3, 2);
732 make_one_trace(&trace, "119", -3, 2);
733 make_one_trace(&trace, "120", -3, 2);
734 make_one_trace(&trace, "121", -3, 2);
735 make_one_trace(&trace, "122", -3, 2); // purge first fails here
736
737 DBUG_SET("-d,opt_trace_oom_in_purge");
738 // 122 could not purge 119, so we should see 119 and 120
739 const char *expected_traces3[] = {"119", "120", nullptr};
740 check(trace, expected_traces3);
741 EXPECT_TRUE(oom);
742
743 // Back to normal:
744 oom = false;
745 make_one_trace(&trace, "123", -3, 2); // purge succeeds
746 const char *expected_traces4[] = {"121", "122", nullptr};
747 check(trace, expected_traces4);
748 EXPECT_FALSE(oom);
749 }
750
751 #endif // !DBUG_OFF
752
753 /** Test filtering by feature */
TEST_F(TraceContentTest,FilteringByFeature)754 TEST_F(TraceContentTest, FilteringByFeature) {
755 ASSERT_FALSE(trace.start(true, false, false, false, -1, 1, ULONG_MAX,
756 Opt_trace_context::MISC));
757 {
758 Opt_trace_object oto(&trace);
759 {
760 Opt_trace_array ota(&trace, "one array");
761 ota.add(200.4);
762 {
763 Opt_trace_object oto1(&trace, Opt_trace_context::GREEDY_SEARCH);
764 oto1.add_alnum("one key", "one value").add("another key", 100LL);
765 Opt_trace_object oto2(&trace, "a fourth key", Opt_trace_context::MISC);
766 oto2.add("another key inside", 5LL);
767 }
768 ota.add(true);
769 }
770 {
771 Opt_trace_object oto3(&trace, "key for oto3",
772 Opt_trace_context::GREEDY_SEARCH);
773 oto3.add("etc", 25);
774 }
775 oto.add("yet another key", -1000LL);
776 {
777 Opt_trace_array ota(&trace, "another array");
778 ota.add(1LL).add(2LL).add(3LL).add(4LL);
779 }
780 }
781 trace.end();
782 Opt_trace_iterator it(&trace);
783 ASSERT_FALSE(it.at_end());
784 Opt_trace_info info;
785 it.get_value(&info);
786 const char expected[] =
787 "{\n"
788 " \"one array\": [\n"
789 " 200.4,\n"
790 " \"...\",\n"
791 " true\n"
792 " ],\n"
793 " \"key for oto3\": \"...\",\n"
794 " \"yet another key\": -1000,\n"
795 " \"another array\": [\n"
796 " 1,\n"
797 " 2,\n"
798 " 3,\n"
799 " 4\n"
800 " ]\n"
801 "}";
802 EXPECT_STREQ(expected, info.trace_ptr);
803 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
804 check_json_compliance(info.trace_ptr, info.trace_length);
805 EXPECT_EQ(0U, info.missing_bytes);
806 EXPECT_FALSE(info.missing_priv);
807 EXPECT_FALSE(oom);
808 it.next();
809 ASSERT_TRUE(it.at_end());
810 }
811
812 /** Test escaping of characters */
TEST_F(TraceContentTest,Escaping)813 TEST_F(TraceContentTest, Escaping) {
814 ASSERT_FALSE(
815 trace.start(true, false, true, false, -1, 1, ULONG_MAX, all_features));
816 // All ASCII 0-127 chars are valid UTF8 encodings
817 char all_chars[130];
818 for (uint c = 0; c < sizeof(all_chars) - 2; c++) all_chars[c] = c;
819 // Now a character with a two-byte code in utf8: ä
820 all_chars[128] = static_cast<char>(0xc3);
821 all_chars[129] = static_cast<char>(0xa4);
822 // all_chars is used both as query...
823 trace.set_query(all_chars, sizeof(all_chars), system_charset_info);
824 {
825 Opt_trace_object oto(&trace);
826 // ... and inside the trace:
827 oto.add_utf8("somekey", all_chars, sizeof(all_chars));
828 }
829 trace.end();
830 Opt_trace_iterator it(&trace);
831 ASSERT_FALSE(it.at_end());
832 Opt_trace_info info;
833 it.get_value(&info);
834 // we get the trace escaped, JSON-compliant:
835 const char expected[] =
836 "{\n"
837 " \"somekey\": "
838 "\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\u0008\\t\\n"
839 "\\u000b\\u000c\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u001"
840 "5\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f "
841 "!\\\"#$%&'()*+,-./"
842 "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`"
843 "abcdefghijklmnopqrstuvwxyz{|}~ä\"\n"
844 "}";
845 EXPECT_STREQ(expected, info.trace_ptr);
846 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
847 check_json_compliance(info.trace_ptr, info.trace_length);
848 EXPECT_EQ(0U, info.missing_bytes);
849 EXPECT_FALSE(info.missing_priv);
850 EXPECT_FALSE(oom);
851 EXPECT_EQ(sizeof(all_chars), info.query_length);
852 // we get the query unescaped, verbatim, not 0-terminated:
853 EXPECT_EQ(0, memcmp(all_chars, info.query_ptr, sizeof(all_chars)));
854 EXPECT_EQ(system_charset_info, info.query_charset);
855 it.next();
856 ASSERT_TRUE(it.at_end());
857 }
858
859 /** Test how the system handles non-UTF8 characters, a violation of its API */
TEST_F(TraceContentTest,NonUtf8)860 TEST_F(TraceContentTest, NonUtf8) {
861 ASSERT_FALSE(
862 trace.start(true, false, true, false, -1, 1, ULONG_MAX, all_features));
863 /*
864 A string which starts with invalid utf8 (the four first bytes are éèÄà in
865 latin1).
866 In utf8, the following holds
867 - E0->EF can only be the start of a 3-byte sequence
868 - C2->DF 2-byte
869 - ASCII a single-byte sequence
870 */
871 const char all_chars[] =
872 "\xe9\xe8\xc4\xe0"
873 "ABC";
874 // We declare a query in latin1
875 trace.set_query(all_chars, sizeof(all_chars), &my_charset_latin1);
876 {
877 Opt_trace_object oto(&trace);
878 /*
879 We pass the non-utf8-compliant string to add_utf8() (violating the
880 API). We get it back unchanged. The trace system could try to be robust,
881 detecting and sanitizing wrong characters (replacing them with '?'); but
882 it does not bother, as the MySQL Server normally does not violate the
883 API.
884 */
885 oto.add_utf8("somekey", all_chars, sizeof(all_chars) - 1);
886 }
887 trace.end();
888 Opt_trace_iterator it(&trace);
889 ASSERT_FALSE(it.at_end());
890 Opt_trace_info info;
891 it.get_value(&info);
892 // This is not UTF8-compliant and thus not JSON-compliant.
893 const char expected[] =
894 "{\n"
895 " \"somekey\": \""
896 "\xe9\xe8\xc4\xe0"
897 "ABC\"\n"
898 "}";
899 EXPECT_STREQ(expected, info.trace_ptr);
900 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
901 EXPECT_EQ(0U, info.missing_bytes);
902 EXPECT_FALSE(info.missing_priv);
903 EXPECT_FALSE(oom);
904 EXPECT_EQ(sizeof(all_chars), info.query_length);
905 // we get the query unescaped, verbatim, not 0-terminated:
906 EXPECT_EQ(0, memcmp(all_chars, info.query_ptr, sizeof(all_chars)));
907 it.next();
908 ASSERT_TRUE(it.at_end());
909 }
910
911 /**
912 Test indentation by many blanks.
913 By creating a 100-level deep structure, we force an indentation which
914 enters the while() block in Opt_trace_stmt::next_line().
915 */
TEST_F(TraceContentTest,Indent)916 TEST_F(TraceContentTest, Indent) {
917 ASSERT_FALSE(
918 trace.start(true, false, false, false, -1, 1, ULONG_MAX, all_features));
919 {
920 Opt_trace_object oto(&trace);
921 open_object(100, &trace, false);
922 }
923 trace.end();
924 Opt_trace_iterator it(&trace);
925 ASSERT_FALSE(it.at_end());
926 Opt_trace_info info;
927 it.get_value(&info);
928 /*
929 Formula for the expected size.
930 Before the Nth call to open_object(), indentation inside the innermost
931 empty object is noted I(N); so the relationship between the size before
932 Nth call and the size after Nth call is:
933 S(N+1) = S(N)
934 + I(N) (indentation before added '"xxx": {\n' )
935 + 9 (length of added '"xxx": {\n' )
936 + I(N) (indentation before added '}\n' )
937 + 2 (length of added '}\n' )
938 and the indentation is increased by two as we are one level deeper:
939 I(N+1) = I(N) + 2
940 With S(1) = 3 (length of '{\n}') and I(1) = 2.
941 So I(N) = 2 * N and
942 S(N+1) - S(N) = 11 + 4 * N
943 So S(N) = 3 + 11 * (N - 1) + 2 * N * (N - 1).
944 For 100 calls, the final size is S(101) = 21303.
945 Each call adds 10 non-space characters, so there should be
946 21303
947 - 10 * 100 (added non-spaces characters)
948 - 3 (non-spaces of initial object before first function call)
949 = 20300 spaces.
950 */
951 EXPECT_EQ(21303U, info.trace_length);
952 uint spaces = 0;
953 for (uint i = 0; i < info.trace_length; i++)
954 if (info.trace_ptr[i] == ' ') spaces++;
955 EXPECT_EQ(20300U, spaces);
956 check_json_compliance(info.trace_ptr, info.trace_length);
957 EXPECT_EQ(0U, info.missing_bytes);
958 EXPECT_FALSE(info.missing_priv);
959 EXPECT_FALSE(oom);
960 it.next();
961 ASSERT_TRUE(it.at_end());
962 }
963
964 /** Test Opt_trace_context::missing_privilege() */
TEST_F(TraceContentTest,MissingPrivilege)965 TEST_F(TraceContentTest, MissingPrivilege) {
966 ASSERT_FALSE(
967 trace.start(true, false, true, false, 0, 100, ULONG_MAX, all_features));
968 {
969 Opt_trace_object oto(&trace);
970 {
971 Opt_trace_array ota(&trace, "one array");
972 ota.add(200.4);
973 {
974 Opt_trace_object oto1(&trace);
975 oto1.add_alnum("one key", "one value").add("another key", 100LL);
976 oto1.add("a third key", false);
977 Opt_trace_object oto2(&trace, "a fourth key");
978 oto2.add("key inside", 1LL);
979 ASSERT_FALSE(trace.start(true, false, true, false, 0, 100, ULONG_MAX,
980 all_features));
981 {
982 Opt_trace_object oto3(&trace);
983 trace.missing_privilege();
984 ASSERT_FALSE(trace.start(true, false, true, false, 0, 100, ULONG_MAX,
985 all_features));
986 {
987 Opt_trace_object oto4(&trace);
988 oto4.add_alnum("in4", "key4");
989 }
990 trace.end();
991 }
992 trace.end(); // this should restore I_S support
993 // so this should be visible
994 oto2.add("another key inside", 5LL);
995 // and this new sub statement too:
996 ASSERT_FALSE(trace.start(true, false, true, false, 0, 100, ULONG_MAX,
997 all_features));
998 {
999 Opt_trace_object oto5(&trace);
1000 oto5.add("in5", true);
1001 }
1002 trace.end();
1003 }
1004 ota.add_alnum("one string element");
1005 ota.add(true);
1006 }
1007 oto.add("yet another key", -1000LL);
1008 {
1009 Opt_trace_array ota(&trace, "another array");
1010 ota.add(1LL).add(2LL).add(3LL).add(4LL);
1011 }
1012 }
1013 trace.end();
1014 Opt_trace_iterator it(&trace);
1015 ASSERT_FALSE(it.at_end());
1016 Opt_trace_info info;
1017 it.get_value(&info);
1018 const char expected[] =
1019 "{\n"
1020 " \"one array\": [\n"
1021 " 200.4,\n"
1022 " {\n"
1023 " \"one key\": \"one value\",\n"
1024 " \"another key\": 100,\n"
1025 " \"a third key\": false,\n"
1026 " \"a fourth key\": {\n"
1027 " \"key inside\": 1,\n"
1028 " \"another key inside\": 5\n"
1029 " } /* a fourth key */\n"
1030 " },\n"
1031 " \"one string element\",\n"
1032 " true\n"
1033 " ] /* one array */,\n"
1034 " \"yet another key\": -1000,\n"
1035 " \"another array\": [\n"
1036 " 1,\n"
1037 " 2,\n"
1038 " 3,\n"
1039 " 4\n"
1040 " ] /* another array */\n"
1041 "}";
1042 EXPECT_STREQ(expected, info.trace_ptr);
1043 EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
1044 check_json_compliance(info.trace_ptr, info.trace_length);
1045 EXPECT_EQ(0U, info.missing_bytes);
1046 EXPECT_FALSE(info.missing_priv);
1047 EXPECT_FALSE(oom);
1048 it.next();
1049 ASSERT_FALSE(it.at_end());
1050 // Now the substatement with a missing privilege
1051 it.get_value(&info);
1052 const char expected2[] = ""; // because of missing privilege...
1053 EXPECT_STREQ(expected2, info.trace_ptr);
1054 EXPECT_EQ(sizeof(expected2) - 1, info.trace_length);
1055 EXPECT_EQ(0U, info.missing_bytes);
1056 EXPECT_TRUE(info.missing_priv); // ... tested here.
1057 it.next();
1058 ASSERT_FALSE(it.at_end());
1059 // And now the last substatement, visible
1060 it.get_value(&info);
1061 const char expected3[] =
1062 "{\n"
1063 " \"in5\": true\n"
1064 "}";
1065 EXPECT_STREQ(expected3, info.trace_ptr);
1066 EXPECT_EQ(sizeof(expected3) - 1, info.trace_length);
1067 check_json_compliance(info.trace_ptr, info.trace_length);
1068 EXPECT_EQ(0U, info.missing_bytes);
1069 EXPECT_FALSE(info.missing_priv);
1070 it.next();
1071 ASSERT_TRUE(it.at_end());
1072 }
1073
1074 /** Test Opt_trace_context::missing_privilege() on absent trace */
TEST_F(TraceContentTest,MissingPrivilege2)1075 TEST_F(TraceContentTest, MissingPrivilege2) {
1076 /*
1077 Ask for neither I_S not debug output, and no
1078 missing_privilege() support
1079 */
1080 ASSERT_FALSE(
1081 trace.start(false, false, true, false, 0, 100, ULONG_MAX, all_features));
1082 EXPECT_FALSE(trace.is_started());
1083 trace.end();
1084 /*
1085 Ask for neither I_S not debug output, but ask that
1086 missing_privilege() is supported.
1087 */
1088 ASSERT_FALSE(
1089 trace.start(false, true, true, false, 0, 100, ULONG_MAX, all_features));
1090 EXPECT_TRUE(trace.is_started());
1091 trace.missing_privilege();
1092 // This above should make the substatement below not be traced:
1093 ASSERT_FALSE(
1094 trace.start(true, false, true, false, 0, 100, ULONG_MAX, all_features));
1095 {
1096 Opt_trace_object oto5(&trace);
1097 oto5.add("in5", true);
1098 }
1099 trace.end();
1100 trace.end();
1101 Opt_trace_iterator it(&trace);
1102 ASSERT_TRUE(it.at_end());
1103 }
1104
1105 /**
1106 Test an optimization: that no Opt_trace_stmt is created in common case
1107 where all statements and substatements ask neither for I_S nor for DBUG,
1108 nor for support of missing_privilege() function.
1109 */
TEST_F(TraceContentTest,NoOptTraceStmt)1110 TEST_F(TraceContentTest, NoOptTraceStmt) {
1111 ASSERT_FALSE(
1112 trace.start(false, false, false, false, -1, 1, ULONG_MAX, all_features));
1113 EXPECT_FALSE(trace.is_started());
1114 // one substatement:
1115 ASSERT_FALSE(
1116 trace.start(false, false, false, false, -1, 1, ULONG_MAX, all_features));
1117 EXPECT_FALSE(trace.is_started());
1118 // another one deeper nested:
1119 ASSERT_FALSE(
1120 trace.start(false, false, false, false, -1, 1, ULONG_MAX, all_features));
1121 EXPECT_FALSE(trace.is_started());
1122 trace.end();
1123 trace.end();
1124 trace.end();
1125 }
1126
1127 } // namespace opt_trace_unittest
1128