Commit 69aee073 authored by unknown's avatar unknown

Rewrite profiler code to be easier to maintain and less buggy.

Between 5.0 and 5.1, the step of incrementing the global query id
changed, which broke how the profiler noticed when a new query had
started.  That reset the state list and caused all but the last 
five (or so) states to be thrown away.

Now, don't watch for query_id changes in the lower level.

Add a bogus state change at the end of profiling so that the last 
real state change is timed.

Emit source reference for the start of the span of time instead of
the end of it.


mysql-test/r/profiling.result:
  Add a test that shows continuation of execution with multi-statement
  packets.
mysql-test/t/profiling.test:
  Add a test that shows continuation of execution with multi-statement
  packets.
sql/sql_parse.cc:
  Insert profiling calls at beginnings and ends of each query.
  
  Remove the old way of keeping or discarding profiles, and flipping 
  to new query profiles.
sql/sql_profile.cc:
  No longer use the thread's query_id to determine when we flip
  to a new statement.
  
  Some status statements are set to be NULL in the server.  We don't
  log those, as it doesn't fit this style of profiling yet.
  
  Rewrite the parser code to be more active and legible.  
  
  Relying on passive/lazy discovery of new queries was buggy.
  
  Add a bogus status change before ending a profile, so that the 
  previous real status has a endpoint.
  
  Emit source reference of the start of the span-of-time instead of
  the end of it.
sql/sql_profile.h:
  Store the server_query_id at instantiation time for a new query.
  
  Rewrite the parser code to be more active.  Relying on passive/lazy
  discovery of new queries was buggy.
  
  Name first state to more honestly describe the state.  We don't 
  really know of initialization that will follow.
sql/sql_show.cc:
  Update comment to note the decidedly weird field_length behavior
  on Decimal types in information_schema.
