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

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