This source file includes following definitions.
- MYSQLND_METHOD
- MYSQLND_METHOD
- MYSQLND_CLASS_METHODS_START
- mysqlnd_debug
- mysqlnd_debug_trace_plugin_register
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #include "php.h"
24 #include "mysqlnd.h"
25 #include "mysqlnd_priv.h"
26 #include "mysqlnd_debug.h"
27
28 static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace";
29 static const char * const mysqlnd_debug_empty_string = "";
30
31
32
33 static enum_func_status
34 MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen)
35 {
36 if (!self->file_name) {
37 return FAIL;
38 }
39
40 self->stream = php_stream_open_wrapper(self->file_name,
41 reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb",
42 REPORT_ERRORS, NULL);
43 return self->stream? PASS:FAIL;
44 }
45
46
47
48
49 static enum_func_status
50 MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self,
51 unsigned int line, const char * const file,
52 unsigned int level, const char * type, const char * message)
53 {
54 char pipe_buffer[512];
55 enum_func_status ret;
56 int i;
57 char * message_line;
58 unsigned int message_line_len;
59 unsigned int flags = self->flags;
60 char pid_buffer[10], time_buffer[30], file_buffer[200],
61 line_buffer[6], level_buffer[7];
62
63 if (!self->stream && FAIL == self->m->open(self, FALSE)) {
64 return FAIL;
65 }
66
67 if (level == -1) {
68 level = zend_stack_count(&self->call_stack);
69 }
70 i = MIN(level, sizeof(pipe_buffer) / 2 - 1);
71 pipe_buffer[i*2] = '\0';
72 for (;i > 0;i--) {
73 pipe_buffer[i*2 - 1] = ' ';
74 pipe_buffer[i*2 - 2] = '|';
75 }
76
77
78 if (flags & MYSQLND_DEBUG_DUMP_PID) {
79 snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
80 pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
81 }
82 if (flags & MYSQLND_DEBUG_DUMP_TIME) {
83
84 #if defined(PHP_WIN32)
85
86
87 SYSTEMTIME loc_t;
88 GetLocalTime(&loc_t);
89 snprintf(time_buffer, sizeof(time_buffer) - 1,
90
91 "%02d:%02d:%02d.%06d ",
92
93 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
94 time_buffer[sizeof(time_buffer) - 1 ] = '\0';
95 #else
96 struct timeval tv;
97 struct tm *tm_p;
98 if (gettimeofday(&tv, NULL) != -1) {
99 if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
100 snprintf(time_buffer, sizeof(time_buffer) - 1,
101
102 "%02d:%02d:%02d.%06d ",
103
104 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
105 (int) (tv.tv_usec));
106 time_buffer[sizeof(time_buffer) - 1 ] = '\0';
107 }
108 }
109 #endif
110 }
111 if (flags & MYSQLND_DEBUG_DUMP_FILE) {
112 snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
113 file_buffer[sizeof(file_buffer) - 1 ] = '\0';
114 }
115 if (flags & MYSQLND_DEBUG_DUMP_LINE) {
116 snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
117 line_buffer[sizeof(line_buffer) - 1 ] = '\0';
118 }
119 if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
120 snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
121 level_buffer[sizeof(level_buffer) - 1 ] = '\0';
122 }
123
124 message_line_len = mnd_sprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
125 flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
126 flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
127 flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
128 flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
129 flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
130 pipe_buffer, type? type:"", message);
131
132 ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
133 mnd_sprintf_free(message_line);
134 if (flags & MYSQLND_DEBUG_FLUSH) {
135 self->m->close(self);
136 self->m->open(self, TRUE);
137 }
138 return ret;
139 }
140
141
142
143
144 static enum_func_status
145 MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self,
146 unsigned int line, const char * const file,
147 unsigned int level, const char * type,
148 const char *format, ...)
149 {
150 char pipe_buffer[512];
151 int i;
152 enum_func_status ret;
153 char * message_line, *buffer;
154 unsigned int message_line_len;
155 va_list args;
156 unsigned int flags = self->flags;
157 char pid_buffer[10], time_buffer[30], file_buffer[200],
158 line_buffer[6], level_buffer[7];
159
160 if (!self->stream && FAIL == self->m->open(self, FALSE)) {
161 return FAIL;
162 }
163
164 if (level == -1) {
165 level = zend_stack_count(&self->call_stack);
166 }
167 i = MIN(level, sizeof(pipe_buffer) / 2 - 1);
168 pipe_buffer[i*2] = '\0';
169 for (;i > 0;i--) {
170 pipe_buffer[i*2 - 1] = ' ';
171 pipe_buffer[i*2 - 2] = '|';
172 }
173
174
175 if (flags & MYSQLND_DEBUG_DUMP_PID) {
176 snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
177 pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
178 }
179 if (flags & MYSQLND_DEBUG_DUMP_TIME) {
180
181 #if defined(PHP_WIN32)
182
183
184 SYSTEMTIME loc_t;
185 GetLocalTime(&loc_t);
186 snprintf(time_buffer, sizeof(time_buffer) - 1,
187
188 "%02d:%02d:%02d.%06d ",
189
190 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
191 time_buffer[sizeof(time_buffer) - 1 ] = '\0';
192 #else
193 struct timeval tv;
194 struct tm *tm_p;
195 if (gettimeofday(&tv, NULL) != -1) {
196 if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
197 snprintf(time_buffer, sizeof(time_buffer) - 1,
198
199 "%02d:%02d:%02d.%06d ",
200
201 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
202 (int) (tv.tv_usec));
203 time_buffer[sizeof(time_buffer) - 1 ] = '\0';
204 }
205 }
206 #endif
207 }
208 if (flags & MYSQLND_DEBUG_DUMP_FILE) {
209 snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
210 file_buffer[sizeof(file_buffer) - 1 ] = '\0';
211 }
212 if (flags & MYSQLND_DEBUG_DUMP_LINE) {
213 snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
214 line_buffer[sizeof(line_buffer) - 1 ] = '\0';
215 }
216 if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
217 snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
218 level_buffer[sizeof(level_buffer) - 1 ] = '\0';
219 }
220
221 va_start(args, format);
222 mnd_vsprintf(&buffer, 0, format, args);
223 va_end(args);
224
225 message_line_len = mnd_sprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
226 flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
227 flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
228 flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
229 flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
230 flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
231 pipe_buffer, type? type:"", buffer);
232 mnd_sprintf_free(buffer);
233 ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
234 mnd_sprintf_free(message_line);
235
236 if (flags & MYSQLND_DEBUG_FLUSH) {
237 self->m->close(self);
238 self->m->open(self, TRUE);
239 }
240 return ret;
241 }
242
243
244
245
246
247 static zend_bool
248 MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self,
249 unsigned int line, const char * const file,
250 const char * const func_name, unsigned int func_name_len)
251 {
252 if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
253 return FALSE;
254 }
255 if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
256 return FALSE;
257 }
258
259 if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 && self->skip_functions) {
260 const char ** p = self->skip_functions;
261 while (*p) {
262 if (*p == func_name) {
263 zend_stack_push(&self->call_stack, &mysqlnd_debug_empty_string);
264 #ifndef MYSQLND_PROFILING_DISABLED
265 if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
266 uint64_t some_time = 0;
267 zend_stack_push(&self->call_time_stack, &some_time);
268 }
269 #endif
270 return FALSE;
271 }
272 p++;
273 }
274 }
275
276 zend_stack_push(&self->call_stack, &func_name);
277 #ifndef MYSQLND_PROFILING_DISABLED
278 if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
279 uint64_t some_time = 0;
280 zend_stack_push(&self->call_time_stack, &some_time);
281 }
282 #endif
283
284 if (zend_hash_num_elements(&self->not_filtered_functions) &&
285 0 == zend_hash_str_exists(&self->not_filtered_functions, func_name, strlen(func_name)))
286 {
287 return FALSE;
288 }
289
290 self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name);
291 return TRUE;
292 }
293
294
295 #ifndef MYSQLND_PROFILING_DISABLED
296 struct st_mysqlnd_dbg_function_profile {
297 uint64_t calls;
298 uint64_t min_own;
299 uint64_t max_own;
300 uint64_t avg_own;
301 uint64_t own_underporm_calls;
302 uint64_t min_in_calls;
303 uint64_t max_in_calls;
304 uint64_t avg_in_calls;
305 uint64_t in_calls_underporm_calls;
306 uint64_t min_total;
307 uint64_t max_total;
308 uint64_t avg_total;
309 uint64_t total_underporm_calls;
310 };
311 #define PROFILE_UNDERPERFORM_THRESHOLD 10
312 #endif
313
314
315 static enum_func_status
316 MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, const char * const file, uint64_t call_time)
317 {
318 char **func_name;
319 uint64_t * parent_non_own_time_ptr = NULL, * mine_non_own_time_ptr = NULL;
320 uint64_t mine_non_own_time = 0;
321 zend_bool profile_calls = self->flags & MYSQLND_DEBUG_PROFILE_CALLS? TRUE:FALSE;
322
323 if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
324 return PASS;
325 }
326 if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
327 return PASS;
328 }
329
330 func_name = zend_stack_top(&self->call_stack);
331
332 #ifndef MYSQLND_PROFILING_DISABLED
333 if (profile_calls) {
334 mine_non_own_time_ptr = zend_stack_top(&self->call_time_stack);
335 mine_non_own_time = *mine_non_own_time_ptr;
336 zend_stack_del_top(&self->call_time_stack);
337 }
338 #endif
339
340 if ((*func_name)[0] == '\0') {
341 ;
342 } else if (!zend_hash_num_elements(&self->not_filtered_functions) ||
343 1 == zend_hash_str_exists(&self->not_filtered_functions, (*func_name), strlen((*func_name))))
344 {
345 #ifndef MYSQLND_PROFILING_DISABLED
346 if (FALSE == profile_calls) {
347 #endif
348 self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", *func_name);
349
350 #ifndef MYSQLND_PROFILING_DISABLED
351 } else {
352 struct st_mysqlnd_dbg_function_profile f_profile_stack = {0};
353 struct st_mysqlnd_dbg_function_profile * f_profile = NULL;
354 uint64_t own_time = call_time - mine_non_own_time;
355 uint func_name_len = strlen(*func_name);
356
357 self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s (total=%u own=%u in_calls=%u)",
358 *func_name, (unsigned int) call_time, (unsigned int) own_time, (unsigned int) mine_non_own_time
359 );
360
361 if ((f_profile = zend_hash_str_find_ptr(&self->function_profiles, *func_name, func_name_len)) != NULL) {
362
363 if (f_profile) {
364 if (mine_non_own_time < f_profile->min_in_calls) {
365 f_profile->min_in_calls = mine_non_own_time;
366 } else if (mine_non_own_time > f_profile->max_in_calls) {
367 f_profile->max_in_calls = mine_non_own_time;
368 }
369 f_profile->avg_in_calls = (f_profile->avg_in_calls * f_profile->calls + mine_non_own_time) / (f_profile->calls + 1);
370
371 if (own_time < f_profile->min_own) {
372 f_profile->min_own = own_time;
373 } else if (own_time > f_profile->max_own) {
374 f_profile->max_own = own_time;
375 }
376 f_profile->avg_own = (f_profile->avg_own * f_profile->calls + own_time) / (f_profile->calls + 1);
377
378 if (call_time < f_profile->min_total) {
379 f_profile->min_total = call_time;
380 } else if (call_time > f_profile->max_total) {
381 f_profile->max_total = call_time;
382 }
383 f_profile->avg_total = (f_profile->avg_total * f_profile->calls + call_time) / (f_profile->calls + 1);
384
385 ++f_profile->calls;
386 if (f_profile->calls > PROFILE_UNDERPERFORM_THRESHOLD) {
387 if (f_profile->avg_in_calls < mine_non_own_time) {
388 f_profile->in_calls_underporm_calls++;
389 }
390 if (f_profile->avg_own < own_time) {
391 f_profile->own_underporm_calls++;
392 }
393 if (f_profile->avg_total < call_time) {
394 f_profile->total_underporm_calls++;
395 }
396 }
397 }
398 } else {
399
400 f_profile = &f_profile_stack;
401 f_profile->min_in_calls = f_profile->max_in_calls = f_profile->avg_in_calls = mine_non_own_time;
402 f_profile->min_total = f_profile->max_total = f_profile->avg_total = call_time;
403 f_profile->min_own = f_profile->max_own = f_profile->avg_own = own_time;
404 f_profile->calls = 1;
405 zend_hash_str_add_mem(&self->function_profiles, *func_name, func_name_len, f_profile, sizeof(struct st_mysqlnd_dbg_function_profile));
406 }
407 if ((uint) zend_stack_count(&self->call_time_stack)) {
408 uint64_t parent_non_own_time = 0;
409
410 parent_non_own_time_ptr = zend_stack_top(&self->call_time_stack);
411 parent_non_own_time = *parent_non_own_time_ptr;
412 parent_non_own_time += call_time;
413 zend_stack_del_top(&self->call_time_stack);
414 zend_stack_push(&self->call_time_stack, &parent_non_own_time);
415 }
416 }
417 #endif
418 }
419
420 return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL;
421 }
422
423
424
425
426 static enum_func_status
427 MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self)
428 {
429 if (self->stream) {
430 #ifndef MYSQLND_PROFILING_DISABLED
431 if (!(self->flags & MYSQLND_DEBUG_FLUSH) && (self->flags & MYSQLND_DEBUG_PROFILE_CALLS)) {
432 struct st_mysqlnd_dbg_function_profile * f_profile;
433 zend_string *string_key = NULL;
434
435 self->m->log_va(self, __LINE__, __FILE__, 0, "info : ",
436 "number of functions: %d", zend_hash_num_elements(&self->function_profiles));
437 ZEND_HASH_FOREACH_STR_KEY_PTR(&self->function_profiles, string_key, f_profile) {
438 self->m->log_va(self, __LINE__, __FILE__, -1, "info : ",
439 "%-40s\tcalls=%5llu own_slow=%5llu in_calls_slow=%5llu total_slow=%5llu"
440 " min_own=%5llu max_own=%7llu avg_own=%7llu "
441 " min_in_calls=%5llu max_in_calls=%7llu avg_in_calls=%7llu"
442 " min_total=%5llu max_total=%7llu avg_total=%7llu"
443 ,ZSTR_VAL(string_key)
444 ,(uint64_t) f_profile->calls
445 ,(uint64_t) f_profile->own_underporm_calls
446 ,(uint64_t) f_profile->in_calls_underporm_calls
447 ,(uint64_t) f_profile->total_underporm_calls
448
449 ,(uint64_t) f_profile->min_own
450 ,(uint64_t) f_profile->max_own
451 ,(uint64_t) f_profile->avg_own
452 ,(uint64_t) f_profile->min_in_calls
453 ,(uint64_t) f_profile->max_in_calls
454 ,(uint64_t) f_profile->avg_in_calls
455 ,(uint64_t) f_profile->min_total
456 ,(uint64_t) f_profile->max_total
457 ,(uint64_t) f_profile->avg_total
458 );
459 } ZEND_HASH_FOREACH_END();
460 }
461 #endif
462
463 php_stream_close(self->stream);
464 self->stream = NULL;
465 }
466
467 return PASS;
468 }
469
470
471
472
473 static enum_func_status
474 MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self)
475 {
476 if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
477 efree(self->file_name);
478 self->file_name = NULL;
479 }
480 zend_stack_destroy(&self->call_stack);
481 zend_stack_destroy(&self->call_time_stack);
482 zend_hash_destroy(&self->not_filtered_functions);
483 zend_hash_destroy(&self->function_profiles);
484 free(self);
485 return PASS;
486 }
487
488
489 enum mysqlnd_debug_parser_state
490 {
491 PARSER_WAIT_MODIFIER,
492 PARSER_WAIT_COLON,
493 PARSER_WAIT_VALUE
494 };
495
496
497
498 static void
499 MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode)
500 {
501 unsigned int mode_len, i;
502 enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER;
503
504 mode_len = mode? strlen(mode) : 0;
505
506 self->flags = 0;
507 self->nest_level_limit = 0;
508 if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
509 efree(self->file_name);
510 self->file_name = NULL;
511 }
512 if (zend_hash_num_elements(&self->not_filtered_functions)) {
513 zend_hash_destroy(&self->not_filtered_functions);
514 zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0);
515 }
516
517 for (i = 0; i < mode_len; i++) {
518 switch (mode[i]) {
519 case 'O':
520 case 'A':
521 self->flags |= MYSQLND_DEBUG_FLUSH;
522 case 'a':
523 case 'o':
524 if (mode[i] == 'a' || mode[i] == 'A') {
525 self->flags |= MYSQLND_DEBUG_APPEND;
526 }
527 if (i + 1 < mode_len && mode[i+1] == ',') {
528 unsigned int j = i + 2;
529 #ifdef PHP_WIN32
530 if (i+4 < mode_len && mode[i+3] == ':' && (mode[i+4] == '\\' || mode[i+5] == '/')) {
531 j = i + 5;
532 }
533 #endif
534 while (j < mode_len) {
535 if (mode[j] == ':') {
536 break;
537 }
538 j++;
539 }
540 if (j > i + 2) {
541 self->file_name = estrndup(mode + i + 2, j - i - 2);
542 }
543 i = j;
544 } else {
545 if (!self->file_name)
546 self->file_name = (char *) mysqlnd_debug_default_trace_file;
547 }
548 state = PARSER_WAIT_COLON;
549 break;
550 case ':':
551 #if 0
552 if (state != PARSER_WAIT_COLON) {
553 php_error_docref(NULL, E_WARNING, "Consecutive semicolons at position %u", i);
554 }
555 #endif
556 state = PARSER_WAIT_MODIFIER;
557 break;
558 case 'f':
559 if (i + 1 < mode_len && mode[i+1] == ',') {
560 unsigned int j = i + 2;
561 i++;
562 while (j < mode_len) {
563 if (mode[j] == ':') {
564
565 if ((j + 1 < mode_len) && mode[j+1] == ':') {
566 j += 2;
567 continue;
568 }
569 }
570 if (mode[j] == ',' || mode[j] == ':') {
571 if (j > i + 2) {
572 char func_name[1024];
573 unsigned int func_name_len = MIN(sizeof(func_name) - 1, j - i - 1);
574 memcpy(func_name, mode + i + 1, func_name_len);
575 func_name[func_name_len] = '\0';
576
577 zend_hash_str_add_empty_element(&self->not_filtered_functions,
578 func_name, func_name_len);
579 i = j;
580 }
581 if (mode[j] == ':') {
582 break;
583 }
584 }
585 j++;
586 }
587 i = j;
588 } else {
589 #if 0
590 php_error_docref(NULL, E_WARNING,
591 "Expected list of functions for '%c' found none", mode[i]);
592 #endif
593 }
594 state = PARSER_WAIT_COLON;
595 break;
596 case 'D':
597 case 'd':
598 case 'g':
599 case 'p':
600
601 if ((i + 1) < mode_len && mode[i+1] == ',') {
602 i+= 2;
603 while (i < mode_len) {
604 if (mode[i] == ':') {
605 break;
606 }
607 i++;
608 }
609 }
610 state = PARSER_WAIT_COLON;
611 break;
612 case 'F':
613 self->flags |= MYSQLND_DEBUG_DUMP_FILE;
614 state = PARSER_WAIT_COLON;
615 break;
616 case 'i':
617 self->flags |= MYSQLND_DEBUG_DUMP_PID;
618 state = PARSER_WAIT_COLON;
619 break;
620 case 'L':
621 self->flags |= MYSQLND_DEBUG_DUMP_LINE;
622 state = PARSER_WAIT_COLON;
623 break;
624 case 'n':
625 self->flags |= MYSQLND_DEBUG_DUMP_LEVEL;
626 state = PARSER_WAIT_COLON;
627 break;
628 case 't':
629 if (mode[i+1] == ',') {
630 unsigned int j = i + 2;
631 while (j < mode_len) {
632 if (mode[j] == ':') {
633 break;
634 }
635 j++;
636 }
637 if (j > i + 2) {
638 char *value_str = estrndup(mode + i + 2, j - i - 2);
639 self->nest_level_limit = atoi(value_str);
640 efree(value_str);
641 }
642 i = j;
643 } else {
644 self->nest_level_limit = 200;
645 }
646 self->flags |= MYSQLND_DEBUG_DUMP_TRACE;
647 state = PARSER_WAIT_COLON;
648 break;
649 case 'T':
650 self->flags |= MYSQLND_DEBUG_DUMP_TIME;
651 state = PARSER_WAIT_COLON;
652 break;
653 case 'N':
654 case 'P':
655 case 'r':
656 case 'S':
657 state = PARSER_WAIT_COLON;
658 break;
659 case 'm':
660 self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS;
661 state = PARSER_WAIT_COLON;
662 break;
663 case 'x':
664 self->flags |= MYSQLND_DEBUG_PROFILE_CALLS;
665 state = PARSER_WAIT_COLON;
666 break;
667 default:
668 if (state == PARSER_WAIT_MODIFIER) {
669 #if 0
670 php_error_docref(NULL, E_WARNING, "Unrecognized format '%c'", mode[i]);
671 #endif
672 if (i+1 < mode_len && mode[i+1] == ',') {
673 i+= 2;
674 while (i < mode_len) {
675 if (mode[i] == ':') {
676 break;
677 }
678 i++;
679 }
680 }
681 state = PARSER_WAIT_COLON;
682 } else if (state == PARSER_WAIT_COLON) {
683 #if 0
684 php_error_docref(NULL, E_WARNING, "Colon expected, '%c' found", mode[i]);
685 #endif
686 }
687 break;
688 }
689 }
690 }
691
692
693 MYSQLND_CLASS_METHODS_START(mysqlnd_debug)
694 MYSQLND_METHOD(mysqlnd_debug, open),
695 MYSQLND_METHOD(mysqlnd_debug, set_mode),
696 MYSQLND_METHOD(mysqlnd_debug, log),
697 MYSQLND_METHOD(mysqlnd_debug, log_va),
698 MYSQLND_METHOD(mysqlnd_debug, func_enter),
699 MYSQLND_METHOD(mysqlnd_debug, func_leave),
700 MYSQLND_METHOD(mysqlnd_debug, close),
701 MYSQLND_METHOD(mysqlnd_debug, free),
702 MYSQLND_CLASS_METHODS_END;
703
704
705
706 PHPAPI MYSQLND_DEBUG *
707 mysqlnd_debug_init(const char * skip_functions[])
708 {
709 MYSQLND_DEBUG *ret = calloc(1, sizeof(MYSQLND_DEBUG));
710
711 ret->nest_level_limit = 0;
712 ret->pid = getpid();
713 zend_stack_init(&ret->call_stack, sizeof(char *));
714 zend_stack_init(&ret->call_time_stack, sizeof(uint64_t));
715 zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0);
716 zend_hash_init(&ret->function_profiles, 0, NULL, NULL, 0);
717
718 ret->m = & mysqlnd_mysqlnd_debug_methods;
719 ret->skip_functions = skip_functions;
720
721 return ret;
722 }
723
724
725
726
727 PHPAPI void mysqlnd_debug(const char * mode)
728 {
729 #if PHP_DEBUG
730 MYSQLND_DEBUG * dbg = MYSQLND_G(dbg);
731 if (!dbg) {
732 struct st_mysqlnd_plugin_trace_log * trace_log_plugin = mysqlnd_plugin_find("debug_trace");
733 if (trace_log_plugin) {
734 dbg = trace_log_plugin->methods.trace_instance_init(mysqlnd_debug_std_no_trace_funcs);
735 if (!dbg) {
736 return;
737 }
738 MYSQLND_G(dbg) = dbg;
739 }
740 }
741 if (dbg) {
742 dbg->m->close(dbg);
743 dbg->m->set_mode(dbg, mode);
744 while (zend_stack_count(&dbg->call_stack)) {
745 zend_stack_del_top(&dbg->call_stack);
746 }
747 while (zend_stack_count(&dbg->call_time_stack)) {
748 zend_stack_del_top(&dbg->call_time_stack);
749 }
750 }
751 #endif
752 }
753
754
755
756 static struct st_mysqlnd_plugin_trace_log mysqlnd_plugin_trace_log_plugin =
757 {
758 {
759 MYSQLND_PLUGIN_API_VERSION,
760 "debug_trace",
761 MYSQLND_VERSION_ID,
762 PHP_MYSQLND_VERSION,
763 "PHP License 3.01",
764 "Andrey Hristov <andrey@mysql.com>, Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
765 {
766 NULL,
767 NULL,
768 },
769 {
770 NULL
771 }
772 },
773 {
774 mysqlnd_debug_init,
775 }
776 };
777
778
779
780 void
781 mysqlnd_debug_trace_plugin_register(void)
782 {
783 mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_plugin_trace_log_plugin);
784 }
785
786
787
788
789
790
791
792
793
794
795