Skip to content

Commit 6846ba4

Browse files
ZNeumannlavarou
andauthored
feat(agent): instrument Drupal 9.4 hooks with oapi (#701)
In addition to OAPI Drupal 9.4 support, this PR does some refactoring: 1. The hook stacks have been renamed from `module_invoke_all` to `invoke_all` to be more aligned with the name of Drupal functions that can now push to those stacks. 2. The generic wrapper function (originally added when Drupal 9.4 instrumentation was introduced) has been updated to support OAPI. 3. `nr_php_wrap_callable_before_after_clean` has been introduced as a parallel for `nr_php_wrap_user_function_before_after_clean`. To facilitate this, many of the internals have been extracted to a common function. 4. Speaking of, lots of the maintenance of the Drupal's hook stacks has been extracted to common functions to reduce code reuse --------- Co-authored-by: Michal Nowacki <[email protected]>
1 parent 33fed1d commit 6846ba4

File tree

9 files changed

+257
-95
lines changed

9 files changed

+257
-95
lines changed

agent/fw_drupal.c

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ static void nr_drupal_wrap_hook_within_module_invoke_all(
470470
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
471471
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
472472
zval* curr_hook
473-
= (zval*)nr_stack_get_top(&NRPRG(drupal_module_invoke_all_hooks));
473+
= (zval*)nr_stack_get_top(&NRPRG(drupal_invoke_all_hooks));
474474
if (!nr_php_is_zval_non_empty_string(curr_hook)) {
475475
nrl_verbosedebug(NRL_FRAMEWORK,
476476
"%s: cannot extract hook name from global stack",
@@ -480,8 +480,8 @@ static void nr_drupal_wrap_hook_within_module_invoke_all(
480480
char* hook_name = Z_STRVAL_P(curr_hook);
481481
size_t hook_len = Z_STRLEN_P(curr_hook);
482482
#else
483-
char* hook_name = NRPRG(drupal_module_invoke_all_hook);
484-
size_t hook_len = NRPRG(drupal_module_invoke_all_hook_len);
483+
char* hook_name = NRPRG(drupal_invoke_all_hook);
484+
size_t hook_len = NRPRG(drupal_invoke_all_hook_len);
485485
#endif
486486
if (NULL == hook_name) {
487487
nrl_verbosedebug(NRL_FRAMEWORK,
@@ -753,33 +753,21 @@ NR_PHP_WRAPPER(nr_drupal_wrap_module_invoke_all_before) {
753753
NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_DRUPAL);
754754

755755
hook_copy = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS);
756-
if (nr_php_is_zval_non_empty_string(hook_copy)) {
757-
nr_stack_push(&NRPRG(drupal_module_invoke_all_hooks), hook_copy);
758-
nr_stack_push(&NRPRG(drupal_module_invoke_all_states), (void*)!NULL);
759-
} else {
760-
nr_stack_push(&NRPRG(drupal_module_invoke_all_states), NULL);
761-
}
756+
nr_drupal_invoke_all_hook_stacks_push(hook_copy);
762757
}
763758
NR_PHP_WRAPPER_END
764759

765-
static void module_invoke_all_clean_stacks() {
766-
if ((bool)nr_stack_pop(&NRPRG(drupal_module_invoke_all_states))) {
767-
zval* hook_copy = nr_stack_pop(&NRPRG(drupal_module_invoke_all_hooks));
768-
nr_php_arg_release(&hook_copy);
769-
}
770-
}
771-
772760
NR_PHP_WRAPPER(nr_drupal_wrap_module_invoke_all_after) {
773761
(void)wraprec;
774-
module_invoke_all_clean_stacks();
762+
nr_drupal_invoke_all_hook_stacks_pop();
775763
}
776764
NR_PHP_WRAPPER_END
777765

778766
NR_PHP_WRAPPER(nr_drupal_wrap_module_invoke_all_clean) {
779767
NR_UNUSED_SPECIALFN;
780768
NR_UNUSED_FUNC_RETURN_VALUE;
781769
(void)wraprec;
782-
module_invoke_all_clean_stacks();
770+
nr_drupal_invoke_all_hook_stacks_pop();
783771
}
784772
NR_PHP_WRAPPER_END
785773

@@ -799,17 +787,17 @@ NR_PHP_WRAPPER(nr_drupal_wrap_module_invoke_all) {
799787
goto leave;
800788
}
801789

802-
prev_hook = NRPRG(drupal_module_invoke_all_hook);
803-
prev_hook_len = NRPRG(drupal_module_invoke_all_hook_len);
804-
NRPRG(drupal_module_invoke_all_hook)
790+
prev_hook = NRPRG(drupal_invoke_all_hook);
791+
prev_hook_len = NRPRG(drupal_invoke_all_hook_len);
792+
NRPRG(drupal_invoke_all_hook)
805793
= nr_strndup(Z_STRVAL_P(hook), Z_STRLEN_P(hook));
806-
NRPRG(drupal_module_invoke_all_hook_len) = Z_STRLEN_P(hook);
794+
NRPRG(drupal_invoke_all_hook_len) = Z_STRLEN_P(hook);
807795

808796
NR_PHP_WRAPPER_CALL;
809797

810-
nr_free(NRPRG(drupal_module_invoke_all_hook));
811-
NRPRG(drupal_module_invoke_all_hook) = prev_hook;
812-
NRPRG(drupal_module_invoke_all_hook_len) = prev_hook_len;
798+
nr_free(NRPRG(drupal_invoke_all_hook));
799+
NRPRG(drupal_invoke_all_hook) = prev_hook;
800+
NRPRG(drupal_invoke_all_hook_len) = prev_hook_len;
813801

814802
leave:
815803
nr_php_arg_release(&hook);

agent/fw_drupal8.c

Lines changed: 112 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,48 @@ static void nr_drupal8_add_method_callback(const zend_class_entry* ce,
7171
}
7272
}
7373

74+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
75+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
76+
static void nr_drupal8_add_method_callback_before_after_clean(
77+
const zend_class_entry* ce,
78+
const char* method,
79+
size_t method_len,
80+
nrspecialfn_t before_callback,
81+
nrspecialfn_t after_callback,
82+
nrspecialfn_t clean_callback) {
83+
zend_function* function = NULL;
84+
85+
if (NULL == ce) {
86+
nrl_verbosedebug(NRL_FRAMEWORK, "Drupal 8: got NULL class entry in %s",
87+
__func__);
88+
return;
89+
}
90+
91+
function = nr_php_find_class_method(ce, method);
92+
if (NULL == function) {
93+
nrl_verbosedebug(NRL_FRAMEWORK,
94+
"Drupal 8+: cannot get zend_function entry for %.*s::%.*s",
95+
NRSAFELEN(nr_php_class_entry_name_length(ce)),
96+
nr_php_class_entry_name(ce), NRSAFELEN(method_len),
97+
method);
98+
return;
99+
}
100+
101+
if (NULL == nr_php_get_wraprec(function)) {
102+
char* class_method = nr_formatf(
103+
"%.*s::%.*s", NRSAFELEN(nr_php_class_entry_name_length(ce)),
104+
nr_php_class_entry_name(ce), NRSAFELEN(method_len), method);
105+
106+
nr_php_wrap_user_function_before_after_clean_with_transience(
107+
class_method, nr_strlen(class_method),
108+
before_callback, after_callback, clean_callback,
109+
NR_WRAPREC_NOT_TRANSIENT);
110+
111+
nr_free(class_method);
112+
}
113+
}
114+
#endif // OAPI
115+
74116
/*
75117
* Purpose : Check if the given function or method is in the current call
76118
* stack.
@@ -383,18 +425,10 @@ NR_PHP_WRAPPER(nr_drupal8_post_implements_hook) {
383425
}
384426
NR_PHP_WRAPPER_END
385427

386-
/*
387-
* Temporary exclusion via #if defined OVERWRITE_ZEND_EXECUTE_DATA
388-
* This needs to be excluded for now because the hooks handling was changed
389-
* for oapi so the hook vars this is referencing do not exist.
390-
*
391-
*/
392-
#if defined OVERWRITE_ZEND_EXECUTE_DATA
393428
/*
394429
* Purpose : Handles ModuleHandlerInterface::invokeAllWith()'s callback
395430
* and ensure that the relevant module_hook function is instrumented.
396431
*/
397-
398432
NR_PHP_WRAPPER(nr_drupal94_invoke_all_with_callback) {
399433
zval* module = NULL;
400434

@@ -406,9 +440,24 @@ NR_PHP_WRAPPER(nr_drupal94_invoke_all_with_callback) {
406440
goto leave;
407441
}
408442

443+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
444+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
445+
zval* curr_hook
446+
= (zval*)nr_stack_get_top(&NRPRG(drupal_invoke_all_hooks));
447+
if (UNEXPECTED(!nr_php_is_zval_non_empty_string(curr_hook))) {
448+
nrl_verbosedebug(NRL_FRAMEWORK,
449+
"%s: cannot extract hook name from global stack",
450+
__func__);
451+
goto leave;
452+
}
409453
nr_drupal_hook_instrument(Z_STRVAL_P(module), Z_STRLEN_P(module),
410-
NRPRG(drupal_module_invoke_all_hook),
411-
NRPRG(drupal_module_invoke_all_hook_len) TSRMLS_CC);
454+
Z_STRVAL_P(curr_hook), Z_STRLEN_P(curr_hook));
455+
#else
456+
nr_drupal_hook_instrument(Z_STRVAL_P(module), Z_STRLEN_P(module),
457+
NRPRG(drupal_invoke_all_hook),
458+
NRPRG(drupal_invoke_all_hook_len) TSRMLS_CC);
459+
#endif // OAPI
460+
412461
leave:
413462
NR_PHP_WRAPPER_CALL;
414463
nr_php_arg_release(&module);
@@ -424,52 +473,77 @@ NR_PHP_WRAPPER_END
424473
NR_PHP_WRAPPER(nr_drupal94_invoke_all_with) {
425474
zval* callback = NULL;
426475
zval* hook = NULL;
476+
#if ZEND_MODULE_API_NO < ZEND_8_0_X_API_NO \
477+
|| defined OVERWRITE_ZEND_EXECUTE_DATA
427478
char* prev_hook = NULL;
428479
int prev_hook_len;
480+
#endif // not OAPI
429481

430482
(void)wraprec;
431483

432484
NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_DRUPAL8);
433485

434486
hook = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS TSRMLS_CC);
435487
if (!nr_php_is_zval_non_empty_string(hook)) {
488+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
489+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
490+
nr_php_arg_release(&hook);
491+
#endif // OAPI
436492
goto leave;
437493
}
438494

439-
prev_hook = NRPRG(drupal_module_invoke_all_hook);
440-
prev_hook_len = NRPRG(drupal_module_invoke_all_hook_len);
441-
NRPRG(drupal_module_invoke_all_hook)
495+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
496+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
497+
nr_drupal_invoke_all_hook_stacks_push(hook);
498+
#else
499+
prev_hook = NRPRG(drupal_invoke_all_hook);
500+
prev_hook_len = NRPRG(drupal_invoke_all_hook_len);
501+
NRPRG(drupal_invoke_all_hook)
442502
= nr_strndup(Z_STRVAL_P(hook), Z_STRLEN_P(hook));
443-
NRPRG(drupal_module_invoke_all_hook_len) = Z_STRLEN_P(hook);
444-
503+
NRPRG(drupal_invoke_all_hook_len) = Z_STRLEN_P(hook);
504+
#endif // OAPI
445505
callback = nr_php_arg_get(2, NR_EXECUTE_ORIG_ARGS TSRMLS_CC);
506+
446507
/* This instrumentation will fail if callback has already been wrapped
447508
* with a special instrumentation callback in a different context.
448509
* In this scenario, we will be unable to instrument hooks and modules for
449510
* this particular call */
450-
451-
/*
452-
* Temporary exclusion via #if defined OVERWRITE_ZEND_EXECUTE_DATA
453-
* This needs to be excluded for now because the hooks handling was changed
454-
* for oapi so the hook vars this is referencing do not exist.
455-
*
456-
*/
457-
#if defined OVERWRITE_ZEND_EXECUTE_DATA
458511
nr_php_wrap_generic_callable(callback,
459512
nr_drupal94_invoke_all_with_callback TSRMLS_CC);
460-
#endif
461513
NR_PHP_WRAPPER_CALL;
462514

463515
nr_php_arg_release(&callback);
464-
nr_free(NRPRG(drupal_module_invoke_all_hook));
465-
NRPRG(drupal_module_invoke_all_hook) = prev_hook;
466-
NRPRG(drupal_module_invoke_all_hook_len) = prev_hook_len;
467-
468-
leave:
516+
#if ZEND_MODULE_API_NO < ZEND_8_0_X_API_NO \
517+
|| defined OVERWRITE_ZEND_EXECUTE_DATA
518+
nr_free(NRPRG(drupal_invoke_all_hook));
519+
NRPRG(drupal_invoke_all_hook) = prev_hook;
520+
NRPRG(drupal_invoke_all_hook_len) = prev_hook_len;
521+
#endif // not OAPI
522+
523+
leave: ;
524+
#if ZEND_MODULE_API_NO < ZEND_8_0_X_API_NO \
525+
|| defined OVERWRITE_ZEND_EXECUTE_DATA
526+
/* for OAPI, the _after callback handles this free */
469527
nr_php_arg_release(&hook);
528+
#endif // not OAPI
529+
}
530+
NR_PHP_WRAPPER_END
531+
532+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
533+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
534+
NR_PHP_WRAPPER(nr_drupal94_invoke_all_with_after) {
535+
(void)wraprec;
536+
nr_drupal_invoke_all_hook_stacks_pop();
537+
}
538+
NR_PHP_WRAPPER_END
539+
540+
NR_PHP_WRAPPER(nr_drupal94_invoke_all_with_clean) {
541+
(void)wraprec;
542+
nr_drupal_invoke_all_hook_stacks_pop();
470543
}
471544
NR_PHP_WRAPPER_END
472-
#endif /* temporary: defined OVERWRITE_ZEND_EXECUTE_DATA */
545+
#endif // OAPI
546+
473547
/*
474548
* Purpose : Wrap the invoke() method of the module handler instance in use.
475549
*/
@@ -502,13 +576,14 @@ NR_PHP_WRAPPER(nr_drupal8_module_handler) {
502576
nr_drupal8_add_method_callback(ce, NR_PSTR("implementshook"),
503577
nr_drupal8_post_implements_hook TSRMLS_CC);
504578
/* Drupal 9.4 introduced a replacement method for getImplentations */
505-
/*
506-
* Temporary exclusion via #if defined OVERWRITE_ZEND_EXECUTE_DATA
507-
* This needs to be excluded for now because the hooks handling was changed
508-
* for oapi so the hook vars this is referencing do not exist.
509-
*
510-
*/
511-
#if defined OVERWRITE_ZEND_EXECUTE_DATA
579+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
580+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
581+
nr_drupal8_add_method_callback_before_after_clean(
582+
ce, NR_PSTR("invokeallwith"),
583+
nr_drupal94_invoke_all_with,
584+
nr_drupal94_invoke_all_with_after,
585+
nr_drupal94_invoke_all_with_clean);
586+
#else
512587
nr_drupal8_add_method_callback(ce, NR_PSTR("invokeallwith"),
513588
nr_drupal94_invoke_all_with TSRMLS_CC);
514589
#endif

agent/fw_drupal_common.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,22 @@ void nr_drupal_headers_add(zval* arg, bool is_drupal_7 TSRMLS_DC) {
294294

295295
nr_php_zval_free(&newrelic_headers);
296296
}
297+
298+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
299+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
300+
void nr_drupal_invoke_all_hook_stacks_push(zval* hook_copy) {
301+
if (nr_php_is_zval_non_empty_string(hook_copy)) {
302+
nr_stack_push(&NRPRG(drupal_invoke_all_hooks), hook_copy);
303+
nr_stack_push(&NRPRG(drupal_invoke_all_states), (void*)!NULL);
304+
} else {
305+
nr_stack_push(&NRPRG(drupal_invoke_all_states), NULL);
306+
}
307+
}
308+
309+
void nr_drupal_invoke_all_hook_stacks_pop() {
310+
if ((bool)nr_stack_pop(&NRPRG(drupal_invoke_all_states))) {
311+
zval* hook_copy = nr_stack_pop(&NRPRG(drupal_invoke_all_hooks));
312+
nr_php_arg_release(&hook_copy);
313+
}
314+
}
315+
#endif

agent/fw_drupal_common.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,23 @@ nr_status_t module_invoke_all_parse_module_and_hook_from_strings(
147147
*/
148148
void nr_drupal_headers_add(zval* arg, bool is_drupal_7 TSRMLS_DC);
149149

150+
151+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
152+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
153+
/*
154+
* Purpose: Before an invoke_all style call, adds the hook to that hook states stacks
155+
*
156+
* Params : 1. A zval holding a copy of the hook invoked, to be managed by the hook
157+
* states stacks and freed by nr_drupal_invoke_all_hook_stacks_pop() after the
158+
* invoke_all call completes
159+
*/
160+
void nr_drupal_invoke_all_hook_stacks_push(zval* hook_copy);
161+
162+
/*
163+
* Purpose: After an invoke_all style call, pops that states stack and conditionally
164+
* pops the hook stack based on the previously popped state
165+
*/
166+
void nr_drupal_invoke_all_hook_stacks_pop();
167+
#endif // OAPI
168+
150169
#endif /* FW_DRUPAL_COMMON_HDR */

agent/php_newrelic.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -410,13 +410,13 @@ int framework_version; /* Current framework version */
410410
/* Without OAPI, we are able to utilize the call stack to keep track
411411
* of the previous hooks. With OAPI, we can no longer do this so
412412
* we track the stack manually */
413-
nr_stack_t drupal_module_invoke_all_hooks; /* stack of Drupal hooks */
414-
nr_stack_t drupal_module_invoke_all_states; /* stack of bools indicating
413+
nr_stack_t drupal_invoke_all_hooks; /* stack of Drupal hooks */
414+
nr_stack_t drupal_invoke_all_states; /* stack of bools indicating
415415
whether the current hook
416416
needs to be released */
417417
#else
418-
char* drupal_module_invoke_all_hook; /* The current Drupal hook */
419-
size_t drupal_module_invoke_all_hook_len; /* The length of the current Drupal
418+
char* drupal_invoke_all_hook; /* The current Drupal hook */
419+
size_t drupal_invoke_all_hook_len; /* The length of the current Drupal
420420
hook */
421421
#endif //OAPI
422422
size_t drupal_http_request_depth; /* The current depth of drupal_http_request()

agent/php_rinit.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,11 @@ PHP_RINIT_FUNCTION(newrelic) {
125125
nr_stack_init(&NRPRG(predis_ctxs), NR_STACK_DEFAULT_CAPACITY);
126126
nr_stack_init(&NRPRG(wordpress_tags), NR_STACK_DEFAULT_CAPACITY);
127127
nr_stack_init(&NRPRG(wordpress_tag_states), NR_STACK_DEFAULT_CAPACITY);
128-
nr_stack_init(&NRPRG(drupal_module_invoke_all_hooks), NR_STACK_DEFAULT_CAPACITY);
129-
nr_stack_init(&NRPRG(drupal_module_invoke_all_states), NR_STACK_DEFAULT_CAPACITY);
128+
nr_stack_init(&NRPRG(drupal_invoke_all_hooks), NR_STACK_DEFAULT_CAPACITY);
129+
nr_stack_init(&NRPRG(drupal_invoke_all_states), NR_STACK_DEFAULT_CAPACITY);
130130
NRPRG(predis_ctxs).dtor = str_stack_dtor;
131131
NRPRG(wordpress_tags).dtor = str_stack_dtor;
132-
NRPRG(drupal_module_invoke_all_hooks).dtor = zval_stack_dtor;
132+
NRPRG(drupal_invoke_all_hooks).dtor = zval_stack_dtor;
133133
#endif
134134

135135
NRPRG(mysql_last_conn) = NULL;

agent/php_rshutdown.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ int nr_php_post_deactivate(void) {
120120
*/
121121
nr_stack_destroy_fields(&NRPRG(wordpress_tags));
122122
nr_stack_destroy_fields(&NRPRG(wordpress_tag_states));
123-
nr_stack_destroy_fields(&NRPRG(drupal_module_invoke_all_hooks));
124-
nr_stack_destroy_fields(&NRPRG(drupal_module_invoke_all_states));
123+
nr_stack_destroy_fields(&NRPRG(drupal_invoke_all_hooks));
124+
nr_stack_destroy_fields(&NRPRG(drupal_invoke_all_states));
125125
#endif
126126

127127
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \

0 commit comments

Comments
 (0)