MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sql_profile.cc
Go to the documentation of this file.
1 /* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
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 Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
16 
32 #include "sql_priv.h"
33 #include "unireg.h" // REQUIRED: for other includes
34 #include "sql_profile.h"
35 #include "my_sys.h"
36 #include "sql_show.h" // schema_table_store_record
37 #include "sql_class.h" // THD
38 
39 #include <algorithm>
40 
41 using std::min;
42 using std::max;
43 
44 #define TIME_FLOAT_DIGITS 9
45 
46 #define TIME_I_S_DECIMAL_SIZE (TIME_FLOAT_DIGITS*100)+(TIME_FLOAT_DIGITS-3)
47 
48 #define MAX_QUERY_LENGTH 300U
49 #define MAX_QUERY_HISTORY 101U
50 
55  Item *cond)
56 {
57 #if defined(ENABLED_PROFILING)
58  return(thd->profiling.fill_statistics_info(thd, tables, cond));
59 #else
60  my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling");
61  return(1);
62 #endif
63 }
64 
65 ST_FIELD_INFO query_profile_statistics_info[]=
66 {
67  /* name, length, type, value, maybe_null, old_name, open_method */
68  {"QUERY_ID", 20, MYSQL_TYPE_LONG, 0, false, "Query_id", SKIP_OPEN_TABLE},
69  {"SEQ", 20, MYSQL_TYPE_LONG, 0, false, "Seq", SKIP_OPEN_TABLE},
70  {"STATE", 30, MYSQL_TYPE_STRING, 0, false, "Status", SKIP_OPEN_TABLE},
71  {"DURATION", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, false, "Duration", SKIP_OPEN_TABLE},
72  {"CPU_USER", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_user", SKIP_OPEN_TABLE},
73  {"CPU_SYSTEM", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_system", SKIP_OPEN_TABLE},
74  {"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_voluntary", SKIP_OPEN_TABLE},
75  {"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_involuntary", SKIP_OPEN_TABLE},
76  {"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_in", SKIP_OPEN_TABLE},
77  {"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_out", SKIP_OPEN_TABLE},
78  {"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0, true, "Messages_sent", SKIP_OPEN_TABLE},
79  {"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0, true, "Messages_received", SKIP_OPEN_TABLE},
80  {"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_major", SKIP_OPEN_TABLE},
81  {"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_minor", SKIP_OPEN_TABLE},
82  {"SWAPS", 20, MYSQL_TYPE_LONG, 0, true, "Swaps", SKIP_OPEN_TABLE},
83  {"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0, true, "Source_function", SKIP_OPEN_TABLE},
84  {"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0, true, "Source_file", SKIP_OPEN_TABLE},
85  {"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0, true, "Source_line", SKIP_OPEN_TABLE},
86  {NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0}
87 };
88 
89 
90 int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
91 {
92  uint profile_options = thd->lex->profile_options;
93  uint fields_include_condition_truth_values[]= {
94  FALSE, /* Query_id */
95  FALSE, /* Seq */
96  TRUE, /* Status */
97  TRUE, /* Duration */
98  profile_options & PROFILE_CPU, /* CPU_user */
99  profile_options & PROFILE_CPU, /* CPU_system */
100  profile_options & PROFILE_CONTEXT, /* Context_voluntary */
101  profile_options & PROFILE_CONTEXT, /* Context_involuntary */
102  profile_options & PROFILE_BLOCK_IO, /* Block_ops_in */
103  profile_options & PROFILE_BLOCK_IO, /* Block_ops_out */
104  profile_options & PROFILE_IPC, /* Messages_sent */
105  profile_options & PROFILE_IPC, /* Messages_received */
106  profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_major */
107  profile_options & PROFILE_PAGE_FAULTS, /* Page_faults_minor */
108  profile_options & PROFILE_SWAPS, /* Swaps */
109  profile_options & PROFILE_SOURCE, /* Source_function */
110  profile_options & PROFILE_SOURCE, /* Source_file */
111  profile_options & PROFILE_SOURCE, /* Source_line */
112  };
113 
115  Name_resolution_context *context= &thd->lex->select_lex.context;
116  int i;
117 
118  for (i= 0; schema_table->fields_info[i].field_name != NULL; i++)
119  {
120  if (! fields_include_condition_truth_values[i])
121  continue;
122 
123  field_info= &schema_table->fields_info[i];
124  Item_field *field= new Item_field(context,
125  NullS, NullS, field_info->field_name);
126  if (field)
127  {
128  field->item_name.copy(field_info->old_name);
129  if (add_item_to_list(thd, field))
130  return 1;
131  }
132  }
133  return 0;
134 }
135 
136 
137 #if defined(ENABLED_PROFILING)
138 
139 #define RUSAGE_USEC(tv) ((tv).tv_sec*1000*1000 + (tv).tv_usec)
140 #define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2)))
141 
142 #ifdef _WIN32
143 static ULONGLONG FileTimeToQuadWord(FILETIME *ft)
144 {
145  // Overlay FILETIME onto a ULONGLONG.
146  union {
147  ULONGLONG qwTime;
148  FILETIME ft;
149  } u;
150 
151  u.ft = *ft;
152  return u.qwTime;
153 }
154 
155 
156 // Get time difference between to FILETIME objects in seconds.
157 static double GetTimeDiffInSeconds(FILETIME *a, FILETIME *b)
158 {
159  return ((FileTimeToQuadWord(a) - FileTimeToQuadWord(b)) / 1e7);
160 }
161 #endif
162 
163 PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, const char
164  *status_arg)
165  :profile(profile_arg)
166 {
167  collect();
168  set_label(status_arg, NULL, NULL, 0);
169 }
170 
171 PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg,
172  const char *status_arg,
173  const char *function_arg,
174  const char *file_arg,
175  unsigned int line_arg)
176  :profile(profile_arg)
177 {
178  collect();
179  set_label(status_arg, function_arg, file_arg, line_arg);
180 }
181 
182 PROF_MEASUREMENT::~PROF_MEASUREMENT()
183 {
184  my_free(allocated_status_memory);
185  status= function= file= NULL;
186 }
187 
188 void PROF_MEASUREMENT::set_label(const char *status_arg,
189  const char *function_arg,
190  const char *file_arg, unsigned int line_arg)
191 {
192  size_t sizes[3]; /* 3 == status+function+file */
193  char *cursor;
194 
195  /*
196  Compute all the space we'll need to allocate one block for everything
197  we'll need, instead of N mallocs.
198  */
199  sizes[0]= (status_arg == NULL) ? 0 : strlen(status_arg) + 1;
200  sizes[1]= (function_arg == NULL) ? 0 : strlen(function_arg) + 1;
201  sizes[2]= (file_arg == NULL) ? 0 : strlen(file_arg) + 1;
202 
203  allocated_status_memory= (char *) my_malloc(sizes[0] + sizes[1] + sizes[2], MYF(0));
204  DBUG_ASSERT(allocated_status_memory != NULL);
205 
206  cursor= allocated_status_memory;
207 
208  if (status_arg != NULL)
209  {
210  strcpy(cursor, status_arg);
211  status= cursor;
212  cursor+= sizes[0];
213  }
214  else
215  status= NULL;
216 
217  if (function_arg != NULL)
218  {
219  strcpy(cursor, function_arg);
220  function= cursor;
221  cursor+= sizes[1];
222  }
223  else
224  function= NULL;
225 
226  if (file_arg != NULL)
227  {
228  strcpy(cursor, file_arg);
229  file= cursor;
230  cursor+= sizes[2];
231  }
232  else
233  file= NULL;
234 
235  line= line_arg;
236 }
237 
246 void PROF_MEASUREMENT::collect()
247 {
248  time_usecs= (double) my_getsystime() / 10.0; /* 1 sec was 1e7, now is 1e6 */
249 #ifdef HAVE_GETRUSAGE
250  getrusage(RUSAGE_SELF, &rusage);
251 #elif defined(_WIN32)
252  FILETIME ftDummy;
253  // NOTE: Get{Process|Thread}Times has a granularity of the clock interval,
254  // which is typically ~15ms. So intervals shorter than that will not be
255  // measurable by this function.
256  GetProcessTimes(GetCurrentProcess(), &ftDummy, &ftDummy, &ftKernel, &ftUser);
257 #endif
258 }
259 
260 
261 QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg)
262  :profiling(profiling_arg), profiling_query_id(0), query_source(NULL)
263 {
264  m_seq_counter= 1;
265  PROF_MEASUREMENT *prof= new PROF_MEASUREMENT(this, status_arg);
266  prof->m_seq= m_seq_counter++;
267  m_start_time_usecs= prof->time_usecs;
268  m_end_time_usecs= m_start_time_usecs;
269  entries.push_back(prof);
270 }
271 
272 QUERY_PROFILE::~QUERY_PROFILE()
273 {
274  while (! entries.is_empty())
275  delete entries.pop();
276 
277  my_free(query_source);
278 }
279 
283 void QUERY_PROFILE::set_query_source(char *query_source_arg,
284  uint query_length_arg)
285 {
286  /* Truncate to avoid DoS attacks. */
287  uint length= min(MAX_QUERY_LENGTH, query_length_arg);
288 
289  DBUG_ASSERT(query_source == NULL); /* we don't leak memory */
290  if (query_source_arg != NULL)
291  query_source= my_strndup(query_source_arg, length, MYF(0));
292 }
293 
294 void QUERY_PROFILE::new_status(const char *status_arg,
295  const char *function_arg, const char *file_arg,
296  unsigned int line_arg)
297 {
298  PROF_MEASUREMENT *prof;
299  DBUG_ENTER("QUERY_PROFILE::status");
300 
301  DBUG_ASSERT(status_arg != NULL);
302 
303  if ((function_arg != NULL) && (file_arg != NULL))
304  prof= new PROF_MEASUREMENT(this, status_arg, function_arg, base_name(file_arg), line_arg);
305  else
306  prof= new PROF_MEASUREMENT(this, status_arg);
307 
308  prof->m_seq= m_seq_counter++;
309  m_end_time_usecs= prof->time_usecs;
310  entries.push_back(prof);
311 
312  /* Maintain the query history size. */
313  while (entries.elements > MAX_QUERY_HISTORY)
314  delete entries.pop();
315 
316  DBUG_VOID_RETURN;
317 }
318 
319 
320 
321 PROFILING::PROFILING()
322  :profile_id_counter(1), current(NULL), last(NULL)
323 {
324 }
325 
326 PROFILING::~PROFILING()
327 {
328  while (! history.is_empty())
329  delete history.pop();
330 
331  if (current != NULL)
332  delete current;
333 }
334 
344 void PROFILING::status_change(const char *status_arg,
345  const char *function_arg,
346  const char *file_arg, unsigned int line_arg)
347 {
348  DBUG_ENTER("PROFILING::status_change");
349 
350  if (status_arg == NULL) /* We don't know how to handle that */
351  DBUG_VOID_RETURN;
352 
353  if (current == NULL) /* This profile was already discarded. */
354  DBUG_VOID_RETURN;
355 
356  if (unlikely(enabled))
357  current->new_status(status_arg, function_arg, file_arg, line_arg);
358 
359  DBUG_VOID_RETURN;
360 }
361 
368 void PROFILING::start_new_query(const char *initial_state)
369 {
370  DBUG_ENTER("PROFILING::start_new_query");
371 
372  /* This should never happen unless the server is radically altered. */
373  if (unlikely(current != NULL))
374  {
375  DBUG_PRINT("warning", ("profiling code was asked to start a new query "
376  "before the old query was finished. This is "
377  "probably a bug."));
378  finish_current_query();
379  }
380 
381  enabled= ((thd->variables.option_bits & OPTION_PROFILING) != 0);
382 
383  if (! enabled) DBUG_VOID_RETURN;
384 
385  DBUG_ASSERT(current == NULL);
386  current= new QUERY_PROFILE(this, initial_state);
387 
388  DBUG_VOID_RETURN;
389 }
390 
395 void PROFILING::discard_current_query()
396 {
397  DBUG_ENTER("PROFILING::discard_current_profile");
398 
399  delete current;
400  current= NULL;
401 
402  DBUG_VOID_RETURN;
403 }
404 
410 void PROFILING::finish_current_query()
411 {
412  DBUG_ENTER("PROFILING::finish_current_profile");
413  if (current != NULL)
414  {
415  /* The last fence-post, so we can support the span before this. */
416  status_change("ending", NULL, NULL, 0);
417 
418  if ((enabled) && /* ON at start? */
419  ((thd->variables.option_bits & OPTION_PROFILING) != 0) && /* and ON at end? */
420  (current->query_source != NULL) &&
421  (! current->entries.is_empty()))
422  {
423  current->profiling_query_id= next_profile_id(); /* assign an id */
424 
425  history.push_back(current);
426  last= current; /* never contains something that is not in the history. */
427  current= NULL;
428  }
429  else
430  {
431  delete current;
432  current= NULL;
433  }
434  }
435 
436  /* Maintain the history size. */
437  while (history.elements > thd->variables.profiling_history_size)
438  delete history.pop();
439 
440  DBUG_VOID_RETURN;
441 }
442 
443 bool PROFILING::show_profiles()
444 {
445  DBUG_ENTER("PROFILING::show_profiles");
446  QUERY_PROFILE *prof;
447  List<Item> field_list;
448 
449  field_list.push_back(new Item_return_int("Query_ID", 10,
450  MYSQL_TYPE_LONG));
451  field_list.push_back(new Item_return_int("Duration", TIME_FLOAT_DIGITS-1,
452  MYSQL_TYPE_DOUBLE));
453  field_list.push_back(new Item_empty_string("Query", 40));
454 
455  if (thd->protocol->send_result_set_metadata(&field_list,
456  Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
457  DBUG_RETURN(TRUE);
458 
459  SELECT_LEX *sel= &thd->lex->select_lex;
460  SELECT_LEX_UNIT *unit= &thd->lex->unit;
461  ha_rows idx= 0;
462  Protocol *protocol= thd->protocol;
463 
464  unit->set_limit(sel);
465 
466  void *iterator;
467  for (iterator= history.new_iterator();
468  iterator != NULL;
469  iterator= history.iterator_next(iterator))
470  {
471  prof= history.iterator_value(iterator);
472 
473  String elapsed;
474 
475  double query_time_usecs= prof->m_end_time_usecs - prof->m_start_time_usecs;
476 
477  if (++idx <= unit->offset_limit_cnt)
478  continue;
479  if (idx > unit->select_limit_cnt)
480  break;
481 
482  protocol->prepare_for_resend();
483  protocol->store((uint32)(prof->profiling_query_id));
484  protocol->store((double)(query_time_usecs/(1000.0*1000)),
485  (uint32) TIME_FLOAT_DIGITS-1, &elapsed);
486  if (prof->query_source != NULL)
487  protocol->store(prof->query_source, strlen(prof->query_source),
488  system_charset_info);
489  else
490  protocol->store_null();
491 
492  if (protocol->write())
493  DBUG_RETURN(TRUE);
494  }
495  my_eof(thd);
496  DBUG_RETURN(FALSE);
497 }
498 
505 void PROFILING::set_query_source(char *query_source_arg, uint query_length_arg)
506 {
507  DBUG_ENTER("PROFILING::set_query_source");
508 
509  if (! enabled)
510  DBUG_VOID_RETURN;
511 
512  if (current != NULL)
513  current->set_query_source(query_source_arg, query_length_arg);
514  else
515  DBUG_PRINT("info", ("no current profile to send query source to"));
516  DBUG_VOID_RETURN;
517 }
518 
524 int PROFILING::fill_statistics_info(THD *thd_arg, TABLE_LIST *tables, Item *cond)
525 {
526  DBUG_ENTER("PROFILING::fill_statistics_info");
527  TABLE *table= tables->table;
528  ulonglong row_number= 0;
529 
530  QUERY_PROFILE *query;
531  /* Go through each query in this thread's stored history... */
532  void *history_iterator;
533  for (history_iterator= history.new_iterator();
534  history_iterator != NULL;
535  history_iterator= history.iterator_next(history_iterator))
536  {
537  query= history.iterator_value(history_iterator);
538 
539  /*
540  Because we put all profiling info into a table that may be reordered, let
541  us also include a numbering of each state per query. The query_id and
542  the "seq" together are unique.
543  */
544  ulong seq;
545 
546  void *entry_iterator;
547  PROF_MEASUREMENT *entry, *previous= NULL;
548  /* ...and for each query, go through all its state-change steps. */
549  for (entry_iterator= query->entries.new_iterator();
550  entry_iterator != NULL;
551  entry_iterator= query->entries.iterator_next(entry_iterator),
552  previous=entry, row_number++)
553  {
554  entry= query->entries.iterator_value(entry_iterator);
555  seq= entry->m_seq;
556 
557  /* Skip the first. We count spans of fence, not fence-posts. */
558  if (previous == NULL) continue;
559 
560  if (thd_arg->lex->sql_command == SQLCOM_SHOW_PROFILE)
561  {
562  /*
563  We got here via a SHOW command. That means that we stored
564  information about the query we wish to show and that isn't
565  in a WHERE clause at a higher level to filter out rows we
566  wish to exclude.
567 
568  Because that functionality isn't available in the server yet,
569  we must filter here, at the wrong level. Once one can con-
570  struct where and having conditions at the SQL layer, then this
571  condition should be ripped out.
572  */
573  if (thd_arg->lex->profile_query_id == 0) /* 0 == show final query */
574  {
575  if (query != last)
576  continue;
577  }
578  else
579  {
580  if (thd_arg->lex->profile_query_id != query->profiling_query_id)
581  continue;
582  }
583  }
584 
585  /* Set default values for this row. */
586  restore_record(table, s->default_values);
587 
588  /*
589  The order of these fields is set by the query_profile_statistics_info
590  array.
591  */
592  table->field[0]->store((ulonglong) query->profiling_query_id, TRUE);
593  table->field[1]->store((ulonglong) seq, TRUE); /* the step in the sequence */
594  /*
595  This entry, n, has a point in time, T(n), and a status phrase, S(n).
596  The status phrase S(n) describes the period of time that begins at
597  T(n). The previous status phrase S(n-1) describes the period of time
598  that starts at T(n-1) and ends at T(n). Since we want to describe the
599  time that a status phrase took T(n)-T(n-1), this line must describe the
600  previous status.
601  */
602  table->field[2]->store(previous->status, strlen(previous->status),
603  system_charset_info);
604 
605  my_decimal duration_decimal;
606  double2my_decimal(E_DEC_FATAL_ERROR,
607  (entry->time_usecs-previous->time_usecs)/(1000.0*1000),
608  &duration_decimal);
609 
610  table->field[3]->store_decimal(&duration_decimal);
611 
612 
613 #ifdef HAVE_GETRUSAGE
614 
615  my_decimal cpu_utime_decimal, cpu_stime_decimal;
616 
617  double2my_decimal(E_DEC_FATAL_ERROR,
618  RUSAGE_DIFF_USEC(entry->rusage.ru_utime,
619  previous->rusage.ru_utime) /
620  (1000.0*1000),
621  &cpu_utime_decimal);
622 
623  double2my_decimal(E_DEC_FATAL_ERROR,
624  RUSAGE_DIFF_USEC(entry->rusage.ru_stime,
625  previous->rusage.ru_stime) /
626  (1000.0*1000),
627  &cpu_stime_decimal);
628 
629  table->field[4]->store_decimal(&cpu_utime_decimal);
630  table->field[5]->store_decimal(&cpu_stime_decimal);
631  table->field[4]->set_notnull();
632  table->field[5]->set_notnull();
633 #elif defined(_WIN32)
634  my_decimal cpu_utime_decimal, cpu_stime_decimal;
635 
636  double2my_decimal(E_DEC_FATAL_ERROR,
637  GetTimeDiffInSeconds(&entry->ftUser,
638  &previous->ftUser),
639  &cpu_utime_decimal);
640  double2my_decimal(E_DEC_FATAL_ERROR,
641  GetTimeDiffInSeconds(&entry->ftKernel,
642  &previous->ftKernel),
643  &cpu_stime_decimal);
644 
645  // Store the result.
646  table->field[4]->store_decimal(&cpu_utime_decimal);
647  table->field[5]->store_decimal(&cpu_stime_decimal);
648  table->field[4]->set_notnull();
649  table->field[5]->set_notnull();
650 #else
651  /* TODO: Add CPU-usage info for non-BSD systems */
652 #endif
653 
654 #ifdef HAVE_GETRUSAGE
655  table->field[6]->store((uint32)(entry->rusage.ru_nvcsw -
656  previous->rusage.ru_nvcsw));
657  table->field[6]->set_notnull();
658  table->field[7]->store((uint32)(entry->rusage.ru_nivcsw -
659  previous->rusage.ru_nivcsw));
660  table->field[7]->set_notnull();
661 #else
662  /* TODO: Add context switch info for non-BSD systems */
663 #endif
664 
665 #ifdef HAVE_GETRUSAGE
666  table->field[8]->store((uint32)(entry->rusage.ru_inblock -
667  previous->rusage.ru_inblock));
668  table->field[8]->set_notnull();
669  table->field[9]->store((uint32)(entry->rusage.ru_oublock -
670  previous->rusage.ru_oublock));
671  table->field[9]->set_notnull();
672 #else
673  /* TODO: Add block IO info for non-BSD systems */
674 #endif
675 
676 #ifdef HAVE_GETRUSAGE
677  table->field[10]->store((uint32)(entry->rusage.ru_msgsnd -
678  previous->rusage.ru_msgsnd), true);
679  table->field[10]->set_notnull();
680  table->field[11]->store((uint32)(entry->rusage.ru_msgrcv -
681  previous->rusage.ru_msgrcv), true);
682  table->field[11]->set_notnull();
683 #else
684  /* TODO: Add message info for non-BSD systems */
685 #endif
686 
687 #ifdef HAVE_GETRUSAGE
688  table->field[12]->store((uint32)(entry->rusage.ru_majflt -
689  previous->rusage.ru_majflt), true);
690  table->field[12]->set_notnull();
691  table->field[13]->store((uint32)(entry->rusage.ru_minflt -
692  previous->rusage.ru_minflt), true);
693  table->field[13]->set_notnull();
694 #else
695  /* TODO: Add page fault info for non-BSD systems */
696 #endif
697 
698 #ifdef HAVE_GETRUSAGE
699  table->field[14]->store((uint32)(entry->rusage.ru_nswap -
700  previous->rusage.ru_nswap), true);
701  table->field[14]->set_notnull();
702 #else
703  /* TODO: Add swap info for non-BSD systems */
704 #endif
705 
706  /* Emit the location that started this step, not that ended it. */
707  if ((previous->function != NULL) && (previous->file != NULL))
708  {
709  table->field[15]->store(previous->function, strlen(previous->function),
710  system_charset_info);
711  table->field[15]->set_notnull();
712  table->field[16]->store(previous->file, strlen(previous->file), system_charset_info);
713  table->field[16]->set_notnull();
714  table->field[17]->store(previous->line, true);
715  table->field[17]->set_notnull();
716  }
717 
718  if (schema_table_store_record(thd_arg, table))
719  DBUG_RETURN(1);
720 
721  }
722  }
723 
724  DBUG_RETURN(0);
725 }
726 #endif /* ENABLED_PROFILING */