-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add new observer API #5857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Add new observer API #5857
Changes from all commits
Commits
Show all changes
57 commits
Select commit
Hold shift + click to select a range
6a73552
Add zend_observer API
morrisonlevi 45a8540
Add temporary observer extension
morrisonlevi ecfca90
Fix some issues
nikic 1e7c59d
Guard observer begin handler and start adding end handler
morrisonlevi 01308fd
WIP .end handler
morrisonlevi dc74744
Update for zend_get_zval_ptr changes
morrisonlevi 23b7d37
Expand ternary short-hand for Windows
morrisonlevi 1553336
Try fixing missing symbol
morrisonlevi 947c955
Free OP1 of INCLUDE_OR_EVAL later so observers can look at it
morrisonlevi fe6b447
Move observer arena allocation for call handers to first-time register
SammyK 3f617d1
Refactor where function init/begin/end handlers are called
SammyK a3e0139
Remove fcall init handlers from zend_call_function()
SammyK e5e7bce
Clean up
SammyK e7badd9
Clean up one more
SammyK 71bbb1f
Add observer hooks to zend_test extension
SammyK 0b13ca6
Merge branch 'master' into levim/observer
SammyK 9837e2b
Add test for magic
SammyK ef1bcdc
Add basic generator support
SammyK f6edb80
Make cleaner test output
SammyK c66d659
Pass proper return value for generators
SammyK a6faa3b
Expose return values in tests
SammyK 5f24f65
Add more generator tests
SammyK 9e9bfc9
Remove temporary observer extension
SammyK b5b2762
Merge branch 'master' into levim/observer
SammyK 2958a20
Fix memleak on TMPVAR with eval()
SammyK 95bcf05
Fix arena allocation again :facepalm:
SammyK 1e3b6be
Whitespace
SammyK 1445b3b
Disable observer API when loaded via dl()
SammyK 228807a
Wrap up some TODOs and cleanup
SammyK fc73942
Implement ZEND_OBSERVE_RETURN
SammyK b4731c5
Clean up fcall observer
morrisonlevi c76e8f5
Revert "Implement ZEND_OBSERVE_RETURN"
SammyK bb429b0
Implement SPEC(OBSERVER) for return handlers
SammyK c57324d
Add SPEC(OBSERVER) to DO_FCALL and friends
SammyK e3cdb9f
Add SPEC(OBSERVER) to INCLUDE_OR_EVAL
SammyK 791c921
Add SPEC(OBSERVER) to fcall INIT handlers
SammyK 0f78895
Add test for observing eval
SammyK bf56f27
Add SPEC(OBSERVER) to ZEND_HANDLE_EXCEPTION
SammyK b8e1408
Do only one observer check in zend_generator_resume
SammyK 7792c18
Add test for observing closures
SammyK f0da73f
Merge remote-tracking branch 'upstream/master' into levim/observer
SammyK 3f93dc8
Check for ZEND_OBSERVER_ENABLED before installing observers for closures
SammyK 00a038b
Install observers only if enabled
SammyK 1d2f9f6
Disable JIT when observer extension present
SammyK d22d421
Add SKIPIF's to the observer tests
SammyK 96d1040
Add observer to zend_call_function
SammyK 639a09e
Move observer install to first time fcall
SammyK 971824b
Move LOAD_OPLINE_EX() back to the end of DO_UCALL handler
SammyK f9e10a0
Rename ZEND_SHOULD_OBSERVE_FN to ZEND_OBSERVABLE_FN
SammyK e4a6c45
Mark SPEC(OBSERVER) handlers as ZEND_VM_COLD
SammyK 58a075f
Only register zend_test as observer when enabled
SammyK 1698665
Add zend_test.observer.show_output INI setting
SammyK 6181716
Skip JIT tests on non-JIT builds
SammyK 63dba3d
Update observer arena size
morrisonlevi 1ca066b
Add ZEND_API to some observer functions
morrisonlevi 85083ff
Fix C++ compat for zend_observer.h
morrisonlevi 6d5faf5
Merge branch 'master' into levim/observer
morrisonlevi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
/* | ||
+----------------------------------------------------------------------+ | ||
| Zend Engine | | ||
+----------------------------------------------------------------------+ | ||
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | | ||
+----------------------------------------------------------------------+ | ||
| This source file is subject to version 2.00 of the Zend license, | | ||
| that is bundled with this package in the file LICENSE, and is | | ||
| available through the world-wide-web at the following url: | | ||
| http://www.zend.com/license/2_00.txt. | | ||
| If you did not receive a copy of the Zend license and are unable to | | ||
| obtain it through the world-wide-web, please send a note to | | ||
| license@zend.com so we can mail you a copy immediately. | | ||
+----------------------------------------------------------------------+ | ||
| Authors: Levi Morrison <levim@php.net> | | ||
| Sammy Kaye Powers <sammyk@php.net> | | ||
+----------------------------------------------------------------------+ | ||
*/ | ||
|
||
#include "zend_observer.h" | ||
|
||
#include "zend_extensions.h" | ||
#include "zend_llist.h" | ||
#include "zend_vm.h" | ||
|
||
zend_llist zend_observers_fcall_list; | ||
int zend_observer_fcall_op_array_extension = -1; | ||
|
||
ZEND_TLS zend_arena *fcall_handlers_arena = NULL; | ||
|
||
ZEND_API extern inline void zend_observer_maybe_fcall_call_begin( | ||
zend_execute_data *execute_data); | ||
ZEND_API extern inline void zend_observer_maybe_fcall_call_end( | ||
zend_execute_data *execute_data, | ||
zval *return_value); | ||
|
||
// Call during minit/startup ONLY | ||
ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init) { | ||
/* We don't want to get an extension handle unless an ext installs an observer */ | ||
if (!ZEND_OBSERVER_ENABLED) { | ||
zend_observer_fcall_op_array_extension = | ||
zend_get_op_array_extension_handle(); | ||
|
||
/* ZEND_CALL_TRAMPOLINE has SPEC(OBSERVER) but zend_init_call_trampoline_op() | ||
* is called before any extensions have registered as an observer. So we | ||
* adjust the offset to the observed handler when we know we need to observe. */ | ||
ZEND_VM_SET_OPCODE_HANDLER(&EG(call_trampoline_op)); | ||
|
||
/* ZEND_HANDLE_EXCEPTION also has SPEC(OBSERVER) and no observer extensions | ||
* exist when zend_init_exception_op() is called. */ | ||
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op)); | ||
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op)+1); | ||
ZEND_VM_SET_OPCODE_HANDLER(EG(exception_op)+2); | ||
} | ||
zend_llist_add_element(&zend_observers_fcall_list, &init); | ||
} | ||
|
||
// Called by engine before MINITs | ||
ZEND_API void zend_observer_startup(void) { | ||
zend_llist_init(&zend_observers_fcall_list, sizeof(zend_observer_fcall_init), NULL, 1); | ||
} | ||
|
||
ZEND_API void zend_observer_activate(void) { | ||
if (ZEND_OBSERVER_ENABLED) { | ||
fcall_handlers_arena = zend_arena_create(4096); | ||
} | ||
} | ||
|
||
ZEND_API void zend_observer_deactivate(void) { | ||
if (fcall_handlers_arena) { | ||
zend_arena_destroy(fcall_handlers_arena); | ||
} | ||
} | ||
|
||
ZEND_API void zend_observer_shutdown(void) { | ||
zend_llist_destroy(&zend_observers_fcall_list); | ||
} | ||
|
||
ZEND_API void zend_observer_fcall_install(zend_function *function) { | ||
zend_llist_element *element; | ||
zend_llist *list = &zend_observers_fcall_list; | ||
zend_op_array *op_array = &function->op_array; | ||
|
||
if (fcall_handlers_arena == NULL) { | ||
return; | ||
} | ||
|
||
ZEND_ASSERT(function->type != ZEND_INTERNAL_FUNCTION); | ||
|
||
zend_llist handlers_list; | ||
zend_llist_init(&handlers_list, sizeof(zend_observer_fcall), NULL, 0); | ||
for (element = list->head; element; element = element->next) { | ||
zend_observer_fcall_init init; | ||
memcpy(&init, element->data, sizeof init); | ||
zend_observer_fcall handlers = init(function); | ||
if (handlers.begin || handlers.end) { | ||
zend_llist_add_element(&handlers_list, &handlers); | ||
} | ||
} | ||
|
||
ZEND_ASSERT(RUN_TIME_CACHE(op_array)); | ||
void *ext; | ||
if (handlers_list.count) { | ||
size_t size = sizeof(zend_observer_fcall_cache) + (handlers_list.count - 1) * sizeof(zend_observer_fcall); | ||
zend_observer_fcall_cache *cache = zend_arena_alloc(&fcall_handlers_arena, size); | ||
zend_observer_fcall *handler = cache->handlers; | ||
for (element = handlers_list.head; element; element = element->next) { | ||
memcpy(handler++, element->data, sizeof *handler); | ||
} | ||
cache->end = handler; | ||
ext = cache; | ||
} else { | ||
ext = ZEND_OBSERVER_NOT_OBSERVED; | ||
} | ||
|
||
ZEND_OBSERVER_HANDLERS(op_array) = ext; | ||
zend_llist_destroy(&handlers_list); | ||
} | ||
|
||
ZEND_API void zend_observe_fcall_begin( | ||
zend_observer_fcall_cache *cache, | ||
zend_execute_data *execute_data) | ||
{ | ||
zend_observer_fcall *handler, *end = cache->end; | ||
for (handler = cache->handlers; handler != end; ++handler) { | ||
if (handler->begin) { | ||
handler->begin(execute_data); | ||
} | ||
} | ||
} | ||
|
||
ZEND_API void zend_observer_fcall_call_end_helper( | ||
zend_execute_data *execute_data, | ||
zval *return_value) | ||
{ | ||
zend_function *func = execute_data->func; | ||
ZEND_ASSUME(ZEND_OBSERVABLE_FN(func->common.fn_flags)); | ||
void *observer_handlers = ZEND_OBSERVER_HANDLERS(&func->op_array); | ||
// TODO: Fix exceptions from generators | ||
// ZEND_ASSERT(observer_handlers); | ||
if (observer_handlers && observer_handlers != ZEND_OBSERVER_NOT_OBSERVED) { | ||
zend_observer_fcall_cache *cache = observer_handlers; | ||
zend_observe_fcall_end(cache, execute_data, return_value); | ||
} | ||
} | ||
|
||
ZEND_API void zend_observe_fcall_end( | ||
zend_observer_fcall_cache *cache, | ||
zend_execute_data *execute_data, | ||
zval *return_value) | ||
{ | ||
zend_observer_fcall *handler = cache->end, *end = cache->handlers; | ||
while (handler-- != end) { | ||
if (handler->end) { | ||
handler->end(execute_data, return_value); | ||
} | ||
} | ||
} | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
/* | ||
+----------------------------------------------------------------------+ | ||
| Zend Engine | | ||
+----------------------------------------------------------------------+ | ||
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | | ||
+----------------------------------------------------------------------+ | ||
| This source file is subject to version 2.00 of the Zend license, | | ||
| that is bundled with this package in the file LICENSE, and is | | ||
| available through the world-wide-web at the following url: | | ||
| http://www.zend.com/license/2_00.txt. | | ||
| If you did not receive a copy of the Zend license and are unable to | | ||
| obtain it through the world-wide-web, please send a note to | | ||
| license@zend.com so we can mail you a copy immediately. | | ||
+----------------------------------------------------------------------+ | ||
| Authors: Levi Morrison <levim@php.net> | | ||
| Sammy Kaye Powers <sammyk@php.net> | | ||
+----------------------------------------------------------------------+ | ||
*/ | ||
|
||
#ifndef ZEND_OBSERVER_H | ||
#define ZEND_OBSERVER_H | ||
|
||
#include "zend.h" | ||
#include "zend_compile.h" | ||
|
||
BEGIN_EXTERN_C() | ||
|
||
extern ZEND_API int zend_observer_fcall_op_array_extension; | ||
|
||
#define ZEND_OBSERVER_ENABLED (zend_observer_fcall_op_array_extension != -1) | ||
|
||
#define ZEND_OBSERVER_HANDLERS(op_array) \ | ||
ZEND_OP_ARRAY_EXTENSION(op_array, zend_observer_fcall_op_array_extension) | ||
|
||
#define ZEND_OBSERVER_NOT_OBSERVED ((void *) 2) | ||
|
||
#define ZEND_OBSERVABLE_FN(fn_flags) \ | ||
(ZEND_OBSERVER_ENABLED && \ | ||
!(fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_FAKE_CLOSURE))) | ||
|
||
struct zend_observer_fcall { | ||
void (*begin)(zend_execute_data *execute_data); | ||
void (*end)(zend_execute_data *execute_data, zval *retval); | ||
}; | ||
typedef struct zend_observer_fcall zend_observer_fcall; | ||
|
||
struct zend_observer_fcall_cache { | ||
// points after the last handler | ||
zend_observer_fcall *end; | ||
// a variadic array using "struct hack" | ||
zend_observer_fcall handlers[1]; | ||
}; | ||
typedef struct zend_observer_fcall_cache zend_observer_fcall_cache; | ||
|
||
/* If the fn should not be observed then return {NULL, NULL} */ | ||
typedef zend_observer_fcall(*zend_observer_fcall_init)(zend_function *func); | ||
|
||
// Call during minit/startup ONLY | ||
ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init); | ||
|
||
ZEND_API void zend_observer_startup(void); // Called by engine before MINITs | ||
ZEND_API void zend_observer_activate(void); | ||
ZEND_API void zend_observer_deactivate(void); | ||
ZEND_API void zend_observer_shutdown(void); | ||
|
||
ZEND_API void zend_observer_fcall_install(zend_function *function); | ||
|
||
ZEND_API void zend_observe_fcall_begin( | ||
zend_observer_fcall_cache *cache, | ||
zend_execute_data *execute_data); | ||
|
||
ZEND_API void zend_observe_fcall_end( | ||
zend_observer_fcall_cache *cache, | ||
zend_execute_data *execute_data, | ||
zval *return_value); | ||
|
||
ZEND_API void zend_observer_fcall_call_end_helper( | ||
zend_execute_data *execute_data, | ||
zval *return_value); | ||
|
||
ZEND_API zend_always_inline void zend_observer_maybe_fcall_call_begin( | ||
zend_execute_data *execute_data) | ||
{ | ||
ZEND_ASSUME(execute_data->func); | ||
zend_op_array *op_array = &execute_data->func->op_array; | ||
uint32_t fn_flags = op_array->fn_flags; | ||
if (ZEND_OBSERVABLE_FN(fn_flags) && !(fn_flags & ZEND_ACC_GENERATOR)) { | ||
void *observer_handlers = ZEND_OBSERVER_HANDLERS(op_array); | ||
if (!observer_handlers) { | ||
zend_observer_fcall_install((zend_function *)op_array); | ||
observer_handlers = ZEND_OBSERVER_HANDLERS(op_array); | ||
} | ||
|
||
ZEND_ASSERT(observer_handlers); | ||
if (observer_handlers != ZEND_OBSERVER_NOT_OBSERVED) { | ||
zend_observe_fcall_begin( | ||
(zend_observer_fcall_cache *)observer_handlers, | ||
execute_data); | ||
} | ||
} | ||
} | ||
|
||
ZEND_API zend_always_inline void zend_observer_maybe_fcall_call_end( | ||
zend_execute_data *execute_data, | ||
zval *return_value) | ||
{ | ||
zend_function *func = execute_data->func; | ||
if (ZEND_OBSERVABLE_FN(func->common.fn_flags)) { | ||
zend_observer_fcall_call_end_helper(execute_data, return_value); | ||
} | ||
} | ||
|
||
END_EXTERN_C() | ||
|
||
#endif /* ZEND_OBSERVER_H */ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.