~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to sql/sql_profile.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
                      (uint) 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
      */
 
555
      table->field[0]->store((ulonglong) query->profiling_query_id, TRUE);
 
556
      table->field[1]->store((ulonglong) seq, TRUE); /* the step in the sequence */
 
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 */