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