1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 2006-2009 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 282779 2009-06-25 19:03:52Z johannes $ */
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_palloc.h"
29 : #include "mysqlnd_statistics.h"
30 : #include "zend_builtin_functions.h"
31 :
32 :
33 : static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace";
34 :
35 : #ifdef ZTS
36 : #define MYSQLND_ZTS(self) TSRMLS_D = (self)->TSRMLS_C
37 : #else
38 : #define MYSQLND_ZTS(self)
39 : #endif
40 :
41 : #define MYSQLND_DEBUG_DUMP_TIME 1
42 : #define MYSQLND_DEBUG_DUMP_TRACE 2
43 : #define MYSQLND_DEBUG_DUMP_PID 4
44 : #define MYSQLND_DEBUG_DUMP_LINE 8
45 : #define MYSQLND_DEBUG_DUMP_FILE 16
46 : #define MYSQLND_DEBUG_DUMP_LEVEL 32
47 : #define MYSQLND_DEBUG_APPEND 64
48 : #define MYSQLND_DEBUG_FLUSH 128
49 : #define MYSQLND_DEBUG_TRACE_MEMORY_CALLS 256
50 :
51 : static char * mysqlnd_emalloc_name = "_mysqlnd_emalloc";
52 : static char * mysqlnd_pemalloc_name = "_mysqlnd_pemalloc";
53 : static char * mysqlnd_ecalloc_name = "_mysqlnd_ecalloc";
54 : static char * mysqlnd_pecalloc_name = "_mysqlnd_pecalloc";
55 : static char * mysqlnd_erealloc_name = "_mysqlnd_erealloc";
56 : static char * mysqlnd_perealloc_name= "_mysqlnd_perealloc";
57 : static char * mysqlnd_efree_name = "_mysqlnd_efree";
58 : static char * mysqlnd_pefree_name = "_mysqlnd_pefree";
59 : static char * mysqlnd_malloc_name = "_mysqlnd_malloc";
60 : static char * mysqlnd_calloc_name = "_mysqlnd_calloc";
61 : static char * mysqlnd_realloc_name = "_mysqlnd_realloc";
62 : static char * mysqlnd_free_name = "_mysqlnd_free";
63 :
64 : /* {{{ mysqlnd_debug::open */
65 : static enum_func_status
66 : MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen)
67 0 : {
68 : MYSQLND_ZTS(self);
69 :
70 0 : if (!self->file_name) {
71 0 : return FAIL;
72 : }
73 :
74 0 : self->stream = php_stream_open_wrapper(self->file_name,
75 : reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb",
76 : REPORT_ERRORS, NULL);
77 0 : return self->stream? PASS:FAIL;
78 : }
79 : /* }}} */
80 :
81 :
82 : /* {{{ mysqlnd_debug::log */
83 : static enum_func_status
84 : MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self,
85 : unsigned int line, const char * const file,
86 : unsigned int level, const char * type, const char * message)
87 0 : {
88 : char pipe_buffer[512];
89 : enum_func_status ret;
90 : int i;
91 : char * message_line;
92 : unsigned int message_line_len;
93 0 : unsigned int flags = self->flags;
94 : char pid_buffer[10], time_buffer[30], file_buffer[200],
95 : line_buffer[6], level_buffer[7];
96 : MYSQLND_ZTS(self);
97 :
98 : #ifdef MYSQLND_THREADED
99 : if (MYSQLND_G(thread_id) != tsrm_thread_id()) {
100 : return PASS; /* don't trace background threads */
101 : }
102 : #endif
103 :
104 0 : if (!self->stream) {
105 0 : if (FAIL == self->m->open(self, FALSE)) {
106 0 : return FAIL;
107 : }
108 : }
109 :
110 0 : if (level == -1) {
111 0 : level = zend_stack_count(&self->call_stack);
112 : }
113 0 : i = MIN(level, sizeof(pipe_buffer) / 2 - 1);
114 0 : pipe_buffer[i*2] = '\0';
115 0 : for (;i > 0;i--) {
116 0 : pipe_buffer[i*2 - 1] = ' ';
117 0 : pipe_buffer[i*2 - 2] = '|';
118 : }
119 :
120 :
121 0 : if (flags & MYSQLND_DEBUG_DUMP_PID) {
122 0 : snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
123 0 : pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
124 : }
125 0 : if (flags & MYSQLND_DEBUG_DUMP_TIME) {
126 : /* The following from FF's DBUG library, which is in the public domain */
127 : #if defined(PHP_WIN32)
128 : /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
129 : in system ticks, 10 ms intervals. See my_getsystime.c for high res */
130 : SYSTEMTIME loc_t;
131 : GetLocalTime(&loc_t);
132 : snprintf(time_buffer, sizeof(time_buffer) - 1,
133 : /* "%04d-%02d-%02d " */
134 : "%02d:%02d:%02d.%06d ",
135 : /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
136 : loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
137 : time_buffer[sizeof(time_buffer) - 1 ] = '\0';
138 : #else
139 : struct timeval tv;
140 : struct tm *tm_p;
141 0 : if (gettimeofday(&tv, NULL) != -1) {
142 0 : if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
143 0 : snprintf(time_buffer, sizeof(time_buffer) - 1,
144 : /* "%04d-%02d-%02d " */
145 : "%02d:%02d:%02d.%06d ",
146 : /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
147 : tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
148 : (int) (tv.tv_usec));
149 0 : time_buffer[sizeof(time_buffer) - 1 ] = '\0';
150 : }
151 : }
152 : #endif
153 : }
154 0 : if (flags & MYSQLND_DEBUG_DUMP_FILE) {
155 0 : snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
156 0 : file_buffer[sizeof(file_buffer) - 1 ] = '\0';
157 : }
158 0 : if (flags & MYSQLND_DEBUG_DUMP_LINE) {
159 0 : snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
160 0 : line_buffer[sizeof(line_buffer) - 1 ] = '\0';
161 : }
162 0 : if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
163 0 : snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
164 0 : level_buffer[sizeof(level_buffer) - 1 ] = '\0';
165 : }
166 :
167 0 : message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
168 : flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
169 : flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
170 : flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
171 : flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
172 : flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
173 : pipe_buffer, type? type:"", message);
174 :
175 0 : ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
176 0 : efree(message_line);
177 0 : if (flags & MYSQLND_DEBUG_FLUSH) {
178 0 : self->m->close(self);
179 0 : self->m->open(self, TRUE);
180 : }
181 0 : return ret;
182 : }
183 : /* }}} */
184 :
185 :
186 : /* {{{ mysqlnd_debug::log_va */
187 : static enum_func_status
188 : MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self,
189 : unsigned int line, const char * const file,
190 : unsigned int level, const char * type,
191 : const char *format, ...)
192 0 : {
193 : char pipe_buffer[512];
194 : int i;
195 : enum_func_status ret;
196 : char * message_line, *buffer;
197 : unsigned int message_line_len;
198 : va_list args;
199 0 : unsigned int flags = self->flags;
200 : char pid_buffer[10], time_buffer[30], file_buffer[200],
201 : line_buffer[6], level_buffer[7];
202 : MYSQLND_ZTS(self);
203 :
204 : #ifdef MYSQLND_THREADED
205 : if (MYSQLND_G(thread_id) != tsrm_thread_id()) {
206 : return PASS; /* don't trace background threads */
207 : }
208 : #endif
209 :
210 0 : if (!self->stream) {
211 0 : if (FAIL == self->m->open(self, FALSE)) {
212 0 : return FAIL;
213 : }
214 : }
215 :
216 0 : if (level == -1) {
217 0 : level = zend_stack_count(&self->call_stack);
218 : }
219 0 : i = MIN(level, sizeof(pipe_buffer) / 2 - 1);
220 0 : pipe_buffer[i*2] = '\0';
221 0 : for (;i > 0;i--) {
222 0 : pipe_buffer[i*2 - 1] = ' ';
223 0 : pipe_buffer[i*2 - 2] = '|';
224 : }
225 :
226 :
227 0 : if (flags & MYSQLND_DEBUG_DUMP_PID) {
228 0 : snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
229 0 : pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
230 : }
231 0 : if (flags & MYSQLND_DEBUG_DUMP_TIME) {
232 : /* The following from FF's DBUG library, which is in the public domain */
233 : #if defined(PHP_WIN32)
234 : /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
235 : in system ticks, 10 ms intervals. See my_getsystime.c for high res */
236 : SYSTEMTIME loc_t;
237 : GetLocalTime(&loc_t);
238 : snprintf(time_buffer, sizeof(time_buffer) - 1,
239 : /* "%04d-%02d-%02d " */
240 : "%02d:%02d:%02d.%06d ",
241 : /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
242 : loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
243 : time_buffer[sizeof(time_buffer) - 1 ] = '\0';
244 : #else
245 : struct timeval tv;
246 : struct tm *tm_p;
247 0 : if (gettimeofday(&tv, NULL) != -1) {
248 0 : if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
249 0 : snprintf(time_buffer, sizeof(time_buffer) - 1,
250 : /* "%04d-%02d-%02d " */
251 : "%02d:%02d:%02d.%06d ",
252 : /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
253 : tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
254 : (int) (tv.tv_usec));
255 0 : time_buffer[sizeof(time_buffer) - 1 ] = '\0';
256 : }
257 : }
258 : #endif
259 : }
260 0 : if (flags & MYSQLND_DEBUG_DUMP_FILE) {
261 0 : snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
262 0 : file_buffer[sizeof(file_buffer) - 1 ] = '\0';
263 : }
264 0 : if (flags & MYSQLND_DEBUG_DUMP_LINE) {
265 0 : snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
266 0 : line_buffer[sizeof(line_buffer) - 1 ] = '\0';
267 : }
268 0 : if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
269 0 : snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
270 0 : level_buffer[sizeof(level_buffer) - 1 ] = '\0';
271 : }
272 :
273 :
274 0 : va_start(args, format);
275 0 : vspprintf(&buffer, 0, format, args);
276 0 : va_end(args);
277 :
278 0 : message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
279 : flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
280 : flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
281 : flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
282 : flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
283 : flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
284 : pipe_buffer, type? type:"", buffer);
285 0 : efree(buffer);
286 :
287 0 : ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
288 0 : efree(message_line);
289 :
290 0 : if (flags & MYSQLND_DEBUG_FLUSH) {
291 0 : self->m->close(self);
292 0 : self->m->open(self, TRUE);
293 : }
294 0 : return ret;
295 : }
296 : /* }}} */
297 :
298 :
299 : /* FALSE - The DBG_ calls won't be traced, TRUE - will be traced */
300 : /* {{{ mysqlnd_res_meta::func_enter */
301 : static zend_bool
302 : MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self,
303 : unsigned int line, const char * const file,
304 : char * func_name, unsigned int func_name_len)
305 0 : {
306 : #ifdef MYSQLND_THREADED
307 : MYSQLND_ZTS(self);
308 : #endif
309 0 : if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
310 0 : return FALSE;
311 : }
312 : #ifdef MYSQLND_THREADED
313 : if (MYSQLND_G(thread_id) != tsrm_thread_id()) {
314 : return FALSE; /* don't trace background threads */
315 : }
316 : #endif
317 0 : if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
318 0 : return FALSE;
319 : }
320 :
321 0 : if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 &&
322 : (func_name == mysqlnd_emalloc_name || func_name == mysqlnd_pemalloc_name ||
323 : func_name == mysqlnd_ecalloc_name || func_name == mysqlnd_pecalloc_name ||
324 : func_name == mysqlnd_erealloc_name || func_name == mysqlnd_perealloc_name ||
325 : func_name == mysqlnd_efree_name || func_name == mysqlnd_pefree_name ||
326 : func_name == mysqlnd_malloc_name || func_name == mysqlnd_calloc_name ||
327 : func_name == mysqlnd_realloc_name || func_name == mysqlnd_free_name ||
328 : func_name == mysqlnd_palloc_zval_ptr_dtor_name || func_name == mysqlnd_palloc_get_zval_name ||
329 : func_name == mysqlnd_read_header_name || func_name == mysqlnd_read_body_name)) {
330 0 : zend_stack_push(&self->call_stack, "", sizeof(""));
331 0 : return FALSE;
332 : }
333 :
334 0 : zend_stack_push(&self->call_stack, func_name, func_name_len + 1);
335 :
336 0 : if (zend_hash_num_elements(&self->not_filtered_functions) &&
337 : 0 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
338 : {
339 0 : return FALSE;
340 : }
341 :
342 0 : self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name);
343 0 : return TRUE;
344 : }
345 : /* }}} */
346 :
347 :
348 : /* {{{ mysqlnd_res_meta::func_leave */
349 : static enum_func_status
350 : MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line,
351 : const char * const file)
352 0 : {
353 : char *func_name;
354 : #ifdef MYSQLND_THREADED
355 : MYSQLND_ZTS(self);
356 : #endif
357 0 : if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
358 0 : return PASS;
359 : }
360 : #ifdef MYSQLND_THREADED
361 : if (MYSQLND_G(thread_id) != tsrm_thread_id()) {
362 : return PASS; /* don't trace background threads */
363 : }
364 : #endif
365 0 : if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
366 0 : return PASS;
367 : }
368 :
369 0 : zend_stack_top(&self->call_stack, (void **)&func_name);
370 :
371 0 : if (func_name[0] == '\0') {
372 : ; /* don't log that function */
373 0 : } else if (!zend_hash_num_elements(&self->not_filtered_functions) ||
374 : 1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
375 : {
376 0 : self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name);
377 : }
378 :
379 0 : return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL;
380 : }
381 : /* }}} */
382 :
383 :
384 : /* {{{ mysqlnd_res_meta::close */
385 : static enum_func_status
386 : MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self)
387 1 : {
388 : MYSQLND_ZTS(self);
389 1 : if (self->stream) {
390 0 : php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE);
391 0 : self->stream = NULL;
392 : }
393 1 : return PASS;
394 : }
395 : /* }}} */
396 :
397 :
398 : /* {{{ mysqlnd_res_meta::free */
399 : static enum_func_status
400 : MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self)
401 1 : {
402 1 : if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
403 1 : efree(self->file_name);
404 1 : self->file_name = NULL;
405 : }
406 1 : zend_stack_destroy(&self->call_stack);
407 1 : zend_hash_destroy(&self->not_filtered_functions);
408 1 : efree(self);
409 1 : return PASS;
410 : }
411 : /* }}} */
412 :
413 : enum mysqlnd_debug_parser_state
414 : {
415 : PARSER_WAIT_MODIFIER,
416 : PARSER_WAIT_COLON,
417 : PARSER_WAIT_VALUE
418 : };
419 :
420 :
421 : /* {{{ mysqlnd_res_meta::set_mode */
422 : static void
423 : MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode)
424 1 : {
425 1 : unsigned int mode_len = strlen(mode), i;
426 1 : enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER;
427 :
428 1 : self->flags = 0;
429 1 : self->nest_level_limit = 0;
430 1 : if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
431 0 : efree(self->file_name);
432 0 : self->file_name = NULL;
433 : }
434 1 : if (zend_hash_num_elements(&self->not_filtered_functions)) {
435 0 : zend_hash_destroy(&self->not_filtered_functions);
436 0 : zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0);
437 : }
438 :
439 4 : for (i = 0; i < mode_len; i++) {
440 3 : switch (mode[i]) {
441 : case 'O':
442 : case 'A':
443 1 : self->flags |= MYSQLND_DEBUG_FLUSH;
444 : case 'a':
445 : case 'o':
446 1 : if (mode[i] == 'a' || mode[i] == 'A') {
447 0 : self->flags |= MYSQLND_DEBUG_APPEND;
448 : }
449 2 : if (i + 1 < mode_len && mode[i+1] == ',') {
450 1 : unsigned int j = i + 2;
451 30 : while (j < mode_len) {
452 28 : if (mode[j] == ':') {
453 0 : break;
454 : }
455 28 : j++;
456 : }
457 1 : if (j > i + 2) {
458 1 : self->file_name = estrndup(mode + i + 2, j - i - 2);
459 : }
460 1 : i = j;
461 : } else {
462 0 : if (!self->file_name)
463 0 : self->file_name = (char *) mysqlnd_debug_default_trace_file;
464 : }
465 1 : state = PARSER_WAIT_COLON;
466 1 : break;
467 : case ':':
468 : #if 0
469 : if (state != PARSER_WAIT_COLON) {
470 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Consecutive semicolons at position %u", i);
471 : }
472 : #endif
473 1 : state = PARSER_WAIT_MODIFIER;
474 1 : break;
475 : case 'f': /* limit output to these functions */
476 0 : if (i + 1 < mode_len && mode[i+1] == ',') {
477 0 : unsigned int j = i + 2;
478 0 : i++;
479 0 : while (j < mode_len) {
480 0 : if (mode[j] == ':') {
481 : /* function names with :: */
482 0 : if ((j + 1 < mode_len) && mode[j+1] == ':') {
483 0 : j += 2;
484 0 : continue;
485 : }
486 : }
487 0 : if (mode[j] == ',' || mode[j] == ':') {
488 0 : if (j > i + 2) {
489 : char func_name[1024];
490 0 : unsigned int func_name_len = MIN(sizeof(func_name) - 1, j - i - 1);
491 0 : memcpy(func_name, mode + i + 1, func_name_len);
492 0 : func_name[func_name_len] = '\0';
493 :
494 0 : zend_hash_add_empty_element(&self->not_filtered_functions,
495 : func_name, func_name_len + 1);
496 0 : i = j;
497 : }
498 0 : if (mode[j] == ':') {
499 0 : break;
500 : }
501 : }
502 0 : j++;
503 : }
504 0 : i = j;
505 : } else {
506 : #if 0
507 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
508 : "Expected list of functions for '%c' found none", mode[i]);
509 : #endif
510 : }
511 0 : state = PARSER_WAIT_COLON;
512 0 : break;
513 : case 'D':
514 : case 'd':
515 : case 'g':
516 : case 'p':
517 : /* unsupported */
518 0 : if ((i + 1) < mode_len && mode[i+1] == ',') {
519 0 : i+= 2;
520 0 : while (i < mode_len) {
521 0 : if (mode[i] == ':') {
522 0 : break;
523 : }
524 0 : i++;
525 : }
526 : }
527 0 : state = PARSER_WAIT_COLON;
528 0 : break;
529 : case 'F':
530 0 : self->flags |= MYSQLND_DEBUG_DUMP_FILE;
531 0 : state = PARSER_WAIT_COLON;
532 0 : break;
533 : case 'i':
534 0 : self->flags |= MYSQLND_DEBUG_DUMP_PID;
535 0 : state = PARSER_WAIT_COLON;
536 0 : break;
537 : case 'L':
538 0 : self->flags |= MYSQLND_DEBUG_DUMP_LINE;
539 0 : state = PARSER_WAIT_COLON;
540 0 : break;
541 : case 'n':
542 0 : self->flags |= MYSQLND_DEBUG_DUMP_LEVEL;
543 0 : state = PARSER_WAIT_COLON;
544 0 : break;
545 : case 't':
546 1 : if (mode[i+1] == ',') {
547 0 : unsigned int j = i + 2;
548 0 : while (j < mode_len) {
549 0 : if (mode[j] == ':') {
550 0 : break;
551 : }
552 0 : j++;
553 : }
554 0 : if (j > i + 2) {
555 0 : char *value_str = estrndup(mode + i + 2, j - i - 2);
556 0 : self->nest_level_limit = atoi(value_str);
557 0 : efree(value_str);
558 : }
559 0 : i = j;
560 : } else {
561 1 : self->nest_level_limit = 200; /* default value for FF DBUG */
562 : }
563 1 : self->flags |= MYSQLND_DEBUG_DUMP_TRACE;
564 1 : state = PARSER_WAIT_COLON;
565 1 : break;
566 : case 'T':
567 0 : self->flags |= MYSQLND_DEBUG_DUMP_TIME;
568 0 : state = PARSER_WAIT_COLON;
569 0 : break;
570 : case 'N':
571 : case 'P':
572 : case 'r':
573 : case 'S':
574 0 : state = PARSER_WAIT_COLON;
575 0 : break;
576 : case 'm': /* mysqlnd extension - trace memory functions */
577 0 : self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS;
578 0 : state = PARSER_WAIT_COLON;
579 0 : break;
580 : default:
581 0 : if (state == PARSER_WAIT_MODIFIER) {
582 : #if 0
583 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized format '%c'", mode[i]);
584 : #endif
585 0 : if (i+1 < mode_len && mode[i+1] == ',') {
586 0 : i+= 2;
587 0 : while (i < mode_len) {
588 0 : if (mode[i] == ':') {
589 0 : break;
590 : }
591 0 : i++;
592 : }
593 : }
594 0 : state = PARSER_WAIT_COLON;
595 : } else if (state == PARSER_WAIT_COLON) {
596 : #if 0
597 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]);
598 : #endif
599 : }
600 : break;
601 : }
602 : }
603 1 : }
604 : /* }}} */
605 :
606 :
607 : MYSQLND_CLASS_METHODS_START(mysqlnd_debug)
608 : MYSQLND_METHOD(mysqlnd_debug, open),
609 : MYSQLND_METHOD(mysqlnd_debug, set_mode),
610 : MYSQLND_METHOD(mysqlnd_debug, log),
611 : MYSQLND_METHOD(mysqlnd_debug, log_va),
612 : MYSQLND_METHOD(mysqlnd_debug, func_enter),
613 : MYSQLND_METHOD(mysqlnd_debug, func_leave),
614 : MYSQLND_METHOD(mysqlnd_debug, close),
615 : MYSQLND_METHOD(mysqlnd_debug, free),
616 : MYSQLND_CLASS_METHODS_END;
617 :
618 :
619 :
620 : /* {{{ mysqlnd_debug_init */
621 : PHPAPI MYSQLND_DEBUG *mysqlnd_debug_init(TSRMLS_D)
622 1 : {
623 1 : MYSQLND_DEBUG *ret = ecalloc(1, sizeof(MYSQLND_DEBUG));
624 : #ifdef ZTS
625 : ret->TSRMLS_C = TSRMLS_C;
626 : #endif
627 1 : ret->nest_level_limit = 0;
628 1 : ret->pid = getpid();
629 1 : zend_stack_init(&ret->call_stack);
630 1 : zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0);
631 :
632 1 : ret->m = & mysqlnd_mysqlnd_debug_methods;
633 :
634 1 : return ret;
635 : }
636 : /* }}} */
637 :
638 :
639 : /* {{{ _mysqlnd_debug */
640 : PHPAPI void _mysqlnd_debug(const char *mode TSRMLS_DC)
641 0 : {
642 : #ifdef PHP_DEBUG
643 0 : MYSQLND_DEBUG *dbg = MYSQLND_G(dbg);
644 0 : if (!dbg) {
645 0 : MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(TSRMLS_C);
646 0 : if (!dbg) {
647 0 : return;
648 : }
649 : }
650 :
651 0 : dbg->m->close(dbg);
652 0 : dbg->m->set_mode(dbg, mode);
653 0 : while (zend_stack_count(&dbg->call_stack)) {
654 0 : zend_stack_del_top(&dbg->call_stack);
655 : }
656 : #endif
657 : }
658 : /* }}} */
659 :
660 :
661 : #if ZEND_DEBUG
662 : #else
663 : #define __zend_filename "/unknown/unknown"
664 : #define __zend_lineno 0
665 : #endif
666 :
667 :
668 : /* {{{ _mysqlnd_emalloc */
669 : void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
670 27242 : {
671 : void *ret;
672 27242 : DBG_ENTER(mysqlnd_emalloc_name);
673 : #ifdef MYSQLND_THREADED
674 : if (MYSQLND_G(thread_id) != tsrm_thread_id()) {
675 : DBG_RETURN(_mysqlnd_pemalloc(size, 1 TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC));
676 : }
677 : #endif
678 :
679 27242 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
680 27242 : DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
681 27242 : ret = emalloc(size);
682 27242 : DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
683 27242 : DBG_INF_FMT("size=%lu ptr=%p", size, ret);
684 :
685 27242 : if (MYSQLND_G(collect_memory_statistics)) {
686 69 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMMOUNT, size);
687 : }
688 27242 : DBG_RETURN(ret);
689 : }
690 : /* }}} */
691 :
692 :
693 : /* {{{ _mysqlnd_pemalloc */
694 : void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
695 15036 : {
696 : void *ret;
697 15036 : DBG_ENTER(mysqlnd_pemalloc_name);
698 15036 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
699 15036 : if (persistent == FALSE) {
700 13065 : DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC));
701 : }
702 :
703 15036 : ret = pemalloc(size, persistent);
704 15036 : DBG_INF_FMT("size=%lu ptr=%p persistent=%d", size, ret, persistent);
705 :
706 15036 : if (persistent == FALSE) {
707 13065 : DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC));
708 : }
709 :
710 15036 : if (MYSQLND_G(collect_memory_statistics)) {
711 58 : enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT;
712 58 : enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMMOUNT:STAT_MEM_EMALLOC_AMMOUNT;
713 58 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
714 : }
715 :
716 15036 : DBG_RETURN(ret);
717 : }
718 : /* }}} */
719 :
720 :
721 : /* {{{ _mysqlnd_ecalloc */
722 : void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
723 35942 : {
724 : void *ret;
725 35942 : DBG_ENTER(mysqlnd_ecalloc_name);
726 : #ifdef MYSQLND_THREADED
727 : if (MYSQLND_G(thread_id) != tsrm_thread_id()) {
728 : DBG_RETURN(_mysqlnd_pecalloc(nmemb, size, 1 TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC));
729 : }
730 : #endif
731 35942 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
732 35942 : DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
733 :
734 35942 : ret = ecalloc(nmemb, size);
735 :
736 35942 : DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
737 35942 : DBG_INF_FMT("size=%lu ptr=%p", size, ret);
738 35942 : if (MYSQLND_G(collect_memory_statistics)) {
739 136 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMMOUNT, size);
740 : }
741 35942 : DBG_RETURN(ret);
742 : }
743 : /* }}} */
744 :
745 :
746 : /* {{{ _mysqlnd_pecalloc */
747 : void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
748 15629 : {
749 : void *ret;
750 15629 : DBG_ENTER(mysqlnd_pecalloc_name);
751 15629 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
752 15629 : if (persistent == FALSE) {
753 14588 : DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC));
754 : }
755 :
756 15629 : ret = pecalloc(nmemb, size, persistent);
757 15629 : DBG_INF_FMT("size=%lu ptr=%p", size, ret);
758 :
759 15629 : if (persistent == FALSE) {
760 14588 : DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC));
761 : }
762 :
763 15629 : if (MYSQLND_G(collect_memory_statistics)) {
764 66 : enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT;
765 66 : enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMMOUNT:STAT_MEM_ECALLOC_AMMOUNT;
766 66 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
767 : }
768 :
769 15629 : DBG_RETURN(ret);
770 : }
771 : /* }}} */
772 :
773 :
774 : /* {{{ _mysqlnd_erealloc */
775 : void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
776 395 : {
777 : void *ret;
778 395 : DBG_ENTER(mysqlnd_erealloc_name);
779 : #ifdef MYSQLND_THREADED
780 : if (MYSQLND_G(thread_id) != tsrm_thread_id()) {
781 : DBG_RETURN(_mysqlnd_perealloc(ptr, new_size, 1 TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC));
782 : }
783 : #endif
784 395 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
785 395 : DBG_INF_FMT("ptr=%p new_size=%lu", ptr, new_size);
786 395 : DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
787 :
788 395 : ret = erealloc(ptr, new_size);
789 :
790 395 : DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
791 395 : DBG_INF_FMT("new_ptr=%p", ret);
792 395 : if (MYSQLND_G(collect_memory_statistics)) {
793 0 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMMOUNT, new_size);
794 : }
795 395 : DBG_RETURN(ret);
796 : }
797 : /* }}} */
798 :
799 :
800 : /* {{{ _mysqlnd_perealloc */
801 : void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
802 11477 : {
803 : void *ret;
804 11477 : DBG_ENTER(mysqlnd_perealloc_name);
805 11477 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
806 11477 : DBG_INF_FMT("ptr=%p new_size=%lu persist=%d", ptr, new_size, persistent);
807 11477 : if (persistent == FALSE) {
808 11468 : DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC));
809 : }
810 :
811 11477 : ret = perealloc(ptr, new_size, persistent);
812 :
813 11477 : DBG_INF_FMT("new_ptr=%p", ret);
814 :
815 11477 : if (persistent == FALSE) {
816 11468 : DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC));
817 : }
818 11477 : MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT);
819 11477 : if (MYSQLND_G(collect_memory_statistics)) {
820 42 : enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT;
821 42 : enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMMOUNT:STAT_MEM_EREALLOC_AMMOUNT;
822 42 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, new_size);
823 : }
824 11477 : DBG_RETURN(ret);
825 : }
826 : /* }}} */
827 :
828 :
829 : /* {{{ _mysqlnd_efree */
830 : void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
831 55730 : {
832 55730 : DBG_ENTER(mysqlnd_efree_name);
833 : #ifdef MYSQLND_THREADED
834 : if (MYSQLND_G(thread_id) != tsrm_thread_id()) {
835 : DBG_RETURN(_mysqlnd_pefree(ptr, 1 TSRMLS_CC ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC));
836 : }
837 : #endif
838 55730 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
839 55730 : DBG_INF_FMT("ptr=%p", ptr);
840 55730 : DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
841 55730 : MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_EFREE_COUNT);
842 :
843 55730 : efree(ptr);
844 :
845 55730 : DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
846 : DBG_VOID_RETURN;
847 : }
848 : /* }}} */
849 :
850 :
851 : /* {{{ _mysqlnd_pefree */
852 : void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
853 81693 : {
854 81693 : DBG_ENTER(mysqlnd_pefree_name);
855 81693 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
856 81693 : DBG_INF_FMT("ptr=%p persistent=%d", ptr, persistent);
857 81693 : if (persistent == FALSE) {
858 71666 : DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC));
859 : }
860 :
861 81693 : pefree(ptr, persistent);
862 :
863 81693 : if (persistent == FALSE) {
864 71666 : DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC));
865 : }
866 81693 : if (MYSQLND_G(collect_memory_statistics)) {
867 370 : MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT);
868 : }
869 : DBG_VOID_RETURN;
870 : }
871 :
872 :
873 : /* {{{ _mysqlnd_malloc */
874 : void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
875 40385 : {
876 : void *ret;
877 40385 : DBG_ENTER(mysqlnd_malloc_name);
878 40385 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
879 :
880 40385 : ret = malloc(size);
881 :
882 40385 : DBG_INF_FMT("size=%lu ptr=%p", size, ret);
883 40385 : if (MYSQLND_G(collect_memory_statistics)) {
884 53 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMMOUNT, size);
885 : }
886 40385 : DBG_RETURN(ret);
887 : }
888 : /* }}} */
889 :
890 :
891 : /* {{{ _mysqlnd_calloc */
892 : void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
893 266042 : {
894 : void *ret;
895 266042 : DBG_ENTER(mysqlnd_calloc_name);
896 266042 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
897 :
898 266042 : ret = calloc(nmemb, size);
899 :
900 266042 : DBG_INF_FMT("size=%lu ptr=%p", size, ret);
901 266042 : if (MYSQLND_G(collect_memory_statistics)) {
902 226 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMMOUNT, size);
903 : }
904 266042 : DBG_RETURN(ret);
905 : }
906 : /* }}} */
907 :
908 :
909 : /* {{{ _mysqlnd_realloc */
910 : void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
911 0 : {
912 : void *ret;
913 0 : DBG_ENTER(mysqlnd_realloc_name);
914 0 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
915 0 : DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr);
916 0 : DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC));
917 :
918 0 : ret = realloc(ptr, new_size);
919 :
920 0 : DBG_INF_FMT("new_ptr=%p", ret);
921 :
922 0 : if (MYSQLND_G(collect_memory_statistics)) {
923 0 : MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMMOUNT, new_size);
924 : }
925 0 : DBG_RETURN(ret);
926 : }
927 : /* }}} */
928 :
929 :
930 : /* {{{ _mysqlnd_free */
931 : void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
932 306475 : {
933 306475 : DBG_ENTER(mysqlnd_free_name);
934 306475 : DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
935 306475 : DBG_INF_FMT("ptr=%p", ptr);
936 :
937 306475 : free(ptr);
938 :
939 306475 : if (MYSQLND_G(collect_memory_statistics)) {
940 279 : MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_FREE_COUNT);
941 : }
942 : DBG_VOID_RETURN;
943 : }
944 : /* }}} */
945 :
946 :
947 :
948 :
949 : /* Follows code borrowed from zend_builtin_functions.c because the functions there are static */
950 :
951 : #if PHP_MAJOR_VERSION >= 6
952 : /* {{{ gettraceasstring() macros */
953 : #define TRACE_APPEND_CHR(chr) \
954 : *str = (char*)erealloc(*str, *len + 1 + 1); \
955 : (*str)[(*len)++] = chr
956 :
957 : #define TRACE_APPEND_STRL(val, vallen) \
958 : { \
959 : int l = vallen; \
960 : *str = (char*)erealloc(*str, *len + l + 1); \
961 : memcpy((*str) + *len, val, l); \
962 : *len += l; \
963 : }
964 :
965 : #define TRACE_APPEND_USTRL(val, vallen) \
966 : { \
967 : zval tmp, copy; \
968 : int use_copy; \
969 : ZVAL_UNICODEL(&tmp, val, vallen, 1); \
970 : zend_make_printable_zval(&tmp, ©, &use_copy); \
971 : TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
972 : zval_dtor(©); \
973 : zval_dtor(&tmp); \
974 : }
975 :
976 : #define TRACE_APPEND_ZVAL(zv) \
977 : if (Z_TYPE_P((zv)) == IS_UNICODE) { \
978 : zval copy; \
979 : int use_copy; \
980 : zend_make_printable_zval((zv), ©, &use_copy); \
981 : TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
982 : zval_dtor(©); \
983 : } else { \
984 : TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
985 : }
986 :
987 : #define TRACE_APPEND_STR(val) \
988 : TRACE_APPEND_STRL(val, sizeof(val)-1)
989 :
990 : #define TRACE_APPEND_KEY(key) \
991 : if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
992 : if (Z_TYPE_PP(tmp) == IS_UNICODE) { \
993 : zval copy; \
994 : int use_copy; \
995 : zend_make_printable_zval(*tmp, ©, &use_copy); \
996 : TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
997 : zval_dtor(©); \
998 : } else { \
999 : TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
1000 : } \
1001 : }
1002 : /* }}} */
1003 :
1004 : /* {{{ mysqlnd_build_trace_args */
1005 : static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
1006 : {
1007 : char **str;
1008 : int *len;
1009 :
1010 : str = va_arg(args, char**);
1011 : len = va_arg(args, int*);
1012 :
1013 : /* the trivial way would be to do:
1014 : * conver_to_string_ex(arg);
1015 : * append it and kill the now tmp arg.
1016 : * but that could cause some E_NOTICE and also damn long lines.
1017 : */
1018 :
1019 : switch (Z_TYPE_PP(arg)) {
1020 : case IS_NULL:
1021 : TRACE_APPEND_STR("NULL, ");
1022 : break;
1023 : case IS_STRING: {
1024 : int l_added;
1025 : TRACE_APPEND_CHR('\'');
1026 : if (Z_STRLEN_PP(arg) > 15) {
1027 : TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
1028 : TRACE_APPEND_STR("...', ");
1029 : l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
1030 : } else {
1031 : l_added = Z_STRLEN_PP(arg);
1032 : TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
1033 : TRACE_APPEND_STR("', ");
1034 : l_added += 3 + 1;
1035 : }
1036 : while (--l_added) {
1037 : if ((unsigned char)(*str)[*len - l_added] < 32) {
1038 : (*str)[*len - l_added] = '?';
1039 : }
1040 : }
1041 : break;
1042 : }
1043 : case IS_UNICODE: {
1044 : int l_added;
1045 :
1046 : /*
1047 : * We do not want to apply current error mode here, since
1048 : * zend_make_printable_zval() uses output encoding converter.
1049 : * Temporarily set output encoding converter to escape offending
1050 : * chars with \uXXXX notation.
1051 : */
1052 : zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA);
1053 : TRACE_APPEND_CHR('\'');
1054 : if (Z_USTRLEN_PP(arg) > 15) {
1055 : TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15);
1056 : TRACE_APPEND_STR("...', ");
1057 : l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
1058 : } else {
1059 : l_added = Z_USTRLEN_PP(arg);
1060 : TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added);
1061 : TRACE_APPEND_STR("', ");
1062 : l_added += 3 + 1;
1063 : }
1064 : /*
1065 : * Reset output encoding converter error mode.
1066 : */
1067 : zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode));
1068 : while (--l_added) {
1069 : if ((unsigned char)(*str)[*len - l_added] < 32) {
1070 : (*str)[*len - l_added] = '?';
1071 : }
1072 : }
1073 : break;
1074 : }
1075 : case IS_BOOL:
1076 : if (Z_LVAL_PP(arg)) {
1077 : TRACE_APPEND_STR("true, ");
1078 : } else {
1079 : TRACE_APPEND_STR("false, ");
1080 : }
1081 : break;
1082 : case IS_RESOURCE:
1083 : TRACE_APPEND_STR("Resource id #");
1084 : /* break; */
1085 : case IS_LONG: {
1086 : long lval = Z_LVAL_PP(arg);
1087 : char s_tmp[MAX_LENGTH_OF_LONG + 1];
1088 : int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
1089 : TRACE_APPEND_STRL(s_tmp, l_tmp);
1090 : TRACE_APPEND_STR(", ");
1091 : break;
1092 : }
1093 : case IS_DOUBLE: {
1094 : double dval = Z_DVAL_PP(arg);
1095 : char *s_tmp;
1096 : int l_tmp;
1097 :
1098 : s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
1099 : l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
1100 : TRACE_APPEND_STRL(s_tmp, l_tmp);
1101 : /* %G already handles removing trailing zeros from the fractional part, yay */
1102 : efree(s_tmp);
1103 : TRACE_APPEND_STR(", ");
1104 : break;
1105 : }
1106 : case IS_ARRAY:
1107 : TRACE_APPEND_STR("Array, ");
1108 : break;
1109 : case IS_OBJECT: {
1110 : zval tmp;
1111 : zstr class_name;
1112 : zend_uint class_name_len;
1113 : int dup;
1114 :
1115 : TRACE_APPEND_STR("Object(");
1116 :
1117 : dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
1118 :
1119 : ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1);
1120 : convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv)));
1121 : TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp));
1122 : zval_dtor(&tmp);
1123 :
1124 : if(!dup) {
1125 : efree(class_name.v);
1126 : }
1127 :
1128 : TRACE_APPEND_STR("), ");
1129 : break;
1130 : }
1131 : default:
1132 : break;
1133 : }
1134 : return ZEND_HASH_APPLY_KEEP;
1135 : }
1136 : /* }}} */
1137 :
1138 :
1139 : static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
1140 : {
1141 : char *s_tmp, **str;
1142 : int *len, *num;
1143 : long line;
1144 : HashTable *ht = Z_ARRVAL_PP(frame);
1145 : zval **file, **tmp;
1146 :
1147 : str = va_arg(args, char**);
1148 : len = va_arg(args, int*);
1149 : num = va_arg(args, int*);
1150 :
1151 : s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
1152 : sprintf(s_tmp, "#%d ", (*num)++);
1153 : TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1154 : efree(s_tmp);
1155 : if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
1156 : if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
1157 : line = Z_LVAL_PP(tmp);
1158 : } else {
1159 : line = 0;
1160 : }
1161 : TRACE_APPEND_ZVAL(*file);
1162 : s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
1163 : sprintf(s_tmp, "(%ld): ", line);
1164 : TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1165 : efree(s_tmp);
1166 : } else {
1167 : TRACE_APPEND_STR("[internal function]: ");
1168 : }
1169 : TRACE_APPEND_KEY("class");
1170 : TRACE_APPEND_KEY("type");
1171 : TRACE_APPEND_KEY("function");
1172 : TRACE_APPEND_CHR('(');
1173 : if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
1174 : int last_len = *len;
1175 : zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
1176 : if (last_len != *len) {
1177 : *len -= 2; /* remove last ', ' */
1178 : }
1179 : }
1180 : TRACE_APPEND_STR(")\n");
1181 : return ZEND_HASH_APPLY_KEEP;
1182 : }
1183 : /* }}} */
1184 :
1185 :
1186 : #else /* PHP 5*/
1187 :
1188 :
1189 : /* {{{ gettraceasstring() macros */
1190 : #define TRACE_APPEND_CHR(chr) \
1191 : *str = (char*)erealloc(*str, *len + 1 + 1); \
1192 : (*str)[(*len)++] = chr
1193 :
1194 : #define TRACE_APPEND_STRL(val, vallen) \
1195 : { \
1196 : int l = vallen; \
1197 : *str = (char*)erealloc(*str, *len + l + 1); \
1198 : memcpy((*str) + *len, val, l); \
1199 : *len += l; \
1200 : }
1201 :
1202 : #define TRACE_APPEND_STR(val) \
1203 : TRACE_APPEND_STRL(val, sizeof(val)-1)
1204 :
1205 : #define TRACE_APPEND_KEY(key) \
1206 : if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
1207 : TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
1208 : }
1209 :
1210 : /* }}} */
1211 :
1212 :
1213 : static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
1214 0 : {
1215 : char **str;
1216 : int *len;
1217 :
1218 0 : str = va_arg(args, char**);
1219 0 : len = va_arg(args, int*);
1220 :
1221 : /* the trivial way would be to do:
1222 : * conver_to_string_ex(arg);
1223 : * append it and kill the now tmp arg.
1224 : * but that could cause some E_NOTICE and also damn long lines.
1225 : */
1226 :
1227 0 : switch (Z_TYPE_PP(arg)) {
1228 : case IS_NULL:
1229 0 : TRACE_APPEND_STR("NULL, ");
1230 0 : break;
1231 : case IS_STRING: {
1232 : int l_added;
1233 0 : TRACE_APPEND_CHR('\'');
1234 0 : if (Z_STRLEN_PP(arg) > 15) {
1235 0 : TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
1236 0 : TRACE_APPEND_STR("...', ");
1237 0 : l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
1238 : } else {
1239 0 : l_added = Z_STRLEN_PP(arg);
1240 0 : TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
1241 0 : TRACE_APPEND_STR("', ");
1242 0 : l_added += 3 + 1;
1243 : }
1244 0 : while (--l_added) {
1245 0 : if ((*str)[*len - l_added] < 32) {
1246 0 : (*str)[*len - l_added] = '?';
1247 : }
1248 : }
1249 0 : break;
1250 : }
1251 : case IS_BOOL:
1252 0 : if (Z_LVAL_PP(arg)) {
1253 0 : TRACE_APPEND_STR("true, ");
1254 : } else {
1255 0 : TRACE_APPEND_STR("false, ");
1256 : }
1257 0 : break;
1258 : case IS_RESOURCE:
1259 0 : TRACE_APPEND_STR("Resource id #");
1260 : /* break; */
1261 : case IS_LONG: {
1262 0 : long lval = Z_LVAL_PP(arg);
1263 : char s_tmp[MAX_LENGTH_OF_LONG + 1];
1264 0 : int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
1265 0 : TRACE_APPEND_STRL(s_tmp, l_tmp);
1266 0 : TRACE_APPEND_STR(", ");
1267 0 : break;
1268 : }
1269 : case IS_DOUBLE: {
1270 0 : double dval = Z_DVAL_PP(arg);
1271 : char *s_tmp;
1272 : int l_tmp;
1273 :
1274 0 : s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
1275 0 : l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
1276 0 : TRACE_APPEND_STRL(s_tmp, l_tmp);
1277 : /* %G already handles removing trailing zeros from the fractional part, yay */
1278 0 : efree(s_tmp);
1279 0 : TRACE_APPEND_STR(", ");
1280 0 : break;
1281 : }
1282 : case IS_ARRAY:
1283 0 : TRACE_APPEND_STR("Array, ");
1284 0 : break;
1285 : case IS_OBJECT: {
1286 : char *class_name;
1287 : zend_uint class_name_len;
1288 : int dup;
1289 :
1290 0 : TRACE_APPEND_STR("Object(");
1291 :
1292 0 : dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
1293 :
1294 0 : TRACE_APPEND_STRL(class_name, class_name_len);
1295 0 : if(!dup) {
1296 0 : efree(class_name);
1297 : }
1298 :
1299 0 : TRACE_APPEND_STR("), ");
1300 : break;
1301 : }
1302 : default:
1303 : break;
1304 : }
1305 0 : return ZEND_HASH_APPLY_KEEP;
1306 : }
1307 : /* }}} */
1308 :
1309 : static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
1310 0 : {
1311 : char *s_tmp, **str;
1312 : int *len, *num;
1313 : long line;
1314 0 : HashTable *ht = Z_ARRVAL_PP(frame);
1315 : zval **file, **tmp;
1316 :
1317 0 : str = va_arg(args, char**);
1318 0 : len = va_arg(args, int*);
1319 0 : num = va_arg(args, int*);
1320 :
1321 0 : s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
1322 0 : sprintf(s_tmp, "#%d ", (*num)++);
1323 0 : TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1324 0 : efree(s_tmp);
1325 0 : if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
1326 0 : if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
1327 0 : line = Z_LVAL_PP(tmp);
1328 : } else {
1329 0 : line = 0;
1330 : }
1331 0 : s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
1332 0 : sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
1333 0 : TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1334 0 : efree(s_tmp);
1335 : } else {
1336 0 : TRACE_APPEND_STR("[internal function]: ");
1337 : }
1338 0 : TRACE_APPEND_KEY("class");
1339 0 : TRACE_APPEND_KEY("type");
1340 0 : TRACE_APPEND_KEY("function");
1341 0 : TRACE_APPEND_CHR('(');
1342 0 : if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
1343 0 : int last_len = *len;
1344 0 : zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
1345 0 : if (last_len != *len) {
1346 0 : *len -= 2; /* remove last ', ' */
1347 : }
1348 : }
1349 0 : TRACE_APPEND_STR(")\n");
1350 0 : return ZEND_HASH_APPLY_KEEP;
1351 : }
1352 : /* }}} */
1353 : #endif
1354 :
1355 :
1356 : char * mysqlnd_get_backtrace(TSRMLS_D)
1357 0 : {
1358 : zval *trace;
1359 0 : char *res = estrdup(""), **str = &res, *s_tmp;
1360 0 : int res_len = 0, *len = &res_len, num = 0;
1361 :
1362 0 : MAKE_STD_ZVAL(trace);
1363 0 : zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC);
1364 :
1365 0 : zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_string, 3, str, len, &num);
1366 0 : zval_ptr_dtor(&trace);
1367 :
1368 0 : s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
1369 0 : sprintf(s_tmp, "#%d {main}", num);
1370 0 : TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
1371 0 : efree(s_tmp);
1372 :
1373 0 : res[res_len] = '\0';
1374 :
1375 0 : return res;
1376 : }
1377 :
1378 : /*
1379 : * Local variables:
1380 : * tab-width: 4
1381 : * c-basic-offset: 4
1382 : * End:
1383 : * vim600: noet sw=4 ts=4 fdm=marker
1384 : * vim<600: noet sw=4 ts=4
1385 : */
|