Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,9 @@ jobs:
werror: 1
xdebug: '3.2.2'

- php: '8.1.0'
os: 'ubuntu-20.04'
expect_native: 1
werror: 1
xdebug: '3.1.2'

- php: '8.1.0'
- php: '8.2.0'
os: 'macos-13'
xdebug: '3.1.2'
xdebug: '3.2.2'

runs-on: ${{ matrix.os }}
continue-on-error: ${{ !!matrix.experimental }}
Expand Down
113 changes: 56 additions & 57 deletions memprof.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "php_memprof.h"
#include "zend_extensions.h"
#include "zend_exceptions.h"
#include "zend_observer.h"
#include <stdint.h>
#include <sys/queue.h>
#include "util.h"
Expand Down Expand Up @@ -198,10 +199,6 @@ static void (*old_free_hook) (void *ptr, const void *caller) = NULL;
static void * (*old_memalign_hook) (size_t alignment, size_t size, const void *caller) = NULL;
#endif /* HAVE_MALLOC_HOOKS */

static void (*old_zend_execute)(zend_execute_data *execute_data);
static void (*old_zend_execute_internal)(zend_execute_data *execute_data_ptr, zval *return_value);
#define zend_execute_fn zend_execute_ex

static void (*old_zend_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
static void (*rinit_zend_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
static zend_bool zend_error_cb_overridden;
Expand All @@ -214,6 +211,7 @@ static int track_mallocs = 0;

static frame root_frame;
static frame * current_frame;
static bool current_frame_ignored;
static alloc_list_head * current_alloc_list;
static alloc_buckets current_alloc_buckets;

Expand Down Expand Up @@ -738,70 +736,77 @@ static void memprof_late_override_error_cb(void) {
zend_error_cb_overridden = 1;
}

static void memprof_zend_execute(zend_execute_data *execute_data)
{
static void memprof_observer_fcall_begin_handler(zend_execute_data *execute_data) {
ZEND_ASSERT(MEMPROF_G(profile_flags).enabled);

if (UNEXPECTED(!zend_error_cb_overridden)) {
memprof_late_override_error_cb();
}

#ifdef MEMPROF_DEBUG_OBSERVER
if (execute_data->func->common.function_name) {
fprintf(stderr, "fcall begin %s\n", ZSTR_VAL(execute_data->func->common.function_name));
}
#endif
if (execute_data->func->type == ZEND_INTERNAL_FUNCTION) {
if (&execute_data->func->internal_function == &zend_pass_function) {
current_frame_ignored = true;
} else if (execute_data->func->common.function_name) {
zend_string * name = execute_data->func->common.function_name;
if (ZSTR_LEN(name) == sizeof("call_user_func")-1
&& 0 == memcmp(name, "call_user_func", sizeof("call_user_func")))
{
current_frame_ignored = true;
} else if (ZSTR_LEN(name) == sizeof("call_user_func_array")-1
&& 0 == memcmp(name, "call_user_func_array", sizeof("call_user_func_array")))
{
current_frame_ignored = true;
}
} else {
current_frame_ignored = false;
}
} else {
current_frame_ignored = false;
}

if (current_frame_ignored) {
return;
}

WITHOUT_MALLOC_TRACKING {

current_frame = get_or_create_frame(execute_data, current_frame);
current_frame->calls++;
current_alloc_list = &current_frame->allocs;

} END_WITHOUT_MALLOC_TRACKING;

old_zend_execute(execute_data);

if (MEMPROF_G(profile_flags).enabled) {
current_frame = current_frame->prev;
current_alloc_list = &current_frame->allocs;
}
}

static void memprof_zend_execute_internal(zend_execute_data *execute_data_ptr, zval *return_value)
{
int ignore = 0;
static void memprof_observer_fcall_end_handler(zend_execute_data *execute_data, zval *retval) {
ZEND_ASSERT(MEMPROF_G(profile_flags).enabled);

if (UNEXPECTED(!zend_error_cb_overridden)) {
memprof_late_override_error_cb();
#ifdef MEMPROF_DEBUG_OBSERVER
if (execute_data->func->common.function_name) {
fprintf(stderr, "fcall end %s\n", ZSTR_VAL(execute_data->func->common.function_name));
}
#endif

if (&execute_data_ptr->func->internal_function == &zend_pass_function) {
ignore = 1;
} else if (execute_data_ptr->func->common.function_name) {
zend_string * name = execute_data_ptr->func->common.function_name;
if (ZSTR_LEN(name) == sizeof("call_user_func")-1
&& 0 == memcmp(name, "call_user_func", sizeof("call_user_func")))
{
ignore = 1;
} else if (ZSTR_LEN(name) == sizeof("call_user_func_array")-1
&& 0 == memcmp(name, "call_user_func_array", sizeof("call_user_func_array")))
{
ignore = 1;
}
if (current_frame_ignored) {
current_frame_ignored = false;
return;
}
current_frame = current_frame->prev;
current_alloc_list = &current_frame->allocs;
}

WITHOUT_MALLOC_TRACKING {

if (!ignore) {
current_frame = get_or_create_frame(execute_data_ptr, current_frame);
current_frame->calls++;
current_alloc_list = &current_frame->allocs;
}

} END_WITHOUT_MALLOC_TRACKING;

if (!old_zend_execute_internal) {
execute_internal(execute_data_ptr, return_value);
static zend_observer_fcall_handlers memprof_observer_fcall_init(zend_execute_data *execute_data) {
if (MEMPROF_G(profile_flags).enabled) {
return (zend_observer_fcall_handlers){
.begin = memprof_observer_fcall_begin_handler,
.end = memprof_observer_fcall_end_handler,
};
} else {
old_zend_execute_internal(execute_data_ptr, return_value);
}

if (!ignore && MEMPROF_G(profile_flags).enabled) {
current_frame = current_frame->prev;
current_alloc_list = &current_frame->allocs;
return (zend_observer_fcall_handlers){0};
}
}

Expand Down Expand Up @@ -973,21 +978,13 @@ static void memprof_enable(memprof_profile_flags * pf)
orig_zheap = NULL;
}

old_zend_execute = zend_execute_fn;
old_zend_execute_internal = zend_execute_internal;
zend_execute_fn = memprof_zend_execute;
zend_execute_internal = memprof_zend_execute_internal;

track_mallocs = 1;
}

static void memprof_disable(void)
{
track_mallocs = 0;

zend_execute_fn = old_zend_execute;
zend_execute_internal = old_zend_execute_internal;

if (zheap) {
zend_mm_set_heap(orig_zheap);
free(zheap);
Expand Down Expand Up @@ -1200,6 +1197,8 @@ PHP_MINIT_FUNCTION(memprof)

REGISTER_INI_ENTRIES();

zend_observer_fcall_register(memprof_observer_fcall_init);

entry = zend_hash_str_find_ptr(EG(ini_directives), "memory_limit", sizeof("memory_limit")-1);

if (entry == NULL) {
Expand Down
36 changes: 34 additions & 2 deletions tests/002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,23 @@ array(6) {
["calls"]=>
int(1)
["called_functions"]=>
array(0) {
array(1) {
["time"]=>
array(6) {
["memory_size"]=>
int(0)
["blocks_count"]=>
int(0)
["memory_size_inclusive"]=>
int(0)
["blocks_count_inclusive"]=>
int(0)
["calls"]=>
int(1)
["called_functions"]=>
array(0) {
}
}
}
}
["memprof_dump_array"]=>
Expand Down Expand Up @@ -158,7 +174,23 @@ array(6) {
["calls"]=>
int(1)
["called_functions"]=>
array(0) {
array(1) {
["time"]=>
array(6) {
["memory_size"]=>
int(0)
["blocks_count"]=>
int(0)
["memory_size_inclusive"]=>
int(0)
["blocks_count_inclusive"]=>
int(0)
["calls"]=>
int(1)
["called_functions"]=>
array(0) {
}
}
}
}
["memprof_dump_array"]=>
Expand Down
9 changes: 9 additions & 0 deletions tests/003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@ $b = Eater::eat();
memprof_dump_callgrind(STDOUT);

--EXPECTF--

version: 1
cmd: unknown
positions: line
events: Memory_Size_(bytes) BlocksCount

fl=php:internal
fn=time
1 0 0

fl=%scommon.php
fn=require %scommon.php
1 0 0
cfl=php:internal
cfn=time
calls=1 1
1 0 0

fl=php:internal
fn=str_repeat
Expand Down
3 changes: 3 additions & 0 deletions tests/common.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?php

// Some statement to prevent inclusion of this file from being optimized out
time();

function eat()
{
return str_repeat('X', 3 * 1024 * 1024);
Expand Down
9 changes: 5 additions & 4 deletions tests/dump-pprof.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ binary=php://stdout
0x0000000000000008 root
0x0000000000000010 require %sdump-pprof.php
0x0000000000000018 require %scommon.php
0x0000000000000020 eat
0x0000000000000028 str_repeat
0x0000000000000030 Eater::eat
0x0000000000000038 memprof_dump_pprof
0x0000000000000020 time
0x0000000000000028 eat
0x0000000000000030 str_repeat
0x0000000000000038 Eater::eat
0x0000000000000040 memprof_dump_pprof
---
--- profile
%a