Skip to content

Commit 50d26d9

Browse files
authored
Merge pull request #1162 from newrelic/dev
Release 12.5
2 parents 02a11df + 85c3694 commit 50d26d9

42 files changed

Lines changed: 1237 additions & 305 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

agent/fw_drupal8.c

Lines changed: 191 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -625,36 +625,77 @@ static bool nr_is_invalid_key_val_arr(nr_php_string_hash_key_t* key,
625625
}
626626

627627
/*
628-
* Purpose: Instrument Drupal Attribute Hooks for Drupal 11.1+
628+
* Extract all hook functions and hook data for the drupal 11.1-11.2
629+
* application
629630
*
630-
* Params: 1. A zval pointer to the moduleHandler instance in use by Drupal.
631+
* @param zval* hookmap: a valid, non-null pointer to the zval containing the
632+
* ModuleHandler `hookImplementationsMap` class property.
631633
*
632-
* Return: bool
634+
* @return bool: true for success, false for failure
635+
*
636+
* Drupal 11.1 introduces the `hookImplementationsMap` property on the
637+
* ModuleHandler class. This map contains all the hooks registered for the
638+
* current drupal application.
639+
*
640+
* The structure of `hookImplementationsMap` resembles the following:
633641
*
642+
* array(348) { <------ the `hookImplementationsMap` property array
643+
* ["hook_info"]=> <------ a subarray keyed by hook name
644+
* array(1) {
645+
* ["Drupal\Core\Extension\ProceduralCall"]=> <- an array keyed by class name
646+
* array(2) {
647+
* ["system_hook_info"]=> <----- the function name implementing the hook
648+
* string(6) "system" <----- the module name
649+
* ["views_hook_info"]=>
650+
* string(5) "views"
651+
* }
652+
* }
653+
* ...
654+
*
655+
* The `ProceduralCall` class name is a special case where the classname is not
656+
* required to call the hook and should be left out of the hookpath used to name
657+
* the hook.
658+
*
659+
* An example of the `help` hook:
660+
*
661+
* ["help"]=>
662+
* array(51) {
663+
* ["Drupal\announcements_feed\Hook\AnnouncementsFeedHooks"]=>
664+
* array(1) {
665+
* ["help"]=>
666+
* string(18) "announcements_feed"
667+
* }
668+
* ["Drupal\automated_cron\Hook\AutomatedCronHooks"]=>
669+
* array(1) {
670+
* ["help"]=>
671+
* string(14) "automated_cron"
672+
* }
673+
* ["Drupal\big_pipe\Hook\BigPipeHooks"]=>
674+
* array(1) {
675+
* ["help"]=>
676+
* string(8) "big_pipe"
677+
* }
678+
* ["Drupal\block\Hook\BlockHooks"]=>
679+
* array(1) {
680+
* ["help"]=>
681+
* string(5) "block"
682+
* }
683+
* ...
684+
*
685+
* To provide hook instrumentation, we loop through all the elements of the
686+
* array and extract the hook key name, class key name, function key name and
687+
* module name, and construct a wrapper using that data for each hook.
634688
*/
635-
static bool nr_drupal_hook_attribute_instrument(zval* module_handler) {
636-
zval* hook_implementation_map = NULL;
637-
689+
static bool nr_drupal_parse_hookmap(zval* hookmap) {
638690
nr_php_string_hash_key_t* hook_key = NULL;
639691
zval* hook_val = NULL;
640692
nr_php_string_hash_key_t* class_key = NULL;
641693
zval* class_val = NULL;
642694
nr_php_string_hash_key_t* method_key = NULL;
643695
zval* module_val = NULL;
644-
645696
char* hookpath = NULL;
646697

647-
hook_implementation_map = nr_php_get_zval_object_property(
648-
module_handler, "hookImplementationsMap");
649-
650-
if (!nr_php_is_zval_valid_array(hook_implementation_map)) {
651-
nrl_verbosedebug(NRL_FRAMEWORK,
652-
"hookImplementationsMap property not a valid array");
653-
return false;
654-
}
655-
656-
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(hook_implementation_map), hook_key,
657-
hook_val) {
698+
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(hookmap), hook_key, hook_val) {
658699
if (nr_is_invalid_key_val_arr(hook_key, hook_val, "hook")) {
659700
return false;
660701
}
@@ -704,6 +745,138 @@ static bool nr_drupal_hook_attribute_instrument(zval* module_handler) {
704745
return true;
705746
}
706747

748+
/*
749+
* Extract all hook functions and hook data for the drupal 11.3+ application
750+
*
751+
* @param zval* hookList: a valid, non-null pointer to the zval containing the
752+
* ModuleHandler `hookLists` class property.
753+
*
754+
* @return bool: true for success, false for failure
755+
*
756+
* Drupal 11.3+ again changes the internal model of how hooks are stored
757+
* and represented from prior versions. The `hookImplementationsMap`
758+
* property introduced in Drupal 11.1 is replaced with `hookLists`, and the
759+
* internal representation of the hook data is flattened compared to 11.1.
760+
*
761+
* The structure of `hookLists` resembles the following:
762+
*
763+
* array(244) { <------ the `hookLists` property array
764+
* ["hook_info"]=> <------ a subarray keyed by hook name
765+
* array(2) {
766+
* ["system_hook_info"]=> <------ the function name implementing the hook
767+
* string(6) "system" <------ the module name
768+
* ["views_hook_info"]=>
769+
* string(5) "views"
770+
* }
771+
* ...
772+
*
773+
* An example of the `help` hook where the full classpath is contained in the
774+
* function key:
775+
*
776+
* ["help"]=>
777+
* array(50) {
778+
* ["Drupal\announcements_feed\Hook\AnnouncementsFeedHelpHooks::help"]=>
779+
* string(18) "announcements_feed"
780+
* ["Drupal\automated_cron\Hook\AutomatedCronHooks::help"]=>
781+
* string(14) "automated_cron"
782+
* ["Drupal\big_pipe\Hook\BigPipeHooks::help"]=>
783+
* string(8) "big_pipe"
784+
* ["Drupal\block\Hook\BlockHooks::help"]=>
785+
* string(5) "block"
786+
* ...
787+
*
788+
* To provide hook instrumentation, we loop through all the elements of the
789+
* array and extract the hook key name, function key name, and module name,
790+
* and construct a wrapper using that data for each hook.
791+
*/
792+
static bool nr_drupal_parse_hooklist(zval* hooklist) {
793+
nr_php_string_hash_key_t* hook_key = NULL;
794+
zval* hook_val = NULL;
795+
nr_php_string_hash_key_t* func_key = NULL;
796+
zval* module_val = NULL;
797+
798+
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(hooklist), hook_key, hook_val) {
799+
if (nr_is_invalid_key_val_arr(hook_key, hook_val, "hook")) {
800+
return false;
801+
}
802+
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(hook_val), func_key, module_val) {
803+
if (NULL == func_key) {
804+
nrl_verbosedebug(NRL_FRAMEWORK,
805+
"hookLists[hook][func]: NULL key for hook: %s",
806+
NRSAFESTR(ZEND_STRING_VALUE(hook_key)));
807+
return false;
808+
}
809+
if (NULL == module_val) {
810+
nrl_verbosedebug(
811+
NRL_FRAMEWORK,
812+
"hookLists[hook][func]: NULL module for func %s on hook %s",
813+
NRSAFESTR(ZEND_STRING_VALUE(func_key)),
814+
NRSAFESTR(ZEND_STRING_VALUE(hook_key)));
815+
return false;
816+
}
817+
if (0 == nr_php_is_zval_valid_string(module_val)) {
818+
nrl_verbosedebug(
819+
NRL_FRAMEWORK,
820+
"hookLists: non-string module value for func %s on hook %s",
821+
NRSAFESTR(ZEND_STRING_VALUE(func_key)),
822+
NRSAFESTR(ZEND_STRING_VALUE(hook_key)));
823+
return false;
824+
}
825+
826+
nrl_verbosedebug(
827+
NRL_FRAMEWORK,
828+
"hookLists: creating wrapper for hook: %s func: %s module: %s",
829+
NRSAFESTR(ZEND_STRING_VALUE(hook_key)),
830+
NRSAFESTR(ZEND_STRING_VALUE(func_key)), Z_STRVAL_P(module_val));
831+
832+
nr_php_wrap_user_function_drupal(
833+
ZEND_STRING_VALUE(func_key), ZEND_STRING_LEN(func_key),
834+
Z_STRVAL_P(module_val), Z_STRLEN_P(module_val),
835+
ZEND_STRING_VALUE(hook_key), ZEND_STRING_LEN(hook_key));
836+
}
837+
ZEND_HASH_FOREACH_END();
838+
}
839+
ZEND_HASH_FOREACH_END();
840+
841+
return true;
842+
}
843+
844+
/*
845+
* Purpose: Instrument Drupal Attribute Hooks for Drupal 11.1+
846+
*
847+
* Params: 1. A zval pointer to the moduleHandler instance in use by Drupal.
848+
*
849+
* Return: bool
850+
*
851+
*/
852+
static bool nr_drupal_hook_attribute_instrument(zval* module_handler) {
853+
zval* hook_implementation_map = NULL;
854+
855+
bool uses_hooklist = false;
856+
857+
hook_implementation_map = nr_php_get_zval_object_property(
858+
module_handler, "hookImplementationsMap");
859+
860+
if (!nr_php_is_zval_valid_array(hook_implementation_map)) {
861+
hook_implementation_map
862+
= nr_php_get_zval_object_property(module_handler, "hookLists");
863+
if (!nr_php_is_zval_valid_array(hook_implementation_map)) {
864+
nrl_verbosedebug(NRL_FRAMEWORK,
865+
"unable to extract hook array from ModuleHandler class");
866+
return false;
867+
}
868+
uses_hooklist = true;
869+
}
870+
871+
if (uses_hooklist) {
872+
nr_drupal_parse_hooklist(hook_implementation_map);
873+
} else {
874+
nr_drupal_parse_hookmap(hook_implementation_map);
875+
}
876+
877+
return true;
878+
}
879+
707880
/*
708881
* Purpose : Wrap the invoke() method of the module handler instance in use.
709882
*/

