Commit 698d98be authored by unknown's avatar unknown

Backport:

B-g#24795: SHOW PROFILE implementation

Don't use memory roots to store profiling information, because
memory roots make freeing the data a no-op, and thus long-running
processes with profiling turned on the whole time could eventually 
use all available memory.

Instead, use regular heap allocation and deallocation calls to 
manage profiling data.  Replace the leaky List usage with a similar-
behaving structure named "Queue".


sql/sql_profile.cc:
  Don't use C++ iterators on our simple Queue implementation.  They're
  not implemented and we don't really need them.
  
  Rip out idea of swapping out the thd's mem_root.
sql/sql_profile.h:
  Rip out idea of needing a mem_root.
  
  Implement a Queue that looks and behaves very similarly to memroot-
  using List.
parent 992fc6b2
...@@ -175,11 +175,8 @@ void QUERY_PROFILE::set_query_source(char *query_source_arg, ...@@ -175,11 +175,8 @@ void QUERY_PROFILE::set_query_source(char *query_source_arg,
QUERY_PROFILE::~QUERY_PROFILE() QUERY_PROFILE::~QUERY_PROFILE()
{ {
PROFILE_ENTRY *entry; while (! entries.is_empty())
List_iterator<PROFILE_ENTRY> it(entries); delete entries.pop();
while ((entry= it++) != NULL)
delete entry;
entries.empty();
if (query_source != NULL) if (query_source != NULL)
my_free(query_source, MYF(0)); my_free(query_source, MYF(0));
...@@ -191,7 +188,6 @@ void QUERY_PROFILE::status(const char *status_arg, ...@@ -191,7 +188,6 @@ void QUERY_PROFILE::status(const char *status_arg,
{ {
THD *thd= profiling->thd; THD *thd= profiling->thd;
PROFILE_ENTRY *prof; PROFILE_ENTRY *prof;
MEM_ROOT *saved_mem_root;
DBUG_ENTER("QUERY_PROFILE::status"); DBUG_ENTER("QUERY_PROFILE::status");
/* Blank status. Just return, and thd->proc_info will be set blank later. */ /* Blank status. Just return, and thd->proc_info will be set blank later. */
...@@ -210,22 +206,6 @@ void QUERY_PROFILE::status(const char *status_arg, ...@@ -210,22 +206,6 @@ void QUERY_PROFILE::status(const char *status_arg,
if (unlikely((thd->query_id != server_query_id) && !thd->spcont)) if (unlikely((thd->query_id != server_query_id) && !thd->spcont))
reset(); reset();
/*
In order to keep the profile information between queries (i.e. from
SELECT to the following SHOW PROFILE command) the following code is
necessary to keep the profile from getting freed automatically when
mysqld cleans up after the query.
The "entries" list allocates is memory from the current thd's mem_root.
We substitute our mem_root temporarily so that we intercept those
allocations into our own mem_root.
The thd->mem_root structure is freed after each query is completed,
so temporarily override it.
*/
saved_mem_root= thd->mem_root;
thd->mem_root= &profiling->mem_root;
if (function_arg && file_arg) if (function_arg && file_arg)
{ {
if ((profile_end= prof= new PROFILE_ENTRY(this, status_arg, function_arg, if ((profile_end= prof= new PROFILE_ENTRY(this, status_arg, function_arg,
...@@ -238,9 +218,6 @@ void QUERY_PROFILE::status(const char *status_arg, ...@@ -238,9 +218,6 @@ void QUERY_PROFILE::status(const char *status_arg,
entries.push_back(prof); entries.push_back(prof);
} }
/* Restore mem_root */
thd->mem_root= saved_mem_root;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -252,11 +229,8 @@ void QUERY_PROFILE::reset() ...@@ -252,11 +229,8 @@ void QUERY_PROFILE::reset()
server_query_id= profiling->thd->query_id; /* despite name, is global */ server_query_id= profiling->thd->query_id; /* despite name, is global */
profile_start.collect(); profile_start.collect();
PROFILE_ENTRY *entry; while (! entries.is_empty())
List_iterator<PROFILE_ENTRY> it(entries); delete entries.pop();
while ((entry= it++) != NULL)
delete entry;
entries.empty();
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -344,10 +318,14 @@ bool QUERY_PROFILE::show(uint options) ...@@ -344,10 +318,14 @@ bool QUERY_PROFILE::show(uint options)
struct rusage *last_rusage= &(profile_start.rusage); struct rusage *last_rusage= &(profile_start.rusage);
#endif #endif
List_iterator<PROFILE_ENTRY> it(entries);
PROFILE_ENTRY *entry; PROFILE_ENTRY *entry;
while ((entry= it++) != NULL) void *iterator;
for (iterator= entries.new_iterator();
iterator != NULL;
iterator= entries.iterator_next(iterator))
{ {
entry= entries.iterator_value(iterator);
#ifdef HAVE_GETRUSAGE #ifdef HAVE_GETRUSAGE
struct rusage *rusage= &(entry->rusage); struct rusage *rusage= &(entry->rusage);
#endif #endif
...@@ -463,24 +441,15 @@ bool QUERY_PROFILE::show(uint options) ...@@ -463,24 +441,15 @@ bool QUERY_PROFILE::show(uint options)
PROFILING::PROFILING() PROFILING::PROFILING()
:profile_id_counter(0), keeping(1), current(NULL), last(NULL) :profile_id_counter(0), keeping(1), current(NULL), last(NULL)
{ {
init_sql_alloc(&mem_root,
PROFILE_ALLOC_BLOCK_SIZE,
PROFILE_ALLOC_PREALLOC_SIZE);
} }
PROFILING::~PROFILING() PROFILING::~PROFILING()
{ {
QUERY_PROFILE *prof; while (! history.is_empty())
delete history.pop();
List_iterator<QUERY_PROFILE> it(history);
while ((prof= it++) != NULL)
delete prof;
history.empty();
if (current != NULL) if (current != NULL)
delete current; delete current;
free_root(&mem_root, MYF(0));
} }
void PROFILING::status_change(const char *status_arg, void PROFILING::status_change(const char *status_arg,
...@@ -505,7 +474,6 @@ void PROFILING::status_change(const char *status_arg, ...@@ -505,7 +474,6 @@ void PROFILING::status_change(const char *status_arg,
void PROFILING::store() void PROFILING::store()
{ {
MEM_ROOT *saved_mem_root;
DBUG_ENTER("PROFILING::store"); DBUG_ENTER("PROFILING::store");
/* Already stored */ /* Already stored */
...@@ -517,21 +485,7 @@ void PROFILING::store() ...@@ -517,21 +485,7 @@ void PROFILING::store()
} }
while (history.elements > thd->variables.profiling_history_size) while (history.elements > thd->variables.profiling_history_size)
{ delete history.pop();
QUERY_PROFILE *tmp= history.pop();
delete tmp;
}
/*
Switch out memory roots so that we're sure that we keep what we need
The "history" list implementation allocates its memory in the current
thd's mem_root. We substitute our mem_root temporarily so that we
intercept those allocations into our own mem_root.
*/
saved_mem_root= thd->mem_root;
thd->mem_root= &mem_root;
if (current != NULL) if (current != NULL)
{ {
...@@ -556,9 +510,6 @@ void PROFILING::store() ...@@ -556,9 +510,6 @@ void PROFILING::store()
DBUG_ASSERT(current == NULL); DBUG_ASSERT(current == NULL);
current= new QUERY_PROFILE(this, thd->query, thd->query_length); current= new QUERY_PROFILE(this, thd->query, thd->query_length);
/* Restore memory root */
thd->mem_root= saved_mem_root;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -598,9 +549,13 @@ bool PROFILING::show_profiles() ...@@ -598,9 +549,13 @@ bool PROFILING::show_profiles()
unit->set_limit(sel); unit->set_limit(sel);
List_iterator<QUERY_PROFILE> it(history); void *iterator;
while ((prof= it++) != NULL) for (iterator= history.new_iterator();
iterator != NULL;
iterator= history.iterator_next(iterator))
{ {
prof= history.iterator_value(iterator);
String elapsed; String elapsed;
PROFILE_ENTRY *ps= &prof->profile_start; PROFILE_ENTRY *ps= &prof->profile_start;
...@@ -651,9 +606,13 @@ bool PROFILING::show(uint options, uint profiling_query_id) ...@@ -651,9 +606,13 @@ bool PROFILING::show(uint options, uint profiling_query_id)
DBUG_ENTER("PROFILING::show"); DBUG_ENTER("PROFILING::show");
QUERY_PROFILE *prof; QUERY_PROFILE *prof;
List_iterator<QUERY_PROFILE> it(history); void *iterator;
while ((prof= it++) != NULL) 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) if(prof->profiling_query_id == profiling_query_id)
DBUG_RETURN(prof->show(options)); DBUG_RETURN(prof->show(options));
} }
...@@ -681,11 +640,15 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item ...@@ -681,11 +640,15 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item
TABLE *table= tables->table; TABLE *table= tables->table;
ulonglong row_number= 0; ulonglong row_number= 0;
List_iterator<QUERY_PROFILE> query_it(history);
QUERY_PROFILE *query; QUERY_PROFILE *query;
/* Go through each query in this thread's stored history... */ /* Go through each query in this thread's stored history... */
while ((query= query_it++) != NULL) void *history_iterator;
for (history_iterator= history.new_iterator();
history_iterator != NULL;
history_iterator= history.iterator_next(history_iterator))
{ {
query= history.iterator_value(history_iterator);
PROFILE_ENTRY *ps= &(query->profile_start); PROFILE_ENTRY *ps= &(query->profile_start);
double last_time= ps->time_usecs; double last_time= ps->time_usecs;
#ifdef HAVE_GETRUSAGE #ifdef HAVE_GETRUSAGE
...@@ -699,16 +662,20 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item ...@@ -699,16 +662,20 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item
*/ */
ulonglong seq; ulonglong seq;
List_iterator<PROFILE_ENTRY> step_it(query->entries); void *entry_iterator;
PROFILE_ENTRY *entry; PROFILE_ENTRY *entry;
/* ...and for each query, go through all its state-change steps. */ /* ...and for each query, go through all its state-change steps. */
for (seq= 0, entry= step_it++; for (seq= 0, entry_iterator= query->entries.new_iterator();
entry != NULL; entry_iterator != NULL;
entry_iterator= query->entries.iterator_next(entry_iterator),
#ifdef HAVE_GETRUSAGE #ifdef HAVE_GETRUSAGE
last_rusage= &(entry->rusage), last_rusage= &(entry->rusage),
#endif #endif
seq++, last_time= entry->time_usecs, entry= step_it++, row_number++) seq++, last_time= entry->time_usecs, row_number++)
{ {
entry= query->entries.iterator_value(entry_iterator);
/* Set default values for this row. */ /* Set default values for this row. */
restore_record(table, s->default_values); restore_record(table, s->default_values);
......
...@@ -83,6 +83,112 @@ class QUERY_PROFILE; ...@@ -83,6 +83,112 @@ class QUERY_PROFILE;
class PROFILING; class PROFILING;
/**
Implements a persistent FIFO using server List method names. Not
thread-safe. Intended to be used on thread-local data only.
*/
template <class T> class Queue
{
private:
struct queue_item
{
T *payload;
struct queue_item *next, *previous;
};
struct queue_item *first, *last;
public:
Queue()
{
elements= 0;
first= last= NULL;
}
void empty()
{
struct queue_item *i, *after_i;
for (i= first; i != NULL; i= after_i)
{
after_i= i->next;
my_free((char *) i, MYF(0));
}
elements= 0;
}
ulong elements; /* The count of items in the Queue */
void push_back(T *payload)
{
struct queue_item *new_item;
new_item= (struct queue_item *) my_malloc(sizeof(struct queue_item), MYF(0));
new_item->payload= payload;
if (first == NULL)
first= new_item;
if (last != NULL)
{
DBUG_ASSERT(last->next == NULL);
last->next= new_item;
}
new_item->previous= last;
new_item->next= NULL;
last= new_item;
elements++;
}
T *pop()
{
struct queue_item *old_item= first;
T *ret= NULL;
if (first == NULL)
{
DBUG_PRINT("warning", ("tried to pop nonexistent item from Queue"));
return NULL;
}
ret= old_item->payload;
if (first->next != NULL)
first->next->previous= NULL;
else
last= NULL;
first= first->next;
my_free((char *)old_item, MYF(0));
elements--;
return ret;
}
bool is_empty()
{
DBUG_ASSERT(((elements > 0) && (first != NULL)) || ((elements == 0) || (first == NULL)));
return (elements == 0);
}
void *new_iterator()
{
return first;
}
void *iterator_next(void *current)
{
return ((struct queue_item *) current)->next;
}
T *iterator_value(void *current)
{
return ((struct queue_item *) current)->payload;
}
};
/** /**
A single entry in a single profile. A single entry in a single profile.
*/ */
...@@ -135,7 +241,7 @@ private: ...@@ -135,7 +241,7 @@ private:
char *query_source; char *query_source;
PROFILE_ENTRY profile_start; PROFILE_ENTRY profile_start;
PROFILE_ENTRY *profile_end; PROFILE_ENTRY *profile_end;
List<PROFILE_ENTRY> entries; Queue<PROFILE_ENTRY> entries;
QUERY_PROFILE(PROFILING *profiling_arg, char *query_source_arg, uint query_length_arg); QUERY_PROFILE(PROFILING *profiling_arg, char *query_source_arg, uint query_length_arg);
...@@ -169,13 +275,12 @@ private: ...@@ -169,13 +275,12 @@ private:
Not the system query_id, but a counter unique to profiling. Not the system query_id, but a counter unique to profiling.
*/ */
query_id_t profile_id_counter; query_id_t profile_id_counter;
MEM_ROOT mem_root;
THD *thd; THD *thd;
bool keeping; bool keeping;
QUERY_PROFILE *current; QUERY_PROFILE *current;
QUERY_PROFILE *last; QUERY_PROFILE *last;
List<QUERY_PROFILE> history; Queue<QUERY_PROFILE> history;
query_id_t next_profile_id() { return(profile_id_counter++); } query_id_t next_profile_id() { return(profile_id_counter++); }
......
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