34 #include "sql_profile.h"
37 #include "sql_class.h"
44 #define TIME_FLOAT_DIGITS 9
46 #define TIME_I_S_DECIMAL_SIZE (TIME_FLOAT_DIGITS*100)+(TIME_FLOAT_DIGITS-3)
48 #define MAX_QUERY_LENGTH 300U
49 #define MAX_QUERY_HISTORY 101U
57 #if defined(ENABLED_PROFILING)
58 return(thd->profiling.fill_statistics_info(thd, tables, cond));
60 my_error(ER_FEATURE_DISABLED, MYF(0),
"SHOW PROFILE",
"enable-profiling");
68 {
"QUERY_ID", 20, MYSQL_TYPE_LONG, 0,
false,
"Query_id", SKIP_OPEN_TABLE},
69 {
"SEQ", 20, MYSQL_TYPE_LONG, 0,
false,
"Seq", SKIP_OPEN_TABLE},
70 {
"STATE", 30, MYSQL_TYPE_STRING, 0,
false,
"Status", SKIP_OPEN_TABLE},
74 {
"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0,
true,
"Context_voluntary", SKIP_OPEN_TABLE},
75 {
"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0,
true,
"Context_involuntary", SKIP_OPEN_TABLE},
76 {
"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0,
true,
"Block_ops_in", SKIP_OPEN_TABLE},
77 {
"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0,
true,
"Block_ops_out", SKIP_OPEN_TABLE},
78 {
"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0,
true,
"Messages_sent", SKIP_OPEN_TABLE},
79 {
"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0,
true,
"Messages_received", SKIP_OPEN_TABLE},
80 {
"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0,
true,
"Page_faults_major", SKIP_OPEN_TABLE},
81 {
"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0,
true,
"Page_faults_minor", SKIP_OPEN_TABLE},
82 {
"SWAPS", 20, MYSQL_TYPE_LONG, 0,
true,
"Swaps", SKIP_OPEN_TABLE},
83 {
"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0,
true,
"Source_function", SKIP_OPEN_TABLE},
84 {
"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0,
true,
"Source_file", SKIP_OPEN_TABLE},
85 {
"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0,
true,
"Source_line", SKIP_OPEN_TABLE},
86 {NULL, 0, MYSQL_TYPE_STRING, 0,
true, NULL, 0}
90 int make_profile_table_for_show(THD *thd,
ST_SCHEMA_TABLE *schema_table)
92 uint profile_options = thd->lex->profile_options;
93 uint fields_include_condition_truth_values[]= {
98 profile_options & PROFILE_CPU,
99 profile_options & PROFILE_CPU,
100 profile_options & PROFILE_CONTEXT,
101 profile_options & PROFILE_CONTEXT,
102 profile_options & PROFILE_BLOCK_IO,
103 profile_options & PROFILE_BLOCK_IO,
104 profile_options & PROFILE_IPC,
105 profile_options & PROFILE_IPC,
106 profile_options & PROFILE_PAGE_FAULTS,
107 profile_options & PROFILE_PAGE_FAULTS,
108 profile_options & PROFILE_SWAPS,
109 profile_options & PROFILE_SOURCE,
110 profile_options & PROFILE_SOURCE,
111 profile_options & PROFILE_SOURCE,
118 for (i= 0; schema_table->fields_info[
i].
field_name != NULL; i++)
120 if (! fields_include_condition_truth_values[i])
123 field_info= &schema_table->fields_info[
i];
128 field->item_name.
copy(field_info->old_name);
129 if (add_item_to_list(thd, field))
137 #if defined(ENABLED_PROFILING)
139 #define RUSAGE_USEC(tv) ((tv).tv_sec*1000*1000 + (tv).tv_usec)
140 #define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2)))
143 static ULONGLONG FileTimeToQuadWord(FILETIME *ft)
157 static double GetTimeDiffInSeconds(FILETIME *a, FILETIME *b)
159 return ((FileTimeToQuadWord(a) - FileTimeToQuadWord(b)) / 1e7);
163 PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg,
const char
165 :profile(profile_arg)
168 set_label(status_arg, NULL, NULL, 0);
171 PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg,
172 const char *status_arg,
173 const char *function_arg,
174 const char *file_arg,
175 unsigned int line_arg)
176 :profile(profile_arg)
179 set_label(status_arg, function_arg, file_arg, line_arg);
182 PROF_MEASUREMENT::~PROF_MEASUREMENT()
184 my_free(allocated_status_memory);
185 status=
function=
file= NULL;
188 void PROF_MEASUREMENT::set_label(
const char *status_arg,
189 const char *function_arg,
190 const char *file_arg,
unsigned int line_arg)
199 sizes[0]= (status_arg == NULL) ? 0 : strlen(status_arg) + 1;
200 sizes[1]= (function_arg == NULL) ? 0 : strlen(function_arg) + 1;
201 sizes[2]= (file_arg == NULL) ? 0 : strlen(file_arg) + 1;
203 allocated_status_memory= (
char *) my_malloc(sizes[0] + sizes[1] + sizes[2], MYF(0));
204 DBUG_ASSERT(allocated_status_memory != NULL);
206 cursor= allocated_status_memory;
208 if (status_arg != NULL)
210 strcpy(cursor, status_arg);
217 if (function_arg != NULL)
219 strcpy(cursor, function_arg);
226 if (file_arg != NULL)
228 strcpy(cursor, file_arg);
246 void PROF_MEASUREMENT::collect()
248 time_usecs= (double) my_getsystime() / 10.0;
249 #ifdef HAVE_GETRUSAGE
250 getrusage(RUSAGE_SELF, &
rusage);
251 #elif defined(_WIN32)
256 GetProcessTimes(GetCurrentProcess(), &ftDummy, &ftDummy, &ftKernel, &ftUser);
261 QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg,
const char *status_arg)
262 :profiling(profiling_arg), profiling_query_id(0), query_source(NULL)
265 PROF_MEASUREMENT *prof=
new PROF_MEASUREMENT(
this, status_arg);
266 prof->m_seq= m_seq_counter++;
267 m_start_time_usecs= prof->time_usecs;
268 m_end_time_usecs= m_start_time_usecs;
269 entries.push_back(prof);
272 QUERY_PROFILE::~QUERY_PROFILE()
274 while (! entries.is_empty())
275 delete entries.pop();
277 my_free(query_source);
283 void QUERY_PROFILE::set_query_source(
char *query_source_arg,
284 uint query_length_arg)
287 uint length= min(MAX_QUERY_LENGTH, query_length_arg);
289 DBUG_ASSERT(query_source == NULL);
290 if (query_source_arg != NULL)
291 query_source= my_strndup(query_source_arg, length, MYF(0));
294 void QUERY_PROFILE::new_status(
const char *status_arg,
295 const char *function_arg,
const char *file_arg,
296 unsigned int line_arg)
298 PROF_MEASUREMENT *prof;
299 DBUG_ENTER(
"QUERY_PROFILE::status");
301 DBUG_ASSERT(status_arg != NULL);
303 if ((function_arg != NULL) && (file_arg != NULL))
304 prof=
new PROF_MEASUREMENT(
this, status_arg, function_arg, base_name(file_arg), line_arg);
306 prof=
new PROF_MEASUREMENT(
this, status_arg);
308 prof->m_seq= m_seq_counter++;
309 m_end_time_usecs= prof->time_usecs;
310 entries.push_back(prof);
313 while (entries.elements > MAX_QUERY_HISTORY)
314 delete entries.pop();
321 PROFILING::PROFILING()
322 :profile_id_counter(1), current(NULL), last(NULL)
326 PROFILING::~PROFILING()
328 while (! history.is_empty())
329 delete history.pop();
344 void PROFILING::status_change(
const char *status_arg,
345 const char *function_arg,
346 const char *file_arg,
unsigned int line_arg)
348 DBUG_ENTER(
"PROFILING::status_change");
350 if (status_arg == NULL)
356 if (unlikely(enabled))
357 current->new_status(status_arg, function_arg, file_arg, line_arg);
368 void PROFILING::start_new_query(
const char *initial_state)
370 DBUG_ENTER(
"PROFILING::start_new_query");
373 if (unlikely(current != NULL))
375 DBUG_PRINT(
"warning", (
"profiling code was asked to start a new query "
376 "before the old query was finished. This is "
378 finish_current_query();
381 enabled= ((thd->variables.option_bits & OPTION_PROFILING) != 0);
383 if (! enabled) DBUG_VOID_RETURN;
385 DBUG_ASSERT(current == NULL);
386 current=
new QUERY_PROFILE(
this, initial_state);
395 void PROFILING::discard_current_query()
397 DBUG_ENTER(
"PROFILING::discard_current_profile");
410 void PROFILING::finish_current_query()
412 DBUG_ENTER(
"PROFILING::finish_current_profile");
416 status_change(
"ending", NULL, NULL, 0);
419 ((thd->variables.option_bits & OPTION_PROFILING) != 0) &&
420 (current->query_source != NULL) &&
421 (! current->entries.is_empty()))
423 current->profiling_query_id= next_profile_id();
425 history.push_back(current);
437 while (history.elements > thd->variables.profiling_history_size)
438 delete history.pop();
443 bool PROFILING::show_profiles()
445 DBUG_ENTER(
"PROFILING::show_profiles");
451 field_list.push_back(
new Item_return_int(
"Duration", TIME_FLOAT_DIGITS-1,
455 if (thd->protocol->send_result_set_metadata(&field_list,
456 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
459 SELECT_LEX *sel= &thd->lex->select_lex;
460 SELECT_LEX_UNIT *unit= &thd->lex->unit;
464 unit->set_limit(sel);
467 for (iterator= history.new_iterator();
469 iterator= history.iterator_next(iterator))
471 prof= history.iterator_value(iterator);
475 double query_time_usecs= prof->m_end_time_usecs - prof->m_start_time_usecs;
477 if (++idx <= unit->offset_limit_cnt)
479 if (idx > unit->select_limit_cnt)
482 protocol->prepare_for_resend();
483 protocol->
store((uint32)(prof->profiling_query_id));
484 protocol->
store((
double)(query_time_usecs/(1000.0*1000)),
485 (uint32) TIME_FLOAT_DIGITS-1, &elapsed);
486 if (prof->query_source != NULL)
487 protocol->
store(prof->query_source, strlen(prof->query_source),
488 system_charset_info);
490 protocol->store_null();
492 if (protocol->write())
505 void PROFILING::set_query_source(
char *query_source_arg, uint query_length_arg)
507 DBUG_ENTER(
"PROFILING::set_query_source");
513 current->set_query_source(query_source_arg, query_length_arg);
515 DBUG_PRINT(
"info", (
"no current profile to send query source to"));
524 int PROFILING::fill_statistics_info(THD *thd_arg,
TABLE_LIST *tables,
Item *cond)
526 DBUG_ENTER(
"PROFILING::fill_statistics_info");
528 ulonglong row_number= 0;
530 QUERY_PROFILE *
query;
532 void *history_iterator;
533 for (history_iterator= history.new_iterator();
534 history_iterator != NULL;
535 history_iterator= history.iterator_next(history_iterator))
537 query= history.iterator_value(history_iterator);
546 void *entry_iterator;
547 PROF_MEASUREMENT *
entry, *previous= NULL;
549 for (entry_iterator= query->entries.new_iterator();
550 entry_iterator != NULL;
551 entry_iterator= query->entries.iterator_next(entry_iterator),
552 previous=
entry, row_number++)
554 entry= query->entries.iterator_value(entry_iterator);
558 if (previous == NULL)
continue;
560 if (thd_arg->lex->sql_command == SQLCOM_SHOW_PROFILE)
573 if (thd_arg->lex->profile_query_id == 0)
580 if (thd_arg->lex->profile_query_id != query->profiling_query_id)
586 restore_record(table, s->default_values);
592 table->field[0]->store((ulonglong) query->profiling_query_id, TRUE);
593 table->field[1]->store((ulonglong) seq, TRUE);
602 table->field[2]->store(previous->status, strlen(previous->status),
603 system_charset_info);
606 double2my_decimal(E_DEC_FATAL_ERROR,
607 (entry->time_usecs-previous->time_usecs)/(1000.0*1000),
610 table->field[3]->store_decimal(&duration_decimal);
613 #ifdef HAVE_GETRUSAGE
615 my_decimal cpu_utime_decimal, cpu_stime_decimal;
617 double2my_decimal(E_DEC_FATAL_ERROR,
618 RUSAGE_DIFF_USEC(entry->rusage.ru_utime,
619 previous->rusage.ru_utime) /
623 double2my_decimal(E_DEC_FATAL_ERROR,
624 RUSAGE_DIFF_USEC(entry->rusage.ru_stime,
625 previous->rusage.ru_stime) /
629 table->field[4]->store_decimal(&cpu_utime_decimal);
630 table->field[5]->store_decimal(&cpu_stime_decimal);
631 table->field[4]->set_notnull();
632 table->field[5]->set_notnull();
633 #elif defined(_WIN32)
634 my_decimal cpu_utime_decimal, cpu_stime_decimal;
636 double2my_decimal(E_DEC_FATAL_ERROR,
637 GetTimeDiffInSeconds(&entry->ftUser,
640 double2my_decimal(E_DEC_FATAL_ERROR,
641 GetTimeDiffInSeconds(&entry->ftKernel,
642 &previous->ftKernel),
646 table->field[4]->store_decimal(&cpu_utime_decimal);
647 table->field[5]->store_decimal(&cpu_stime_decimal);
648 table->field[4]->set_notnull();
649 table->field[5]->set_notnull();
654 #ifdef HAVE_GETRUSAGE
655 table->field[6]->store((uint32)(entry->rusage.ru_nvcsw -
656 previous->rusage.ru_nvcsw));
657 table->field[6]->set_notnull();
658 table->field[7]->store((uint32)(entry->rusage.ru_nivcsw -
659 previous->rusage.ru_nivcsw));
660 table->field[7]->set_notnull();
665 #ifdef HAVE_GETRUSAGE
666 table->field[8]->store((uint32)(entry->rusage.ru_inblock -
667 previous->rusage.ru_inblock));
668 table->field[8]->set_notnull();
669 table->field[9]->store((uint32)(entry->rusage.ru_oublock -
670 previous->rusage.ru_oublock));
671 table->field[9]->set_notnull();
676 #ifdef HAVE_GETRUSAGE
677 table->field[10]->store((uint32)(entry->rusage.ru_msgsnd -
678 previous->rusage.ru_msgsnd),
true);
679 table->field[10]->set_notnull();
680 table->field[11]->store((uint32)(entry->rusage.ru_msgrcv -
681 previous->rusage.ru_msgrcv),
true);
682 table->field[11]->set_notnull();
687 #ifdef HAVE_GETRUSAGE
688 table->field[12]->store((uint32)(entry->rusage.ru_majflt -
689 previous->rusage.ru_majflt),
true);
690 table->field[12]->set_notnull();
691 table->field[13]->store((uint32)(entry->rusage.ru_minflt -
692 previous->rusage.ru_minflt),
true);
693 table->field[13]->set_notnull();
698 #ifdef HAVE_GETRUSAGE
699 table->field[14]->store((uint32)(entry->rusage.ru_nswap -
700 previous->rusage.ru_nswap),
true);
701 table->field[14]->set_notnull();
707 if ((previous->function != NULL) && (previous->file != NULL))
709 table->field[15]->store(previous->function, strlen(previous->function),
710 system_charset_info);
711 table->field[15]->set_notnull();
712 table->field[16]->store(previous->file, strlen(previous->file), system_charset_info);
713 table->field[16]->set_notnull();
714 table->field[17]->store(previous->line,
true);
715 table->field[17]->set_notnull();
718 if (schema_table_store_record(thd_arg, table))