Annotation of embedaddon/php/ext/mysqlnd/mysqlnd_debug.c, revision 1.1.1.1
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 2006-2012 The PHP Group |
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:
21: /* $Id: mysqlnd_debug.c 321634 2012-01-01 13:15:04Z felipe $ */
22:
23: #include "php.h"
24: #include "mysqlnd.h"
25: #include "mysqlnd_priv.h"
26: #include "mysqlnd_debug.h"
27: #include "mysqlnd_wireprotocol.h"
28: #include "mysqlnd_statistics.h"
29: #include "zend_builtin_functions.h"
30:
31: static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace";
32:
33: #ifdef ZTS
34: #define MYSQLND_ZTS(self) TSRMLS_D = (self)->TSRMLS_C
35: #else
36: #define MYSQLND_ZTS(self)
37: #endif
38:
39: static const char mysqlnd_emalloc_name[] = "_mysqlnd_emalloc";
40: static const char mysqlnd_pemalloc_name[] = "_mysqlnd_pemalloc";
41: static const char mysqlnd_ecalloc_name[] = "_mysqlnd_ecalloc";
42: static const char mysqlnd_pecalloc_name[] = "_mysqlnd_pecalloc";
43: static const char mysqlnd_erealloc_name[] = "_mysqlnd_erealloc";
44: static const char mysqlnd_perealloc_name[] = "_mysqlnd_perealloc";
45: static const char mysqlnd_efree_name[] = "_mysqlnd_efree";
46: static const char mysqlnd_pefree_name[] = "_mysqlnd_pefree";
47: static const char mysqlnd_malloc_name[] = "_mysqlnd_malloc";
48: static const char mysqlnd_calloc_name[] = "_mysqlnd_calloc";
49: static const char mysqlnd_realloc_name[] = "_mysqlnd_realloc";
50: static const char mysqlnd_free_name[] = "_mysqlnd_free";
51: static const char mysqlnd_pestrndup_name[] = "_mysqlnd_pestrndup";
52: static const char mysqlnd_pestrdup_name[] = "_mysqlnd_pestrdup";
53:
54: const char * mysqlnd_debug_std_no_trace_funcs[] =
55: {
56: mysqlnd_emalloc_name,
57: mysqlnd_ecalloc_name,
58: mysqlnd_efree_name,
59: mysqlnd_erealloc_name,
60: mysqlnd_pemalloc_name,
61: mysqlnd_pecalloc_name,
62: mysqlnd_pefree_name,
63: mysqlnd_perealloc_name,
64: mysqlnd_malloc_name,
65: mysqlnd_calloc_name,
66: mysqlnd_realloc_name,
67: mysqlnd_free_name,
68: mysqlnd_pestrndup_name,
69: mysqlnd_read_header_name,
70: mysqlnd_read_body_name,
71: NULL /* must be always last */
72: };
73:
74:
75: /* {{{ mysqlnd_debug::open */
76: static enum_func_status
77: MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen)
78: {
79: MYSQLND_ZTS(self);
80:
81: if (!self->file_name) {
82: return FAIL;
83: }
84:
85: self->stream = php_stream_open_wrapper(self->file_name,
86: reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb",
87: REPORT_ERRORS, NULL);
88: return self->stream? PASS:FAIL;
89: }
90: /* }}} */
91:
92:
93: /* {{{ mysqlnd_debug::log */
94: static enum_func_status
95: MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self,
96: unsigned int line, const char * const file,
97: unsigned int level, const char * type, const char * message)
98: {
99: char pipe_buffer[512];
100: enum_func_status ret;
101: int i;
102: char * message_line;
103: unsigned int message_line_len;
104: unsigned int flags = self->flags;
105: char pid_buffer[10], time_buffer[30], file_buffer[200],
106: line_buffer[6], level_buffer[7];
107: MYSQLND_ZTS(self);
108:
109: if (!self->stream && FAIL == self->m->open(self, FALSE)) {
110: return FAIL;
111: }
112:
113: if (level == -1) {
114: level = zend_stack_count(&self->call_stack);
115: }
116: i = MIN(level, sizeof(pipe_buffer) / 2 - 1);
117: pipe_buffer[i*2] = '\0';
118: for (;i > 0;i--) {
119: pipe_buffer[i*2 - 1] = ' ';
120: pipe_buffer[i*2 - 2] = '|';
121: }
122:
123:
124: if (flags & MYSQLND_DEBUG_DUMP_PID) {
125: snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
126: pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
127: }
128: if (flags & MYSQLND_DEBUG_DUMP_TIME) {
129: /* The following from FF's DBUG library, which is in the public domain */
130: #if defined(PHP_WIN32)
131: /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
132: in system ticks, 10 ms intervals. See my_getsystime.c for high res */
133: SYSTEMTIME loc_t;
134: GetLocalTime(&loc_t);
135: snprintf(time_buffer, sizeof(time_buffer) - 1,
136: /* "%04d-%02d-%02d " */
137: "%02d:%02d:%02d.%06d ",
138: /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
139: loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
140: time_buffer[sizeof(time_buffer) - 1 ] = '\0';
141: #else
142: struct timeval tv;
143: struct tm *tm_p;
144: if (gettimeofday(&tv, NULL) != -1) {
145: if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
146: snprintf(time_buffer, sizeof(time_buffer) - 1,
147: /* "%04d-%02d-%02d " */
148: "%02d:%02d:%02d.%06d ",
149: /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
150: tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
151: (int) (tv.tv_usec));
152: time_buffer[sizeof(time_buffer) - 1 ] = '\0';
153: }
154: }
155: #endif
156: }
157: if (flags & MYSQLND_DEBUG_DUMP_FILE) {
158: snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
159: file_buffer[sizeof(file_buffer) - 1 ] = '\0';
160: }
161: if (flags & MYSQLND_DEBUG_DUMP_LINE) {
162: snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
163: line_buffer[sizeof(line_buffer) - 1 ] = '\0';
164: }
165: if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
166: snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
167: level_buffer[sizeof(level_buffer) - 1 ] = '\0';
168: }
169:
170: message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
171: flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
172: flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
173: flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
174: flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
175: flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
176: pipe_buffer, type? type:"", message);
177:
178: ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
179: efree(message_line); /* allocated by spprintf */
180: if (flags & MYSQLND_DEBUG_FLUSH) {
181: self->m->close(self);
182: self->m->open(self, TRUE);
183: }
184: return ret;
185: }
186: /* }}} */
187:
188:
189: /* {{{ mysqlnd_debug::log_va */
190: static enum_func_status
191: MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self,
192: unsigned int line, const char * const file,
193: unsigned int level, const char * type,
194: const char *format, ...)
195: {
196: char pipe_buffer[512];
197: int i;
198: enum_func_status ret;
199: char * message_line, *buffer;
200: unsigned int message_line_len;
201: va_list args;
202: unsigned int flags = self->flags;
203: char pid_buffer[10], time_buffer[30], file_buffer[200],
204: line_buffer[6], level_buffer[7];
205: MYSQLND_ZTS(self);
206:
207: if (!self->stream && FAIL == self->m->open(self, FALSE)) {
208: return FAIL;
209: }
210:
211: if (level == -1) {
212: level = zend_stack_count(&self->call_stack);
213: }
214: i = MIN(level, sizeof(pipe_buffer) / 2 - 1);
215: pipe_buffer[i*2] = '\0';
216: for (;i > 0;i--) {
217: pipe_buffer[i*2 - 1] = ' ';
218: pipe_buffer[i*2 - 2] = '|';
219: }
220:
221:
222: if (flags & MYSQLND_DEBUG_DUMP_PID) {
223: snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
224: pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
225: }
226: if (flags & MYSQLND_DEBUG_DUMP_TIME) {
227: /* The following from FF's DBUG library, which is in the public domain */
228: #if defined(PHP_WIN32)
229: /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
230: in system ticks, 10 ms intervals. See my_getsystime.c for high res */
231: SYSTEMTIME loc_t;
232: GetLocalTime(&loc_t);
233: snprintf(time_buffer, sizeof(time_buffer) - 1,
234: /* "%04d-%02d-%02d " */
235: "%02d:%02d:%02d.%06d ",
236: /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
237: loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
238: time_buffer[sizeof(time_buffer) - 1 ] = '\0';
239: #else
240: struct timeval tv;
241: struct tm *tm_p;
242: if (gettimeofday(&tv, NULL) != -1) {
243: if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
244: snprintf(time_buffer, sizeof(time_buffer) - 1,
245: /* "%04d-%02d-%02d " */
246: "%02d:%02d:%02d.%06d ",
247: /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
248: tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
249: (int) (tv.tv_usec));
250: time_buffer[sizeof(time_buffer) - 1 ] = '\0';
251: }
252: }
253: #endif
254: }
255: if (flags & MYSQLND_DEBUG_DUMP_FILE) {
256: snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
257: file_buffer[sizeof(file_buffer) - 1 ] = '\0';
258: }
259: if (flags & MYSQLND_DEBUG_DUMP_LINE) {
260: snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
261: line_buffer[sizeof(line_buffer) - 1 ] = '\0';
262: }
263: if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
264: snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
265: level_buffer[sizeof(level_buffer) - 1 ] = '\0';
266: }
267:
268:
269: va_start(args, format);
270: vspprintf(&buffer, 0, format, args);
271: va_end(args);
272:
273: message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
274: flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
275: flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
276: flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
277: flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
278: flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
279: pipe_buffer, type? type:"", buffer);
280: efree(buffer);
281: ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
282: efree(message_line); /* allocated by spprintf */
283:
284: if (flags & MYSQLND_DEBUG_FLUSH) {
285: self->m->close(self);
286: self->m->open(self, TRUE);
287: }
288: return ret;
289: }
290: /* }}} */
291:
292:
293: /* FALSE - The DBG_ calls won't be traced, TRUE - will be traced */
294: /* {{{ mysqlnd_debug::func_enter */
295: static zend_bool
296: MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self,
297: unsigned int line, const char * const file,
298: const char * const func_name, unsigned int func_name_len)
299: {
300: if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
301: return FALSE;
302: }
303: if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
304: return FALSE;
305: }
306:
307: if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 && self->skip_functions) {
308: const char ** p = self->skip_functions;
309: while (*p) {
310: if (*p == func_name) {
311: zend_stack_push(&self->call_stack, "", sizeof(""));
312: #ifndef MYSQLND_PROFILING_DISABLED
313: if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
314: uint64_t some_time = 0;
315: zend_stack_push(&self->call_time_stack, &some_time, sizeof(some_time));
316: }
317: #endif
318: return FALSE;
319: }
320: p++;
321: }
322: }
323:
324: zend_stack_push(&self->call_stack, func_name, func_name_len + 1);
325: #ifndef MYSQLND_PROFILING_DISABLED
326: if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
327: uint64_t some_time = 0;
328: zend_stack_push(&self->call_time_stack, &some_time, sizeof(some_time));
329: }
330: #endif
331:
332: if (zend_hash_num_elements(&self->not_filtered_functions) &&
333: 0 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
334: {
335: return FALSE;
336: }
337:
338: self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name);
339: return TRUE;
340: }
341: /* }}} */
342:
343: #ifndef MYSQLND_PROFILING_DISABLED
344: struct st_mysqlnd_dbg_function_profile {
345: uint64_t calls;
346: uint64_t min_own;
347: uint64_t max_own;
348: uint64_t avg_own;
349: uint64_t own_underporm_calls;
350: uint64_t min_in_calls;
351: uint64_t max_in_calls;
352: uint64_t avg_in_calls;
353: uint64_t in_calls_underporm_calls;
354: uint64_t min_total;
355: uint64_t max_total;
356: uint64_t avg_total;
357: uint64_t total_underporm_calls;
358: };
359: #define PROFILE_UNDERPERFORM_THRESHOLD 10
360: #endif
361:
362: /* {{{ mysqlnd_debug::func_leave */
363: static enum_func_status
364: MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, const char * const file, uint64_t call_time)
365: {
366: char *func_name;
367: uint64_t * parent_non_own_time_ptr = NULL, * mine_non_own_time_ptr = NULL;
368: uint64_t mine_non_own_time = 0;
369: zend_bool profile_calls = self->flags & MYSQLND_DEBUG_PROFILE_CALLS? TRUE:FALSE;
370:
371: if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
372: return PASS;
373: }
374: if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
375: return PASS;
376: }
377:
378: zend_stack_top(&self->call_stack, (void **)&func_name);
379:
380: #ifndef MYSQLND_PROFILING_DISABLED
381: if (profile_calls) {
382: zend_stack_top(&self->call_time_stack, (void **)&mine_non_own_time_ptr);
383: mine_non_own_time = *mine_non_own_time_ptr;
384: zend_stack_del_top(&self->call_time_stack); /* callee - removing ourselves */
385: }
386: #endif
387:
388: if (func_name[0] == '\0') {
389: ; /* don't log that function */
390: } else if (!zend_hash_num_elements(&self->not_filtered_functions) ||
391: 1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
392: {
393: #ifndef MYSQLND_PROFILING_DISABLED
394: if (FALSE == profile_calls) {
395: #endif
396: self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name);
397:
398: #ifndef MYSQLND_PROFILING_DISABLED
399: } else {
400: struct st_mysqlnd_dbg_function_profile f_profile_stack = {0};
401: struct st_mysqlnd_dbg_function_profile * f_profile = NULL;
402: uint64_t own_time = call_time - mine_non_own_time;
403: uint func_name_len = strlen(func_name);
404:
405: self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s (total=%u own=%u in_calls=%u)",
406: func_name, (unsigned int) call_time, (unsigned int) own_time, (unsigned int) mine_non_own_time
407: );
408:
409: if (SUCCESS == zend_hash_find(&self->function_profiles, func_name, func_name_len + 1, (void **) &f_profile)) {
410: /* found */
411: if (f_profile) {
412: if (mine_non_own_time < f_profile->min_in_calls) {
413: f_profile->min_in_calls = mine_non_own_time;
414: } else if (mine_non_own_time > f_profile->max_in_calls) {
415: f_profile->max_in_calls = mine_non_own_time;
416: }
417: f_profile->avg_in_calls = (f_profile->avg_in_calls * f_profile->calls + mine_non_own_time) / (f_profile->calls + 1);
418:
419: if (own_time < f_profile->min_own) {
420: f_profile->min_own = own_time;
421: } else if (own_time > f_profile->max_own) {
422: f_profile->max_own = own_time;
423: }
424: f_profile->avg_own = (f_profile->avg_own * f_profile->calls + own_time) / (f_profile->calls + 1);
425:
426: if (call_time < f_profile->min_total) {
427: f_profile->min_total = call_time;
428: } else if (call_time > f_profile->max_total) {
429: f_profile->max_total = call_time;
430: }
431: f_profile->avg_total = (f_profile->avg_total * f_profile->calls + call_time) / (f_profile->calls + 1);
432:
433: ++f_profile->calls;
434: if (f_profile->calls > PROFILE_UNDERPERFORM_THRESHOLD) {
435: if (f_profile->avg_in_calls < mine_non_own_time) {
436: f_profile->in_calls_underporm_calls++;
437: }
438: if (f_profile->avg_own < own_time) {
439: f_profile->own_underporm_calls++;
440: }
441: if (f_profile->avg_total < call_time) {
442: f_profile->total_underporm_calls++;
443: }
444: }
445: }
446: } else {
447: /* add */
448: f_profile = &f_profile_stack;
449: f_profile->min_in_calls = f_profile->max_in_calls = f_profile->avg_in_calls = mine_non_own_time;
450: f_profile->min_total = f_profile->max_total = f_profile->avg_total = call_time;
451: f_profile->min_own = f_profile->max_own = f_profile->avg_own = own_time;
452: f_profile->calls = 1;
453: zend_hash_add(&self->function_profiles, func_name, func_name_len+1, f_profile, sizeof(struct st_mysqlnd_dbg_function_profile), NULL);
454: }
455: if ((uint) zend_stack_count(&self->call_time_stack)) {
456: uint64_t parent_non_own_time = 0;
457:
458: zend_stack_top(&self->call_time_stack, (void **)&parent_non_own_time_ptr);
459: parent_non_own_time = *parent_non_own_time_ptr;
460: parent_non_own_time += call_time;
461: zend_stack_del_top(&self->call_time_stack); /* the caller */
462: zend_stack_push(&self->call_time_stack, &parent_non_own_time, sizeof(parent_non_own_time)); /* add back the caller */
463: }
464: }
465: #endif
466: }
467:
468: return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL;
469: }
470: /* }}} */
471:
472:
473: /* {{{ mysqlnd_debug::close */
474: static enum_func_status
475: MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self)
476: {
477: MYSQLND_ZTS(self);
478: if (self->stream) {
479: #ifndef MYSQLND_PROFILING_DISABLED
480: if (!(self->flags & MYSQLND_DEBUG_FLUSH) && (self->flags & MYSQLND_DEBUG_PROFILE_CALLS)) {
481: struct st_mysqlnd_dbg_function_profile * f_profile;
482: HashPosition pos_values;
483:
484: self->m->log_va(self, __LINE__, __FILE__, 0, "info : ",
485: "number of functions: %d", zend_hash_num_elements(&self->function_profiles));
486: zend_hash_internal_pointer_reset_ex(&self->function_profiles, &pos_values);
487: while (zend_hash_get_current_data_ex(&self->function_profiles, (void **) &f_profile, &pos_values) == SUCCESS) {
488: char *string_key = NULL;
489: uint string_key_len;
490: ulong num_key;
491:
492: zend_hash_get_current_key_ex(&self->function_profiles, &string_key, &string_key_len, &num_key, 0, &pos_values);
493:
494: self->m->log_va(self, __LINE__, __FILE__, -1, "info : ",
495: "%-40s\tcalls=%5llu own_slow=%5llu in_calls_slow=%5llu total_slow=%5llu"
496: " min_own=%5llu max_own=%7llu avg_own=%7llu "
497: " min_in_calls=%5llu max_in_calls=%7llu avg_in_calls=%7llu"
498: " min_total=%5llu max_total=%7llu avg_total=%7llu"
499: ,string_key
500: ,(uint64_t) f_profile->calls
501: ,(uint64_t) f_profile->own_underporm_calls
502: ,(uint64_t) f_profile->in_calls_underporm_calls
503: ,(uint64_t) f_profile->total_underporm_calls
504:
505: ,(uint64_t) f_profile->min_own
506: ,(uint64_t) f_profile->max_own
507: ,(uint64_t) f_profile->avg_own
508: ,(uint64_t) f_profile->min_in_calls
509: ,(uint64_t) f_profile->max_in_calls
510: ,(uint64_t) f_profile->avg_in_calls
511: ,(uint64_t) f_profile->min_total
512: ,(uint64_t) f_profile->max_total
513: ,(uint64_t) f_profile->avg_total
514: );
515: zend_hash_move_forward_ex(&self->function_profiles, &pos_values);
516: }
517: }
518: #endif
519:
520: php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE);
521: self->stream = NULL;
522: }
523: /* no DBG_RETURN please */
524: return PASS;
525: }
526: /* }}} */
527:
528:
529: /* {{{ mysqlnd_res_meta::free */
530: static enum_func_status
531: MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self)
532: {
533: if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
534: efree(self->file_name);
535: self->file_name = NULL;
536: }
537: zend_stack_destroy(&self->call_stack);
538: zend_stack_destroy(&self->call_time_stack);
539: zend_hash_destroy(&self->not_filtered_functions);
540: zend_hash_destroy(&self->function_profiles);
541: efree(self);
542: return PASS;
543: }
544: /* }}} */
545:
546: enum mysqlnd_debug_parser_state
547: {
548: PARSER_WAIT_MODIFIER,
549: PARSER_WAIT_COLON,
550: PARSER_WAIT_VALUE
551: };
552:
553:
554: /* {{{ mysqlnd_res_meta::set_mode */
555: static void
556: MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode)
557: {
558: unsigned int mode_len = strlen(mode), i;
559: enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER;
560:
561: self->flags = 0;
562: self->nest_level_limit = 0;
563: if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
564: efree(self->file_name);
565: self->file_name = NULL;
566: }
567: if (zend_hash_num_elements(&self->not_filtered_functions)) {
568: zend_hash_destroy(&self->not_filtered_functions);
569: zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0);
570: }
571:
572: for (i = 0; i < mode_len; i++) {
573: switch (mode[i]) {
574: case 'O':
575: case 'A':
576: self->flags |= MYSQLND_DEBUG_FLUSH;
577: case 'a':
578: case 'o':
579: if (mode[i] == 'a' || mode[i] == 'A') {
580: self->flags |= MYSQLND_DEBUG_APPEND;
581: }
582: if (i + 1 < mode_len && mode[i+1] == ',') {
583: unsigned int j = i + 2;
584: #ifdef PHP_WIN32
585: if (i+4 < mode_len && mode[i+3] == ':' && (mode[i+4] == '\\' || mode[i+5] == '/')) {
586: j = i + 5;
587: }
588: #endif
589: while (j < mode_len) {
590: if (mode[j] == ':') {
591: break;
592: }
593: j++;
594: }
595: if (j > i + 2) {
596: self->file_name = estrndup(mode + i + 2, j - i - 2);
597: }
598: i = j;
599: } else {
600: if (!self->file_name)
601: self->file_name = (char *) mysqlnd_debug_default_trace_file;
602: }
603: state = PARSER_WAIT_COLON;
604: break;
605: case ':':
606: #if 0
607: if (state != PARSER_WAIT_COLON) {
608: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Consecutive semicolons at position %u", i);
609: }
610: #endif
611: state = PARSER_WAIT_MODIFIER;
612: break;
613: case 'f': /* limit output to these functions */
614: if (i + 1 < mode_len && mode[i+1] == ',') {
615: unsigned int j = i + 2;
616: i++;
617: while (j < mode_len) {
618: if (mode[j] == ':') {
619: /* function names with :: */
620: if ((j + 1 < mode_len) && mode[j+1] == ':') {
621: j += 2;
622: continue;
623: }
624: }
625: if (mode[j] == ',' || mode[j] == ':') {
626: if (j > i + 2) {
627: char func_name[1024];
628: unsigned int func_name_len = MIN(sizeof(func_name) - 1, j - i - 1);
629: memcpy(func_name, mode + i + 1, func_name_len);
630: func_name[func_name_len] = '\0';
631:
632: zend_hash_add_empty_element(&self->not_filtered_functions,
633: func_name, func_name_len + 1);
634: i = j;
635: }
636: if (mode[j] == ':') {
637: break;
638: }
639: }
640: j++;
641: }
642: i = j;
643: } else {
644: #if 0
645: php_error_docref(NULL TSRMLS_CC, E_WARNING,
646: "Expected list of functions for '%c' found none", mode[i]);
647: #endif
648: }
649: state = PARSER_WAIT_COLON;
650: break;
651: case 'D':
652: case 'd':
653: case 'g':
654: case 'p':
655: /* unsupported */
656: if ((i + 1) < mode_len && mode[i+1] == ',') {
657: i+= 2;
658: while (i < mode_len) {
659: if (mode[i] == ':') {
660: break;
661: }
662: i++;
663: }
664: }
665: state = PARSER_WAIT_COLON;
666: break;
667: case 'F':
668: self->flags |= MYSQLND_DEBUG_DUMP_FILE;
669: state = PARSER_WAIT_COLON;
670: break;
671: case 'i':
672: self->flags |= MYSQLND_DEBUG_DUMP_PID;
673: state = PARSER_WAIT_COLON;
674: break;
675: case 'L':
676: self->flags |= MYSQLND_DEBUG_DUMP_LINE;
677: state = PARSER_WAIT_COLON;
678: break;
679: case 'n':
680: self->flags |= MYSQLND_DEBUG_DUMP_LEVEL;
681: state = PARSER_WAIT_COLON;
682: break;
683: case 't':
684: if (mode[i+1] == ',') {
685: unsigned int j = i + 2;
686: while (j < mode_len) {
687: if (mode[j] == ':') {
688: break;
689: }
690: j++;
691: }
692: if (j > i + 2) {
693: char *value_str = estrndup(mode + i + 2, j - i - 2);
694: self->nest_level_limit = atoi(value_str);
695: efree(value_str);
696: }
697: i = j;
698: } else {
699: self->nest_level_limit = 200; /* default value for FF DBUG */
700: }
701: self->flags |= MYSQLND_DEBUG_DUMP_TRACE;
702: state = PARSER_WAIT_COLON;
703: break;
704: case 'T':
705: self->flags |= MYSQLND_DEBUG_DUMP_TIME;
706: state = PARSER_WAIT_COLON;
707: break;
708: case 'N':
709: case 'P':
710: case 'r':
711: case 'S':
712: state = PARSER_WAIT_COLON;
713: break;
714: case 'm': /* mysqlnd extension - trace memory functions */
715: self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS;
716: state = PARSER_WAIT_COLON;
717: break;
718: case 'x': /* mysqlnd extension - profile calls */
719: self->flags |= MYSQLND_DEBUG_PROFILE_CALLS;
720: state = PARSER_WAIT_COLON;
721: break;
722: default:
723: if (state == PARSER_WAIT_MODIFIER) {
724: #if 0
725: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized format '%c'", mode[i]);
726: #endif
727: if (i+1 < mode_len && mode[i+1] == ',') {
728: i+= 2;
729: while (i < mode_len) {
730: if (mode[i] == ':') {
731: break;
732: }
733: i++;
734: }
735: }
736: state = PARSER_WAIT_COLON;
737: } else if (state == PARSER_WAIT_COLON) {
738: #if 0
739: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]);
740: #endif
741: }
742: break;
743: }
744: }
745: }
746: /* }}} */
747:
748: MYSQLND_CLASS_METHODS_START(mysqlnd_debug)
749: MYSQLND_METHOD(mysqlnd_debug, open),
750: MYSQLND_METHOD(mysqlnd_debug, set_mode),
751: MYSQLND_METHOD(mysqlnd_debug, log),
752: MYSQLND_METHOD(mysqlnd_debug, log_va),
753: MYSQLND_METHOD(mysqlnd_debug, func_enter),
754: MYSQLND_METHOD(mysqlnd_debug, func_leave),
755: MYSQLND_METHOD(mysqlnd_debug, close),
756: MYSQLND_METHOD(mysqlnd_debug, free),
757: MYSQLND_CLASS_METHODS_END;
758:
759:
760: /* {{{ mysqlnd_debug_init */
761: PHPAPI MYSQLND_DEBUG *
762: mysqlnd_debug_init(const char * skip_functions[] TSRMLS_DC)
763: {
764: MYSQLND_DEBUG *ret = ecalloc(1, sizeof(MYSQLND_DEBUG));
765: #ifdef ZTS
766: ret->TSRMLS_C = TSRMLS_C;
767: #endif
768: ret->nest_level_limit = 0;
769: ret->pid = getpid();
770: zend_stack_init(&ret->call_stack);
771: zend_stack_init(&ret->call_time_stack);
772: zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0);
773: zend_hash_init(&ret->function_profiles, 0, NULL, NULL, 0);
774:
775: ret->m = & mysqlnd_mysqlnd_debug_methods;
776: ret->skip_functions = skip_functions;
777:
778: return ret;
779: }
780: /* }}} */
781:
782:
783: /* {{{ _mysqlnd_debug */
784: PHPAPI void _mysqlnd_debug(const char * mode TSRMLS_DC)
785: {
786: #ifdef PHP_DEBUG
787: MYSQLND_DEBUG *dbg = MYSQLND_G(dbg);
788: if (!dbg) {
789: MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(mysqlnd_debug_std_no_trace_funcs TSRMLS_CC);
790: if (!dbg) {
791: return;
792: }
793: }
794:
795: dbg->m->close(dbg);
796: dbg->m->set_mode(dbg, mode);
797: while (zend_stack_count(&dbg->call_stack)) {
798: zend_stack_del_top(&dbg->call_stack);
799: }
800: while (zend_stack_count(&dbg->call_time_stack)) {
801: zend_stack_del_top(&dbg->call_time_stack);
802: }
803: #endif
804: }
805: /* }}} */
806:
807:
808: #if ZEND_DEBUG
809: #else
810: #define __zend_filename "/unknown/unknown"
811: #define __zend_lineno 0
812: #endif
813:
814: #define REAL_SIZE(s) (collect_memory_statistics? (s) + sizeof(size_t) : (s))
815: #define REAL_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) - sizeof(size_t)) : (p))
816: #define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + sizeof(size_t)) : (p))
817:
818: /* {{{ _mysqlnd_emalloc */
819: void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
820: {
821: void *ret;
822: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
823: long * threshold = &MYSQLND_G(debug_emalloc_fail_threshold);
824: DBG_ENTER(mysqlnd_emalloc_name);
825:
826: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
827:
828: #ifdef PHP_DEBUG
829: /* -1 is also "true" */
830: if (*threshold) {
831: #endif
832: ret = emalloc(REAL_SIZE(size));
833: #ifdef PHP_DEBUG
834: --*threshold;
835: } else if (*threshold == 0) {
836: ret = NULL;
837: }
838: #endif
839:
840: DBG_INF_FMT("size=%lu ptr=%p", size, ret);
841:
842: if (ret && collect_memory_statistics) {
843: *(size_t *) ret = size;
844: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMOUNT, size);
845: }
846: DBG_RETURN(FAKE_PTR(ret));
847: }
848: /* }}} */
849:
850:
851: /* {{{ _mysqlnd_pemalloc */
852: void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
853: {
854: void *ret;
855: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
856: long * threshold = persistent? &MYSQLND_G(debug_malloc_fail_threshold):&MYSQLND_G(debug_emalloc_fail_threshold);
857: DBG_ENTER(mysqlnd_pemalloc_name);
858: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
859:
860: #ifdef PHP_DEBUG
861: /* -1 is also "true" */
862: if (*threshold) {
863: #endif
864: ret = pemalloc(REAL_SIZE(size), persistent);
865: #ifdef PHP_DEBUG
866: --*threshold;
867: } else if (*threshold == 0) {
868: ret = NULL;
869: }
870: #endif
871:
872: DBG_INF_FMT("size=%lu ptr=%p persistent=%u", size, ret, persistent);
873:
874: if (ret && collect_memory_statistics) {
875: enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT;
876: enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMOUNT:STAT_MEM_EMALLOC_AMOUNT;
877: *(size_t *) ret = size;
878: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
879: }
880:
881: DBG_RETURN(FAKE_PTR(ret));
882: }
883: /* }}} */
884:
885:
886: /* {{{ _mysqlnd_ecalloc */
887: void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
888: {
889: void *ret;
890: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
891: long * threshold = &MYSQLND_G(debug_ecalloc_fail_threshold);
892: DBG_ENTER(mysqlnd_ecalloc_name);
893: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
894: DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
895:
896: #ifdef PHP_DEBUG
897: /* -1 is also "true" */
898: if (*threshold) {
899: #endif
900: ret = ecalloc(nmemb, REAL_SIZE(size));
901: #ifdef PHP_DEBUG
902: --*threshold;
903: } else if (*threshold == 0) {
904: ret = NULL;
905: }
906: #endif
907:
908: DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
909: DBG_INF_FMT("size=%lu ptr=%p", size, ret);
910: if (ret && collect_memory_statistics) {
911: *(size_t *) ret = size;
912: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMOUNT, size);
913: }
914: DBG_RETURN(FAKE_PTR(ret));
915: }
916: /* }}} */
917:
918:
919: /* {{{ _mysqlnd_pecalloc */
920: void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
921: {
922: void *ret;
923: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
924: long * threshold = persistent? &MYSQLND_G(debug_calloc_fail_threshold):&MYSQLND_G(debug_ecalloc_fail_threshold);
925: DBG_ENTER(mysqlnd_pecalloc_name);
926: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
927:
928: #ifdef PHP_DEBUG
929: /* -1 is also "true" */
930: if (*threshold) {
931: #endif
932: ret = pecalloc(nmemb, REAL_SIZE(size), persistent);
933: #ifdef PHP_DEBUG
934: --*threshold;
935: } else if (*threshold == 0) {
936: ret = NULL;
937: }
938: #endif
939:
940: DBG_INF_FMT("size=%lu ptr=%p", size, ret);
941:
942: if (ret && collect_memory_statistics) {
943: enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT;
944: enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMOUNT:STAT_MEM_ECALLOC_AMOUNT;
945: *(size_t *) ret = size;
946: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
947: }
948:
949: DBG_RETURN(FAKE_PTR(ret));
950: }
951: /* }}} */
952:
953:
954: /* {{{ _mysqlnd_erealloc */
955: void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
956: {
957: void *ret;
958: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
959: size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
960: long * threshold = &MYSQLND_G(debug_erealloc_fail_threshold);
961: DBG_ENTER(mysqlnd_erealloc_name);
962: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
963: DBG_INF_FMT("ptr=%p old_size=%lu, new_size=%lu", ptr, old_size, new_size);
964:
965: #ifdef PHP_DEBUG
966: /* -1 is also "true" */
967: if (*threshold) {
968: #endif
969: ret = erealloc(REAL_PTR(ptr), REAL_SIZE(new_size));
970: #ifdef PHP_DEBUG
971: --*threshold;
972: } else if (*threshold == 0) {
973: ret = NULL;
974: }
975: #endif
976:
977: DBG_INF_FMT("new_ptr=%p", (char*)ret);
978: if (ret && collect_memory_statistics) {
979: *(size_t *) ret = new_size;
980: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMOUNT, new_size);
981: }
982: DBG_RETURN(FAKE_PTR(ret));
983: }
984: /* }}} */
985:
986:
987: /* {{{ _mysqlnd_perealloc */
988: void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
989: {
990: void *ret;
991: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
992: size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
993: long * threshold = persistent? &MYSQLND_G(debug_realloc_fail_threshold):&MYSQLND_G(debug_erealloc_fail_threshold);
994: DBG_ENTER(mysqlnd_perealloc_name);
995: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
996: DBG_INF_FMT("ptr=%p old_size=%lu new_size=%lu persistent=%u", ptr, old_size, new_size, persistent);
997:
998: #ifdef PHP_DEBUG
999: /* -1 is also "true" */
1000: if (*threshold) {
1001: #endif
1002: ret = perealloc(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
1003: #ifdef PHP_DEBUG
1004: --*threshold;
1005: } else if (*threshold == 0) {
1006: ret = NULL;
1007: }
1008: #endif
1009:
1010: DBG_INF_FMT("new_ptr=%p", (char*)ret);
1011:
1012: if (ret && collect_memory_statistics) {
1013: enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT;
1014: enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMOUNT:STAT_MEM_EREALLOC_AMOUNT;
1015: *(size_t *) ret = new_size;
1016: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, new_size);
1017: }
1018: DBG_RETURN(FAKE_PTR(ret));
1019: }
1020: /* }}} */
1021:
1022:
1023: /* {{{ _mysqlnd_efree */
1024: void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
1025: {
1026: size_t free_amount = 0;
1027: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
1028: DBG_ENTER(mysqlnd_efree_name);
1029: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
1030: DBG_INF_FMT("ptr=%p", ptr);
1031:
1032: if (ptr) {
1033: if (collect_memory_statistics) {
1034: free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
1035: DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
1036: }
1037: efree(REAL_PTR(ptr));
1038: }
1039:
1040: if (collect_memory_statistics) {
1041: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EFREE_COUNT, 1, STAT_MEM_EFREE_AMOUNT, free_amount);
1042: }
1043: DBG_VOID_RETURN;
1044: }
1045: /* }}} */
1046:
1047:
1048: /* {{{ _mysqlnd_pefree */
1049: void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
1050: {
1051: size_t free_amount = 0;
1052: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
1053: DBG_ENTER(mysqlnd_pefree_name);
1054: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
1055: DBG_INF_FMT("ptr=%p persistent=%u", ptr, persistent);
1056:
1057: if (ptr) {
1058: if (collect_memory_statistics) {
1059: free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
1060: DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
1061: }
1062: pefree(REAL_PTR(ptr), persistent);
1063: }
1064:
1065: if (collect_memory_statistics) {
1066: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT, 1,
1067: persistent? STAT_MEM_FREE_AMOUNT:STAT_MEM_EFREE_AMOUNT, free_amount);
1068: }
1069: DBG_VOID_RETURN;
1070: }
1071:
1072:
1073: /* {{{ _mysqlnd_malloc */
1074: void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
1075: {
1076: void *ret;
1077: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
1078: long * threshold = &MYSQLND_G(debug_malloc_fail_threshold);
1079: DBG_ENTER(mysqlnd_malloc_name);
1080: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
1081:
1082: #ifdef PHP_DEBUG
1083: /* -1 is also "true" */
1084: if (*threshold) {
1085: #endif
1086: ret = malloc(REAL_SIZE(size));
1087: #ifdef PHP_DEBUG
1088: --*threshold;
1089: } else if (*threshold == 0) {
1090: ret = NULL;
1091: }
1092: #endif
1093:
1094: DBG_INF_FMT("size=%lu ptr=%p", size, ret);
1095: if (ret && collect_memory_statistics) {
1096: *(size_t *) ret = size;
1097: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMOUNT, size);
1098: }
1099: DBG_RETURN(FAKE_PTR(ret));
1100: }
1101: /* }}} */
1102:
1103:
1104: /* {{{ _mysqlnd_calloc */
1105: void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
1106: {
1107: void *ret;
1108: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
1109: long * threshold = &MYSQLND_G(debug_calloc_fail_threshold);
1110: DBG_ENTER(mysqlnd_calloc_name);
1111: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
1112:
1113: #ifdef PHP_DEBUG
1114: /* -1 is also "true" */
1115: if (*threshold) {
1116: #endif
1117: ret = calloc(nmemb, REAL_SIZE(size));
1118: #ifdef PHP_DEBUG
1119: --*threshold;
1120: } else if (*threshold == 0) {
1121: ret = NULL;
1122: }
1123: #endif
1124:
1125: DBG_INF_FMT("size=%lu ptr=%p", size, ret);
1126: if (ret && collect_memory_statistics) {
1127: *(size_t *) ret = size;
1128: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMOUNT, size);
1129: }
1130: DBG_RETURN(FAKE_PTR(ret));
1131: }
1132: /* }}} */
1133:
1134:
1135: /* {{{ _mysqlnd_realloc */
1136: void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
1137: {
1138: void *ret;
1139: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
1140: long * threshold = &MYSQLND_G(debug_realloc_fail_threshold);
1141: DBG_ENTER(mysqlnd_realloc_name);
1142: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
1143: DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr);
1144: DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC));
1145:
1146: #ifdef PHP_DEBUG
1147: /* -1 is also "true" */
1148: if (*threshold) {
1149: #endif
1150: ret = realloc(REAL_PTR(ptr), REAL_SIZE(new_size));
1151: #ifdef PHP_DEBUG
1152: --*threshold;
1153: } else if (*threshold == 0) {
1154: ret = NULL;
1155: }
1156: #endif
1157:
1158: DBG_INF_FMT("new_ptr=%p", (char*)ret);
1159:
1160: if (ret && collect_memory_statistics) {
1161: *(size_t *) ret = new_size;
1162: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMOUNT, new_size);
1163: }
1164: DBG_RETURN(FAKE_PTR(ret));
1165: }
1166: /* }}} */
1167:
1168:
1169: /* {{{ _mysqlnd_free */
1170: void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
1171: {
1172: size_t free_amount = 0;
1173: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
1174: DBG_ENTER(mysqlnd_free_name);
1175: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
1176: DBG_INF_FMT("ptr=%p", ptr);
1177:
1178: if (ptr) {
1179: if (collect_memory_statistics) {
1180: free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
1181: DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
1182: }
1183: free(REAL_PTR(ptr));
1184: }
1185:
1186: if (collect_memory_statistics) {
1187: MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_FREE_COUNT, 1, STAT_MEM_FREE_AMOUNT, free_amount);
1188: }
1189: DBG_VOID_RETURN;
1190: }
1191: /* }}} */
1192:
1193: #define SMART_STR_START_SIZE 2048
1194: #define SMART_STR_PREALLOC 512
1195: #include "ext/standard/php_smart_str.h"
1196:
1197:
1198: /* {{{ _mysqlnd_pestrndup */
1199: char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
1200: {
1201: char * ret;
1202: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
1203: DBG_ENTER(mysqlnd_pestrndup_name);
1204: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
1205: DBG_INF_FMT("ptr=%p", ptr);
1206:
1207: ret = pemalloc(REAL_SIZE(length) + 1, persistent);
1208: {
1209: size_t l = length;
1210: char * p = (char *) ptr;
1211: char * dest = (char *) FAKE_PTR(ret);
1212: while (*p && l--) {
1213: *dest++ = *p++;
1214: }
1215: *dest = '\0';
1216: }
1217:
1218: if (collect_memory_statistics) {
1219: *(size_t *) ret = length;
1220: MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRNDUP_COUNT : STAT_MEM_ESTRNDUP_COUNT);
1221: }
1222:
1223: DBG_RETURN(FAKE_PTR(ret));
1224: }
1225: /* }}} */
1226:
1227:
1228: /* {{{ _mysqlnd_pestrdup */
1229: char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
1230: {
1231: char * ret;
1232: smart_str tmp_str = {0, 0, 0};
1233: const char * p = ptr;
1234: zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
1235: DBG_ENTER(mysqlnd_pestrdup_name);
1236: DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
1237: DBG_INF_FMT("ptr=%p", ptr);
1238: do {
1239: smart_str_appendc(&tmp_str, *p);
1240: } while (*p++);
1241:
1242: ret = pemalloc(tmp_str.len + sizeof(size_t), persistent);
1243: memcpy(FAKE_PTR(ret), tmp_str.c, tmp_str.len);
1244:
1245: if (ret && collect_memory_statistics) {
1246: *(size_t *) ret = tmp_str.len;
1247: MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRDUP_COUNT : STAT_MEM_ESTRDUP_COUNT);
1248: }
1249: smart_str_free(&tmp_str);
1250:
1251: DBG_RETURN(FAKE_PTR(ret));
1252: }
1253: /* }}} */
1254:
1255: #define MYSQLND_DEBUG_MEMORY 1
1256:
1257: #if MYSQLND_DEBUG_MEMORY == 0
1258:
1259: /* {{{ mysqlnd_zend_mm_emalloc */
1260: static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
1261: {
1262: return emalloc(size);
1263: }
1264: /* }}} */
1265:
1266:
1267: /* {{{ mysqlnd_zend_mm_pemalloc */
1268: static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
1269: {
1270: return pemalloc(size, persistent);
1271: }
1272: /* }}} */
1273:
1274:
1275: /* {{{ mysqlnd_zend_mm_ecalloc */
1276: static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
1277: {
1278: return ecalloc(nmemb, size);
1279: }
1280: /* }}} */
1281:
1282:
1283: /* {{{ mysqlnd_zend_mm_pecalloc */
1284: static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
1285: {
1286: return pecalloc(nmemb, size, persistent);
1287: }
1288: /* }}} */
1289:
1290:
1291: /* {{{ mysqlnd_zend_mm_erealloc */
1292: static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
1293: {
1294: return erealloc(ptr, new_size);
1295: }
1296: /* }}} */
1297:
1298:
1299: /* {{{ mysqlnd_zend_mm_perealloc */
1300: static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
1301: {
1302: return perealloc(ptr, new_size, persistent);
1303: }
1304: /* }}} */
1305:
1306:
1307: /* {{{ mysqlnd_zend_mm_efree */
1308: static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
1309: {
1310: efree(ptr);
1311: }
1312: /* }}} */
1313:
1314:
1315: /* {{{ mysqlnd_zend_mm_pefree */
1316: static void mysqlnd_zend_mm_pefree(void * ptr, zend_bool persistent MYSQLND_MEM_D)
1317: {
1318: pefree(ptr, persistent);
1319: }
1320: /* }}} */
1321:
1322:
1323: /* {{{ mysqlnd_zend_mm_malloc */
1324: static void * mysqlnd_zend_mm_malloc(size_t size MYSQLND_MEM_D)
1325: {
1326: return malloc(size);
1327: }
1328: /* }}} */
1329:
1330:
1331: /* {{{ mysqlnd_zend_mm_calloc */
1332: static void * mysqlnd_zend_mm_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
1333: {
1334: return calloc(nmemb, size);
1335: }
1336: /* }}} */
1337:
1338:
1339: /* {{{ mysqlnd_zend_mm_realloc */
1340: static void * mysqlnd_zend_mm_realloc(void * ptr, size_t new_size MYSQLND_MEM_D)
1341: {
1342: return realloc(ptr, new_size);
1343: }
1344: /* }}} */
1345:
1346:
1347: /* {{{ mysqlnd_zend_mm_free */
1348: static void mysqlnd_zend_mm_free(void * ptr MYSQLND_MEM_D)
1349: {
1350: free(ptr);
1351: }
1352: /* }}} */
1353:
1354:
1355: /* {{{ mysqlnd_zend_mm_pestrndup */
1356: static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
1357: {
1358: return pestrndup(ptr, length, persistent);
1359: }
1360: /* }}} */
1361:
1362:
1363: /* {{{ mysqlnd_zend_mm_pestrdup */
1364: static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
1365: {
1366: return pestrdup(ptr, persistent);
1367: }
1368: /* }}} */
1369:
1370: #endif
1371:
1372:
1373: PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
1374: {
1375: #if MYSQLND_DEBUG_MEMORY
1376: _mysqlnd_emalloc,
1377: _mysqlnd_pemalloc,
1378: _mysqlnd_ecalloc,
1379: _mysqlnd_pecalloc,
1380: _mysqlnd_erealloc,
1381: _mysqlnd_perealloc,
1382: _mysqlnd_efree,
1383: _mysqlnd_pefree,
1384: _mysqlnd_malloc,
1385: _mysqlnd_calloc,
1386: _mysqlnd_realloc,
1387: _mysqlnd_free,
1388: _mysqlnd_pestrndup,
1389: _mysqlnd_pestrdup
1390: #else
1391: mysqlnd_zend_mm_emalloc,
1392: mysqlnd_zend_mm_pemalloc,
1393: mysqlnd_zend_mm_ecalloc,
1394: mysqlnd_zend_mm_pecalloc,
1395: mysqlnd_zend_mm_erealloc,
1396: mysqlnd_zend_mm_perealloc,
1397: mysqlnd_zend_mm_efree,
1398: mysqlnd_zend_mm_pefree,
1399: mysqlnd_zend_mm_malloc,
1400: mysqlnd_zend_mm_calloc,
1401: mysqlnd_zend_mm_realloc,
1402: mysqlnd_zend_mm_free,
1403: mysqlnd_zend_mm_pestrndup,
1404: mysqlnd_zend_mm_pestrdup
1405: #endif
1406: };
1407:
1408:
1409:
1410: /* Follows code borrowed from zend_builtin_functions.c because the functions there are static */
1411:
1412: #if MYSQLND_UNICODE
1413: /* {{{ gettraceasstring() macros */
1414: #define TRACE_APPEND_CHR(chr) \
1415: *str = (char*)erealloc(*str, *len + 1 + 1); \
1416: (*str)[(*len)++] = chr
1417:
1418: #define TRACE_APPEND_STRL(val, vallen) \
1419: { \
1420: int l = vallen; \
1421: *str = (char*)erealloc(*str, *len + l + 1); \
1422: memcpy((*str) + *len, val, l); \
1423: *len += l; \
1424: }
1425:
1426: #define TRACE_APPEND_USTRL(val, vallen) \
1427: { \
1428: zval tmp, copy; \
1429: int use_copy; \
1430: ZVAL_UNICODEL(&tmp, val, vallen, 1); \
1431: zend_make_printable_zval(&tmp, ©, &use_copy); \
1432: TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
1433: zval_dtor(©); \
1434: zval_dtor(&tmp); \
1435: }
1436:
1437: #define TRACE_APPEND_ZVAL(zv) \
1438: if (Z_TYPE_P((zv)) == IS_UNICODE) { \
1439: zval copy; \
1440: int use_copy; \
1441: zend_make_printable_zval((zv), ©, &use_copy); \
1442: TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
1443: zval_dtor(©); \
1444: } else { \
1445: TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
1446: }
1447:
1448: #define TRACE_APPEND_STR(val) \
1449: TRACE_APPEND_STRL(val, sizeof(val)-1)
1450:
1451: #define TRACE_APPEND_KEY(key) \
1452: if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
1453: if (Z_TYPE_PP(tmp) == IS_UNICODE) { \
1454: zval copy; \
1455: int use_copy; \
1456: zend_make_printable_zval(*tmp, ©, &use_copy); \
1457: TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
1458: zval_dtor(©); \
1459: } else { \
1460: TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
1461: } \
1462: }
1463: /* }}} */
1464:
1465: /* {{{ mysqlnd_build_trace_args */
1466: static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
1467: {
1468: char **str;
1469: int *len;
1470:
1471: str = va_arg(args, char**);
1472: len = va_arg(args, int*);
1473:
1474: /* the trivial way would be to do:
1475: * conver_to_string_ex(arg);
1476: * append it and kill the now tmp arg.
1477: * but that could cause some E_NOTICE and also damn long lines.
1478: */
1479:
1480: switch (Z_TYPE_PP(arg)) {
1481: case IS_NULL:
1482: TRACE_APPEND_STR("NULL, ");
1483: break;
1484: case IS_STRING: {
1485: int l_added;
1486: TRACE_APPEND_CHR('\'');
1487: if (Z_STRLEN_PP(arg) > 15) {
1488: TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
1489: TRACE_APPEND_STR("...', ");
1490: l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
1491: } else {
1492: l_added = Z_STRLEN_PP(arg);
1493: TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
1494: TRACE_APPEND_STR("', ");
1495: l_added += 3 + 1;
1496: }
1497: while (--l_added) {
1498: if ((unsigned char)(*str)[*len - l_added] < 32) {
1499: (*str)[*len - l_added] = '?';
1500: }
1501: }
1502: break;
1503: }
1504: case IS_UNICODE: {
1505: int l_added;
1506:
1507: /*
1508: * We do not want to apply current error mode here, since
1509: * zend_make_printable_zval() uses output encoding converter.
1510: * Temporarily set output encoding converter to escape offending
1511: * chars with \uXXXX notation.
1512: */
1513: zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA);
1514: TRACE_APPEND_CHR('\'');
1515: if (Z_USTRLEN_PP(arg) > 15) {
1516: TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15);
1517: TRACE_APPEND_STR("...', ");
1518: l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
1519: } else {
1520: l_added = Z_USTRLEN_PP(arg);
1521: TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added);
1522: TRACE_APPEND_STR("', ");
1523: l_added += 3 + 1;
1524: }
1525: /*
1526: * Reset output encoding converter error mode.
1527: */
1528: zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode));
1529: while (--l_added) {
1530: if ((unsigned char)(*str)[*len - l_added] < 32) {
1531: (*str)[*len - l_added] = '?';
1532: }
1533: }
1534: break;
1535: }
1536: case IS_BOOL:
1537: if (Z_LVAL_PP(arg)) {
1538: TRACE_APPEND_STR("true, ");
1539: } else {
1540: TRACE_APPEND_STR("false, ");
1541: }
1542: break;
1543: case IS_RESOURCE:
1544: TRACE_APPEND_STR("Resource id #");
1545: /* break; */
1546: case IS_LONG: {
1547: long lval = Z_LVAL_PP(arg);
1548: char s_tmp[MAX_LENGTH_OF_LONG + 1];
1549: int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
1550: TRACE_APPEND_STRL(s_tmp, l_tmp);
1551: TRACE_APPEND_STR(", ");
1552: break;
1553: }
1554: case IS_DOUBLE: {
1555: double dval = Z_DVAL_PP(arg);
1556: char *s_tmp;
1557: int l_tmp;
1558:
1559: s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
1560: l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
1561: TRACE_APPEND_STRL(s_tmp, l_tmp);
1562: /* %G already handles removing trailing zeros from the fractional part, yay */
1563: efree(s_tmp);
1564: TRACE_APPEND_STR(", ");
1565: break;
1566: }
1567: case IS_ARRAY:
1568: TRACE_APPEND_STR("Array, ");
1569: break;
1570: case IS_OBJECT: {
1571: zval tmp;
1572: zstr class_name;
1573: zend_uint class_name_len;
1574: int dup;
1575:
1576: TRACE_APPEND_STR("Object(");
1577:
1578: dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
1579:
1580: ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1);
1581: convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv)));
1582: TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp));
1583: zval_dtor(&tmp);
1584:
1585: if(!dup) {
1586: efree(class_name.v);
1587: }
1588:
1589: TRACE_APPEND_STR("), ");
1590: break;
1591: }
1592: default:
1593: break;
1594: }
1595: return ZEND_HASH_APPLY_KEEP;
1596: }
1597: /* }}} */
1598:
1599:
1600: static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
1601: {
1602: char *s_tmp, **str;
1603: int *len, *num;
1604: long line;
1605: HashTable *ht = Z_ARRVAL_PP(frame);
1606: zval **file, **tmp;
1607: uint * level;
1608:
1609: level = va_arg(args, uint *);
1610: str = va_arg(args, char**);
1611: len = va_arg(args, int*);
1612: num = va_arg(args, int*);
1613:
1614: if (!*level) {
1615: return ZEND_HASH_APPLY_KEEP;
1616: }
1617: --*level;
1618:
1619: s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
1620: sprintf(s_tmp, "#%d ", (*num)++);
1621: TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1622: efree(s_tmp);
1623: if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
1624: if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
1625: line = Z_LVAL_PP(tmp);
1626: } else {
1627: line = 0;
1628: }
1629: TRACE_APPEND_ZVAL(*file);
1630: s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
1631: sprintf(s_tmp, "(%ld): ", line);
1632: TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1633: efree(s_tmp);
1634: } else {
1635: TRACE_APPEND_STR("[internal function]: ");
1636: }
1637: TRACE_APPEND_KEY("class");
1638: TRACE_APPEND_KEY("type");
1639: TRACE_APPEND_KEY("function");
1640: TRACE_APPEND_CHR('(');
1641: if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
1642: int last_len = *len;
1643: zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
1644: if (last_len != *len) {
1645: *len -= 2; /* remove last ', ' */
1646: }
1647: }
1648: TRACE_APPEND_STR(")\n");
1649: return ZEND_HASH_APPLY_KEEP;
1650: }
1651: /* }}} */
1652:
1653:
1654: #else /* PHP 5*/
1655:
1656:
1657: /* {{{ gettraceasstring() macros */
1658: #define TRACE_APPEND_CHR(chr) \
1659: *str = (char*)erealloc(*str, *len + 1 + 1); \
1660: (*str)[(*len)++] = chr
1661:
1662: #define TRACE_APPEND_STRL(val, vallen) \
1663: { \
1664: int l = vallen; \
1665: *str = (char*)erealloc(*str, *len + l + 1); \
1666: memcpy((*str) + *len, val, l); \
1667: *len += l; \
1668: }
1669:
1670: #define TRACE_APPEND_STR(val) \
1671: TRACE_APPEND_STRL(val, sizeof(val)-1)
1672:
1673: #define TRACE_APPEND_KEY(key) \
1674: if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
1675: TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
1676: }
1677:
1678: /* }}} */
1679:
1680:
1681: static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
1682: {
1683: char **str;
1684: int *len;
1685:
1686: str = va_arg(args, char**);
1687: len = va_arg(args, int*);
1688:
1689: /* the trivial way would be to do:
1690: * conver_to_string_ex(arg);
1691: * append it and kill the now tmp arg.
1692: * but that could cause some E_NOTICE and also damn long lines.
1693: */
1694:
1695: switch (Z_TYPE_PP(arg)) {
1696: case IS_NULL:
1697: TRACE_APPEND_STR("NULL, ");
1698: break;
1699: case IS_STRING: {
1700: int l_added;
1701: TRACE_APPEND_CHR('\'');
1702: if (Z_STRLEN_PP(arg) > 15) {
1703: TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
1704: TRACE_APPEND_STR("...', ");
1705: l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
1706: } else {
1707: l_added = Z_STRLEN_PP(arg);
1708: TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
1709: TRACE_APPEND_STR("', ");
1710: l_added += 3 + 1;
1711: }
1712: while (--l_added) {
1713: if ((*str)[*len - l_added] < 32) {
1714: (*str)[*len - l_added] = '?';
1715: }
1716: }
1717: break;
1718: }
1719: case IS_BOOL:
1720: if (Z_LVAL_PP(arg)) {
1721: TRACE_APPEND_STR("true, ");
1722: } else {
1723: TRACE_APPEND_STR("false, ");
1724: }
1725: break;
1726: case IS_RESOURCE:
1727: TRACE_APPEND_STR("Resource id #");
1728: /* break; */
1729: case IS_LONG: {
1730: long lval = Z_LVAL_PP(arg);
1731: char s_tmp[MAX_LENGTH_OF_LONG + 1];
1732: int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
1733: TRACE_APPEND_STRL(s_tmp, l_tmp);
1734: TRACE_APPEND_STR(", ");
1735: break;
1736: }
1737: case IS_DOUBLE: {
1738: double dval = Z_DVAL_PP(arg);
1739: char *s_tmp;
1740: int l_tmp;
1741:
1742: s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
1743: l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
1744: TRACE_APPEND_STRL(s_tmp, l_tmp);
1745: /* %G already handles removing trailing zeros from the fractional part, yay */
1746: efree(s_tmp);
1747: TRACE_APPEND_STR(", ");
1748: break;
1749: }
1750: case IS_ARRAY:
1751: TRACE_APPEND_STR("Array, ");
1752: break;
1753: case IS_OBJECT: {
1754: char *class_name;
1755: zend_uint class_name_len;
1756: int dupl;
1757:
1758: TRACE_APPEND_STR("Object(");
1759:
1760: dupl = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
1761:
1762: TRACE_APPEND_STRL(class_name, class_name_len);
1763: if (!dupl) {
1764: efree(class_name);
1765: }
1766:
1767: TRACE_APPEND_STR("), ");
1768: break;
1769: }
1770: default:
1771: break;
1772: }
1773: return ZEND_HASH_APPLY_KEEP;
1774: }
1775: /* }}} */
1776:
1777: static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
1778: {
1779: char *s_tmp, **str;
1780: int *len, *num;
1781: long line;
1782: HashTable *ht = Z_ARRVAL_PP(frame);
1783: zval **file, **tmp;
1784: uint * level;
1785:
1786: level = va_arg(args, uint *);
1787: str = va_arg(args, char**);
1788: len = va_arg(args, int*);
1789: num = va_arg(args, int*);
1790:
1791: if (!*level) {
1792: return ZEND_HASH_APPLY_KEEP;
1793: }
1794: --*level;
1795:
1796: s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
1797: sprintf(s_tmp, "#%d ", (*num)++);
1798: TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1799: efree(s_tmp);
1800: if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
1801: if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
1802: line = Z_LVAL_PP(tmp);
1803: } else {
1804: line = 0;
1805: }
1806: s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
1807: sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
1808: TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1809: efree(s_tmp);
1810: } else {
1811: TRACE_APPEND_STR("[internal function]: ");
1812: }
1813: TRACE_APPEND_KEY("class");
1814: TRACE_APPEND_KEY("type");
1815: TRACE_APPEND_KEY("function");
1816: TRACE_APPEND_CHR('(');
1817: if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
1818: int last_len = *len;
1819: zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
1820: if (last_len != *len) {
1821: *len -= 2; /* remove last ', ' */
1822: }
1823: }
1824: TRACE_APPEND_STR(")\n");
1825: return ZEND_HASH_APPLY_KEEP;
1826: }
1827: /* }}} */
1828: #endif
1829:
1830:
1831: PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC)
1832: {
1833: zval *trace;
1834: char *res = estrdup(""), **str = &res, *s_tmp;
1835: int res_len = 0, *len = &res_len, num = 0;
1836: if (max_levels == 0) {
1837: max_levels = 99999;
1838: }
1839:
1840: MAKE_STD_ZVAL(trace);
1841: zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC);
1842:
1843: zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_string, 4, &max_levels, str, len, &num);
1844: zval_ptr_dtor(&trace);
1845:
1846: if (max_levels) {
1847: s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
1848: sprintf(s_tmp, "#%d {main}", num);
1849: TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1850: efree(s_tmp);
1851: }
1852:
1853: res[res_len] = '\0';
1854: *length = res_len;
1855:
1856: return res;
1857: }
1858:
1859: /*
1860: * Local variables:
1861: * tab-width: 4
1862: * c-basic-offset: 4
1863: * End:
1864: * vim600: noet sw=4 ts=4 fdm=marker
1865: * vim<600: noet sw=4 ts=4
1866: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>