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