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