PageRenderTime 57ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/ext/mysqlnd/mysqlnd_debug.c

http://github.com/infusion/PHP
C | 1866 lines | 1474 code | 227 blank | 165 comment | 291 complexity | a69209b129b46f9e097c3ac4a438f58c MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file