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