root/ext/mysqlnd/mysqlnd_debug.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. MYSQLND_METHOD
  2. MYSQLND_METHOD
  3. MYSQLND_CLASS_METHODS_START
  4. _mysqlnd_debug
  5. mysqlnd_debug_trace_plugin_register

   1 /*
   2   +----------------------------------------------------------------------+
   3   | PHP Version 5                                                        |
   4   +----------------------------------------------------------------------+
   5   | Copyright (c) 2006-2016 The PHP Group                                |
   6   +----------------------------------------------------------------------+
   7   | This source file is subject to version 3.01 of the PHP license,      |
   8   | that is bundled with this package in the file LICENSE, and is        |
   9   | available through the world-wide-web at the following url:           |
  10   | http://www.php.net/license/3_01.txt                                  |
  11   | If you did not receive a copy of the PHP license and are unable to   |
  12   | obtain it through the world-wide-web, please send a note to          |
  13   | license@php.net so we can mail you a copy immediately.               |
  14   +----------------------------------------------------------------------+
  15   | Authors: Georg Richter <georg@mysql.com>                             |
  16   |          Andrey Hristov <andrey@mysql.com>                           |
  17   |          Ulf Wendel <uwendel@mysql.com>                              |
  18   +----------------------------------------------------------------------+
  19 */
  20 
  21 /* $Id$ */
  22 
  23 #include "php.h"
  24 #include "mysqlnd.h"
  25 #include "mysqlnd_priv.h"
  26 #include "mysqlnd_debug.h"
  27 
  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 
 132         message_line_len = mnd_sprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
 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;
 141         mnd_sprintf_free(message_line);
 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);
 231         mnd_vsprintf(&buffer, 0, format, args);
 232         va_end(args);
 233 
 234         message_line_len = mnd_sprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
 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);
 241         mnd_sprintf_free(buffer);
 242         ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
 243         mnd_sprintf_free(message_line);
 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);
 502         free(self);
 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 {
 519         unsigned int mode_len, i;
 520         enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER;
 521 
 522         mode_len = mode? strlen(mode) : 0;
 523 
 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 {
 727         MYSQLND_DEBUG *ret = calloc(1, sizeof(MYSQLND_DEBUG));
 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 {
 749 #if PHP_DEBUG
 750         MYSQLND_DEBUG * dbg = MYSQLND_G(dbg);
 751         if (!dbg) {
 752                 struct st_mysqlnd_plugin_trace_log * trace_log_plugin = mysqlnd_plugin_find("debug_trace");
 753                 if (trace_log_plugin) {
 754                         dbg = trace_log_plugin->methods.trace_instance_init(mysqlnd_debug_std_no_trace_funcs TSRMLS_CC);
 755                         if (!dbg) {
 756                                 return;
 757                         }
 758                         MYSQLND_G(dbg) = dbg;
 759                 }
 760         }
 761         if (dbg) {
 762                 dbg->m->close(dbg);
 763                 dbg->m->set_mode(dbg, mode);
 764                 while (zend_stack_count(&dbg->call_stack)) {
 765                         zend_stack_del_top(&dbg->call_stack);
 766                 }
 767                 while (zend_stack_count(&dbg->call_time_stack)) {
 768                         zend_stack_del_top(&dbg->call_time_stack);
 769                 }
 770         }
 771 #endif
 772 }
 773 /* }}} */
 774 
 775 
 776 static struct st_mysqlnd_plugin_trace_log mysqlnd_plugin_trace_log_plugin =
 777 {
 778         {
 779                 MYSQLND_PLUGIN_API_VERSION,
 780                 "debug_trace",
 781                 MYSQLND_VERSION_ID,
 782                 MYSQLND_VERSION,
 783                 "PHP License 3.01",
 784                 "Andrey Hristov <andrey@mysql.com>,  Ulf Wendel <uwendel@mysql.com>, Georg Richter <georg@mysql.com>",
 785                 {
 786                         NULL, /* no statistics , will be filled later if there are some */
 787                         NULL, /* no statistics */
 788                 },
 789                 {
 790                         NULL /* plugin shutdown */
 791                 }
 792         },
 793         {/* methods */
 794                 mysqlnd_debug_init,
 795                 mysqlnd_get_backtrace
 796         }
 797 };
 798 
 799 
 800 /* {{{ mysqlnd_debug_trace_plugin_register */
 801 void
 802 mysqlnd_debug_trace_plugin_register(TSRMLS_D)
 803 {
 804         mysqlnd_plugin_register_ex((struct st_mysqlnd_plugin_header *) &mysqlnd_plugin_trace_log_plugin TSRMLS_CC);
 805 }
 806 /* }}} */
 807 
 808 
 809 /*
 810  * Local variables:
 811  * tab-width: 4
 812  * c-basic-offset: 4
 813  * End:
 814  * vim600: noet sw=4 ts=4 fdm=marker
 815  * vim<600: noet sw=4 ts=4
 816  */

/* [<][>][^][v][top][bottom][index][help] */