|
|
|
@ -41,27 +41,77 @@ int fill_query_profile_statistics_info(THD *thd, struct st_table_list *tables,
|
|
|
|
|
ST_FIELD_INFO query_profile_statistics_info[]=
|
|
|
|
|
{
|
|
|
|
|
/* name, length, type, value, maybe_null, old_name */
|
|
|
|
|
{"QUERY_ID", 20, MYSQL_TYPE_LONG, 0, false, NULL},
|
|
|
|
|
{"SEQ", 20, MYSQL_TYPE_LONG, 0, false, NULL},
|
|
|
|
|
{"STATE", 30, MYSQL_TYPE_STRING, 0, false, NULL},
|
|
|
|
|
{"DURATION", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, false, NULL},
|
|
|
|
|
{"CPU_USER", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, true, NULL},
|
|
|
|
|
{"CPU_SYSTEM", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, true, NULL},
|
|
|
|
|
{"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"SWAPS", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0, true, NULL},
|
|
|
|
|
{"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0, true, NULL},
|
|
|
|
|
{"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0, true, NULL},
|
|
|
|
|
{"QUERY_ID", 20, MYSQL_TYPE_LONG, 0, false, "Query_id"},
|
|
|
|
|
{"SEQ", 20, MYSQL_TYPE_LONG, 0, false, "Seq"},
|
|
|
|
|
{"STATE", 30, MYSQL_TYPE_STRING, 0, false, "Status"},
|
|
|
|
|
{"DURATION", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, false, "Duration"},
|
|
|
|
|
{"CPU_USER", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, true, "CPU_user"},
|
|
|
|
|
{"CPU_SYSTEM", TIME_FLOAT_DIGITS, MYSQL_TYPE_DOUBLE, 0, true, "CPU_system"},
|
|
|
|
|
{"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_voluntary"},
|
|
|
|
|
{"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_involuntary"},
|
|
|
|
|
{"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_in"},
|
|
|
|
|
{"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_out"},
|
|
|
|
|
{"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0, true, "Messages_sent"},
|
|
|
|
|
{"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0, true, "Messages_received"},
|
|
|
|
|
{"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_major"},
|
|
|
|
|
{"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_minor"},
|
|
|
|
|
{"SWAPS", 20, MYSQL_TYPE_LONG, 0, true, "Swaps"},
|
|
|
|
|
{"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0, true, "Source_function"},
|
|
|
|
|
{"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0, true, "Source_file"},
|
|
|
|
|
{"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0, true, "Source_line"},
|
|
|
|
|
{NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
|
|
|
|
|
{
|
|
|
|
|
int profile_options = thd->lex->profile_options;
|
|
|
|
|
int fields_include_condition_truth_values[]= {
|
|
|
|
|
FALSE, /* Query_id */
|
|
|
|
|
FALSE, /* Seq */
|
|
|
|
|
TRUE, /* Status */
|
|
|
|
|
TRUE, /* Duration */
|
|
|
|
|
profile_options & PROFILE_CPU, /* CPU_user */
|
|
|
|
|
profile_options & PROFILE_CPU, /* CPU_system */
|
|
|
|
|
profile_options & PROFILE_CONTEXT, /* Context_voluntary */
|
|
|
|
|
profile_options & PROFILE_CONTEXT, /* Context_involuntary */
|
|
|
|
|
profile_options & PROFILE_BLOCK_IO, /* Block_ops_in */
|
|
|
|
|
profile_options & PROFILE_BLOCK_IO, /* Block_ops_out */
|
|
|
|
|
profile_options & PROFILE_IPC, /* Messages_sent */
|
|
|
|
|
profile_options & PROFILE_IPC, /* Messages_received */
|
|
|
|
|
profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_major */
|
|
|
|
|
profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_minor */
|
|
|
|
|
profile_options & PROFILE_SWAPS, /* Swaps */
|
|
|
|
|
profile_options & PROFILE_SOURCE, /* Source_function */
|
|
|
|
|
profile_options & PROFILE_SOURCE, /* Source_file */
|
|
|
|
|
profile_options & PROFILE_SOURCE, /* Source_line */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ST_FIELD_INFO *field_info;
|
|
|
|
|
Name_resolution_context *context= &thd->lex->select_lex.context;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i= 0; schema_table->fields_info[i].field_name != NULL; i++)
|
|
|
|
|
{
|
|
|
|
|
if (! fields_include_condition_truth_values[i])
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
field_info= &schema_table->fields_info[i];
|
|
|
|
|
Item_field *field= new Item_field(context,
|
|
|
|
|
NullS, NullS, field_info->field_name);
|
|
|
|
|
if (field)
|
|
|
|
|
{
|
|
|
|
|
field->set_name(field_info->old_name,
|
|
|
|
|
strlen(field_info->old_name),
|
|
|
|
|
system_charset_info);
|
|
|
|
|
if (add_item_to_list(thd, field))
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLED_PROFILING
|
|
|
|
|
|
|
|
|
|
#define RUSAGE_USEC(tv) ((tv).tv_sec*1000*1000 + (tv).tv_usec)
|
|
|
|
@ -242,212 +292,6 @@ void QUERY_PROFILE::reset()
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool QUERY_PROFILE::show(uint options)
|
|
|
|
|
{
|
|
|
|
|
THD *thd= profiling->thd;
|
|
|
|
|
List<Item> field_list;
|
|
|
|
|
DBUG_ENTER("QUERY_PROFILE::show");
|
|
|
|
|
|
|
|
|
|
field_list.push_back(new Item_empty_string("Status", MYSQL_ERRMSG_SIZE));
|
|
|
|
|
field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS,
|
|
|
|
|
MYSQL_TYPE_DOUBLE));
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_CPU)
|
|
|
|
|
{
|
|
|
|
|
field_list.push_back(new Item_return_int("CPU_user", TIME_FLOAT_DIGITS,
|
|
|
|
|
MYSQL_TYPE_DOUBLE));
|
|
|
|
|
field_list.push_back(new Item_return_int("CPU_system", TIME_FLOAT_DIGITS,
|
|
|
|
|
MYSQL_TYPE_DOUBLE));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_MEMORY)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_CONTEXT)
|
|
|
|
|
{
|
|
|
|
|
field_list.push_back(new Item_return_int("Context_voluntary", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
field_list.push_back(new Item_return_int("Context_involuntary", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_BLOCK_IO)
|
|
|
|
|
{
|
|
|
|
|
field_list.push_back(new Item_return_int("Block_ops_in", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
field_list.push_back(new Item_return_int("Block_ops_out", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_IPC)
|
|
|
|
|
{
|
|
|
|
|
field_list.push_back(new Item_return_int("Messages_sent", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
field_list.push_back(new Item_return_int("Messages_received", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_PAGE_FAULTS)
|
|
|
|
|
{
|
|
|
|
|
field_list.push_back(new Item_return_int("Page_faults_major", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
field_list.push_back(new Item_return_int("Page_faults_minor", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_SWAPS)
|
|
|
|
|
{
|
|
|
|
|
field_list.push_back(new Item_return_int("Swaps", 10, MYSQL_TYPE_LONG));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_SOURCE)
|
|
|
|
|
{
|
|
|
|
|
field_list.push_back(new Item_empty_string("Source_function",
|
|
|
|
|
MYSQL_ERRMSG_SIZE));
|
|
|
|
|
field_list.push_back(new Item_empty_string("Source_file",
|
|
|
|
|
MYSQL_ERRMSG_SIZE));
|
|
|
|
|
field_list.push_back(new Item_return_int("Source_line", 10,
|
|
|
|
|
MYSQL_TYPE_LONG));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (thd->protocol->send_fields(&field_list,
|
|
|
|
|
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
|
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
|
|
|
|
|
|
Protocol *protocol= thd->protocol;
|
|
|
|
|
SELECT_LEX *sel= &thd->lex->select_lex;
|
|
|
|
|
SELECT_LEX_UNIT *unit= &thd->lex->unit;
|
|
|
|
|
ha_rows idx= 0;
|
|
|
|
|
unit->set_limit(sel);
|
|
|
|
|
PROFILE_ENTRY *previous= &profile_start;
|
|
|
|
|
|
|
|
|
|
PROFILE_ENTRY *entry;
|
|
|
|
|
void *iterator;
|
|
|
|
|
for (iterator= entries.new_iterator();
|
|
|
|
|
iterator != NULL;
|
|
|
|
|
iterator= entries.iterator_next(iterator))
|
|
|
|
|
{
|
|
|
|
|
entry= entries.iterator_value(iterator);
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_GETRUSAGE
|
|
|
|
|
struct rusage *rusage= &(entry->rusage);
|
|
|
|
|
#endif
|
|
|
|
|
String elapsed;
|
|
|
|
|
|
|
|
|
|
if (++idx <= unit->offset_limit_cnt)
|
|
|
|
|
continue;
|
|
|
|
|
if (idx > unit->select_limit_cnt)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
protocol->prepare_for_resend();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
This entry, n, has a point in time, T(n), and a status phrase, S(n). The
|
|
|
|
|
status phrase S(n) describes the period of time that begins at T(n). The
|
|
|
|
|
previous status phrase S(n-1) describes the period of time that starts at
|
|
|
|
|
T(n-1) and ends at T(n). Since we want to describe the time that a status
|
|
|
|
|
phrase took T(n)-T(n-1), this line must describe the previous status.
|
|
|
|
|
*/
|
|
|
|
|
protocol->store(previous->status, strlen(previous->status),
|
|
|
|
|
system_charset_info);
|
|
|
|
|
protocol->store((double)(entry->time_usecs -
|
|
|
|
|
previous->time_usecs)/(1000.0*1000),
|
|
|
|
|
(uint32) TIME_FLOAT_DIGITS-1, &elapsed);
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_CPU)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_GETRUSAGE
|
|
|
|
|
String cpu_utime, cpu_stime;
|
|
|
|
|
protocol->store((double)(RUSAGE_DIFF_USEC(rusage->ru_utime,
|
|
|
|
|
previous->rusage.ru_utime))/(1000.0*1000),
|
|
|
|
|
(uint32) TIME_FLOAT_DIGITS-1, &cpu_utime);
|
|
|
|
|
protocol->store((double)(RUSAGE_DIFF_USEC(rusage->ru_stime,
|
|
|
|
|
previous->rusage.ru_stime))/(1000.0*1000),
|
|
|
|
|
(uint32) TIME_FLOAT_DIGITS-1, &cpu_stime);
|
|
|
|
|
#else
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_CONTEXT)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_GETRUSAGE
|
|
|
|
|
protocol->store((uint32)(rusage->ru_nvcsw - previous->rusage.ru_nvcsw));
|
|
|
|
|
protocol->store((uint32)(rusage->ru_nivcsw - previous->rusage.ru_nivcsw));
|
|
|
|
|
#else
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_BLOCK_IO)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_GETRUSAGE
|
|
|
|
|
protocol->store((uint32)(rusage->ru_inblock - previous->rusage.ru_inblock));
|
|
|
|
|
protocol->store((uint32)(rusage->ru_oublock - previous->rusage.ru_oublock));
|
|
|
|
|
#else
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_IPC)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_GETRUSAGE
|
|
|
|
|
protocol->store((uint32)(rusage->ru_msgsnd - previous->rusage.ru_msgsnd));
|
|
|
|
|
protocol->store((uint32)(rusage->ru_msgrcv - previous->rusage.ru_msgrcv));
|
|
|
|
|
#else
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_PAGE_FAULTS)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_GETRUSAGE
|
|
|
|
|
protocol->store((uint32)(rusage->ru_majflt - previous->rusage.ru_majflt));
|
|
|
|
|
protocol->store((uint32)(rusage->ru_minflt - previous->rusage.ru_minflt));
|
|
|
|
|
#else
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_SWAPS)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_GETRUSAGE
|
|
|
|
|
protocol->store((uint32)(rusage->ru_nswap - previous->rusage.ru_nswap));
|
|
|
|
|
#else
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options & PROFILE_SOURCE)
|
|
|
|
|
{
|
|
|
|
|
if ((entry->function != NULL) && (entry->file != NULL))
|
|
|
|
|
{
|
|
|
|
|
protocol->store(entry->function, strlen(entry->function),
|
|
|
|
|
system_charset_info);
|
|
|
|
|
protocol->store(entry->file, strlen(entry->file), system_charset_info);
|
|
|
|
|
protocol->store((uint32) entry->line);
|
|
|
|
|
} else {
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
protocol->store_null();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (protocol->write())
|
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
|
|
|
|
|
|
previous= entry;
|
|
|
|
|
}
|
|
|
|
|
send_eof(thd);
|
|
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PROFILING::PROFILING()
|
|
|
|
|
:profile_id_counter(1), keeping(TRUE), enabled(FALSE), current(NULL), last(NULL)
|
|
|
|
|
{
|
|
|
|
@ -630,38 +474,11 @@ void PROFILING::set_query_source(char *query_source_arg, uint query_length_arg)
|
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PROFILING::show(uint options, uint profiling_query_id)
|
|
|
|
|
{
|
|
|
|
|
DBUG_ENTER("PROFILING::show");
|
|
|
|
|
QUERY_PROFILE *prof;
|
|
|
|
|
|
|
|
|
|
void *iterator;
|
|
|
|
|
for (iterator= history.new_iterator();
|
|
|
|
|
iterator != NULL;
|
|
|
|
|
iterator= history.iterator_next(iterator))
|
|
|
|
|
{
|
|
|
|
|
prof= history.iterator_value(iterator);
|
|
|
|
|
|
|
|
|
|
if(prof->profiling_query_id == profiling_query_id)
|
|
|
|
|
DBUG_RETURN(prof->show(options));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my_error(ER_WRONG_ARGUMENTS, MYF(0), "SHOW PROFILE");
|
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PROFILING::show_last(uint options)
|
|
|
|
|
{
|
|
|
|
|
DBUG_ENTER("PROFILING::show_last");
|
|
|
|
|
if (!history.is_empty()) {
|
|
|
|
|
DBUG_RETURN(last->show(options));
|
|
|
|
|
}
|
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Fill the information schema table, "query_profile", as defined in show.cc .
|
|
|
|
|
There are two ways to get to this function: Selecting from the information
|
|
|
|
|
schema, and a SHOW command.
|
|
|
|
|
*/
|
|
|
|
|
int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item *cond)
|
|
|
|
|
{
|
|
|
|
@ -696,6 +513,31 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item
|
|
|
|
|
{
|
|
|
|
|
entry= query->entries.iterator_value(entry_iterator);
|
|
|
|
|
|
|
|
|
|
if (thd->lex->orig_sql_command == SQLCOM_SHOW_PROFILE)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
We got here via a SHOW command. That means that we stored
|
|
|
|
|
information about the query we wish to show and that isn't
|
|
|
|
|
in a WHERE clause at a higher level to filter out rows we
|
|
|
|
|
wish to exclude.
|
|
|
|
|
|
|
|
|
|
Because that functionality isn't available in the server yet,
|
|
|
|
|
we must filter here, at the wrong level. Once one can con-
|
|
|
|
|
struct where and having conditions at the SQL layer, then this
|
|
|
|
|
condition should be ripped out.
|
|
|
|
|
*/
|
|
|
|
|
if (thd->lex->profile_query_id == 0) /* 0 == show final query */
|
|
|
|
|
{
|
|
|
|
|
if (query != last)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (thd->lex->profile_query_id != query->profiling_query_id)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set default values for this row. */
|
|
|
|
|
restore_record(table, s->default_values);
|
|
|
|
|
|
|
|
|
|