Commit 358b942b authored by unknown's avatar unknown

Unify profiling SHOW code and INFORMATION_SCHEMA code for

profiling.  Also,

Bug#26938: profiling client hang if used before enabled

In the SHOW command, not sending header data because we had no 
rows to send was a protocol violation.  Porting the SHOW PROFILE
command to use the Information Schema table avoids that problem.


mysql-test/r/profiling.result:
  Add headers of pre-profile SHOW test.
mysql-test/t/profiling.test:
  Verify that the protocol isn't violated if we ask for profiling 
  info before profiling has recorded anything.
sql/sql_parse.cc:
  Remove SQLCOM_SHOW_PROFILE and add a I_S schema table entry.
sql/sql_profile.cc:
  Add SHOW column-names and a new function that takes profile options
  set in the parser and packs a list of selected fields from the 
  I_S table implementation.
sql/sql_profile.h:
  Remove unused functions and add a new function.
sql/sql_show.cc:
  Add a pointer to the function that selects fields from I_S table
  for SHOW command.
sql/sql_yacc.yy:
  Prepare an I_S table for SHOW PROFILE.
sql/table.h:
  Rename to match the intention.
parent d29f4da4
show profiles;
Query_ID Duration Query
show profile all;
Status Duration CPU_user CPU_system Context_voluntary Context_involuntary Block_ops_in Block_ops_out Messages_sent Messages_received Page_faults_major Page_faults_minor Swaps Source_function Source_file Source_line
show session variables like 'profil%';
Variable_name Value
profiling OFF
......
# Verify that the protocol isn't violated if we ask for profiling info
# before profiling has recorded anything.
show profiles;
show profile all;
# default is OFF
show session variables like 'profil%';
select @@profiling;
......
......@@ -2315,6 +2315,15 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
break;
}
#endif
case SCH_PROFILES:
/*
Mark this current profiling record to be discarded. We don't
wish to have SHOW commands show up in profiling.
*/
#ifdef ENABLED_PROFILING
thd->profiling.discard();
#endif
break;
case SCH_OPEN_TABLES:
case SCH_VARIABLES:
case SCH_STATUS:
......@@ -2755,23 +2764,6 @@ mysql_execute_command(THD *thd)
#else
my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILES", "enable-profiling");
goto error;
#endif
break;
}
case SQLCOM_SHOW_PROFILE:
{
#ifdef ENABLED_PROFILING
thd->profiling.store();
thd->profiling.discard(); // will get re-enabled by reset()
if (lex->profile_query_id != 0)
res= thd->profiling.show(lex->profile_options, lex->profile_query_id);
else
res= thd->profiling.show_last(lex->profile_options);
if (res)
goto error;
#else
my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling");
goto error;
#endif
break;
}
......
......@@ -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);
......
......@@ -39,6 +39,7 @@ extern const char * const _unknown_func_;
extern ST_FIELD_INFO query_profile_statistics_info[];
int fill_query_profile_statistics_info(THD *thd, struct st_table_list *tables, Item *cond);
int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table);
#define PROFILE_NONE 0
......@@ -332,12 +333,6 @@ public:
/* SHOW PROFILES */
bool show_profiles();
/* SHOW PROFILE FOR QUERY query_id */
bool show(uint options, uint profiling_query_id);
/* SHOW PROFILE */
bool show_last(uint options);
/* ... from INFORMATION_SCHEMA.PROFILING ... */
int fill_statistics_info(THD *thd, struct st_table_list *tables, Item *cond);
};
......
......@@ -4344,7 +4344,8 @@ ST_SCHEMA_TABLE schema_tables[]=
{"OPEN_TABLES", open_tables_fields_info, create_schema_table,
fill_open_tables, make_old_format, 0, -1, -1, 1},
{"PROFILING", query_profile_statistics_info, create_schema_table,
fill_query_profile_statistics_info, NULL, NULL, -1, -1, false},
fill_query_profile_statistics_info, make_profile_table_for_show,
NULL, -1, -1, false},
{"ROUTINES", proc_fields_info, create_schema_table,
fill_schema_proc, make_proc_old_format, 0, -1, -1, 0},
{"SCHEMATA", schema_fields_info, create_schema_table,
......
......@@ -7044,7 +7044,13 @@ show_param:
| PROFILES_SYM
{ Lex->sql_command = SQLCOM_SHOW_PROFILES; }
| PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause_init
{ Lex->sql_command = SQLCOM_SHOW_PROFILE; }
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SELECT;
lex->orig_sql_command= SQLCOM_SHOW_PROFILE;
if (prepare_schema_table(YYTHD, lex, NULL, SCH_PROFILES) != 0)
YYABORT;
}
| opt_var_type STATUS_SYM wild_and_where
{
LEX *lex= Lex;
......
......@@ -320,7 +320,7 @@ enum enum_schema_tables
SCH_COLUMN_PRIVILEGES,
SCH_KEY_COLUMN_USAGE,
SCH_OPEN_TABLES,
SCH_PROFILING,
SCH_PROFILES,
SCH_PROCEDURES,
SCH_SCHEMATA,
SCH_SCHEMA_PRIVILEGES,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment