Skip to content

Commit d3abca4

Browse files
committed
Save previous observer on the VM stack
Signed-off-by: Bob Weinand <[email protected]>
1 parent ce4727f commit d3abca4

File tree

9 files changed

+60
-42
lines changed

9 files changed

+60
-42
lines changed

Zend/Optimizer/zend_optimizer.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "zend_inference.h"
3232
#include "zend_dump.h"
3333
#include "php.h"
34+
#include "zend_observer.h"
3435

3536
#ifndef ZEND_OPTIMIZER_MAX_REGISTERED_PASSES
3637
# define ZEND_OPTIMIZER_MAX_REGISTERED_PASSES 32
@@ -1094,6 +1095,8 @@ static void zend_revert_pass_two(zend_op_array *op_array)
10941095
}
10951096
#endif
10961097

1098+
op_array->T -= ZEND_OBSERVER_ENABLED;
1099+
10971100
op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO;
10981101
}
10991102

@@ -1123,6 +1126,8 @@ static void zend_redo_pass_two(zend_op_array *op_array)
11231126
}
11241127
#endif
11251128

1129+
op_array->T += ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled
1130+
11261131
opline = op_array->opcodes;
11271132
end = opline + op_array->last;
11281133
while (opline < end) {
@@ -1550,6 +1555,12 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
15501555
}
15511556
}
15521557

1558+
if (ZEND_OBSERVER_ENABLED) {
1559+
for (i = 0; i < call_graph.op_arrays_count; i++) {
1560+
++call_graph.op_arrays[i]->T; // ensure accurate temporary count for stack size precalculation
1561+
}
1562+
}
1563+
15531564
if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
15541565
for (i = 0; i < call_graph.op_arrays_count; i++) {
15551566
zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
@@ -1565,6 +1576,8 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
15651576
zend_recalc_live_ranges(op_array, needs_live_range);
15661577
}
15671578
} else {
1579+
op_array->T -= ZEND_OBSERVER_ENABLED; // redo_pass_two will re-increment it
1580+
15681581
zend_redo_pass_two(op_array);
15691582
if (op_array->live_range) {
15701583
zend_recalc_live_ranges(op_array, NULL);

Zend/zend_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,6 +2685,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
26852685
}
26862686
internal_function->type = ZEND_INTERNAL_FUNCTION;
26872687
internal_function->module = EG(current_module);
2688+
internal_function->T = 0;
26882689
memset(internal_function->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*));
26892690

