php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
zend_observer.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Levi Morrison <levim@php.net> |
16 | Sammy Kaye Powers <sammyk@php.net> |
17 | Bob Weinand <bobwei9@hotmail.com> |
18 +----------------------------------------------------------------------+
19*/
20
21#include "zend_observer.h"
22
23#include "zend_extensions.h"
24#include "zend_llist.h"
25#include "zend_vm.h"
26
27#define ZEND_OBSERVER_NOT_OBSERVED ((void *) 2)
28
29static zend_llist zend_observers_fcall_list;
30static zend_llist zend_observer_function_declared_callbacks;
31static zend_llist zend_observer_class_linked_callbacks;
32static zend_llist zend_observer_error_callbacks;
33static zend_llist zend_observer_fiber_init;
34static zend_llist zend_observer_fiber_switch;
35static zend_llist zend_observer_fiber_destroy;
36
42
43// Call during minit/startup ONLY
45{
46 zend_llist_add_element(&zend_observers_fcall_list, &init);
47}
48
49// Called by engine before MINITs
51{
52 zend_llist_init(&zend_observers_fcall_list, sizeof(zend_observer_fcall_init), NULL, 1);
53 zend_llist_init(&zend_observer_function_declared_callbacks, sizeof(zend_observer_function_declared_cb), NULL, 1);
54 zend_llist_init(&zend_observer_class_linked_callbacks, sizeof(zend_observer_class_linked_cb), NULL, 1);
55 zend_llist_init(&zend_observer_error_callbacks, sizeof(zend_observer_error_cb), NULL, 1);
56 zend_llist_init(&zend_observer_fiber_init, sizeof(zend_observer_fiber_init_handler), NULL, 1);
57 zend_llist_init(&zend_observer_fiber_switch, sizeof(zend_observer_fiber_switch_handler), NULL, 1);
58 zend_llist_init(&zend_observer_fiber_destroy, sizeof(zend_observer_fiber_destroy_handler), NULL, 1);
59
62}
63
65{
66 if (zend_observers_fcall_list.count) {
67 /* We don't want to get an extension handle unless an ext installs an observer
68 * Allocate each a begin and an end pointer */
70 zend_get_op_array_extension_handles("Zend Observer", (int) zend_observers_fcall_list.count * 2);
71
73 zend_get_internal_function_extension_handles("Zend Observer", (int) zend_observers_fcall_list.count * 2);
74
75 /* ZEND_CALL_TRAMPOLINE has SPEC(OBSERVER) but zend_init_call_trampoline_op()
76 * is called before any extensions have registered as an observer. So we
77 * adjust the offset to the observed handler when we know we need to observe. */
78 ZEND_VM_SET_OPCODE_HANDLER(&EG(call_trampoline_op));
79
80 /* ZEND_HANDLE_EXCEPTION also has SPEC(OBSERVER) and no observer extensions
81 * exist when zend_init_exception_op() is called. */
82 ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op));
83 ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op) + 1);
84 ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op) + 2);
85
86 // Add an observer temporary to store previous observed frames
88 ZEND_HASH_FOREACH_PTR(CG(function_table), zif) {
89 ++zif->T;
92 ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) {
94 ++zif->T;
97 }
98}
99
101{
102 EG(current_observed_frame) = NULL;
103}
104
106{
107 zend_llist_destroy(&zend_observers_fcall_list);
108 zend_llist_destroy(&zend_observer_function_declared_callbacks);
109 zend_llist_destroy(&zend_observer_class_linked_callbacks);
110 zend_llist_destroy(&zend_observer_error_callbacks);
111 zend_llist_destroy(&zend_observer_fiber_init);
112 zend_llist_destroy(&zend_observer_fiber_switch);
113 zend_llist_destroy(&zend_observer_fiber_destroy);
114}
115
116static void zend_observer_fcall_install(zend_execute_data *execute_data)
117{
118 zend_llist *list = &zend_observers_fcall_list;
120
122 zend_observer_fcall_begin_handler *begin_handlers = ZEND_OBSERVER_DATA(function), *begin_handlers_start = begin_handlers;
123 zend_observer_fcall_end_handler *end_handlers = (zend_observer_fcall_end_handler *)begin_handlers + list->count, *end_handlers_start = end_handlers;
124
125 *begin_handlers = ZEND_OBSERVER_NOT_OBSERVED;
126 *end_handlers = ZEND_OBSERVER_NOT_OBSERVED;
127 bool has_handlers = false;
128
129 for (zend_llist_element *element = list->head; element; element = element->next) {
131 memcpy(&init, element->data, sizeof init);
133 if (handlers.begin) {
134 *(begin_handlers++) = handlers.begin;
135 has_handlers = true;
136 }
137 if (handlers.end) {
138 *(end_handlers++) = handlers.end;
139 has_handlers = true;
140 }
141 }
142
143 // end handlers are executed in reverse order
144 for (--end_handlers; end_handlers_start < end_handlers; --end_handlers, ++end_handlers_start) {
145 zend_observer_fcall_end_handler tmp = *end_handlers;
146 *end_handlers = *end_handlers_start;
147 *end_handlers_start = tmp;
148 }
149
150 if (!has_handlers) {
151 *begin_handlers_start = ZEND_OBSERVER_NONE_OBSERVED;
152 }
153}
154
155/* We need to provide the ability to retrieve the handler which will move onto the position the current handler was.
156 * The fundamental problem is that, if a handler is removed while it's being executed, it will move handlers around:
157 * the previous next handler is now at the place where the current handler was.
158 * Hence, the next handler executed will be the one after the next handler.
159 * Callees must thus invoke the next handler themselves, with the same arguments they were passed. */
160static bool zend_observer_remove_handler(void **first_handler, void *old_handler, void **next_handler) {
161 size_t registered_observers = zend_observers_fcall_list.count;
162
163 void **last_handler = first_handler + registered_observers - 1;
164 for (void **cur_handler = first_handler; cur_handler <= last_handler; ++cur_handler) {
165 if (*cur_handler == old_handler) {
166 if (registered_observers == 1 || (cur_handler == first_handler && cur_handler[1] == NULL)) {
167 *cur_handler = ZEND_OBSERVER_NOT_OBSERVED;
168 *next_handler = NULL;
169 } else {
170 if (cur_handler != last_handler) {
171 memmove(cur_handler, cur_handler + 1, sizeof(cur_handler) * (last_handler - cur_handler));
172 }
173 *last_handler = NULL;
174 *next_handler = *cur_handler;
175 }
176 return true;
177 }
178 }
179 return false;
180}
181
183 size_t registered_observers = zend_observers_fcall_list.count;
184 zend_observer_fcall_begin_handler *first_handler = ZEND_OBSERVER_DATA(function), *last_handler = first_handler + registered_observers - 1;
185 if (*first_handler == ZEND_OBSERVER_NOT_OBSERVED || *first_handler == ZEND_OBSERVER_NONE_OBSERVED) {
186 *first_handler = begin;
187 } else {
188 for (zend_observer_fcall_begin_handler *cur_handler = first_handler + 1; cur_handler <= last_handler; ++cur_handler) {
189 if (*cur_handler == NULL) {
190 *cur_handler = begin;
191 return;
192 }
193 }
194 // there's no space for new handlers, then it's forbidden to call this function
196 }
197}
198
200 void **begin_handlers = (void **)ZEND_OBSERVER_DATA(function);
201 if (zend_observer_remove_handler(begin_handlers, begin, (void**)next)) {
202 // Ensure invariant: ZEND_OBSERVER_NONE_OBSERVED in begin_handlers if both are not observed
203 if (*begin_handlers == ZEND_OBSERVER_NOT_OBSERVED) {
204 size_t registered_observers = zend_observers_fcall_list.count;
205 if (begin_handlers[registered_observers] /* first end handler */ == ZEND_OBSERVER_NOT_OBSERVED) {
206 *begin_handlers = ZEND_OBSERVER_NONE_OBSERVED;
207 }
208 }
209 return true;
210 }
211 return false;
212}
213
215 size_t registered_observers = zend_observers_fcall_list.count;
216 void **begin_handler = (void **)ZEND_OBSERVER_DATA(function);
217 zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)begin_handler + registered_observers;
218 // to allow to preserve the invariant that end handlers are in reverse order of begin handlers, push the new end handler in front
219 if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) {
220 // there's no space for new handlers, then it's forbidden to call this function
221 ZEND_ASSERT(end_handler[registered_observers - 1] == NULL);
222 memmove(end_handler + 1, end_handler, sizeof(end_handler) * (registered_observers - 1));
223 } else if (*begin_handler == ZEND_OBSERVER_NONE_OBSERVED) {
224 *begin_handler = ZEND_OBSERVER_NOT_OBSERVED;
225 }
226 *end_handler = end;
227}
228
230 size_t registered_observers = zend_observers_fcall_list.count;
231 void **begin_handlers = (void **)ZEND_OBSERVER_DATA(function);
232 void **end_handlers = begin_handlers + registered_observers;
233 if (zend_observer_remove_handler(end_handlers, end, (void**)next)) {
234 // Ensure invariant: ZEND_OBSERVER_NONE_OBSERVED in begin_handlers if both are not observed
235 if (*begin_handlers == ZEND_OBSERVER_NOT_OBSERVED && *end_handlers == ZEND_OBSERVER_NOT_OBSERVED) {
236 *begin_handlers = ZEND_OBSERVER_NONE_OBSERVED;
237 }
238 return true;
239 }
240 return false;
241}
242
243static inline zend_execute_data **prev_observed_frame(zend_execute_data *execute_data) {
246 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));
247}
248
249static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_data) {
251 return;
252 }
253
254 zend_observer_fcall_begin_specialized(execute_data, true);
255}
256
258{
259 zend_observer_fcall_begin_handler *possible_handlers_end = handler + zend_observers_fcall_list.count;
260
261 if (!*handler) {
262 zend_observer_fcall_install(execute_data);
263 if (zend_observer_handler_is_unobserved(handler)) {
264 return;
265 }
266 }
267
268 zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)possible_handlers_end;
269 if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) {
270 *prev_observed_frame(execute_data) = EG(current_observed_frame);
271 EG(current_observed_frame) = execute_data;
272
273 if (*handler == ZEND_OBSERVER_NOT_OBSERVED) { // this function must not be called if ZEND_OBSERVER_NONE_OBSERVED, hence sufficient to check
274 return;
275 }
276 }
277
278 do {
279 (*handler)(execute_data);
280 } while (++handler != possible_handlers_end && *handler != NULL);
281}
282
287
289{
291 if (!(EX(func)->common.fn_flags & ZEND_ACC_GENERATOR)) {
292 _zend_observe_fcall_begin(execute_data);
293 }
294}
295
296static inline void call_end_observers(zend_execute_data *execute_data, zval *return_value) {
299
301 // TODO: Fix exceptions from generators
302 // ZEND_ASSERT(fcall_data);
304 return;
305 }
306
307 zend_observer_fcall_end_handler *possible_handlers_end = handler + zend_observers_fcall_list.count;
308 do {
309 (*handler)(execute_data, return_value);
310 } while (++handler != possible_handlers_end && *handler != NULL);
311}
312
314{
315 call_end_observers(execute_data, return_value);
316 EG(current_observed_frame) = *prev_observed_frame(execute_data);
317}
318
320{
321 zend_execute_data *execute_data = EG(current_observed_frame), *original_execute_data = EG(current_execute_data);
322 EG(current_observed_frame) = NULL;
323 while (execute_data) {
324 EG(current_execute_data) = execute_data;
325 call_end_observers(execute_data, NULL);
326 execute_data = *prev_observed_frame(execute_data);
327 }
328 EG(current_execute_data) = original_execute_data;
329}
330
336
338{
339 if (CG(compiler_options) & ZEND_COMPILE_IGNORE_OBSERVER) {
340 return;
341 }
342
343 for (zend_llist_element *element = zend_observer_function_declared_callbacks.head; element; element = element->next) {
345 callback(op_array, name);
346 }
347}
348
354
356{
357 if (CG(compiler_options) & ZEND_COMPILE_IGNORE_OBSERVER) {
358 return;
359 }
360
361 for (zend_llist_element *element = zend_observer_class_linked_callbacks.head; element; element = element->next) {
363 callback(ce, name);
364 }
365}
366
372
373ZEND_API void _zend_observer_error_notify(int type, zend_string *error_filename, uint32_t error_lineno, zend_string *message)
374{
375 for (zend_llist_element *element = zend_observer_error_callbacks.head; element; element = element->next) {
377 callback(type, error_filename, error_lineno, message);
378 }
379}
380
385
390
395
397{
398 zend_llist_element *element;
400
401 initializing->top_observed_frame = NULL;
402
403 for (element = zend_observer_fiber_init.head; element; element = element->next) {
405 callback(initializing);
406 }
407}
408
410{
411 zend_llist_element *element;
413
414 if (from->status == ZEND_FIBER_STATUS_DEAD) {
415 zend_observer_fcall_end_all(); // fiber is either finished (call will do nothing) or has bailed out
416 }
417
418 for (element = zend_observer_fiber_switch.head; element; element = element->next) {
420 callback(from, to);
421 }
422
423 from->top_observed_frame = EG(current_observed_frame);
424 EG(current_observed_frame) = to->top_observed_frame;
425}
426
428{
429 zend_llist_element *element;
431
432 for (element = zend_observer_fiber_destroy.head; element; element = element->next) {
434 callback(destroying);
435 }
436}
char * cb
Definition assert.c:26
zval callback
Definition assert.c:25
int begin
Definition eaw_table.h:20
zend_ffi_type * type
Definition ffi.c:3812
memcpy(ptr1, ptr2, size)
#define NULL
Definition gdcache.h:45
#define next(ls)
Definition minilua.c:2661
#define memmove(a, b, c)
unsigned const char * end
Definition php_ffi.h:51
zend_stack handlers
Definition php_output.h:139
zend_execute_data * original_execute_data
HashTable function_table
Definition zend.h:163
zend_execute_data * top_observed_frame
Definition zend_fibers.h:96
zend_fiber_status status
Definition zend_fibers.h:93
struct _zend_llist_element * next
Definition zend_llist.h:26
zend_llist_element * head
Definition zend_llist.h:38
size_t count
Definition zend_llist.h:40
struct _zval_struct zval
execute_data func
#define ZEND_USER_CODE(type)
#define EX_VAR_NUM(n)
#define RUN_TIME_CACHE(op_array)
#define EX(element)
#define ZEND_ACC_GENERATOR
struct _zend_op_array zend_op_array
#define ZEND_CALL_NUM_ARGS(call)
struct _zend_internal_function zend_internal_function
#define ZEND_COMPILE_IGNORE_OBSERVER
#define ZEND_API
ZEND_API int zend_get_internal_function_extension_handles(const char *module_name, int handles)
ZEND_API int zend_get_op_array_extension_handles(const char *module_name, int handles)
@ ZEND_FIBER_STATUS_DEAD
Definition zend_fibers.h:37
union _zend_function zend_function
struct _zend_fiber_context zend_fiber_context
#define CG(v)
#define EG(v)
#define ZEND_HASH_MAP_FOREACH_PTR(ht, _ptr)
Definition zend_hash.h:1326
#define ZEND_HASH_FOREACH_PTR(ht, _ptr)
Definition zend_hash.h:1118
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
ZEND_API void zend_llist_destroy(zend_llist *l)
Definition zend_llist.c:102
ZEND_API void zend_llist_add_element(zend_llist *l, const void *element)
Definition zend_llist.c:34
ZEND_API void zend_llist_init(zend_llist *l, size_t size, llist_dtor_func_t dtor, unsigned char persistent)
Definition zend_llist.c:24
struct _zend_llist_element zend_llist_element
struct _zend_llist zend_llist
struct _zend_string zend_string
ZEND_API bool zend_observer_remove_end_handler(zend_function *function, zend_observer_fcall_end_handler end, zend_observer_fcall_end_handler *next)
ZEND_API void ZEND_FASTCALL zend_observer_fiber_switch_notify(zend_fiber_context *from, zend_fiber_context *to)
ZEND_API void zend_observer_fcall_end_all(void)
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end_prechecked(zend_execute_data *execute_data, zval *return_value)
ZEND_API void zend_observer_fiber_init_register(zend_observer_fiber_init_handler handler)
bool zend_observer_function_declared_observed
ZEND_API void zend_observer_class_linked_register(zend_observer_class_linked_cb cb)
int zend_observer_fcall_op_array_extension
ZEND_API void zend_observer_shutdown(void)
int zend_observer_fcall_internal_function_extension
bool zend_observer_class_linked_observed
bool zend_observer_errors_observed
ZEND_API void ZEND_FASTCALL zend_observer_fiber_destroy_notify(zend_fiber_context *destroying)
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin_prechecked(zend_execute_data *execute_data, zend_observer_fcall_begin_handler *handler)
ZEND_API void zend_observer_post_startup(void)
ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init)
ZEND_API void zend_observer_startup(void)
#define ZEND_OBSERVER_NOT_OBSERVED
ZEND_API bool zend_observer_remove_begin_handler(zend_function *function, zend_observer_fcall_begin_handler begin, zend_observer_fcall_begin_handler *next)
ZEND_API void ZEND_FASTCALL _zend_observer_function_declared_notify(zend_op_array *op_array, zend_string *name)
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(zend_execute_data *execute_data)
ZEND_API void ZEND_FASTCALL zend_observer_fiber_init_notify(zend_fiber_context *initializing)
ZEND_API void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler)
ZEND_API void zend_observer_error_register(zend_observer_error_cb cb)
ZEND_API void zend_observer_add_end_handler(zend_function *function, zend_observer_fcall_end_handler end)
ZEND_API void _zend_observer_error_notify(int type, zend_string *error_filename, uint32_t error_lineno, zend_string *message)
ZEND_API void zend_observer_function_declared_register(zend_observer_function_declared_cb cb)
ZEND_API void zend_observer_activate(void)
ZEND_API void zend_observer_fiber_destroy_register(zend_observer_fiber_destroy_handler handler)
ZEND_API void ZEND_FASTCALL _zend_observer_class_linked_notify(zend_class_entry *ce, zend_string *name)
ZEND_API void zend_observer_add_begin_handler(zend_function *function, zend_observer_fcall_begin_handler begin)
ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(zend_execute_data *execute_data)
#define ZEND_OBSERVER_DATA(function)
void(* zend_observer_class_linked_cb)(zend_class_entry *ce, zend_string *name)
#define ZEND_OBSERVER_NONE_OBSERVED
#define ZEND_OBSERVER_ENABLED
void(* zend_observer_fiber_switch_handler)(zend_fiber_context *from, zend_fiber_context *to)
void(* zend_observer_fiber_init_handler)(zend_fiber_context *initializing)
void(* zend_observer_function_declared_cb)(zend_op_array *op_array, zend_string *name)
void(* zend_observer_fcall_end_handler)(zend_execute_data *execute_data, zval *retval)
void(* zend_observer_fcall_begin_handler)(zend_execute_data *execute_data)
void(* zend_observer_error_cb)(int type, zend_string *error_filename, uint32_t error_lineno, zend_string *message)
void(* zend_observer_fiber_destroy_handler)(zend_fiber_context *destroying)
zend_observer_fcall_handlers(* zend_observer_fcall_init)(zend_execute_data *execute_data)
struct _zend_observer_fcall_handlers zend_observer_fcall_handlers
#define ZEND_FASTCALL
#define ZEND_ASSERT(c)
#define ZEND_UNREACHABLE()
#define ZEND_ASSUME(c)
struct _zend_class_entry zend_class_entry
#define Z_PTR_P(zval_p)
struct _zend_execute_data zend_execute_data
Definition zend_types.h:91
#define ZEND_VM_SET_OPCODE_HANDLER(opline)
Definition zend_vm.h:44
zval * return_value
zend_string * name
fbc internal_function handler(call, ret)
function(EX_VAR(opline->result.var))
execute_data