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