Skip to content

Commit 4867dee

Browse files
authored
Migrate to zend observer API for function calls (#109)
1 parent 281dea6 commit 4867dee

File tree

6 files changed

+109
-71
lines changed

6 files changed

+109
-71
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,9 @@ jobs:
3030
werror: 1
3131
xdebug: '3.2.2'
3232

33-
- php: '8.1.0'
34-
os: 'ubuntu-20.04'
35-
expect_native: 1
36-
werror: 1
37-
xdebug: '3.1.2'
38-
39-
- php: '8.1.0'
33+
- php: '8.2.0'
4034
os: 'macos-13'
41-
xdebug: '3.1.2'
35+
xdebug: '3.2.2'
4236

4337
runs-on: ${{ matrix.os }}
4438
continue-on-error: ${{ !!matrix.experimental }}

memprof.c

Lines changed: 56 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "php_memprof.h"
2424
#include "zend_extensions.h"
2525
#include "zend_exceptions.h"
26+
#include "zend_observer.h"
2627
#include <stdint.h>
2728
#include <sys/queue.h>
2829
#include "util.h"
@@ -198,10 +199,6 @@ static void (*old_free_hook) (void *ptr, const void *caller) = NULL;
198199
static void * (*old_memalign_hook) (size_t alignment, size_t size, const void *caller) = NULL;
199200
#endif /* HAVE_MALLOC_HOOKS */
200201

201-
static void (*old_zend_execute)(zend_execute_data *execute_data);
202-
static void (*old_zend_execute_internal)(zend_execute_data *execute_data_ptr, zval *return_value);
203-
#define zend_execute_fn zend_execute_ex
204-
205202
static void (*old_zend_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
206203
static void (*rinit_zend_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
207204
static zend_bool zend_error_cb_overridden;
@@ -214,6 +211,7 @@ static int track_mallocs = 0;
214211

215212
static frame root_frame;
216213
static frame * current_frame;
214+
static bool current_frame_ignored;
217215
static alloc_list_head * current_alloc_list;
218216
static alloc_buckets current_alloc_buckets;
219217

@@ -738,70 +736,77 @@ static void memprof_late_override_error_cb(void) {
738736
zend_error_cb_overridden = 1;
739737
}
740738

741-
static void memprof_zend_execute(zend_execute_data *execute_data)
742-
{
739+
static void memprof_observer_fcall_begin_handler(zend_execute_data *execute_data) {
740+
ZEND_ASSERT(MEMPROF_G(profile_flags).enabled);
741+
743742
if (UNEXPECTED(!zend_error_cb_overridden)) {
744743
memprof_late_override_error_cb();
745744
}
746745

746+
#ifdef MEMPROF_DEBUG_OBSERVER
747+
if (execute_data->func->common.function_name) {
748+
fprintf(stderr, "fcall begin %s\n", ZSTR_VAL(execute_data->func->common.function_name));
749+
}
750+
#endif
751+
if (execute_data->func->type == ZEND_INTERNAL_FUNCTION) {
752+
if (&execute_data->func->internal_function == &zend_pass_function) {
753+
current_frame_ignored = true;
754+
} else if (execute_data->func->common.function_name) {
755+
zend_string * name = execute_data->func->common.function_name;
756+
if (ZSTR_LEN(name) == sizeof("call_user_func")-1
757+
&& 0 == memcmp(name, "call_user_func", sizeof("call_user_func")))
758+
{
759+
current_frame_ignored = true;
760+
} else if (ZSTR_LEN(name) == sizeof("call_user_func_array")-1
761+
&& 0 == memcmp(name, "call_user_func_array", sizeof("call_user_func_array")))
762+
{
763+
current_frame_ignored = true;
764+
}
765+
} else {
766+
current_frame_ignored = false;
767+
}
768+
} else {
769+
current_frame_ignored = false;
770+
}
771+
772+
if (current_frame_ignored) {
773+
return;
774+
}
775+
747776
WITHOUT_MALLOC_TRACKING {
748777

749778
current_frame = get_or_create_frame(execute_data, current_frame);
750779
current_frame->calls++;
751780
current_alloc_list = &current_frame->allocs;
752781

753782
} END_WITHOUT_MALLOC_TRACKING;
754-
755-
old_zend_execute(execute_data);
756-
757-
if (MEMPROF_G(profile_flags).enabled) {
758-
current_frame = current_frame->prev;
759-
current_alloc_list = &current_frame->allocs;
760-
}
761783
}
762784

763-
static void memprof_zend_execute_internal(zend_execute_data *execute_data_ptr, zval *return_value)
764-
{
765-
int ignore = 0;
785+
static void memprof_observer_fcall_end_handler(zend_execute_data *execute_data, zval *retval) {
786+
ZEND_ASSERT(MEMPROF_G(profile_flags).enabled);
766787

767-
if (UNEXPECTED(!zend_error_cb_overridden)) {
768-
memprof_late_override_error_cb();
788+
#ifdef MEMPROF_DEBUG_OBSERVER
789+
if (execute_data->func->common.function_name) {
790+
fprintf(stderr, "fcall end %s\n", ZSTR_VAL(execute_data->func->common.function_name));
769791
}
792+
#endif
770793

771-
if (&execute_data_ptr->func->internal_function == &zend_pass_function) {
772-
ignore = 1;
773-
} else if (execute_data_ptr->func->common.function_name) {
774-
zend_string * name = execute_data_ptr->func->common.function_name;
775-
if (ZSTR_LEN(name) == sizeof("call_user_func")-1
776-
&& 0 == memcmp(name, "call_user_func", sizeof("call_user_func")))
777-
{
778-
ignore = 1;
779-
} else if (ZSTR_LEN(name) == sizeof("call_user_func_array")-1
780-
&& 0 == memcmp(name, "call_user_func_array", sizeof("call_user_func_array")))
781-
{
782-
ignore = 1;
783-
}
794+
if (current_frame_ignored) {
795+
current_frame_ignored = false;
796+
return;
784797
}
798+
current_frame = current_frame->prev;
799+
current_alloc_list = &current_frame->allocs;
800+
}
785801

786-
WITHOUT_MALLOC_TRACKING {
787-
788-
if (!ignore) {
789-
current_frame = get_or_create_frame(execute_data_ptr, current_frame);
790-
current_frame->calls++;
791-
current_alloc_list = &current_frame->allocs;
792-
}
793-
794-
} END_WITHOUT_MALLOC_TRACKING;
795-
796-
if (!old_zend_execute_internal) {
797-
execute_internal(execute_data_ptr, return_value);
802+
static zend_observer_fcall_handlers memprof_observer_fcall_init(zend_execute_data *execute_data) {
803+
if (MEMPROF_G(profile_flags).enabled) {
804+
return (zend_observer_fcall_handlers){
805+
.begin = memprof_observer_fcall_begin_handler,
806+
.end = memprof_observer_fcall_end_handler,
807+
};
798808
} else {
799-
old_zend_execute_internal(execute_data_ptr, return_value);
800-
}
801-
802-
if (!ignore && MEMPROF_G(profile_flags).enabled) {
803-
current_frame = current_frame->prev;
804-
current_alloc_list = &current_frame->allocs;
809+
return (zend_observer_fcall_handlers){0};
805810
}
806811
}
807812

@@ -973,21 +978,13 @@ static void memprof_enable(memprof_profile_flags * pf)
973978
orig_zheap = NULL;
974979
}
975980

976-
old_zend_execute = zend_execute_fn;
977-
old_zend_execute_internal = zend_execute_internal;
978-
zend_execute_fn = memprof_zend_execute;
979-
zend_execute_internal = memprof_zend_execute_internal;
980-
981981
track_mallocs = 1;
982982
}
983983

984984
static void memprof_disable(void)
985985
{
986986
track_mallocs = 0;
987987

988-
zend_execute_fn = old_zend_execute;
989-
zend_execute_internal = old_zend_execute_internal;
990-
991988
if (zheap) {
992989
zend_mm_set_heap(orig_zheap);
993990
free(zheap);
@@ -1200,6 +1197,8 @@ PHP_MINIT_FUNCTION(memprof)
12001197

12011198
REGISTER_INI_ENTRIES();
12021199

1200+
zend_observer_fcall_register(memprof_observer_fcall_init);
1201+
12031202
entry = zend_hash_str_find_ptr(EG(ini_directives), "memory_limit", sizeof("memory_limit")-1);
12041203

12051204
if (entry == NULL) {

tests/002.phpt

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,23 @@ array(6) {
6363
["calls"]=>
6464
int(1)
6565
["called_functions"]=>
66-
array(0) {
66+
array(1) {
67+
["time"]=>
68+
array(6) {
69+
["memory_size"]=>
70+
int(0)
71+
["blocks_count"]=>
72+
int(0)
73+
["memory_size_inclusive"]=>
74+
int(0)
75+
["blocks_count_inclusive"]=>
76+
int(0)
77+
["calls"]=>
78+
int(1)
79+
["called_functions"]=>
80+
array(0) {
81+
}
82+
}
6783
}
6884
}
6985
["memprof_dump_array"]=>
@@ -158,7 +174,23 @@ array(6) {
158174
["calls"]=>
159175
int(1)
160176
["called_functions"]=>
161-
array(0) {
177+
array(1) {
178+
["time"]=>
179+
array(6) {
180+
["memory_size"]=>
181+
int(0)
182+
["blocks_count"]=>
183+
int(0)
184+
["memory_size_inclusive"]=>
185+
int(0)
186+
["blocks_count_inclusive"]=>
187+
int(0)
188+
["calls"]=>
189+
int(1)
190+
["called_functions"]=>
191+
array(0) {
192+
}
193+
}
162194
}
163195
}
164196
["memprof_dump_array"]=>

tests/003.phpt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,23 @@ $b = Eater::eat();
1313
memprof_dump_callgrind(STDOUT);
1414

1515
--EXPECTF--
16+
1617
version: 1
1718
cmd: unknown
1819
positions: line
1920
events: Memory_Size_(bytes) BlocksCount
2021

22+
fl=php:internal
23+
fn=time
24+
1 0 0
25+
2126
fl=%scommon.php
2227
fn=require %scommon.php
2328
1 0 0
29+
cfl=php:internal
30+
cfn=time
31+
calls=1 1
32+
1 0 0
2433

2534
fl=php:internal
2635
fn=str_repeat

tests/common.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<?php
22

3+
// Some statement to prevent inclusion of this file from being optimized out
4+
time();
5+
36
function eat()
47
{
58
return str_repeat('X', 3 * 1024 * 1024);

tests/dump-pprof.phpt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ binary=php://stdout
1818
0x0000000000000008 root
1919
0x0000000000000010 require %sdump-pprof.php
2020
0x0000000000000018 require %scommon.php
21-
0x0000000000000020 eat
22-
0x0000000000000028 str_repeat
23-
0x0000000000000030 Eater::eat
24-
0x0000000000000038 memprof_dump_pprof
21+
0x0000000000000020 time
22+
0x0000000000000028 eat
23+
0x0000000000000030 str_repeat
24+
0x0000000000000038 Eater::eat
25+
0x0000000000000040 memprof_dump_pprof
2526
---
2627
--- profile
2728
%a

0 commit comments

Comments
 (0)