26902691
while (ptr->fname) {

Zend/zend_compile.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,12 @@ struct _zend_op_array {
448448
uint32_t required_num_args;
449449
zend_arg_info *arg_info;
450450
HashTable *attributes;
451+
uint32_t T; /* number of temporary variables */
451452
ZEND_MAP_PTR_DEF(void **, run_time_cache);
452453
/* END of common elements */
453454

454455
int cache_size; /* number of run_time_cache_slots * sizeof(void*) */
455456
int last_var; /* number of CV variables */
456-
uint32_t T; /* number of temporary variables */
457457
uint32_t last; /* number of opcodes */
458458

459459
zend_op *opcodes;
@@ -503,6 +503,7 @@ typedef struct _zend_internal_function {
503503
uint32_t required_num_args;
504504
zend_internal_arg_info *arg_info;
505505
HashTable *attributes;
506+
uint32_t T; /* number of temporary variables */
506507
ZEND_MAP_PTR_DEF(void **, run_time_cache);
507508
/* END of common elements */
508509

@@ -528,6 +529,7 @@ union _zend_function {
528529
uint32_t required_num_args;
529530
zend_arg_info *arg_info; /* index -1 represents the return value info, if any */
530531
HashTable *attributes;
532+
uint32_t T; /* number of temporary variables */
531533
ZEND_MAP_PTR_DEF(void **, run_time_cache);
532534
} common;
533535

Zend/zend_enum.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "zend_interfaces.h"
2424
#include "zend_enum.h"
2525
#include "zend_extensions.h"
26+
#include "zend_observer.h"
2627

2728
#define ZEND_ENUM_DISALLOW_MAGIC_METHOD(propertyName, methodName) \
2829
do { \
@@ -407,6 +408,7 @@ static void zend_enum_register_func(zend_class_entry *ce, zend_known_string_id n
407408
zif->type = ZEND_INTERNAL_FUNCTION;
408409
zif->module = EG(current_module);
409410
zif->scope = ce;
411+
zif->T = ZEND_OBSERVER_ENABLED;
410412
ZEND_MAP_PTR_NEW(zif->run_time_cache);
411413
ZEND_MAP_PTR_SET(zif->run_time_cache, zend_arena_alloc(&CG(arena), zend_internal_run_time_cache_reserved_size()));
412414

Zend/zend_execute.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ ZEND_API const zend_internal_function zend_pass_function = {
144144
0, /* required_num_args */
145145
(zend_internal_arg_info *) zend_pass_function_arg_info + 1, /* arg_info */
146146
NULL, /* attributes */
147+
0, /* T */
147148
NULL, /* run_time_cache */
148149
ZEND_FN(pass), /* handler */
149150
NULL, /* module */

Zend/zend_execute.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,10 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(ui
245245

246246
static zend_always_inline uint32_t zend_vm_calc_used_stack(uint32_t num_args, zend_function *func)
247247
{
248-
uint32_t used_stack = ZEND_CALL_FRAME_SLOT + num_args;
248+
uint32_t used_stack = ZEND_CALL_FRAME_SLOT + num_args + func->common.T;
249249

250250
if (EXPECTED(ZEND_USER_CODE(func->type))) {
251-
used_stack += func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args);
251+
used_stack += func->op_array.last_var - MIN(func->op_array.num_args, num_args);
252252
}
253253
return used_stack * sizeof(zval);
254254
}

Zend/zend_observer.c

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ ZEND_API void zend_observer_post_startup(void)
7878
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op));
7979
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op) + 1);
8080
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op) + 2);
81+
82+
// Add an observer temporary to store previous observed frames
83+
zend_internal_function *zif;
84+
ZEND_HASH_FOREACH_PTR(CG(function_table), zif) {
85+
++zif->T;
86+
} ZEND_HASH_FOREACH_END();
87+
zend_class_entry *ce;
88+
ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) {
89+
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, zif) {
90+
++zif->T;
91+
} ZEND_HASH_FOREACH_END();
92+
} ZEND_HASH_FOREACH_END();
8193
}
8294
}
8395

@@ -187,6 +199,11 @@ ZEND_API bool zend_observer_remove_end_handler(zend_function *function, zend_obs
187199
return zend_observer_remove_handler((void **)&ZEND_OBSERVER_DATA(function) + registered_observers, end);
188200
}
189201

202+
static inline zend_execute_data **prev_observed_frame(zend_execute_data *execute_data) {
203+
zend_function *func = EX(func);
204+
return (zend_execute_data **)&Z_PTR_P(EX_VAR_NUM((ZEND_USER_CODE(func->type) ? func->op_array.last_var : ZEND_CALL_NUM_ARGS(execute_data)) + func->common.T - 1));
205+
}
206+
190207
static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_data)
191208
{
192209
if (!ZEND_OBSERVER_ENABLED) {
@@ -208,9 +225,7 @@ static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_d
208225

209226
zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)possible_handlers_end;
210227
if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) {
211-
if (first_observed_frame == NULL) {
212-
first_observed_frame = execute_data;
213-
}
228+
*prev_observed_frame(execute_data) = current_observed_frame;
214229
current_observed_frame = execute_data;
215230
}
216231

@@ -236,29 +251,9 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(zend_execute_data *execute
236251
}
237252
}
238253

