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