Annotation of embedaddon/php/ext/mysqlnd/mysqlnd_debug.c, revision 1.1.1.4

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>