Skip to content

Commit 93daa73

Browse files
committed
Make native tracking opt-in
1 parent ced7a54 commit 93daa73

File tree

5 files changed

+103
-22
lines changed

5 files changed

+103
-22
lines changed

README.md

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,18 @@ Profiling is enabled at request startup when one of these is true:
6363
* `$_GET["MEMPROF_PROFILE"]` is non-empty
6464
* `$_POST["MEMPROF_PROFILE"]` is non-empty
6565

66-
### 2. Dumping the profile
66+
The `memprof_enabled()` function can be called to check whether profiling is
67+
currently enabled.
6768

68-
Once profiling is enabled, the program must call ``memprof_dump_callgrind()`` or
69-
one it its variants to dump the memory profile.
69+
### 2. Dumping the profile
7070

71-
This can be done at anytime during the program, ideally when the leak is large,
72-
so that it will be more visible in the profile.
71+
When profiling is enabled, and once the program has reached a large memory usage,
72+
call ``memprof_dump_callgrind()`` or one it its variants to save the full
73+
profiling information to a file.
7374

74-
This can be done multiple times during the same execution, but this is not necessary.
75+
* Calling the function multiple times is not necessary
76+
* Waiting for a high memory usage before saving the profile makes it easier to
77+
find a leak
7578

7679
### 3. Visualizing the profile
7780

