List:Commits« Previous MessageNext Message »
From:Guilhem Bichot Date:February 28 2011 3:52pm
Subject:bzr push into mysql-trunk branch (guilhem.bichot:3268 to 3269)
View as plain text  
 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 Bichot28 Feb