Back to index

php5  5.3.10
mysqlnd_debug.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2006-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Georg Richter <georg@mysql.com>                             |
00016   |          Andrey Hristov <andrey@mysql.com>                           |
00017   |          Ulf Wendel <uwendel@mysql.com>                              |
00018   +----------------------------------------------------------------------+
00019 */
00020 
00021 /* $Id: mysqlnd_debug.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 
00023 #include "php.h"
00024 #include "mysqlnd.h"
00025 #include "mysqlnd_priv.h"
00026 #include "mysqlnd_debug.h"
00027 #include "mysqlnd_wireprotocol.h"
00028 #include "mysqlnd_statistics.h"
00029 #include "zend_builtin_functions.h"
00030 
00031 static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace";
00032 
00033 #ifdef ZTS 
00034 #define MYSQLND_ZTS(self) TSRMLS_D = (self)->TSRMLS_C
00035 #else
00036 #define MYSQLND_ZTS(self)
00037 #endif
00038 
00039 static const char mysqlnd_emalloc_name[]  = "_mysqlnd_emalloc";
00040 static const char mysqlnd_pemalloc_name[] = "_mysqlnd_pemalloc";
00041 static const char mysqlnd_ecalloc_name[]  = "_mysqlnd_ecalloc";
00042 static const char mysqlnd_pecalloc_name[] = "_mysqlnd_pecalloc";
00043 static const char mysqlnd_erealloc_name[] = "_mysqlnd_erealloc";
00044 static const char mysqlnd_perealloc_name[]       = "_mysqlnd_perealloc";
00045 static const char mysqlnd_efree_name[]           = "_mysqlnd_efree";
00046 static const char mysqlnd_pefree_name[]          = "_mysqlnd_pefree";
00047 static const char mysqlnd_malloc_name[]          = "_mysqlnd_malloc";
00048 static const char mysqlnd_calloc_name[]          = "_mysqlnd_calloc";
00049 static const char mysqlnd_realloc_name[]  = "_mysqlnd_realloc";
00050 static const char mysqlnd_free_name[]            = "_mysqlnd_free";
00051 static const char mysqlnd_pestrndup_name[]       = "_mysqlnd_pestrndup";
00052 static const char mysqlnd_pestrdup_name[] = "_mysqlnd_pestrdup";
00053 
00054 const char * mysqlnd_debug_std_no_trace_funcs[] =
00055 {
00056        mysqlnd_emalloc_name,
00057        mysqlnd_ecalloc_name,
00058        mysqlnd_efree_name,
00059        mysqlnd_erealloc_name,
00060        mysqlnd_pemalloc_name,
00061        mysqlnd_pecalloc_name,
00062        mysqlnd_pefree_name,
00063        mysqlnd_perealloc_name,
00064        mysqlnd_malloc_name,
00065        mysqlnd_calloc_name,
00066        mysqlnd_realloc_name,
00067        mysqlnd_free_name,
00068        mysqlnd_pestrndup_name,
00069        mysqlnd_read_header_name,
00070        mysqlnd_read_body_name,
00071        NULL /* must be always last */
00072 };
00073 
00074 
00075 /* {{{ mysqlnd_debug::open */
00076 static enum_func_status
00077 MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen)
00078 {
00079        MYSQLND_ZTS(self);
00080 
00081        if (!self->file_name) {
00082               return FAIL;
00083        }
00084 
00085        self->stream = php_stream_open_wrapper(self->file_name,
00086                                                                          reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb",
00087                                                                          REPORT_ERRORS, NULL);
00088        return self->stream? PASS:FAIL;
00089 }
00090 /* }}} */
00091 
00092 
00093 /* {{{ mysqlnd_debug::log */
00094 static enum_func_status
00095 MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self,
00096                                                            unsigned int line, const char * const file,
00097                                                            unsigned int level, const char * type, const char * message)
00098 {
00099        char pipe_buffer[512];
00100        enum_func_status ret;
00101        int i;
00102        char * message_line;
00103        unsigned int message_line_len;
00104        unsigned int flags = self->flags;
00105        char pid_buffer[10], time_buffer[30], file_buffer[200],
00106                line_buffer[6], level_buffer[7];
00107        MYSQLND_ZTS(self);
00108 
00109        if (!self->stream && FAIL == self->m->open(self, FALSE)) {
00110               return FAIL;
00111        }
00112 
00113        if (level == -1) {
00114               level = zend_stack_count(&self->call_stack);
00115        }
00116        i = MIN(level, sizeof(pipe_buffer) / 2  - 1);
00117        pipe_buffer[i*2] = '\0';
00118        for (;i > 0;i--) {
00119               pipe_buffer[i*2 - 1] = ' ';
00120               pipe_buffer[i*2 - 2] = '|';
00121        }
00122 
00123 
00124        if (flags & MYSQLND_DEBUG_DUMP_PID) {
00125               snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
00126               pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
00127        }
00128        if (flags & MYSQLND_DEBUG_DUMP_TIME) {
00129               /* The following from FF's DBUG library, which is in the public domain */
00130 #if defined(PHP_WIN32)
00131               /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
00132               in system ticks, 10 ms intervals. See my_getsystime.c for high res */
00133               SYSTEMTIME loc_t;
00134               GetLocalTime(&loc_t);
00135               snprintf(time_buffer, sizeof(time_buffer) - 1,
00136                              /* "%04d-%02d-%02d " */
00137                              "%02d:%02d:%02d.%06d ",
00138                              /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
00139                              loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
00140               time_buffer[sizeof(time_buffer) - 1 ] = '\0';
00141 #else
00142               struct timeval tv;
00143               struct tm *tm_p;
00144               if (gettimeofday(&tv, NULL) != -1) {
00145                      if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
00146                             snprintf(time_buffer, sizeof(time_buffer) - 1,
00147                                            /* "%04d-%02d-%02d " */
00148                                            "%02d:%02d:%02d.%06d ",
00149                                            /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
00150                                            tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
00151                                            (int) (tv.tv_usec));
00152                             time_buffer[sizeof(time_buffer) - 1 ] = '\0';
00153                      }
00154               }
00155 #endif
00156        }
00157        if (flags & MYSQLND_DEBUG_DUMP_FILE) {
00158               snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
00159               file_buffer[sizeof(file_buffer) - 1 ] = '\0';
00160        }
00161        if (flags & MYSQLND_DEBUG_DUMP_LINE) {
00162               snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
00163               line_buffer[sizeof(line_buffer) - 1 ] = '\0';
00164        }
00165        if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
00166               snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
00167               level_buffer[sizeof(level_buffer) - 1 ] = '\0';
00168        }
00169 
00170        message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
00171                                                         flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
00172                                                         flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
00173                                                         flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
00174                                                         flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
00175                                                         flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
00176                                                         pipe_buffer, type? type:"", message);
00177 
00178        ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
00179        efree(message_line); /* allocated by spprintf */
00180        if (flags & MYSQLND_DEBUG_FLUSH) {
00181               self->m->close(self);
00182               self->m->open(self, TRUE);
00183        }
00184        return ret;
00185 }
00186 /* }}} */
00187 
00188 
00189 /* {{{ mysqlnd_debug::log_va */
00190 static enum_func_status
00191 MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self,
00192                                                                  unsigned int line, const char * const file,
00193                                                                  unsigned int level, const char * type,
00194                                                                  const char *format, ...)
00195 {
00196        char pipe_buffer[512];
00197        int i;
00198        enum_func_status ret;
00199        char * message_line, *buffer;
00200        unsigned int message_line_len;
00201        va_list args;
00202        unsigned int flags = self->flags;
00203        char pid_buffer[10], time_buffer[30], file_buffer[200],
00204                line_buffer[6], level_buffer[7];
00205        MYSQLND_ZTS(self);
00206 
00207        if (!self->stream && FAIL == self->m->open(self, FALSE)) {
00208               return FAIL;
00209        }
00210 
00211        if (level == -1) {
00212               level = zend_stack_count(&self->call_stack);
00213        }
00214        i = MIN(level, sizeof(pipe_buffer) / 2  - 1);
00215        pipe_buffer[i*2] = '\0';
00216        for (;i > 0;i--) {
00217               pipe_buffer[i*2 - 1] = ' ';
00218               pipe_buffer[i*2 - 2] = '|';
00219        }
00220 
00221 
00222        if (flags & MYSQLND_DEBUG_DUMP_PID) {
00223               snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
00224               pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
00225        }
00226        if (flags & MYSQLND_DEBUG_DUMP_TIME) {
00227               /* The following from FF's DBUG library, which is in the public domain */
00228 #if defined(PHP_WIN32)
00229               /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
00230               in system ticks, 10 ms intervals. See my_getsystime.c for high res */
00231               SYSTEMTIME loc_t;
00232               GetLocalTime(&loc_t);
00233               snprintf(time_buffer, sizeof(time_buffer) - 1,
00234                              /* "%04d-%02d-%02d " */
00235                              "%02d:%02d:%02d.%06d ",
00236                              /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
00237                              loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
00238               time_buffer[sizeof(time_buffer) - 1 ] = '\0';
00239 #else
00240               struct timeval tv;
00241               struct tm *tm_p;
00242               if (gettimeofday(&tv, NULL) != -1) {
00243                      if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
00244                             snprintf(time_buffer, sizeof(time_buffer) - 1,
00245                                            /* "%04d-%02d-%02d " */
00246                                            "%02d:%02d:%02d.%06d ",
00247                                            /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
00248                                            tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
00249                                            (int) (tv.tv_usec));
00250                             time_buffer[sizeof(time_buffer) - 1 ] = '\0';
00251                      }
00252               }
00253 #endif
00254        }
00255        if (flags & MYSQLND_DEBUG_DUMP_FILE) {
00256               snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
00257               file_buffer[sizeof(file_buffer) - 1 ] = '\0';
00258        }
00259        if (flags & MYSQLND_DEBUG_DUMP_LINE) {
00260               snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
00261               line_buffer[sizeof(line_buffer) - 1 ] = '\0';
00262        }
00263        if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
00264               snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
00265               level_buffer[sizeof(level_buffer) - 1 ] = '\0';
00266        }
00267 
00268 
00269        va_start(args, format);
00270        vspprintf(&buffer, 0, format, args);
00271        va_end(args);
00272 
00273        message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
00274                                                         flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
00275                                                         flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
00276                                                         flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
00277                                                         flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
00278                                                         flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
00279                                                         pipe_buffer, type? type:"", buffer);
00280        efree(buffer);
00281        ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
00282        efree(message_line); /* allocated by spprintf */
00283 
00284        if (flags & MYSQLND_DEBUG_FLUSH) {
00285               self->m->close(self);
00286               self->m->open(self, TRUE);
00287        }
00288        return ret;
00289 }
00290 /* }}} */
00291 
00292 
00293 /* FALSE - The DBG_ calls won't be traced, TRUE - will be traced */
00294 /* {{{ mysqlnd_debug::func_enter */
00295 static zend_bool
00296 MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self,
00297                                                                         unsigned int line, const char * const file,
00298                                                                         const char * const func_name, unsigned int func_name_len)
00299 {
00300        if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
00301               return FALSE;
00302        }
00303        if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
00304               return FALSE;
00305        }
00306 
00307        if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 && self->skip_functions) {
00308               const char ** p = self->skip_functions;
00309               while (*p) {
00310                      if (*p == func_name) {
00311                             zend_stack_push(&self->call_stack, "", sizeof(""));
00312 #ifndef MYSQLND_PROFILING_DISABLED
00313                             if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
00314                                    uint64_t some_time = 0;
00315                                    zend_stack_push(&self->call_time_stack, &some_time, sizeof(some_time));
00316                             }
00317 #endif
00318                             return FALSE;
00319                      }
00320                      p++;
00321               }
00322        }
00323 
00324        zend_stack_push(&self->call_stack, func_name, func_name_len + 1);
00325 #ifndef MYSQLND_PROFILING_DISABLED
00326        if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
00327               uint64_t some_time = 0;
00328               zend_stack_push(&self->call_time_stack, &some_time, sizeof(some_time));
00329        }
00330 #endif
00331 
00332        if (zend_hash_num_elements(&self->not_filtered_functions) &&
00333               0 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
00334        {
00335               return FALSE;
00336        }
00337 
00338        self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name);
00339        return TRUE;
00340 }
00341 /* }}} */
00342 
00343 #ifndef MYSQLND_PROFILING_DISABLED
00344 struct st_mysqlnd_dbg_function_profile {
00345        uint64_t calls;
00346        uint64_t min_own;
00347        uint64_t max_own;
00348        uint64_t avg_own;
00349        uint64_t own_underporm_calls;
00350        uint64_t min_in_calls;
00351        uint64_t max_in_calls;
00352        uint64_t avg_in_calls;
00353        uint64_t in_calls_underporm_calls;
00354        uint64_t min_total;
00355        uint64_t max_total;
00356        uint64_t avg_total;  
00357        uint64_t total_underporm_calls;
00358 };
00359 #define PROFILE_UNDERPERFORM_THRESHOLD 10
00360 #endif
00361 
00362 /* {{{ mysqlnd_debug::func_leave */
00363 static enum_func_status
00364 MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, const char * const file, uint64_t call_time)
00365 {
00366        char *func_name;
00367        uint64_t * parent_non_own_time_ptr = NULL, * mine_non_own_time_ptr = NULL;
00368        uint64_t mine_non_own_time = 0;
00369        zend_bool profile_calls = self->flags & MYSQLND_DEBUG_PROFILE_CALLS? TRUE:FALSE;
00370 
00371        if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
00372               return PASS;
00373        }
00374        if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
00375               return PASS;
00376        }
00377 
00378        zend_stack_top(&self->call_stack, (void **)&func_name);
00379 
00380 #ifndef MYSQLND_PROFILING_DISABLED
00381        if (profile_calls) {
00382               zend_stack_top(&self->call_time_stack, (void **)&mine_non_own_time_ptr);
00383               mine_non_own_time = *mine_non_own_time_ptr;
00384               zend_stack_del_top(&self->call_time_stack); /* callee - removing ourselves */
00385        }
00386 #endif
00387 
00388        if (func_name[0] == '\0') {
00389               ; /* don't log that function */
00390        } else if (!zend_hash_num_elements(&self->not_filtered_functions) ||
00391                         1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
00392        {
00393 #ifndef MYSQLND_PROFILING_DISABLED
00394               if (FALSE == profile_calls) {
00395 #endif
00396                      self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name);
00397 
00398 #ifndef MYSQLND_PROFILING_DISABLED
00399               } else {
00400                      struct st_mysqlnd_dbg_function_profile f_profile_stack = {0};
00401                      struct st_mysqlnd_dbg_function_profile * f_profile = NULL;
00402                      uint64_t own_time = call_time - mine_non_own_time;
00403                      uint func_name_len = strlen(func_name);
00404 
00405                      self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s (total=%u own=%u in_calls=%u)",
00406                                           func_name, (unsigned int) call_time, (unsigned int) own_time, (unsigned int) mine_non_own_time
00407                                    );
00408 
00409                      if (SUCCESS == zend_hash_find(&self->function_profiles, func_name, func_name_len + 1, (void **) &f_profile)) {
00410                             /* found */
00411                                    if (f_profile) {
00412                                    if (mine_non_own_time < f_profile->min_in_calls) {
00413                                           f_profile->min_in_calls = mine_non_own_time;
00414                                    } else if (mine_non_own_time > f_profile->max_in_calls) {
00415                                           f_profile->max_in_calls = mine_non_own_time;
00416                                    }
00417                                    f_profile->avg_in_calls = (f_profile->avg_in_calls * f_profile->calls + mine_non_own_time) / (f_profile->calls + 1);
00418 
00419                                    if (own_time < f_profile->min_own) {
00420                                           f_profile->min_own = own_time;
00421                                    } else if (own_time > f_profile->max_own) {
00422                                           f_profile->max_own = own_time;
00423                                    }
00424                                    f_profile->avg_own = (f_profile->avg_own * f_profile->calls + own_time) / (f_profile->calls + 1);
00425 
00426                                    if (call_time < f_profile->min_total) {
00427                                           f_profile->min_total = call_time;
00428                                    } else if (call_time > f_profile->max_total) {
00429                                           f_profile->max_total = call_time;
00430                                    }
00431                                    f_profile->avg_total = (f_profile->avg_total * f_profile->calls + call_time) / (f_profile->calls + 1);
00432 
00433                                    ++f_profile->calls;
00434                                    if (f_profile->calls > PROFILE_UNDERPERFORM_THRESHOLD) {
00435                                           if (f_profile->avg_in_calls < mine_non_own_time) {
00436                                                  f_profile->in_calls_underporm_calls++;
00437                                           }
00438                                           if (f_profile->avg_own < own_time) {
00439                                                  f_profile->own_underporm_calls++;
00440                                           }
00441                                           if (f_profile->avg_total < call_time) {
00442                                                  f_profile->total_underporm_calls++;
00443                                           }
00444                                    }
00445                             }
00446                      } else {
00447                             /* add */
00448                             f_profile = &f_profile_stack;
00449                             f_profile->min_in_calls = f_profile->max_in_calls = f_profile->avg_in_calls = mine_non_own_time;
00450                             f_profile->min_total = f_profile->max_total = f_profile->avg_total = call_time;
00451                             f_profile->min_own = f_profile->max_own = f_profile->avg_own = own_time;
00452                             f_profile->calls = 1;
00453                             zend_hash_add(&self->function_profiles, func_name, func_name_len+1, f_profile, sizeof(struct st_mysqlnd_dbg_function_profile), NULL);
00454                      }
00455                      if ((uint) zend_stack_count(&self->call_time_stack)) {
00456                             uint64_t parent_non_own_time = 0;
00457 
00458                             zend_stack_top(&self->call_time_stack, (void **)&parent_non_own_time_ptr);
00459                             parent_non_own_time = *parent_non_own_time_ptr;
00460                             parent_non_own_time += call_time;
00461                             zend_stack_del_top(&self->call_time_stack); /* the caller */
00462                             zend_stack_push(&self->call_time_stack, &parent_non_own_time, sizeof(parent_non_own_time)); /* add back the caller */
00463                      }
00464               }
00465 #endif
00466        }
00467 
00468        return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL;
00469 }
00470 /* }}} */
00471 
00472 
00473 /* {{{ mysqlnd_debug::close */
00474 static enum_func_status
00475 MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self)
00476 {
00477        MYSQLND_ZTS(self);
00478        if (self->stream) {
00479 #ifndef MYSQLND_PROFILING_DISABLED
00480               if (!(self->flags & MYSQLND_DEBUG_FLUSH) && (self->flags & MYSQLND_DEBUG_PROFILE_CALLS)) {
00481                      struct st_mysqlnd_dbg_function_profile * f_profile;
00482                      HashPosition pos_values;
00483 
00484                      self->m->log_va(self, __LINE__, __FILE__, 0, "info : ", 
00485                                    "number of functions: %d", zend_hash_num_elements(&self->function_profiles));
00486                      zend_hash_internal_pointer_reset_ex(&self->function_profiles, &pos_values);
00487                      while (zend_hash_get_current_data_ex(&self->function_profiles, (void **) &f_profile, &pos_values) == SUCCESS) {
00488                             char   *string_key = NULL;
00489                             uint   string_key_len;
00490                             ulong  num_key;
00491 
00492                             zend_hash_get_current_key_ex(&self->function_profiles, &string_key, &string_key_len, &num_key, 0, &pos_values);
00493 
00494                             self->m->log_va(self, __LINE__, __FILE__, -1, "info : ",
00495                                           "%-40s\tcalls=%5llu  own_slow=%5llu  in_calls_slow=%5llu  total_slow=%5llu"
00496                                           "   min_own=%5llu  max_own=%7llu  avg_own=%7llu   "
00497                                           "   min_in_calls=%5llu  max_in_calls=%7llu  avg_in_calls=%7llu"
00498                                           "   min_total=%5llu  max_total=%7llu  avg_total=%7llu"
00499                                           ,string_key
00500                                           ,(uint64_t) f_profile->calls
00501                                           ,(uint64_t) f_profile->own_underporm_calls
00502                                           ,(uint64_t) f_profile->in_calls_underporm_calls
00503                                           ,(uint64_t) f_profile->total_underporm_calls
00504                                           
00505                                           ,(uint64_t) f_profile->min_own
00506                                           ,(uint64_t) f_profile->max_own
00507                                           ,(uint64_t) f_profile->avg_own
00508                                           ,(uint64_t) f_profile->min_in_calls
00509                                           ,(uint64_t) f_profile->max_in_calls
00510                                           ,(uint64_t) f_profile->avg_in_calls
00511                                           ,(uint64_t) f_profile->min_total
00512                                           ,(uint64_t) f_profile->max_total
00513                                           ,(uint64_t) f_profile->avg_total
00514                                           );
00515                             zend_hash_move_forward_ex(&self->function_profiles, &pos_values);
00516                      }
00517               }
00518 #endif 
00519 
00520               php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE);
00521               self->stream = NULL;
00522        }
00523        /* no DBG_RETURN please */
00524        return PASS;
00525 }
00526 /* }}} */
00527 
00528 
00529 /* {{{ mysqlnd_res_meta::free */
00530 static enum_func_status
00531 MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self)
00532 {
00533        if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
00534               efree(self->file_name);
00535               self->file_name = NULL;
00536        }
00537        zend_stack_destroy(&self->call_stack);
00538        zend_stack_destroy(&self->call_time_stack);
00539        zend_hash_destroy(&self->not_filtered_functions);
00540        zend_hash_destroy(&self->function_profiles);
00541        efree(self);
00542        return PASS;
00543 }
00544 /* }}} */
00545 
00546 enum mysqlnd_debug_parser_state
00547 {
00548        PARSER_WAIT_MODIFIER,
00549        PARSER_WAIT_COLON,
00550        PARSER_WAIT_VALUE
00551 };
00552 
00553 
00554 /* {{{ mysqlnd_res_meta::set_mode */
00555 static void
00556 MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode)
00557 {
00558        unsigned int mode_len = strlen(mode), i;
00559        enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER;
00560 
00561        self->flags = 0;
00562        self->nest_level_limit = 0;
00563        if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
00564               efree(self->file_name);
00565               self->file_name = NULL;
00566        }
00567        if (zend_hash_num_elements(&self->not_filtered_functions)) {
00568               zend_hash_destroy(&self->not_filtered_functions);
00569               zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0);
00570        }
00571 
00572        for (i = 0; i < mode_len; i++) {
00573               switch (mode[i]) {
00574                      case 'O':
00575                      case 'A':
00576                             self->flags |= MYSQLND_DEBUG_FLUSH;
00577                      case 'a':
00578                      case 'o':
00579                             if (mode[i] == 'a' || mode[i] == 'A') {
00580                                    self->flags |= MYSQLND_DEBUG_APPEND;
00581                             }
00582                             if (i + 1 < mode_len && mode[i+1] == ',') {
00583                                    unsigned int j = i + 2;
00584 #ifdef PHP_WIN32
00585                                    if (i+4 < mode_len && mode[i+3] == ':' && (mode[i+4] == '\\' || mode[i+5] == '/')) {
00586                                           j = i + 5;
00587                                    }
00588 #endif
00589                                    while (j < mode_len) {
00590                                           if (mode[j] == ':') {
00591                                                  break;
00592                                           }
00593                                           j++;
00594                                    }
00595                                    if (j > i + 2) {
00596                                           self->file_name = estrndup(mode + i + 2, j - i - 2);
00597                                    }
00598                                    i = j;
00599                             } else {
00600                                    if (!self->file_name)
00601                                           self->file_name = (char *) mysqlnd_debug_default_trace_file;
00602                             }
00603                             state = PARSER_WAIT_COLON;
00604                             break;
00605                      case ':':
00606 #if 0
00607                             if (state != PARSER_WAIT_COLON) {
00608                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Consecutive semicolons at position %u", i);
00609                             }
00610 #endif
00611                             state = PARSER_WAIT_MODIFIER;
00612                             break;
00613                      case 'f': /* limit output to these functions */
00614                             if (i + 1 < mode_len && mode[i+1] == ',') {
00615                                    unsigned int j = i + 2;
00616                                    i++;
00617                                    while (j < mode_len) {
00618                                           if (mode[j] == ':') {
00619                                                  /* function names with :: */
00620                                                  if ((j + 1 < mode_len) && mode[j+1] == ':') {
00621                                                         j += 2;
00622                                                         continue;
00623                                                  }
00624                                           }
00625                                           if (mode[j] == ',' || mode[j] == ':') {
00626                                                  if (j > i + 2) {
00627                                                         char func_name[1024];
00628                                                         unsigned int func_name_len = MIN(sizeof(func_name) - 1, j - i - 1);
00629                                                         memcpy(func_name, mode + i + 1, func_name_len);
00630                                                         func_name[func_name_len] = '\0'; 
00631 
00632                                                         zend_hash_add_empty_element(&self->not_filtered_functions,
00633                                                                                                          func_name, func_name_len + 1);
00634                                                         i = j;
00635                                                  }
00636                                                  if (mode[j] == ':') {
00637                                                         break;
00638                                                  }
00639                                           }
00640                                           j++;
00641                                    }
00642                                    i = j;
00643                             } else {
00644 #if 0
00645                                    php_error_docref(NULL TSRMLS_CC, E_WARNING,
00646                                                                 "Expected list of functions for '%c' found none", mode[i]);
00647 #endif
00648                             }
00649                             state = PARSER_WAIT_COLON;
00650                             break;
00651                      case 'D':
00652                      case 'd':
00653                      case 'g':
00654                      case 'p':
00655                             /* unsupported */
00656                             if ((i + 1) < mode_len && mode[i+1] == ',') {
00657                                    i+= 2;
00658                                    while (i < mode_len) {
00659                                           if (mode[i] == ':') {
00660                                                  break;
00661                                           }
00662                                           i++;
00663                                    }
00664                             }
00665                             state = PARSER_WAIT_COLON;
00666                             break;
00667                      case 'F':
00668                             self->flags |= MYSQLND_DEBUG_DUMP_FILE;
00669                             state = PARSER_WAIT_COLON;
00670                             break;
00671                      case 'i':
00672                             self->flags |= MYSQLND_DEBUG_DUMP_PID;
00673                             state = PARSER_WAIT_COLON;
00674                             break;
00675                      case 'L':
00676                             self->flags |= MYSQLND_DEBUG_DUMP_LINE;
00677                             state = PARSER_WAIT_COLON;
00678                             break;
00679                      case 'n':
00680                             self->flags |= MYSQLND_DEBUG_DUMP_LEVEL;
00681                             state = PARSER_WAIT_COLON;
00682                             break;
00683                      case 't':
00684                             if (mode[i+1] == ',') {
00685                                    unsigned int j = i + 2;
00686                                    while (j < mode_len) {
00687                                           if (mode[j] == ':') {
00688                                                  break;
00689                                           }
00690                                           j++;
00691                                    }
00692                                    if (j > i + 2) {
00693                                           char *value_str = estrndup(mode + i + 2, j - i - 2);
00694                                           self->nest_level_limit = atoi(value_str);
00695                                           efree(value_str);
00696                                    }
00697                                    i = j;
00698                             } else {
00699                                    self->nest_level_limit = 200; /* default value for FF DBUG */
00700                             }
00701                             self->flags |= MYSQLND_DEBUG_DUMP_TRACE;
00702                             state = PARSER_WAIT_COLON;
00703                             break;
00704                      case 'T':
00705                             self->flags |= MYSQLND_DEBUG_DUMP_TIME;
00706                             state = PARSER_WAIT_COLON;
00707                             break;
00708                      case 'N':
00709                      case 'P':
00710                      case 'r':
00711                      case 'S':
00712                             state = PARSER_WAIT_COLON;
00713                             break;
00714                      case 'm': /* mysqlnd extension - trace memory functions */
00715                             self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS;
00716                             state = PARSER_WAIT_COLON;
00717                             break;
00718                      case 'x': /* mysqlnd extension - profile calls */
00719                             self->flags |= MYSQLND_DEBUG_PROFILE_CALLS;
00720                             state = PARSER_WAIT_COLON;
00721                             break;                      
00722                      default:
00723                             if (state == PARSER_WAIT_MODIFIER) {
00724 #if 0
00725                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized format '%c'", mode[i]);
00726 #endif
00727                                    if (i+1 < mode_len && mode[i+1] == ',') {
00728                                           i+= 2;
00729                                           while (i < mode_len) {
00730                                                  if (mode[i] == ':') {
00731                                                         break;
00732                                                  }
00733                                                  i++;
00734                                           }
00735                                    }
00736                                    state = PARSER_WAIT_COLON;
00737                             } else if (state == PARSER_WAIT_COLON) {
00738 #if 0
00739                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]);
00740 #endif
00741                             }
00742                             break;
00743               }
00744        }
00745 }
00746 /* }}} */
00747 
00748 MYSQLND_CLASS_METHODS_START(mysqlnd_debug)
00749        MYSQLND_METHOD(mysqlnd_debug, open),
00750        MYSQLND_METHOD(mysqlnd_debug, set_mode),
00751        MYSQLND_METHOD(mysqlnd_debug, log),
00752        MYSQLND_METHOD(mysqlnd_debug, log_va),
00753        MYSQLND_METHOD(mysqlnd_debug, func_enter),
00754        MYSQLND_METHOD(mysqlnd_debug, func_leave),
00755        MYSQLND_METHOD(mysqlnd_debug, close),
00756        MYSQLND_METHOD(mysqlnd_debug, free),
00757 MYSQLND_CLASS_METHODS_END;
00758 
00759 
00760 /* {{{ mysqlnd_debug_init */
00761 PHPAPI MYSQLND_DEBUG *
00762 mysqlnd_debug_init(const char * skip_functions[] TSRMLS_DC)
00763 {
00764        MYSQLND_DEBUG *ret = ecalloc(1, sizeof(MYSQLND_DEBUG));
00765 #ifdef ZTS
00766        ret->TSRMLS_C = TSRMLS_C;
00767 #endif
00768        ret->nest_level_limit = 0;
00769        ret->pid = getpid();
00770        zend_stack_init(&ret->call_stack);
00771        zend_stack_init(&ret->call_time_stack);
00772        zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0);
00773        zend_hash_init(&ret->function_profiles, 0, NULL, NULL, 0);
00774 
00775        ret->m = & mysqlnd_mysqlnd_debug_methods;
00776        ret->skip_functions = skip_functions;
00777 
00778        return ret;
00779 }
00780 /* }}} */
00781 
00782 
00783 /* {{{ _mysqlnd_debug */
00784 PHPAPI void _mysqlnd_debug(const char * mode TSRMLS_DC)
00785 {
00786 #ifdef PHP_DEBUG
00787        MYSQLND_DEBUG *dbg = MYSQLND_G(dbg);
00788        if (!dbg) {
00789               MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(mysqlnd_debug_std_no_trace_funcs TSRMLS_CC);
00790               if (!dbg) {
00791                      return;
00792               }
00793        }
00794 
00795        dbg->m->close(dbg);
00796        dbg->m->set_mode(dbg, mode);
00797        while (zend_stack_count(&dbg->call_stack)) {
00798               zend_stack_del_top(&dbg->call_stack);
00799        }
00800        while (zend_stack_count(&dbg->call_time_stack)) {
00801               zend_stack_del_top(&dbg->call_time_stack);
00802        }
00803 #endif
00804 }
00805 /* }}} */
00806 
00807 
00808 #if ZEND_DEBUG
00809 #else
00810 #define __zend_filename "/unknown/unknown"
00811 #define __zend_lineno   0
00812 #endif
00813 
00814 #define REAL_SIZE(s) (collect_memory_statistics? (s) + sizeof(size_t) : (s))
00815 #define REAL_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) - sizeof(size_t)) : (p))
00816 #define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + sizeof(size_t)) : (p))
00817 
00818 /* {{{ _mysqlnd_emalloc */
00819 void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
00820 {
00821        void *ret;
00822        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
00823        long * threshold = &MYSQLND_G(debug_emalloc_fail_threshold);
00824        DBG_ENTER(mysqlnd_emalloc_name);
00825 
00826        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
00827 
00828 #ifdef PHP_DEBUG
00829        /* -1 is also "true" */
00830        if (*threshold) {
00831 #endif
00832               ret = emalloc(REAL_SIZE(size));
00833 #ifdef PHP_DEBUG
00834               --*threshold;
00835        } else if (*threshold == 0) {
00836               ret = NULL;
00837        }
00838 #endif
00839 
00840        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
00841 
00842        if (ret && collect_memory_statistics) {
00843               *(size_t *) ret = size;
00844               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMOUNT, size);
00845        }
00846        DBG_RETURN(FAKE_PTR(ret));
00847 }
00848 /* }}} */
00849 
00850 
00851 /* {{{ _mysqlnd_pemalloc */
00852 void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
00853 {
00854        void *ret;
00855        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
00856        long * threshold = persistent? &MYSQLND_G(debug_malloc_fail_threshold):&MYSQLND_G(debug_emalloc_fail_threshold);
00857        DBG_ENTER(mysqlnd_pemalloc_name);
00858        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
00859 
00860 #ifdef PHP_DEBUG
00861        /* -1 is also "true" */
00862        if (*threshold) {
00863 #endif
00864               ret = pemalloc(REAL_SIZE(size), persistent);
00865 #ifdef PHP_DEBUG
00866               --*threshold;
00867        } else if (*threshold == 0) {
00868               ret = NULL;
00869        }
00870 #endif
00871 
00872        DBG_INF_FMT("size=%lu ptr=%p persistent=%u", size, ret, persistent);
00873 
00874        if (ret && collect_memory_statistics) {
00875               enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT;
00876               enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMOUNT:STAT_MEM_EMALLOC_AMOUNT;
00877               *(size_t *) ret = size;
00878               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
00879        }
00880 
00881        DBG_RETURN(FAKE_PTR(ret));
00882 }
00883 /* }}} */
00884 
00885 
00886 /* {{{ _mysqlnd_ecalloc */
00887 void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
00888 {
00889        void *ret;
00890        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
00891        long * threshold = &MYSQLND_G(debug_ecalloc_fail_threshold);
00892        DBG_ENTER(mysqlnd_ecalloc_name);
00893        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
00894        DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
00895 
00896 #ifdef PHP_DEBUG
00897        /* -1 is also "true" */
00898        if (*threshold) {
00899 #endif
00900               ret = ecalloc(nmemb, REAL_SIZE(size));
00901 #ifdef PHP_DEBUG
00902               --*threshold;
00903        } else if (*threshold == 0) {
00904               ret = NULL;
00905        }
00906 #endif
00907 
00908        DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
00909        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
00910        if (ret && collect_memory_statistics) {
00911               *(size_t *) ret = size;
00912               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMOUNT, size);
00913        }
00914        DBG_RETURN(FAKE_PTR(ret));
00915 }
00916 /* }}} */
00917 
00918 
00919 /* {{{ _mysqlnd_pecalloc */
00920 void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
00921 {
00922        void *ret;
00923        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
00924        long * threshold = persistent? &MYSQLND_G(debug_calloc_fail_threshold):&MYSQLND_G(debug_ecalloc_fail_threshold);
00925        DBG_ENTER(mysqlnd_pecalloc_name);
00926        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
00927 
00928 #ifdef PHP_DEBUG
00929        /* -1 is also "true" */
00930        if (*threshold) {
00931 #endif
00932               ret = pecalloc(nmemb, REAL_SIZE(size), persistent);
00933 #ifdef PHP_DEBUG
00934               --*threshold;
00935        } else if (*threshold == 0) {
00936               ret = NULL;
00937        }
00938 #endif
00939 
00940        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
00941 
00942        if (ret && collect_memory_statistics) {
00943               enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT;
00944               enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMOUNT:STAT_MEM_ECALLOC_AMOUNT;
00945               *(size_t *) ret = size;
00946               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
00947        }
00948 
00949        DBG_RETURN(FAKE_PTR(ret));
00950 }
00951 /* }}} */
00952 
00953 
00954 /* {{{ _mysqlnd_erealloc */
00955 void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
00956 {
00957        void *ret;
00958        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
00959        size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
00960        long * threshold = &MYSQLND_G(debug_erealloc_fail_threshold);
00961        DBG_ENTER(mysqlnd_erealloc_name);
00962        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
00963        DBG_INF_FMT("ptr=%p old_size=%lu, new_size=%lu", ptr, old_size, new_size); 
00964 
00965 #ifdef PHP_DEBUG
00966        /* -1 is also "true" */
00967        if (*threshold) {
00968 #endif
00969               ret = erealloc(REAL_PTR(ptr), REAL_SIZE(new_size));
00970 #ifdef PHP_DEBUG
00971               --*threshold;
00972        } else if (*threshold == 0) {
00973               ret = NULL;
00974        }
00975 #endif
00976 
00977        DBG_INF_FMT("new_ptr=%p", (char*)ret);
00978        if (ret && collect_memory_statistics) {
00979               *(size_t *) ret = new_size;
00980               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMOUNT, new_size);
00981        }
00982        DBG_RETURN(FAKE_PTR(ret));
00983 }
00984 /* }}} */
00985 
00986 
00987 /* {{{ _mysqlnd_perealloc */
00988 void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
00989 {
00990        void *ret;
00991        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
00992        size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
00993        long * threshold = persistent? &MYSQLND_G(debug_realloc_fail_threshold):&MYSQLND_G(debug_erealloc_fail_threshold);
00994        DBG_ENTER(mysqlnd_perealloc_name);
00995        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
00996        DBG_INF_FMT("ptr=%p old_size=%lu new_size=%lu persistent=%u", ptr, old_size, new_size, persistent); 
00997 
00998 #ifdef PHP_DEBUG
00999        /* -1 is also "true" */
01000        if (*threshold) {
01001 #endif
01002               ret = perealloc(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
01003 #ifdef PHP_DEBUG
01004               --*threshold;
01005        } else if (*threshold == 0) {
01006               ret = NULL;
01007        }
01008 #endif
01009 
01010        DBG_INF_FMT("new_ptr=%p", (char*)ret);
01011 
01012        if (ret && collect_memory_statistics) {
01013               enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT;
01014               enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMOUNT:STAT_MEM_EREALLOC_AMOUNT;
01015               *(size_t *) ret = new_size;
01016               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, new_size);
01017        }
01018        DBG_RETURN(FAKE_PTR(ret));
01019 }
01020 /* }}} */
01021 
01022 
01023 /* {{{ _mysqlnd_efree */
01024 void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
01025 {
01026        size_t free_amount = 0;
01027        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
01028        DBG_ENTER(mysqlnd_efree_name);
01029        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
01030        DBG_INF_FMT("ptr=%p", ptr); 
01031 
01032        if (ptr) {
01033               if (collect_memory_statistics) {
01034                      free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
01035                      DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
01036               }
01037               efree(REAL_PTR(ptr));
01038        }
01039 
01040        if (collect_memory_statistics) {
01041               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EFREE_COUNT, 1, STAT_MEM_EFREE_AMOUNT, free_amount);
01042        }
01043        DBG_VOID_RETURN;
01044 }
01045 /* }}} */
01046 
01047 
01048 /* {{{ _mysqlnd_pefree */
01049 void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
01050 {
01051        size_t free_amount = 0;
01052        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
01053        DBG_ENTER(mysqlnd_pefree_name);
01054        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
01055        DBG_INF_FMT("ptr=%p persistent=%u", ptr, persistent); 
01056 
01057        if (ptr) {
01058               if (collect_memory_statistics) {
01059                      free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
01060                      DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
01061               }
01062               pefree(REAL_PTR(ptr), persistent);
01063        }
01064 
01065        if (collect_memory_statistics) {
01066               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT, 1,
01067                                                                                persistent? STAT_MEM_FREE_AMOUNT:STAT_MEM_EFREE_AMOUNT, free_amount);
01068        }
01069        DBG_VOID_RETURN;
01070 }
01071 
01072 
01073 /* {{{ _mysqlnd_malloc */
01074 void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
01075 {
01076        void *ret;
01077        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
01078        long * threshold = &MYSQLND_G(debug_malloc_fail_threshold);
01079        DBG_ENTER(mysqlnd_malloc_name);
01080        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
01081 
01082 #ifdef PHP_DEBUG
01083        /* -1 is also "true" */
01084        if (*threshold) {
01085 #endif
01086               ret = malloc(REAL_SIZE(size));
01087 #ifdef PHP_DEBUG
01088               --*threshold;
01089        } else if (*threshold == 0) {
01090               ret = NULL;
01091        }
01092 #endif
01093 
01094        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
01095        if (ret && collect_memory_statistics) {
01096               *(size_t *) ret = size;
01097               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMOUNT, size);
01098        }
01099        DBG_RETURN(FAKE_PTR(ret));
01100 }
01101 /* }}} */
01102 
01103 
01104 /* {{{ _mysqlnd_calloc */
01105 void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
01106 {
01107        void *ret;
01108        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
01109        long * threshold = &MYSQLND_G(debug_calloc_fail_threshold);
01110        DBG_ENTER(mysqlnd_calloc_name);
01111        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
01112 
01113 #ifdef PHP_DEBUG
01114        /* -1 is also "true" */
01115        if (*threshold) {
01116 #endif
01117               ret = calloc(nmemb, REAL_SIZE(size));
01118 #ifdef PHP_DEBUG
01119               --*threshold;
01120        } else if (*threshold == 0) {
01121               ret = NULL;
01122        }
01123 #endif
01124 
01125        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
01126        if (ret && collect_memory_statistics) {
01127               *(size_t *) ret = size;
01128               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMOUNT, size);
01129        }
01130        DBG_RETURN(FAKE_PTR(ret));
01131 }
01132 /* }}} */
01133 
01134 
01135 /* {{{ _mysqlnd_realloc */
01136 void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
01137 {
01138        void *ret;
01139        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
01140        long * threshold = &MYSQLND_G(debug_realloc_fail_threshold);
01141        DBG_ENTER(mysqlnd_realloc_name);
01142        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
01143        DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr); 
01144        DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC));
01145 
01146 #ifdef PHP_DEBUG
01147        /* -1 is also "true" */
01148        if (*threshold) {
01149 #endif
01150               ret = realloc(REAL_PTR(ptr), REAL_SIZE(new_size));
01151 #ifdef PHP_DEBUG
01152               --*threshold;
01153        } else if (*threshold == 0) {
01154               ret = NULL;
01155        }
01156 #endif
01157 
01158        DBG_INF_FMT("new_ptr=%p", (char*)ret);
01159 
01160        if (ret && collect_memory_statistics) {
01161               *(size_t *) ret = new_size;
01162               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMOUNT, new_size);
01163        }
01164        DBG_RETURN(FAKE_PTR(ret));
01165 }
01166 /* }}} */
01167 
01168 
01169 /* {{{ _mysqlnd_free */
01170 void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
01171 {
01172        size_t free_amount = 0;
01173        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
01174        DBG_ENTER(mysqlnd_free_name);
01175        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
01176        DBG_INF_FMT("ptr=%p", ptr); 
01177 
01178        if (ptr) {
01179               if (collect_memory_statistics) {
01180                      free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
01181                      DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
01182               }
01183               free(REAL_PTR(ptr));
01184        }
01185 
01186        if (collect_memory_statistics) {
01187               MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_FREE_COUNT, 1, STAT_MEM_FREE_AMOUNT, free_amount);
01188        }
01189        DBG_VOID_RETURN;
01190 }
01191 /* }}} */
01192 
01193 #define SMART_STR_START_SIZE 2048
01194 #define SMART_STR_PREALLOC 512
01195 #include "ext/standard/php_smart_str.h"
01196 
01197 
01198 /* {{{ _mysqlnd_pestrndup */
01199 char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
01200 {
01201        char * ret;
01202        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
01203        DBG_ENTER(mysqlnd_pestrndup_name);
01204        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
01205        DBG_INF_FMT("ptr=%p", ptr); 
01206 
01207        ret = pemalloc(REAL_SIZE(length) + 1, persistent);
01208        {
01209               size_t l = length;
01210               char * p = (char *) ptr;
01211               char * dest = (char *) FAKE_PTR(ret);
01212               while (*p && l--) {
01213                      *dest++ = *p++;
01214               }
01215               *dest = '\0';
01216        }
01217 
01218        if (collect_memory_statistics) {
01219               *(size_t *) ret = length;
01220               MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRNDUP_COUNT : STAT_MEM_ESTRNDUP_COUNT);
01221        }
01222 
01223        DBG_RETURN(FAKE_PTR(ret));
01224 }
01225 /* }}} */
01226 
01227 
01228 /* {{{ _mysqlnd_pestrdup */
01229 char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
01230 {
01231        char * ret;
01232        smart_str tmp_str = {0, 0, 0};
01233        const char * p = ptr;
01234        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
01235        DBG_ENTER(mysqlnd_pestrdup_name);
01236        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
01237        DBG_INF_FMT("ptr=%p", ptr);
01238        do {
01239               smart_str_appendc(&tmp_str, *p);
01240        } while (*p++);
01241 
01242        ret = pemalloc(tmp_str.len + sizeof(size_t), persistent);
01243        memcpy(FAKE_PTR(ret), tmp_str.c, tmp_str.len);
01244 
01245        if (ret && collect_memory_statistics) {
01246               *(size_t *) ret = tmp_str.len;
01247               MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRDUP_COUNT : STAT_MEM_ESTRDUP_COUNT);
01248        }
01249        smart_str_free(&tmp_str);
01250 
01251        DBG_RETURN(FAKE_PTR(ret));
01252 }
01253 /* }}} */
01254 
01255 #define MYSQLND_DEBUG_MEMORY 1
01256 
01257 #if MYSQLND_DEBUG_MEMORY == 0
01258 
01259 /* {{{ mysqlnd_zend_mm_emalloc */
01260 static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
01261 {
01262        return emalloc(size);
01263 }
01264 /* }}} */
01265 
01266 
01267 /* {{{ mysqlnd_zend_mm_pemalloc */
01268 static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
01269 {
01270        return pemalloc(size, persistent);
01271 }
01272 /* }}} */
01273 
01274 
01275 /* {{{ mysqlnd_zend_mm_ecalloc */
01276 static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
01277 {
01278        return ecalloc(nmemb, size);
01279 }
01280 /* }}} */
01281 
01282 
01283 /* {{{ mysqlnd_zend_mm_pecalloc */
01284 static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
01285 {
01286        return pecalloc(nmemb, size, persistent);
01287 }
01288 /* }}} */
01289 
01290 
01291 /* {{{ mysqlnd_zend_mm_erealloc */
01292 static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
01293 {
01294        return erealloc(ptr, new_size);
01295 }
01296 /* }}} */
01297 
01298 
01299 /* {{{ mysqlnd_zend_mm_perealloc */
01300 static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
01301 {
01302        return perealloc(ptr, new_size, persistent);
01303 }
01304 /* }}} */
01305 
01306 
01307 /* {{{ mysqlnd_zend_mm_efree */
01308 static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
01309 {
01310        efree(ptr);
01311 }
01312 /* }}} */
01313 
01314 
01315 /* {{{ mysqlnd_zend_mm_pefree */
01316 static void mysqlnd_zend_mm_pefree(void * ptr, zend_bool persistent MYSQLND_MEM_D)
01317 {
01318        pefree(ptr, persistent);
01319 }
01320 /* }}} */
01321 
01322 
01323 /* {{{ mysqlnd_zend_mm_malloc */
01324 static void * mysqlnd_zend_mm_malloc(size_t size MYSQLND_MEM_D)
01325 {
01326        return malloc(size);
01327 }
01328 /* }}} */
01329 
01330 
01331 /* {{{ mysqlnd_zend_mm_calloc */
01332 static void * mysqlnd_zend_mm_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
01333 {
01334        return calloc(nmemb, size);
01335 }
01336 /* }}} */
01337 
01338 
01339 /* {{{ mysqlnd_zend_mm_realloc */
01340 static void * mysqlnd_zend_mm_realloc(void * ptr, size_t new_size MYSQLND_MEM_D)
01341 {
01342        return realloc(ptr, new_size);
01343 }
01344 /* }}} */
01345 
01346 
01347 /* {{{ mysqlnd_zend_mm_free */
01348 static void mysqlnd_zend_mm_free(void * ptr MYSQLND_MEM_D)
01349 {
01350        free(ptr);
01351 }
01352 /* }}} */
01353 
01354 
01355 /* {{{ mysqlnd_zend_mm_pestrndup */
01356 static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
01357 {
01358        return pestrndup(ptr, length, persistent);
01359 }
01360 /* }}} */
01361 
01362 
01363 /* {{{ mysqlnd_zend_mm_pestrdup */
01364 static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
01365 {
01366        return pestrdup(ptr, persistent);
01367 }
01368 /* }}} */
01369 
01370 #endif
01371 
01372 
01373 PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator = 
01374 {
01375 #if MYSQLND_DEBUG_MEMORY
01376        _mysqlnd_emalloc,
01377        _mysqlnd_pemalloc,
01378        _mysqlnd_ecalloc,
01379        _mysqlnd_pecalloc,
01380        _mysqlnd_erealloc,
01381        _mysqlnd_perealloc,
01382        _mysqlnd_efree,
01383        _mysqlnd_pefree,
01384        _mysqlnd_malloc,
01385        _mysqlnd_calloc,
01386        _mysqlnd_realloc,
01387        _mysqlnd_free,
01388        _mysqlnd_pestrndup,
01389        _mysqlnd_pestrdup
01390 #else
01391        mysqlnd_zend_mm_emalloc,
01392        mysqlnd_zend_mm_pemalloc,
01393        mysqlnd_zend_mm_ecalloc,
01394        mysqlnd_zend_mm_pecalloc,
01395        mysqlnd_zend_mm_erealloc,
01396        mysqlnd_zend_mm_perealloc,
01397        mysqlnd_zend_mm_efree,
01398        mysqlnd_zend_mm_pefree,
01399        mysqlnd_zend_mm_malloc,
01400        mysqlnd_zend_mm_calloc,
01401        mysqlnd_zend_mm_realloc,
01402        mysqlnd_zend_mm_free,
01403        mysqlnd_zend_mm_pestrndup,
01404        mysqlnd_zend_mm_pestrdup
01405 #endif
01406 };
01407 
01408 
01409 
01410 /* Follows code borrowed from zend_builtin_functions.c because the functions there are static */
01411 
01412 #if MYSQLND_UNICODE
01413 /* {{{ gettraceasstring() macros */
01414 #define TRACE_APPEND_CHR(chr)                                            \
01415        *str = (char*)erealloc(*str, *len + 1 + 1);                          \
01416        (*str)[(*len)++] = chr
01417 
01418 #define TRACE_APPEND_STRL(val, vallen)                                   \
01419        {                                                                    \
01420               int l = vallen;                                                  \
01421               *str = (char*)erealloc(*str, *len + l + 1);                      \
01422               memcpy((*str) + *len, val, l);                                   \
01423               *len += l;                                                       \
01424        }
01425 
01426 #define TRACE_APPEND_USTRL(val, vallen) \
01427        { \
01428               zval tmp, copy; \
01429               int use_copy; \
01430               ZVAL_UNICODEL(&tmp, val, vallen, 1); \
01431               zend_make_printable_zval(&tmp, &copy, &use_copy); \
01432               TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
01433               zval_dtor(&copy); \
01434               zval_dtor(&tmp); \
01435        }
01436 
01437 #define TRACE_APPEND_ZVAL(zv) \
01438        if (Z_TYPE_P((zv)) == IS_UNICODE) { \
01439               zval copy; \
01440               int use_copy; \
01441               zend_make_printable_zval((zv), &copy, &use_copy); \
01442               TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
01443               zval_dtor(&copy); \
01444        } else { \
01445               TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
01446        }
01447 
01448 #define TRACE_APPEND_STR(val)                                            \
01449        TRACE_APPEND_STRL(val, sizeof(val)-1)
01450 
01451 #define TRACE_APPEND_KEY(key)                                            \
01452        if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
01453               if (Z_TYPE_PP(tmp) == IS_UNICODE) { \
01454                      zval copy; \
01455                      int use_copy; \
01456                      zend_make_printable_zval(*tmp, &copy, &use_copy); \
01457                      TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
01458                      zval_dtor(&copy); \
01459               } else { \
01460               TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \
01461               } \
01462        }
01463 /* }}} */
01464 
01465 /* {{{ mysqlnd_build_trace_args */
01466 static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
01467 {
01468        char **str;
01469        int *len;
01470 
01471        str = va_arg(args, char**);
01472        len = va_arg(args, int*);
01473 
01474        /* the trivial way would be to do:
01475         * conver_to_string_ex(arg);
01476         * append it and kill the now tmp arg.
01477         * but that could cause some E_NOTICE and also damn long lines.
01478         */
01479 
01480        switch (Z_TYPE_PP(arg)) {
01481               case IS_NULL:
01482                      TRACE_APPEND_STR("NULL, ");
01483                      break;
01484               case IS_STRING: {
01485                      int l_added;
01486                      TRACE_APPEND_CHR('\'');
01487                      if (Z_STRLEN_PP(arg) > 15) {
01488                             TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
01489                             TRACE_APPEND_STR("...', ");
01490                             l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
01491                      } else {
01492                             l_added = Z_STRLEN_PP(arg);
01493                             TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
01494                             TRACE_APPEND_STR("', ");
01495                             l_added += 3 + 1;
01496                      }
01497                      while (--l_added) {
01498                             if ((unsigned char)(*str)[*len - l_added] < 32) {
01499                                    (*str)[*len - l_added] = '?';
01500                             }
01501                      }
01502                      break;
01503               }
01504               case IS_UNICODE: {
01505                      int l_added;
01506 
01507                      /*
01508                       * We do not want to apply current error mode here, since
01509                       * zend_make_printable_zval() uses output encoding converter.
01510                       * Temporarily set output encoding converter to escape offending
01511                       * chars with \uXXXX notation.
01512                       */
01513                      zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA);
01514                      TRACE_APPEND_CHR('\'');
01515                      if (Z_USTRLEN_PP(arg) > 15) {
01516                             TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15);
01517                             TRACE_APPEND_STR("...', ");
01518                             l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
01519                      } else {
01520                             l_added = Z_USTRLEN_PP(arg);
01521                             TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added);
01522                             TRACE_APPEND_STR("', ");
01523                             l_added += 3 + 1;
01524                      }
01525                      /*
01526                       * Reset output encoding converter error mode.
01527                       */
01528                      zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode));
01529                      while (--l_added) {
01530                             if ((unsigned char)(*str)[*len - l_added] < 32) {
01531                                    (*str)[*len - l_added] = '?';
01532                             }
01533                      }
01534                      break;
01535               }
01536               case IS_BOOL:
01537                      if (Z_LVAL_PP(arg)) {
01538                             TRACE_APPEND_STR("true, ");
01539                      } else {
01540                             TRACE_APPEND_STR("false, ");
01541                      }
01542                      break;
01543               case IS_RESOURCE:
01544                      TRACE_APPEND_STR("Resource id #");
01545                      /* break; */
01546               case IS_LONG: {
01547                      long lval = Z_LVAL_PP(arg);
01548                      char s_tmp[MAX_LENGTH_OF_LONG + 1];
01549                      int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
01550                      TRACE_APPEND_STRL(s_tmp, l_tmp);
01551                      TRACE_APPEND_STR(", ");
01552                      break;
01553               }
01554               case IS_DOUBLE: {
01555                      double dval = Z_DVAL_PP(arg);
01556                      char *s_tmp;
01557                      int l_tmp;
01558 
01559                      s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
01560                      l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
01561                      TRACE_APPEND_STRL(s_tmp, l_tmp);
01562                      /* %G already handles removing trailing zeros from the fractional part, yay */
01563                      efree(s_tmp);
01564                      TRACE_APPEND_STR(", ");
01565                      break;
01566               }
01567               case IS_ARRAY:
01568                      TRACE_APPEND_STR("Array, ");
01569                      break;
01570               case IS_OBJECT: {
01571                      zval tmp;
01572                      zstr class_name;
01573                      zend_uint class_name_len;
01574                      int dup;
01575 
01576                      TRACE_APPEND_STR("Object(");
01577 
01578                      dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
01579 
01580                      ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1);
01581                      convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv)));
01582                      TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp));
01583                      zval_dtor(&tmp);
01584 
01585                      if(!dup) {
01586                             efree(class_name.v);
01587                      }
01588 
01589                      TRACE_APPEND_STR("), ");
01590                      break;
01591               }
01592               default:
01593                      break;
01594        }
01595        return ZEND_HASH_APPLY_KEEP;
01596 }
01597 /* }}} */
01598 
01599 
01600 static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
01601 {
01602        char *s_tmp, **str;
01603        int *len, *num;
01604        long line;
01605        HashTable *ht = Z_ARRVAL_PP(frame);
01606        zval **file, **tmp;
01607        uint * level;
01608 
01609        level = va_arg(args, uint *);
01610        str = va_arg(args, char**);
01611        len = va_arg(args, int*);
01612        num = va_arg(args, int*);
01613 
01614        if (!*level) {
01615               return ZEND_HASH_APPLY_KEEP;
01616        }
01617        --*level;
01618 
01619        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
01620        sprintf(s_tmp, "#%d ", (*num)++);
01621        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
01622        efree(s_tmp);
01623        if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
01624               if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
01625                      line = Z_LVAL_PP(tmp);
01626               } else {
01627                      line = 0;
01628               }
01629               TRACE_APPEND_ZVAL(*file);
01630               s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
01631               sprintf(s_tmp, "(%ld): ", line);
01632               TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
01633               efree(s_tmp);
01634        } else {
01635               TRACE_APPEND_STR("[internal function]: ");
01636        }
01637        TRACE_APPEND_KEY("class");
01638        TRACE_APPEND_KEY("type");
01639        TRACE_APPEND_KEY("function");
01640        TRACE_APPEND_CHR('(');
01641        if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
01642               int last_len = *len;
01643               zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
01644               if (last_len != *len) {
01645                      *len -= 2; /* remove last ', ' */
01646               }
01647        }
01648        TRACE_APPEND_STR(")\n");
01649        return ZEND_HASH_APPLY_KEEP;
01650 }
01651 /* }}} */
01652 
01653 
01654 #else /* PHP 5*/
01655 
01656 
01657 /* {{{ gettraceasstring() macros */
01658 #define TRACE_APPEND_CHR(chr)                                            \
01659        *str = (char*)erealloc(*str, *len + 1 + 1);                          \
01660        (*str)[(*len)++] = chr
01661 
01662 #define TRACE_APPEND_STRL(val, vallen)                                   \
01663        {                                                                    \
01664               int l = vallen;                                                  \
01665               *str = (char*)erealloc(*str, *len + l + 1);                      \
01666               memcpy((*str) + *len, val, l);                                   \
01667               *len += l;                                                       \
01668        }
01669 
01670 #define TRACE_APPEND_STR(val)                                            \
01671        TRACE_APPEND_STRL(val, sizeof(val)-1)
01672 
01673 #define TRACE_APPEND_KEY(key)                                            \
01674        if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
01675            TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \
01676        }
01677 
01678 /* }}} */
01679 
01680 
01681 static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
01682 {
01683        char **str;
01684        int *len;
01685 
01686        str = va_arg(args, char**);
01687        len = va_arg(args, int*);
01688 
01689        /* the trivial way would be to do:
01690         * conver_to_string_ex(arg);
01691         * append it and kill the now tmp arg.
01692         * but that could cause some E_NOTICE and also damn long lines.
01693         */
01694 
01695        switch (Z_TYPE_PP(arg)) {
01696               case IS_NULL:
01697                      TRACE_APPEND_STR("NULL, ");
01698                      break;
01699               case IS_STRING: {
01700                      int l_added;
01701                      TRACE_APPEND_CHR('\'');
01702                      if (Z_STRLEN_PP(arg) > 15) {
01703                             TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
01704                             TRACE_APPEND_STR("...', ");
01705                             l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
01706                      } else {
01707                             l_added = Z_STRLEN_PP(arg);
01708                             TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
01709                             TRACE_APPEND_STR("', ");
01710                             l_added += 3 + 1;
01711                      }
01712                      while (--l_added) {
01713                             if ((*str)[*len - l_added] < 32) {
01714                                    (*str)[*len - l_added] = '?';
01715                             }
01716                      }
01717                      break;
01718               }
01719               case IS_BOOL:
01720                      if (Z_LVAL_PP(arg)) {
01721                             TRACE_APPEND_STR("true, ");
01722                      } else {
01723                             TRACE_APPEND_STR("false, ");
01724                      }
01725                      break;
01726               case IS_RESOURCE:
01727                      TRACE_APPEND_STR("Resource id #");
01728                      /* break; */
01729               case IS_LONG: {
01730                      long lval = Z_LVAL_PP(arg);
01731                      char s_tmp[MAX_LENGTH_OF_LONG + 1];
01732                      int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
01733                      TRACE_APPEND_STRL(s_tmp, l_tmp);
01734                      TRACE_APPEND_STR(", ");
01735                      break;
01736               }
01737               case IS_DOUBLE: {
01738                      double dval = Z_DVAL_PP(arg);
01739                      char *s_tmp;
01740                      int l_tmp;
01741 
01742                      s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
01743                      l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
01744                      TRACE_APPEND_STRL(s_tmp, l_tmp);
01745                      /* %G already handles removing trailing zeros from the fractional part, yay */
01746                      efree(s_tmp);
01747                      TRACE_APPEND_STR(", ");
01748                      break;
01749               }
01750               case IS_ARRAY:
01751                      TRACE_APPEND_STR("Array, ");
01752                      break;
01753               case IS_OBJECT: {
01754                      char *class_name;
01755                      zend_uint class_name_len;
01756                      int dupl;
01757 
01758                      TRACE_APPEND_STR("Object(");
01759 
01760                      dupl = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
01761 
01762                      TRACE_APPEND_STRL(class_name, class_name_len);
01763                      if (!dupl) {
01764                             efree(class_name);
01765                      }
01766 
01767                      TRACE_APPEND_STR("), ");
01768                      break;
01769               }
01770               default:
01771                      break;
01772        }
01773        return ZEND_HASH_APPLY_KEEP;
01774 }
01775 /* }}} */
01776 
01777 static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
01778 {
01779        char *s_tmp, **str;
01780        int *len, *num;
01781        long line;
01782        HashTable *ht = Z_ARRVAL_PP(frame);
01783        zval **file, **tmp;
01784        uint * level;
01785 
01786        level = va_arg(args, uint *);
01787        str = va_arg(args, char**);
01788        len = va_arg(args, int*);
01789        num = va_arg(args, int*);
01790 
01791        if (!*level) {
01792               return ZEND_HASH_APPLY_KEEP;
01793        }
01794        --*level;
01795 
01796        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
01797        sprintf(s_tmp, "#%d ", (*num)++);
01798        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
01799        efree(s_tmp);
01800        if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
01801               if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
01802                      line = Z_LVAL_PP(tmp);
01803               } else {
01804                      line = 0;
01805               }
01806               s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
01807               sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
01808               TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
01809               efree(s_tmp);
01810        } else {
01811               TRACE_APPEND_STR("[internal function]: ");
01812        }
01813        TRACE_APPEND_KEY("class");
01814        TRACE_APPEND_KEY("type");
01815        TRACE_APPEND_KEY("function");
01816        TRACE_APPEND_CHR('(');
01817        if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
01818               int last_len = *len;
01819               zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
01820               if (last_len != *len) {
01821                      *len -= 2; /* remove last ', ' */
01822               }
01823        }
01824        TRACE_APPEND_STR(")\n");
01825        return ZEND_HASH_APPLY_KEEP;
01826 }
01827 /* }}} */
01828 #endif
01829 
01830 
01831 PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC)
01832 {
01833        zval *trace;
01834        char *res = estrdup(""), **str = &res, *s_tmp;
01835        int res_len = 0, *len = &res_len, num = 0;
01836        if (max_levels == 0) {
01837               max_levels = 99999;
01838        }
01839 
01840        MAKE_STD_ZVAL(trace);
01841        zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC);
01842 
01843        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);
01844        zval_ptr_dtor(&trace);
01845 
01846        if (max_levels) {
01847               s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
01848               sprintf(s_tmp, "#%d {main}", num);
01849               TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
01850               efree(s_tmp);
01851        }
01852 
01853        res[res_len] = '\0';
01854        *length = res_len;
01855 
01856        return res;
01857 }
01858 
01859 /*
01860  * Local variables:
01861  * tab-width: 4
01862  * c-basic-offset: 4
01863  * End:
01864  * vim600: noet sw=4 ts=4 fdm=marker
01865  * vim<600: noet sw=4 ts=4
01866  */