1 /* Copyright (c) 2007, 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
26 Implement query profiling as as list of metaphorical fences, with one fence
27 per query, and each fencepost a change of thd->proc_info state (with a
28 snapshot of system statistics). When asked, we can then iterate over the
29 fenceposts and calculate the distance between them, to inform the user what
30 happened during a particular query or thd->proc_info state.
31
32 User variables that inform profiling behavior:
33 - "profiling", boolean, session only, "Are queries profiled?"
34 - "profiling_history_size", integer, session + global, "Num queries stored?"
35 */
36
37 #include "sql/sql_profile.h"
38
39 #include "my_config.h"
40
41 #include <string.h>
42 #include <algorithm>
43
44 #include "decimal.h"
45 #include "m_ctype.h"
46 #include "m_string.h"
47 #include "my_base.h"
48 #include "my_compiler.h"
49 #include "my_dbug.h"
50 #include "my_sqlcommand.h"
51 #include "my_sys.h"
52 #include "my_systime.h"
53 #include "sql/field.h"
54 #include "sql/item.h"
55 #include "sql/my_decimal.h"
56 #include "sql/protocol.h"
57 #include "sql/psi_memory_key.h"
58 #include "sql/query_options.h"
59 #include "sql/sql_class.h" // THD
60 #include "sql/sql_error.h"
61 #include "sql/sql_lex.h"
62 #include "sql/sql_list.h"
63 #include "sql/sql_show.h" // schema_table_store_record
64 #include "sql/system_variables.h"
65 #include "sql_string.h"
66
67 using std::max;
68 using std::min;
69
70 #define TIME_FLOAT_DIGITS 9
71 /** two vals encoded: (dec*100)+len */
72 #define TIME_I_S_DECIMAL_SIZE \
73 (TIME_FLOAT_DIGITS * 100) + (TIME_FLOAT_DIGITS - 3)
74
75 static const size_t MAX_QUERY_LENGTH = 300;
76 #define MAX_QUERY_HISTORY 101U
77
78 /**
79 Connects Information_Schema and Profiling.
80 */
fill_query_profile_statistics_info(THD * thd MY_ATTRIBUTE ((unused)),TABLE_LIST * tables MY_ATTRIBUTE ((unused)),Item *)81 int fill_query_profile_statistics_info(
82 THD *thd MY_ATTRIBUTE((unused)), TABLE_LIST *tables MY_ATTRIBUTE((unused)),
83 Item *) {
84 #if defined(ENABLED_PROFILING)
85 const char *old = thd->lex->sql_command == SQLCOM_SHOW_PROFILE
86 ? "SHOW PROFILE"
87 : "INFORMATION_SCHEMA.PROFILING";
88
89 DBUG_ASSERT(thd->lex->sql_command != SQLCOM_SHOW_PROFILES);
90
91 push_deprecated_warn(thd, old, "Performance Schema");
92 return (thd->profiling->fill_statistics_info(thd, tables));
93 #else
94 my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling");
95 return (1);
96 #endif
97 }
98
99 ST_FIELD_INFO query_profile_statistics_info[] = {
100 /* name, length, type, value, maybe_null, old_name, open_method */
101 {"QUERY_ID", 20, MYSQL_TYPE_LONG, 0, false, "Query_id", 0},
102 {"SEQ", 20, MYSQL_TYPE_LONG, 0, false, "Seq", 0},
103 {"STATE", 30, MYSQL_TYPE_STRING, 0, false, "Status", 0},
104 {"DURATION", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, false,
105 "Duration", 0},
106 {"CPU_USER", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_user",
107 0},
108 {"CPU_SYSTEM", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true,
109 "CPU_system", 0},
110 {"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_voluntary", 0},
111 {"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_involuntary",
112 0},
113 {"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_in", 0},
114 {"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_out", 0},
115 {"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0, true, "Messages_sent", 0},
116 {"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0, true, "Messages_received", 0},
117 {"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_major", 0},
118 {"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_minor", 0},
119 {"SWAPS", 20, MYSQL_TYPE_LONG, 0, true, "Swaps", 0},
120 {"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0, true, "Source_function", 0},
121 {"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0, true, "Source_file", 0},
122 {"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0, true, "Source_line", 0},
123 {nullptr, 0, MYSQL_TYPE_STRING, 0, true, nullptr, 0}};
124
make_profile_table_for_show(THD * thd,ST_SCHEMA_TABLE * schema_table)125 int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table) {
126 uint profile_options = thd->lex->profile_options;
127 uint fields_include_condition_truth_values[] = {
128 false, /* Query_id */
129 false, /* Seq */
130 true, /* Status */
131 true, /* Duration */
132 profile_options & PROFILE_CPU, /* CPU_user */
133 profile_options & PROFILE_CPU, /* CPU_system */
134 profile_options & PROFILE_CONTEXT, /* Context_voluntary */
135 profile_options & PROFILE_CONTEXT, /* Context_involuntary */
136 profile_options & PROFILE_BLOCK_IO, /* Block_ops_in */
137 profile_options & PROFILE_BLOCK_IO, /* Block_ops_out */
138 profile_options & PROFILE_IPC, /* Messages_sent */
139 profile_options & PROFILE_IPC, /* Messages_received */
140 profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_major */
141 profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_minor */
142 profile_options & PROFILE_SWAPS, /* Swaps */
143 profile_options & PROFILE_SOURCE, /* Source_function */
144 profile_options & PROFILE_SOURCE, /* Source_file */
145 profile_options & PROFILE_SOURCE, /* Source_line */
146 };
147
148 ST_FIELD_INFO *field_info;
149 Name_resolution_context *context = &thd->lex->select_lex->context;
150 int i;
151
152 for (i = 0; schema_table->fields_info[i].field_name != nullptr; i++) {
153 if (!fields_include_condition_truth_values[i]) continue;
154
155 field_info = &schema_table->fields_info[i];
156 Item_field *field =
157 new Item_field(context, NullS, NullS, field_info->field_name);
158 if (field) {
159 field->item_name.copy(field_info->old_name);
160 if (add_item_to_list(thd, field)) return 1;
161 }
162 }
163 return 0;
164 }
165
166 #if defined(ENABLED_PROFILING)
167
168 #define RUSAGE_USEC(tv) ((tv).tv_sec * 1000 * 1000 + (tv).tv_usec)
169 #define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1)) - RUSAGE_USEC((tv2)))
170
171 #ifdef _WIN32
FileTimeToQuadWord(FILETIME * ft)172 static ULONGLONG FileTimeToQuadWord(FILETIME *ft) {
173 // Overlay FILETIME onto a ULONGLONG.
174 union {
175 ULONGLONG qwTime;
176 FILETIME ft;
177 } u;
178
179 u.ft = *ft;
180 return u.qwTime;
181 }
182
183 // Get time difference between to FILETIME objects in seconds.
GetTimeDiffInSeconds(FILETIME * a,FILETIME * b)184 static double GetTimeDiffInSeconds(FILETIME *a, FILETIME *b) {
185 return ((FileTimeToQuadWord(a) - FileTimeToQuadWord(b)) / 1e7);
186 }
187 #endif
188
PROF_MEASUREMENT(QUERY_PROFILE * profile_arg,const char * status_arg)189 PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg,
190 const char *status_arg)
191 : profile(profile_arg) {
192 collect();
193 set_label(status_arg, nullptr, nullptr, 0);
194 }
195
PROF_MEASUREMENT(QUERY_PROFILE * profile_arg,const char * status_arg,const char * function_arg,const char * file_arg,unsigned int line_arg)196 PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg,
197 const char *status_arg,
198 const char *function_arg,
199 const char *file_arg, unsigned int line_arg)
200 : profile(profile_arg) {
201 collect();
202 set_label(status_arg, function_arg, file_arg, line_arg);
203 }
204
~PROF_MEASUREMENT()205 PROF_MEASUREMENT::~PROF_MEASUREMENT() {
206 my_free(allocated_status_memory);
207 status = function = file = nullptr;
208 }
209
set_label(const char * status_arg,const char * function_arg,const char * file_arg,unsigned int line_arg)210 void PROF_MEASUREMENT::set_label(const char *status_arg,
211 const char *function_arg, const char *file_arg,
212 unsigned int line_arg) {
213 size_t sizes[3]; /* 3 == status+function+file */
214 char *cursor;
215
216 /*
217 Compute all the space we'll need to allocate one block for everything
218 we'll need, instead of N mallocs.
219 */
220 sizes[0] = (status_arg == nullptr) ? 0 : strlen(status_arg) + 1;
221 sizes[1] = (function_arg == nullptr) ? 0 : strlen(function_arg) + 1;
222 sizes[2] = (file_arg == nullptr) ? 0 : strlen(file_arg) + 1;
223
224 allocated_status_memory = (char *)my_malloc(
225 key_memory_PROFILE, sizes[0] + sizes[1] + sizes[2], MYF(0));
226 DBUG_ASSERT(allocated_status_memory != nullptr);
227
228 cursor = allocated_status_memory;
229
230 if (status_arg != nullptr) {
231 strcpy(cursor, status_arg);
232 status = cursor;
233 cursor += sizes[0];
234 } else
235 status = nullptr;
236
237 if (function_arg != nullptr) {
238 strcpy(cursor, function_arg);
239 function = cursor;
240 cursor += sizes[1];
241 } else
242 function = nullptr;
243
244 if (file_arg != nullptr) {
245 strcpy(cursor, file_arg);
246 file = cursor;
247 cursor += sizes[2];
248 } else
249 file = nullptr;
250
251 line = line_arg;
252 }
253
254 /**
255 This updates the statistics for this moment of time. It captures the state
256 of the running system, so later we can compare points in time and infer what
257 happened in the mean time. It should only be called immediately upon
258 instantiation of this PROF_MEASUREMENT.
259
260 @todo Implement resource capture for OSes not like BSD.
261 */
collect()262 void PROF_MEASUREMENT::collect() {
263 time_usecs = (double)my_getsystime() / 10.0; /* 1 sec was 1e7, now is 1e6 */
264 #ifdef HAVE_GETRUSAGE
265 getrusage(RUSAGE_SELF, &rusage);
266 #elif defined(_WIN32)
267 FILETIME ftDummy;
268 // NOTE: Get{Process|Thread}Times has a granularity of the clock interval,
269 // which is typically ~15ms. So intervals shorter than that will not be
270 // measurable by this function.
271 GetProcessTimes(GetCurrentProcess(), &ftDummy, &ftDummy, &ftKernel, &ftUser);
272 #endif
273 }
274
QUERY_PROFILE(PROFILING * profiling_arg,const char * status_arg)275 QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg)
276 : profiling(profiling_arg),
277 profiling_query_id(0),
278 m_query_source(NULL_STR) {
279 m_seq_counter = 1;
280 PROF_MEASUREMENT *prof = new PROF_MEASUREMENT(this, status_arg);
281 prof->m_seq = m_seq_counter++;
282 m_start_time_usecs = prof->time_usecs;
283 m_end_time_usecs = m_start_time_usecs;
284 entries.push_back(prof);
285 }
286
~QUERY_PROFILE()287 QUERY_PROFILE::~QUERY_PROFILE() {
288 while (!entries.is_empty()) delete entries.pop();
289
290 my_free(m_query_source.str);
291 }
292
293 /**
294 @todo Provide a way to include the full text, as in SHOW PROCESSLIST.
295 */
set_query_source(const char * query_source_arg,size_t query_length_arg)296 void QUERY_PROFILE::set_query_source(const char *query_source_arg,
297 size_t query_length_arg) {
298 /* Truncate to avoid DoS attacks. */
299 size_t length = min(MAX_QUERY_LENGTH, query_length_arg);
300
301 DBUG_ASSERT(m_query_source.str == nullptr); /* we don't leak memory */
302 if (query_source_arg != nullptr) {
303 m_query_source.str =
304 my_strndup(key_memory_PROFILE, query_source_arg, length, MYF(0));
305 m_query_source.length = length;
306 }
307 }
308
new_status(const char * status_arg,const char * function_arg,const char * file_arg,unsigned int line_arg)309 void QUERY_PROFILE::new_status(const char *status_arg, const char *function_arg,
310 const char *file_arg, unsigned int line_arg) {
311 PROF_MEASUREMENT *prof;
312 DBUG_TRACE;
313
314 DBUG_ASSERT(status_arg != nullptr);
315
316 if ((function_arg != nullptr) && (file_arg != nullptr))
317 prof = new PROF_MEASUREMENT(this, status_arg, function_arg,
318 base_name(file_arg), line_arg);
319 else
320 prof = new PROF_MEASUREMENT(this, status_arg);
321
322 prof->m_seq = m_seq_counter++;
323 m_end_time_usecs = prof->time_usecs;
324 entries.push_back(prof);
325
326 /* Maintain the query history size. */
327 while (entries.elements > MAX_QUERY_HISTORY) delete entries.pop();
328 }
329
PROFILING()330 PROFILING::PROFILING()
331 : profile_id_counter(1), current(nullptr), last(nullptr) {}
332
~PROFILING()333 PROFILING::~PROFILING() {
334 while (!history.is_empty()) delete history.pop();
335
336 if (current != nullptr) delete current;
337 }
338
339 /**
340 A new state is given, and that signals the profiler to start a new
341 timed step for the current query's profile.
342
343 @param status_arg name of this step
344 @param function_arg calling function (usually supplied from compiler)
345 @param file_arg calling file (usually supplied from compiler)
346 @param line_arg calling line number (usually supplied from compiler)
347 */
status_change(const char * status_arg,const char * function_arg,const char * file_arg,unsigned int line_arg)348 void PROFILING::status_change(const char *status_arg, const char *function_arg,
349 const char *file_arg, unsigned int line_arg) {
350 DBUG_TRACE;
351
352 if (status_arg == nullptr) /* We don't know how to handle that */
353 return;
354
355 if (current == nullptr) /* This profile was already discarded. */
356 return;
357
358 if (unlikely(enabled))
359 current->new_status(status_arg, function_arg, file_arg, line_arg);
360 }
361
362 /**
363 Prepare to start processing a new query. It is an error to do this
364 if there's a query already in process; nesting is not supported.
365
366 @param initial_state (optional) name of period before first state change
367 */
start_new_query(const char * initial_state)368 void PROFILING::start_new_query(const char *initial_state) {
369 DBUG_TRACE;
370
371 /* This should never happen unless the server is radically altered. */
372 if (unlikely(current != nullptr)) {
373 DBUG_PRINT("warning", ("profiling code was asked to start a new query "
374 "before the old query was finished. This is "
375 "probably a bug."));
376 finish_current_query();
377 }
378
379 enabled = ((thd->variables.option_bits & OPTION_PROFILING) != 0);
380
381 if (!enabled) return;
382
383 DBUG_ASSERT(current == nullptr);
384 current = new QUERY_PROFILE(this, initial_state);
385 }
386
387 /**
388 Throw away the current profile, because it's useless or unwanted
389 or corrupted.
390 */
discard_current_query()391 void PROFILING::discard_current_query() {
392 DBUG_TRACE;
393
394 delete current;
395 current = nullptr;
396 }
397
398 /**
399 Try to save the current profile entry, clean up the data if it shouldn't be
400 saved, and maintain the profile history size. Naturally, this may not
401 succeed if the profile was previously discarded, and that's expected.
402 */
finish_current_query()403 void PROFILING::finish_current_query() {
404 DBUG_TRACE;
405 if (current != nullptr) {
406 /* The last fence-post, so we can support the span before this. */
407 status_change("ending", nullptr, nullptr, 0);
408
409 if ((enabled) && /* ON at start? */
410 ((thd->variables.option_bits & OPTION_PROFILING) !=
411 0) && /* and ON at end? */
412 (current->m_query_source.str != nullptr) &&
413 (!current->entries.is_empty())) {
414 current->profiling_query_id = next_profile_id(); /* assign an id */
415
416 history.push_back(current);
417 last = current; /* never contains something that is not in the history. */
418 current = nullptr;
419 } else {
420 delete current;
421 current = nullptr;
422 }
423 }
424
425 /* Maintain the history size. */
426 while (history.elements > thd->variables.profiling_history_size)
427 delete history.pop();
428 }
429
show_profiles()430 bool PROFILING::show_profiles() {
431 DBUG_TRACE;
432 QUERY_PROFILE *prof;
433 List<Item> field_list;
434
435 field_list.push_back(new Item_return_int("Query_ID", 10, MYSQL_TYPE_LONG));
436 field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS - 1,
437 MYSQL_TYPE_DOUBLE));
438 field_list.push_back(new Item_empty_string("Query", 40));
439
440 if (thd->send_result_metadata(&field_list,
441 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
442 return true;
443
444 SELECT_LEX *sel = thd->lex->select_lex;
445 SELECT_LEX_UNIT *unit = thd->lex->unit;
446 ha_rows idx = 0;
447 Protocol *protocol = thd->get_protocol();
448
449 unit->set_limit(thd, sel);
450
451 void *iterator;
452 for (iterator = history.new_iterator(); iterator != nullptr;
453 iterator = history.iterator_next(iterator)) {
454 prof = history.iterator_value(iterator);
455
456 double query_time_usecs = prof->m_end_time_usecs - prof->m_start_time_usecs;
457
458 if (++idx <= unit->offset_limit_cnt) continue;
459 if (idx > unit->select_limit_cnt) break;
460
461 protocol->start_row();
462 protocol->store((uint32)(prof->profiling_query_id));
463 protocol->store_double(query_time_usecs / (1000.0 * 1000),
464 TIME_FLOAT_DIGITS - 1, 0);
465 if (prof->m_query_source.str != nullptr)
466 protocol->store_string(prof->m_query_source.str,
467 prof->m_query_source.length, system_charset_info);
468 else
469 protocol->store_null();
470
471 if (protocol->end_row()) return true;
472 }
473 my_eof(thd);
474 return false;
475 }
476
477 /**
478 At a point in execution where we know the query source, save the text
479 of it in the query profile.
480
481 This must be called exactly once per descrete statement.
482 */
set_query_source(const char * query_source_arg,size_t query_length_arg)483 void PROFILING::set_query_source(const char *query_source_arg,
484 size_t query_length_arg) {
485 DBUG_TRACE;
486
487 if (!enabled) return;
488
489 if (current != nullptr)
490 current->set_query_source(query_source_arg, query_length_arg);
491 else
492 DBUG_PRINT("info", ("no current profile to send query source to"));
493 }
494
495 /**
496 Fill the information schema table, "query_profile", as defined in show.cc .
497 There are two ways to get to this function: Selecting from the information
498 schema, and a SHOW command.
499 */
fill_statistics_info(THD * thd_arg,TABLE_LIST * tables)500 int PROFILING::fill_statistics_info(THD *thd_arg, TABLE_LIST *tables) {
501 DBUG_TRACE;
502 TABLE *table = tables->table;
503 ulonglong row_number = 0;
504
505 QUERY_PROFILE *query;
506 /* Go through each query in this thread's stored history... */
507 void *history_iterator;
508 for (history_iterator = history.new_iterator(); history_iterator != nullptr;
509 history_iterator = history.iterator_next(history_iterator)) {
510 query = history.iterator_value(history_iterator);
511
512 /*
513 Because we put all profiling info into a table that may be reordered, let
514 us also include a numbering of each state per query. The query_id and
515 the "seq" together are unique.
516 */
517 ulong seq;
518
519 void *entry_iterator;
520 PROF_MEASUREMENT *entry, *previous = nullptr;
521 /* ...and for each query, go through all its state-change steps. */
522 for (entry_iterator = query->entries.new_iterator();
523 entry_iterator != nullptr;
524 entry_iterator = query->entries.iterator_next(entry_iterator),
525 previous = entry, row_number++) {
526 entry = query->entries.iterator_value(entry_iterator);
527 seq = entry->m_seq;
528
529 /* Skip the first. We count spans of fence, not fence-posts. */
530 if (previous == nullptr) continue;
531
532 if (thd_arg->lex->sql_command == SQLCOM_SHOW_PROFILE) {
533 /*
534 We got here via a SHOW command. That means that we stored
535 information about the query we wish to show and that isn't
536 in a WHERE clause at a higher level to filter out rows we
537 wish to exclude.
538
539 Because that functionality isn't available in the server yet,
540 we must filter here, at the wrong level. Once one can con-
541 struct where and having conditions at the SQL layer, then this
542 condition should be ripped out.
543 */
544 if (thd_arg->lex->show_profile_query_id ==
545 0) /* 0 == show final query */
546 {
547 if (query != last) continue;
548 } else {
549 if (thd_arg->lex->show_profile_query_id != query->profiling_query_id)
550 continue;
551 }
552 }
553
554 /* Set default values for this row. */
555 restore_record(table, s->default_values);
556
557 /*
558 The order of these fields is set by the query_profile_statistics_info
559 array.
560 */
561 table->field[0]->store((ulonglong)query->profiling_query_id, true);
562 table->field[1]->store((ulonglong)seq,
563 true); /* the step in the sequence */
564 /*
565 This entry, n, has a point in time, T(n), and a status phrase, S(n).
566 The status phrase S(n) describes the period of time that begins at
567 T(n). The previous status phrase S(n-1) describes the period of time
568 that starts at T(n-1) and ends at T(n). Since we want to describe the
569 time that a status phrase took T(n)-T(n-1), this line must describe the
570 previous status.
571 */
572 table->field[2]->store(previous->status, strlen(previous->status),
573 system_charset_info);
574
575 my_decimal duration_decimal;
576 double2my_decimal(
577 E_DEC_FATAL_ERROR,
578 (entry->time_usecs - previous->time_usecs) / (1000.0 * 1000),
579 &duration_decimal);
580
581 table->field[3]->store_decimal(&duration_decimal);
582
583 #ifdef HAVE_GETRUSAGE
584
585 my_decimal cpu_utime_decimal, cpu_stime_decimal;
586
587 double2my_decimal(
588 E_DEC_FATAL_ERROR,
589 RUSAGE_DIFF_USEC(entry->rusage.ru_utime, previous->rusage.ru_utime) /
590 (1000.0 * 1000),
591 &cpu_utime_decimal);
592
593 double2my_decimal(
594 E_DEC_FATAL_ERROR,
595 RUSAGE_DIFF_USEC(entry->rusage.ru_stime, previous->rusage.ru_stime) /
596 (1000.0 * 1000),
597 &cpu_stime_decimal);
598
599 table->field[4]->store_decimal(&cpu_utime_decimal);
600 table->field[5]->store_decimal(&cpu_stime_decimal);
601 table->field[4]->set_notnull();
602 table->field[5]->set_notnull();
603 #elif defined(_WIN32)
604 my_decimal cpu_utime_decimal, cpu_stime_decimal;
605
606 double2my_decimal(E_DEC_FATAL_ERROR,
607 GetTimeDiffInSeconds(&entry->ftUser, &previous->ftUser),
608 &cpu_utime_decimal);
609 double2my_decimal(
610 E_DEC_FATAL_ERROR,
611 GetTimeDiffInSeconds(&entry->ftKernel, &previous->ftKernel),
612 &cpu_stime_decimal);
613
614 // Store the result.
615 table->field[4]->store_decimal(&cpu_utime_decimal);
616 table->field[5]->store_decimal(&cpu_stime_decimal);
617 table->field[4]->set_notnull();
618 table->field[5]->set_notnull();
619 #else
620 /* TODO: Add CPU-usage info for non-BSD systems */
621 #endif
622
623 #ifdef HAVE_GETRUSAGE
624 table->field[6]->store(
625 (uint32)(entry->rusage.ru_nvcsw - previous->rusage.ru_nvcsw));
626 table->field[6]->set_notnull();
627 table->field[7]->store(
628 (uint32)(entry->rusage.ru_nivcsw - previous->rusage.ru_nivcsw));
629 table->field[7]->set_notnull();
630 #else
631 /* TODO: Add context switch info for non-BSD systems */
632 #endif
633
634 #ifdef HAVE_GETRUSAGE
635 table->field[8]->store(
636 (uint32)(entry->rusage.ru_inblock - previous->rusage.ru_inblock));
637 table->field[8]->set_notnull();
638 table->field[9]->store(
639 (uint32)(entry->rusage.ru_oublock - previous->rusage.ru_oublock));
640 table->field[9]->set_notnull();
641 #else
642 /* TODO: Add block IO info for non-BSD systems */
643 #endif
644
645 #ifdef HAVE_GETRUSAGE
646 table->field[10]->store(
647 (uint32)(entry->rusage.ru_msgsnd - previous->rusage.ru_msgsnd), true);
648 table->field[10]->set_notnull();
649 table->field[11]->store(
650 (uint32)(entry->rusage.ru_msgrcv - previous->rusage.ru_msgrcv), true);
651 table->field[11]->set_notnull();
652 #else
653 /* TODO: Add message info for non-BSD systems */
654 #endif
655
656 #ifdef HAVE_GETRUSAGE
657 table->field[12]->store(
658 (uint32)(entry->rusage.ru_majflt - previous->rusage.ru_majflt), true);
659 table->field[12]->set_notnull();
660 table->field[13]->store(
661 (uint32)(entry->rusage.ru_minflt - previous->rusage.ru_minflt), true);
662 table->field[13]->set_notnull();
663 #else
664 /* TODO: Add page fault info for non-BSD systems */
665 #endif
666
667 #ifdef HAVE_GETRUSAGE
668 table->field[14]->store(
669 (uint32)(entry->rusage.ru_nswap - previous->rusage.ru_nswap), true);
670 table->field[14]->set_notnull();
671 #else
672 /* TODO: Add swap info for non-BSD systems */
673 #endif
674
675 /* Emit the location that started this step, not that ended it. */
676 if ((previous->function != nullptr) && (previous->file != nullptr)) {
677 table->field[15]->store(previous->function, strlen(previous->function),
678 system_charset_info);
679 table->field[15]->set_notnull();
680 table->field[16]->store(previous->file, strlen(previous->file),
681 system_charset_info);
682 table->field[16]->set_notnull();
683 table->field[17]->store(previous->line, true);
684 table->field[17]->set_notnull();
685 }
686
687 if (schema_table_store_record(thd_arg, table)) return 1;
688 }
689 }
690
691 return 0;
692 }
693 /**
694 Clear all the profiling information.
695 */
cleanup()696 void PROFILING::cleanup() {
697 while (!history.is_empty()) delete history.pop();
698 delete current;
699 current = nullptr;
700 }
701
702 #endif /* ENABLED_PROFILING */
703