Skip to content

Commit 9816842

Browse files
author
cmiller@zippy.cornsilk.net
committed
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".
1 parent 0b3dfea commit 9816842

File tree

2 files changed

+147
-75
lines changed

2 files changed

+147
-75
lines changed

sql/sql_profile.cc

+39-72
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,8 @@ void QUERY_PROFILE::set_query_source(char *query_source_arg,
175175

176176
QUERY_PROFILE::~QUERY_PROFILE()
177177
{
178-
PROFILE_ENTRY *entry;
179-
List_iterator<PROFILE_ENTRY> it(entries);
180-
while ((entry= it++) != NULL)
181-
delete entry;
182-
entries.empty();
178+
while (! entries.is_empty())
179+
delete entries.pop();
183180

184181
if (query_source != NULL)
185182
my_free(query_source, MYF(0));
@@ -191,7 +188,6 @@ void QUERY_PROFILE::status(const char *status_arg,
191188
{
192189
THD *thd= profiling->thd;
193190
PROFILE_ENTRY *prof;
194-
MEM_ROOT *saved_mem_root;
195191
DBUG_ENTER("QUERY_PROFILE::status");
196192

197193
/* 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,
210206
if (unlikely((thd->query_id != server_query_id) && !thd->spcont))
211207
reset();
212208

213-
/*
214-
In order to keep the profile information between queries (i.e. from
215-
SELECT to the following SHOW PROFILE command) the following code is
216-
necessary to keep the profile from getting freed automatically when
217-
mysqld cleans up after the query.
218-
219-
The "entries" list allocates is memory from the current thd's mem_root.
220-
We substitute our mem_root temporarily so that we intercept those
221-
allocations into our own mem_root.
222-
223-
The thd->mem_root structure is freed after each query is completed,
224-
so temporarily override it.
225-
*/
226-
saved_mem_root= thd->mem_root;
227-
thd->mem_root= &profiling->mem_root;
228-
229209
if (function_arg && file_arg)
230210
{
231211
if ((profile_end= prof= new PROFILE_ENTRY(this, status_arg, function_arg,
@@ -238,9 +218,6 @@ void QUERY_PROFILE::status(const char *status_arg,
238218
entries.push_back(prof);
239219
}
240220

241-
/* Restore mem_root */
242-
thd->mem_root= saved_mem_root;
243-
244221
DBUG_VOID_RETURN;
245222
}
246223

@@ -252,11 +229,8 @@ void QUERY_PROFILE::reset()
252229
server_query_id= profiling->thd->query_id; /* despite name, is global */
253230
profile_start.collect();
254231

255-
PROFILE_ENTRY *entry;
256-
List_iterator<PROFILE_ENTRY> it(entries);
257-
while ((entry= it++) != NULL)
258-
delete entry;
259-
entries.empty();
232+
while (! entries.is_empty())
233+
delete entries.pop();
260234
}
261235
DBUG_VOID_RETURN;
262236
}
@@ -344,10 +318,14 @@ bool QUERY_PROFILE::show(uint options)
344318
struct rusage *last_rusage= &(profile_start.rusage);
345319
#endif
346320

347-
List_iterator<PROFILE_ENTRY> it(entries);
348321
PROFILE_ENTRY *entry;
349-
while ((entry= it++) != NULL)
322+
void *iterator;
323+
for (iterator= entries.new_iterator();
324+
iterator != NULL;
325+
iterator= entries.iterator_next(iterator))
350326
{
327+
entry= entries.iterator_value(iterator);
328+
351329
#ifdef HAVE_GETRUSAGE
352330
struct rusage *rusage= &(entry->rusage);
353331
#endif
@@ -463,24 +441,15 @@ bool QUERY_PROFILE::show(uint options)
463441
PROFILING::PROFILING()
464442
:profile_id_counter(0), keeping(1), current(NULL), last(NULL)
465443
{
466-
init_sql_alloc(&mem_root,
467-
PROFILE_ALLOC_BLOCK_SIZE,
468-
PROFILE_ALLOC_PREALLOC_SIZE);
469444
}
470445

471446
PROFILING::~PROFILING()
472447
{
473-
QUERY_PROFILE *prof;
474-
475-
List_iterator<QUERY_PROFILE> it(history);
476-
while ((prof= it++) != NULL)
477-
delete prof;
478-
history.empty();
448+
while (! history.is_empty())
449+
delete history.pop();
479450

480451
if (current != NULL)
481452
delete current;
482-
483-
free_root(&mem_root, MYF(0));
484453
}
485454

486455
void PROFILING::status_change(const char *status_arg,
@@ -505,7 +474,6 @@ void PROFILING::status_change(const char *status_arg,
505474

506475
void PROFILING::store()
507476
{
508-
MEM_ROOT *saved_mem_root;
509477
DBUG_ENTER("PROFILING::store");
510478

511479
/* Already stored */
@@ -517,22 +485,8 @@ void PROFILING::store()
517485
}
518486

519487
while (history.elements > thd->variables.profiling_history_size)
520-
{
521-
QUERY_PROFILE *tmp= history.pop();
522-
delete tmp;
523-
}
524-
525-
/*
526-
Switch out memory roots so that we're sure that we keep what we need
527-
528-
The "history" list implementation allocates its memory in the current
529-
thd's mem_root. We substitute our mem_root temporarily so that we
530-
intercept those allocations into our own mem_root.
531-
*/
488+
delete history.pop();
532489

533-
saved_mem_root= thd->mem_root;
534-
thd->mem_root= &mem_root;
535-
536490
if (current != NULL)
537491
{
538492
if (keeping &&
@@ -556,9 +510,6 @@ void PROFILING::store()
556510
DBUG_ASSERT(current == NULL);
557511
current= new QUERY_PROFILE(this, thd->query, thd->query_length);
558512

559-
/* Restore memory root */
560-
thd->mem_root= saved_mem_root;
561-
562513
DBUG_VOID_RETURN;
563514
}
564515

@@ -598,9 +549,13 @@ bool PROFILING::show_profiles()
598549

599550
unit->set_limit(sel);
600551

601-
List_iterator<QUERY_PROFILE> it(history);
602-
while ((prof= it++) != NULL)
552+
void *iterator;
553+
for (iterator= history.new_iterator();
554+
iterator != NULL;
555+
iterator= history.iterator_next(iterator))
603556
{
557+
prof= history.iterator_value(iterator);
558+
604559
String elapsed;
605560

606561
PROFILE_ENTRY *ps= &prof->profile_start;
@@ -651,9 +606,13 @@ bool PROFILING::show(uint options, uint profiling_query_id)
651606
DBUG_ENTER("PROFILING::show");
652607
QUERY_PROFILE *prof;
653608

654-
List_iterator<QUERY_PROFILE> it(history);
655-
while ((prof= it++) != NULL)
609+
void *iterator;
610+
for (iterator= history.new_iterator();
611+
iterator != NULL;
612+
iterator= history.iterator_next(iterator))
656613
{
614+
prof= history.iterator_value(iterator);
615+
657616
if(prof->profiling_query_id == profiling_query_id)
658617
DBUG_RETURN(prof->show(options));
659618
}
@@ -681,11 +640,15 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item
681640
TABLE *table= tables->table;
682641
ulonglong row_number= 0;
683642

684-
List_iterator<QUERY_PROFILE> query_it(history);
685643
QUERY_PROFILE *query;
686644
/* Go through each query in this thread's stored history... */
687-
while ((query= query_it++) != NULL)
645+
void *history_iterator;
646+
for (history_iterator= history.new_iterator();
647+
history_iterator != NULL;
648+
history_iterator= history.iterator_next(history_iterator))
688649
{
650+
query= history.iterator_value(history_iterator);
651+
689652
PROFILE_ENTRY *ps= &(query->profile_start);
690653
double last_time= ps->time_usecs;
691654
#ifdef HAVE_GETRUSAGE
@@ -699,16 +662,20 @@ int PROFILING::fill_statistics_info(THD *thd, struct st_table_list *tables, Item
699662
*/
700663
ulonglong seq;
701664

702-
List_iterator<PROFILE_ENTRY> step_it(query->entries);
665+
void *entry_iterator;
703666
PROFILE_ENTRY *entry;
704667
/* ...and for each query, go through all its state-change steps. */
705-
for (seq= 0, entry= step_it++;
706-
entry != NULL;
668+
for (seq= 0, entry_iterator= query->entries.new_iterator();
669+
entry_iterator != NULL;
670+
entry_iterator= query->entries.iterator_next(entry_iterator),
707671
#ifdef HAVE_GETRUSAGE
708672
last_rusage= &(entry->rusage),
709673
#endif
710-
seq++, last_time= entry->time_usecs, entry= step_it++, row_number++)
674+
seq++, last_time= entry->time_usecs, row_number++)
711675
{
676+
entry= query->entries.iterator_value(entry_iterator);
677+
678+
712679
/* Set default values for this row. */
713680
restore_record(table, s->default_values);
714681

sql/sql_profile.h

+108-3
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,112 @@ class QUERY_PROFILE;
8383
class PROFILING;
8484

8585

86+
/**
87+
Implements a persistent FIFO using server List method names. Not
88+
thread-safe. Intended to be used on thread-local data only.
89+
*/
90+
template <class T> class Queue
91+
{
92+
private:
93+
94+
struct queue_item
95+
{
96+
T *payload;
97+
struct queue_item *next, *previous;
98+
};
99+
100+
struct queue_item *first, *last;
101+
102+
public:
103+
Queue()
104+
{
105+
elements= 0;
106+
first= last= NULL;
107+
}
108+
109+
void empty()
110+
{
111+
struct queue_item *i, *after_i;
112+
for (i= first; i != NULL; i= after_i)
113+
{
114+
after_i= i->next;
115+
my_free((char *) i, MYF(0));
116+
}
117+
elements= 0;
118+
}
119+
120+
ulong elements; /* The count of items in the Queue */
121+
122+
void push_back(T *payload)
123+
{
124+
struct queue_item *new_item;
125+
126+
new_item= (struct queue_item *) my_malloc(sizeof(struct queue_item), MYF(0));
127+
128+
new_item->payload= payload;
129+
130+
if (first == NULL)
131+
first= new_item;
132+
if (last != NULL)
133+
{
134+
DBUG_ASSERT(last->next == NULL);
135+
last->next= new_item;
136+
}
137+
new_item->previous= last;
138+
new_item->next= NULL;
139+
last= new_item;
140+
141+
elements++;
142+
}
143+
144+
T *pop()
145+
{
146+
struct queue_item *old_item= first;
147+
T *ret= NULL;
148+
149+
if (first == NULL)
150+
{
151+
DBUG_PRINT("warning", ("tried to pop nonexistent item from Queue"));
152+
return NULL;
153+
}
154+
155+
ret= old_item->payload;
156+
if (first->next != NULL)
157+
first->next->previous= NULL;
158+
else
159+
last= NULL;
160+
first= first->next;
161+
162+
my_free((char *)old_item, MYF(0));
163+
elements--;
164+
165+
return ret;
166+
}
167+
168+
bool is_empty()
169+
{
170+
DBUG_ASSERT(((elements > 0) && (first != NULL)) || ((elements == 0) || (first == NULL)));
171+
return (elements == 0);
172+
}
173+
174+
void *new_iterator()
175+
{
176+
return first;
177+
}
178+
179+
void *iterator_next(void *current)
180+
{
181+
return ((struct queue_item *) current)->next;
182+
}
183+
184+
T *iterator_value(void *current)
185+
{
186+
return ((struct queue_item *) current)->payload;
187+
}
188+
189+
};
190+
191+
86192
/**
87193
A single entry in a single profile.
88194
*/
@@ -135,7 +241,7 @@ class QUERY_PROFILE
135241
char *query_source;
136242
PROFILE_ENTRY profile_start;
137243
PROFILE_ENTRY *profile_end;
138-
List<PROFILE_ENTRY> entries;
244+
Queue<PROFILE_ENTRY> entries;
139245

140246

141247
QUERY_PROFILE(PROFILING *profiling_arg, char *query_source_arg, uint query_length_arg);
@@ -169,13 +275,12 @@ class PROFILING
169275
Not the system query_id, but a counter unique to profiling.
170276
*/
171277
query_id_t profile_id_counter;
172-
MEM_ROOT mem_root;
173278
THD *thd;
174279
bool keeping;
175280

176281
QUERY_PROFILE *current;
177282
QUERY_PROFILE *last;
178-
List<QUERY_PROFILE> history;
283+
Queue<QUERY_PROFILE> history;
179284

180285
query_id_t next_profile_id() { return(profile_id_counter++); }
181286

0 commit comments

Comments
 (0)