parent 6dbc0c5a
...@@ -364,5 +364,17 @@ drop view if exists v1; ...@@ -364,5 +364,17 @@ drop view if exists v1;
Warnings: Warnings:
Note 1051 Unknown table 'test.v1' Note 1051 Unknown table 'test.v1'
drop function if exists f1; drop function if exists f1;
select 1; select 2; select 3;;
1
1
2
2
3
3
select state from information_schema.profiling where seq=1 order by query_id desc limit 3;
state
continuing
continuing
initializing
set session profiling = OFF; set session profiling = OFF;
End of 5.0 tests End of 5.0 tests
...@@ -195,6 +195,11 @@ drop table if exists t1, t2, t3; ...@@ -195,6 +195,11 @@ drop table if exists t1, t2, t3;
drop view if exists v1; drop view if exists v1;
drop function if exists f1; drop function if exists f1;
# Multiple queries in one packet.
--eval select 1; select 2; select 3;
# two continuations, one initialization
select state from information_schema.profiling where seq=1 order by query_id desc limit 3;
## last thing in the file ## last thing in the file
set session profiling = OFF; set session profiling = OFF;
......
...@@ -426,6 +426,10 @@ pthread_handler_t handle_bootstrap(void *arg) ...@@ -426,6 +426,10 @@ pthread_handler_t handle_bootstrap(void *arg)
QUERY_CACHE_FLAGS_SIZE); QUERY_CACHE_FLAGS_SIZE);
thd->query[length] = '\0'; thd->query[length] = '\0';
DBUG_PRINT("query",("%-.4096s",thd->query)); DBUG_PRINT("query",("%-.4096s",thd->query));
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.set_query_source(thd->query, length);
#endif
/* /*
We don't need to obtain LOCK_thread_count here because in bootstrap We don't need to obtain LOCK_thread_count here because in bootstrap
mode we have only one thread. mode we have only one thread.
...@@ -645,6 +649,9 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) ...@@ -645,6 +649,9 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
/* /*
Read one command from connection and execute it (query or simple command). Read one command from connection and execute it (query or simple command).
This function is called in loop from thread function. This function is called in loop from thread function.
For profiling to work, it must never be called recursively.
SYNOPSIS SYNOPSIS
do_command() do_command()
RETURN VALUE RETURN VALUE
...@@ -654,12 +661,16 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) ...@@ -654,12 +661,16 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion)
bool do_command(THD *thd) bool do_command(THD *thd)
{ {
bool return_value;
char *packet= 0; char *packet= 0;
ulong packet_length; ulong packet_length;
NET *net= &thd->net; NET *net= &thd->net;
enum enum_server_command command; enum enum_server_command command;
DBUG_ENTER("do_command"); DBUG_ENTER("do_command");
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.start_new_query();
#endif
/* /*
indicator of uninitialized lex => normal flow of errors handling indicator of uninitialized lex => normal flow of errors handling
(see my_message_sql) (see my_message_sql)
...@@ -686,11 +697,15 @@ bool do_command(THD *thd) ...@@ -686,11 +697,15 @@ bool do_command(THD *thd)
/* Check if we can continue without closing the connection */ /* Check if we can continue without closing the connection */
if (net->error != 3) if (net->error != 3)
DBUG_RETURN(TRUE); // We have to close it. {
return_value= TRUE; // We have to close it.
goto out;
}
net_send_error(thd, net->last_errno, NullS); net_send_error(thd, net->last_errno, NullS);
net->error= 0; net->error= 0;
DBUG_RETURN(FALSE); return_value= FALSE;
goto out;
} }
packet= (char*) net->read_pos; packet= (char*) net->read_pos;
...@@ -724,7 +739,13 @@ bool do_command(THD *thd) ...@@ -724,7 +739,13 @@ bool do_command(THD *thd)
my_net_set_read_timeout(net, thd->variables.net_read_timeout); my_net_set_read_timeout(net, thd->variables.net_read_timeout);
DBUG_ASSERT(packet_length); DBUG_ASSERT(packet_length);
DBUG_RETURN(dispatch_command(command, thd, packet+1, (uint) (packet_length-1))); return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
out:
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.finish_current_query();
#endif
DBUG_RETURN(return_value);
} }
#endif /* EMBEDDED_LIBRARY */ #endif /* EMBEDDED_LIBRARY */
...@@ -982,19 +1003,22 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -982,19 +1003,22 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break; // fatal error is set break; // fatal error is set
char *packet_end= thd->query + thd->query_length; char *packet_end= thd->query + thd->query_length;
/* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */ /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
const char* found_semicolon= NULL; const char* end_of_stmt= NULL;
general_log_write(thd, command, thd->query, thd->query_length); general_log_write(thd, command, thd->query, thd->query_length);
DBUG_PRINT("query",("%-.4096s",thd->query)); DBUG_PRINT("query",("%-.4096s",thd->query));
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.set_query_source(thd->query, thd->query_length);
#endif
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR); my_pthread_setprio(pthread_self(),QUERY_PRIOR);
mysql_parse(thd, thd->query, thd->query_length, & found_semicolon); mysql_parse(thd, thd->query, thd->query_length, &end_of_stmt);
while (!thd->killed && found_semicolon && !thd->net.report_error) while (!thd->killed && (end_of_stmt != NULL) && !thd->net.report_error)
{ {
char *next_packet= (char*) found_semicolon; char *beginning_of_next_stmt= (char*) end_of_stmt;
net->no_send_error= 0; net->no_send_error= 0;
/* /*
Multiple queries exits, execute them individually Multiple queries exits, execute them individually
...@@ -1002,24 +1026,31 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1002,24 +1026,31 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (thd->lock || thd->open_tables || thd->derived_tables || if (thd->lock || thd->open_tables || thd->derived_tables ||
thd->prelocked_mode) thd->prelocked_mode)
close_thread_tables(thd); close_thread_tables(thd);
ulong length= (ulong)(packet_end - next_packet); ulong length= (ulong)(packet_end - beginning_of_next_stmt);
log_slow_statement(thd); log_slow_statement(thd);
/* Remove garbage at start of query */ /* Remove garbage at start of query */
while (my_isspace(thd->charset(), *next_packet) && length > 0) while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt))
{ {
next_packet++; beginning_of_next_stmt++;
length--; length--;
} }
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.finish_current_query();
thd->profiling.start_new_query("continuing");
thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif
VOID(pthread_mutex_lock(&LOCK_thread_count)); VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_length= length; thd->query_length= length;
thd->query= next_packet; thd->query= beginning_of_next_stmt;
thd->query_id= next_query_id(); thd->query_id= next_query_id();
thd->set_time(); /* Reset the query start time. */ thd->set_time(); /* Reset the query start time. */
/* TODO: set thd->lex->sql_command to SQLCOM_END here */ /* TODO: set thd->lex->sql_command to SQLCOM_END here */
VOID(pthread_mutex_unlock(&LOCK_thread_count)); VOID(pthread_mutex_unlock(&LOCK_thread_count));
mysql_parse(thd, next_packet, length, & found_semicolon); mysql_parse(thd, beginning_of_next_stmt, length, &end_of_stmt);
} }
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
...@@ -1516,8 +1547,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, ...@@ -1516,8 +1547,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
Mark this current profiling record to be discarded. We don't Mark this current profiling record to be discarded. We don't
wish to have SHOW commands show up in profiling. wish to have SHOW commands show up in profiling.
*/ */
#ifdef ENABLED_PROFILING #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.discard(); thd->profiling.discard_current_query();
#endif #endif
break; break;
case SCH_OPEN_TABLES: case SCH_OPEN_TABLES:
...@@ -2012,8 +2043,7 @@ mysql_execute_command(THD *thd) ...@@ -2012,8 +2043,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_PROFILES: case SQLCOM_SHOW_PROFILES:
{ {
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.store(); thd->profiling.discard_current_query();
thd->profiling.discard();
res= thd->profiling.show_profiles(); res= thd->profiling.show_profiles();
if (res) if (res)
goto error; goto error;
...@@ -5274,9 +5304,6 @@ void mysql_reset_thd_for_next_command(THD *thd) ...@@ -5274,9 +5304,6 @@ void mysql_reset_thd_for_next_command(THD *thd)
thd->total_warn_count=0; // Warnings for this query thd->total_warn_count=0; // Warnings for this query
thd->rand_used= 0; thd->rand_used= 0;
thd->sent_row_count= thd->examined_row_count= 0; thd->sent_row_count= thd->examined_row_count= 0;
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
thd->profiling.reset();
#endif
} }
/* /*
Because we come here only for start of top-statements, binlog format is Because we come here only for start of top-statements, binlog format is
......
This diff is collapsed.
...@@ -62,7 +62,7 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table); ...@@ -62,7 +62,7 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table);
#endif #endif
class PROFILE_ENTRY; class PROF_MEASUREMENT;
class QUERY_PROFILE; class QUERY_PROFILE;
class PROFILING; class PROFILING;
...@@ -176,7 +176,7 @@ template <class T> class Queue ...@@ -176,7 +176,7 @@ template <class T> class Queue
/** /**
A single entry in a single profile. A single entry in a single profile.
*/ */
class PROFILE_ENTRY class PROF_MEASUREMENT
{ {
private: private:
friend class QUERY_PROFILE; friend class QUERY_PROFILE;
...@@ -195,22 +195,22 @@ class PROFILE_ENTRY ...@@ -195,22 +195,22 @@ class PROFILE_ENTRY
double time_usecs; double time_usecs;
char *allocated_status_memory; char *allocated_status_memory;
void set_status(const char *status_arg, const char *function_arg, void set_label(const char *status_arg, const char *function_arg,
const char *file_arg, unsigned int line_arg); const char *file_arg, unsigned int line_arg);
void clean_up(); void clean_up();
PROFILE_ENTRY(); PROF_MEASUREMENT();
PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg); PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, const char *status_arg);
PROFILE_ENTRY(QUERY_PROFILE *profile_arg, const char *status_arg, PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, const char *status_arg,
const char *function_arg, const char *function_arg,
const char *file_arg, unsigned int line_arg); const char *file_arg, unsigned int line_arg);
~PROFILE_ENTRY(); ~PROF_MEASUREMENT();
void collect(); void collect();
}; };
/** /**
The full profile for a single query, and includes multiple PROFILE_ENTRY The full profile for a single query, and includes multiple PROF_MEASUREMENT
objects. objects.
*/ */
class QUERY_PROFILE class QUERY_PROFILE
...@@ -220,21 +220,21 @@ class QUERY_PROFILE ...@@ -220,21 +220,21 @@ class QUERY_PROFILE
PROFILING *profiling; PROFILING *profiling;
query_id_t server_query_id; /* Global id. */
query_id_t profiling_query_id; /* Session-specific id. */ query_id_t profiling_query_id; /* Session-specific id. */
char *query_source; char *query_source;
PROFILE_ENTRY profile_start;
PROFILE_ENTRY *profile_end;
Queue<PROFILE_ENTRY> entries;
PROF_MEASUREMENT *profile_start;
PROF_MEASUREMENT *profile_end;
Queue<PROF_MEASUREMENT> entries;
QUERY_PROFILE(PROFILING *profiling_arg, char *query_source_arg, uint query_length_arg);
QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg);
~QUERY_PROFILE(); ~QUERY_PROFILE();
void set_query_source(char *query_source_arg, uint query_length_arg); void set_query_source(char *query_source_arg, uint query_length_arg);
/* Add a profile status change to the current profile. */ /* Add a profile status change to the current profile. */
void status(const char *status_arg, void new_status(const char *status_arg,
const char *function_arg, const char *function_arg,
const char *file_arg, unsigned int line_arg); const char *file_arg, unsigned int line_arg);
...@@ -252,7 +252,7 @@ class QUERY_PROFILE ...@@ -252,7 +252,7 @@ class QUERY_PROFILE
class PROFILING class PROFILING
{ {
private: private:
friend class PROFILE_ENTRY; friend class PROF_MEASUREMENT;
friend class QUERY_PROFILE; friend class QUERY_PROFILE;
/* /*
...@@ -274,39 +274,12 @@ class PROFILING ...@@ -274,39 +274,12 @@ class PROFILING
~PROFILING(); ~PROFILING();
void set_query_source(char *query_source_arg, uint query_length_arg); void set_query_source(char *query_source_arg, uint query_length_arg);
/** Reset the current profile and state of profiling for the next query. */ void start_new_query(const char *initial_state= "starting");
void reset();
/** void discard_current_query();
Do we intend to keep the currently collected profile?
We don't keep profiles for some commands, such as SHOW PROFILE, SHOW
PROFILES, and some SQLCOM commands which aren't useful to profile. The
keep() and discard() functions can be called many times, only the final
setting when the query finishes is used to decide whether to discard the
profile.
The default is to keep the profile for all queries.
*/
inline void keep() { keeping= true; };
/** void finish_current_query();
Do we intend to keep the currently collected profile?
@see keep()
*/
inline void discard() { keeping= false; };
/**
Stash this profile in the profile history and remove the oldest
profile if the history queue is full, as defined by the
profiling_history_size system variable.
*/
void store();
/**
Called with every update of the status via thd_proc_info() , and is
therefore the main hook into the profiling code.
*/
void status_change(const char *status_arg, void status_change(const char *status_arg,
const char *function_arg, const char *function_arg,
const char *file_arg, unsigned int line_arg); const char *file_arg, unsigned int line_arg);
......
...@@ -5361,17 +5361,24 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx) ...@@ -5361,17 +5361,24 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx)
} }
/* /**
Create information_schema table using schema_table data Create information_schema table using schema_table data.
SYNOPSIS @note
create_schema_table() For MYSQL_TYPE_DECIMAL fields only, the field_length member has encoded
into it two numbers, based on modulus of base-10 numbers. In the ones
position is the number of decimals. Tens position is unused. In the
hundreds and thousands position is a two-digit decimal number representing
length. Encode this value with (decimals*100)+length , where
0<decimals<10 and 0<=length<100 .
@param
thd thread handler thd thread handler
@param
schema_table pointer to 'shema_tables' element schema_table pointer to 'shema_tables' element
RETURN @retval \# Pointer to created table
# Pointer to created table @retval NULL Can't create table
0 Can't create table
*/ */
TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
......
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