From 9816842b581d3f81030b97f83a46d19e1b08cdd5 Mon Sep 17 00:00:00 2001 From: "cmiller@zippy.cornsilk.net" <> Date: Tue, 3 Apr 2007 17:59:52 -0400 Subject: [PATCH] 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 | 111 ++++++++++++++++----------------------------- sql/sql_profile.h | 111 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 147 insertions(+), 75 deletions(-) diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index 028363f167e..8ed3df9cd2e 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -175,11 +175,8 @@ void QUERY_PROFILE::set_query_source(char *query_source_arg, QUERY_PROFILE::~QUERY_PROFILE() { - PROFILE_ENTRY *entry; - List_iterator it(entries); - while ((entry= it++) != NULL) - delete entry; - entries.empty(); + while (! entries.is_empty()) + delete entries.pop(); if (query_source != NULL) my_free(query_source, MYF(0)); @@ -191,7 +188,6 @@ void QUERY_PROFILE::status(const char *status_arg, { THD *thd= profiling->thd; PROFILE_ENTRY *prof; - MEM_ROOT *saved_mem_root; DBUG_ENTER("QUERY_PROFILE::status"); /* 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, if (unlikely((thd->query_id != server_query_id) && !thd->spcont)) 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 ((profile_end= prof= new PROFILE_ENTRY(this, status_arg, function_arg, @@ -238,9 +218,6 @@ void QUERY_PROFILE::status(const char *status_arg, entries.push_back(prof); } - /* Restore mem_root */ - thd->mem_root= saved_mem_root; - DBUG_VOID_RETURN; } @@ -252,11 +229,8 @@ void QUERY_PROFILE::reset() server_query_id= profiling->thd->query_id; /* despite name, is global */ profile_start.collect(); - PROFILE_ENTRY *entry; - List_iterator it(entries); - while ((entry= it++) != NULL) - delete entry; - entries.empty(); + while (! entries.is_empty()) + delete entries.pop(); } DBUG_VOID_RETURN; } @@ -344,10 +318,14 @@ bool QUERY_PROFILE::show(uint options) struct rusage *last_rusage= &(profile_start.rusage); #endif - List_iterator it(entries); 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 struct rusage *rusage= &(entry->rusage); #endif @@ -463,24 +441,15 @@ bool QUERY_PROFILE::show(uint options) PROFILING::PROFILING() :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() { - QUERY_PROFILE *prof; - - List_iterator it(history); - while ((prof= it++) != NULL) - delete prof; - history.empty(); + while (! history.is_empty()) + delete history.pop(); if (current != NULL) delete current; - - free_root(&mem_root, MYF(0)); } void PROFILING::status_change(const char *status_arg, @@ -505,7 +474,6 @@ void PROFILING::status_change(const char *status_arg, void PROFILING::store() { - MEM_ROOT *saved_mem_root; DBUG_ENTER("PROFILING::store"); /* Already stored */ @@ -517,22 +485,8 @@ void PROFILING::store() } while (history.elements > thd->variables.profiling_history_size) - { - QUERY_PROFILE *tmp= history.pop(); - delete tmp; - } + delete history.pop(); - /* - 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 (keeping && @@ -556,9 +510,6 @@ void PROFILING::store() DBUG_ASSERT(current == NULL); current= new QUERY_PROFILE(this, thd->query, thd->query_length); - /* Restore memory root */ - thd->mem_root= saved_mem_root; - DBUG_VOID_RETURN; } @@ -598,9 +549,13 @@ bool PROFILING::show_profiles() unit->set_limit(sel); - List_iterator it(history); - while ((prof= it++) != NULL) + void *iterator; + for (iterator= history.new_iterator(); + iterator != NULL; + iterator= history.iterator_next(iterator)) { + prof= history.iterator_value(iterator); + String elapsed; PROFILE_ENTRY *ps= &prof->profile_start; @@ -651,9 +606,13 @@ bool PROFILING::show(uint options, uint profiling_query_id) DBUG_ENTER("PROFILING::show"); QUERY_PROFILE *prof; - List_iterator it(history); - while ((prof= it++) != NULL) + 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)); } @@ -681,11 +640,15 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item TABLE *table= tables->table; ulonglong row_number= 0; - List_iterator query_it(history); QUERY_PROFILE *query; /* 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); double last_time= ps->time_usecs; #ifdef HAVE_GETRUSAGE @@ -699,16 +662,20 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item */ ulonglong seq; - List_iterator step_it(query->entries); + void *entry_iterator; PROFILE_ENTRY *entry; /* ...and for each query, go through all its state-change steps. */ - for (seq= 0, entry= step_it++; - entry != NULL; + for (seq= 0, entry_iterator= query->entries.new_iterator(); + entry_iterator != NULL; + entry_iterator= query->entries.iterator_next(entry_iterator), #ifdef HAVE_GETRUSAGE last_rusage= &(entry->rusage), #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. */ restore_record(table, s->default_values); diff --git a/sql/sql_profile.h b/sql/sql_profile.h index 0b387f2a7cb..7913d037551 100644 --- a/sql/sql_profile.h +++ b/sql/sql_profile.h @@ -83,6 +83,112 @@ class QUERY_PROFILE; 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 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. */ @@ -135,7 +241,7 @@ private: char *query_source; PROFILE_ENTRY profile_start; PROFILE_ENTRY *profile_end; - List entries; + Queue entries; QUERY_PROFILE(PROFILING *profiling_arg, char *query_source_arg, uint query_length_arg); @@ -169,13 +275,12 @@ private: Not the system query_id, but a counter unique to profiling. */ query_id_t profile_id_counter; - MEM_ROOT mem_root; THD *thd; bool keeping; QUERY_PROFILE *current; QUERY_PROFILE *last; - List history; + Queue history; query_id_t next_profile_id() { return(profile_id_counter++); }