239-
static inline bool zend_observer_is_skipped_frame(zend_execute_data *execute_data) {
240-
zend_function *func = execute_data->func;
241-
242-
if (!func || !ZEND_OBSERVABLE_FN(func)) {
243-
return true;
244-
}
245-
246-
zend_observer_fcall_end_handler end_handler = (&ZEND_OBSERVER_DATA(func))[zend_observers_fcall_list.count];
247-
if (end_handler == NULL || end_handler == ZEND_OBSERVER_NOT_OBSERVED) {
248-
return true;
249-
}
250-
251-
return false;
252-
}
253-
254-
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(zend_execute_data *execute_data, zval *return_value)
255-
{
254+
static inline void call_end_observers(zend_execute_data *execute_data, zval *return_value) {
256255
zend_function *func = execute_data->func;
257256

258-
if (!ZEND_OBSERVER_ENABLED || !ZEND_OBSERVABLE_FN(func)) {
259-
return;
260-
}
261-
262257
zend_observer_fcall_end_handler *handler = (zend_observer_fcall_end_handler *)&ZEND_OBSERVER_DATA(func) + zend_observers_fcall_list.count;
263258
// TODO: Fix exceptions from generators
264259
// ZEND_ASSERT(fcall_data);
@@ -270,28 +265,27 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(zend_execute_data *execute_d
270265
do {
271266
(*handler)(execute_data, return_value);
272267
} while (++handler != possible_handlers_end && *handler != NULL);
268+
}
273269

274-
if (first_observed_frame == execute_data) {
275-
first_observed_frame = NULL;
276-
current_observed_frame = NULL;
277-
} else {
278-
zend_execute_data *ex = execute_data->prev_execute_data;
279-
while (ex && zend_observer_is_skipped_frame(ex)) {
280-
ex = ex->prev_execute_data;
281-
}
282-
current_observed_frame = ex;
270+
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(zend_execute_data *execute_data, zval *return_value)
271+
{
272+
if (execute_data != current_observed_frame) {
273+
return;
283274
}
275+
call_end_observers(execute_data, return_value);
276+
current_observed_frame = *prev_observed_frame(execute_data);
284277
}
285278

286279
ZEND_API void zend_observer_fcall_end_all(void)
287280
{
288-
zend_execute_data *ex = current_observed_frame;
289-
while (ex != NULL) {
290-
if (ex->func) {
291-
zend_observer_fcall_end(ex, NULL);
292-
}
293-
ex = ex->prev_execute_data;
281+
zend_execute_data *execute_data = current_observed_frame, *original_execute_data = EG(current_execute_data);
282+
current_observed_frame = NULL;
283+
while (execute_data) {
284+
EG(current_execute_data) = execute_data;
285+
call_end_observers(execute_data, NULL);
286+
execute_data = *prev_observed_frame(execute_data);
294287
}
288+
EG(current_execute_data) = original_execute_data;
295289
}
296290

297291
ZEND_API void zend_observer_error_register(zend_observer_error_cb cb)

Zend/zend_opcode.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "zend_API.h"
2828
#include "zend_sort.h"
2929
#include "zend_constants.h"
30+
#include "zend_observer.h"
3031

3132
#include "zend_vm.h"
3233

@@ -1049,6 +1050,8 @@ ZEND_API void pass_two(zend_op_array *op_array)
10491050
CG(context).literals_size = op_array->last_literal;
10501051
#endif
10511052

1053+
op_array->T += ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled
1054+
10521055
/* Needs to be set directly after the opcode/literal reallocation, to ensure destruction
10531056
* happens correctly if any of the following fixups generate a fatal error. */
10541057
op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;

ext/pdo/pdo_dbh.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "zend_object_handlers.h"
3434
#include "zend_hash.h"
3535
#include "pdo_dbh_arginfo.h"
36+
#include "zend_observer.h"
3637

3738
static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value);
3839

@@ -1246,6 +1247,7 @@ bool pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
12461247
func.scope = dbh_obj->std.ce;
12471248
func.prototype = NULL;
12481249
ZEND_MAP_PTR(func.run_time_cache) = NULL;
1250+
func.T = ZEND_OBSERVER_ENABLED;
12491251
if (funcs->flags) {
12501252
func.fn_flags = funcs->flags | ZEND_ACC_NEVER_CACHE;
12511253
} else {

0 commit comments

Comments
 (0)