3269 Guilhem Bichot 2011-02-28
Better handling of OOM in optimizer trace, so that we crash in no case.
@ sql/opt_trace.cc
Handle all Sql_array::append() errors (always due to OOM).
The idea is always that if we cannot append(), we need
to signal it (returning some bool error status) so that
nobody later tries to pop():
- in Opt_trace_stmt::disable_I_S_for_this_and_children(),
if OOM then don't disable, return error status,
then Opt_trace_disable_I_S has to set itself to "dummy".
- in Opt_trace_stmt::open_struct(), if OOM then return error status,
then the Opt_trace_struct has to set itself to "dummy".
With that, no need to pre-allocate many cells in the dynamic arrays.
Additionally, call my_error() if new() or my_malloc() fail (String::append()
and Sql_array::append() calls already do so if they fail). This serves to
inform the user of out-of-memory conditions, when it's not possible to do it
anyway with the OPTIMIZER_TRACE.OS_MALLOC_ERROR column (when there is not
even a row in this column).
@ sql/opt_trace.h
More static bools for unit testing
@ sql/opt_trace2server.cc
call my_error() if new() fails (Sql_array::append()
and String::append() calls already do so if they fail, I
want to be consistent). And as the user used
optimizer_trace=enabled=on or --debug, she/he
is interested in the content of the optimizer trace or debug log,
so wants to know if some traces will be completely absent due to OOM:
we cannot be silent about this.
@ sql/sql_array.h
comments, deleting function added in previous commit and now unneeded.
@ unittest/gunit/opt_trace-t.cc
more tests for OOM.
modified:
mysql-test/r/optimizer_trace_oom.result
mysql-test/t/optimizer_trace_oom.test
sql/opt_trace.cc
sql/opt_trace.h
sql/opt_trace2server.cc
sql/sql_array.h
unittest/gunit/opt_trace-t.cc
3268 Guilhem Bichot 2011-02-19
fix for bugs:
bug (a): if @@optimizer_trace=enabled=on, "SELECT FROM I_S.OPTIMIZER_TRACE" would
internally create an Opt_trace_stmt (not visible in I_S, so correct,
but still inefficient: it was possible to not create the object).
bug (b): if inside a routine's body @@optimizer_trace changed to "enabled=off",
the substatements would attach their traces to the trace of their parent
statement (thus they would still kind of be traced).
@ mysql-test/include/optimizer_trace2.inc
- DISTINCT is not needed, we have variants for no_prot and ps_prot already
- test for bug (b): check that if a tracing gets disabled in a routine's body,
substatements are not traced (without the code changes, the trace of
"SELECT 1" and friends was inserted into the trace of
"SELECT f1("c")"!
@ mysql-test/t/optimizer_trace_debug.test
test for bug (a): without the code fix, when doing
set optimizer_trace="enabled=on";
select * from information_schema.OPTIMIZER_TRACE;
the SELECT would start a dummy internal trace (inefficient).
@ sql/opt_trace2server.cc
reworking logic to fix bugs. We first decide on the type of I_S support
that we want, then we decide between optimizing (not creating Opt_trace_stmt),
or not optimizing (when impossible).
added:
mysql-test/r/optimizer_trace_debug.result
mysql-test/t/optimizer_trace_debug.test
modified:
mysql-test/include/optimizer_trace2.inc
mysql-test/r/optimizer_trace2_no_prot.result
mysql-test/r/optimizer_trace2_ps_prot.result
sql/opt_trace2server.cc
=== modified file 'mysql-test/r/optimizer_trace_oom.result'
--- a/mysql-test/r/optimizer_trace_oom.result 2011-02-15 20:53:19 +0000
+++ b/mysql-test/r/optimizer_trace_oom.result 2011-02-28 15:51:31 +0000
@@ -2,6 +2,7 @@ set @@session.optimizer_trace="enabled=o
select @@debug;
@@debug
+OOM when creating a new "statement trace"
select 1;
1
1
@@ -37,11 +38,9 @@ select 1 {
} 0 0
set session debug="+d,opt_trace_oom1";
select @@debug;
-@@debug
-d,opt_trace_oom1
+ERROR HY001: Out of memory; restart server and try again (needed 192 bytes)
select 2;
-2
-2
+ERROR HY001: Out of memory; restart server and try again (needed 192 bytes)
select * from information_schema.OPTIMIZER_TRACE;
QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE OS_MALLOC_ERROR
select 1 {
@@ -72,7 +71,6 @@ select 1 {
}
]
} 0 0
-set @@session.optimizer_trace="enabled=on";
set session debug=default;
select @@debug;
@@debug
=== modified file 'mysql-test/t/optimizer_trace_oom.test'
--- a/mysql-test/t/optimizer_trace_oom.test 2011-02-15 17:19:30 +0000
+++ b/mysql-test/t/optimizer_trace_oom.test 2011-02-28 15:51:31 +0000
@@ -1,4 +1,6 @@
-# Test how opt trace reacts to out-of-memory
+# Test how opt trace reacts to out-of-memory, when used in the
+# server.
+# Other OOM tests are in opt_trace-t.
--source include/have_optimizer_trace.inc
--source include/have_debug.inc
@@ -7,19 +9,22 @@ set @@session.optimizer_trace="enabled=o
select @@debug;
+--echo OOM when creating a new "statement trace"
+
select 1;
select * from information_schema.OPTIMIZER_TRACE;
set session debug="+d,opt_trace_oom1";
+--error ER_OUTOFMEMORY
select @@debug;
# this shouldn't be traced
+--error ER_OUTOFMEMORY
select 2;
# so we should still see "select 1":
select * from information_schema.OPTIMIZER_TRACE;
-set @@session.optimizer_trace="enabled=on";
set session debug=default;
select @@debug;
=== modified file 'sql/opt_trace.cc'
--- a/sql/opt_trace.cc 2011-02-16 13:52:44 +0000
+++ b/sql/opt_trace.cc 2011-02-28 15:51:31 +0000
@@ -26,20 +26,6 @@
#ifdef OPTIMIZER_TRACE
-namespace {
-/**
- Minimum amount of cells we want in our Dynamic_array-s when they're just
- created. In our tests, nesting of structures is below 20: 48 is
- reasonable.
-*/
-const int array_min_room= 48;
-/**
- By how many cells the Dynamic_array should be extended, after the initial
- allocation.
-*/
-const int array_extend= 16;
-}
-
/**
@class Opt_trace_stmt
@@ -55,24 +41,11 @@ public:
Constructor, starts a trace
@param ctx_arg context
@param support_I_S_arg should trace be in information_schema
- @note make sure to call @c well_constructed() after this; if it returns
- false don't use the instance.
*/
Opt_trace_stmt(Opt_trace_context *ctx_arg,
enum enum_support_I_S support_I_S_arg);
/**
- @returns whether the constructor went perfectly well.
- @sa Opt_trace_context::well_constructed()
- */
- bool well_constructed() const
- {
- return
- stack_of_current_structs.guaranteed_room(array_min_room) ||
- stack_of_values_of_support_I_S.guaranteed_room(array_min_room);
- }
-
- /**
Ends a trace; destruction may not be possible immediately as we may have
to keep the trace in case the user later reads it from I_S.
*/
@@ -96,8 +69,11 @@ public:
@param struc structure being created
@param feature optimizer feature to which the structure belongs
@param opening_bracket opening bracket to use
+ @retval false ok
+ @retval true error, Opt_trace_struct must set itself to dummy; trace
+ may have been written to, will likely be invalid JSON.
*/
- void open_struct(const char *key, Opt_trace_struct *struc,
+ bool open_struct(const char *key, Opt_trace_struct *struc,
Opt_trace_context::feature_value feature,
char opening_bracket);
/**
@@ -228,13 +204,34 @@ private:
/**
Temporarily disables I_S. This is private because only our friend
Opt_trace_disable_I_S is trusted enough to use it.
+ @retval false ok
+ @retval true error (function had no effect, no disabling was done)
*/
- void disable_I_S_for_this_and_children()
+ bool disable_I_S_for_this_and_children()
{
- stack_of_values_of_support_I_S.append(support_I_S);
+ if (unlikely(stack_of_values_of_support_I_S.append(support_I_S)))
+ {
+ /*
+ Note that append() above calls my_error() if it fails, so user is
+ informed.
+ Actually error is not in the trace buffer, but the end user doesn't
+ care:
+ */
+ trace_buffer.set_malloc_error();
+ return true;
+ }
support_I_S= NO_FOR_THIS_AND_CHILDREN;
+ return false;
}
- void restore_I_S() ///< Restores after disable_I_S_for_this_and_children().
+ /**
+ Restores I_S support to what it was before the previous successful call
+ to disable_I_S_for_this_and_children().
+ @note we said "_successful_": indeed if we failed to append() to the
+ dynamic array before, a pop() now would be wrong: it would pop a wrong
+ cell, or if no cell, would dereference a NULL pointer (@sa pop_dynamic())
+ and crash.
+ */
+ void restore_I_S()
{
support_I_S= stack_of_values_of_support_I_S.pop();
}
@@ -318,8 +315,6 @@ private:
// implementation of class Opt_trace_struct
-bool Opt_trace_struct::dbug_assert_on_syntax_error= true;
-
namespace {
/// opening and closing symbols for arrays ([])and objects ({})
const char brackets[]= { '[', '{', ']', '}' };
@@ -344,11 +339,12 @@ void Opt_trace_struct::do_construct(Opt_
DBUG_PRINT("opt", ("%s: starting struct", key));
stmt= ctx->get_current_stmt_in_gen();
- started= true;
#ifndef DBUG_OFF
previous_key[0]= 0;
#endif
- stmt->open_struct(key, this, feature, opening_bracket(requires_key));
+ if (likely(!stmt->open_struct(key, this, feature,
+ opening_bracket(requires_key))))
+ started= true;
}
@@ -498,9 +494,7 @@ const char *Opt_trace_struct::check_key(
Opt_trace_stmt::Opt_trace_stmt(Opt_trace_context *ctx_arg,
enum enum_support_I_S support_I_S_arg):
ended(false), support_I_S(support_I_S_arg), ctx(ctx_arg),
- current_struct(NULL),
- stack_of_current_structs(array_min_room, array_extend),
- stack_of_values_of_support_I_S(array_min_room, array_extend)
+ current_struct(NULL)
{
// Trace is always in UTF8, it's "all" that JSON accepts
trace_buffer.set_charset(system_charset_info);
@@ -573,7 +567,7 @@ bool Opt_trace_stmt::set_query(const cha
}
-void Opt_trace_stmt::open_struct(const char *key, Opt_trace_struct *struc,
+bool Opt_trace_stmt::open_struct(const char *key, Opt_trace_struct *struc,
Opt_trace_context::feature_value feature,
char opening_bracket)
{
@@ -599,7 +593,12 @@ void Opt_trace_stmt::open_struct(const c
else
current_struct->add_alnum("...");
}
- stack_of_values_of_support_I_S.append(support_I_S);
+#ifndef DBUG_OFF
+ if (unlikely(Opt_trace_context::simulate_oom_in_open_struct))
+ goto err;
+#endif
+ if (unlikely(stack_of_values_of_support_I_S.append(support_I_S)))
+ goto err;
support_I_S= NO_FOR_THIS_AND_CHILDREN;
has_disabled_I_S= true;
}
@@ -623,9 +622,18 @@ void Opt_trace_stmt::open_struct(const c
d.current_struct= current_struct;
d.has_disabled_I_S= has_disabled_I_S;
d.current_struct_empty= current_struct_empty;
- stack_of_current_structs.append(d);
+#ifndef DBUG_OFF
+ if (unlikely(Opt_trace_context::simulate_oom_in_open_struct))
+ goto err;
+#endif
+ if (unlikely(stack_of_current_structs.append(d)))
+ goto err;
current_struct= struc;
current_struct_empty= true;
+ return false;
+err:
+ trace_buffer.set_malloc_error();
+ return true;
}
@@ -735,7 +743,7 @@ void Opt_trace_stmt::add(const char *key
else
{
uint32 new_length= system_charset_info->mbmaxlen * val_length;
- char *utf8_str= (char *)my_malloc(new_length, MYF(0));
+ char *utf8_str= static_cast<char *>(my_malloc(new_length, MYF(MY_WME)));
if (utf8_str == NULL)
trace_buffer.set_malloc_error();
else
@@ -769,7 +777,7 @@ void Opt_trace_stmt::syntax_error(const
{
DBUG_PRINT("opt", ("syntax error key: %s", key));
DBUG_ASSERT(support_I_S == YES_FOR_THIS);
- DBUG_ASSERT(!Opt_trace_struct::dbug_assert_on_syntax_error);
+ DBUG_ASSERT(!Opt_trace_context::dbug_assert_on_syntax_error);
/*
Inserting some warning text into the trace below helps locating where
things went wrong.
@@ -1022,15 +1030,14 @@ Opt_trace_context::default_features=
Opt_trace_context::DYNAMIC_RANGE |
Opt_trace_context::REPEATED_SUBSELECT);
-bool Opt_trace_context::simulate_oom_in_buffers= false;
+bool Opt_trace_context::dbug_assert_on_syntax_error= true,
+ Opt_trace_context::simulate_oom_in_buffers= false,
+ Opt_trace_context::simulate_oom_in_open_struct= false,
+ Opt_trace_context::simulate_oom_in_purge= false;
Opt_trace_context::Opt_trace_context() :
- current_stmt_in_gen(NULL),
- stack_of_current_stmts(array_min_room, array_extend),
- all_stmts_for_I_S(array_min_room, array_extend),
- all_stmts_to_del(array_min_room, array_extend),
- since_offset_0(0)
- {}
+ current_stmt_in_gen(NULL), since_offset_0(0)
+{}
Opt_trace_context::~Opt_trace_context()
{
@@ -1043,37 +1050,6 @@ Opt_trace_context::~Opt_trace_context()
}
-bool Opt_trace_context::well_constructed() const
-{
- /*
- Dynamic array allocates memory from the heap, potentially on each
- append() call. It is hard to leave all data structures in a correct state
- if an append() fails.
- The constructors of Dynamic_array have normally preallocated a reasonable
- amount of cells, sufficient for most scenarios. It is possible that they
- failed to do it though (@sa init_dynamic_array2()); if that is the case,
- we prefer to fail.
- Even if the check is successful, it can happen that the preallocated cells
- are later exhausted, the array is re-allocated, which hits OOM, then we
- will indeed crash :-/
- */
- if (stack_of_current_stmts.elements() == 0 &&
- all_stmts_for_I_S.elements() == 0 &&
- all_stmts_to_del.elements() == 0)
- {
- /*
- About the if() above: it's sufficient to run this check right after
- construction, not all the time.
- */
- if (!stack_of_current_stmts.guaranteed_room(array_min_room) ||
- !all_stmts_for_I_S.guaranteed_room(array_min_room) ||
- !all_stmts_to_del.guaranteed_room(array_min_room))
- return false;
- }
- return true;
-}
-
-
bool Opt_trace_context::start(enum enum_support_I_S support_I_S_arg,
bool end_marker_arg, bool one_line_arg,
long offset_arg, long limit_arg,
@@ -1090,8 +1066,6 @@ bool Opt_trace_context::start(enum enum_
bool rc;
DBUG_ENTER("Opt_trace_context::start");
- if (unlikely(!well_constructed()))
- DBUG_RETURN(true);
/*
Tracing may already be started when we come here, for example if we are
starting execution of a sub-statement of a stored routine (CALL has
@@ -1153,12 +1127,21 @@ bool Opt_trace_context::start(enum enum_
});
if (unlikely(stmt == NULL))
+ {
+ /*
+ Usually OOM is reported by setting
+ Opt_trace_stmt::trace_buffer::malloc_error but here there is not even an
+ Opt_trace_stmt.
+ */
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(Opt_trace_stmt));
goto err;
- if (unlikely(!stmt->well_constructed()))
- goto err;
+ }
if (unlikely(stack_of_current_stmts.append(current_stmt_in_gen)))
- goto err;
+ {
+ // append() above called my_error()
+ goto err;
+ }
if (new_stmt_support_I_S == YES_FOR_THIS)
rc= all_stmts_for_I_S.append(stmt);
@@ -1178,11 +1161,9 @@ bool Opt_trace_context::start(enum enum_
current_stmt_in_gen= stmt;
- /*
- As we just added one trace, maybe the previous ones are unneeded now
- */
+ // As we just added one trace, maybe the previous ones are unneeded now
purge_stmts(false);
- /* This purge may have freed space, compute max allowed size: */
+ // This purge may have freed space, compute max allowed size:
stmt->set_allowed_mem_size(allowed_mem_size_for_current_stmt());
DBUG_RETURN(false);
err:
@@ -1256,10 +1237,25 @@ void Opt_trace_context::purge_stmts(bool
}
else
{
- /* Remember to free it (as in @c free()) when possible... */
- all_stmts_to_del.append(all_stmts_for_I_S.at(idx));
- /* ... For now, make it invisible in OPTIMIZER_TRACE tabl */
- all_stmts_for_I_S.del(idx);
+ /*
+ Remember to free it (as in @c free()) when possible. For now, make it
+ invisible in OPTIMIZER_TRACE table.
+ */
+#ifndef DBUG_OFF
+ if (!Opt_trace_context::simulate_oom_in_purge)
+#endif
+ {
+ if (likely(!all_stmts_to_del.append(all_stmts_for_I_S.at(idx))))
+ all_stmts_for_I_S.del(idx);
+ else
+ {
+ /*
+ OOM. Cannot purge. Which at worse should only break the
+ offset/limit feature (the trace will accidentally still show up in
+ the OPTIMIZER_TRACE table). append() above has called my_error().
+ */
+ }
+ }
}
}
/* Examine list of "to be freed" traces and free what can be */
@@ -1398,7 +1394,8 @@ Opt_trace_disable_I_S::Opt_trace_disable
{
stmt= ctx_arg->get_current_stmt_in_gen();
if (stmt != NULL)
- stmt->disable_I_S_for_this_and_children();
+ if (unlikely(stmt->disable_I_S_for_this_and_children()))
+ disable= false; // failed to disable: be a dummy object
}
else
stmt= NULL;
=== modified file 'sql/opt_trace.h'
--- a/sql/opt_trace.h 2011-02-15 20:53:19 +0000
+++ b/sql/opt_trace.h 2011-02-28 15:51:31 +0000
@@ -519,7 +519,13 @@ public:
{ return features & f; }
/// Turn this on only in unit tests for out-of-memory testing
- static bool simulate_oom_in_buffers;
+ static bool simulate_oom_in_buffers, simulate_oom_in_open_struct,
+ simulate_oom_in_purge;
+ /**
+ Whether a JSON syntax error should cause an assertion in debug binaries;
+ turn this off only in unit tests for bad syntax testing
+ */
+ static bool dbug_assert_on_syntax_error;
/**
Opt_trace_struct is passed Opt_trace_context*, and needs to know
@@ -613,9 +619,6 @@ private:
*/
long since_offset_0;
- /// @returns whether the constructor went perfectly well.
- bool well_constructed() const;
-
/**
Find and delete unneeded traces.
For example if OFFSET=-1,LIMIT=1, only the last trace is needed. When a
@@ -966,12 +969,6 @@ public:
*/
void end() { if (unlikely(started)) do_destruct(); }
- /**
- Whether a JSON syntax error should cause an assertion in debug binaries;
- turn this off only in unit tests for bad syntax testing
- */
- static bool dbug_assert_on_syntax_error;
-
private:
/// Full initialization. @sa Opt_trace_struct::Opt_trace_struct
=== modified file 'sql/opt_trace2server.cc'
--- a/sql/opt_trace2server.cc 2011-02-19 12:40:31 +0000
+++ b/sql/opt_trace2server.cc 2011-02-28 15:51:31 +0000
@@ -182,7 +182,10 @@ bool opt_trace_start(THD *thd, const TAB
{
// As we use gcc and not g++, this new() behaves as new(std::nothrow).
if ((thd->opt_trace= new Opt_trace_context) == NULL)
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(Opt_trace_context));
DBUG_RETURN(false);
+ }
allocated_here= true;
}
=== modified file 'sql/sql_array.h'
--- a/sql/sql_array.h 2011-02-16 15:06:06 +0000
+++ b/sql/sql_array.h 2011-02-28 15:51:31 +0000
@@ -60,6 +60,10 @@ public:
return ((Elem*)array.buffer) + array.elements;
}
+ /**
+ @retval false ok
+ @retval true OOM, @c my_error() has been called.
+ */
bool append(const Elem &el)
{
return insert_dynamic(&array, &el);
@@ -84,17 +88,6 @@ public:
{
array.elements= 0;
}
- /**
- @returns whether the array guarantees that it contains free cells already
- successfully allocated from memory.
- @param required caller requires this amount of free allocated cells
- Caller can use this to make sure that its @c append() calls will succeed,
- not fail due to out-of-memory.
- */
- bool guaranteed_room(uint required) const
- {
- return (array.max_element - array.elements) >= required;
- }
~Dynamic_array()
{
=== modified file 'unittest/gunit/opt_trace-t.cc'
--- a/unittest/gunit/opt_trace-t.cc 2011-02-15 20:53:19 +0000
+++ b/unittest/gunit/opt_trace-t.cc 2011-02-28 15:51:31 +0000
@@ -256,9 +256,9 @@ TEST_F(TraceContentTest, BuggyObject)
ota.add(200.4);
{
Opt_trace_object oto1(&trace);
- Opt_trace_struct::dbug_assert_on_syntax_error= false;
+ Opt_trace_context::dbug_assert_on_syntax_error= false;
oto1.add_alnum("one value"); // no key, which is wrong
- Opt_trace_struct::dbug_assert_on_syntax_error= true;
+ Opt_trace_context::dbug_assert_on_syntax_error= true;
}
ota.add_alnum("one string element");
ota.add(true);
@@ -310,9 +310,9 @@ TEST_F(TraceContentTest, BuggyArray)
Opt_trace_object oto(&trace);
{
Opt_trace_array ota(&trace, "one array");
- Opt_trace_struct::dbug_assert_on_syntax_error= false;
+ Opt_trace_context::dbug_assert_on_syntax_error= false;
ota.add("superfluous key", 200.4); // key, which is wrong
- Opt_trace_struct::dbug_assert_on_syntax_error= true;
+ Opt_trace_context::dbug_assert_on_syntax_error= true;
}
oto.add("yet another key", -1000LL);
{
@@ -443,7 +443,7 @@ void make_one_trace(Opt_trace_context *t
*/
#define check(trace, names) { SCOPED_TRACE(""); do_check(&trace, names); }
-void do_check(Opt_trace_context *trace, const char**names)
+void do_check(Opt_trace_context *trace, const char **names)
{
Opt_trace_iterator it(trace);
Opt_trace_info info;
@@ -622,9 +622,9 @@ TEST(TraceSettingsTest, MaxMemSize2)
}
-/** Test reaction to out-of-memory condition */
#ifndef DBUG_OFF
-TEST_F(TraceContentTest, OutOfMemory)
+/// Test reaction to out-of-memory condition in trace buffer
+TEST_F(TraceContentTest, OOMinBuffer)
{
ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -1, 1, ULONG_MAX,
all_features));
@@ -655,6 +655,104 @@ TEST_F(TraceContentTest, OutOfMemory)
it.next();
ASSERT_TRUE(it.at_end());
}
+
+
+/// Test reaction to out-of-memory condition in book-keeping data structures
+TEST_F(TraceContentTest, OOMinBookKeeping1)
+{
+ ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -1, 1, ULONG_MAX,
+ all_features));
+ {
+ Opt_trace_object oto(&trace);
+ {
+ Opt_trace_array ota(&trace, "one array");
+ Opt_trace_context::simulate_oom_in_open_struct= true;
+ Opt_trace_object oto1(&trace);
+ Opt_trace_context::simulate_oom_in_open_struct= false;
+ }
+ }
+ trace.end();
+ Opt_trace_iterator it(&trace);
+ ASSERT_FALSE(it.at_end());
+ Opt_trace_info info;
+ it.get_value(&info);
+ const char expected[]=
+ "{\n"
+ " \"one array\": [\n"
+ " {\n" // there is only '{' for this phantom object
+ " ]\n"
+ "}";
+ EXPECT_STREQ(expected, info.trace_ptr);
+ EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
+ EXPECT_EQ(0U, info.missing_bytes);
+ EXPECT_EQ(true, info.malloc_error); // malloc error reported
+ it.next();
+ ASSERT_TRUE(it.at_end());
+}
+
+
+/// Same as OOMinBookKeeping1 but when creating object for disabled feature
+TEST_F(TraceContentTest, OOMinBookKeeping2)
+{
+ ASSERT_EQ(false, trace.start(YES_FOR_THIS, false, false, -1, 1, ULONG_MAX,
+ Opt_trace_context::MISC));
+ {
+ Opt_trace_object oto(&trace);
+ {
+ Opt_trace_array ota(&trace, "one array");
+ Opt_trace_context::simulate_oom_in_open_struct= true;
+ Opt_trace_object oto1(&trace, Opt_trace_context::GREEDY_SEARCH);
+ Opt_trace_context::simulate_oom_in_open_struct= false;
+ /*
+ Assume that now there is free memory. Because oto1 was changed to be
+ dummy, oto2 will be in the wrong context (as element of 'ota'),
+ causing a syntax error.
+ */
+ Opt_trace_context::dbug_assert_on_syntax_error= false;
+ Opt_trace_object oto2(&trace, "memory_is_back");
+ Opt_trace_context::dbug_assert_on_syntax_error= true;
+ }
+ }
+ trace.end();
+ Opt_trace_iterator it(&trace);
+ ASSERT_FALSE(it.at_end());
+ Opt_trace_info info;
+ it.get_value(&info);
+ const char expected[]=
+ "{\n"
+ " \"one array\": [\n"
+ " \"...\"** invalid JSON (unexpected key \"memory_is_back\") ** ,\n"
+ " {\n" // there is only '{' for this phantom object
+ " }\n"
+ " ]\n"
+ "}";
+ EXPECT_STREQ(expected, info.trace_ptr);
+ EXPECT_EQ(sizeof(expected) - 1, info.trace_length);
+ EXPECT_EQ(0U, info.missing_bytes);
+ EXPECT_EQ(true, info.malloc_error); // malloc error reported
+ it.next();
+ ASSERT_TRUE(it.at_end());
+}
+
+
+/// Test reaction to OOM when purging traces
+TEST_F(TraceContentTest, OOMinPurge)
+{
+ Opt_trace_context trace;
+ make_one_trace(&trace, "103", -3, 2);
+ make_one_trace(&trace, "104", -3, 2);
+ Opt_trace_context::simulate_oom_in_purge= true;
+ make_one_trace(&trace, "105", -3, 2);
+ make_one_trace(&trace, "106", -3, 2);
+ make_one_trace(&trace, "107", -3, 2);
+ make_one_trace(&trace, "108", -3, 2);
+ make_one_trace(&trace, "109", -3, 2);
+ Opt_trace_context::simulate_oom_in_purge= false;
+ // The two first could not be purged so are still shown:
+ const char *expected_traces3[]= {"103", "104", NULL};
+ check(trace, expected_traces3);
+}
+
#endif // !DBUG_OFF
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (guilhem.bichot:3268 to 3269) | Guilhem Bichot | 28 Feb |