1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2013 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$ */
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
MYSQLND_METHOD(mysqlnd_debug,open)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
MYSQLND_METHOD(mysqlnd_debug,log)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
MYSQLND_METHOD(mysqlnd_debug,log_va)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
MYSQLND_METHOD(mysqlnd_debug,func_enter)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
MYSQLND_METHOD(mysqlnd_debug,func_leave)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
MYSQLND_METHOD(mysqlnd_debug,close)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
MYSQLND_METHOD(mysqlnd_debug,free)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
MYSQLND_METHOD(mysqlnd_debug,set_mode)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 *
mysqlnd_debug_init(const char * skip_functions[]TSRMLS_DC)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 */
_mysqlnd_debug(const char * mode TSRMLS_DC)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 */
_mysqlnd_emalloc(size_t size MYSQLND_MEM_D)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 */
_mysqlnd_pemalloc(size_t size,zend_bool persistent MYSQLND_MEM_D)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 */
_mysqlnd_ecalloc(unsigned int nmemb,size_t size MYSQLND_MEM_D)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 */
_mysqlnd_pecalloc(unsigned int nmemb,size_t size,zend_bool persistent MYSQLND_MEM_D)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 */
_mysqlnd_erealloc(void * ptr,size_t new_size MYSQLND_MEM_D)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 */
_mysqlnd_perealloc(void * ptr,size_t new_size,zend_bool persistent MYSQLND_MEM_D)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 */
_mysqlnd_efree(void * ptr MYSQLND_MEM_D)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 */
_mysqlnd_pefree(void * ptr,zend_bool persistent MYSQLND_MEM_D)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 */
_mysqlnd_malloc(size_t size MYSQLND_MEM_D)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 */
_mysqlnd_calloc(unsigned int nmemb,size_t size MYSQLND_MEM_D)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 */
_mysqlnd_realloc(void * ptr,size_t new_size MYSQLND_MEM_D)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 */
_mysqlnd_free(void * ptr MYSQLND_MEM_D)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 */
_mysqlnd_pestrndup(const char * const ptr,size_t length,zend_bool persistent MYSQLND_MEM_D)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 */
_mysqlnd_pestrdup(const char * const ptr,zend_bool persistent MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_pemalloc(size_t size,zend_bool persistent MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_ecalloc(unsigned int nmemb,size_t size MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_pecalloc(unsigned int nmemb,size_t size,zend_bool persistent MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_erealloc(void * ptr,size_t new_size MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_perealloc(void * ptr,size_t new_size,zend_bool persistent MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_pefree(void * ptr,zend_bool persistent MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_malloc(size_t size MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_calloc(unsigned int nmemb,size_t size MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_realloc(void * ptr,size_t new_size MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_free(void * ptr MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_pestrndup(const char * const ptr,size_t length,zend_bool persistent MYSQLND_MEM_D)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 */
mysqlnd_zend_mm_pestrdup(const char * const ptr,zend_bool persistent MYSQLND_MEM_D)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 */
mysqlnd_build_trace_args(zval ** arg TSRMLS_DC,int num_args,va_list args,zend_hash_key * hash_key)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
mysqlnd_build_trace_string(zval ** frame TSRMLS_DC,int num_args,va_list args,zend_hash_key * hash_key)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
mysqlnd_build_trace_args(zval ** arg TSRMLS_DC,int num_args,va_list args,zend_hash_key * hash_key)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
mysqlnd_build_trace_string(zval ** frame TSRMLS_DC,int num_args,va_list args,zend_hash_key * hash_key)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
mysqlnd_get_backtrace(uint max_levels,size_t * length TSRMLS_DC)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 */
1867