agent/lib_mongodb.c

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,10 @@ NR_PHP_WRAPPER(nr_mongodb_operation_before) {
190190
NR_PHP_WRAPPER_END
191191

192192
NR_PHP_WRAPPER(nr_mongodb_operation_after) {
193-
const char* this_klass = "MongoDB\\Operation\\Executable";
194193
zval* collection = NULL;
195194
zval* database = NULL;
196195
zval* server = NULL;
197196
zval* this_var = NULL;
198-
bool discard_segment = false;
199197
nr_datastore_instance_t instance = {
200198
.host = NULL,
201199
.port_path_or_id = NULL,
@@ -218,18 +216,8 @@ NR_PHP_WRAPPER(nr_mongodb_operation_after) {
218216
},
219217
};
220218
#pragma GCC diagnostic pop
221-
/*
222-
* We check for the interface all Collection operations extend, rather than
223-
* their specific class. Not all operations have the properties we need but
224-
* the ones we hook do (as of mongo-php-library v.1.1).
225-
*/
219+
226220
this_var = nr_php_scope_get(NR_EXECUTE_ORIG_ARGS);
227-
if (!nr_php_object_instanceof_class(this_var, this_klass)) {
228-
nrl_verbosedebug(NRL_FRAMEWORK, "%s: operation is not %s", __func__,
229-
this_klass);
230-
discard_segment = true;
231-
goto leave;
232-
}
233221

234222
collection = nr_php_get_zval_object_property(this_var, "collectionName");
235223
if (nr_php_is_zval_valid_string(collection)) {
@@ -245,12 +233,7 @@ NR_PHP_WRAPPER(nr_mongodb_operation_after) {
245233
nr_mongodb_get_host_and_port_path_or_id(server, &instance.host,
246234
&instance.port_path_or_id);
247235

248-
leave:
249-
if (discard_segment) {
250-
nr_segment_discard(&auto_segment);
251-
} else {
252-
nr_segment_datastore_end(&auto_segment, &params);
253-
}
236+
nr_segment_datastore_end(&auto_segment, &params);
254237
nr_php_arg_release(&server);
255238
nr_php_scope_release(&this_var);
256239
nr_free(instance.host);

axiom/nr_segment_datastore.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ static char* create_metrics(nr_segment_t* segment,
7474
}
7575
}
7676

77-
instance_metric = nr_formatf("Datastore/instance/%s/%s/%s", product,
78-
instance->host, instance->port_path_or_id);
79-
80-
nr_segment_add_metric(segment, instance_metric, false);
8177
nr_datastore_instance_set_host(&datastore->instance, instance->host);
8278
nr_datastore_instance_set_port_path_or_id(&datastore->instance,
8379
instance->port_path_or_id);
8480

81+
instance_metric = nr_formatf("Datastore/instance/%s/%s/%s", product,
82+
datastore->instance.host, datastore->instance.port_path_or_id);
83+
nr_segment_add_metric(segment, instance_metric, false);
84+
8585
nr_free(instance_metric);
8686

8787
return scoped_metric;

0 commit comments

Comments
 (0)