1 /* Copyright (c) 2011, 2012, 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 #ifndef OPT_TRACE_CONTEXT_INCLUDED
24 #define OPT_TRACE_CONTEXT_INCLUDED
25 
26 #include "my_config.h"  // OPTIMIZER_TRACE
27 #include "sql_array.h"  // Dynamic_array
28 
29 /**
30    @file
31    This contains the declaration of class Opt_trace_context, which is needed
32    to declare THD.
33    It is recommend to read opt_trace.h first.
34 */
35 
36 #ifdef OPTIMIZER_TRACE
37 
38 class Opt_trace_stmt;           // implementation detail local to opt_trace.cc
39 
40 
41 /**
42   @class Opt_trace_context
43 
44   A per-session context which is always available at any point of execution,
45   because in practice it's accessible from THD which contains:
46   @verbatim Opt_trace_context opt_trace; @endverbatim
47   It maintains properties of the session's regarding tracing: enabled/disabled
48   state, style (all trace on one line, or not, etc), a list of all remembered
49   traces of previous and current SQL statement (as restricted by
50   OFFSET/LIMIT), and a pointer to the current (being-generated) trace (which
51   itself has a pointer to its current open object/array).
52 
53   Here is why the context needs to provide the current open object/array:
54 
55   @li When adding a value (scalar or array or object) to an array, or adding a
56   key/value pair to an object, we need this outer object or array (from now
57   on, we will use the term "structure" for "object or array", as both are
58   structured types).
59 
60   @li The most natural way would be that the outer object would be passed in
61   argument to the adder (the function which wants to add the value or
62   key/value).
63 
64   @li But tracing is sometimes produced from deep down the stack trace, with
65   many intermediate frames doing no tracing (writing nothing to the trace), so
66   it would require passing the outer structure through many levels, thus
67   modifying many function prototypes.
68   Example (in gdb "backtrace" notation: inner frames first):
69 @verbatim
70     #0  Item_in_subselect::single_value_transformer
71         - opens an object for key "transformation"
72     #1  Item_in_subselect::select_in_like_transformer - does no tracing
73     #2  Item_allany_subselect::select_transformer - does no tracing
74     #3  JOIN::prepare - opens an object for key "join_preparation"
75 @endverbatim
76   So the object opened in #3 would have to be passed in argument to #2 and #1
77   in order to finally reach #0 where object "transformation" would be added to
78   it.
79 
80   @li So, as we cannot practically pass the object down, we rather maintain a
81   "current object or array" accessible from the Opt_trace_context context;
82   it's a pointer to an instance of Opt_trace_struct, and the function deep
83   down (frame #0) grabs it from the context, where it was depositted by the
84   function high up (frame #3 in the last example).
85 */
86 
87 class Opt_trace_context
88 {
89 public:
90 
Opt_trace_context()91   Opt_trace_context() : pimpl(NULL), I_S_disabled(0) {}
92   ~Opt_trace_context();
93 
94   /**
95      Starts a new trace.
96      @param  support_I_S      Whether this statement should have its trace in
97                               information_schema
98      @param  support_dbug_or_missing_priv  'true' if this statement
99                               should have its trace in the dbug log (--debug),
100                               or if missing_privilege() may be called on this
101                               trace
102      @param  end_marker       For a key/(object|array) pair, should the key be
103                               repeated in a comment when the object|array
104                               closes? like
105 @verbatim
106                               "key_foo": {
107                                            multi-line blah
108                                          } / * key_foo * /
109 @endverbatim
110                               This is for human-readability only, not valid in
111                               JSON. Note that YAML supports #-prefixed
112                               comments (we would just need to put the next
113                               item's "," before the current item's "#").
114      @param  one_line         Should the trace be on a single line without
115                               indentation? (More compact for network transfer
116                               to programs, less human-readable.)
117      @param  offset           Offset for restricting trace production.
118      @param  limit            Limit for restricting trace production.
119      @param  max_mem_size     Maximum allowed for cumulated size of all
120                               remembered traces.
121      @param  features         Only these optimizer features should be traced.
122 
123      @retval false            ok
124      @retval true             error (OOM): instance is unusable, so only
125                               destructor is permitted on it; any other
126                               member function has undefined effects.
127   */
128   bool start(bool support_I_S,
129              bool support_dbug_or_missing_priv,
130              bool end_marker, bool one_line,
131              long offset, long limit, ulong max_mem_size,
132              ulonglong features);
133 
134   /**
135     Ends the current (=open, unfinished, being-generated) trace.
136 
137     If @c missing_privilege() has been called between start() and end(),
138     end() restores I_S support to what it was before the call to
139     @c missing_privilege(). This is the key to ensure that missing_privilege()
140     does not disable I_S support for the rest of the connection's life!
141   */
142   void end();
143 
144   /// Returns whether there is a current trace
is_started()145   bool is_started() const
146   { return unlikely(pimpl != NULL) && pimpl->current_stmt_in_gen != NULL; }
147 
148   /**
149      @returns whether the current trace writes to I_S.
150      This function should rarely be used. Don't you use this for some clever
151      optimizations bypassing opt trace!
152   */
153   bool support_I_S() const;
154 
155   /**
156      Set the "original" query (not transformed, as sent by client) for the
157      current trace.
158      @param   query    query
159      @param   length   query's length
160      @param   charset  charset which was used to encode this query
161   */
162   void set_query(const char* query, size_t length,
163                  const CHARSET_INFO *charset);
164 
165   /**
166      Brainwash: deletes all remembered traces and resets counters regarding
167      OFFSET/LIMIT (so that the next statement is considered as "at offset
168      0"). Does not reset the @@@@optimizer_trace_offset/limit variables.
169   */
170   void reset();
171 
172   /// @sa parameters of Opt_trace_context::start()
get_end_marker()173   bool get_end_marker() const { return pimpl->end_marker; }
174   /// @sa parameters of Opt_trace_context::start()
get_one_line()175   bool get_one_line() const { return pimpl->one_line; }
176 
177   /**
178      Names of flags for @@@@optimizer_trace variable of @c sys_vars.cc :
179      @li "enabled" = tracing enabled
180      @li "one_line"= see parameter of @ref Opt_trace_context::start
181      @li "default".
182   */
183   static const char *flag_names[];
184 
185   /** Flags' numeric values for @@@@optimizer_trace variable */
186   enum {
187     FLAG_DEFAULT=    0,
188     FLAG_ENABLED=    1 << 0,
189     FLAG_ONE_LINE=   1 << 1
190   };
191 
192   /**
193      Features' names for @@@@optimizer_trace_features variable of
194      @c sys_vars.cc:
195      @li "greedy_search" = the greedy search for a plan
196      @li "range_optimizer" = the cost analysis of accessing data through
197      ranges in indices
198      @li "dynamic_range" = the range optimization performed for each record
199                            when access method is dynamic range
200      @li "repeated_subselect" = the repeated execution of subselects
201      @li "default".
202   */
203   static const char *feature_names[];
204 
205   /** Features' numeric values for @@@@optimizer_trace_features variable */
206   enum feature_value {
207     GREEDY_SEARCH=      1 << 0,
208     RANGE_OPTIMIZER=    1 << 1,
209     DYNAMIC_RANGE=      1 << 2,
210     REPEATED_SUBSELECT= 1 << 3,
211     /*
212       If you add here, update feature_value of empty implementation
213       and default_features!
214     */
215     /**
216        Anything unclassified, including the top object (thus, by "inheritance
217        from parent", disabling MISC makes an empty trace).
218        This feature cannot be disabled by the user; for this it is important
219        that it always has biggest flag; flag's value itself does not matter.
220     */
221     MISC=               1 << 7
222   };
223 
224   /**
225      User lacks privileges to see the current trace. Make the trace appear
226      empty in Opt_trace_info, and disable I_S for all its upcoming children.
227 
228      Once a call to this function has been made, subsequent calls to it before
229      @c end() have no effects.
230   */
231   void missing_privilege();
232 
233   /// Optimizer features which are traced by default.
234   static const feature_value default_features;
235 
236   /**
237      @returns whether an optimizer feature should be traced.
238      @param  f  feature
239   */
feature_enabled(feature_value f)240   bool feature_enabled (feature_value f) const
241   { return unlikely(pimpl != NULL) && (pimpl->features & f); }
242 
243   /**
244      Opt_trace_struct is passed Opt_trace_context*, and needs to know
245      to which statement's trace to attach, so Opt_trace_context must provide
246      this information.
247   */
get_current_stmt_in_gen()248   Opt_trace_stmt *get_current_stmt_in_gen()
249   { return pimpl->current_stmt_in_gen; }
250 
251   /**
252      @returns the next statement to show in I_S.
253      @param[in,out]  got_so_far  How many statements the caller got so far
254      (by previous calls to this function); function updates this count.
255      @note traces are returned from oldest to newest.
256    */
257   const Opt_trace_stmt *get_next_stmt_for_I_S(long *got_so_far) const;
258 
259   /// Temporarily disables I_S for this trace and its children.
disable_I_S_for_this_and_children()260   void disable_I_S_for_this_and_children()
261   {
262     ++I_S_disabled;
263     if (unlikely(pimpl != NULL))
264       pimpl->disable_I_S_for_this_and_children();
265   }
266 
267   /**
268      Restores I_S support to what it was before the previous call to
269      disable_I_S_for_this_and_children().
270   */
restore_I_S()271   void restore_I_S()
272   {
273     --I_S_disabled;
274     DBUG_ASSERT(I_S_disabled >= 0);
275     if (unlikely(pimpl != NULL))
276       pimpl->restore_I_S();
277   }
278 
279 private:
280 
281   /**
282      To have the smallest impact on THD's size, most of the implementation is
283      moved to a separate class Opt_trace_context_impl which is instantiated on
284      the heap when really needed. So if a connection never sets
285      @@@@optimizer_trace to "enabled=on" and does not use --debug, this heap
286      allocation never happens.
287      This class is declared here so that frequently called functions like
288      Opt_trace_context::is_started() can be inlined.
289   */
290   class Opt_trace_context_impl
291   {
292   public:
Opt_trace_context_impl()293     Opt_trace_context_impl() : current_stmt_in_gen(NULL),
294       features(feature_value(0)), offset(0), limit(0), since_offset_0(0)
295     {}
296 
297     void disable_I_S_for_this_and_children();
298     void restore_I_S();
299 
300     /**
301        Trace which is currently being generated, where structures are being
302        added. "in_gen" stands for "in generation", being-generated.
303 
304        In simple cases it is equal to the last element of array
305        all_stmts_for_I_S. But it can be prior to it, for example when calling
306        a stored routine:
307 @verbatim
308        CALL statement starts executing
309          create trace of CALL (call it "trace #1")
310          add structure to trace #1
311          add structure to trace #1
312          First sub-statement executing
313            create trace of sub-statement (call it "trace #2")
314            add structure to trace #2
315            add structure to trace #2
316          First sub-statement ends
317          add structure to trace #1
318 @endverbatim
319        In the beginning, the CALL statement's trace is the newest and current;
320        when the first sub-statement is executing, that sub-statement's trace
321        is the newest and current; when the first sub-statement ends, it is
322        still the newest but it's not the current anymore: the current is then
323        again the CALL's one, where structures will be added, until a second
324        sub-statement is executed.
325        Another case is when the current statement sends only to DBUG:
326        all_stmts_for_I_S lists only traces shown in OPTIMIZER_TRACE.
327     */
328     Opt_trace_stmt *current_stmt_in_gen;
329 
330     /**
331        To keep track of what is the current statement, as execution goes into
332        a sub-statement, and back to the upper statement, we have a stack of
333        successive values of current_stmt_in_gen:
334        when in a statement we enter a substatement (a new trace), we push the
335        statement's trace on the stack and change current_stmt_in_gen to the
336        substatement's trace; when leaving the substatement we pop from the
337        stack and set current_stmt_in_gen to the popped value.
338     */
339     Dynamic_array<Opt_trace_stmt *> stack_of_current_stmts;
340 
341     /**
342        List of remembered traces for putting into the OPTIMIZER_TRACE
343        table. Element 0 is the one created first, will be first row of
344        OPTIMIZER_TRACE table. The array structure fullfills those needs:
345        - to output traces "oldest first" in OPTIMIZER_TRACE
346        - to preserve traces "newest first" when @@@@optimizer_trace_offset<0
347        - to delete a trace in the middle of the list when it is permanently
348        out of the offset/limit showable window.
349     */
350     Dynamic_array<Opt_trace_stmt *> all_stmts_for_I_S;
351     /**
352        List of traces which are unneeded because of OFFSET/LIMIT, and
353        scheduled for deletion from memory.
354     */
355     Dynamic_array<Opt_trace_stmt *> all_stmts_to_del;
356 
357     bool end_marker;        ///< copy of parameter of Opt_trace_context::start
358     bool one_line;
359     feature_value features;
360     long offset;
361     long limit;
362     size_t max_mem_size;
363 
364     /**
365        Number of statements traced so far since "offset 0", for comparison
366        with OFFSET and LIMIT, when OFFSET >= 0.
367     */
368     long since_offset_0;
369   };
370 
371   Opt_trace_context_impl *pimpl;     /// Dynamically allocated implementation.
372 
373   /**
374     <>0 <=> any to-be-created statement's trace should not be in
375     information_schema. This applies to next statements, their substatements,
376     etc.
377   */
378   int I_S_disabled;
379 
380   /**
381      Find and delete unneeded traces.
382      For example if OFFSET=-1,LIMIT=1, only the last trace is needed. When a
383      new trace is started, the previous traces becomes unneeded and this
384      function deletes them which frees memory.
385      @param  purge_all  if true, ignore OFFSET and thus delete everything
386   */
387   void purge_stmts(bool purge_all);
388 
389   /**
390      Compute maximum allowed memory size for current trace. The current trace
391      is the only one active. Other traces break down in two groups:
392      - the finished ones (from previously executed statements),
393      - the "started but not finished ones": they are not current, are not
394      being updated at this moment: this must be the trace of a top
395      statement calling a substatement which is the current trace now: trace's
396      top statement is not being updated at this moment.
397      So the current trace can grow in the room left by all traces above.
398   */
399   size_t allowed_mem_size_for_current_stmt() const;
400 
401   /// Not defined copy constructor, to disallow copy.
402   Opt_trace_context(const Opt_trace_context&);
403   /// Not defined assignment operator, to disallow assignment.
404   Opt_trace_context& operator=(const Opt_trace_context&);
405 };
406 
407 #else /* OPTIMIZER_TRACE */
408 
409 /** Empty implementation used when optimizer trace is not compiled in */
410 class Opt_trace_context
411 {
412 public:
413   /// We need those enums even if tracing is disabled
414   enum feature_value {
415     GREEDY_SEARCH=      1 << 0,
416     RANGE_OPTIMIZER=    1 << 1,
417     DYNAMIC_RANGE=      1 << 2,
418     REPEATED_SUBSELECT= 1 << 3,
419     MISC=               1 << 7
420   };
421   enum {
422     FLAG_DEFAULT=    0,
423     FLAG_ENABLED=    1 << 0,
424     FLAG_ONE_LINE=   1 << 1
425   };
is_started()426   static bool is_started() { return false; }
427 };
428 
429 #endif /* OPTIMIZER_TRACE */
430 
431 #endif /* OPT_TRACE_CONTEXT_INCLUDED */
432