1
by Chuck Short
Import upstream version 5.1.30 |
1 |
/* Copyright (C) 2007 MySQL AB
|
2 |
||
3 |
This program is free software; you can redistribute it and/or modify
|
|
4 |
it under the terms of the GNU General Public License as published by
|
|
5 |
the Free Software Foundation; version 2 of the License.
|
|
6 |
||
7 |
This program is distributed in the hope that it will be useful,
|
|
8 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10 |
GNU General Public License for more details.
|
|
11 |
||
12 |
You should have received a copy of the GNU General Public License
|
|
13 |
along with this program; if not, write to the Free Software
|
|
14 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
15 |
||
16 |
||
17 |
/**
|
|
18 |
@file
|
|
19 |
||
20 |
Implement query profiling as as list of metaphorical fences, with one fence
|
|
21 |
per query, and each fencepost a change of thd->proc_info state (with a
|
|
22 |
snapshot of system statistics). When asked, we can then iterate over the
|
|
23 |
fenceposts and calculate the distance between them, to inform the user what
|
|
24 |
happened during a particular query or thd->proc_info state.
|
|
25 |
||
26 |
User variables that inform profiling behavior:
|
|
27 |
- "profiling", boolean, session only, "Are queries profiled?"
|
|
28 |
- "profiling_history_size", integer, session + global, "Num queries stored?"
|
|
29 |
*/
|
|
30 |
||
31 |
||
32 |
#include "mysql_priv.h" |
|
33 |
#include "my_sys.h" |
|
34 |
||
35 |
#define TIME_FLOAT_DIGITS 9
|
|
36 |
/** two vals encoded: (dec*100)+len */
|
|
37 |
#define TIME_I_S_DECIMAL_SIZE (TIME_FLOAT_DIGITS*100)+(TIME_FLOAT_DIGITS-3)
|
|
38 |
||
39 |
#define MAX_QUERY_LENGTH 300
|
|
40 |
||
41 |
/* Reserved for systems that can't record the function name in source. */
|
|
42 |
const char * const _unknown_func_ = "<unknown>"; |
|
43 |
||
44 |
/**
|
|
45 |
Connects Information_Schema and Profiling.
|
|
46 |
*/
|
|
47 |
int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables, |
|
48 |
Item *cond) |
|
49 |
{
|
|
50 |
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
|
|
51 |
return(thd->profiling.fill_statistics_info(thd, tables, cond)); |
|
52 |
#else
|
|
53 |
my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling"); |
|
54 |
return(1); |
|
55 |
#endif
|
|
56 |
}
|
|
57 |
||
58 |
ST_FIELD_INFO query_profile_statistics_info[]= |
|
59 |
{
|
|
60 |
/* name, length, type, value, maybe_null, old_name, open_method */
|
|
61 |
{"QUERY_ID", 20, MYSQL_TYPE_LONG, 0, false, "Query_id", SKIP_OPEN_TABLE}, |
|
62 |
{"SEQ", 20, MYSQL_TYPE_LONG, 0, false, "Seq", SKIP_OPEN_TABLE}, |
|
63 |
{"STATE", 30, MYSQL_TYPE_STRING, 0, false, "Status", SKIP_OPEN_TABLE}, |
|
64 |
{"DURATION", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, false, "Duration", SKIP_OPEN_TABLE}, |
|
65 |
{"CPU_USER", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_user", SKIP_OPEN_TABLE}, |
|
66 |
{"CPU_SYSTEM", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_system", SKIP_OPEN_TABLE}, |
|
67 |
{"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_voluntary", SKIP_OPEN_TABLE}, |
|
68 |
{"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_involuntary", SKIP_OPEN_TABLE}, |
|
69 |
{"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_in", SKIP_OPEN_TABLE}, |
|
70 |
{"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_out", SKIP_OPEN_TABLE}, |
|
71 |
{"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0, true, "Messages_sent", SKIP_OPEN_TABLE}, |
|
72 |
{"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0, true, "Messages_received", SKIP_OPEN_TABLE}, |
|
73 |
{"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_major", SKIP_OPEN_TABLE}, |
|
74 |
{"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_minor", SKIP_OPEN_TABLE}, |
|
75 |
{"SWAPS", 20, MYSQL_TYPE_LONG, 0, true, "Swaps", SKIP_OPEN_TABLE}, |
|
76 |
{"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0, true, "Source_function", SKIP_OPEN_TABLE}, |
|
77 |
{"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0, true, "Source_file", SKIP_OPEN_TABLE}, |
|
78 |
{"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0, true, "Source_line", SKIP_OPEN_TABLE}, |
|
79 |
{NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0} |
|
80 |
};
|
|
81 |
||
82 |
||
83 |
int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table) |
|
84 |
{
|
|
85 |
int profile_options = thd->lex->profile_options; |
|
86 |
int fields_include_condition_truth_values[]= { |
|
87 |
FALSE, /* Query_id */ |
|
88 |
FALSE, /* Seq */ |
|
89 |
TRUE, /* Status */ |
|
90 |
TRUE, /* Duration */ |
|
91 |
profile_options & PROFILE_CPU, /* CPU_user */ |
|
92 |
profile_options & PROFILE_CPU, /* CPU_system */ |
|
93 |
profile_options & PROFILE_CONTEXT, /* Context_voluntary */ |
|
94 |
profile_options & PROFILE_CONTEXT, /* Context_involuntary */ |
|
95 |
profile_options & PROFILE_BLOCK_IO, /* Block_ops_in */ |
|
96 |
profile_options & PROFILE_BLOCK_IO, /* Block_ops_out */ |
|
97 |
profile_options & PROFILE_IPC, /* Messages_sent */ |
|
98 |
profile_options & PROFILE_IPC, /* Messages_received */ |
|
99 |
profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_major */ |
|
100 |
profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_minor */ |
|
101 |
profile_options & PROFILE_SWAPS, /* Swaps */ |
|
102 |
profile_options & PROFILE_SOURCE, /* Source_function */ |
|
103 |
profile_options & PROFILE_SOURCE, /* Source_file */ |
|
104 |
profile_options & PROFILE_SOURCE, /* Source_line */ |
|
105 |
};
|
|
106 |
||
107 |
ST_FIELD_INFO *field_info; |
|
108 |
Name_resolution_context *context= &thd->lex->select_lex.context; |
|
109 |
int i; |
|
110 |
||
111 |
for (i= 0; schema_table->fields_info[i].field_name != NULL; i++) |
|
112 |
{
|
|
113 |
if (! fields_include_condition_truth_values[i]) |
|
114 |
continue; |
|
115 |
||
116 |
field_info= &schema_table->fields_info[i]; |
|
117 |
Item_field *field= new Item_field(context, |
|
118 |
NullS, NullS, field_info->field_name); |
|
119 |
if (field) |
|
120 |
{
|
|
121 |
field->set_name(field_info->old_name, |
|
122 |
strlen(field_info->old_name), |
|
123 |
system_charset_info); |
|
124 |
if (add_item_to_list(thd, field)) |
|
125 |
return 1; |
|
126 |
}
|
|
127 |
}
|
|
128 |
return 0; |
|
129 |
}
|
|
130 |
||
131 |
||
132 |
#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
|
|
133 |
||
134 |
#define RUSAGE_USEC(tv) ((tv).tv_sec*1000*1000 + (tv).tv_usec)
|
|
135 |
#define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2)))
|
|
136 |
||
137 |
||
138 |
PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, const char |
|
139 |
*status_arg) |
|
140 |
:profile(profile_arg) |
|
141 |
{
|
|
142 |
collect(); |
|
143 |
set_label(status_arg, NULL, NULL, 0); |
|
144 |
}
|
|
145 |
||
146 |
PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, |
|
147 |
const char *status_arg, |
|
148 |
const char *function_arg, |
|
149 |
const char *file_arg, |
|
150 |
unsigned int line_arg) |
|
151 |
:profile(profile_arg) |
|
152 |
{
|
|
153 |
collect(); |
|
154 |
set_label(status_arg, function_arg, file_arg, line_arg); |
|
155 |
}
|
|
156 |
||
157 |
PROF_MEASUREMENT::~PROF_MEASUREMENT() |
|
158 |
{
|
|
159 |
if (allocated_status_memory != NULL) |
|
160 |
my_free(allocated_status_memory, MYF(0)); |
|
161 |
status= function= file= NULL; |
|
162 |
}
|
|
163 |
||
164 |
void PROF_MEASUREMENT::set_label(const char *status_arg, |
|
165 |
const char *function_arg, |
|
166 |
const char *file_arg, unsigned int line_arg) |
|
167 |
{
|
|
168 |
size_t sizes[3]; /* 3 == status+function+file */ |
|
169 |
char *cursor; |
|
170 |
||
171 |
/*
|
|
172 |
Compute all the space we'll need to allocate one block for everything
|
|
173 |
we'll need, instead of N mallocs.
|
|
174 |
*/
|
|
175 |
sizes[0]= (status_arg == NULL) ? 0 : strlen(status_arg) + 1; |
|
176 |
sizes[1]= (function_arg == NULL) ? 0 : strlen(function_arg) + 1; |
|
177 |
sizes[2]= (file_arg == NULL) ? 0 : strlen(file_arg) + 1; |
|
178 |
||
179 |
allocated_status_memory= (char *) my_malloc(sizes[0] + sizes[1] + sizes[2], MYF(0)); |
|
180 |
DBUG_ASSERT(allocated_status_memory != NULL); |
|
181 |
||
182 |
cursor= allocated_status_memory; |
|
183 |
||
184 |
if (status_arg != NULL) |
|
185 |
{
|
|
186 |
strcpy(cursor, status_arg); |
|
187 |
status= cursor; |
|
188 |
cursor+= sizes[0]; |
|
189 |
}
|
|
190 |
else
|
|
191 |
status= NULL; |
|
192 |
||
193 |
if (function_arg != NULL) |
|
194 |
{
|
|
195 |
strcpy(cursor, function_arg); |
|
196 |
function= cursor; |
|
197 |
cursor+= sizes[1]; |
|
198 |
}
|
|
199 |
else
|
|
200 |
function= NULL; |
|
201 |
||
202 |
if (file_arg != NULL) |
|
203 |
{
|
|
204 |
strcpy(cursor, file_arg); |
|
205 |
file= cursor; |
|
206 |
cursor+= sizes[2]; |
|
207 |
}
|
|
208 |
else
|
|
209 |
file= NULL; |
|
210 |
||
211 |
line= line_arg; |
|
212 |
}
|
|
213 |
||
214 |
/**
|
|
215 |
This updates the statistics for this moment of time. It captures the state
|
|
216 |
of the running system, so later we can compare points in time and infer what
|
|
217 |
happened in the mean time. It should only be called immediately upon
|
|
218 |
instantiation of this PROF_MEASUREMENT.
|
|
219 |
||
220 |
@todo Implement resource capture for OSes not like BSD.
|
|
221 |
*/
|
|
222 |
void PROF_MEASUREMENT::collect() |
|
223 |
{
|
|
224 |
time_usecs= (double) my_getsystime() / 10.0; /* 1 sec was 1e7, now is 1e6 */ |
|
225 |
#ifdef HAVE_GETRUSAGE
|
|
226 |
getrusage(RUSAGE_SELF, &rusage); |
|
227 |
#endif
|
|
228 |
}
|
|
229 |
||
230 |
||
231 |
QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg) |
|
232 |
:profiling(profiling_arg), profiling_query_id(0), query_source(NULL) |
|
233 |
{
|
|
234 |
profile_start= new PROF_MEASUREMENT(this, status_arg); |
|
235 |
entries.push_back(profile_start); |
|
236 |
profile_end= profile_start; |
|
237 |
}
|
|
238 |
||
239 |
QUERY_PROFILE::~QUERY_PROFILE() |
|
240 |
{
|
|
241 |
while (! entries.is_empty()) |
|
242 |
delete entries.pop(); |
|
243 |
||
244 |
if (query_source != NULL) |
|
245 |
my_free(query_source, MYF(0)); |
|
246 |
}
|
|
247 |
||
248 |
/**
|
|
249 |
@todo Provide a way to include the full text, as in SHOW PROCESSLIST.
|
|
250 |
*/
|
|
251 |
void QUERY_PROFILE::set_query_source(char *query_source_arg, |
|
252 |
uint query_length_arg) |
|
253 |
{
|
|
254 |
/* Truncate to avoid DoS attacks. */
|
|
255 |
uint length= min(MAX_QUERY_LENGTH, query_length_arg); |
|
256 |
||
257 |
DBUG_ASSERT(query_source == NULL); /* we don't leak memory */ |
|
258 |
if (query_source_arg != NULL) |
|
259 |
query_source= my_strndup(query_source_arg, length, MYF(0)); |
|
260 |
}
|
|
261 |
||
262 |
void QUERY_PROFILE::new_status(const char *status_arg, |
|
263 |
const char *function_arg, const char *file_arg, |
|
264 |
unsigned int line_arg) |
|
265 |
{
|
|
266 |
PROF_MEASUREMENT *prof; |
|
267 |
DBUG_ENTER("QUERY_PROFILE::status"); |
|
268 |
||
269 |
DBUG_ASSERT(status_arg != NULL); |
|
270 |
||
271 |
if ((function_arg != NULL) && (file_arg != NULL)) |
|
272 |
prof= new PROF_MEASUREMENT(this, status_arg, function_arg, file_arg, line_arg); |
|
273 |
else
|
|
274 |
prof= new PROF_MEASUREMENT(this, status_arg); |
|
275 |
||
276 |
profile_end= prof; |
|
277 |
entries.push_back(prof); |
|
278 |
||
279 |
DBUG_VOID_RETURN; |
|
280 |
}
|
|
281 |
||
282 |
||
283 |
||
284 |
PROFILING::PROFILING() |
|
285 |
:profile_id_counter(1), current(NULL), last(NULL) |
|
286 |
{
|
|
287 |
}
|
|
288 |
||
289 |
PROFILING::~PROFILING() |
|
290 |
{
|
|
291 |
while (! history.is_empty()) |
|
292 |
delete history.pop(); |
|
293 |
||
294 |
if (current != NULL) |
|
295 |
delete current; |
|
296 |
}
|
|
297 |
||
298 |
/**
|
|
299 |
A new state is given, and that signals the profiler to start a new
|
|
300 |
timed step for the current query's profile.
|
|
301 |
||
302 |
@param status_arg name of this step
|
|
303 |
@param function_arg calling function (usually supplied from compiler)
|
|
304 |
@param function_arg calling file (usually supplied from compiler)
|
|
305 |
@param function_arg calling line number (usually supplied from compiler)
|
|
306 |
*/
|
|
307 |
void PROFILING::status_change(const char *status_arg, |
|
308 |
const char *function_arg, |
|
309 |
const char *file_arg, unsigned int line_arg) |
|
310 |
{
|
|
311 |
DBUG_ENTER("PROFILING::status_change"); |
|
312 |
||
313 |
if (status_arg == NULL) /* We don't know how to handle that */ |
|
314 |
DBUG_VOID_RETURN; |
|
315 |
||
316 |
if (current == NULL) /* This profile was already discarded. */ |
|
317 |
DBUG_VOID_RETURN; |
|
318 |
||
319 |
if (unlikely(enabled)) |
|
320 |
current->new_status(status_arg, function_arg, file_arg, line_arg); |
|
321 |
||
322 |
DBUG_VOID_RETURN; |
|
323 |
}
|
|
324 |
||
325 |
/**
|
|
326 |
Prepare to start processing a new query. It is an error to do this
|
|
327 |
if there's a query already in process; nesting is not supported.
|
|
328 |
||
329 |
@param initial_state (optional) name of period before first state change
|
|
330 |
*/
|
|
331 |
void PROFILING::start_new_query(const char *initial_state) |
|
332 |
{
|
|
333 |
DBUG_ENTER("PROFILING::start_new_query"); |
|
334 |
||
335 |
/* This should never happen unless the server is radically altered. */
|
|
336 |
if (unlikely(current != NULL)) |
|
337 |
{
|
|
338 |
DBUG_PRINT("warning", ("profiling code was asked to start a new query " |
|
339 |
"before the old query was finished. This is "
|
|
340 |
"probably a bug.")); |
|
341 |
finish_current_query(); |
|
342 |
}
|
|
343 |
||
344 |
enabled= (((thd)->options & OPTION_PROFILING) != 0); |
|
345 |
||
346 |
if (! enabled) DBUG_VOID_RETURN; |
|
347 |
||
348 |
DBUG_ASSERT(current == NULL); |
|
349 |
current= new QUERY_PROFILE(this, initial_state); |
|
350 |
||
351 |
DBUG_VOID_RETURN; |
|
352 |
}
|
|
353 |
||
354 |
/**
|
|
355 |
Throw away the current profile, because it's useless or unwanted
|
|
356 |
or corrupted.
|
|
357 |
*/
|
|
358 |
void PROFILING::discard_current_query() |
|
359 |
{
|
|
360 |
DBUG_ENTER("PROFILING::discard_current_profile"); |
|
361 |
||
362 |
delete current; |
|
363 |
current= NULL; |
|
364 |
||
365 |
DBUG_VOID_RETURN; |
|
366 |
}
|
|
367 |
||
368 |
/**
|
|
369 |
Try to save the current profile entry, clean up the data if it shouldn't be
|
|
370 |
saved, and maintain the profile history size. Naturally, this may not
|
|
371 |
succeed if the profile was previously discarded, and that's expected.
|
|
372 |
*/
|
|
373 |
void PROFILING::finish_current_query() |
|
374 |
{
|
|
375 |
DBUG_ENTER("PROFILING::finish_current_profile"); |
|
376 |
if (current != NULL) |
|
377 |
{
|
|
378 |
/* The last fence-post, so we can support the span before this. */
|
|
379 |
status_change("ending", NULL, NULL, 0); |
|
380 |
||
381 |
if ((enabled) && /* ON at start? */ |
|
382 |
((thd->options & OPTION_PROFILING) != 0) && /* and ON at end? */ |
|
383 |
(current->query_source != NULL) && |
|
384 |
(! current->entries.is_empty())) |
|
385 |
{
|
|
386 |
current->profiling_query_id= next_profile_id(); /* assign an id */ |
|
387 |
||
388 |
history.push_back(current); |
|
389 |
last= current; /* never contains something that is not in the history. */ |
|
390 |
current= NULL; |
|
391 |
}
|
|
392 |
else
|
|
393 |
{
|
|
394 |
delete current; |
|
395 |
current= NULL; |
|
396 |
}
|
|
397 |
}
|
|
398 |
||
399 |
/* Maintain the history size. */
|
|
400 |
while (history.elements > thd->variables.profiling_history_size) |
|
401 |
delete history.pop(); |
|
402 |
||
403 |
DBUG_VOID_RETURN; |
|
404 |
}
|
|
405 |
||
406 |
bool PROFILING::show_profiles() |
|
407 |
{
|
|
408 |
DBUG_ENTER("PROFILING::show_profiles"); |
|
409 |
QUERY_PROFILE *prof; |
|
410 |
List<Item> field_list; |
|
411 |
||
412 |
field_list.push_back(new Item_return_int("Query_ID", 10, |
|
413 |
MYSQL_TYPE_LONG)); |
|
414 |
field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS-1, |
|
415 |
MYSQL_TYPE_DOUBLE)); |
|
416 |
field_list.push_back(new Item_empty_string("Query", 40)); |
|
417 |
||
418 |
if (thd->protocol->send_fields(&field_list, |
|
419 |
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) |
|
420 |
DBUG_RETURN(TRUE); |
|
421 |
||
422 |
SELECT_LEX *sel= &thd->lex->select_lex; |
|
423 |
SELECT_LEX_UNIT *unit= &thd->lex->unit; |
|
424 |
ha_rows idx= 0; |
|
425 |
Protocol *protocol= thd->protocol; |
|
426 |
||
427 |
unit->set_limit(sel); |
|
428 |
||
429 |
void *iterator; |
|
430 |
for (iterator= history.new_iterator(); |
|
431 |
iterator != NULL; |
|
432 |
iterator= history.iterator_next(iterator)) |
|
433 |
{
|
|
434 |
prof= history.iterator_value(iterator); |
|
435 |
||
436 |
String elapsed; |
|
437 |
||
438 |
PROF_MEASUREMENT *ps= prof->profile_start; |
|
439 |
PROF_MEASUREMENT *pe= prof->profile_end; |
|
440 |
||
441 |
if (++idx <= unit->offset_limit_cnt) |
|
442 |
continue; |
|
443 |
if (idx > unit->select_limit_cnt) |
|
444 |
break; |
|
445 |
||
446 |
protocol->prepare_for_resend(); |
|
447 |
protocol->store((uint32)(prof->profiling_query_id)); |
|
448 |
protocol->store((double)(pe->time_usecs - ps->time_usecs)/(1000.0*1000), |
|
449 |
(uint32) TIME_FLOAT_DIGITS-1, &elapsed); |
|
450 |
if (prof->query_source != NULL) |
|
451 |
protocol->store(prof->query_source, strlen(prof->query_source), |
|
452 |
system_charset_info); |
|
453 |
else
|
|
454 |
protocol->store_null(); |
|
455 |
||
456 |
if (protocol->write()) |
|
457 |
DBUG_RETURN(TRUE); |
|
458 |
}
|
|
459 |
my_eof(thd); |
|
460 |
DBUG_RETURN(FALSE); |
|
461 |
}
|
|
462 |
||
463 |
/**
|
|
464 |
At a point in execution where we know the query source, save the text
|
|
465 |
of it in the query profile.
|
|
466 |
||
467 |
This must be called exactly once per descrete statement.
|
|
468 |
*/
|
|
469 |
void PROFILING::set_query_source(char *query_source_arg, uint query_length_arg) |
|
470 |
{
|
|
471 |
DBUG_ENTER("PROFILING::set_query_source"); |
|
472 |
||
473 |
if (! enabled) |
|
474 |
DBUG_VOID_RETURN; |
|
475 |
||
476 |
if (current != NULL) |
|
477 |
current->set_query_source(query_source_arg, query_length_arg); |
|
478 |
else
|
|
479 |
DBUG_PRINT("info", ("no current profile to send query source to")); |
|
480 |
DBUG_VOID_RETURN; |
|
481 |
}
|
|
482 |
||
483 |
/**
|
|
484 |
Fill the information schema table, "query_profile", as defined in show.cc .
|
|
485 |
There are two ways to get to this function: Selecting from the information
|
|
486 |
schema, and a SHOW command.
|
|
487 |
*/
|
|
488 |
int PROFILING::fill_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond) |
|
489 |
{
|
|
490 |
DBUG_ENTER("PROFILING::fill_statistics_info"); |
|
491 |
TABLE *table= tables->table; |
|
492 |
ulonglong row_number= 0; |
|
493 |
||
494 |
QUERY_PROFILE *query; |
|
495 |
/* Go through each query in this thread's stored history... */
|
|
496 |
void *history_iterator; |
|
497 |
for (history_iterator= history.new_iterator(); |
|
498 |
history_iterator != NULL; |
|
499 |
history_iterator= history.iterator_next(history_iterator)) |
|
500 |
{
|
|
501 |
query= history.iterator_value(history_iterator); |
|
502 |
||
503 |
/*
|
|
504 |
Because we put all profiling info into a table that may be reordered, let
|
|
505 |
us also include a numbering of each state per query. The query_id and
|
|
506 |
the "seq" together are unique.
|
|
507 |
*/
|
|
508 |
ulonglong seq; |
|
509 |
||
510 |
void *entry_iterator; |
|
511 |
PROF_MEASUREMENT *entry, *previous= NULL; |
|
512 |
/* ...and for each query, go through all its state-change steps. */
|
|
513 |
for (seq= 0, entry_iterator= query->entries.new_iterator(); |
|
514 |
entry_iterator != NULL; |
|
515 |
entry_iterator= query->entries.iterator_next(entry_iterator), |
|
516 |
seq++, previous=entry, row_number++) |
|
517 |
{
|
|
518 |
entry= query->entries.iterator_value(entry_iterator); |
|
519 |
||
520 |
/* Skip the first. We count spans of fence, not fence-posts. */
|
|
521 |
if (previous == NULL) continue; |
|
522 |
||
523 |
if (thd->lex->sql_command == SQLCOM_SHOW_PROFILE) |
|
524 |
{
|
|
525 |
/*
|
|
526 |
We got here via a SHOW command. That means that we stored
|
|
527 |
information about the query we wish to show and that isn't
|
|
528 |
in a WHERE clause at a higher level to filter out rows we
|
|
529 |
wish to exclude.
|
|
530 |
||
531 |
Because that functionality isn't available in the server yet,
|
|
532 |
we must filter here, at the wrong level. Once one can con-
|
|
533 |
struct where and having conditions at the SQL layer, then this
|
|
534 |
condition should be ripped out.
|
|
535 |
*/
|
|
536 |
if (thd->lex->profile_query_id == 0) /* 0 == show final query */ |
|
537 |
{
|
|
538 |
if (query != last) |
|
539 |
continue; |
|
540 |
}
|
|
541 |
else
|
|
542 |
{
|
|
543 |
if (thd->lex->profile_query_id != query->profiling_query_id) |
|
544 |
continue; |
|
545 |
}
|
|
546 |
}
|
|
547 |
||
548 |
/* Set default values for this row. */
|
|
549 |
restore_record(table, s->default_values); |
|
550 |
||
551 |
/*
|
|
552 |
The order of these fields is set by the query_profile_statistics_info
|
|
553 |
array.
|
|
554 |
*/
|
|
1.1.1
by Mathias Gug
Import upstream version 5.1.31 |
555 |
table->field[0]->store((ulonglong) query->profiling_query_id, TRUE); |
556 |
table->field[1]->store((ulonglong) seq, TRUE); /* the step in the sequence */ |
|
1
by Chuck Short
Import upstream version 5.1.30 |
557 |
/*
|
558 |
This entry, n, has a point in time, T(n), and a status phrase, S(n).
|
|
559 |
The status phrase S(n) describes the period of time that begins at
|
|
560 |
T(n). The previous status phrase S(n-1) describes the period of time
|
|
561 |
that starts at T(n-1) and ends at T(n). Since we want to describe the
|
|
562 |
time that a status phrase took T(n)-T(n-1), this line must describe the
|
|
563 |
previous status.
|
|
564 |
*/
|
|
565 |
table->field[2]->store(previous->status, strlen(previous->status), |
|
566 |
system_charset_info); |
|
567 |
||
568 |
my_decimal duration_decimal; |
|
569 |
double2my_decimal(E_DEC_FATAL_ERROR, |
|
570 |
(entry->time_usecs-previous->time_usecs)/(1000.0*1000), |
|
571 |
&duration_decimal); |
|
572 |
||
573 |
table->field[3]->store_decimal(&duration_decimal); |
|
574 |
||
575 |
||
576 |
#ifdef HAVE_GETRUSAGE
|
|
577 |
||
578 |
my_decimal cpu_utime_decimal, cpu_stime_decimal; |
|
579 |
||
580 |
double2my_decimal(E_DEC_FATAL_ERROR, |
|
581 |
RUSAGE_DIFF_USEC(entry->rusage.ru_utime, |
|
582 |
previous->rusage.ru_utime) / |
|
583 |
(1000.0*1000), |
|
584 |
&cpu_utime_decimal); |
|
585 |
||
586 |
double2my_decimal(E_DEC_FATAL_ERROR, |
|
587 |
RUSAGE_DIFF_USEC(entry->rusage.ru_stime, |
|
588 |
previous->rusage.ru_stime) / |
|
589 |
(1000.0*1000), |
|
590 |
&cpu_stime_decimal); |
|
591 |
||
592 |
table->field[4]->store_decimal(&cpu_utime_decimal); |
|
593 |
table->field[5]->store_decimal(&cpu_stime_decimal); |
|
594 |
table->field[4]->set_notnull(); |
|
595 |
table->field[5]->set_notnull(); |
|
596 |
#else
|
|
597 |
/* TODO: Add CPU-usage info for non-BSD systems */
|
|
598 |
#endif
|
|
599 |
||
600 |
#ifdef HAVE_GETRUSAGE
|
|
601 |
table->field[6]->store((uint32)(entry->rusage.ru_nvcsw - |
|
602 |
previous->rusage.ru_nvcsw)); |
|
603 |
table->field[6]->set_notnull(); |
|
604 |
table->field[7]->store((uint32)(entry->rusage.ru_nivcsw - |
|
605 |
previous->rusage.ru_nivcsw)); |
|
606 |
table->field[7]->set_notnull(); |
|
607 |
#else
|
|
608 |
/* TODO: Add context switch info for non-BSD systems */
|
|
609 |
#endif
|
|
610 |
||
611 |
#ifdef HAVE_GETRUSAGE
|
|
612 |
table->field[8]->store((uint32)(entry->rusage.ru_inblock - |
|
613 |
previous->rusage.ru_inblock)); |
|
614 |
table->field[8]->set_notnull(); |
|
615 |
table->field[9]->store((uint32)(entry->rusage.ru_oublock - |
|
616 |
previous->rusage.ru_oublock)); |
|
617 |
table->field[9]->set_notnull(); |
|
618 |
#else
|
|
619 |
/* TODO: Add block IO info for non-BSD systems */
|
|
620 |
#endif
|
|
621 |
||
622 |
#ifdef HAVE_GETRUSAGE
|
|
623 |
table->field[10]->store((uint32)(entry->rusage.ru_msgsnd - |
|
624 |
previous->rusage.ru_msgsnd), true); |
|
625 |
table->field[10]->set_notnull(); |
|
626 |
table->field[11]->store((uint32)(entry->rusage.ru_msgrcv - |
|
627 |
previous->rusage.ru_msgrcv), true); |
|
628 |
table->field[11]->set_notnull(); |
|
629 |
#else
|
|
630 |
/* TODO: Add message info for non-BSD systems */
|
|
631 |
#endif
|
|
632 |
||
633 |
#ifdef HAVE_GETRUSAGE
|
|
634 |
table->field[12]->store((uint32)(entry->rusage.ru_majflt - |
|
635 |
previous->rusage.ru_majflt), true); |
|
636 |
table->field[12]->set_notnull(); |
|
637 |
table->field[13]->store((uint32)(entry->rusage.ru_minflt - |
|
638 |
previous->rusage.ru_minflt), true); |
|
639 |
table->field[13]->set_notnull(); |
|
640 |
#else
|
|
641 |
/* TODO: Add page fault info for non-BSD systems */
|
|
642 |
#endif
|
|
643 |
||
644 |
#ifdef HAVE_GETRUSAGE
|
|
645 |
table->field[14]->store((uint32)(entry->rusage.ru_nswap - |
|
646 |
previous->rusage.ru_nswap), true); |
|
647 |
table->field[14]->set_notnull(); |
|
648 |
#else
|
|
649 |
/* TODO: Add swap info for non-BSD systems */
|
|
650 |
#endif
|
|
651 |
||
652 |
/* Emit the location that started this step, not that ended it. */
|
|
653 |
if ((previous->function != NULL) && (previous->file != NULL)) |
|
654 |
{
|
|
655 |
table->field[15]->store(previous->function, strlen(previous->function), |
|
656 |
system_charset_info); |
|
657 |
table->field[15]->set_notnull(); |
|
658 |
table->field[16]->store(previous->file, strlen(previous->file), system_charset_info); |
|
659 |
table->field[16]->set_notnull(); |
|
660 |
table->field[17]->store(previous->line, true); |
|
661 |
table->field[17]->set_notnull(); |
|
662 |
}
|
|
663 |
||
664 |
if (schema_table_store_record(thd, table)) |
|
665 |
DBUG_RETURN(1); |
|
666 |
||
667 |
}
|
|
668 |
}
|
|
669 |
||
670 |
DBUG_RETURN(0); |
|
671 |
}
|
|
672 |
#endif /* ENABLED_PROFILING */ |