1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Andrey Hristov <andrey@php.net> |
14 | Ulf Wendel <uw@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #include "php.h"
19 #include "mysqlnd.h"
20 #include "mysqlnd_priv.h"
21 #include "mysqlnd_debug.h"
22 #include "mysqlnd_wireprotocol.h"
23 #include "mysqlnd_statistics.h"
24
25 #define MYSQLND_DEBUG_MEMORY 1
26
27 static const char mysqlnd_emalloc_name[] = "_mysqlnd_emalloc";
28 static const char mysqlnd_pemalloc_name[] = "_mysqlnd_pemalloc";
29 static const char mysqlnd_ecalloc_name[] = "_mysqlnd_ecalloc";
30 static const char mysqlnd_pecalloc_name[] = "_mysqlnd_pecalloc";
31 static const char mysqlnd_erealloc_name[] = "_mysqlnd_erealloc";
32 static const char mysqlnd_perealloc_name[] = "_mysqlnd_perealloc";
33 static const char mysqlnd_efree_name[] = "_mysqlnd_efree";
34 static const char mysqlnd_pefree_name[] = "_mysqlnd_pefree";
35 static const char mysqlnd_pememdup_name[] = "_mysqlnd_pememdup";
36 static const char mysqlnd_pestrndup_name[] = "_mysqlnd_pestrndup";
37 static const char mysqlnd_pestrdup_name[] = "_mysqlnd_pestrdup";
38
39 PHPAPI const char * mysqlnd_debug_std_no_trace_funcs[] =
40 {
41 mysqlnd_emalloc_name,
42 mysqlnd_ecalloc_name,
43 mysqlnd_efree_name,
44 mysqlnd_erealloc_name,
45 mysqlnd_pemalloc_name,
46 mysqlnd_pecalloc_name,
47 mysqlnd_pefree_name,
48 mysqlnd_perealloc_name,
49 mysqlnd_pestrndup_name,
50 mysqlnd_read_header_name,
51 mysqlnd_read_body_name,
52 NULL /* must be always last */
53 };
54
55 #if MYSQLND_DEBUG_MEMORY
56
57
58 #if ZEND_DEBUG
59 #else
60 #define __zend_orig_filename "/unknown/unknown"
61 #define __zend_orig_lineno 0
62 #endif
63
64 #define EXTRA_SIZE ZEND_MM_ALIGNED_SIZE(sizeof(size_t))
65 #define REAL_SIZE(s) (collect_memory_statistics? (s) + EXTRA_SIZE : (s))
66 #define REAL_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) - EXTRA_SIZE) : (p))
67 #define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + EXTRA_SIZE) : (p))
68
69 /* {{{ _mysqlnd_emalloc */
_mysqlnd_emalloc(size_t size MYSQLND_MEM_D)70 static void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
71 {
72 void *ret;
73 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
74 TRACE_ALLOC_ENTER(mysqlnd_emalloc_name);
75 ret = emalloc_rel(REAL_SIZE(size));
76
77 TRACE_ALLOC_INF_FMT("size=%zu ptr=%p", size, ret);
78
79 if (collect_memory_statistics) {
80 *(size_t *) ret = size;
81 MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMOUNT, size);
82 }
83 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
84 }
85 /* }}} */
86
87
88 /* {{{ _mysqlnd_pemalloc */
_mysqlnd_pemalloc(size_t size,bool persistent MYSQLND_MEM_D)89 static void * _mysqlnd_pemalloc(size_t size, bool persistent MYSQLND_MEM_D)
90 {
91 void *ret;
92 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
93 TRACE_ALLOC_ENTER(mysqlnd_pemalloc_name);
94 ret = pemalloc_rel(REAL_SIZE(size), persistent);
95
96 TRACE_ALLOC_INF_FMT("size=%zu ptr=%p persistent=%u", size, ret, persistent);
97
98 if (collect_memory_statistics) {
99 enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT;
100 enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMOUNT:STAT_MEM_EMALLOC_AMOUNT;
101 *(size_t *) ret = size;
102 MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
103 }
104
105 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
106 }
107 /* }}} */
108
109
110 /* {{{ _mysqlnd_ecalloc */
_mysqlnd_ecalloc(unsigned int nmemb,size_t size MYSQLND_MEM_D)111 static void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
112 {
113 void *ret;
114 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
115 TRACE_ALLOC_ENTER(mysqlnd_ecalloc_name);
116 TRACE_ALLOC_INF_FMT("before: %zu", zend_memory_usage(FALSE));
117 ret = ecalloc_rel(nmemb, REAL_SIZE(size));
118
119 TRACE_ALLOC_INF_FMT("after : %zu", zend_memory_usage(FALSE));
120 TRACE_ALLOC_INF_FMT("size=%zu ptr=%p", size, ret);
121 if (collect_memory_statistics) {
122 *(size_t *) ret = size;
123 MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMOUNT, size);
124 }
125 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
126 }
127 /* }}} */
128
129
130 /* {{{ _mysqlnd_pecalloc */
_mysqlnd_pecalloc(unsigned int nmemb,size_t size,bool persistent MYSQLND_MEM_D)131 static void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, bool persistent MYSQLND_MEM_D)
132 {
133 void *ret;
134 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
135 TRACE_ALLOC_ENTER(mysqlnd_pecalloc_name);
136 ret = pecalloc_rel(nmemb, REAL_SIZE(size), persistent);
137
138 TRACE_ALLOC_INF_FMT("size=%zu ptr=%p", size, ret);
139
140 if (collect_memory_statistics) {
141 enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT;
142 enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMOUNT:STAT_MEM_ECALLOC_AMOUNT;
143 *(size_t *) ret = size;
144 MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
145 }
146
147 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
148 }
149 /* }}} */
150
151
152 /* {{{ _mysqlnd_erealloc */
_mysqlnd_erealloc(void * ptr,size_t new_size MYSQLND_MEM_D)153 static void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
154 {
155 void *ret;
156 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
157 size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
158 TRACE_ALLOC_ENTER(mysqlnd_erealloc_name);
159 TRACE_ALLOC_INF_FMT("ptr=%p old_size=%zu, new_size=%zu", ptr, old_size, new_size);
160 ret = erealloc_rel(REAL_PTR(ptr), REAL_SIZE(new_size));
161
162 TRACE_ALLOC_INF_FMT("new_ptr=%p", (char*)ret);
163 if (collect_memory_statistics) {
164 *(size_t *) ret = new_size;
165 MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMOUNT, new_size);
166 }
167 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
168 }
169 /* }}} */
170
171
172 /* {{{ _mysqlnd_perealloc */
_mysqlnd_perealloc(void * ptr,size_t new_size,bool persistent MYSQLND_MEM_D)173 static void * _mysqlnd_perealloc(void *ptr, size_t new_size, bool persistent MYSQLND_MEM_D)
174 {
175 void *ret;
176 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
177 size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
178 TRACE_ALLOC_ENTER(mysqlnd_perealloc_name);
179 TRACE_ALLOC_INF_FMT("ptr=%p old_size=%zu new_size=%zu persistent=%u", ptr, old_size, new_size, persistent);
180 ret = perealloc_rel(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
181
182 TRACE_ALLOC_INF_FMT("new_ptr=%p", (char*)ret);
183
184 if (collect_memory_statistics) {
185 enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT;
186 enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMOUNT:STAT_MEM_EREALLOC_AMOUNT;
187 *(size_t *) ret = new_size;
188 MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, new_size);
189 }
190 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
191 }
192 /* }}} */
193
194
195 /* {{{ _mysqlnd_efree */
_mysqlnd_efree(void * ptr MYSQLND_MEM_D)196 static void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
197 {
198 size_t free_amount = 0;
199 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
200 TRACE_ALLOC_ENTER(mysqlnd_efree_name);
201
202 #if PHP_DEBUG
203 {
204 char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
205 TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
206 }
207 #endif
208 TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
209
210 if (ptr) {
211 if (collect_memory_statistics) {
212 free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
213 TRACE_ALLOC_INF_FMT("ptr=%p size=%zu", ((char*)ptr) - sizeof(size_t), free_amount);
214 }
215 efree_rel(REAL_PTR(ptr));
216 }
217
218 if (collect_memory_statistics) {
219 MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EFREE_COUNT, 1, STAT_MEM_EFREE_AMOUNT, free_amount);
220 }
221 TRACE_ALLOC_VOID_RETURN;
222 }
223 /* }}} */
224
225
226 /* {{{ _mysqlnd_pefree */
_mysqlnd_pefree(void * ptr,bool persistent MYSQLND_MEM_D)227 static void _mysqlnd_pefree(void *ptr, bool persistent MYSQLND_MEM_D)
228 {
229 size_t free_amount = 0;
230 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
231 TRACE_ALLOC_ENTER(mysqlnd_pefree_name);
232
233 #if PHP_DEBUG
234 {
235 char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
236 TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
237 }
238 #endif
239 TRACE_ALLOC_INF_FMT("ptr=%p persistent=%u", ptr, persistent);
240
241 if (ptr) {
242 if (collect_memory_statistics) {
243 free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
244 TRACE_ALLOC_INF_FMT("ptr=%p size=%zu", ((char*)ptr) - sizeof(size_t), free_amount);
245 }
246 pefree_rel(REAL_PTR(ptr), persistent);
247 }
248
249 if (collect_memory_statistics) {
250 MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT, 1,
251 persistent? STAT_MEM_FREE_AMOUNT:STAT_MEM_EFREE_AMOUNT, free_amount);
252 }
253 TRACE_ALLOC_VOID_RETURN;
254 }
255 /* }}} */
256
257
258 /* {{{ _mysqlnd_pememdup */
_mysqlnd_pememdup(const char * const ptr,size_t length,bool persistent MYSQLND_MEM_D)259 static char * _mysqlnd_pememdup(const char * const ptr, size_t length, bool persistent MYSQLND_MEM_D)
260 {
261 char * ret;
262 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
263 TRACE_ALLOC_ENTER(mysqlnd_pememdup_name);
264
265 #if PHP_DEBUG
266 {
267 char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
268 TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
269 }
270 #endif
271 TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
272
273 ret = pemalloc_rel(REAL_SIZE(length + 1), persistent);
274 {
275 char * dest = (char *) FAKE_PTR(ret);
276 memcpy(dest, ptr, length);
277 }
278
279 if (collect_memory_statistics) {
280 *(size_t *) ret = length;
281 MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_DUP_COUNT : STAT_MEM_EDUP_COUNT);
282 }
283
284 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
285 }
286 /* }}} */
287
288
289 /* {{{ _mysqlnd_pestrndup */
_mysqlnd_pestrndup(const char * const ptr,size_t length,bool persistent MYSQLND_MEM_D)290 static char * _mysqlnd_pestrndup(const char * const ptr, size_t length, bool persistent MYSQLND_MEM_D)
291 {
292 char * ret;
293 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
294 TRACE_ALLOC_ENTER(mysqlnd_pestrndup_name);
295
296 #if PHP_DEBUG
297 {
298 char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
299 TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
300 }
301 #endif
302 TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
303
304 ret = pemalloc_rel(REAL_SIZE(length + 1), persistent);
305 {
306 size_t l = length;
307 char * p = (char *) ptr;
308 char * dest = (char *) FAKE_PTR(ret);
309 while (*p && l--) {
310 *dest++ = *p++;
311 }
312 *dest = '\0';
313 }
314
315 if (collect_memory_statistics) {
316 *(size_t *) ret = length;
317 MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRNDUP_COUNT : STAT_MEM_ESTRNDUP_COUNT);
318 }
319
320 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
321 }
322 /* }}} */
323
324
325 #define SMART_STR_START_SIZE 2048
326 #define SMART_STR_PREALLOC 512
327 #include "zend_smart_str.h"
328
329
330 /* {{{ _mysqlnd_pestrdup */
_mysqlnd_pestrdup(const char * const ptr,bool persistent MYSQLND_MEM_D)331 static char * _mysqlnd_pestrdup(const char * const ptr, bool persistent MYSQLND_MEM_D)
332 {
333 char * ret;
334 smart_str tmp_str = {0, 0};
335 const char * p = ptr;
336 bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
337 TRACE_ALLOC_ENTER(mysqlnd_pestrdup_name);
338 #if PHP_DEBUG
339 {
340 char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR);
341 TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno);
342 }
343 #endif
344 TRACE_ALLOC_INF_FMT("ptr=%p", ptr);
345 do {
346 smart_str_appendc(&tmp_str, *p);
347 } while (*p++);
348
349 ret = pemalloc_rel(REAL_SIZE(ZSTR_LEN(tmp_str.s)), persistent);
350 memcpy(FAKE_PTR(ret), ZSTR_VAL(tmp_str.s), ZSTR_LEN(tmp_str.s));
351
352 if (ret && collect_memory_statistics) {
353 *(size_t *) ret = ZSTR_LEN(tmp_str.s);
354 MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRDUP_COUNT : STAT_MEM_ESTRDUP_COUNT);
355 }
356 smart_str_free(&tmp_str);
357
358 TRACE_ALLOC_RETURN(FAKE_PTR(ret));
359 }
360 /* }}} */
361
362
363 #else
364
365 /* {{{ mysqlnd_zend_mm_emalloc */
mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)366 static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
367 {
368 return emalloc_rel(size);
369 }
370 /* }}} */
371
372
373 /* {{{ mysqlnd_zend_mm_pemalloc */
mysqlnd_zend_mm_pemalloc(size_t size,bool persistent MYSQLND_MEM_D)374 static void * mysqlnd_zend_mm_pemalloc(size_t size, bool persistent MYSQLND_MEM_D)
375 {
376 return pemalloc_rel(size, persistent);
377 }
378 /* }}} */
379
380
381 /* {{{ mysqlnd_zend_mm_ecalloc */
mysqlnd_zend_mm_ecalloc(unsigned int nmemb,size_t size MYSQLND_MEM_D)382 static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
383 {
384 return ecalloc_rel(nmemb, size);
385 }
386 /* }}} */
387
388
389 /* {{{ mysqlnd_zend_mm_pecalloc */
mysqlnd_zend_mm_pecalloc(unsigned int nmemb,size_t size,bool persistent MYSQLND_MEM_D)390 static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, bool persistent MYSQLND_MEM_D)
391 {
392 return pecalloc_rel(nmemb, size, persistent);
393 }
394 /* }}} */
395
396
397 /* {{{ mysqlnd_zend_mm_erealloc */
mysqlnd_zend_mm_erealloc(void * ptr,size_t new_size MYSQLND_MEM_D)398 static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
399 {
400 return erealloc_rel(ptr, new_size);
401 }
402 /* }}} */
403
404
405 /* {{{ mysqlnd_zend_mm_perealloc */
mysqlnd_zend_mm_perealloc(void * ptr,size_t new_size,bool persistent MYSQLND_MEM_D)406 static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, bool persistent MYSQLND_MEM_D)
407 {
408 return perealloc_rel(ptr, new_size, persistent);
409 }
410 /* }}} */
411
412
413 /* {{{ mysqlnd_zend_mm_efree */
mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)414 static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
415 {
416 efree_rel(ptr);
417 }
418 /* }}} */
419
420
421 /* {{{ mysqlnd_zend_mm_pefree */
mysqlnd_zend_mm_pefree(void * ptr,bool persistent MYSQLND_MEM_D)422 static void mysqlnd_zend_mm_pefree(void * ptr, bool persistent MYSQLND_MEM_D)
423 {
424 pefree_rel(ptr, persistent);
425 }
426 /* }}} */
427
428
429 /* {{{ mysqlnd_zend_mm_pememdup */
mysqlnd_zend_mm_pememdup(const char * const ptr,size_t length,bool persistent MYSQLND_MEM_D)430 static char * mysqlnd_zend_mm_pememdup(const char * const ptr, size_t length, bool persistent MYSQLND_MEM_D)
431 {
432 char * dest = pemalloc_rel(length, persistent);
433 if (dest) {
434 memcpy(dest, ptr, length);
435 }
436 return dest;
437 }
438 /* }}} */
439
440
441 /* {{{ mysqlnd_zend_mm_pestrndup */
mysqlnd_zend_mm_pestrndup(const char * const ptr,size_t length,bool persistent MYSQLND_MEM_D)442 static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, bool persistent MYSQLND_MEM_D)
443 {
444 return persistent? zend_strndup(ptr, length ) : estrndup_rel(ptr, length);
445 }
446 /* }}} */
447
448
449 /* {{{ mysqlnd_zend_mm_pestrdup */
mysqlnd_zend_mm_pestrdup(const char * const ptr,bool persistent MYSQLND_MEM_D)450 static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, bool persistent MYSQLND_MEM_D)
451 {
452 return pestrdup_rel(ptr, persistent);
453 }
454 /* }}} */
455
456 #endif /* MYSQLND_DEBUG_MEMORY */
457
458
459 PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator =
460 {
461 #if MYSQLND_DEBUG_MEMORY == 1
462 _mysqlnd_emalloc,
463 _mysqlnd_pemalloc,
464 _mysqlnd_ecalloc,
465 _mysqlnd_pecalloc,
466 _mysqlnd_erealloc,
467 _mysqlnd_perealloc,
468 _mysqlnd_efree,
469 _mysqlnd_pefree,
470 _mysqlnd_pememdup,
471 _mysqlnd_pestrndup,
472 _mysqlnd_pestrdup
473 #else
474 mysqlnd_zend_mm_emalloc,
475 mysqlnd_zend_mm_pemalloc,
476 mysqlnd_zend_mm_ecalloc,
477 mysqlnd_zend_mm_pecalloc,
478 mysqlnd_zend_mm_erealloc,
479 mysqlnd_zend_mm_perealloc,
480 mysqlnd_zend_mm_efree,
481 mysqlnd_zend_mm_pefree,
482 mysqlnd_zend_mm_pememdup,
483 mysqlnd_zend_mm_pestrndup,
484 mysqlnd_zend_mm_pestrdup
485 #endif
486 };
487