1 /* Copyright (c) 2007, 2012, Oracle and/or its affiliates.
2    Copyright (c) 2008, 2012, Monty Program Ab
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
16 
17 
18 /**
19   @file
20 
21   Implement query profiling as as list of metaphorical fences, with one fence
22   per query, and each fencepost a change of thd->proc_info state (with a
23   snapshot of system statistics).  When asked, we can then iterate over the
24   fenceposts and calculate the distance between them, to inform the user what
25   happened during a particular query or thd->proc_info state.
26 
27   User variables that inform profiling behavior:
28   - "profiling", boolean, session only, "Are queries profiled?"
29   - "profiling_history_size", integer, session + global, "Num queries stored?"
30 */
31 
32 #include "mariadb.h"
33 #include "sql_priv.h"
34 #include "sql_profile.h"
35 #include "sql_i_s.h"                      // schema_table_store_record
36 #include "sql_class.h"                    // THD
37 
38 #ifdef _WIN32
39 #pragma comment(lib,"psapi.lib")
40 #endif
41 
42 #define TIME_FLOAT_DIGITS 9
43 /** two vals encoded: (len*100)+dec */
44 #define TIME_I_S_DECIMAL_SIZE (TIME_FLOAT_DIGITS*100)+(TIME_FLOAT_DIGITS-3)
45 
46 #define MAX_QUERY_LENGTH 300
47 #define MAX_QUERY_HISTORY 101
48 
49 /**
50   Connects Information_Schema and Profiling.
51 */
fill_query_profile_statistics_info(THD * thd,TABLE_LIST * tables,Item * cond)52 int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables,
53                                        Item *cond)
54 {
55 #if defined(ENABLED_PROFILING)
56   return(thd->profiling.fill_statistics_info(thd, tables, cond));
57 #else
58   my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling");
59   return(1);
60 #endif
61 }
62 
63 namespace Show {
64 
65 ST_FIELD_INFO query_profile_statistics_info[]=
66 {
67   Column("QUERY_ID",            SLong(20),   NOT_NULL, "Query_id"),
68   Column("SEQ",                 SLong(20),   NOT_NULL, "Seq"),
69   Column("STATE",               Varchar(30), NOT_NULL, "Status"),
70   Column("DURATION", Decimal(TIME_I_S_DECIMAL_SIZE), NOT_NULL, "Duration"),
71   Column("CPU_USER", Decimal(TIME_I_S_DECIMAL_SIZE), NULLABLE, "CPU_user"),
72   Column("CPU_SYSTEM", Decimal(TIME_I_S_DECIMAL_SIZE), NULLABLE, "CPU_system"),
73   Column("CONTEXT_VOLUNTARY",   SLong(20),   NULLABLE, "Context_voluntary"),
74   Column("CONTEXT_INVOLUNTARY", SLong(20),   NULLABLE, "Context_involuntary"),
75   Column("BLOCK_OPS_IN",        SLong(20),   NULLABLE, "Block_ops_in"),
76   Column("BLOCK_OPS_OUT",       SLong(20),   NULLABLE, "Block_ops_out"),
77   Column("MESSAGES_SENT",       SLong(20),   NULLABLE, "Messages_sent"),
78   Column("MESSAGES_RECEIVED",   SLong(20),   NULLABLE, "Messages_received"),
79   Column("PAGE_FAULTS_MAJOR",   SLong(20),   NULLABLE, "Page_faults_major"),
80   Column("PAGE_FAULTS_MINOR",   SLong(20),   NULLABLE, "Page_faults_minor"),
81   Column("SWAPS",               SLong(20),   NULLABLE, "Swaps"),
82   Column("SOURCE_FUNCTION",     Varchar(30), NULLABLE, "Source_function"),
83   Column("SOURCE_FILE",         Varchar(20), NULLABLE, "Source_file"),
84   Column("SOURCE_LINE",         SLong(20),   NULLABLE, "Source_line"),
85   CEnd()
86 };
87 
88 } // namespace Show
89 
make_profile_table_for_show(THD * thd,ST_SCHEMA_TABLE * schema_table)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 
114   ST_FIELD_INFO *field_info;
115   Name_resolution_context *context= &thd->lex->first_select_lex()->context;
116   int i;
117 
118   for (i= 0; !schema_table->fields_info[i].end_marker(); 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 (thd->mem_root) Item_field(thd, context,
125                                                       field_info->name());
126     if (field)
127     {
128       field->set_name(thd, 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
FileTimeToQuadWord(FILETIME * ft)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.
GetTimeDiffInSeconds(FILETIME * a,FILETIME * b)157 static double GetTimeDiffInSeconds(FILETIME *a, FILETIME *b)
158 {
159   return ((FileTimeToQuadWord(a) - FileTimeToQuadWord(b)) / 1e7);
160 }
161 #endif
162 
PROF_MEASUREMENT(QUERY_PROFILE * profile_arg,const char * status_arg)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 
PROF_MEASUREMENT(QUERY_PROFILE * profile_arg,const char * status_arg,const char * function_arg,const char * file_arg,unsigned int line_arg)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 
~PROF_MEASUREMENT()182 PROF_MEASUREMENT::~PROF_MEASUREMENT()
183 {
184   my_free(allocated_status_memory);
185   status= function= file= NULL;
186 }
187 
set_label(const char * status_arg,const char * function_arg,const char * file_arg,unsigned int line_arg)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(key_memory_PROFILE, sizes[0] +
204                                               sizes[1] + sizes[2], MYF(0));
205   DBUG_ASSERT(allocated_status_memory != NULL);
206 
207   cursor= allocated_status_memory;
208 
209   if (status_arg != NULL)
210   {
211     strcpy(cursor, status_arg);
212     status= cursor;
213     cursor+= sizes[0];
214   }
215   else
216     status= NULL;
217 
218   if (function_arg != NULL)
219   {
220     strcpy(cursor, function_arg);
221     function= cursor;
222     cursor+= sizes[1];
223   }
224   else
225     function= NULL;
226 
227   if (file_arg != NULL)
228   {
229     strcpy(cursor, file_arg);
230     file= cursor;
231     cursor+= sizes[2];
232   }
233   else
234     file= NULL;
235 
236   line= line_arg;
237 }
238 
239 /**
240   This updates the statistics for this moment of time.  It captures the state
241   of the running system, so later we can compare points in time and infer what
242   happened in the mean time.  It should only be called immediately upon
243   instantiation of this PROF_MEASUREMENT.
244 
245   @todo  Implement resource capture for OSes not like BSD.
246 */
collect()247 void PROF_MEASUREMENT::collect()
248 {
249   time_usecs= my_interval_timer() / 1e3;  /* ns to us */
250 #ifdef HAVE_GETRUSAGE
251   getrusage(RUSAGE_SELF, &rusage);
252 #elif defined(_WIN32)
253   FILETIME ftDummy;
254   // NOTE: Get{Process|Thread}Times has a granularity of the clock interval,
255   // which is typically ~15ms. So intervals shorter than that will not be
256   // measurable by this function.
257   GetProcessTimes(GetCurrentProcess(), &ftDummy, &ftDummy, &ftKernel, &ftUser);
258   GetProcessIoCounters(GetCurrentProcess(), &io_count);
259   GetProcessMemoryInfo(GetCurrentProcess(), &mem_count, sizeof(mem_count));
260 #endif
261 }
262 
263 
QUERY_PROFILE(PROFILING * profiling_arg,const char * status_arg)264 QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg)
265   :profiling(profiling_arg), profiling_query_id(0), query_source(NULL)
266 {
267   m_seq_counter= 1;
268   PROF_MEASUREMENT *prof= new PROF_MEASUREMENT(this, status_arg);
269   prof->m_seq= m_seq_counter++;
270   m_start_time_usecs= prof->time_usecs;
271   m_end_time_usecs= m_start_time_usecs;
272   entries.push_back(prof);
273 }
274 
~QUERY_PROFILE()275 QUERY_PROFILE::~QUERY_PROFILE()
276 {
277   while (! entries.is_empty())
278     delete entries.pop();
279 
280   my_free(query_source);
281 }
282 
283 /**
284   @todo  Provide a way to include the full text, as in  SHOW PROCESSLIST.
285 */
set_query_source(char * query_source_arg,size_t query_length_arg)286 void QUERY_PROFILE::set_query_source(char *query_source_arg, size_t query_length_arg)
287 {
288   /* Truncate to avoid DoS attacks. */
289   size_t length= MY_MIN(MAX_QUERY_LENGTH, query_length_arg);
290 
291   DBUG_ASSERT(query_source == NULL); /* we don't leak memory */
292   if (query_source_arg != NULL)
293     query_source= my_strndup(key_memory_PROFILE, query_source_arg, length, MYF(0));
294 }
295 
new_status(const char * status_arg,const char * function_arg,const char * file_arg,unsigned int line_arg)296 void QUERY_PROFILE::new_status(const char *status_arg,
297                                const char *function_arg, const char *file_arg,
298                                unsigned int line_arg)
299 {
300   PROF_MEASUREMENT *prof;
301   DBUG_ENTER("QUERY_PROFILE::status");
302 
303   if (!status_arg)
304     DBUG_VOID_RETURN;
305 
306   if ((function_arg != NULL) && (file_arg != NULL))
307     prof= new PROF_MEASUREMENT(this, status_arg, function_arg, base_name(file_arg), line_arg);
308   else
309     prof= new PROF_MEASUREMENT(this, status_arg);
310 
311   prof->m_seq= m_seq_counter++;
312   m_end_time_usecs= prof->time_usecs;
313   entries.push_back(prof);
314 
315   /* Maintain the query history size. */
316   while (entries.elements > MAX_QUERY_HISTORY)
317     delete entries.pop();
318 
319   DBUG_VOID_RETURN;
320 }
321 
322 
323 
PROFILING()324 PROFILING::PROFILING()
325   :profile_id_counter(1), current(NULL), last(NULL)
326 {
327 }
328 
~PROFILING()329 PROFILING::~PROFILING()
330 {
331   restart();
332 }
333 
334 /*
335   Restart profiling from scratch
336 */
337 
restart()338 void PROFILING::restart()
339 {
340   while (! history.is_empty())
341     delete history.pop();
342 
343   if (current != NULL)
344     delete current;
345   /* Ensure that profiling object can be reused */
346   profile_id_counter= 1;
347   current= NULL;
348   last= NULL;
349 }
350 
351 
352 /**
353   Throw away the current profile, because it's useless or unwanted
354   or corrupted.
355 */
discard_current_query()356 void PROFILING::discard_current_query()
357 {
358   DBUG_ENTER("PROFILING::discard_current_profile");
359 
360   delete current;
361   current= NULL;
362 
363   DBUG_VOID_RETURN;
364 }
365 
366 /**
367   Try to save the current profile entry, clean up the data if it shouldn't be
368   saved, and maintain the profile history size.  Naturally, this may not
369   succeed if the profile was previously discarded, and that's expected.
370 */
finish_current_query_impl()371 void PROFILING::finish_current_query_impl()
372 {
373   DBUG_ENTER("PROFILING::finish_current_profile");
374   DBUG_ASSERT(current);
375 
376   /* The last fence-post, so we can support the span before this. */
377   status_change("ending", NULL, NULL, 0);
378 
379   if (enabled &&   /* ON at end? */
380       (current->query_source != NULL) &&
381       (! current->entries.is_empty()))
382   {
383     current->profiling_query_id= next_profile_id();   /* assign an id */
384 
385     history.push_back(current);
386     last= current; /* never contains something that is not in the history. */
387 
388     /* Maintain the history size. */
389     while (history.elements > thd->variables.profiling_history_size)
390       delete history.pop();
391   }
392   else
393     delete current;
394 
395   current= NULL;
396   DBUG_VOID_RETURN;
397 }
398 
show_profiles()399 bool PROFILING::show_profiles()
400 {
401   QUERY_PROFILE *prof;
402   List<Item> field_list;
403   MEM_ROOT *mem_root= thd->mem_root;
404   SELECT_LEX *sel= thd->lex->first_select_lex();
405   SELECT_LEX_UNIT *unit= &thd->lex->unit;
406   ha_rows idx;
407   Protocol *protocol= thd->protocol;
408   void *iterator;
409   DBUG_ENTER("PROFILING::show_profiles");
410 
411   field_list.push_back(new (mem_root)
412                        Item_return_int(thd, "Query_ID", 10,
413                                        MYSQL_TYPE_LONG),
414                        mem_root);
415   field_list.push_back(new (mem_root)
416                        Item_return_int(thd, "Duration",
417                                        TIME_FLOAT_DIGITS - 1,
418                                        MYSQL_TYPE_DOUBLE),
419                        mem_root);
420   field_list.push_back(new (mem_root) Item_empty_string(thd, "Query", 40),
421                        mem_root);
422 
423   if (protocol->send_result_set_metadata(&field_list,
424                                          Protocol::SEND_NUM_ROWS |
425                                          Protocol::SEND_EOF))
426     DBUG_RETURN(TRUE);
427 
428   unit->set_limit(sel);
429 
430   for (iterator= history.new_iterator(), idx= 1;
431        iterator != NULL;
432        iterator= history.iterator_next(iterator), idx++)
433   {
434     prof= history.iterator_value(iterator);
435 
436     double query_time_usecs= prof->m_end_time_usecs - prof->m_start_time_usecs;
437 
438     if (unit->lim.check_offset(idx))
439       continue;
440     if (idx > unit->lim.get_select_limit())
441       break;
442 
443     protocol->prepare_for_resend();
444     protocol->store((uint32)(prof->profiling_query_id));
445     protocol->store_double(query_time_usecs/(1000.0*1000),
446                            (uint32) TIME_FLOAT_DIGITS-1);
447     if (prof->query_source != NULL)
448       protocol->store(prof->query_source, strlen(prof->query_source),
449                       system_charset_info);
450     else
451       protocol->store_null();
452 
453     if (protocol->write())
454       DBUG_RETURN(TRUE);
455   }
456   my_eof(thd);
457   DBUG_RETURN(FALSE);
458 }
459 
460 /**
461   Fill the information schema table, "query_profile", as defined in show.cc .
462   There are two ways to get to this function:  Selecting from the information
463   schema, and a SHOW command.
464 */
fill_statistics_info(THD * thd_arg,TABLE_LIST * tables,Item * cond)465 int PROFILING::fill_statistics_info(THD *thd_arg, TABLE_LIST *tables, Item *cond)
466 {
467   DBUG_ENTER("PROFILING::fill_statistics_info");
468   TABLE *table= tables->table;
469   ulonglong row_number= 0;
470 
471   QUERY_PROFILE *query;
472   /* Go through each query in this thread's stored history... */
473   void *history_iterator;
474   for (history_iterator= history.new_iterator();
475        history_iterator != NULL;
476        history_iterator= history.iterator_next(history_iterator))
477   {
478     query= history.iterator_value(history_iterator);
479 
480     /*
481       Because we put all profiling info into a table that may be reordered, let
482       us also include a numbering of each state per query.  The query_id and
483       the "seq" together are unique.
484     */
485     ulong seq;
486 
487     void *entry_iterator;
488     PROF_MEASUREMENT *entry, *previous= NULL;
489     /* ...and for each query, go through all its state-change steps. */
490     for (entry_iterator= query->entries.new_iterator();
491          entry_iterator != NULL;
492          entry_iterator= query->entries.iterator_next(entry_iterator),
493          previous=entry, row_number++)
494     {
495       entry= query->entries.iterator_value(entry_iterator);
496       seq= entry->m_seq;
497 
498       /* Skip the first.  We count spans of fence, not fence-posts. */
499       if (previous == NULL) continue;
500 
501       if (thd_arg->lex->sql_command == SQLCOM_SHOW_PROFILE)
502       {
503         /*
504           We got here via a SHOW command.  That means that we stored
505           information about the query we wish to show and that isn't
506           in a WHERE clause at a higher level to filter out rows we
507           wish to exclude.
508 
509           Because that functionality isn't available in the server yet,
510           we must filter here, at the wrong level.  Once one can con-
511           struct where and having conditions at the SQL layer, then this
512           condition should be ripped out.
513         */
514         if (thd_arg->lex->profile_query_id == 0) /* 0 == show final query */
515         {
516           if (query != last)
517             continue;
518         }
519         else
520         {
521           if (thd_arg->lex->profile_query_id != query->profiling_query_id)
522             continue;
523         }
524       }
525 
526       /* Set default values for this row. */
527       restore_record(table, s->default_values);
528 
529       /*
530         The order of these fields is set by the  query_profile_statistics_info
531         array.
532       */
533       table->field[0]->store((ulonglong) query->profiling_query_id, TRUE);
534       table->field[1]->store((ulonglong) seq, TRUE); /* the step in the sequence */
535       /*
536         This entry, n, has a point in time, T(n), and a status phrase, S(n).
537         The status phrase S(n) describes the period of time that begins at
538         T(n).  The previous status phrase S(n-1) describes the period of time
539         that starts at T(n-1) and ends at T(n).  Since we want to describe the
540         time that a status phrase took T(n)-T(n-1), this line must describe the
541         previous status.
542       */
543       table->field[2]->store(previous->status, strlen(previous->status),
544                              system_charset_info);
545 
546       my_decimal duration_decimal;
547       double2my_decimal(E_DEC_FATAL_ERROR,
548                         (entry->time_usecs-previous->time_usecs)/(1000.0*1000),
549                         &duration_decimal);
550 
551       table->field[3]->store_decimal(&duration_decimal);
552 
553 
554 #ifdef HAVE_GETRUSAGE
555 
556       my_decimal cpu_utime_decimal, cpu_stime_decimal;
557 
558       double2my_decimal(E_DEC_FATAL_ERROR,
559                         RUSAGE_DIFF_USEC(entry->rusage.ru_utime,
560                                          previous->rusage.ru_utime) /
561                                                         (1000.0*1000),
562                         &cpu_utime_decimal);
563 
564       double2my_decimal(E_DEC_FATAL_ERROR,
565                         RUSAGE_DIFF_USEC(entry->rusage.ru_stime,
566                                          previous->rusage.ru_stime) /
567                                                         (1000.0*1000),
568                         &cpu_stime_decimal);
569 
570       table->field[4]->store_decimal(&cpu_utime_decimal);
571       table->field[5]->store_decimal(&cpu_stime_decimal);
572       table->field[4]->set_notnull();
573       table->field[5]->set_notnull();
574 #elif defined(_WIN32)
575       my_decimal cpu_utime_decimal, cpu_stime_decimal;
576 
577       double2my_decimal(E_DEC_FATAL_ERROR,
578                         GetTimeDiffInSeconds(&entry->ftUser,
579                                              &previous->ftUser),
580                         &cpu_utime_decimal);
581       double2my_decimal(E_DEC_FATAL_ERROR,
582                         GetTimeDiffInSeconds(&entry->ftKernel,
583                                              &previous->ftKernel),
584                         &cpu_stime_decimal);
585 
586       // Store the result.
587       table->field[4]->store_decimal(&cpu_utime_decimal);
588       table->field[5]->store_decimal(&cpu_stime_decimal);
589       table->field[4]->set_notnull();
590       table->field[5]->set_notnull();
591 #else
592       /* TODO: Add CPU-usage info for non-BSD systems */
593 #endif
594 
595 #ifdef HAVE_GETRUSAGE
596       table->field[6]->store((uint32)(entry->rusage.ru_nvcsw -
597                              previous->rusage.ru_nvcsw));
598       table->field[6]->set_notnull();
599       table->field[7]->store((uint32)(entry->rusage.ru_nivcsw -
600                              previous->rusage.ru_nivcsw));
601       table->field[7]->set_notnull();
602 #else
603       /* TODO: Add context switch info for non-BSD systems */
604 #endif
605 
606 #ifdef HAVE_GETRUSAGE
607       table->field[8]->store((uint32)(entry->rusage.ru_inblock -
608                              previous->rusage.ru_inblock));
609       table->field[8]->set_notnull();
610       table->field[9]->store((uint32)(entry->rusage.ru_oublock -
611                              previous->rusage.ru_oublock));
612       table->field[9]->set_notnull();
613 #elif defined(__WIN__)
614       ULONGLONG reads_delta = entry->io_count.ReadOperationCount -
615                               previous->io_count.ReadOperationCount;
616       ULONGLONG writes_delta = entry->io_count.WriteOperationCount -
617                               previous->io_count.WriteOperationCount;
618 
619       table->field[8]->store((uint32)reads_delta);
620       table->field[8]->set_notnull();
621 
622       table->field[9]->store((uint32)writes_delta);
623       table->field[9]->set_notnull();
624 #else
625       /* TODO: Add block IO info for non-BSD systems */
626 #endif
627 
628 #ifdef HAVE_GETRUSAGE
629       table->field[10]->store((uint32)(entry->rusage.ru_msgsnd -
630                              previous->rusage.ru_msgsnd), true);
631       table->field[10]->set_notnull();
632       table->field[11]->store((uint32)(entry->rusage.ru_msgrcv -
633                              previous->rusage.ru_msgrcv), true);
634       table->field[11]->set_notnull();
635 #else
636       /* TODO: Add message info for non-BSD systems */
637 #endif
638 
639 #ifdef HAVE_GETRUSAGE
640       table->field[12]->store((uint32)(entry->rusage.ru_majflt -
641                              previous->rusage.ru_majflt), true);
642       table->field[12]->set_notnull();
643       table->field[13]->store((uint32)(entry->rusage.ru_minflt -
644                              previous->rusage.ru_minflt), true);
645       table->field[13]->set_notnull();
646 #elif defined(__WIN__)
647       /* Windows APIs don't easily distinguish between hard and soft page
648          faults, so we just fill the 'major' column and leave the second NULL.
649       */
650       table->field[12]->store((uint32)(entry->mem_count.PageFaultCount -
651                              previous->mem_count.PageFaultCount), true);
652       table->field[12]->set_notnull();
653 #else
654       /* TODO: Add page fault info for non-BSD systems */
655 #endif
656 
657 #ifdef HAVE_GETRUSAGE
658       table->field[14]->store((uint32)(entry->rusage.ru_nswap -
659                              previous->rusage.ru_nswap), true);
660       table->field[14]->set_notnull();
661 #else
662       /* TODO: Add swap info for non-BSD systems */
663 #endif
664 
665       /* Emit the location that started this step, not that ended it. */
666       if ((previous->function != NULL) && (previous->file != NULL))
667       {
668         table->field[15]->store(previous->function, strlen(previous->function),
669                         system_charset_info);
670         table->field[15]->set_notnull();
671         table->field[16]->store(previous->file, strlen(previous->file), system_charset_info);
672         table->field[16]->set_notnull();
673         table->field[17]->store(previous->line, true);
674         table->field[17]->set_notnull();
675       }
676 
677       if (schema_table_store_record(thd_arg, table))
678         DBUG_RETURN(1);
679 
680     }
681   }
682 
683   DBUG_RETURN(0);
684 }
685 
686 
reset()687 void PROFILING::reset()
688 {
689   enabled= (thd->variables.option_bits & OPTION_PROFILING) != 0;
690 }
691 #endif /* ENABLED_PROFILING */
692