@@ -127,12 +130,32 @@ Setting a POST field works as well:
127130
curl -d MEMPROF_PROFILE=1 http://127.0.0.1/test.php
128131
```
129132

133+
### Profiling native allocations
134+
135+
Memprof doesn't track native allocations by default, but this can be enabled
136+
by setting `MEMPROF_PROFILE` to `native`.
137+
138+
Native allocations are the allocations made outside of PHP's own memory
139+
allocator. Typically, external libraries such as libxml2 (used in the DOM
140+
extension) make native allocations. PHP can also make native allocations for
141+
persistent resources.
142+
143+
Enabling native allocation tracking will profile these allocations in addition
144+
to PHP's own allocations.
145+
146+
Note that when native tracking is enabled, the program will crash if a native
147+
library uses threads, because the underlying hooks are not thread safe.
148+
130149
## Functions documentation
131150

132151
### memprof_enabled()
133152

134153
Returns whether memory profiling is currently enabled (see above).
135154

155+
### memprof_enabled_flags()
156+
157+
Returns whether memory profiling and native profiling are enabled (see above).
158+
136159
### memprof_dump_callgrind(resource $stream)
137160

138161
Dumps the current profile in callgrind format. The result can be visualized with tools such as
@@ -265,7 +288,6 @@ Example output:
265288

266289
## Troubleshooting
267290

268-
* If you are experiencing crashes, try disabling malloc hooks by setting HAVE_MALLOC_HOOKS to 0 in config.h after running configure; then run ``make clean && make && make install``. (Using malloc hooks may crash if some other extension uses threads internally.)
269291
* The extensions may conflict with xdebug, blackfire, or other extensions. If that's the case for you, please report it.
270292

271293
## PHP versions
@@ -274,10 +296,6 @@ The current branch supports PHP 7.1 to PHP 8.
274296

275297
The php5 branch supports PHP 5.
276298

277-
## TODO
278-
279-
* Thread-safe malloc hooks
280-
281299
## How it works
282300

283301
See [INTERNALS.md][7]

memprof.c

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include <assert.h>
3434

3535
#define MEMPROF_ENV_PROFILE "MEMPROF_PROFILE"
36+
#define MEMPROF_FLAG_NATIVE "native"
37+
3638
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
3739

3840
#ifdef ZTS
@@ -188,6 +190,9 @@ static void (*old_zend_execute_internal)(zend_execute_data *execute_data_ptr, zv
188190

189191
static PHP_INI_MH((*origOnChangeMemoryLimit)) = NULL;
190192

193+
#define MEMPROF_ENABLED (1<<0)
194+
#define MEMPROF_ENABLED_NATIVE (1<<1)
195+
191196
static int memprof_enabled = 0;
192197
static int memprof_dumped = 0;
193198
static int track_mallocs = 0;
@@ -732,8 +737,10 @@ static PHP_INI_MH(OnChangeMemoryLimit)
732737
return SUCCESS;
733738
}
734739

735-
static void memprof_enable()
740+
static void memprof_enable(int flags)
736741
{
742+
assert(flags & MEMPROF_ENABLED);
743+
737744
alloc_buckets_init(&current_alloc_buckets);
738745

739746
init_frame(&root_frame, &root_frame, "root", sizeof("root")-1);
@@ -742,10 +749,12 @@ static void memprof_enable()
742749
current_frame = &root_frame;
743750
current_alloc_list = &root_frame.allocs;
744751

745-
MALLOC_HOOK_SAVE_OLD();
746-
MALLOC_HOOK_SET_OWN();
752+
if (flags & MEMPROF_ENABLED_NATIVE) {
753+
MALLOC_HOOK_SAVE_OLD();
754+
MALLOC_HOOK_SET_OWN();
755+
}
747756

748-
memprof_enabled = 1;
757+
memprof_enabled = flags;
749758
memprof_dumped = 0;
750759

751760
if (is_zend_mm()) {
@@ -781,7 +790,9 @@ static void memprof_disable()
781790
free(zheap);
782791
}
783792

784-
MALLOC_HOOK_RESTORE_OLD();
793+
if (memprof_enabled & MEMPROF_ENABLED_NATIVE) {
794+
MALLOC_HOOK_RESTORE_OLD();
795+
}
785796

786797
memprof_enabled = 0;
787798

@@ -852,12 +863,22 @@ static zend_string* read_env_get_post(char *name, size_t len)
852863

853864
static int should_autostart()
854865
{
866+
char *saveptr;
867+
const char *delim = ",";
868+
char *flag;
869+
855870
zend_string *value = read_env_get_post(MEMPROF_ENV_PROFILE, strlen(MEMPROF_ENV_PROFILE));
856871
if (value == NULL) {
857872
return 0;
858873
}
859874

860-
int autostart = ZSTR_LEN(value) > 0;
875+
int autostart = ZSTR_LEN(value) > 0 ? MEMPROF_ENABLED : 0;
876+
877+
for (flag = strtok_r(ZSTR_VAL(value), delim, &saveptr); flag != NULL; flag = strtok_r(NULL, delim, &saveptr)) {
878+
if (HAVE_MALLOC_HOOKS && strcmp(MEMPROF_FLAG_NATIVE, flag) == 0) {
879+
autostart |= MEMPROF_ENABLED_NATIVE;
880+
}
881+
}
861882

862883
zend_string_release(value);
863884

@@ -913,6 +934,7 @@ ZEND_END_ARG_INFO()
913934
*/
914935
const zend_function_entry memprof_functions[] = {
915936
PHP_FE(memprof_enabled, arginfo_void)
937+
PHP_FE(memprof_enabled_flags, arginfo_void)
916938
PHP_FE(memprof_enable, arginfo_void)
917939
PHP_FE(memprof_disable, arginfo_void)
918940
PHP_FE(memprof_dump_array, arginfo_void)
@@ -1008,9 +1030,11 @@ PHP_MSHUTDOWN_FUNCTION(memprof)
10081030
*/
10091031
PHP_RINIT_FUNCTION(memprof)
10101032
{
1011-
if (should_autostart()) {
1033+
int flags = should_autostart();
1034+
1035+
if (flags) {
10121036
disable_opcache();
1013-
memprof_enable();
1037+
memprof_enable(flags);
10141038
}
10151039

10161040
return SUCCESS;
@@ -1036,6 +1060,7 @@ PHP_MINFO_FUNCTION(memprof)
10361060
php_info_print_table_start();
10371061
php_info_print_table_header(2, "memprof support", "enabled");
10381062
php_info_print_table_header(2, "memprof version", PHP_MEMPROF_VERSION);
1063+
php_info_print_table_header(2, "memprof native malloc support", HAVE_MALLOC_HOOKS ? "yes" : "no");
10391064
#if MEMPROF_DEBUG
10401065
php_info_print_table_header(2, "debug build", "Yes");
10411066
#endif
@@ -1444,7 +1469,7 @@ PHP_FUNCTION(memprof_enable)
14441469

14451470
zend_error(E_WARNING, "Calling memprof_enable() manually may not work as expected because of PHP optimizations. Prefer using MEMPROF_PROFILE=1 as environment variable, GET, or POST");
14461471

1447-
memprof_enable();
1472+
memprof_enable(MEMPROF_ENABLED);
14481473

14491474
RETURN_TRUE;
14501475
}
@@ -1469,14 +1494,28 @@ PHP_FUNCTION(memprof_disable)
14691494
}
14701495
/* }}} */
14711496

1472-
/* {{{ proto bool memprof_disabled()
1497+
/* {{{ proto bool memprof_enabled()
14731498
Returns whether memprof is enabled */
14741499
PHP_FUNCTION(memprof_enabled)
14751500
{
14761501
if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
14771502
return;
14781503
}
14791504

1480-
RETURN_BOOL(memprof_enabled);
1505+
RETURN_BOOL(memprof_enabled & MEMPROF_ENABLED);
1506+
}
1507+
/* }}} */
1508+
1509+
/* {{{ proto array memprof_enabled_flags()
1510+
Returns whether memprof is enabled */
1511+
PHP_FUNCTION(memprof_enabled_flags)
1512+
{
1513+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
1514+
return;
1515+
}
1516+
1517+
array_init(return_value);
1518+
add_assoc_bool(return_value, "enabled", (memprof_enabled & MEMPROF_ENABLED) != 0);
1519+
add_assoc_bool(return_value, "native", (memprof_enabled & MEMPROF_ENABLED_NATIVE) != 0);
14811520
}
14821521
/* }}} */

php_memprof.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@ PHP_FUNCTION(memprof_memory_get_peak_usage);
5151
PHP_FUNCTION(memprof_enable);
5252
PHP_FUNCTION(memprof_disable);
5353
PHP_FUNCTION(memprof_enabled);
54+
PHP_FUNCTION(memprof_enabled_flags);
5455

5556
#endif /* PHP_MEMPROF_H */

tests/004.phpt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ MEMPROF_PROFILE=1
55
--FILE--
66
<?php
77
var_dump(memprof_enabled());
8+
var_dump(memprof_enabled_flags());
89
memprof_dump_array();
910
--EXPECT--
1011
bool(true)
12+
array(2) {
13+
["enabled"]=>
14+
bool(true)
15+
["native"]=>
16+
bool(false)
17+
}

tests/007.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Enable native profiling
3+
--ENV--
4+
MEMPROF_PROFILE=native
5+
--FILE--
6+
<?php
7+
var_dump(memprof_enabled());
8+
var_dump(memprof_enabled_flags());
9+
--EXPECT--
10+
bool(true)
11+
array(2) {
12+
["enabled"]=>
13+
bool(true)
14+
["native"]=>
15+
bool(true)
16+
}

0 commit comments

Comments
 (0)