From 1291a76ff1281c200de07a230c329a4ab12ff8fd Mon Sep 17 00:00:00 2001 From: Amber Sistla Date: Tue, 6 Aug 2024 07:50:10 -0700 Subject: [PATCH 01/45] chore: Bump version to 11.1 (#945) --- VERSION | 2 +- axiom/nr_version.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 4a68b557e..68d8f15e2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.0.0 \ No newline at end of file +11.1.0 diff --git a/axiom/nr_version.c b/axiom/nr_version.c index 8f4a3646d..018770a1a 100644 --- a/axiom/nr_version.c +++ b/axiom/nr_version.c @@ -46,8 +46,9 @@ * wallflower 06May2024 (10.20) * xerophyllum 20May2024 (10.21) * yarrow 26Jun2024 (10.22) + * zinnia 30Jul2024 (11.0) */ -#define NR_CODENAME "zinnia" +#define NR_CODENAME "amethyst" const char* nr_version(void) { return NR_STR2(NR_VERSION); From a95eb5120aab89b7ea075adb9009fc11ee095610 Mon Sep 17 00:00:00 2001 From: Michael Fulbright <89205663+mfulb@users.noreply.github.com> Date: Tue, 6 Aug 2024 12:46:56 -0400 Subject: [PATCH 02/45] fix(testing): Fixes count check for apdex metrics (#946) The current code for EXPECT_METRICS_EXIST would verify the count of any metric was >0 before passing the test. However, with apdex metrics the first field is not the count of metrics but the count of the number of times the transaction time was less than apdex_t. This change allows for the count to be 0 for apdex metrics. --- daemon/internal/newrelic/integration/test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/daemon/internal/newrelic/integration/test.go b/daemon/internal/newrelic/integration/test.go index 0f9342030..f80b3e986 100644 --- a/daemon/internal/newrelic/integration/test.go +++ b/daemon/internal/newrelic/integration/test.go @@ -544,7 +544,12 @@ func (t *Test) compareMetricsExist(harvest *newrelic.Harvest) { actualCount := int64(math.Round(actualData.([]interface{})[0].(float64))) metricPasses := false - if (count == -1 && actualCount > 0) || (actualCount == count) { + + // apdex metrics can have a count of 0 since the count field is + // actually the "satisfied" count, not a total count of metric + // as it is for other types of metrics + apdex_metric := strings.HasPrefix(expected, "Apdex/") + if (count == -1 && (apdex_metric || actualCount > 0)) || (actualCount == count) { metricPasses = true } From 010f1f19f4a32bd19b58e113bd98b0accd76ea63 Mon Sep 17 00:00:00 2001 From: Michael Fulbright <89205663+mfulb@users.noreply.github.com> Date: Thu, 15 Aug 2024 12:41:19 -0400 Subject: [PATCH 03/45] chore(agent): Changes code to use NR string routines (#949) Switches to use nr_strlcpy and nr_strcpy instead of libc versions for better platform generality. --- agent/lib_aws_sdk_php.c | 4 ++-- axiom/nr_distributed_trace.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/lib_aws_sdk_php.c b/agent/lib_aws_sdk_php.c index d9055690d..cff08dbfd 100644 --- a/agent/lib_aws_sdk_php.c +++ b/agent/lib_aws_sdk_php.c @@ -88,9 +88,9 @@ void nr_lib_aws_sdk_php_add_supportability_service_metric( } cp = buf; - strcpy(cp, PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX); + nr_strcpy(cp, PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX); cp += PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX_LEN - 1; - strlcpy(cp, service_name, MAX_AWS_SERVICE_NAME_LEN); + nr_strlcpy(cp, service_name, MAX_AWS_SERVICE_NAME_LEN); nrm_force_add(NRPRG(txn) ? NRTXN(unscoped_metrics) : 0, buf, 0); } diff --git a/axiom/nr_distributed_trace.c b/axiom/nr_distributed_trace.c index bdc587e24..25fcd27d5 100644 --- a/axiom/nr_distributed_trace.c +++ b/axiom/nr_distributed_trace.c @@ -497,7 +497,7 @@ void nr_distributed_trace_set_trace_id(nr_distributed_trace_t* dt, for (int i = 0; i < padding; i++) { dest[i] = '0'; } - strcpy(dest + padding, trace_id); + nr_strcpy(dest + padding, trace_id); dt->trace_id = dest; } else { dt->trace_id = nr_strdup(trace_id); From 0ba870b7b7f434884c9f863668cc53f9e7c0de02 Mon Sep 17 00:00:00 2001 From: Michael Fulbright <89205663+mfulb@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:47:04 -0400 Subject: [PATCH 04/45] feat(agent): Adds supportability metrics for PHP and agent version (#947) Adds supportability metrics for the agent and PHP version. I did clean up some ZTS macros as well but I think it is obvious what is a real change and what isnt. --------- Co-authored-by: Michal Nowacki --- agent/Makefile.frag | 12 +- agent/php_newrelic.c | 5 +- agent/php_txn.c | 45 ++++++ agent/php_txn_private.h | 29 ++++ agent/tests/test_txn.c | 136 +++++++++++++++++- daemon/cmd/integration_runner/main.go | 8 ++ daemon/internal/newrelic/integration/test.go | 2 + daemon/internal/newrelic/integration/util.go | 33 +++++ .../test_php_and_agent_version_metrics.php | 27 ++++ 9 files changed, 290 insertions(+), 7 deletions(-) create mode 100644 daemon/internal/newrelic/integration/util.go create mode 100644 tests/integration/supportability/test_php_and_agent_version_metrics.php diff --git a/agent/Makefile.frag b/agent/Makefile.frag index def094486..1cfe1606c 100644 --- a/agent/Makefile.frag +++ b/agent/Makefile.frag @@ -49,13 +49,17 @@ $(PHP_MODULES): .libs/deps.mk newrelic.la: $(PHP_AXIOM)/libaxiom.a # -# The version number is needed by php_newrelic.c as a static string literal, +# The version number is needed by several source files as a static string literal, # so it can be placed in the module entry. # include ../make/version.mk + php_newrelic.lo: CPPFLAGS += -DNR_VERSION="\"$(AGENT_VERSION)\"" php_newrelic.lo: ../VERSION +php_txn.lo: CPPFLAGS += -DNR_VERSION="\"$(AGENT_VERSION)\"" +php_txn.lo: ../VERSION + # # Unit tests! # @@ -261,6 +265,12 @@ ifeq (/opt/nr/lamp/lib,$(findstring /opt/nr/lamp/lib,$(PHP_EMBED_LIBRARY))) endif endif +# +# Need agent version for test_txn +# +tests/test_txn.o: EXTRA_CFLAGS += -DNR_VERSION="\"$(AGENT_VERSION)\"" +tests/test_txn.o: ../VERSION + # # Used when linking test binaries. # diff --git a/agent/php_newrelic.c b/agent/php_newrelic.c index 210c04428..912dff365 100644 --- a/agent/php_newrelic.c +++ b/agent/php_newrelic.c @@ -120,8 +120,8 @@ ZEND_BEGIN_ARG_INFO_EX(newrelic_arginfo_void, 0, 0, 0) ZEND_END_ARG_INFO() #endif /* PHP 8.0+ */ -ZEND_BEGIN_ARG_INFO_EX(newrelic_get_request_metadata_arginfo, 0, 0, 0) -ZEND_ARG_INFO(0, transport) +ZEND_BEGIN_ARG_INFO_EX(newrelic_get_request_metadata_arginfo, 0, 0, 0) +ZEND_ARG_INFO(0, transport) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(newrelic_add_custom_parameter_arginfo, 0, 0, 2) @@ -223,7 +223,6 @@ ZEND_BEGIN_ARG_INFO_EX(newrelic_set_user_id_arginfo, 0, 0, 1) ZEND_ARG_INFO(0, uuid) ZEND_END_ARG_INFO() - ZEND_BEGIN_ARG_INFO_EX(newrelic_set_error_group_callback_arginfo, 0, 0, 1) ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() diff --git a/agent/php_txn.c b/agent/php_txn.c index cdfa1269f..785007b41 100644 --- a/agent/php_txn.c +++ b/agent/php_txn.c @@ -669,6 +669,48 @@ static void nr_php_txn_send_metrics_once(nrtxn_t* txn TSRMLS_DC) { #undef FMT_BOOL } +void nr_php_txn_create_agent_version_metric(nrtxn_t* txn) { + if (NULL == txn) { + return; + } + + nrm_force_add(NRTXN(unscoped_metrics), + "Supportability/PHP/AgentVersion/" NR_VERSION, 0); +} + +void nr_php_txn_create_php_version_metric(nrtxn_t* txn, const char* version) { + char* metric_name = NULL; + + if (NULL == txn) { + return; + } + + if (nr_strempty(version)) { + return; + } + + metric_name = nr_formatf("Supportability/PHP/Version/%s", version); + nrm_force_add(NRTXN(unscoped_metrics), metric_name, 0); + nr_free(metric_name); +} + +void nr_php_txn_create_agent_php_version_metrics(nrtxn_t* txn) { + char* version = NULL; + + if (NULL == txn) { + return; + } + nr_php_txn_create_agent_version_metric(txn); + + if (!nr_strempty(NR_PHP_PROCESS_GLOBALS(php_version))) { + version = NR_PHP_PROCESS_GLOBALS(php_version); + } else { + version = "unknown"; + } + + nr_php_txn_create_php_version_metric(txn, version); +} + nr_status_t nr_php_txn_begin(const char* appnames, const char* license TSRMLS_DC) { nrtxnopt_t opts; @@ -1120,6 +1162,9 @@ nr_status_t nr_php_txn_end(int ignoretxn, int in_post_deactivate TSRMLS_DC) { "Supportability/execute/allocated_segment_count", nr_txn_allocated_segment_count(txn)); + /* Agent and PHP version metrics*/ + nr_php_txn_create_agent_php_version_metrics(txn); + /* Add CPU and memory metrics */ nr_php_resource_usage_sampler_end(TSRMLS_C); diff --git a/agent/php_txn_private.h b/agent/php_txn_private.h index 743adc06f..bf49eb107 100644 --- a/agent/php_txn_private.h +++ b/agent/php_txn_private.h @@ -36,3 +36,32 @@ nrobj_t* nr_php_txn_get_supported_security_policy_settings(); * Params : 1. The current transaction. */ extern void nr_php_txn_handle_fpm_error(nrtxn_t* txn TSRMLS_DC); + +/* + * Purpose : Create and record metrics for the PHP and agent versions. + * + * Params : 1. The current transaction. + * + * Notes : This function relies on NR_VERSION and the value of + * NRPRG(php_version) to create the metrics. + */ +extern void nr_php_txn_create_agent_php_version_metrics(nrtxn_t* txn); + +/* + * Purpose : Create and record metric for a specific agent version. + * + * Params : 1. The current transaction. + * + * Notes : This function relies on the value of the macro NR_VERSION + * to create. + */ +extern void nr_php_txn_create_agent_version_metric(nrtxn_t* txn); + +/* + * Purpose : Create and record metric for a specific PHP version. + * + * Params : 1. The current transaction. + * 2. The PHP agent version. + */ +extern void nr_php_txn_create_php_version_metric(nrtxn_t* txn, + const char* version); diff --git a/agent/tests/test_txn.c b/agent/tests/test_txn.c index 3209e3c27..50c4ff8bc 100644 --- a/agent/tests/test_txn.c +++ b/agent/tests/test_txn.c @@ -161,6 +161,133 @@ static void test_max_segments_config_values(TSRMLS_D) { tlib_php_request_end(); } +#define PHP_VERSION_METRIC_BASE "Supportability/PHP/Version" +#define AGENT_VERSION_METRIC_BASE "Supportability/PHP/AgentVersion" + +static void test_create_php_version_metric() { + nrtxn_t* txn; + int count; + + tlib_php_request_start(); + txn = NRPRG(txn); + + count = nrm_table_size(txn->unscoped_metrics); + + /* Test invalid values are properly handled */ + nr_php_txn_create_php_version_metric(NULL, NULL); + tlib_pass_if_int_equal("PHP version metric shouldnt be created 1", count, + nrm_table_size(txn->unscoped_metrics)); + + nr_php_txn_create_php_version_metric(txn, NULL); + tlib_pass_if_int_equal("PHP version metric shouldnt be created 2", count, + nrm_table_size(txn->unscoped_metrics)); + + nr_php_txn_create_php_version_metric(NULL, "7.4.0"); + tlib_pass_if_int_equal("PHP version metric shouldnt be created 3", count, + nrm_table_size(txn->unscoped_metrics)); + + nr_php_txn_create_php_version_metric(txn, ""); + tlib_pass_if_int_equal("PHP version metric shouldnt be created 4", count, + nrm_table_size(txn->unscoped_metrics)); + + /* test valid values */ + nr_php_txn_create_php_version_metric(txn, "7.4.0"); + tlib_pass_if_int_equal("PHP version metric should be create", count + 1, + nrm_table_size(txn->unscoped_metrics)); + + const nrmetric_t* metric + = nrm_find(txn->unscoped_metrics, PHP_VERSION_METRIC_BASE "/7.4.0"); + const char* metric_name = nrm_get_name(txn->unscoped_metrics, metric); + + tlib_pass_if_not_null("PHP version metric found", metric); + tlib_pass_if_str_equal("PHP version metric name check", metric_name, + PHP_VERSION_METRIC_BASE "/7.4.0"); + + tlib_php_request_end(); +} + +static void test_create_agent_version_metric() { + nrtxn_t* txn; + int count; + + tlib_php_request_start(); + txn = NRPRG(txn); + + count = nrm_table_size(txn->unscoped_metrics); + + /* Test invalid values are properly handled */ + nr_php_txn_create_agent_version_metric(NULL); + tlib_pass_if_int_equal("Agent version metric shouldnt be created - txn is NULL", count, + nrm_table_size(txn->unscoped_metrics)); + + /* Test valid values */ + nr_php_txn_create_agent_version_metric(txn); + tlib_pass_if_int_equal("Agent version metric should be created - txn is not NULL", count + 1, + nrm_table_size(txn->unscoped_metrics)); + + const nrmetric_t* metric + = nrm_find(txn->unscoped_metrics, AGENT_VERSION_METRIC_BASE "/" NR_VERSION); + const char* metric_name = nrm_get_name(txn->unscoped_metrics, metric); + + tlib_pass_if_not_null("Agent version metric found", metric); + tlib_pass_if_str_equal("Agent version metric name check", metric_name, + AGENT_VERSION_METRIC_BASE "/" NR_VERSION); + + tlib_php_request_end(); +} + +static void test_create_agent_php_version_metrics() { + nrtxn_t* txn; + + /* + * Test : Create agent PHP version metrics. + */ + tlib_php_request_start(); + txn = NRPRG(txn); + + zval* expected_php_zval = tlib_php_request_eval_expr("phpversion();"); + + char* php_version_name = nr_formatf(PHP_VERSION_METRIC_BASE "/%s", + Z_STRVAL_P(expected_php_zval)); + + nr_php_zval_free(&expected_php_zval); + + char* agent_version_name + = nr_formatf(AGENT_VERSION_METRIC_BASE "/%s", NR_VERSION); + + nr_php_txn_create_agent_php_version_metrics(txn); + + /* Test the PHP version metric creation */ + const nrmetric_t* metric = nrm_find(txn->unscoped_metrics, php_version_name); + const char* metric_name = nrm_get_name(txn->unscoped_metrics, metric); + + tlib_pass_if_not_null("happy path: PHP version metric created", metric); + tlib_pass_if_not_null("happy path: PHP version metric name created", + metric_name); + + tlib_pass_if_str_equal("happy path: PHP version metric name check", + metric_name, php_version_name); + + /* Test the agent version metric creation*/ + metric = nrm_find(txn->unscoped_metrics, agent_version_name); + metric_name = nrm_get_name(txn->unscoped_metrics, metric); + + tlib_pass_if_not_null("happy path: Agent version metric created", metric); + tlib_pass_if_not_null("happy path: Agent version metric name created", + metric_name); + + tlib_pass_if_str_equal("happy path: Agent version metric name check", + metric_name, agent_version_name); + + nr_free(agent_version_name); + nr_free(php_version_name); + + tlib_php_request_end(); +} + +#undef PHP_VERSION_METRIC_BASE +#undef AGENT_VERSION_METRIC_BASE + tlib_parallel_info_t parallel_info = {.suggested_nthreads = 1, .state_size = 0}; void test_main(void* p NRUNUSED) { @@ -175,8 +302,11 @@ void test_main(void* p NRUNUSED) { tlib_php_engine_create( "newrelic.transaction_events.attributes.include=request.uri" PTSRMLS_CC); - test_handle_fpm_error(TSRMLS_C); - test_max_segments_config_values(TSRMLS_C); + test_handle_fpm_error(); + test_max_segments_config_values(); + test_create_php_version_metric(); + test_create_agent_version_metric(); + test_create_agent_php_version_metrics(); - tlib_php_engine_destroy(TSRMLS_C); + tlib_php_engine_destroy(); } diff --git a/daemon/cmd/integration_runner/main.go b/daemon/cmd/integration_runner/main.go index 40f472af4..f1a65a402 100644 --- a/daemon/cmd/integration_runner/main.go +++ b/daemon/cmd/integration_runner/main.go @@ -376,6 +376,14 @@ func main() { // Env vars common to all tests. ctx.Env["EXTERNAL_HOST"] = externalHost + ctx.Env["PHP_VERSION"] = integration.GetPHPVersion() + + agent_extension, ok := ctx.Settings["extension"] + if !ok { + agent_extension = "newrelic.so" + } + ctx.Env["AGENT_VERSION"] = integration.GetAgentVersion(agent_extension) + handler, err := startDaemon("unix", *flagPort, flagSecurityToken.String(), flagSecuityPolicies.String()) if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/daemon/internal/newrelic/integration/test.go b/daemon/internal/newrelic/integration/test.go index f80b3e986..36c21472a 100644 --- a/daemon/internal/newrelic/integration/test.go +++ b/daemon/internal/newrelic/integration/test.go @@ -756,6 +756,8 @@ var ( regexp.MustCompile(`^Supportability\/InstrumentedFunction`), regexp.MustCompile(`^Supportability\/TxnData\/.*`), regexp.MustCompile(`^Supportability/C/NewrelicVersion/.*`), + regexp.MustCompile(`^Supportability/PHP/Version/.*`), + regexp.MustCompile(`^Supportability/PHP/AgentVersion/.*`), } ) diff --git a/daemon/internal/newrelic/integration/util.go b/daemon/internal/newrelic/integration/util.go new file mode 100644 index 000000000..25ab9462f --- /dev/null +++ b/daemon/internal/newrelic/integration/util.go @@ -0,0 +1,33 @@ +// +// Copyright 2020 New Relic Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +// + +package integration + +import ( + "fmt" + "os/exec" +) + +func GetPHPVersion() string { + cmd := exec.Command("php", "-r", "echo PHP_VERSION;") + + output, err := cmd.Output() + if err != nil { + fmt.Printf("Failed to get PHP version: %v\n", err) + return "failed" + } + + return string(output) +} + +func GetAgentVersion(agent_extension string) string { + cmd := exec.Command("php", "-d", "extension="+agent_extension, "-r", "echo phpversion('newrelic');") + + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("Failed to get agent version: %v", err).Error() + } + return string(output) +} diff --git a/tests/integration/supportability/test_php_and_agent_version_metrics.php b/tests/integration/supportability/test_php_and_agent_version_metrics.php new file mode 100644 index 000000000..bc63110f8 --- /dev/null +++ b/tests/integration/supportability/test_php_and_agent_version_metrics.php @@ -0,0 +1,27 @@ + + Date: Mon, 26 Aug 2024 15:36:19 -0400 Subject: [PATCH 05/45] chore: Bumps version to 11.2.0 (#952) --- VERSION | 2 +- axiom/nr_version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 68d8f15e2..b85c6c7b0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.1.0 +11.2.0 diff --git a/axiom/nr_version.c b/axiom/nr_version.c index 018770a1a..e608278e9 100644 --- a/axiom/nr_version.c +++ b/axiom/nr_version.c @@ -23,7 +23,6 @@ /* * Current version naming scheme is flowers * - * cosmos 29Jun2022 (10.0) * dahlia 19Sep2022 (10.1) * echinacea 03Oct2022 (10.2) * freesia 03Nov2022 (10.3) @@ -47,8 +46,9 @@ * xerophyllum 20May2024 (10.21) * yarrow 26Jun2024 (10.22) * zinnia 30Jul2024 (11.0) + * amethyst 26Aug2024 (11.1) */ -#define NR_CODENAME "amethyst" +#define NR_CODENAME "bowenite" const char* nr_version(void) { return NR_STR2(NR_VERSION); From 8a5c2f8b7e559a86f357052ba9b93772b8a317ba Mon Sep 17 00:00:00 2001 From: Michael Fulbright <89205663+mfulb@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:30:23 -0400 Subject: [PATCH 06/45] fix(testing): Handles Apdex metrics count correctly (#957) The existing code did not handle an Apdex metric correctly if a count of "1" was specified. It only worked if no count was specified. This change allows either form to work. --- daemon/internal/newrelic/integration/test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/internal/newrelic/integration/test.go b/daemon/internal/newrelic/integration/test.go index 36c21472a..631be4615 100644 --- a/daemon/internal/newrelic/integration/test.go +++ b/daemon/internal/newrelic/integration/test.go @@ -549,7 +549,7 @@ func (t *Test) compareMetricsExist(harvest *newrelic.Harvest) { // actually the "satisfied" count, not a total count of metric // as it is for other types of metrics apdex_metric := strings.HasPrefix(expected, "Apdex/") - if (count == -1 && (apdex_metric || actualCount > 0)) || (actualCount == count) { + if (apdex_metric || (count == -1 && actualCount > 0)) || (actualCount == count) { metricPasses = true } From 108af43cf58fb9786f6526a4a19671db188d95e4 Mon Sep 17 00:00:00 2001 From: Amber Sistla Date: Tue, 10 Sep 2024 09:37:00 -0700 Subject: [PATCH 07/45] ci: fix test-pull-request workflow (#961) [`actions/upload-artifacts@v4.4.0`](https://github.com/actions/upload-artifact/releases/tag/v4.4.0) includes a breaking change of that causes hidden directories and files not being uploaded. Since code coverage artifacts are in `agent/.libs` directory, this functionality needs to disabled. Set `include-hidden-files` to true when uploading code coverage artifacts. --- .github/workflows/test-agent.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-agent.yml b/.github/workflows/test-agent.yml index 78a27ee41..328be3a39 100644 --- a/.github/workflows/test-agent.yml +++ b/.github/workflows/test-agent.yml @@ -193,6 +193,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: agent.gcov-${{matrix.platform}}-${{matrix.arch}}-${{matrix.php}} + include-hidden-files: true path: php-agent/agent/.libs/*.gc* integration-tests: needs: [daemon-unit-tests, agent-unit-test] From 79e86d37e3cb4d911610d91d92212faf39048f60 Mon Sep 17 00:00:00 2001 From: bduranleau-nr <106178551+bduranleau-nr@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:00:21 -0500 Subject: [PATCH 08/45] ci: fix issue response (#956) Fix gh command failure when no github repository can be found. --- .github/workflows/issue-comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-comment.yml b/.github/workflows/issue-comment.yml index cd72f7874..ba345e98a 100644 --- a/.github/workflows/issue-comment.yml +++ b/.github/workflows/issue-comment.yml @@ -25,4 +25,4 @@ jobs: steps: - name: respond to issue run: | - gh issue comment ${{ inputs.issue-number }} --body "${{ inputs.message }}" + gh issue -R ${{ github.repository }} comment ${{ inputs.issue-number }} --body "${{ inputs.message }}" From 8de09b010d060c22e16bf0583ac5cc25ecaff7db Mon Sep 17 00:00:00 2001 From: Michael Fulbright <89205663+mfulb@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:40:42 -0400 Subject: [PATCH 09/45] fix(agent): Fixes newrelic_notice_error() API for PHP 8+ (#960) Starting with PHP 8.0 the `set_error_handler()` callback function signature drops the `$errcontext` final parameter so it only contains 4 parameters. This caused using `newrelic_notice_error()` as a callback handler for PHP 8+ to not work as the API call did not accept only 4 arguments. This PR adds support for this function signature. There are also numerous tests added that will hopefully caught this kind of issue in the future. --------- Co-authored-by: Amber Sistla Co-authored-by: Hitesh Ahuja <108540135+hahuja2@users.noreply.github.com> --- agent/php_api.c | 31 +++- .../api/notice_error/test_bad_inputs.php | 13 +- .../test_good_1_arg_exception.php | 133 ++++++++++++++++ .../test_good_1_arg_exception_handler.php | 150 ++++++++++++++++++ .../notice_error/test_good_1_arg_string.php | 131 +++++++++++++++ .../api/notice_error/test_good_2_args.php | 129 +++++++++++++++ .../api/notice_error/test_good_4_args.php | 131 +++++++++++++++ .../test_good_4_args_error_handler.php | 149 +++++++++++++++++ .../api/notice_error/test_good_5_args.php | 130 +++++++++++++++ .../test_good_5_args_error_handler.php | 149 +++++++++++++++++ 10 files changed, 1141 insertions(+), 5 deletions(-) create mode 100644 tests/integration/api/notice_error/test_good_1_arg_exception.php create mode 100644 tests/integration/api/notice_error/test_good_1_arg_exception_handler.php create mode 100644 tests/integration/api/notice_error/test_good_1_arg_string.php create mode 100644 tests/integration/api/notice_error/test_good_2_args.php create mode 100644 tests/integration/api/notice_error/test_good_4_args.php create mode 100644 tests/integration/api/notice_error/test_good_4_args_error_handler.php create mode 100644 tests/integration/api/notice_error/test_good_5_args.php create mode 100644 tests/integration/api/notice_error/test_good_5_args_error_handler.php diff --git a/agent/php_api.c b/agent/php_api.c index f9dc5c903..865344442 100644 --- a/agent/php_api.c +++ b/agent/php_api.c @@ -58,10 +58,30 @@ void nr_php_api_add_supportability_metric(const char* name TSRMLS_DC) { /* * Purpose : (New Relic API) Pretend that there is an error at this exact spot. - * Useful for business logic errors. newrelic_notice_error($errstr) + * Useful for business logic errors. + * - newrelic_notice_error($errstr) + * - $errstr : string : The error message to record * - newrelic_notice_error($exception) + * - $exception : object : The exception to use to record the exception + * NOTE: This version is compatible with being a callback for set_exception_handler() * - newrelic_notice_error($errstr,$exception) + * - $errstr : string : The error message to record + * - $exception : object : The exception to use to record the exception + * NOTE: The $errstr value is ignored! Started with agent version 4.23 + * - newrelic_notice_error($errno,$errstr,$fname,$line_nr) + * - $errno : int : The error number + * - $errstr : string : The error message + * - $fname : string : The filename where the error occurred + * - $line_nr : int : The line number where the error occurred + * NOTE: This version is compatible with being a callback for set_error_handler() for PHP 8+ * - newrelic_notice_error($errno,$errstr,$fname,$line_nr,$ctx) + * - $errno : int : The error number + * - $errstr : string : The error message + * - $fname : string : The filename where the error occurred + * - $line_nr : int : The line number where the error occurred + * - $ctx : array : The context of the error + * NOTE: This version is compatible with being a callback for set_error_handler() for PHP < 8 + * The $ctx is ignored! */ #ifdef TAGS void zif_newrelic_notice_error(void); /* ctags landing pad only */ @@ -141,10 +161,15 @@ PHP_FUNCTION(newrelic_notice_error) { } break; + case 4: case 5: + /* PHP 8+ will only pass the first 4 parameters so the 5th parameter is + * declared to be optional. Also this parameter is completely ignored + * so it doesn't matter if it is passed or not. + */ if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, - ZEND_NUM_ARGS() TSRMLS_CC, "lsslz!", + ZEND_NUM_ARGS() TSRMLS_CC, "lssl|z!", &ignore1, &errormsgstr, &errormsglen, &ignore2, &ignore3, &ignore4, &ignore5)) { nrl_debug(NRL_API, "newrelic_notice_error: invalid five arguments"); @@ -153,7 +178,7 @@ PHP_FUNCTION(newrelic_notice_error) { break; default: - nrl_debug(NRL_API, "newrelic_notice_error: invalid number of arguments"); + nrl_debug(NRL_API, "newrelic_notice_error: invalid number of arguments: %d", ZEND_NUM_ARGS()); RETURN_NULL(); } diff --git a/tests/integration/api/notice_error/test_bad_inputs.php b/tests/integration/api/notice_error/test_bad_inputs.php index 8f72e2d25..75d582884 100644 --- a/tests/integration/api/notice_error/test_bad_inputs.php +++ b/tests/integration/api/notice_error/test_bad_inputs.php @@ -15,6 +15,9 @@ ok - 2 args ok - 3 args ok - 4 args +ok - 4 args +ok - 4 args +ok - 4 args ok - 5 args ok - 6 args */ @@ -32,9 +35,15 @@ tap_equal(null, newrelic_notice_error("", 42), "2 args"); tap_equal(null, newrelic_notice_error("", array()), "2 args"); -// Three and four argument forms are not allowed. +// Three argument forms are not allowed. tap_equal(null, newrelic_notice_error(42, "message", "file"), "3 args"); -tap_equal(null, newrelic_notice_error(42, "message", "file", __LINE__), "4 args"); + +// Four argument form requires integer, string, string, integer +// This is like the five argument form but for PHP 8+ where the context is not supplied +tap_equal(null, newrelic_notice_error("", "message", "file", __LINE__), "4 args"); +tap_equal(null, newrelic_notice_error(42, array(), "file", __LINE__), "4 args"); +tap_equal(null, newrelic_notice_error("", "message", array(), __LINE__), "4 args"); +tap_equal(null, newrelic_notice_error("", "message", "file", ""), "4 args"); // Five argument form requires second arg to be convertible to a string. tap_equal(null, newrelic_notice_error("", curl_init()), "5 args"); diff --git a/tests/integration/api/notice_error/test_good_1_arg_exception.php b/tests/integration/api/notice_error/test_good_1_arg_exception.php new file mode 100644 index 000000000..c71750439 --- /dev/null +++ b/tests/integration/api/notice_error/test_good_1_arg_exception.php @@ -0,0 +1,133 @@ + Date: Thu, 19 Sep 2024 12:46:17 -0700 Subject: [PATCH 10/45] security(daemon): upgrade golang to 1.23.1 (#964) --- daemon/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/go.mod b/daemon/go.mod index a7354c622..5fb45e76e 100644 --- a/daemon/go.mod +++ b/daemon/go.mod @@ -1,7 +1,7 @@ module github.com/newrelic/newrelic-php-agent/daemon go 1.21 -toolchain go1.22.5 +toolchain go1.23.1 require ( github.com/golang/protobuf v1.5.3 From 498b8f39dca6cf886a5d9b41d74501011ba6c586 Mon Sep 17 00:00:00 2001 From: Michal Nowacki Date: Thu, 26 Sep 2024 16:46:59 -0400 Subject: [PATCH 11/45] feat(agent): use composer for vuln mgmt package info (#962) If possible, use Composer's runtime API to collect information about PHP packages used by the application for New Relic Vulnerability Management. This feature is disabled by default and can be enabled by setting `newrelic.vulnerability_management.composer_detection.enabled` to `true`. --------- Co-authored-by: Hitesh Ahuja Co-authored-by: bduranleau-nr <106178551+bduranleau-nr@users.noreply.github.com> Co-authored-by: Hitesh Ahuja <108540135+hahuja2@users.noreply.github.com> Co-authored-by: Amber Sistla Co-authored-by: Michael Fulbright <89205663+mfulb@users.noreply.github.com> --- agent/Makefile.frag | 1 + agent/config.m4 | 3 +- agent/fw_drupal.c | 10 + agent/fw_drupal8.c | 5 +- agent/fw_hooks.h | 1 + agent/fw_laminas3.c | 7 +- agent/fw_laravel.c | 5 +- agent/fw_lumen.c | 8 +- agent/fw_slim.c | 4 +- agent/fw_support.c | 26 +- agent/fw_support.h | 5 +- agent/fw_symfony4.c | 7 +- agent/fw_wordpress.c | 8 +- agent/fw_yii.c | 9 + agent/lib_aws_sdk_php.c | 6 +- agent/lib_composer.c | 272 ++++++++++++++++++ agent/lib_doctrine2.c | 7 +- agent/lib_guzzle4.c | 7 +- agent/lib_guzzle6.c | 22 +- agent/lib_mongodb.c | 7 +- agent/lib_monolog.c | 7 +- agent/lib_phpunit.c | 7 +- agent/lib_predis.c | 6 +- agent/php_execute.c | 37 ++- agent/php_newrelic.h | 2 + agent/php_nrini.c | 9 + agent/php_txn.c | 49 ++++ agent/php_txn_private.h | 21 ++ agent/scripts/newrelic.ini.template | 9 + agent/tests/test_fw_support.c | 136 +++++++-- agent/tests/test_lib_aws_sdk_php.c | 107 +++---- agent/tests/test_php_txn.c | 268 +++++++++++++++++ axiom/nr_php_packages.c | 49 +++- axiom/nr_php_packages.h | 77 ++++- axiom/nr_txn.c | 43 ++- axiom/nr_txn.h | 55 +++- axiom/tests/.gitignore | 1 + axiom/tests/test_php_packages.c | 181 +++++++++++- axiom/tests/test_txn.c | 128 +++++++++ .../newrelic/integration/php_packages.go | 66 +++-- daemon/internal/newrelic/integration/test.go | 72 ++++- .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 32 +++ .../vendor/composer/autoload_real.php | 9 + .../vendor/composer/installed.php | 31 ++ .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 40 +++ .../vendor/composer/autoload_real.php | 9 + .../vendor/composer/installed.php | 31 ++ .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 40 +++ .../vendor/composer/autoload_real.php | 9 + .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 39 +++ .../vendor/composer/autoload_real.php | 9 + .../vendor/composer/installed.php | 31 ++ .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 42 +++ .../vendor/composer/autoload_real.php | 9 + .../vendor/composer/installed.php | 31 ++ .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 39 +++ .../vendor/composer/autoload_real.php | 9 + .../vendor/composer/installed.php | 31 ++ .../vendor/autoload.php | 12 + .../integration/autoloader/composer-show.php | 26 ++ .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 32 +++ .../vendor/composer/autoload_real.php | 9 + .../vendor/composer/installed.php | 36 +++ .../vendor/symfony/http-kernel/HttpKernel.php | 12 + .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 32 +++ .../vendor/composer/autoload_real.php | 9 + .../vendor/composer/installed.php | 36 +++ .../src/Illuminate/Foundation/Application.php | 25 ++ .../vendor/autoload.php | 12 + .../vendor/composer/InstalledVersions.php | 51 ++++ .../vendor/composer/autoload_real.php | 9 + .../vendor/composer/installed.php | 44 +++ .../src/Illuminate/Foundation/Application.php | 25 ++ ...est_autoloader_with_broken_composer_00.php | 32 +++ ...est_autoloader_with_broken_composer_01.php | 32 +++ ...est_autoloader_with_broken_composer_02.php | 32 +++ ...test_autoloader_with_composer_disabled.php | 28 ++ .../test_autoloader_with_composer_enabled.php | 29 ++ ...ser_enabled_package_detection_disabled.php | 30 ++ ...utoloader_with_composer_throwing_error.php | 34 +++ ...oader_with_composer_throwing_exception.php | 34 +++ ...t_autoloader_without_composer_disabled.php | 23 ++ ...st_autoloader_without_composer_enabled.php | 27 ++ .../test_packages_with_broken_composer_00.php | 40 +++ .../test_packages_with_broken_composer_01.php | 41 +++ .../test_packages_with_broken_composer_02.php | 44 +++ .../logging/monolog2/test_monolog_basic.php | 4 +- .../logging/monolog2/test_monolog_cat.php | 4 +- .../monolog2/test_monolog_context_simple.php | 2 +- ...test_monolog_decoration_and_forwarding.php | 4 +- .../monolog2/test_monolog_disable_metrics.php | 4 +- .../monolog2/test_monolog_drop_empty.php | 4 +- .../test_monolog_large_message_limit.php | 4 +- ...test_monolog_large_message_limit_drops.php | 4 +- .../test_monolog_limit_log_events.php | 4 +- .../test_monolog_limit_zero_events.php | 4 +- ...log_events_max_samples_stored_invalid1.php | 4 +- ...log_events_max_samples_stored_invalid2.php | 4 +- ...log_events_max_samples_stored_invalid3.php | 4 +- ...log_events_max_samples_stored_invalid4.php | 4 +- .../test_monolog_log_level_filter.php | 4 +- .../test_monolog_log_level_filter_invalid.php | 4 +- .../logging/monolog3/test_monolog_basic.php | 4 +- .../logging/monolog3/test_monolog_cat.php | 4 +- .../monolog3/test_monolog_context_simple.php | 2 +- ...test_monolog_decoration_and_forwarding.php | 4 +- .../monolog3/test_monolog_disable_metrics.php | 4 +- .../monolog3/test_monolog_drop_empty.php | 4 +- .../test_monolog_large_message_limit.php | 4 +- ...test_monolog_large_message_limit_drops.php | 4 +- .../test_monolog_limit_log_events.php | 4 +- .../test_monolog_limit_zero_events.php | 4 +- ...log_events_max_samples_stored_invalid1.php | 4 +- ...log_events_max_samples_stored_invalid2.php | 4 +- ...log_events_max_samples_stored_invalid3.php | 4 +- ...log_events_max_samples_stored_invalid4.php | 4 +- .../test_monolog_log_level_filter.php | 4 +- .../test_monolog_log_level_filter_invalid.php | 4 +- 126 files changed, 2972 insertions(+), 235 deletions(-) create mode 100644 agent/lib_composer.c create mode 100644 agent/tests/test_php_txn.c create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-00/vendor/autoload.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-00/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-00/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-00/vendor/composer/installed.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-01/vendor/autoload.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-01/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-01/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-01/vendor/composer/installed.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-02/vendor/autoload.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-02/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/autoload-with-broken-composer-02/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/autoload-with-composer-throwing-error/vendor/autoload.php create mode 100644 tests/integration/autoloader/autoload-with-composer-throwing-error/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/autoload-with-composer-throwing-error/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/autoload-with-composer-throwing-error/vendor/composer/installed.php create mode 100644 tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/autoload.php create mode 100644 tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/composer/installed.php create mode 100644 tests/integration/autoloader/autoload-with-composer/vendor/autoload.php create mode 100644 tests/integration/autoloader/autoload-with-composer/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/autoload-with-composer/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/autoload-with-composer/vendor/composer/installed.php create mode 100644 tests/integration/autoloader/autoload-without-composer/vendor/autoload.php create mode 100644 tests/integration/autoloader/composer-show.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-00/vendor/autoload.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-00/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-00/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-00/vendor/composer/installed.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-00/vendor/symfony/http-kernel/HttpKernel.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-01/vendor/autoload.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-01/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-01/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-01/vendor/composer/installed.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-01/vendor/laravel/framework/src/Illuminate/Foundation/Application.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-02/vendor/autoload.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-02/vendor/composer/InstalledVersions.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-02/vendor/composer/autoload_real.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-02/vendor/composer/installed.php create mode 100644 tests/integration/autoloader/packages-with-broken-composer-02/vendor/laravel/framework/src/Illuminate/Foundation/Application.php create mode 100644 tests/integration/autoloader/test_autoloader_with_broken_composer_00.php create mode 100644 tests/integration/autoloader/test_autoloader_with_broken_composer_01.php create mode 100644 tests/integration/autoloader/test_autoloader_with_broken_composer_02.php create mode 100644 tests/integration/autoloader/test_autoloader_with_composer_disabled.php create mode 100644 tests/integration/autoloader/test_autoloader_with_composer_enabled.php create mode 100644 tests/integration/autoloader/test_autoloader_with_composer_enabled_package_detection_disabled.php create mode 100644 tests/integration/autoloader/test_autoloader_with_composer_throwing_error.php create mode 100644 tests/integration/autoloader/test_autoloader_with_composer_throwing_exception.php create mode 100644 tests/integration/autoloader/test_autoloader_without_composer_disabled.php create mode 100644 tests/integration/autoloader/test_autoloader_without_composer_enabled.php create mode 100644 tests/integration/autoloader/test_packages_with_broken_composer_00.php create mode 100644 tests/integration/autoloader/test_packages_with_broken_composer_01.php create mode 100644 tests/integration/autoloader/test_packages_with_broken_composer_02.php diff --git a/agent/Makefile.frag b/agent/Makefile.frag index 1cfe1606c..648fc84b3 100644 --- a/agent/Makefile.frag +++ b/agent/Makefile.frag @@ -107,6 +107,7 @@ TEST_BINARIES = \ tests/test_php_minit \ tests/test_php_stack \ tests/test_php_stacked_segment \ + tests/test_php_txn \ tests/test_php_wrapper \ tests/test_predis \ tests/test_redis \ diff --git a/agent/config.m4 b/agent/config.m4 index a77122c37..ee0859e30 100644 --- a/agent/config.m4 +++ b/agent/config.m4 @@ -230,7 +230,8 @@ if test "$PHP_NEWRELIC" = "yes"; then fw_zend2.c fw_zend.c" LIBRARIES="lib_aws_sdk_php.c lib_monolog.c lib_doctrine2.c lib_guzzle3.c \ lib_guzzle4.c lib_guzzle6.c lib_guzzle_common.c \ - lib_mongodb.c lib_phpunit.c lib_predis.c lib_zend_http.c" + lib_mongodb.c lib_phpunit.c lib_predis.c lib_zend_http.c \ + lib_composer.c" PHP_NEW_EXTENSION(newrelic, $FRAMEWORKS $LIBRARIES $NEWRELIC_AGENT, $ext_shared,, \\$(NEWRELIC_CFLAGS)) PHP_SUBST(NEWRELIC_CFLAGS) diff --git a/agent/fw_drupal.c b/agent/fw_drupal.c index 12e43b5cf..3537f8b42 100644 --- a/agent/fw_drupal.c +++ b/agent/fw_drupal.c @@ -20,6 +20,8 @@ #include "util_memory.h" #include "util_strings.h" +#define PHP_PACKAGE_NAME "drupal/drupal" + /* * Set the Web Transaction (WT) name to "(cached page)" * @@ -879,4 +881,12 @@ void nr_drupal_enable(TSRMLS_D) { nr_php_user_function_add_declared_callback( NR_PSTR("drupal_http_request"), nr_drupal_replace_http_request TSRMLS_CC); #endif + + if (NRINI(vulnerability_management_package_detection_enabled)) { + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); + } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/fw_drupal8.c b/agent/fw_drupal8.c index 6593e17a8..541cb8b87 100644 --- a/agent/fw_drupal8.c +++ b/agent/fw_drupal8.c @@ -689,8 +689,6 @@ void nr_drupal_version() { if (NRINI(vulnerability_management_package_detection_enabled)) { nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version); } - nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, - version); } nr_php_zval_free(&zval_version); @@ -762,4 +760,7 @@ void nr_drupal8_enable(TSRMLS_D) { nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/fw_hooks.h b/agent/fw_hooks.h index e78f65cbd..c4500aeb2 100644 --- a/agent/fw_hooks.h +++ b/agent/fw_hooks.h @@ -56,6 +56,7 @@ extern void nr_phpunit_enable(TSRMLS_D); extern void nr_predis_enable(TSRMLS_D); extern void nr_zend_http_enable(TSRMLS_D); extern void nr_monolog_enable(TSRMLS_D); +extern void nr_composer_handle_autoload(const char* filename); /* Vulnerability Management Packages */ extern void nr_drupal_version(void); diff --git a/agent/fw_laminas3.c b/agent/fw_laminas3.c index 604976bf0..a5d27de94 100644 --- a/agent/fw_laminas3.c +++ b/agent/fw_laminas3.c @@ -13,6 +13,8 @@ #include "util_logging.h" #include "util_memory.h" +#define PHP_PACKAGE_NAME "laminas/laminas-mvc" + /* * Laminas is a rebranding of Zend, but the logic remains the same, * it is simply a name change and corresponds directly to Zend 3.x. @@ -163,7 +165,10 @@ void nr_laminas3_enable(TSRMLS_D) { nr_laminas3_name_the_wt TSRMLS_CC); if (NRINI(vulnerability_management_package_detection_enabled)) { - nr_txn_add_php_package(NRPRG(txn), "laminas/laminas-mvc", + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/fw_laravel.c b/agent/fw_laravel.c index 11718a7e6..bbac65b20 100644 --- a/agent/fw_laravel.c +++ b/agent/fw_laravel.c @@ -963,8 +963,9 @@ NR_PHP_WRAPPER(nr_laravel_application_construct) { // Add php package to transaction nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version); } - nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, - version); + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + version); if (version) { nrl_debug(NRL_FRAMEWORK, "Laravel version is " NRP_FMT, NRP_PHP(version)); diff --git a/agent/fw_lumen.c b/agent/fw_lumen.c index 239578ae1..2d34551f0 100644 --- a/agent/fw_lumen.c +++ b/agent/fw_lumen.c @@ -11,10 +11,13 @@ #include "php_wrapper.h" #include "php_hash.h" #include "fw_hooks.h" +#include "fw_support.h" #include "util_logging.h" #include "util_memory.h" #include "util_strings.h" +#define PHP_PACKAGE_NAME "laravel/lumen-framework" + /* * Sets the web transaction name. If strip_base == true, * leading class path components will be stripped. @@ -232,7 +235,10 @@ void nr_lumen_enable(TSRMLS_D) { #endif if (NRINI(vulnerability_management_package_detection_enabled)) { - nr_txn_add_php_package(NRPRG(txn), "laravel/lumen-framework", + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/fw_slim.c b/agent/fw_slim.c index a70323459..facaeecc1 100644 --- a/agent/fw_slim.c +++ b/agent/fw_slim.c @@ -163,8 +163,8 @@ NR_PHP_WRAPPER(nr_slim_application_construct) { nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version); } - nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, - version); + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + version); nr_free(version); nr_php_scope_release(&this_var); diff --git a/agent/fw_support.c b/agent/fw_support.c index daff2692c..23c9c44c1 100644 --- a/agent/fw_support.c +++ b/agent/fw_support.c @@ -58,23 +58,39 @@ void nr_fw_support_add_logging_supportability_metric(nrtxn_t* txn, void nr_fw_support_add_package_supportability_metric( nrtxn_t* txn, const char* package_name, - const char* package_version) { - if (NULL == txn || NULL == package_name || NULL == package_version) { + const char* package_version, + nr_php_package_t* p) { + if (NULL == txn || NULL == package_name) { return; } char* metname = NULL; char major_version[MAJOR_VERSION_LENGTH] = {0}; + const char* version = package_version; + + // override provided package_version only if: + // - php_package is provided + // - its version is not NULL + // - its version is not PHP_PACKAGE_VERSION_UNKNOWN + if (NULL != p && NULL != p->package_version + && 0 != nr_strcmp(p->package_version, PHP_PACKAGE_VERSION_UNKNOWN)) { + version = p->package_version; + } + + // only generate metric if version is known + if (NULL == version || 0 == nr_strcmp(version, PHP_PACKAGE_VERSION_UNKNOWN)) { + return; + } /* The below for loop checks if the major version of the package is more than * one digit and keeps looping until a '.' is encountered or one of the * conditions is met. */ - for (int i = 0; package_version[i] && i < MAJOR_VERSION_LENGTH - 1; i++) { - if ('.' == package_version[i]) { + for (int i = 0; version[i] && i < MAJOR_VERSION_LENGTH - 1; i++) { + if ('.' == version[i]) { break; } - major_version[i] = package_version[i]; + major_version[i] = version[i]; } if (NR_FW_UNSET == NRINI(force_framework)) { diff --git a/agent/fw_support.h b/agent/fw_support.h index 5099a7d35..ad4b02722 100644 --- a/agent/fw_support.h +++ b/agent/fw_support.h @@ -8,6 +8,7 @@ #define FW_SUPPORT_HDR #include "php_user_instrument.h" +#include "nr_php_packages.h" extern void nr_php_framework_add_supportability_metric( const char* framework_name, @@ -44,11 +45,13 @@ extern void nr_fw_support_add_logging_supportability_metric( * Params : 1. Transaction object * 2. Package name * 3. Package version + * 4. PHP package reported for vulnerability management * */ extern void nr_fw_support_add_package_supportability_metric( nrtxn_t* txn, const char* package_name, - const char* package_version); + const char* package_version, + nr_php_package_t* p); #endif /* FW_SUPPORT_HDR */ diff --git a/agent/fw_symfony4.c b/agent/fw_symfony4.c index 6e1c9982d..8592186ac 100644 --- a/agent/fw_symfony4.c +++ b/agent/fw_symfony4.c @@ -10,6 +10,8 @@ #include "fw_support.h" #include "fw_symfony_common.h" +#define PHP_PACKAGE_NAME "symfony/http-kernel" + NR_PHP_WRAPPER(nr_symfony4_exception) { int priority = nr_php_error_get_priority(E_ERROR); zval* event = NULL; @@ -277,7 +279,10 @@ void nr_symfony4_enable(TSRMLS_D) { #endif if (NRINI(vulnerability_management_package_detection_enabled)) { - nr_txn_add_php_package(NRPRG(txn), "symfony/http-kernel", + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/fw_wordpress.c b/agent/fw_wordpress.c index 55a0be5f0..050750f6a 100644 --- a/agent/fw_wordpress.c +++ b/agent/fw_wordpress.c @@ -804,8 +804,7 @@ void nr_wordpress_version() { "})();"; zval retval; - int result - = zend_eval_string(func_string, &retval, "Get Wordpress Version"); + int result = zend_eval_string(func_string, &retval, "Get Wordpress Version"); // Add php package to transaction if (SUCCESS == result) { if (nr_php_is_zval_valid_string(&retval)) { @@ -813,8 +812,9 @@ void nr_wordpress_version() { if (NRINI(vulnerability_management_package_detection_enabled)) { nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version); } - nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, - version); + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + version); } zval_dtor(&retval); } diff --git a/agent/fw_yii.c b/agent/fw_yii.c index 74aa19335..0b1af7c96 100644 --- a/agent/fw_yii.c +++ b/agent/fw_yii.c @@ -14,6 +14,7 @@ #include "util_memory.h" #include "util_strings.h" +#define PHP_PACKAGE_NAME "yiisoft/yii2" /* * Yii1: Set the web transaction name from the controllerId + actionId combo. * @@ -221,4 +222,12 @@ void nr_yii2_enable(TSRMLS_D) { nr_php_wrap_user_function(NR_PSTR("yii\\base\\ErrorHandler::logException"), nr_yii2_error_handler_wrapper TSRMLS_CC); #endif + + if (NRINI(vulnerability_management_package_detection_enabled)) { + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); + } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/lib_aws_sdk_php.c b/agent/lib_aws_sdk_php.c index cff08dbfd..ad956ac1e 100644 --- a/agent/lib_aws_sdk_php.c +++ b/agent/lib_aws_sdk_php.c @@ -69,8 +69,10 @@ void nr_lib_aws_sdk_php_handle_version() { /* Add php package to transaction */ nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version); } - nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, - version); + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + version); + nr_php_zval_free(&zval_version); } diff --git a/agent/lib_composer.c b/agent/lib_composer.c new file mode 100644 index 000000000..f6596639a --- /dev/null +++ b/agent/lib_composer.c @@ -0,0 +1,272 @@ +/* + * Copyright 2022 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "php_agent.h" +#include "fw_hooks.h" +#include "fw_support.h" +#include "nr_txn.h" +#include "util_logging.h" +#include "util_memory.h" +#include "util_syscalls.h" + +static bool nr_execute_handle_autoload_composer_is_initialized() { + zend_class_entry* zce = NULL; + + if (NULL == (zce = nr_php_find_class("composer\\installedversions"))) { + nrl_verbosedebug(NRL_INSTRUMENT, + "Composer\\InstalledVersions class not found"); + return false; + }; + + // the class is found - there's hope! + if (NULL == nr_php_find_class_method(zce, "getallrawdata") + || NULL == nr_php_find_class_method(zce, "getrootpackage")) { + nrl_verbosedebug( + NRL_INSTRUMENT, + "Composer\\InstalledVersions class found, but methods not found"); + return false; + } + + return true; +} + +static int nr_execute_handle_autoload_composer_init(const char* vendor_path) { + char* code = NULL; + zval retval; + int result = FAILURE; + + if (nr_execute_handle_autoload_composer_is_initialized()) { + nrl_verbosedebug(NRL_INSTRUMENT, "%s: already initialized", __func__); + return NR_SUCCESS; + } + + code = nr_formatf("include_once '%s/composer/InstalledVersions.php';", + vendor_path); + + result = zend_eval_string(code, &retval, "newrelic\\init_composer_api"); + if (result != SUCCESS) { + nrl_verbosedebug(NRL_INSTRUMENT, + "%s: zend_eval_string(%s) failed, result=%d", __func__, + code, result); + nr_free(code); + return NR_FAILURE; + } + + zval_dtor(&retval); + nr_free(code); + + // Make sure runtime API is available after loading + // Composer\\InstalledVersions class: + if (!nr_execute_handle_autoload_composer_is_initialized()) { + nrl_verbosedebug(NRL_INSTRUMENT, + "%s: unable to initialize Composer runtime API", __func__); + return NR_FAILURE; + } + + return NR_SUCCESS; +} + +static void nr_execute_handle_autoload_composer_get_packages_information( + const char* vendor_path) { + zval retval; // This is used as a return value for zend_eval_string. + // It will only be set if the result of the eval is SUCCESS. + int result = FAILURE; + + // nrunlikely because this should alredy be ensured by the caller + if (nrunlikely(!NRINI(vulnerability_management_package_detection_enabled))) { + // do nothing when collecting package information for vulnerability + // management is disabled + return; + } + + // nrunlikely because this should alredy be ensured by the caller + if (nrunlikely(!NRINI(vulnerability_management_composer_api_enabled))) { + // do nothing when use of composer to collect package info is disabled + return; + } + + // clang-format off + char* getallrawdata + = "" + "(function() {" + " try {" + " $root_package = \\Composer\\InstalledVersions::getRootPackage();" + " $packages = array();" + " foreach (\\Composer\\InstalledVersions::getAllRawData() as $installed) { " + " foreach ($installed['versions'] as $packageName => $packageData) {" + " if (!is_string($packageName)) {" + " continue;" + " }" + " if (is_array($root_package) && array_key_exists('name', $root_package) && $packageName == $root_package['name']) {" + " continue;" + " }" + " if (!array_key_exists('pretty_version', $packageData)) {" + " continue;" + " }" + " $pretty_version = $packageData['pretty_version'];" + " if (is_string($pretty_version)) {" + " $packages[$packageName] = ltrim($pretty_version, 'v');" + " }" + " }" + " }" + " return $packages;" + " } catch (Throwable $e) {" + " return NULL;" + " }" + "})();"; + // clang-format on + + if (NR_SUCCESS != nr_execute_handle_autoload_composer_init(vendor_path)) { + nrl_debug(NRL_INSTRUMENT, + "%s - unable to initialize Composer runtime API - package info " + "unavailable", + __func__); + return; + } + + nrl_verbosedebug(NRL_INSTRUMENT, "%s - Composer runtime API available", + __func__); + + result + = zend_eval_string(getallrawdata, &retval, "composer_getallrawdata.php"); + if (SUCCESS != result) { + nrl_verbosedebug(NRL_INSTRUMENT, "%s - composer_getallrawdata.php failed", + __func__); + return; + } + if (IS_ARRAY == Z_TYPE(retval)) { + zend_string* package_name = NULL; + zval* package_version = NULL; + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(retval), package_name, + package_version) { + if (NULL == package_name || NULL == package_version) { + continue; + } + if (nr_php_is_zval_non_empty_string(package_version)) { + nrl_verbosedebug(NRL_INSTRUMENT, "package %s, version %s", + NRSAFESTR(ZSTR_VAL(package_name)), + NRSAFESTR(Z_STRVAL_P(package_version))); + nr_txn_add_php_package_from_source(NRPRG(txn), ZSTR_VAL(package_name), + Z_STRVAL_P(package_version), + NR_PHP_PACKAGE_SOURCE_COMPOSER); + } + } + ZEND_HASH_FOREACH_END(); + } else { + char strbuf[80]; + nr_format_zval_for_debug(&retval, strbuf, 0, sizeof(strbuf) - 1, 0); + nrl_verbosedebug(NRL_INSTRUMENT, + "%s - installed packages is: " NRP_FMT ", not an array", + __func__, NRP_ARGSTR(strbuf)); + } + zval_dtor(&retval); +} + +static char* nr_execute_handle_autoload_composer_get_vendor_path( + const char* filename) { + char* vendor_path = NULL; // result of dirname(filename) + char* cp = NULL; + + // nrunlikely because this should alredy be ensured by the caller + if (nrunlikely(NULL == filename)) { + nrl_verbosedebug(NRL_FRAMEWORK, "%s - filename is NULL", __func__); + return NULL; + } + + // vendor_path = dirname(filename): + // 1. copy filename to vendor_path + vendor_path = nr_strdup(filename); + // 2. // find last occurence of '/' in vendor_path + cp = nr_strrchr(vendor_path, '/'); + // 3. replace '/' with '\0' to get the directory path + if (NULL != cp) { + *cp = '\0'; + } else { + nrl_verbosedebug(NRL_FRAMEWORK, "%s - no '/' in filename '%s'", __func__, + filename); + } + + return vendor_path; +} + +static bool nr_execute_handle_autoload_composer_file_exists( + const char* vendor_path, + const char* filename) { + char* composer_magic_file = NULL; // vendor_path + filename + bool file_exists = false; + + // nrunlikely because this should alredy be ensured by the caller + if (nrunlikely(NULL == vendor_path)) { + nrl_verbosedebug(NRL_FRAMEWORK, "%s - vendor_path is NULL", __func__); + return false; + } + + // nrunlikely because this should alredy be ensured by the caller + if (nrunlikely(NULL == filename)) { + nrl_verbosedebug(NRL_FRAMEWORK, "%s - filename is NULL", __func__); + return false; + } + + composer_magic_file = nr_formatf("%s/%s", vendor_path, filename); + if (0 == nr_access(composer_magic_file, F_OK | R_OK)) { + file_exists = true; + } + nr_free(composer_magic_file); + return file_exists; +} + +void nr_composer_handle_autoload(const char* filename) { +// Composer signature file" +#define COMPOSER_MAGIC_FILE_1 "composer/autoload_real.php" +#define COMPOSER_MAGIC_FILE_1_LEN (sizeof(COMPOSER_MAGIC_FILE_1) - 1) +// Composer runtime API files: +#define COMPOSER_MAGIC_FILE_2 "composer/InstalledVersions.php" +#define COMPOSER_MAGIC_FILE_2_LEN (sizeof(COMPOSER_MAGIC_FILE_2) - 1) +#define COMPOSER_MAGIC_FILE_3 "composer/installed.php" +#define COMPOSER_MAGIC_FILE_3_LEN (sizeof(COMPOSER_MAGIC_FILE_3) - 1) + char* vendor_path = NULL; // result of dirname(filename) + + // nrunlikely because this should alredy be ensured by the caller + if (nrunlikely(NULL == filename)) { + nrl_verbosedebug(NRL_FRAMEWORK, "%s - filename is NULL", __func__); + return; + } + + vendor_path = nr_execute_handle_autoload_composer_get_vendor_path(filename); + if (NULL == vendor_path) { + nrl_verbosedebug(NRL_FRAMEWORK, "unable to get vendor path from '%s'", + filename); + return; + } + + if (!nr_execute_handle_autoload_composer_file_exists(vendor_path, + COMPOSER_MAGIC_FILE_1)) { + nrl_verbosedebug(NRL_FRAMEWORK, "'%s' not found in '%s'", + COMPOSER_MAGIC_FILE_1, vendor_path); + goto leave; + } + + if (!nr_execute_handle_autoload_composer_file_exists(vendor_path, + COMPOSER_MAGIC_FILE_2)) { + nrl_verbosedebug(NRL_FRAMEWORK, "'%s' not found in '%s'", + COMPOSER_MAGIC_FILE_2, vendor_path); + goto leave; + } + + if (!nr_execute_handle_autoload_composer_file_exists(vendor_path, + COMPOSER_MAGIC_FILE_3)) { + nrl_verbosedebug(NRL_FRAMEWORK, "'%s' not found in '%s'", + COMPOSER_MAGIC_FILE_3, vendor_path); + goto leave; + } + + nrl_verbosedebug(NRL_FRAMEWORK, "detected composer"); + NRPRG(txn)->composer_info.composer_detected = true; + nr_fw_support_add_library_supportability_metric(NRPRG(txn), "Composer"); + + nr_execute_handle_autoload_composer_get_packages_information(vendor_path); +leave: + nr_free(vendor_path); +} diff --git a/agent/lib_doctrine2.c b/agent/lib_doctrine2.c index e767d253f..8f8b8dfe3 100644 --- a/agent/lib_doctrine2.c +++ b/agent/lib_doctrine2.c @@ -16,6 +16,8 @@ #include "php_call.h" #include "lib_doctrine2.h" +#define PHP_PACKAGE_NAME "doctrine/orm" + /* * This answers the somewhat complicated question of whether we should * instrument DQL, which is dependent on the input query setting as well as SQL @@ -106,7 +108,10 @@ void nr_doctrine2_enable(TSRMLS_D) { #endif /* OAPI */ if (NRINI(vulnerability_management_package_detection_enabled)) { - nr_txn_add_php_package(NRPRG(txn), "doctrine/orm", + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/lib_guzzle4.c b/agent/lib_guzzle4.c index f3dfc94e9..c52ddffff 100644 --- a/agent/lib_guzzle4.c +++ b/agent/lib_guzzle4.c @@ -33,6 +33,8 @@ #include "util_logging.h" #include "util_memory.h" +#define PHP_PACKAGE_NAME "guzzlehttp/guzzle" + /* * We rely on the const correctness of certain Zend functions that weren't * const correct before 5.3 and/or 5.4: since Guzzle 4 requires 5.4.0 anyway, @@ -520,9 +522,12 @@ void nr_guzzle4_enable(TSRMLS_D) { nr_guzzle_client_construct TSRMLS_CC); if (NRINI(vulnerability_management_package_detection_enabled)) { - nr_txn_add_php_package(NRPRG(txn), "guzzlehttp/guzzle", + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } void nr_guzzle4_minit(TSRMLS_D) { diff --git a/agent/lib_guzzle6.c b/agent/lib_guzzle6.c index d9003545c..e65a684b7 100644 --- a/agent/lib_guzzle6.c +++ b/agent/lib_guzzle6.c @@ -359,15 +359,25 @@ NR_PHP_WRAPPER_START(nr_guzzle6_client_construct) { } /* - * If we were unable to get the full version before, at least we can extract - * the major version to send to the supportability metric. - * This is relevant to guzzle7+ which no longer supplies full version. - */ + * If we were unable to get the full version before, at least we can extract + * the major version to send to the supportability metric now, as + * this incomplete version will not be stored in a php package record + * and so the supportability metric cannot be created later like for + * most packages. + * + * This is relevant to guzzle7+ which no longer supplies full version. + */ if (NULL == version) { version = nr_php_get_object_constant(this_var, "MAJOR_VERSION"); } - nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, - version); + + /* if version is still NULL that is OK this next call will accept + * that value and when supportability metrics are made for + * packages if another method has determined the package version + * (composer api for example) then it will be filled in at that time + */ + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + version); nr_free(version); (void)wraprec; diff --git a/agent/lib_mongodb.c b/agent/lib_mongodb.c index c21e1e01b..8d129ae9d 100644 --- a/agent/lib_mongodb.c +++ b/agent/lib_mongodb.c @@ -18,6 +18,8 @@ #include "lib_mongodb_private.h" +#define PHP_PACKAGE_NAME "mongodb/mongodb" + static int nr_mongodb_is_server(const zval* obj TSRMLS_DC) { return nr_php_object_instanceof_class(obj, "MongoDB\\Driver\\Server" TSRMLS_CC); @@ -445,7 +447,10 @@ void nr_mongodb_enable() { #endif /* OAPI */ if (NRINI(vulnerability_management_package_detection_enabled)) { - nr_txn_add_php_package(NRPRG(txn), "mongodb/mongodb", + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/lib_monolog.c b/agent/lib_monolog.c index 667e33583..fcdb1ac06 100644 --- a/agent/lib_monolog.c +++ b/agent/lib_monolog.c @@ -378,8 +378,8 @@ NR_PHP_WRAPPER(nr_monolog_logger_addrecord) { = nr_monolog_get_timestamp(api, argc, NR_EXECUTE_ORIG_ARGS TSRMLS_CC); char version[MAJOR_VERSION_LENGTH]; snprintf(version, sizeof(version), "%d", api); - nr_fw_support_add_package_supportability_metric(NRPRG(txn), - PHP_PACKAGE_NAME, version); + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + version); } /* Record the log event */ @@ -523,4 +523,7 @@ void nr_monolog_enable(TSRMLS_D) { nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/lib_phpunit.c b/agent/lib_phpunit.c index e41c55b29..4050bf969 100644 --- a/agent/lib_phpunit.c +++ b/agent/lib_phpunit.c @@ -14,6 +14,8 @@ #include "util_logging.h" #include "util_strings.h" +#define PHP_PACKAGE_NAME "phpunit/phpunit" + /* * PHPUnit instrumentation * ======================= @@ -697,7 +699,10 @@ void nr_phpunit_enable(TSRMLS_D) { nr_phpunit_instrument_testresult_adderror TSRMLS_CC); if (NRINI(vulnerability_management_package_detection_enabled)) { - nr_txn_add_php_package(NRPRG(txn), "phpunit/phpunit", + nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, PHP_PACKAGE_VERSION_UNKNOWN); } + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); } diff --git a/agent/lib_predis.c b/agent/lib_predis.c index b299c5595..607e11ed2 100644 --- a/agent/lib_predis.c +++ b/agent/lib_predis.c @@ -654,8 +654,10 @@ NR_PHP_WRAPPER(nr_predis_client_construct) { // Add php package to transaction nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version); } - nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, - version); + + nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, + version); + nr_free(version); /* diff --git a/agent/php_execute.c b/agent/php_execute.c index 3ecb33100..83ff4d5a9 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -411,7 +411,7 @@ static const nr_framework_table_t all_frameworks[] = { NR_PSTR("symfony/bundle/frameworkbundle/frameworkbundle.php"), 0, nr_symfony2_enable, NR_FW_SYMFONY2}, /* also Symfony 3 */ {"Symfony4", "symfony4", NR_PSTR("http-kernel/httpkernel.php"), 0, - nr_symfony4_enable, NR_FW_SYMFONY4}, /* also Symfony 5 */ + nr_symfony4_enable, NR_FW_SYMFONY4}, /* also Symfony 5/6/7 */ {"WordPress", "wordpress", NR_PSTR("wp-config.php"), 0, nr_wordpress_enable, NR_FW_WORDPRESS}, @@ -931,6 +931,40 @@ static void nr_execute_handle_library(const char* filename, } } +static void nr_execute_handle_autoload(const char* filename, + const size_t filename_len) { +#define AUTOLOAD_MAGIC_FILE "vendor/autoload.php" +#define AUTOLOAD_MAGIC_FILE_LEN (sizeof(AUTOLOAD_MAGIC_FILE) - 1) + + if (!NRINI(vulnerability_management_package_detection_enabled)) { + // do nothing when vulnerability management package detection is disabled + return; + } + + if (!NRINI(vulnerability_management_composer_api_enabled)) { + // do nothing when use of composer to collect package info is disabled + return; + } + + if (NRPRG(txn)->composer_info.autoload_detected) { + // autoload already handled + return; + } + + if (!nr_striendswith(STR_AND_LEN(filename), AUTOLOAD_MAGIC_FILE, + AUTOLOAD_MAGIC_FILE_LEN)) { + // not an autoload file + return; + } + + nrl_debug(NRL_FRAMEWORK, "detected autoload with %s, which ends with %s", + filename, AUTOLOAD_MAGIC_FILE); + NRPRG(txn)->composer_info.autoload_detected = true; + nr_fw_support_add_library_supportability_metric(NRPRG(txn), "Autoloader"); + + nr_composer_handle_autoload(filename); +} + static void nr_execute_handle_logging_framework(const char* filename, const size_t filename_len TSRMLS_DC) { @@ -999,6 +1033,7 @@ static void nr_php_user_instrumentation_from_file(const char* filename, nr_execute_handle_framework(all_frameworks, num_all_frameworks, filename, filename_len TSRMLS_CC); nr_execute_handle_library(filename, filename_len TSRMLS_CC); + nr_execute_handle_autoload(filename, filename_len); nr_execute_handle_logging_framework(filename, filename_len TSRMLS_CC); if (NRINI(vulnerability_management_package_detection_enabled)) { nr_execute_handle_package(filename); diff --git a/agent/php_newrelic.h b/agent/php_newrelic.h index 04ebd1ad2..1860b5167 100644 --- a/agent/php_newrelic.h +++ b/agent/php_newrelic.h @@ -593,6 +593,8 @@ nrinibool_t nrinibool_t vulnerability_management_package_detection_enabled; /* newrelic.vulnerability_management.package_detection.enabled */ +nrinibool_t + vulnerability_management_composer_api_enabled; /* newrelic.vulnerability_management.composer_api.enabled */ #if ZEND_MODULE_API_NO < ZEND_7_4_X_API_NO /* diff --git a/agent/php_nrini.c b/agent/php_nrini.c index 6fdd898d3..6c21d1bdf 100644 --- a/agent/php_nrini.c +++ b/agent/php_nrini.c @@ -3091,6 +3091,15 @@ STD_PHP_INI_ENTRY_EX("newrelic.vulnerability_management.package_detection.enable newrelic_globals, nr_enabled_disabled_dh) +STD_PHP_INI_ENTRY_EX("newrelic.vulnerability_management.composer_api.enabled", + "0", + NR_PHP_REQUEST, + nr_boolean_mh, + vulnerability_management_composer_api_enabled, + zend_newrelic_globals, + newrelic_globals, + nr_enabled_disabled_dh) + PHP_INI_END() /* } */ void nr_php_register_ini_entries(int module_number TSRMLS_DC) { diff --git a/agent/php_txn.c b/agent/php_txn.c index 785007b41..b5b57975c 100644 --- a/agent/php_txn.c +++ b/agent/php_txn.c @@ -19,10 +19,12 @@ #include "nr_agent.h" #include "nr_commands.h" #include "nr_header.h" +#include "nr_php_packages.h" #include "nr_rum.h" #include "nr_segment_children.h" #include "nr_txn.h" #include "nr_version.h" +#include "fw_support.h" #include "util_labels.h" #include "util_logging.h" #include "util_memory.h" @@ -711,6 +713,50 @@ void nr_php_txn_create_agent_php_version_metrics(nrtxn_t* txn) { nr_php_txn_create_php_version_metric(txn, version); } +void nr_php_txn_php_package_create_major_metric(void* value, + const char* key, + size_t key_len, + void* user_data) { + nrtxn_t* txn = (nrtxn_t*)user_data; + nr_php_package_t* suggested = value; + nr_php_package_t* actual = NULL; + + (void)key; + (void)key_len; + + if (NULL == txn) { + return; + } + + if (NULL == suggested) { + return; + } + + /* see if the actual packages has a version we can use over the + * one provided with the suggested package + */ + actual + = nr_php_packages_get_package(txn->php_packages, suggested->package_name); + + nrl_verbosedebug( + NRL_INSTRUMENT, + "Creating PHP Package Supportability Metric for package " + "'%s', suggested version '%s', actual version '%s'", + NRSAFESTR(suggested->package_name), NRSAFESTR(suggested->package_version), + NRSAFESTR(NULL != actual ? actual->package_version : "NULL")); + nr_fw_support_add_package_supportability_metric( + txn, suggested->package_name, suggested->package_version, actual); +} + +void nr_php_txn_create_packages_major_metrics(nrtxn_t* txn) { + if (NULL == txn) { + return; + } + + nr_php_packages_iterate(txn->php_package_major_version_metrics_suggestions, + nr_php_txn_php_package_create_major_metric, txn); +} + nr_status_t nr_php_txn_begin(const char* appnames, const char* license TSRMLS_DC) { nrtxnopt_t opts; @@ -1165,6 +1211,9 @@ nr_status_t nr_php_txn_end(int ignoretxn, int in_post_deactivate TSRMLS_DC) { /* Agent and PHP version metrics*/ nr_php_txn_create_agent_php_version_metrics(txn); + /* PHP packages major version metrics */ + nr_php_txn_create_packages_major_metrics(txn); + /* Add CPU and memory metrics */ nr_php_resource_usage_sampler_end(TSRMLS_C); diff --git a/agent/php_txn_private.h b/agent/php_txn_private.h index bf49eb107..6788877ad 100644 --- a/agent/php_txn_private.h +++ b/agent/php_txn_private.h @@ -65,3 +65,24 @@ extern void nr_php_txn_create_agent_version_metric(nrtxn_t* txn); */ extern void nr_php_txn_create_php_version_metric(nrtxn_t* txn, const char* version); + +/* + * Purpose : Callback for nr_php_packages_iterate to create major + * version metrics. + * + * Params : 1. PHP suggestion package version + * 2. PHP suggestion package name + * 3. PHP suggestion package name length + * 4. The current transaction (via userdata) + */ +extern void nr_php_txn_php_package_create_major_metric(void* value, + const char* key, + size_t key_len, + void* user_data); + +/* + * Purpose : Create and record metric for a package major versions. + * + * Params : 1. The current transaction. + */ +extern void nr_php_txn_create_packages_major_metrics(nrtxn_t* txn); diff --git a/agent/scripts/newrelic.ini.template b/agent/scripts/newrelic.ini.template index 0e5da6ab3..4ed06a091 100644 --- a/agent/scripts/newrelic.ini.template +++ b/agent/scripts/newrelic.ini.template @@ -1332,3 +1332,12 @@ newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log" ; for vulnerability management. ; ;newrelic.vulnerability_management.package_detection.enabled = true + +; Setting: newrelic.vulnerability_management.composer_api.enabled +; Type : boolean +; Scope : per-directory +; Default: false +; Info : Toggles whether the agent should try using Composer's runtime API +; to gather package information for vulnerability management. +; +;newrelic.vulnerability_management.composer_api.enabled = false diff --git a/agent/tests/test_fw_support.c b/agent/tests/test_fw_support.c index c8b8e021d..980dc02aa 100644 --- a/agent/tests/test_fw_support.c +++ b/agent/tests/test_fw_support.c @@ -11,7 +11,12 @@ tlib_parallel_info_t parallel_info = {.suggested_nthreads = -1, .state_size = 0}; -static void test_fw_supportability_metrics(void) { +// When package detection for vulnerability management is disabled, +// txn->php_packages is not populated and package version cannot +// be obtained from php_package. This test is to ensure that +// the package supportability metric is created in case php_package +// is not available and that fallback version is used. +static void test_fw_supportability_metrics_with_vm_disabled(void) { #define LIBRARY_NAME "php-package" #define LIBRARY_MAJOR_VERSION "7" #define LIBRARY_MAJOR_VERSION_2 "10" @@ -22,9 +27,11 @@ static void test_fw_supportability_metrics(void) { #define LIBRARY_MAJOR_VERSION_7 "0.4.5" #define LIBRARY_METRIC "Supportability/library/" LIBRARY_NAME "/detected" #define LOGGING_LIBRARY_METRIC "Supportability/Logging/PHP/" LIBRARY_NAME -#define PACKAGE_METRIC "Supportability/PHP/package/" LIBRARY_NAME +#define PACKAGE_METRIC_PREFIX "Supportability/PHP/package/" +#define PACKAGE_METRIC PACKAGE_METRIC_PREFIX LIBRARY_NAME nrtxn_t t; nrtxn_t* txn = &t; + nr_php_package_t* php_package = NULL; txn->unscoped_metrics = nrm_table_create(10); /* NULL tests - don't blow up */ @@ -44,15 +51,18 @@ static void test_fw_supportability_metrics(void) { tlib_pass_if_int_equal("NULL logging library metric not created", 0, nrm_table_size(txn->unscoped_metrics)); - nr_fw_support_add_package_supportability_metric(NULL, LIBRARY_NAME, LIBRARY_MAJOR_VERSION); + nr_fw_support_add_package_supportability_metric( + NULL, LIBRARY_NAME, LIBRARY_MAJOR_VERSION, php_package); tlib_pass_if_int_equal("package metric not created in NULL metrics", 0, nrm_table_size(txn->unscoped_metrics)); - nr_fw_support_add_package_supportability_metric(txn, NULL, LIBRARY_MAJOR_VERSION); + nr_fw_support_add_package_supportability_metric( + txn, NULL, LIBRARY_MAJOR_VERSION, php_package); tlib_pass_if_int_equal("NULL package name, metric not created", 0, nrm_table_size(txn->unscoped_metrics)); - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, NULL); + nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, NULL, + php_package); tlib_pass_if_int_equal("NULL major version, metric not created", 0, nrm_table_size(txn->unscoped_metrics)); @@ -71,51 +81,51 @@ static void test_fw_supportability_metrics(void) { "happy path: logging library metric created", nrm_find(txn->unscoped_metrics, LOGGING_LIBRARY_METRIC "/disabled")); - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, - LIBRARY_MAJOR_VERSION); + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION, php_package); tlib_pass_if_not_null("happy path test 1: package metric created", nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/" LIBRARY_MAJOR_VERSION "/detected")); - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, - LIBRARY_MAJOR_VERSION_2); + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION_2, php_package); tlib_pass_if_not_null("happy path test 2: package metric created", nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/" LIBRARY_MAJOR_VERSION_2 "/detected")); - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, - LIBRARY_MAJOR_VERSION_3); + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION_3, php_package); tlib_pass_if_not_null("happy path test 3: package metric created", nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/" LIBRARY_MAJOR_VERSION_3 "/detected")); - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, - LIBRARY_MAJOR_VERSION_4); + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION_4, php_package); tlib_pass_if_not_null( "happy path test 4: package metric created", nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/1/detected")); - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, - LIBRARY_MAJOR_VERSION_5); + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION_5, php_package); tlib_pass_if_not_null( "happy path test 5: package metric created", nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/12/detected")); - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, - LIBRARY_MAJOR_VERSION_6); + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION_6, php_package); tlib_pass_if_not_null( "happy path test 6: package metric created", nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/123/detected")); - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, - LIBRARY_MAJOR_VERSION_7); + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION_7, php_package); tlib_pass_if_not_null( "happy path test 7: package metric created", nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/0/detected")); NRINI(force_framework) = true; - nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, - LIBRARY_MAJOR_VERSION); + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION, php_package); tlib_pass_if_not_null( "happy path test 8: package metric created", nrm_find(txn->unscoped_metrics, PACKAGE_METRIC "/7/forced")); @@ -123,6 +133,88 @@ static void test_fw_supportability_metrics(void) { nrm_table_destroy(&txn->unscoped_metrics); } +// When package detection for vulnerability management is enabled, +// txn->php_packages is populated and package version can be obtained +// from php_package stored in txn->php_packages. This test is to ensure +// that the package supportability metric is created in case php_package +// is available and that package version from php_package is used. +static void test_fw_supportability_metrics_with_vm_enabled(void) { +#define PHP_PACKAGE_MAJOR_VERSION "8" +#define PHP_PACKAGE_VERSION PHP_PACKAGE_MAJOR_VERSION ".4.0" + nrtxn_t t; + nrtxn_t* txn = &t; + nr_php_package_t php_package + = {.package_name = LIBRARY_NAME, + .package_version = PHP_PACKAGE_VERSION, + .source_priority = NR_PHP_PACKAGE_SOURCE_COMPOSER}; + nr_php_package_t php_package_null_version + = {.package_name = LIBRARY_NAME, + .package_version = NULL, + .source_priority = NR_PHP_PACKAGE_SOURCE_COMPOSER}; + nr_php_package_t php_package_unknown_version + = {.package_name = LIBRARY_NAME, + .package_version = PHP_PACKAGE_VERSION_UNKNOWN, + .source_priority = NR_PHP_PACKAGE_SOURCE_COMPOSER}; + txn->unscoped_metrics = nrm_table_create(10); + + NRINI(force_framework) = false; + nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, NULL, + &php_package_null_version); + tlib_pass_if_null( + "library major version metric not created when version is unknown - " + "version is NULL and package version is NULL", + nrm_get_metric(txn->unscoped_metrics, 0)); + + nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, + PHP_PACKAGE_VERSION_UNKNOWN, + &php_package_null_version); + tlib_pass_if_null( + "library major version metric not created when version is unknown - " + "version is PHP_PACKAGE_VERSION_UNKNOWN and package version is NULL", + nrm_get_metric(txn->unscoped_metrics, 0)); + + nr_fw_support_add_package_supportability_metric(txn, LIBRARY_NAME, NULL, + &php_package_unknown_version); + tlib_pass_if_null( + "library major version metric not created when version is unknown - " + "version is NULL and package version is PHP_PACKAGE_VERSION_UNKNOWN", + nrm_get_metric(txn->unscoped_metrics, 0)); + + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION, &php_package); + tlib_pass_if_not_null( + "php package major version is used for 'detected' metric", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" PHP_PACKAGE_MAJOR_VERSION "/detected")); + + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION, &php_package_null_version); + tlib_pass_if_not_null( + "library major version is used for 'detected' metric when php package " + "version is NULL", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" LIBRARY_MAJOR_VERSION "/detected")); + + NRINI(force_framework) = true; + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION, &php_package); + tlib_pass_if_not_null( + "php package major version is used for 'detected' metric", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" PHP_PACKAGE_MAJOR_VERSION "/forced")); + + nr_fw_support_add_package_supportability_metric( + txn, LIBRARY_NAME, LIBRARY_MAJOR_VERSION, &php_package_null_version); + tlib_pass_if_not_null( + "library major version is used for 'forced' metric when php package " + "version is NULL", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" LIBRARY_MAJOR_VERSION "/detected")); + + nrm_table_destroy(&txn->unscoped_metrics); +} + void test_main(void* p NRUNUSED) { - test_fw_supportability_metrics(); + test_fw_supportability_metrics_with_vm_disabled(); + test_fw_supportability_metrics_with_vm_enabled(); } diff --git a/agent/tests/test_lib_aws_sdk_php.c b/agent/tests/test_lib_aws_sdk_php.c index 8ddffce9d..91fa88b26 100644 --- a/agent/tests/test_lib_aws_sdk_php.c +++ b/agent/tests/test_lib_aws_sdk_php.c @@ -80,14 +80,14 @@ static void test_nr_lib_aws_sdk_php_add_supportability_service_metric(void) { static void test_nr_lib_aws_sdk_php_handle_version(void) { #define LIBRARY_NAME "aws/aws-sdk-php" -#define LIBRARY_MAJOR_VERSION "7" -#define LIBRARY_MAJOR_VERSION_2 "10" -#define LIBRARY_MAJOR_VERSION_3 "100" -#define LIBRARY_MAJOR_VERSION_4 "4.23" -#define LIBRARY_MAJOR_VERSION_55 "55.34" -#define LIBRARY_MAJOR_VERSION_6123 "6123.45" -#define LIBRARY_MAJOR_VERSION_0 "0.4.5" -#define PACKAGE_METRIC "Supportability/PHP/package/" LIBRARY_NAME + const char* library_versions[] + = {"7", "10", "100", "4.23", "55.34", "6123.45", "0.4.5"}; + nr_php_package_t* p = NULL; +#define TEST_DESCRIPTION_FMT \ + "nr_lib_aws_sdk_php_handle_version with library_versions[%ld]=%s: package " \ + "major version metric - %s" + char* test_description = NULL; + size_t i = 0; /* * If lib_aws_sdk_php_handle_version function is ever called, we have already @@ -95,73 +95,54 @@ static void test_nr_lib_aws_sdk_php_handle_version(void) { */ /* - * Aws/Sdk exists. Should return aws package metric with + * Aws/Sdk class exists. Should create aws package metric suggestion with * version */ - tlib_php_request_start(); - declare_aws_sdk_class("Aws", "Sdk", LIBRARY_MAJOR_VERSION); - nr_lib_aws_sdk_php_handle_version(); - tlib_pass_if_not_null("version test 1: package metric created", - nrm_find(NRPRG(txn)->unscoped_metrics, PACKAGE_METRIC - "/" LIBRARY_MAJOR_VERSION "/detected")); - tlib_php_request_end(); + for (i = 0; i < sizeof(library_versions) / sizeof(library_versions[0]); i++) { + tlib_php_request_start(); - tlib_php_request_start(); - declare_aws_sdk_class("Aws", "Sdk", LIBRARY_MAJOR_VERSION_2); - nr_lib_aws_sdk_php_handle_version(); - tlib_pass_if_not_null("version test 2: package metric created", - nrm_find(NRPRG(txn)->unscoped_metrics, PACKAGE_METRIC - "/" LIBRARY_MAJOR_VERSION_2 "/detected")); - tlib_php_request_end(); + declare_aws_sdk_class("Aws", "Sdk", library_versions[i]); + nr_lib_aws_sdk_php_handle_version(); - tlib_php_request_start(); - declare_aws_sdk_class("Aws", "Sdk", LIBRARY_MAJOR_VERSION_3); - nr_lib_aws_sdk_php_handle_version(); - tlib_pass_if_not_null("version test 3: package metric created", - nrm_find(NRPRG(txn)->unscoped_metrics, PACKAGE_METRIC - "/" LIBRARY_MAJOR_VERSION_3 "/detected")); - tlib_php_request_end(); + p = nr_php_packages_get_package( + NRPRG(txn)->php_package_major_version_metrics_suggestions, + LIBRARY_NAME); - tlib_php_request_start(); - declare_aws_sdk_class("Aws", "Sdk", LIBRARY_MAJOR_VERSION_4); - nr_lib_aws_sdk_php_handle_version(); - tlib_pass_if_not_null( - "version test 4: package metric created", - nrm_find(NRPRG(txn)->unscoped_metrics, PACKAGE_METRIC "/4/detected")); - tlib_php_request_end(); + test_description = nr_formatf(TEST_DESCRIPTION_FMT, i, library_versions[i], + "suggestion created"); + tlib_pass_if_not_null(test_description, p); + nr_free(test_description); - tlib_php_request_start(); - declare_aws_sdk_class("Aws", "Sdk", LIBRARY_MAJOR_VERSION_55); - nr_lib_aws_sdk_php_handle_version(); - tlib_pass_if_not_null( - "version test 5: package metric created", - nrm_find(NRPRG(txn)->unscoped_metrics, PACKAGE_METRIC "/55/detected")); - tlib_php_request_end(); + test_description = nr_formatf(TEST_DESCRIPTION_FMT, i, library_versions[i], + "suggested version set"); + tlib_pass_if_str_equal(test_description, library_versions[i], + p->package_version); + nr_free(test_description); - tlib_php_request_start(); - declare_aws_sdk_class("Aws", "Sdk", LIBRARY_MAJOR_VERSION_6123); - nr_lib_aws_sdk_php_handle_version(); - tlib_pass_if_not_null( - "version test 6: package metric created", - nrm_find(NRPRG(txn)->unscoped_metrics, PACKAGE_METRIC "/6123/detected")); - tlib_php_request_end(); - - tlib_php_request_start(); - declare_aws_sdk_class("Aws", "Sdk", LIBRARY_MAJOR_VERSION_0); - nr_lib_aws_sdk_php_handle_version(); - tlib_pass_if_not_null( - "version test 7: package metric created", - nrm_find(NRPRG(txn)->unscoped_metrics, PACKAGE_METRIC "/0/detected")); - tlib_php_request_end(); + tlib_php_request_end(); + } /* - * Aws/Sdk does not exist, should not return package metric if no version - * This case should never happen in real situations. + * Aws/Sdk class does not exist, should create package metric suggestion + * with PHP_PACKAGE_VERSION_UNKNOWN version. This case should never happen + * in real situations. */ tlib_php_request_start(); + nr_lib_aws_sdk_php_handle_version(); - tlib_pass_if_null("aws library metric created", - nrm_find(NRPRG(txn)->unscoped_metrics, PACKAGE_METRIC)); + + p = nr_php_packages_get_package( + NRPRG(txn)->php_package_major_version_metrics_suggestions, LIBRARY_NAME); + + tlib_pass_if_not_null( + "nr_lib_aws_sdk_php_handle_version when Aws\\Sdk class is not defined - " + "suggestion created", + p); + tlib_pass_if_str_equal( + "nr_lib_aws_sdk_php_handle_version when Aws\\Sdk class is not defined - " + "suggested version set to PHP_PACKAGE_VERSION_UNKNOWN", + PHP_PACKAGE_VERSION_UNKNOWN, p->package_version); + tlib_php_request_end(); } diff --git a/agent/tests/test_php_txn.c b/agent/tests/test_php_txn.c new file mode 100644 index 000000000..d0665bb26 --- /dev/null +++ b/agent/tests/test_php_txn.c @@ -0,0 +1,268 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "tlib_php.h" + +#include "php_agent.h" +#include "php_txn_private.h" + +#define LIBRARY_NAME "vendor_name/package_name" +#define LIBRARY_VERSION "1.2.3" +#define DEVEL_LIBRARY_VERSION "1.2.x-dev" +#define LIBRARY_MAJOR_VERSION "1" +#define COMPOSER_PACKAGE_VERSION "2.1.3" +#define COMPOSER_MAJOR_VERSION "2" +#define PACKAGE_METRIC_PREFIX "Supportability/PHP/package/" +#define PACKAGE_METRIC PACKAGE_METRIC_PREFIX LIBRARY_NAME + +tlib_parallel_info_t parallel_info = {.suggested_nthreads = 1, .state_size = 0}; + +nr_php_package_t php_package + = {.package_name = LIBRARY_NAME, + .package_version = LIBRARY_VERSION, + .source_priority = NR_PHP_PACKAGE_SOURCE_COMPOSER}; + +static void test_nr_php_txn_php_package_create_major_metric() { + nrtxn_t t; + nrtxn_t* txn = &t; + + txn->unscoped_metrics = nrm_table_create(10); + txn->php_packages = nr_php_packages_create(); + txn->php_package_major_version_metrics_suggestions = nr_php_packages_create(); + + tlib_php_request_start(); + + /* need to call callback with invalid values to make sure it doesnt crash + * code depends on txn and txn->php_packages existing so these are created + * above with the package suggestions data structure included for good measure + */ + + /* this tests these params in callback: + * suggested = NULL + * actual = NULL + * key = NULL + * txn = NULL + */ + nr_php_txn_php_package_create_major_metric(NULL, NULL, 0, NULL); + tlib_pass_if_int_equal("NULL txn, metric not created", 0, + nrm_table_size(txn->unscoped_metrics)); + + /* this tests these params in callback: + * suggested = NULL + * actual = NULL + * key != NULL + * txn != NULL + */ + nr_php_txn_php_package_create_major_metric(NULL, LIBRARY_NAME, + strlen(LIBRARY_NAME), (void*)txn); + tlib_pass_if_int_equal("NULL value, metric not created", 0, + nrm_table_size(txn->unscoped_metrics)); + + /* the key is not actually used by the callback - just the package name + * in the suggested package so this casee will still create a metric + */ + /* this tests these params in callback: + * suggested != NULL + * actual = NULL + * key = NULL + * txn = != NULL + */ + nr_php_txn_php_package_create_major_metric(&php_package, NULL, 0, (void*)txn); + tlib_pass_if_int_equal("NULL key, metric created", 1, + nrm_table_size(txn->unscoped_metrics)); + + /* cleanup */ + nr_php_packages_destroy(&txn->php_packages); + nr_php_packages_destroy(&txn->php_package_major_version_metrics_suggestions); + nrm_table_destroy(&txn->unscoped_metrics); + + tlib_php_request_end(); +} + +static void test_nr_php_txn_create_packages_major_metrics() { + nrtxn_t t; + nrtxn_t* txn = &t; + + txn->unscoped_metrics = nrm_table_create(10); + txn->php_packages = nr_php_packages_create(); + txn->php_package_major_version_metrics_suggestions = nr_php_packages_create(); + + tlib_php_request_start(); + + /* invalid txn should not crash */ + nr_php_txn_create_packages_major_metrics(NULL); + tlib_pass_if_int_equal("NULL txn, metric not created", 0, + nrm_table_size(txn->unscoped_metrics)); + + /* test with valid txn no package suggestions */ + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal("valid txn with no suggestions, metric not created", 0, + nrm_table_size(txn->unscoped_metrics)); + + /* + * Tests: + * 1. suggestion with NULL version, no packages + * 2. suggestion with PHP_PACKAGE_VERSION_UNKNOWN version, no packages + * 3. suggestion with known version, no packages + * 4. package with known version and suggestion with known version + * 5. package with known version and suggestion with unknown version + * 6. package with unknown version and suggestion with known version + * 7. package with unknown version and suggestion with unknown version + * 8. test that causes "actual" to be NULL in callback + * 9. package with known "dev" version with and suggestion with unknown version + */ + + /* 1. suggestion with NULL version, no packages */ + nr_txn_suggest_package_supportability_metric(txn, LIBRARY_NAME, NULL); + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal("suggestion with NULL version, metric not created", 0, + nrm_table_size(txn->unscoped_metrics)); + + /* 2. suggestion with PHP_PACKAGE_VERSION_UNKNOWN version, no packages + * also + * 8. test that causes "actual" to be NULL in callback + */ + nr_txn_suggest_package_supportability_metric(txn, LIBRARY_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal( + "suggestion with PHP_PACKAGE_VERSION_UNKNOWN version, metric not created", + 0, nrm_table_size(txn->unscoped_metrics)); + + /* 3. suggestion with known version, no packages + * also + * 8. test that causes "actual" to be NULL in callback + */ + nr_txn_suggest_package_supportability_metric(txn, LIBRARY_NAME, + LIBRARY_VERSION); + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal("suggestion with valid version, metric created", 1, + nrm_table_size(txn->unscoped_metrics)); + tlib_pass_if_not_null( + "php package major version is used for 'detected' metric", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" LIBRARY_MAJOR_VERSION "/detected")); + + /* reset metrics */ + nrm_table_destroy(&txn->unscoped_metrics); + txn->unscoped_metrics = nrm_table_create(10); + + /* 4. package with known version and suggestion with known version + * + * add a package with a "better" version determined from composer api + * and use existing suggestion which has a different version + */ + nr_txn_add_php_package_from_source(txn, LIBRARY_NAME, + COMPOSER_PACKAGE_VERSION, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal("suggestion with valid version, metric created", 1, + nrm_table_size(txn->unscoped_metrics)); + tlib_pass_if_not_null( + "php package major version is used for 'detected' metric", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" COMPOSER_MAJOR_VERSION "/detected")); + + /* reset suggestions, leave package with known version in place */ + nr_php_packages_destroy(&txn->php_package_major_version_metrics_suggestions); + txn->php_package_major_version_metrics_suggestions = nr_php_packages_create(); + + /* reset metrics */ + nrm_table_destroy(&txn->unscoped_metrics); + txn->unscoped_metrics = nrm_table_create(10); + + /* 5. package with known version and suggestion with unknown version + * + * add a suggestion with no version and test metric uses package version + */ + nr_txn_suggest_package_supportability_metric(txn, LIBRARY_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal("suggestion with valid version, metric created", 1, + nrm_table_size(txn->unscoped_metrics)); + tlib_pass_if_not_null( + "php package major version is used for 'detected' metric", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" COMPOSER_MAJOR_VERSION "/detected")); + + /* reset everything */ + nrm_table_destroy(&txn->unscoped_metrics); + txn->unscoped_metrics = nrm_table_create(10); + nr_php_packages_destroy(&txn->php_packages); + txn->php_packages = nr_php_packages_create(); + nr_php_packages_destroy(&txn->php_package_major_version_metrics_suggestions); + txn->php_package_major_version_metrics_suggestions = nr_php_packages_create(); + + /* 6. package with unknown version and suggestion with known version */ + nr_txn_suggest_package_supportability_metric(txn, LIBRARY_NAME, + LIBRARY_VERSION); + nr_txn_add_php_package_from_source(txn, LIBRARY_NAME, + PHP_PACKAGE_VERSION_UNKNOWN, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal("suggestion with valid version, metric created", 1, + nrm_table_size(txn->unscoped_metrics)); + tlib_pass_if_not_null( + "php package suggestion major version is used for 'detected' metric", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" LIBRARY_MAJOR_VERSION "/detected")); + + /* reset everything */ + nrm_table_destroy(&txn->unscoped_metrics); + txn->unscoped_metrics = nrm_table_create(10); + nr_php_packages_destroy(&txn->php_packages); + txn->php_packages = nr_php_packages_create(); + nr_php_packages_destroy(&txn->php_package_major_version_metrics_suggestions); + txn->php_package_major_version_metrics_suggestions = nr_php_packages_create(); + + /* 7. package with unknown version and suggestion with unknown version */ + nr_txn_suggest_package_supportability_metric(txn, LIBRARY_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); + nr_txn_add_php_package_from_source(txn, LIBRARY_NAME, + PHP_PACKAGE_VERSION_UNKNOWN, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal( + "suggestion and package w/o version, metric not created", 0, + nrm_table_size(txn->unscoped_metrics)); + + /* reset everything */ + nrm_table_destroy(&txn->unscoped_metrics); + txn->unscoped_metrics = nrm_table_create(10); + nr_php_packages_destroy(&txn->php_packages); + txn->php_packages = nr_php_packages_create(); + nr_php_packages_destroy(&txn->php_package_major_version_metrics_suggestions); + txn->php_package_major_version_metrics_suggestions = nr_php_packages_create(); + + /* 9. package with known "dev" version with and suggestion with unknown version */ + nr_txn_suggest_package_supportability_metric(txn, LIBRARY_NAME, + PHP_PACKAGE_VERSION_UNKNOWN); + nr_txn_add_php_package_from_source(txn, LIBRARY_NAME, + DEVEL_LIBRARY_VERSION, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + nr_php_txn_create_packages_major_metrics(txn); + tlib_pass_if_int_equal("suggestion with valid 'dev' version, metric created", 1, + nrm_table_size(txn->unscoped_metrics)); + tlib_pass_if_not_null( + "php package suggestion major version with 'dev' is used for 'detected' metric", + nrm_find(txn->unscoped_metrics, + PACKAGE_METRIC "/" LIBRARY_MAJOR_VERSION "/detected")); + + + /* cleanup */ + nr_php_packages_destroy(&txn->php_packages); + nr_php_packages_destroy(&txn->php_package_major_version_metrics_suggestions); + nrm_table_destroy(&txn->unscoped_metrics); + + tlib_php_request_end(); +} + + +void test_main(void* p NRUNUSED) { + tlib_php_engine_create(""); + test_nr_php_txn_php_package_create_major_metric(); + test_nr_php_txn_create_packages_major_metrics(); + tlib_php_engine_destroy(); +} diff --git a/axiom/nr_php_packages.c b/axiom/nr_php_packages.c index 9c1f97ef8..b8a034086 100644 --- a/axiom/nr_php_packages.c +++ b/axiom/nr_php_packages.c @@ -23,7 +23,23 @@ typedef struct { bool package_added; } nr_php_package_json_builder_t; -nr_php_package_t* nr_php_package_create(char* name, char* version) { +static inline const char* nr_php_package_source_priority_to_string(const nr_php_package_source_priority_t source_priority) { + switch (source_priority) { + case NR_PHP_PACKAGE_SOURCE_SUGGESTION: + return "suggestion"; + case NR_PHP_PACKAGE_SOURCE_LEGACY: + return "legacy"; + case NR_PHP_PACKAGE_SOURCE_COMPOSER: + return "composer"; + default: + return "unknown"; + } +} + +nr_php_package_t* nr_php_package_create_with_source( + const char* name, + const char* version, + const nr_php_package_source_priority_t source_priority) { nr_php_package_t* p = NULL; if (NULL == name) { @@ -43,12 +59,17 @@ nr_php_package_t* nr_php_package_create(char* name, char* version) { PHP_PACKAGE_VERSION_UNKNOWN); // if null, version is set to an empty // string with a space according to spec } + p->source_priority = source_priority; - nrl_verbosedebug(NRL_INSTRUMENT, "Creating PHP Package '%s', version '%s'", - p->package_name, p->package_version); + nrl_verbosedebug(NRL_INSTRUMENT, "Creating PHP Package '%s', version '%s', source %s", + p->package_name, p->package_version, nr_php_package_source_priority_to_string(source_priority)); return p; } +nr_php_package_t* nr_php_package_create(const char* name, const char* version) { + return nr_php_package_create_with_source(name, version, NR_PHP_PACKAGE_SOURCE_LEGACY); +} + void nr_php_package_destroy(nr_php_package_t* p) { if (NULL != p) { nr_free(p->package_name); @@ -72,14 +93,15 @@ nr_php_packages_t* nr_php_packages_create() { return h; } -void nr_php_packages_add_package(nr_php_packages_t* h, nr_php_package_t* p) { +nr_php_package_t* nr_php_packages_add_package(nr_php_packages_t* h, + nr_php_package_t* p) { nr_php_package_t* package; if (NULL == h) { - return; + return NULL; } if (NULL == p || NULL == p->package_name || NULL == p->package_version) { - return; + return NULL; } // If package with the same key already exists, we will check if the value is @@ -87,15 +109,26 @@ void nr_php_packages_add_package(nr_php_packages_t* h, nr_php_package_t* p) { package = (nr_php_package_t*)nr_hashmap_get(h->data, p->package_name, nr_strlen(p->package_name)); if (NULL != package) { - if (0 != nr_strcmp(package->package_version, p->package_version)) { + if (package->source_priority <= p->source_priority && 0 != nr_strcmp(package->package_version, p->package_version)) { nr_free(package->package_version); package->package_version = nr_strdup(p->package_version); } nr_php_package_destroy(p); - return; + return package; } nr_hashmap_set(h->data, p->package_name, nr_strlen(p->package_name), p); + return p; +} + +void nr_php_packages_iterate(nr_php_packages_t* packages, + nr_php_packages_iter_t callback, + void* userdata) { + if (NULL == packages || NULL == callback) { + return; + } + + nr_hashmap_apply(packages->data, (nr_hashmap_apply_func_t)callback, userdata); } char* nr_php_package_to_json(nr_php_package_t* package) { diff --git a/axiom/nr_php_packages.h b/axiom/nr_php_packages.h index 118a63f79..f4945c43e 100644 --- a/axiom/nr_php_packages.h +++ b/axiom/nr_php_packages.h @@ -10,32 +10,65 @@ #include "util_random.h" #include "util_vector.h" #include "util_hashmap.h" +#include "util_strings.h" #define PHP_PACKAGE_VERSION_UNKNOWN " " +typedef enum { + NR_PHP_PACKAGE_SOURCE_SUGGESTION, + NR_PHP_PACKAGE_SOURCE_LEGACY, + NR_PHP_PACKAGE_SOURCE_COMPOSER +} nr_php_package_source_priority_t; + typedef struct _nr_php_package_t { char* package_name; char* package_version; + nr_php_package_source_priority_t source_priority; } nr_php_package_t; typedef struct _nr_php_packages_t { nr_hashmap_t* data; } nr_php_packages_t; +typedef void(nr_php_packages_iter_t)(void* value, + const char* name, + size_t name_len, + void* user_data); + /* - * Purpose : Create a new php package. If the name is null, then no package will + * Purpose : Create a new php package with desired source priority. If the name is null, then no package will * be created. If the version is null (version = NULL), then * the package will still be created and the version will be set to an * empty string with a space. * * Params : 1. Package name * 2. Package version + * 3. Package source priority (legacy or composer) * * Returns : A php package that has a name and version. If * nr_php_packages_add_package() is not called, then it must be freed * by nr_php_package_destroy() */ -extern nr_php_package_t* nr_php_package_create(char* name, char* version); +extern nr_php_package_t* nr_php_package_create_with_source( + const char* name, + const char* version, + const nr_php_package_source_priority_t source_priority); + +/* + * Purpose : Create a new php package with legacy source priority. If the name is null, then no package will + * be created. If the version is null (version = NULL), then + * the package will still be created and the version will be set to an + * empty string with a space. + * + * Params : 1. Package name + * 2. Package version + * + * Returns : A php package that has a name and version. If + * nr_php_packages_add_package() is not called, then it must be freed + * by nr_php_package_destroy() + */ +extern nr_php_package_t* nr_php_package_create(const char* name, + const char* version); /* * Purpose : Destroy/free php package @@ -64,10 +97,10 @@ extern nr_php_packages_t* nr_php_packages_create(void); * 2. A pointer to the php package that needs to be added to the * collection * - * Returns : Nothing + * Returns : pointer to added package on success or NULL otherwise. */ -extern void nr_php_packages_add_package(nr_php_packages_t* h, - nr_php_package_t* p); +extern nr_php_package_t* nr_php_packages_add_package(nr_php_packages_t* h, + nr_php_package_t* p); /* * Purpose : Destroy/free the collection @@ -118,6 +151,40 @@ static inline int nr_php_packages_has_package(nr_php_packages_t* h, return 0; } +/* + * Purpose : Retrieve a pointer to php package from the collection + * + * Params : 1. A pointer to nr_php_packages_t + * 2. The name of the package to retrieve + * + * Returns : Returns pointer to php package if the package exists or NULL + */ +static inline nr_php_package_t* nr_php_packages_get_package( + nr_php_packages_t* php_packages, + const char* package_name) { + if (NULL == package_name) { + return NULL; + } + + if (nrlikely(NULL != php_packages && NULL != php_packages->data)) { + return (nr_php_package_t*)nr_hashmap_get(php_packages->data, package_name, nr_strlen(package_name)); + } + return NULL; +} + +/* + * Purpose : Iterate over packages calling callback function + * + * Params : 1. A pointer to nr_php_packages_t + * 2. Callback function (nr_php_packages_iter_t) + * 3. Pointer to user data (can be NULL) + * + * Returns : Nothing + */ +void nr_php_packages_iterate(nr_php_packages_t* packages, + nr_php_packages_iter_t callback, + void* userdata); + /* * Purpose : Converts a package to a json * diff --git a/axiom/nr_txn.c b/axiom/nr_txn.c index c3667db81..774c44671 100644 --- a/axiom/nr_txn.c +++ b/axiom/nr_txn.c @@ -542,6 +542,7 @@ nrtxn_t* nr_txn_begin(nrapp_t* app, nt->custom_events = nr_analytics_events_create(app->limits.custom_events); nt->log_events = nr_log_events_create(app->limits.log_events); nt->php_packages = nr_php_packages_create(); + nt->php_package_major_version_metrics_suggestions = nr_php_packages_create(); /* * reset flag for creation of one-time logging metrics @@ -1246,6 +1247,7 @@ void nr_txn_destroy_fields(nrtxn_t* txn) { nr_segment_destroy_tree(txn->segment_root); nr_hashmap_destroy(&txn->parent_stacks); nr_php_packages_destroy(&txn->php_packages); + nr_php_packages_destroy(&txn->php_package_major_version_metrics_suggestions); nr_stack_destroy_fields(&txn->default_parent_stack); nr_slab_destroy(&txn->segment_slab); nr_minmax_heap_set_destructor(txn->segment_heap, NULL, NULL); @@ -3494,9 +3496,35 @@ void nr_txn_record_log_event(nrtxn_t* txn, nr_txn_add_logging_metrics(txn, log_level_name); } -void nr_txn_add_php_package(nrtxn_t* txn, - char* package_name, - char* package_version) { +nr_php_package_t* nr_txn_add_php_package_from_source( + nrtxn_t* txn, + char* package_name, + char* package_version, + const nr_php_package_source_priority_t source) { + nr_php_package_t* p = NULL; + + if (nrunlikely(NULL == txn)) { + return NULL; + } + + if (nr_strempty(package_name)) { + return NULL; + } + + p = nr_php_package_create_with_source(package_name, package_version, source); + return nr_php_packages_add_package(txn->php_packages, p); +} + +nr_php_package_t* nr_txn_add_php_package(nrtxn_t* txn, + char* package_name, + char* package_version) { + return nr_txn_add_php_package_from_source(txn, package_name, package_version, + NR_PHP_PACKAGE_SOURCE_LEGACY); +} + +void nr_txn_suggest_package_supportability_metric(nrtxn_t* txn, + const char* package_name, + const char* package_version) { nr_php_package_t* p = NULL; if (nrunlikely(NULL == txn)) { @@ -3507,6 +3535,11 @@ void nr_txn_add_php_package(nrtxn_t* txn, return; } - p = nr_php_package_create(package_name, package_version); - nr_php_packages_add_package(txn->php_packages, p); + nrl_verbosedebug(NRL_TXN, "Suggesting package %s %s", NRSAFESTR(package_name), + NRSAFESTR(package_version)); + + p = nr_php_package_create_with_source(package_name, package_version, + NR_PHP_PACKAGE_SOURCE_SUGGESTION); + nr_php_packages_add_package( + txn->php_package_major_version_metrics_suggestions, p); } diff --git a/axiom/nr_txn.h b/axiom/nr_txn.h index a6f942f66..8874260ec 100644 --- a/axiom/nr_txn.h +++ b/axiom/nr_txn.h @@ -203,6 +203,11 @@ typedef enum _nr_cpu_usage_t { NR_CPU_USAGE_COUNT = 2 } nr_cpu_usage_t; +typedef struct _nr_composer_info_t { + bool autoload_detected; + bool composer_detected; +} nr_composer_info_t; + /* * Possible transaction types, which go into the type bitfield in the nrtxn_t * struct. @@ -281,6 +286,9 @@ typedef struct _nrtxn_t { custom_events; /* Custom events created through the API. */ nr_log_events_t* log_events; /* Log events pool */ nr_php_packages_t* php_packages; /* Detected php packages */ + nr_php_packages_t* + php_package_major_version_metrics_suggestions; /* Suggested packages for + major metric creation */ nrtime_t user_cpu[NR_CPU_USAGE_COUNT]; /* User CPU usage */ nrtime_t sys_cpu[NR_CPU_USAGE_COUNT]; /* System CPU usage */ @@ -302,6 +310,7 @@ typedef struct _nrtxn_t { nr_distributed_trace_t* distributed_trace; /* distributed tracing metadata for the transaction */ nr_span_queue_t* span_queue; /* span queue when 8T is enabled */ + nr_composer_info_t composer_info; /* * flag to indicate if one time (per transaction) logging metrics @@ -1164,16 +1173,52 @@ static inline nr_segment_t* nr_txn_allocate_segment(nrtxn_t* txn) { } /* - * Purpose : Add php packages to transaction. This function should only be - * called when Vulnerability Management is enabled. + * Purpose : Add php package to transaction from desired source. This function + * should only be called when Vulnerability Management is enabled. + * + * Params : 1. The transaction + * 2. Package name + * 3. Package version + * 4. Source priority + * + * Returns : pointer to added package on success or NULL otherwise. + */ +nr_php_package_t* nr_txn_add_php_package_from_source( + nrtxn_t* txn, + char* package_name, + char* package_version, + const nr_php_package_source_priority_t source); + +/* + * Purpose : Add php package to transaction from legacy source. This function + * should only be called when Vulnerability Management is enabled. * * Params : 1. The transaction * 2. Package name * 3. Package version * + * Returns : pointer to added package on success or NULL otherwise. */ -void nr_txn_add_php_package(nrtxn_t* txn, - char* package_name, - char* package_version); +extern nr_php_package_t* nr_txn_add_php_package(nrtxn_t* txn, + char* package_name, + char* package_version); +/* + * Purpose : Add php package suggestion to transaction. This function + * can be used when Vulnerability Management is not enabled. It will + * add the package to the transaction's + * php_package_major_version_metrics_suggestions list. At the end of the + * transaction this list is traversed and any suggestions with a known version + * will have a package major version metric created. + * + * Params : 1. The transaction + * 2. Package name + * 3. Package version (can be NULL or PHP_PACKAGE_VERSION_UNKNOWN) + * + * Returns : Nothing. + */ +extern void nr_txn_suggest_package_supportability_metric( + nrtxn_t* txn, + const char* package_name, + const char* package_version); #endif /* NR_TXN_HDR */ diff --git a/axiom/tests/.gitignore b/axiom/tests/.gitignore index e2e2db564..22ee3b042 100644 --- a/axiom/tests/.gitignore +++ b/axiom/tests/.gitignore @@ -82,6 +82,7 @@ test_number_converter test_obfuscate test_object test_offsets +test_php_packages test_postgres test_quarantine test_random diff --git a/axiom/tests/test_php_packages.c b/axiom/tests/test_php_packages.c index 427ece058..bd2323b2f 100644 --- a/axiom/tests/test_php_packages.c +++ b/axiom/tests/test_php_packages.c @@ -192,6 +192,183 @@ static void test_php_package_without_version(void) { nr_php_packages_destroy(&hm); } +static void test_php_package_priority(void) { +#define PACKAGE_NAME "vendor/package" +#define NO_VERSION NULL +#define PACKAGE_VERSION "1.0.0" +#define COMPOSER_VERSION "1.0.1" +#define COMPOSER_VERSION_2 "2.0.1" + nr_php_package_t* legacy_package; + nr_php_package_t* composer_package; + nr_php_package_t* composer_package_2; + nr_php_package_t* p; + nr_php_packages_t* hm = NULL; + int count; + char* legacy_versions[] = {NO_VERSION, PACKAGE_VERSION}; + + // Package added with legacy priority first - version from composer should win + for (size_t i = 0; i < sizeof(legacy_versions) / sizeof(legacy_versions[0]); + i++) { + legacy_package = nr_php_package_create( + PACKAGE_NAME, legacy_versions[i]); // legacy priority + tlib_pass_if_int_equal("create package by uses legacy priority", + NR_PHP_PACKAGE_SOURCE_LEGACY, + legacy_package->source_priority); + composer_package = nr_php_package_create_with_source( + PACKAGE_NAME, COMPOSER_VERSION, + NR_PHP_PACKAGE_SOURCE_COMPOSER); // composer priority + tlib_pass_if_int_equal("create package by uses composer priority", + NR_PHP_PACKAGE_SOURCE_COMPOSER, + composer_package->source_priority); + + hm = nr_php_packages_create(); + // order of adding packages: legacy first, composer second + nr_php_packages_add_package(hm, legacy_package); + nr_php_packages_add_package(hm, composer_package); + + count = nr_php_packages_count(hm); + tlib_pass_if_int_equal("add same package", 1, count); + + p = nr_php_packages_get_package(hm, PACKAGE_NAME); + tlib_pass_if_not_null("package exists", p); + tlib_pass_if_str_equal("package version from composer wins", + COMPOSER_VERSION, p->package_version); + + nr_php_packages_destroy(&hm); + } + + // Package added with composer priority first - version from composer should + // win + for (size_t i = 0; i < sizeof(legacy_versions) / sizeof(legacy_versions[0]); + i++) { + legacy_package = nr_php_package_create( + PACKAGE_NAME, legacy_versions[i]); // legacy priority + tlib_pass_if_int_equal("create package by uses legacy priority", + NR_PHP_PACKAGE_SOURCE_LEGACY, + legacy_package->source_priority); + composer_package = nr_php_package_create_with_source( + PACKAGE_NAME, COMPOSER_VERSION, + NR_PHP_PACKAGE_SOURCE_COMPOSER); // composer priority + tlib_pass_if_int_equal("create package by uses composer priority", + NR_PHP_PACKAGE_SOURCE_COMPOSER, + composer_package->source_priority); + + hm = nr_php_packages_create(); + // order of adding packages: legacy first, composer second + nr_php_packages_add_package(hm, composer_package); + nr_php_packages_add_package(hm, legacy_package); + + count = nr_php_packages_count(hm); + tlib_pass_if_int_equal("add same package", 1, count); + + p = nr_php_packages_get_package(hm, PACKAGE_NAME); + tlib_pass_if_not_null("package exists", p); + tlib_pass_if_str_equal("package version from composer wins", + COMPOSER_VERSION, p->package_version); + + nr_php_packages_destroy(&hm); + } + + // Package added with composer priority only - last version from composer + // should win + composer_package = nr_php_package_create_with_source( + PACKAGE_NAME, COMPOSER_VERSION, + NR_PHP_PACKAGE_SOURCE_COMPOSER); // composer priority + tlib_pass_if_int_equal("create package by uses composer priority", + NR_PHP_PACKAGE_SOURCE_COMPOSER, + composer_package->source_priority); + + composer_package_2 = nr_php_package_create_with_source( + PACKAGE_NAME, COMPOSER_VERSION_2, + NR_PHP_PACKAGE_SOURCE_COMPOSER); // composer priority + tlib_pass_if_int_equal("create package by uses composer priority", + NR_PHP_PACKAGE_SOURCE_COMPOSER, + composer_package_2->source_priority); + + hm = nr_php_packages_create(); + // order of adding packages: composer first, composer second + nr_php_packages_add_package(hm, composer_package); + nr_php_packages_add_package(hm, composer_package_2); + + count = nr_php_packages_count(hm); + tlib_pass_if_int_equal("add same package", 1, count); + + p = nr_php_packages_get_package(hm, PACKAGE_NAME); + tlib_pass_if_not_null("package exists", p); + tlib_pass_if_str_equal("package version from last composer wins", + COMPOSER_VERSION_2, p->package_version); + + nr_php_packages_destroy(&hm); +} + +static void nr_php_packages_itereate_callback(void* value, + const char* key, + size_t key_len, + void* user_data) { + nr_php_package_t* package = value; + const char* name = key; + nrbuf_t* buf = (nrbuf_t*)user_data; + + if (NULL == buf) { + return; + } + + /* append name, len, version to string */ + nr_buffer_add(buf, name, key_len); + nr_buffer_add(buf, NR_PSTR(",")); + nr_buffer_add(buf, package->package_version, + nr_strlen(package->package_version)); + nr_buffer_add(buf, NR_PSTR("\n")); +} + +static void test_php_package_iterate(void) { + nr_php_package_t* package1; + nr_php_package_t* package2; + nr_php_package_t* package3; + nr_php_packages_t* hm = nr_php_packages_create(); + nrbuf_t* buf; + int count; + + // Test: create multiple new packages and add to hashmap + package1 = nr_php_package_create("name1", "name1_version"); + package2 = nr_php_package_create("name2", "name2_version"); + package3 = nr_php_package_create("name3", "name3_version"); + + nr_php_packages_add_package(hm, package1); + nr_php_packages_add_package(hm, package2); + nr_php_packages_add_package(hm, package3); + + count = nr_php_packages_count(hm); + + tlib_pass_if_int_equal("package count", 3, count); + + /* tests with invalid values + * NOTE: nr_buffer_cptr(buf) will return NULL if no data is in the buffer + */ + buf = nr_buffer_create(0, 0); + nr_php_packages_iterate(NULL, nr_php_packages_itereate_callback, (void*)buf); + tlib_pass_if_null("iterate with NULL hashmap", nr_buffer_cptr(buf)); + nr_php_packages_iterate(hm, nr_php_packages_itereate_callback, NULL); + tlib_pass_if_null("iterate with NULL userdata", nr_buffer_cptr(buf)); + nr_php_packages_iterate(hm, NULL, (void*)buf); + tlib_pass_if_null("iterate with NULL callback", nr_buffer_cptr(buf)); + nr_php_packages_iterate(NULL, NULL, NULL); + tlib_pass_if_null("iterate with all NULL", nr_buffer_cptr(buf)); + nr_buffer_destroy(&buf); + + /* test with valid values */ + buf = nr_buffer_create(0, 0); + nr_php_packages_iterate(hm, nr_php_packages_itereate_callback, (void*)buf); + nr_buffer_add(buf, NR_PSTR("\0")); + tlib_pass_if_str_equal("iterate created proper string", + "name1,name1_version\nname2,name2_version\nname3,name3_version\n", nr_buffer_cptr(buf)); + nr_buffer_destroy(&buf); + + nr_php_packages_destroy(&hm); +} + + + tlib_parallel_info_t parallel_info = {.suggested_nthreads = -1, .state_size = 0}; @@ -203,4 +380,6 @@ void test_main(void* p NRUNUSED) { test_php_packages_to_json(); test_php_package_exists_in_hashmap(); test_php_package_without_version(); -} \ No newline at end of file + test_php_package_priority(); + test_php_package_iterate(); +} diff --git a/axiom/tests/test_txn.c b/axiom/tests/test_txn.c index 59b19f9b0..9230a416a 100644 --- a/axiom/tests/test_txn.c +++ b/axiom/tests/test_txn.c @@ -8597,6 +8597,8 @@ static void test_nr_txn_add_php_package(void) { char* package_name4 = "Wordpress"; char* package_version4 = PHP_PACKAGE_VERSION_UNKNOWN; nrtxn_t* txn = new_txn(0); + nr_php_package_t* p1 = NULL; + nr_php_package_t* p2 = NULL; /* * NULL parameters: ensure it does not crash @@ -8621,6 +8623,130 @@ static void test_nr_txn_add_php_package(void) { nr_free(json); nr_txn_destroy(&txn); + + txn = new_txn(0); + p1 = nr_txn_add_php_package(txn, package_name1, package_version1); + p2 = nr_txn_add_php_package(txn, package_name1, package_version2); + tlib_pass_if_ptr_equal( + "same package name, different version, add returns same pointer", p1, p2); + nr_txn_destroy(&txn); +} + +static void test_nr_txn_add_php_package_from_source(void) { + char* json; + char* package_name1 = "Laravel"; + char* package_version1 = "8.83.27"; + char* package_name2 = "Slim"; + char* package_version2 = "4.12.0"; + char* package_name3 = "Drupal"; + char* package_version3 = NULL; + char* package_name4 = "Wordpress"; + char* package_version4 = PHP_PACKAGE_VERSION_UNKNOWN; + nrtxn_t* txn = new_txn(0); + nr_php_package_t* p1 = NULL; + nr_php_package_t* p2 = NULL; + + /* + * NULL parameters: ensure it does not crash + */ + nr_txn_add_php_package_from_source(NULL, NULL, NULL, 0); + nr_txn_add_php_package_from_source(NULL, package_name1, package_version1, 0); + nr_txn_add_php_package_from_source(txn, NULL, package_version1, 0); + nr_txn_add_php_package_from_source(txn, package_name1, NULL, 0); + + // Test: add php packages to transaction + nr_txn_add_php_package_from_source(txn, package_name1, package_version1, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + nr_txn_add_php_package_from_source(txn, package_name2, package_version2, + NR_PHP_PACKAGE_SOURCE_LEGACY); + nr_txn_add_php_package_from_source(txn, package_name3, package_version3, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + nr_txn_add_php_package_from_source(txn, package_name4, package_version4, + NR_PHP_PACKAGE_SOURCE_LEGACY); + json = nr_php_packages_to_json(txn->php_packages); + + tlib_pass_if_str_equal("correct json", + "[[\"Laravel\",\"8.83.27\",{}]," + "[\"Drupal\",\" \",{}],[\"Wordpress\",\" \",{}]," + "[\"Slim\",\"4.12.0\",{}]]", + json); + + nr_free(json); + nr_txn_destroy(&txn); + + txn = new_txn(0); + p1 = nr_txn_add_php_package_from_source(txn, package_name1, package_version1, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + p2 = nr_txn_add_php_package_from_source(txn, package_name1, package_version2, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + tlib_pass_if_ptr_equal( + "same package name, different version, add returns same pointer", p1, p2); + nr_txn_destroy(&txn); + + txn = new_txn(0); + p1 = nr_txn_add_php_package_from_source(txn, package_name1, package_version1, + NR_PHP_PACKAGE_SOURCE_LEGACY); + p2 = nr_txn_add_php_package_from_source(txn, package_name1, package_version2, + NR_PHP_PACKAGE_SOURCE_COMPOSER); + tlib_pass_if_ptr_equal( + "same package name, different version, add returns different pointer", p1, + p2); + tlib_pass_if_str_equal("composer version used", package_version2, + p2->package_version); + + nr_txn_destroy(&txn); +} + +static void test_nr_txn_suggest_package_supportability_metric(void) { + char* json; + char* package_name1 = "Laravel"; + char* package_version1 = "8.83.27"; + char* package_name2 = "Slim"; + char* package_version2 = "4.12.0"; + char* package_name3 = "Drupal"; + char* package_version3 = NULL; + char* package_name4 = "Wordpress"; + char* package_version4 = PHP_PACKAGE_VERSION_UNKNOWN; + nrtxn_t* txn = new_txn(0); + nr_php_package_t* p1 = NULL; + nr_php_package_t* p2 = NULL; + + /* + * NULL parameters: ensure it does not crash + */ + nr_txn_suggest_package_supportability_metric(NULL, NULL, NULL); + nr_txn_suggest_package_supportability_metric(NULL, package_name1, + package_version1); + nr_txn_suggest_package_supportability_metric(txn, NULL, package_version1); + nr_txn_suggest_package_supportability_metric(txn, package_name1, NULL); + + // Test: add php packages to transaction + nr_txn_suggest_package_supportability_metric(txn, package_name1, + package_version1); + nr_txn_suggest_package_supportability_metric(txn, package_name2, + package_version2); + nr_txn_suggest_package_supportability_metric(txn, package_name3, + package_version3); + nr_txn_suggest_package_supportability_metric(txn, package_name4, + package_version4); + json = nr_php_packages_to_json( + txn->php_package_major_version_metrics_suggestions); + + tlib_pass_if_str_equal("correct json", + "[[\"Laravel\",\"8.83.27\",{}]," + "[\"Drupal\",\" \",{}],[\"Wordpress\",\" \",{}]," + "[\"Slim\",\"4.12.0\",{}]]", + json); + + nr_free(json); + nr_txn_destroy(&txn); + + txn = new_txn(0); + p1 = nr_txn_add_php_package(txn, package_name1, package_version1); + p2 = nr_txn_add_php_package(txn, package_name1, package_version2); + tlib_pass_if_ptr_equal( + "same package name, different version, add returns same pointer", p1, p2); + nr_txn_destroy(&txn); } tlib_parallel_info_t parallel_info @@ -8725,4 +8851,6 @@ void test_main(void* p NRUNUSED) { test_record_log_event(); test_txn_log_configuration(); test_nr_txn_add_php_package(); + test_nr_txn_add_php_package_from_source(); + test_nr_txn_suggest_package_supportability_metric(); } diff --git a/daemon/internal/newrelic/integration/php_packages.go b/daemon/internal/newrelic/integration/php_packages.go index 375954b0d..fc9a10a59 100644 --- a/daemon/internal/newrelic/integration/php_packages.go +++ b/daemon/internal/newrelic/integration/php_packages.go @@ -31,12 +31,13 @@ type PhpPackagesCollection struct { // PHP packages config describes how to collect the JSON for the packages installed // for the current test case type PhpPackagesConfiguration struct { - path string - command string - supportedListFile string - overrideVersionsFile string - expectedPackages []string - packageNameOnly []string + path string // + command string // command to run to detect packages + supportedListFile string // JSON file containing list of packages we expect agent to detect + overrideVersionsFile string // JSON file containing overrides for expected package versions + expectedPackages []string // manual override of packages we expect to detect + packageNameOnly []string // list of packages which only have a name because agent cannot determine the version + expectAllDetected bool // flag to indicate we expect all packages detected by the command "command" } // composer package JSON @@ -159,16 +160,22 @@ func NewPhpPackagesCollection(path string, config []byte) (*PhpPackagesCollectio } } - if supportedOK && expectedOK { - return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - cannot specify 'supported_packages' and 'expected packages' - got %+v", params) + // or "expect_all" which means we expect the agent to detect all the packages that the "command" option would detect + _, expectAllOK := params["expect_all"] + + if (supportedOK && expectedOK) || (supportedOK && expectAllOK) || (expectedOK && expectAllOK) { + return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify one of 'supported_packages', "+ + "'expected packages' or 'expect_all' - got %+v", params) } - if !supportedOK && !expectedOK { - return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify 'supported_packages' or 'expected packages' - got %+v", params) + if !supportedOK && !expectedOK && !expectAllOK { + return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify 'supported_packages' or 'expected packages' "+ + "or 'expect_all' - got %+v", params) } - if supportedOK && !commandOK { - return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify 'command' option with `supported_packages` - got %+v", params) + if (supportedOK || expectAllOK) && !commandOK { + return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify 'command' option with `supported_packages` / "+ + "'expect_all' - got %+v", params) } // optional option to specify which packages will only have a name because agent cannot determine the version @@ -209,7 +216,8 @@ func NewPhpPackagesCollection(path string, config []byte) (*PhpPackagesCollectio supportedListFile: supportedListFile, overrideVersionsFile: overrideVersionsFile, expectedPackages: expectedPackagesArr, - packageNameOnly: packageNameOnlyArr}, + packageNameOnly: packageNameOnlyArr, + expectAllDetected: expectAllOK}, } return p, nil @@ -296,18 +304,27 @@ func (pkgs *PhpPackagesCollection) GatherInstalledPackages() ([]PhpPackage, erro var supported []string // get list of packages we expected the agent to detect - // this can be one of 2 scenarios: + // this can be one of 3 scenarios: // 1) test case used the "supported_packages" option which gives a JSON file which // lists all the packages the agent can detect // 2) test case used the "expected_packages" options which provides a comma separated // list of packages we expect the agent to detect + // 3) test case used the "expect_all" option which means we expect the agent to + // detect all the packages that the "command" option would detect + // + // Options #1 and #2 are mutually exclusive, and are intended for testing the legacy VM detection + // mechanism where the agent looks for "magic" files of a package and examinew internals of the + // package to determine its version. // - // Option #1 is preferable as it provides the most comprehensive view of what the agent can do. + // Option #1 is preferable when it available as it provides the most comprehensive view of what the agent can do. // // Option #2 is needed because some test cases do not exercise all the packages which are // installed and so the agent will not detect everything for that test case run which it could // theorectically detect if the test case used all the available packages installed. // + // Option #3 is used when testing the agent's ability to detect packages using the Composer API. In + // this case we expect the agent to detect the exact same packages as composer would detect. + // // Once the list of packages the agent is expected to detect is created it is used to filter // down the package list returned by running the "command" (usually composer) option for the // test case provided. @@ -318,8 +335,9 @@ func (pkgs *PhpPackagesCollection) GatherInstalledPackages() ([]PhpPackage, erro } } else if 0 < len(pkgs.config.expectedPackages) { supported = pkgs.config.expectedPackages - } else { - return nil, fmt.Errorf("Error determining expected packages - supported_packages and expected_packages are both empty") + } else if !pkgs.config.expectAllDetected { + return nil, fmt.Errorf("Error determining expected packages - supported_packages and expected_packages are both empty " + + "and expect_all is false") } splitCmd := strings.Split(pkgs.config.command, " ") @@ -339,7 +357,7 @@ func (pkgs *PhpPackagesCollection) GatherInstalledPackages() ([]PhpPackage, erro json.Unmarshal([]byte(out), &detected) for _, v := range detected.Installed { //fmt.Printf("composer detected %s %s\n", v.Name, v.Version) - if StringSliceContains(supported, v.Name) { + if pkgs.config.expectAllDetected || StringSliceContains(supported, v.Name) { var version string // remove any 'v' from front of version string @@ -371,6 +389,18 @@ func (pkgs *PhpPackagesCollection) GatherInstalledPackages() ([]PhpPackage, erro if 0 < len(version) { pkgs.packages = append(pkgs.packages, PhpPackage{"wordpress", version}) } + } else if 1 < len(splitCmd) && "composer-show.php" == splitCmd[1] { + lines := strings.Split(string(out), "\n") + version := "" + for _, line := range lines { + //fmt.Printf("line is |%s|\n", line) + splitLine := strings.Split(line, "=>") + if 2 == len(splitLine) { + name := strings.TrimSpace(splitLine[0]) + version = strings.TrimSpace(splitLine[1]) + pkgs.packages = append(pkgs.packages, PhpPackage{name, version}) + } + } } else { return nil, fmt.Errorf("ERROR - unknown method '%s'\n", splitCmd[0]) } diff --git a/daemon/internal/newrelic/integration/test.go b/daemon/internal/newrelic/integration/test.go index 631be4615..3e70e769a 100644 --- a/daemon/internal/newrelic/integration/test.go +++ b/daemon/internal/newrelic/integration/test.go @@ -637,6 +637,7 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) { var expectedPkgsCollection *PhpPackagesCollection var expectNullPkgs bool var version_overrides map[string]interface{} + var subsetMatch bool = false if nil != t.phpPackagesConfig { var err error @@ -656,6 +657,19 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) { t.Fatal(err) return } + + // Determine if we expect an exact match between expected and actual packages + // when using the composer API in the agent to detect packages it is possible + // it will return packages which the "composer show" command does not. These + // are usually virtual or other types of packages which "composer show" + // decides to not show. + // + // Currently the test handles this by listing all the packages that the agent + // detected but "composer show" did not as "Notes" on the test results. + // + // Tests for the legacy package detection mechanism still expect an exact match. + // + subsetMatch = expectedPkgsCollection.config.expectAllDetected } } else { // no configuration given for package (no EXPECT_PHP_PACKAGES in test case) so don't run test @@ -690,22 +704,44 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) { if nil == expectedPackages { t.Fail(fmt.Errorf("No expected PHP packages, harvest contains %+v\n", actualPackages)) } - // compare expected and actual lists of packages + // Compare expected and actual lists of packages // since package names should be identical, iterate over // expected list and compare element by element with same // position in actual list. Name and version should match. // this works because the functions which generate these // lists sort them by package name for us - if len(expectedPackages) != len(actualPackages) { + // + // As explained above - if testing results from the Composer API + // agent detection mechanism, we may not get an exact match so + // we relax the test that the lengths of the two lists are the same + if !subsetMatch && (len(expectedPackages) != len(actualPackages)) { t.Fail(fmt.Errorf("Expected and actual php packages differ in length %d vs %d: expected %+v actual %+v", len(expectedPackages), len(actualPackages), expectedPackages, actualPackages)) return } for i, _ := range expectedPackages { - if expectedPackages[i].Name == actualPackages[i].Name { + var matchingIdx int = -1 + for j, pkg := range actualPackages { + //fmt.Printf("Comparing %s to %s\n", pkg.Name, expectedPackages[i].Name) + if pkg.Name == expectedPackages[i].Name { + //fmt.Printf("Match - index = %d\n", j) + matchingIdx = j + break + } + } + + //fmt.Printf("MatchingIdx: %d\n", matchingIdx) + //fmt.Printf("expectedPatckages[%d]: %+v\n", i, expectedPackages[i]) + // if -1 != matchingIdx { + // fmt.Printf("actualPackages[%d]: %+v\n", matchingIdx, actualPackages[matchingIdx]) + // } else { + // fmt.Printf("no match in actualPackages!\n") + // } + + if -1 != matchingIdx { testPackageNameOnly := false if nil != expectedPkgsCollection.config.packageNameOnly { - testPackageNameOnly = StringSliceContains(expectedPkgsCollection.config.packageNameOnly, actualPackages[i].Name) + testPackageNameOnly = StringSliceContains(expectedPkgsCollection.config.packageNameOnly, actualPackages[matchingIdx].Name) if testPackageNameOnly { t.AddNote(fmt.Sprintf("Tested package name only for packages: %+v", expectedPkgsCollection.config.packageNameOnly)) } @@ -720,23 +756,41 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) { } if testPackageNameOnly { - if " " != actualPackages[i].Version { + if " " != actualPackages[matchingIdx].Version { t.Fail(fmt.Errorf("Expected no package version and a package version was detected - expected \" \" actual %+v. ", - actualPackages[i].Version)) + actualPackages[matchingIdx].Version)) return } else { continue } } - if expected_version == actualPackages[i].Version { + if expected_version == actualPackages[matchingIdx].Version { continue + } else { + t.Fail(fmt.Errorf("Expected version %s does not match actual version %s for package %s", + expected_version, actualPackages[matchingIdx].Version, expectedPackages[i].Name)) + return } } - t.Fail(fmt.Errorf("Expected and actual Php packages do not match: expected %+v actual %+v. Complete expected %v actual %v.", - expectedPackages[i], actualPackages[i], expectedPackages, actualPackages)) + t.Fail(fmt.Errorf("Expected Php packages do not match any actual packages: expected %+v\nComplete:\nexpected %v\nactual %v.\n", + expectedPackages[i], expectedPackages, actualPackages)) return } + + // create notes for all packages in the actual list not in the expected list + for ii, _ := range actualPackages { + var found bool = false + for _, pkg := range expectedPackages { + if pkg.Name == actualPackages[ii].Name { + found = true + break + } + } + if !found { + t.AddNote(fmt.Sprintf("Detected package not in expected: %+v", actualPackages[ii])) + } + } } else { if nil != expectedPackages { t.Fail(fmt.Errorf("Expected PHP packages %+v, harvest contains none\n", expectedPackages)) diff --git a/tests/integration/autoloader/autoload-with-broken-composer-00/vendor/autoload.php b/tests/integration/autoloader/autoload-with-broken-composer-00/vendor/autoload.php new file mode 100644 index 000000000..01d206c0f --- /dev/null +++ b/tests/integration/autoloader/autoload-with-broken-composer-00/vendor/autoload.php @@ -0,0 +1,12 @@ + $info) { + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/autoload-with-broken-composer-00/vendor/composer/autoload_real.php b/tests/integration/autoloader/autoload-with-broken-composer-00/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/autoload-with-broken-composer-00/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'type' => 'project' + ), + // Mocked data: installed packages and their versions + 'versions' => array( + 'vendor1/package1' => array( + 'pretty_version' => 'v1.1.3', + 'version' => '1.1.3.0', + 'type' => 'library' + ), + 'vendor2/package2' => array( + 'pretty_version' => '2.1.5', + 'version' => '2.1.5.0', + 'type' => 'library' + ) + ) + ); diff --git a/tests/integration/autoloader/autoload-with-broken-composer-01/vendor/autoload.php b/tests/integration/autoloader/autoload-with-broken-composer-01/vendor/autoload.php new file mode 100644 index 000000000..01d206c0f --- /dev/null +++ b/tests/integration/autoloader/autoload-with-broken-composer-01/vendor/autoload.php @@ -0,0 +1,12 @@ + $info) { + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/autoload-with-broken-composer-01/vendor/composer/autoload_real.php b/tests/integration/autoloader/autoload-with-broken-composer-01/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/autoload-with-broken-composer-01/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'type' => 'project' + ), + // Mocked data: installed packages and their versions + 'versions' => array( + 'vendor1/package1' => array( + 'pretty_version' => 'v1.1.3', + 'version' => '1.1.3.0', + 'type' => 'library' + ), + 'vendor2/package2' => array( + 'pretty_version' => '2.1.5', + 'version' => '2.1.5.0', + 'type' => 'library' + ) + ) + ); diff --git a/tests/integration/autoloader/autoload-with-broken-composer-02/vendor/autoload.php b/tests/integration/autoloader/autoload-with-broken-composer-02/vendor/autoload.php new file mode 100644 index 000000000..01d206c0f --- /dev/null +++ b/tests/integration/autoloader/autoload-with-broken-composer-02/vendor/autoload.php @@ -0,0 +1,12 @@ + $info) { + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/autoload-with-broken-composer-02/vendor/composer/autoload_real.php b/tests/integration/autoloader/autoload-with-broken-composer-02/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/autoload-with-broken-composer-02/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + $info) { + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/autoload-with-composer-throwing-error/vendor/composer/autoload_real.php b/tests/integration/autoloader/autoload-with-composer-throwing-error/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/autoload-with-composer-throwing-error/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'type' => 'project' + ), + // Mocked data: installed packages and their versions + 'versions' => array( + 'vendor1/package1' => array( + 'pretty_version' => 'v1.1.3', + 'version' => '1.1.3.0', + 'type' => 'library' + ), + 'vendor2/package2' => array( + 'pretty_version' => '2.1.5', + 'version' => '2.1.5.0', + 'type' => 'library' + ) + ) + ); diff --git a/tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/autoload.php b/tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/autoload.php new file mode 100644 index 000000000..01d206c0f --- /dev/null +++ b/tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/autoload.php @@ -0,0 +1,12 @@ + $info) { + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/composer/autoload_real.php b/tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/autoload-with-composer-throwing-exception/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'type' => 'project' + ), + // Mocked data: installed packages and their versions + 'versions' => array( + 'vendor1/package1' => array( + 'pretty_version' => 'v1.1.3', + 'version' => '1.1.3.0', + 'type' => 'library' + ), + 'vendor2/package2' => array( + 'pretty_version' => '2.1.5', + 'version' => '2.1.5.0', + 'type' => 'library' + ) + ) + ); diff --git a/tests/integration/autoloader/autoload-with-composer/vendor/autoload.php b/tests/integration/autoloader/autoload-with-composer/vendor/autoload.php new file mode 100644 index 000000000..01d206c0f --- /dev/null +++ b/tests/integration/autoloader/autoload-with-composer/vendor/autoload.php @@ -0,0 +1,12 @@ + $info) { + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/autoload-with-composer/vendor/composer/autoload_real.php b/tests/integration/autoloader/autoload-with-composer/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/autoload-with-composer/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'type' => 'project' + ), + // Mocked data: installed packages and their versions + 'versions' => array( + 'vendor1/package1' => array( + 'pretty_version' => 'v1.1.3', + 'version' => '1.1.3.0', + 'type' => 'library' + ), + 'vendor2/package2' => array( + 'pretty_version' => '2.1.5', + 'version' => '2.1.5.0', + 'type' => 'library' + ) + ) + ); diff --git a/tests/integration/autoloader/autoload-without-composer/vendor/autoload.php b/tests/integration/autoloader/autoload-without-composer/vendor/autoload.php new file mode 100644 index 000000000..0a6947fef --- /dev/null +++ b/tests/integration/autoloader/autoload-without-composer/vendor/autoload.php @@ -0,0 +1,12 @@ + 1) { + $installedVersions = $argv[1]; +} + +include $installedVersions; +if ($argc > 2) { + $installed = Composer\InstalledVersions::getAllRawData(); + $package = $argv[2]; + $version = ltrim($installed[0]['versions'][$package]['pretty_version'], 'v'); + echo "$package => $version\n"; +} else { + Composer\InstalledVersions::show(); +} diff --git a/tests/integration/autoloader/packages-with-broken-composer-00/vendor/autoload.php b/tests/integration/autoloader/packages-with-broken-composer-00/vendor/autoload.php new file mode 100644 index 000000000..01d206c0f --- /dev/null +++ b/tests/integration/autoloader/packages-with-broken-composer-00/vendor/autoload.php @@ -0,0 +1,12 @@ + $info) { + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/packages-with-broken-composer-00/vendor/composer/autoload_real.php b/tests/integration/autoloader/packages-with-broken-composer-00/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/packages-with-broken-composer-00/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'type' => 'project' + ), + // Mocked data: installed packages and their versions + 'versions' => array( + 'vendor1/package1' => array( + 'pretty_version' => 'v1.1.3', + 'version' => '1.1.3.0', + 'type' => 'library' + ), + 'vendor2/package2' => array( + 'pretty_version' => '2.1.5', + 'version' => '2.1.5.0', + 'type' => 'library' + ), + 'symfony/http-kernel' => array( + 'pretty_version' => '5.4.5', + 'version' => '5.4.5.0', + 'type' => 'library' + ), + ) + ); diff --git a/tests/integration/autoloader/packages-with-broken-composer-00/vendor/symfony/http-kernel/HttpKernel.php b/tests/integration/autoloader/packages-with-broken-composer-00/vendor/symfony/http-kernel/HttpKernel.php new file mode 100644 index 000000000..2cceb6ac7 --- /dev/null +++ b/tests/integration/autoloader/packages-with-broken-composer-00/vendor/symfony/http-kernel/HttpKernel.php @@ -0,0 +1,12 @@ + $info) { + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/packages-with-broken-composer-01/vendor/composer/autoload_real.php b/tests/integration/autoloader/packages-with-broken-composer-01/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/packages-with-broken-composer-01/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'type' => 'project' + ), + // Mocked data: installed packages and their versions + 'versions' => array( + 'vendor1/package1' => array( + 'pretty_version' => 'v1.1.3', + 'version' => '1.1.3.0', + 'type' => 'library' + ), + 'vendor2/package2' => array( + 'pretty_version' => '2.1.5', + 'version' => '2.1.5.0', + 'type' => 'library' + ), + 'laravel/framework' => array( + 'pretty_version' => '11.4.5', + 'version' => '11.4.5', + 'type' => 'library' + ), + ) + ); diff --git a/tests/integration/autoloader/packages-with-broken-composer-01/vendor/laravel/framework/src/Illuminate/Foundation/Application.php b/tests/integration/autoloader/packages-with-broken-composer-01/vendor/laravel/framework/src/Illuminate/Foundation/Application.php new file mode 100644 index 000000000..3c6718e89 --- /dev/null +++ b/tests/integration/autoloader/packages-with-broken-composer-01/vendor/laravel/framework/src/Illuminate/Foundation/Application.php @@ -0,0 +1,25 @@ + $info) { + if (!is_string($package)) { + continue; + } + if (!is_array($info)) { + continue; + } + if (!array_key_exists('pretty_version', $info)) { + continue; + } + if (!is_string($info['pretty_version'])) { + continue; + } + $version = ltrim($info['pretty_version'], 'v'); + echo "$package => $version\n"; + } + } +} diff --git a/tests/integration/autoloader/packages-with-broken-composer-02/vendor/composer/autoload_real.php b/tests/integration/autoloader/packages-with-broken-composer-02/vendor/composer/autoload_real.php new file mode 100644 index 000000000..014c86e9a --- /dev/null +++ b/tests/integration/autoloader/packages-with-broken-composer-02/vendor/composer/autoload_real.php @@ -0,0 +1,9 @@ + array( + 'pretty_version' => 'v1.0.0', + 'version' => '1.0.0.0', + 'type' => 'project' + ), + // Mocked invalid package data: + // - package without name and version + // - package without name but with version + // - package with name but without version + // Mocked valid package data: + // - package with name and version + 'versions' => array( + array( + 'version' => '1.1.3.0', + 'type' => 'library' + ), + array( + 'pretty_version' => 'v2.1.3', + 'version' => '2.1.3.0', + 'type' => 'library' + ), + 'vendor2/package2' => array( + 'version' => '3.1.5.0', + 'type' => 'library' + ), + 'laravel/framework' => array( + 'pretty_version' => '11.4.5', + 'version' => '11.4.5', + 'type' => 'library' + ), + ) + ); diff --git a/tests/integration/autoloader/packages-with-broken-composer-02/vendor/laravel/framework/src/Illuminate/Foundation/Application.php b/tests/integration/autoloader/packages-with-broken-composer-02/vendor/laravel/framework/src/Illuminate/Foundation/Application.php new file mode 100644 index 000000000..3c6718e89 --- /dev/null +++ b/tests/integration/autoloader/packages-with-broken-composer-02/vendor/laravel/framework/src/Illuminate/Foundation/Application.php @@ -0,0 +1,25 @@ +emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_cat.php b/tests/integration/logging/monolog2/test_monolog_cat.php index d96e38aec..b192b9ce9 100644 --- a/tests/integration/logging/monolog2/test_monolog_cat.php +++ b/tests/integration/logging/monolog2/test_monolog_cat.php @@ -56,7 +56,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -183,4 +183,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_context_simple.php b/tests/integration/logging/monolog2/test_monolog_context_simple.php index 769694845..a0f0b7260 100644 --- a/tests/integration/logging/monolog2/test_monolog_context_simple.php +++ b/tests/integration/logging/monolog2/test_monolog_context_simple.php @@ -59,7 +59,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/logging/monolog2/test_monolog_decoration_and_forwarding.php b/tests/integration/logging/monolog2/test_monolog_decoration_and_forwarding.php index 1737a919c..82ad4be61 100644 --- a/tests/integration/logging/monolog2/test_monolog_decoration_and_forwarding.php +++ b/tests/integration/logging/monolog2/test_monolog_decoration_and_forwarding.php @@ -113,7 +113,7 @@ [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/api/get_linking_metadata"}, [16, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -257,4 +257,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_disable_metrics.php b/tests/integration/logging/monolog2/test_monolog_disable_metrics.php index 95b84d636..eaed5d5d4 100644 --- a/tests/integration/logging/monolog2/test_monolog_disable_metrics.php +++ b/tests/integration/logging/monolog2/test_monolog_disable_metrics.php @@ -51,7 +51,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -153,4 +153,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_drop_empty.php b/tests/integration/logging/monolog2/test_monolog_drop_empty.php index 8139e3606..5d48030ca 100644 --- a/tests/integration/logging/monolog2/test_monolog_drop_empty.php +++ b/tests/integration/logging/monolog2/test_monolog_drop_empty.php @@ -57,7 +57,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -171,4 +171,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_large_message_limit.php b/tests/integration/logging/monolog2/test_monolog_large_message_limit.php index d7c9b677a..7a0e1eacd 100644 --- a/tests/integration/logging/monolog2/test_monolog_large_message_limit.php +++ b/tests/integration/logging/monolog2/test_monolog_large_message_limit.php @@ -40,7 +40,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -78,4 +78,4 @@ function test_logging() { } } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_large_message_limit_drops.php b/tests/integration/logging/monolog2/test_monolog_large_message_limit_drops.php index 0dfe1dc2d..313e73edd 100644 --- a/tests/integration/logging/monolog2/test_monolog_large_message_limit_drops.php +++ b/tests/integration/logging/monolog2/test_monolog_large_message_limit_drops.php @@ -40,7 +40,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1666, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -78,4 +78,4 @@ function test_logging() { } } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_limit_log_events.php b/tests/integration/logging/monolog2/test_monolog_limit_log_events.php index c9106b2ea..ccc75de41 100644 --- a/tests/integration/logging/monolog2/test_monolog_limit_log_events.php +++ b/tests/integration/logging/monolog2/test_monolog_limit_log_events.php @@ -58,7 +58,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -161,4 +161,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_limit_zero_events.php b/tests/integration/logging/monolog2/test_monolog_limit_zero_events.php index 6ff0fa7d1..573850d3a 100644 --- a/tests/integration/logging/monolog2/test_monolog_limit_zero_events.php +++ b/tests/integration/logging/monolog2/test_monolog_limit_zero_events.php @@ -58,7 +58,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -101,4 +101,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid1.php b/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid1.php index f760b19ac..c346aa55e 100644 --- a/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid1.php +++ b/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid1.php @@ -45,7 +45,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -81,4 +81,4 @@ function test_logging() { } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid2.php b/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid2.php index ada730ed7..ab55e0359 100644 --- a/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid2.php +++ b/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid2.php @@ -45,7 +45,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -81,4 +81,4 @@ function test_logging() { } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid3.php b/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid3.php index cdd99bc5f..616eb81f8 100644 --- a/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid3.php +++ b/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid3.php @@ -45,7 +45,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -81,4 +81,4 @@ function test_logging() { } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid4.php b/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid4.php index 354108a09..0f13144eb 100644 --- a/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid4.php +++ b/tests/integration/logging/monolog2/test_monolog_log_events_max_samples_stored_invalid4.php @@ -45,7 +45,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -81,4 +81,4 @@ function test_logging() { } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_log_level_filter.php b/tests/integration/logging/monolog2/test_monolog_log_level_filter.php index 939dd9cf5..fa7902395 100644 --- a/tests/integration/logging/monolog2/test_monolog_log_level_filter.php +++ b/tests/integration/logging/monolog2/test_monolog_log_level_filter.php @@ -60,7 +60,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -171,4 +171,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_log_level_filter_invalid.php b/tests/integration/logging/monolog2/test_monolog_log_level_filter_invalid.php index d2eac9410..22107fd98 100644 --- a/tests/integration/logging/monolog2/test_monolog_log_level_filter_invalid.php +++ b/tests/integration/logging/monolog2/test_monolog_log_level_filter_invalid.php @@ -61,7 +61,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/2/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -172,4 +172,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_basic.php b/tests/integration/logging/monolog3/test_monolog_basic.php index 5cbc3a1da..0e494b078 100644 --- a/tests/integration/logging/monolog3/test_monolog_basic.php +++ b/tests/integration/logging/monolog3/test_monolog_basic.php @@ -56,7 +56,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -200,4 +200,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_cat.php b/tests/integration/logging/monolog3/test_monolog_cat.php index dcf557213..12b0c76ae 100644 --- a/tests/integration/logging/monolog3/test_monolog_cat.php +++ b/tests/integration/logging/monolog3/test_monolog_cat.php @@ -56,7 +56,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -184,4 +184,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_context_simple.php b/tests/integration/logging/monolog3/test_monolog_context_simple.php index 1e77e6d25..ddcfd65f9 100644 --- a/tests/integration/logging/monolog3/test_monolog_context_simple.php +++ b/tests/integration/logging/monolog3/test_monolog_context_simple.php @@ -59,7 +59,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/logging/monolog3/test_monolog_decoration_and_forwarding.php b/tests/integration/logging/monolog3/test_monolog_decoration_and_forwarding.php index fa0bbb6cb..3d8edcc7c 100644 --- a/tests/integration/logging/monolog3/test_monolog_decoration_and_forwarding.php +++ b/tests/integration/logging/monolog3/test_monolog_decoration_and_forwarding.php @@ -113,7 +113,7 @@ [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/api/get_linking_metadata"}, [16, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -257,4 +257,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_disable_metrics.php b/tests/integration/logging/monolog3/test_monolog_disable_metrics.php index cb5beb8e1..11bad2177 100644 --- a/tests/integration/logging/monolog3/test_monolog_disable_metrics.php +++ b/tests/integration/logging/monolog3/test_monolog_disable_metrics.php @@ -51,7 +51,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -153,4 +153,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_drop_empty.php b/tests/integration/logging/monolog3/test_monolog_drop_empty.php index 61937fb9f..6a470653b 100644 --- a/tests/integration/logging/monolog3/test_monolog_drop_empty.php +++ b/tests/integration/logging/monolog3/test_monolog_drop_empty.php @@ -57,7 +57,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -171,4 +171,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_large_message_limit.php b/tests/integration/logging/monolog3/test_monolog_large_message_limit.php index 191800f49..50b2a26a5 100644 --- a/tests/integration/logging/monolog3/test_monolog_large_message_limit.php +++ b/tests/integration/logging/monolog3/test_monolog_large_message_limit.php @@ -40,7 +40,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -78,4 +78,4 @@ function test_logging() { } } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_large_message_limit_drops.php b/tests/integration/logging/monolog3/test_monolog_large_message_limit_drops.php index eed9a410a..c2913419d 100644 --- a/tests/integration/logging/monolog3/test_monolog_large_message_limit_drops.php +++ b/tests/integration/logging/monolog3/test_monolog_large_message_limit_drops.php @@ -40,7 +40,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1666, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -78,4 +78,4 @@ function test_logging() { } } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_limit_log_events.php b/tests/integration/logging/monolog3/test_monolog_limit_log_events.php index b1f72e6b6..b5ffaf258 100644 --- a/tests/integration/logging/monolog3/test_monolog_limit_log_events.php +++ b/tests/integration/logging/monolog3/test_monolog_limit_log_events.php @@ -58,7 +58,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -161,4 +161,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_limit_zero_events.php b/tests/integration/logging/monolog3/test_monolog_limit_zero_events.php index 38abfe890..4b65d64f3 100644 --- a/tests/integration/logging/monolog3/test_monolog_limit_zero_events.php +++ b/tests/integration/logging/monolog3/test_monolog_limit_zero_events.php @@ -58,7 +58,7 @@ [{"name": "OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name": "OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], @@ -101,4 +101,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid1.php b/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid1.php index f33d832af..f94ea94af 100644 --- a/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid1.php +++ b/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid1.php @@ -45,7 +45,7 @@ [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -81,4 +81,4 @@ function test_logging() { } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid2.php b/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid2.php index 73c70e58e..5d0363243 100644 --- a/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid2.php +++ b/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid2.php @@ -45,7 +45,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -81,4 +81,4 @@ function test_logging() { } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid3.php b/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid3.php index 72ca4b06b..8b451bdea 100644 --- a/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid3.php +++ b/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid3.php @@ -45,7 +45,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -81,4 +81,4 @@ function test_logging() { } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid4.php b/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid4.php index 2f60d6bc1..fe4185197 100644 --- a/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid4.php +++ b/tests/integration/logging/monolog3/test_monolog_log_events_max_samples_stored_invalid4.php @@ -45,7 +45,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [833, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -81,4 +81,4 @@ function test_logging() { } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_log_level_filter.php b/tests/integration/logging/monolog3/test_monolog_log_level_filter.php index 529ef1459..69d482e9b 100644 --- a/tests/integration/logging/monolog3/test_monolog_log_level_filter.php +++ b/tests/integration/logging/monolog3/test_monolog_log_level_filter.php @@ -60,7 +60,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -171,4 +171,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_log_level_filter_invalid.php b/tests/integration/logging/monolog3/test_monolog_log_level_filter_invalid.php index e5387616f..649cd5acf 100644 --- a/tests/integration/logging/monolog3/test_monolog_log_level_filter_invalid.php +++ b/tests/integration/logging/monolog3/test_monolog_log_level_filter_invalid.php @@ -61,7 +61,7 @@ [{"name": "Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/Logging/PHP/Monolog/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [8, "??", "??", "??", "??", "??"]], + [{"name": "Supportability/PHP/package/monolog/monolog/3/detected"}, [1, "??", "??", "??", "??", "??"]], [{"name": "Supportability/library/Monolog/detected"}, [1, "??", "??", "??", "??", "??"]] ] ] @@ -172,4 +172,4 @@ function test_logging() { $logger->emergency("emergency"); } -test_logging(); \ No newline at end of file +test_logging(); From 71674c9d96f4027b4a49212343c791eecf57f338 Mon Sep 17 00:00:00 2001 From: bduranleau-nr <106178551+bduranleau-nr@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:55:52 -0500 Subject: [PATCH 12/45] chore: Bump version to 11.3.0 (#967) --- VERSION | 2 +- axiom/nr_version.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index b85c6c7b0..f628d2eaf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.2.0 +11.3.0 diff --git a/axiom/nr_version.c b/axiom/nr_version.c index e608278e9..2c950be4f 100644 --- a/axiom/nr_version.c +++ b/axiom/nr_version.c @@ -21,9 +21,8 @@ #endif /* - * Current version naming scheme is flowers + * Current version naming scheme is gemstones * - * dahlia 19Sep2022 (10.1) * echinacea 03Oct2022 (10.2) * freesia 03Nov2022 (10.3) * goldenrod 12Dec2022 (10.4) @@ -47,8 +46,9 @@ * yarrow 26Jun2024 (10.22) * zinnia 30Jul2024 (11.0) * amethyst 26Aug2024 (11.1) + * bowenite 30Sep2024 (11.2) */ -#define NR_CODENAME "bowenite" +#define NR_CODENAME "corundum" const char* nr_version(void) { return NR_STR2(NR_VERSION); From aa15a2d49ca7cbb3a7c8856102fa10dbca99f938 Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Fri, 4 Oct 2024 09:02:44 -0600 Subject: [PATCH 13/45] feat(agent): Memcached instance metrics with host name (#958) These metrics can be used by the backend to create AWS relationship maps with memcached --------- Co-authored-by: Michal Nowacki --- agent/Makefile.frag | 1 + agent/config.m4 | 2 +- agent/php_internal_instrument.c | 59 ++++++++ agent/php_memcached.c | 34 +++++ agent/php_memcached.h | 35 +++++ agent/tests/test_memcached.c | 127 ++++++++++++++++++ .../memcached/test_add_servers.php | 39 ++++++ .../memcached/test_add_servers_bad.php | 68 ++++++++++ tests/integration/memcached/test_basic.php | 1 + .../memcached/test_basic_logging_off.php | 1 + tests/integration/memcached/test_by_key.php | 1 + tests/integration/memcached/test_cas.php7.php | 1 + .../memcached/test_cas_by_key.php7.php | 1 + tests/integration/memcached/test_concat.php | 1 + .../memcached/test_concat_by_key.php | 1 + .../test_concat_by_key_logging_off.php | 1 + tests/integration/memcached/test_multi.php | 1 + .../memcached/test_multi_by_key.php | 1 + tests/integration/memcached/test_socket.php | 30 +++++ 19 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 agent/php_memcached.c create mode 100644 agent/php_memcached.h create mode 100644 agent/tests/test_memcached.c create mode 100644 tests/integration/memcached/test_add_servers.php create mode 100644 tests/integration/memcached/test_add_servers_bad.php create mode 100644 tests/integration/memcached/test_socket.php diff --git a/agent/Makefile.frag b/agent/Makefile.frag index 648fc84b3..fbff46d69 100644 --- a/agent/Makefile.frag +++ b/agent/Makefile.frag @@ -93,6 +93,7 @@ TEST_BINARIES = \ tests/test_internal_instrument \ tests/test_hash \ tests/test_lib_aws_sdk_php \ + tests/test_memcached \ tests/test_mongodb \ tests/test_monolog \ tests/test_mysql \ diff --git a/agent/config.m4 b/agent/config.m4 index ee0859e30..5806caac5 100644 --- a/agent/config.m4 +++ b/agent/config.m4 @@ -215,7 +215,7 @@ if test "$PHP_NEWRELIC" = "yes"; then php_error.c php_execute.c php_explain.c php_explain_mysqli.c \ php_explain_pdo_mysql.c php_extension.c php_file_get_contents.c \ php_globals.c php_hash.c php_header.c php_httprequest_send.c \ - php_internal_instrument.c php_minit.c php_mshutdown.c php_mysql.c \ + php_internal_instrument.c php_memcached.c php_minit.c php_mshutdown.c php_mysql.c \ php_mysqli.c php_newrelic.c php_nrini.c php_observer.c php_output.c php_pdo.c \ php_pdo_mysql.c php_pdo_pgsql.c php_pgsql.c php_psr7.c php_redis.c \ php_rinit.c php_rshutdown.c php_samplers.c php_stack.c \ diff --git a/agent/php_internal_instrument.c b/agent/php_internal_instrument.c index 821379edb..ce6c089ec 100644 --- a/agent/php_internal_instrument.c +++ b/agent/php_internal_instrument.c @@ -13,8 +13,10 @@ #include "php_explain_mysqli.h" #include "php_file_get_contents.h" #include "php_globals.h" +#include "php_hash.h" #include "php_httprequest_send.h" #include "php_internal_instrument.h" +#include "php_memcached.h" #include "php_mysql.h" #include "php_mysqli.h" #include "php_pdo.h" @@ -1531,6 +1533,57 @@ NR_INNER_WRAPPER(memcache_function) { INTERNAL_FUNCTION_PARAM_PASSTHRU); } +NR_INNER_WRAPPER(memcached_add_server) { + char* host = NULL; + nr_string_len_t host_len = 0; + zend_long port = 0; + zend_long weight = 0; + int zcaught = 0; + + if (SUCCESS + == zend_parse_parameters_ex( + ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s|ll", &host, + &host_len, &port, &weight) && + NULL != host) { + nr_php_memcached_create_instance_metric(host, port); + } + zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler, + INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (zcaught) { + zend_bailout(); + /* NOTREACHED */ + } +} + +NR_INNER_WRAPPER(memcached_add_servers) { + zval* servers = NULL; + zval* server = NULL; + int zcaught = 0; + + if (SUCCESS + == zend_parse_parameters_ex( + ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "a", &servers)) { + if (NULL != servers && Z_TYPE_P(servers) == IS_ARRAY) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(servers), server) { + zval* host = nr_php_zend_hash_index_find(Z_ARRVAL_P(server), 0); + zval* port = nr_php_zend_hash_index_find(Z_ARRVAL_P(server), 1); + if (nr_php_is_zval_valid_string(host) && + nr_php_is_zval_valid_integer(port)) { + nr_php_memcached_create_instance_metric(Z_STRVAL_P(host), Z_LVAL_P(port)); + } + } + ZEND_HASH_FOREACH_END(); + } + } + zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler, + INTERNAL_FUNCTION_PARAM_PASSTHRU); + + if (zcaught) { + zend_bailout(); + /* NOTREACHED */ + } +} + /* * Handle * bool redis::connect ( string $host[, int $port = 6379 ... ] ) @@ -3098,6 +3151,8 @@ NR_OUTER_WRAPPER(memcached_set) NR_OUTER_WRAPPER(memcached_setbykey) NR_OUTER_WRAPPER(memcached_setmulti) NR_OUTER_WRAPPER(memcached_setmultibykey) +NR_OUTER_WRAPPER(memcached_addserver) +NR_OUTER_WRAPPER(memcached_addservers) NR_OUTER_WRAPPER(redis_append) NR_OUTER_WRAPPER(redis_bitcount) @@ -3511,6 +3566,10 @@ void nr_php_generate_internal_wrap_records(void) { memcache_function, 0, "set") NR_INTERNAL_WRAPREC("memcached::setmultibykey", memcached_setmultibykey, memcache_function, 0, "set") + NR_INTERNAL_WRAPREC("memcached::addserver", memcached_addserver, + memcached_add_server, 0, 0); + NR_INTERNAL_WRAPREC("memcached::addservers", memcached_addservers, + memcached_add_servers, 0, 0); NR_INTERNAL_WRAPREC("redis::connect", redis_connect, redis_connect, 0, "connect") diff --git a/agent/php_memcached.c b/agent/php_memcached.c new file mode 100644 index 000000000..ee13e1e73 --- /dev/null +++ b/agent/php_memcached.c @@ -0,0 +1,34 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "php_memcached.h" +#include "nr_datastore_instance.h" +#include "php_agent.h" + +nr_datastore_instance_t* nr_php_memcached_create_datastore_instance( + const char* host_or_socket, + zend_long port) { + nr_datastore_instance_t* instance = NULL; + if (port == 0) { // local socket + instance = nr_datastore_instance_create("localhost", host_or_socket, NULL); + } else { + char* port_str = nr_formatf("%ld", (long)port); + instance = nr_datastore_instance_create(host_or_socket, port_str, NULL); + nr_free(port_str); + } + return instance; +} + +void nr_php_memcached_create_instance_metric( + const char* host_or_socket, + zend_long port) { + nr_datastore_instance_t* instance + = nr_php_memcached_create_datastore_instance(host_or_socket, port); + char* instance_metric = nr_formatf("Datastore/instance/Memcached/%s/%s", + instance->host, instance->port_path_or_id); + nrm_force_add(NRPRG(txn)->unscoped_metrics, instance_metric, 0); + nr_datastore_instance_destroy(&instance); + nr_free(instance_metric); +} diff --git a/agent/php_memcached.h b/agent/php_memcached.h new file mode 100644 index 000000000..f449030ea --- /dev/null +++ b/agent/php_memcached.h @@ -0,0 +1,35 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef PHP_MEMCACHED_HDR +#define PHP_MEMCACHED_HDR + +#include "nr_datastore_instance.h" +#include "php_includes.h" + +/* + * Purpose : Create a datastore instance metadata for a Memcached server. + * + * Params : 1. The memcached host or socket name as given to Memcached::addServer(). + * 2. The memcached port as given as given to Memcached::addServer(). + * + * Returns: nr_datastore_instance_t* that the caller is responsible for freeing + */ +nr_datastore_instance_t* nr_php_memcached_create_datastore_instance( + const char* host_or_socket, + zend_long port); + +/* + * Purpose : Create a memcached instance metric + * + * Params : 1. The memcached host or socket name as given to Memcached::addServer(). + * 2. The memcached port as given as given to Memcached::addServer(). + */ +extern void nr_php_memcached_create_instance_metric( + const char* host_or_socket, + zend_long port); + + +#endif diff --git a/agent/tests/test_memcached.c b/agent/tests/test_memcached.c new file mode 100644 index 000000000..1f05c2443 --- /dev/null +++ b/agent/tests/test_memcached.c @@ -0,0 +1,127 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +#include "tlib_php.h" +#include "tlib_datastore.h" + +#include "php_agent.h" +#include "php_memcached.h" +#include "util_system.h" + +tlib_parallel_info_t parallel_info + = {.suggested_nthreads = -1, .state_size = 0}; + +static char* system_host_name; + +static void test_create_datastore_instance(void) { + assert_datastore_instance_equals_destroy( + "named socket", + &((nr_datastore_instance_t){ + .host = system_host_name, + .database_name = "unknown", + .port_path_or_id = "/tmp/memcached.sock", + }), + nr_php_memcached_create_datastore_instance("/tmp/memcached.sock", 0)); + + assert_datastore_instance_equals_destroy( + "empty socket", + &((nr_datastore_instance_t){ + .host = system_host_name, + .database_name = "unknown", + .port_path_or_id = "unknown", + }), + nr_php_memcached_create_datastore_instance("", 0)); + + assert_datastore_instance_equals_destroy( + "empty host", + &((nr_datastore_instance_t){ + .host = system_host_name, + .database_name = "unknown", + .port_path_or_id = "unknown", + }), + nr_php_memcached_create_datastore_instance(NULL, 0)); + + assert_datastore_instance_equals_destroy( + "host.name socket", + &((nr_datastore_instance_t){ + .host = "host.name", + .database_name = "unknown", + .port_path_or_id = "11211", + }), + nr_php_memcached_create_datastore_instance("host.name", 11211)); + + assert_datastore_instance_equals_destroy( + "host and port", + &((nr_datastore_instance_t){ + .host = "unknown", + .database_name = "unknown", + .port_path_or_id = "6379", + }), + nr_php_memcached_create_datastore_instance("", 6379)); + + assert_datastore_instance_equals_destroy( + "NULL socket", + &((nr_datastore_instance_t){ + .host = "unknown", + .database_name = "unknown", + .port_path_or_id = "11211", + }), + nr_php_memcached_create_datastore_instance(NULL, 11211)); +} + +static void test_create_instance_metric(void) { + nrtxn_t* txn; + nrmetric_t* metric; + char* metric_str; + tlib_php_engine_create(""); + tlib_php_request_start(); + txn = NRPRG(txn); + + nr_php_memcached_create_instance_metric("host", 11211); + metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/host/11211"); + tlib_pass_if_not_null("metric found", metric); + + nr_php_memcached_create_instance_metric("", 11211); + metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/unknown/11211"); + tlib_pass_if_not_null("metric found", metric); + + nr_php_memcached_create_instance_metric(NULL, 7); + metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/unknown/7"); + tlib_pass_if_not_null("metric found", metric); + + nr_php_memcached_create_instance_metric("path/to/sock", 0); + metric_str = nr_formatf("Datastore/instance/Memcached/%s/path/to/sock", system_host_name); + metric = nrm_find(txn->unscoped_metrics, metric_str); + nr_free(metric_str); + tlib_pass_if_not_null("metric found", metric); + + nr_php_memcached_create_instance_metric("", 0); + metric_str = nr_formatf("Datastore/instance/Memcached/%s/unknown", system_host_name); + metric = nrm_find(txn->unscoped_metrics, metric_str); + nr_free(metric_str); + tlib_pass_if_not_null("metric found", metric); + + // restart the transaction because the next metric is the same as a previous metric + tlib_php_request_end(); + tlib_php_request_start(); + txn = NRPRG(txn); + + nr_php_memcached_create_instance_metric(NULL, 0); + metric_str = nr_formatf("Datastore/instance/Memcached/%s/unknown", system_host_name); + metric = nrm_find(txn->unscoped_metrics, metric_str); + nr_free(metric_str); + tlib_pass_if_not_null("metric found", metric); + + tlib_php_request_end(); + tlib_php_engine_destroy(); +} + +void test_main(void* p NRUNUSED) { + system_host_name = nr_system_get_hostname(); + + test_create_datastore_instance(); + test_create_instance_metric(); + + nr_free(system_host_name); +} diff --git a/tests/integration/memcached/test_add_servers.php b/tests/integration/memcached/test_add_servers.php new file mode 100644 index 000000000..41e9b5a63 --- /dev/null +++ b/tests/integration/memcached/test_add_servers.php @@ -0,0 +1,39 @@ + +*/ + +/*INI +*/ + +/*EXPECT_METRICS_EXIST +Datastore/instance/Memcached/host1/1, 1 +Datastore/instance/Memcached/host2/2, 1 +Datastore/instance/Memcached/host3/11211, 1 +Datastore/instance/Memcached/host4/1, 1 +*/ + +/*EXPECT_ERROR_EVENTS null */ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); +require_once(realpath (dirname ( __FILE__ )) . '/../../include/tap.php'); +require_once(realpath (dirname ( __FILE__ )) . '/memcache.inc'); + +$memcached = new Memcached(); +$memcached->addServers(array( + array("host1", 1), + array("host2", 2), + array("host3", 11211))); +$memcached->addServers(array()); +$memcached->addServers(array(array("host4", 1, "test field"))); +$memcached->quit(); diff --git a/tests/integration/memcached/test_add_servers_bad.php b/tests/integration/memcached/test_add_servers_bad.php new file mode 100644 index 000000000..c4887584f --- /dev/null +++ b/tests/integration/memcached/test_add_servers_bad.php @@ -0,0 +1,68 @@ + +*/ + +/*INI +*/ + +/*EXPECT_REGEX + +.*(PHP )?Warning:.*could not add entry.* + +.*(PHP )?Warning:.*could not add entry.* + +*/ + +/*EXPECT_ERROR_EVENTS +[ + "?? agent run id", + { + "reservoir_size": "??", + "events_seen": 1 + }, + [ + [ + { + "type": "TransactionError", + "timestamp": "??", + "error.class": "E_WARNING", + "error.message": "Memcached::addServers(): could not add entry #2 to the server list", + "transactionName": "OtherTransaction\/php__FILE__", + "duration": "??", + "nr.transactionGuid": "??", + "guid": "??", + "sampled": true, + "priority": "??", + "traceId": "??", + "spanId": "??" + }, + {}, + {} + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); +require_once(realpath (dirname ( __FILE__ )) . '/../../include/tap.php'); +require_once(realpath (dirname ( __FILE__ )) . '/memcache.inc'); + +$memcached = new Memcached(); +$memcached->addServer(5, 5); +//$memcached->addServer("host", string); crashes PHP +$memcached->addServers(array(array(1))); +$memcached->addServers(array(array("host1"))); +$memcached->addServers(array(array(1, "host1"))); +//$memcahed->addServers("string"); crashes PHP +$memcached->quit(); diff --git a/tests/integration/memcached/test_basic.php b/tests/integration/memcached/test_basic.php index 6ccde57b7..1e24bfd7e 100644 --- a/tests/integration/memcached/test_basic.php +++ b/tests/integration/memcached/test_basic.php @@ -46,6 +46,7 @@ [{"name":"Datastore/allOther"}, [15, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [15, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [15, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/add"}, [2, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/add", "scope":"OtherTransaction/php__FILE__"}, [2, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_basic_logging_off.php b/tests/integration/memcached/test_basic_logging_off.php index 521fc1d44..05e87a4c1 100644 --- a/tests/integration/memcached/test_basic_logging_off.php +++ b/tests/integration/memcached/test_basic_logging_off.php @@ -49,6 +49,7 @@ [{"name":"Datastore/allOther"}, [15, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [15, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [15, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/add"}, [2, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/add", "scope":"OtherTransaction/php__FILE__"}, [2, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_by_key.php b/tests/integration/memcached/test_by_key.php index 006d6cd7d..ceb2758d8 100644 --- a/tests/integration/memcached/test_by_key.php +++ b/tests/integration/memcached/test_by_key.php @@ -48,6 +48,7 @@ [{"name":"Datastore/allOther"}, [11, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [11, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [11, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/add"}, [2, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/add", "scope":"OtherTransaction/php__FILE__"}, [2, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_cas.php7.php b/tests/integration/memcached/test_cas.php7.php index b69010e59..650bb22ec 100644 --- a/tests/integration/memcached/test_cas.php7.php +++ b/tests/integration/memcached/test_cas.php7.php @@ -42,6 +42,7 @@ [{"name":"Datastore/allOther"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [5, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete", "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_cas_by_key.php7.php b/tests/integration/memcached/test_cas_by_key.php7.php index f43a6dc29..10b073016 100644 --- a/tests/integration/memcached/test_cas_by_key.php7.php +++ b/tests/integration/memcached/test_cas_by_key.php7.php @@ -42,6 +42,7 @@ [{"name":"Datastore/allOther"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [5, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete", "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_concat.php b/tests/integration/memcached/test_concat.php index 092bd5683..0a526adcd 100644 --- a/tests/integration/memcached/test_concat.php +++ b/tests/integration/memcached/test_concat.php @@ -35,6 +35,7 @@ [{"name":"Datastore/allOther"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [5, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete", "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_concat_by_key.php b/tests/integration/memcached/test_concat_by_key.php index 7339eed12..2cb041974 100644 --- a/tests/integration/memcached/test_concat_by_key.php +++ b/tests/integration/memcached/test_concat_by_key.php @@ -35,6 +35,7 @@ [{"name":"Datastore/allOther"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [5, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete", "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_concat_by_key_logging_off.php b/tests/integration/memcached/test_concat_by_key_logging_off.php index d4e3f2d60..4d644a74d 100644 --- a/tests/integration/memcached/test_concat_by_key_logging_off.php +++ b/tests/integration/memcached/test_concat_by_key_logging_off.php @@ -38,6 +38,7 @@ [{"name":"Datastore/allOther"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [5, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/delete", "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_multi.php b/tests/integration/memcached/test_multi.php index 82328762a..78dbd2fa3 100644 --- a/tests/integration/memcached/test_multi.php +++ b/tests/integration/memcached/test_multi.php @@ -42,6 +42,7 @@ [{"name":"Datastore/allOther"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [5, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/get"}, [4, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/get", "scope":"OtherTransaction/php__FILE__"}, [4, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_multi_by_key.php b/tests/integration/memcached/test_multi_by_key.php index d5867c36d..5d4c2e9d3 100644 --- a/tests/integration/memcached/test_multi_by_key.php +++ b/tests/integration/memcached/test_multi_by_key.php @@ -42,6 +42,7 @@ [{"name":"Datastore/allOther"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/all"}, [5, "??", "??", "??", "??", "??"]], [{"name":"Datastore/Memcached/allOther"}, [5, "??", "??", "??", "??", "??"]], + [{"name":"Datastore/instance/Memcached/ENV[MEMCACHE_HOST]/11211"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/get"}, [4, "??", "??", "??", "??", "??"]], [{"name":"Datastore/operation/Memcached/get", "scope":"OtherTransaction/php__FILE__"}, [4, "??", "??", "??", "??", "??"]], diff --git a/tests/integration/memcached/test_socket.php b/tests/integration/memcached/test_socket.php new file mode 100644 index 000000000..19a1787e9 --- /dev/null +++ b/tests/integration/memcached/test_socket.php @@ -0,0 +1,30 @@ + +*/ + +/*INI +*/ + +/*EXPECT_METRICS_EXIST +Datastore/instance/Memcached/__HOST__/my/socket, 1 +*/ + +/*EXPECT_ERROR_EVENTS null */ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); +require_once(realpath (dirname ( __FILE__ )) . '/../../include/tap.php'); +require_once(realpath (dirname ( __FILE__ )) . '/memcache.inc'); + +$memcached = new Memcached(); +$memcached->addServer("my/socket", 0); +$memcached->quit(); From f9530ed288f4ad47f10353590dd41ad877ac8800 Mon Sep 17 00:00:00 2001 From: Michal Nowacki Date: Mon, 7 Oct 2024 22:58:05 -0400 Subject: [PATCH 14/45] refactor(agent): improve magic file recognition performance (#970) Speed up package detection by performing a suffix match on the 'magic' file pattern with case insensitive string comparison instead of a substring search within a lowercased filename. This is possible because all of the 'magic' file search patterns patterns are right anchored. Fixup of e11b992c with changes from 24c1c656. --- agent/php_execute.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/agent/php_execute.c b/agent/php_execute.c index 83ff4d5a9..e54dc7828 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -603,14 +603,17 @@ static size_t num_logging_frameworks typedef struct _nr_vuln_mgmt_table_t { const char* package_name; const char* file_to_check; + size_t file_to_check_len; nr_vuln_mgmt_enable_fn_t enable; } nr_vuln_mgmt_table_t; /* Note that all paths should be in lowercase. */ +// clang-format: off static const nr_vuln_mgmt_table_t vuln_mgmt_packages[] = { - {"Drupal", "drupal/component/dependencyinjection/container.php", nr_drupal_version}, - {"Wordpress", "wp-includes/version.php", nr_wordpress_version}, + {"Drupal", NR_PSTR("drupal/component/dependencyinjection/container.php"), nr_drupal_version}, + {"Wordpress", NR_PSTR("wp-includes/version.php"), nr_wordpress_version}, }; +// clang-format: on static const size_t num_packages = sizeof(vuln_mgmt_packages) / sizeof(nr_vuln_mgmt_table_t); @@ -990,28 +993,22 @@ static void nr_execute_handle_logging_framework(const char* filename, } } -#undef STR_AND_LEN - -static void nr_execute_handle_package(const char* filename) { - if (NULL == filename || 0 >= nr_strlen(filename)) { - nrl_verbosedebug(NRL_FRAMEWORK, "%s: The file name is NULL", - __func__); - return; - } - char* filename_lower = nr_string_to_lowercase(filename); +static void nr_execute_handle_package(const char* filename, + const size_t filename_len) { size_t i = 0; for (i = 0; i < num_packages; i++) { - if (nr_stridx(filename_lower, vuln_mgmt_packages[i].file_to_check) >= 0) { + if (nr_striendswith(STR_AND_LEN(filename), + STR_AND_LEN(vuln_mgmt_packages[i].file_to_check))) { if (NULL != vuln_mgmt_packages[i].enable) { vuln_mgmt_packages[i].enable(); } } } - - nr_free(filename_lower); } +#undef STR_AND_LEN + /* * Purpose : Detect library and framework usage from a PHP file. * @@ -1036,7 +1033,7 @@ static void nr_php_user_instrumentation_from_file(const char* filename, nr_execute_handle_autoload(filename, filename_len); nr_execute_handle_logging_framework(filename, filename_len TSRMLS_CC); if (NRINI(vulnerability_management_package_detection_enabled)) { - nr_execute_handle_package(filename); + nr_execute_handle_package(filename, filename_len); } } From 7a5f848e99900715e5b2594a83b6abb9cb1d7cc1 Mon Sep 17 00:00:00 2001 From: Michal Nowacki Date: Tue, 15 Oct 2024 00:22:49 -0400 Subject: [PATCH 15/45] refactor(agent): improve magic file recognition performance (#975) Speed up magic file recognition performance by removing files from `libraries` and `logging_frameworks` that belong to old and un-instrumented packages. --- agent/php_execute.c | 55 ------------------- .../analog/test_supportability_metric.php | 25 --------- .../analog/analog/lib/Analog/Analog.php | 6 -- .../test_supportability_metric.php | 24 -------- .../cakephp-log/vendor/cakephp/log/Log.php | 6 -- 5 files changed, 116 deletions(-) delete mode 100644 tests/integration/logging/analog/test_supportability_metric.php delete mode 100644 tests/integration/logging/analog/vendor/analog/analog/lib/Analog/Analog.php delete mode 100644 tests/integration/logging/cakephp-log/test_supportability_metric.php delete mode 100644 tests/integration/logging/cakephp-log/vendor/cakephp/log/Log.php diff --git a/agent/php_execute.c b/agent/php_execute.c index e54dc7828..e8e0db9ca 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -523,59 +523,7 @@ static nr_library_table_t libraries[] = { */ {"Laminas_Http", NR_PSTR("laminas-http/src/client.php"), nr_laminas_http_enable}, - /* - * Other frameworks, detected only, but not specifically - * instrumented. We detect these as libraries so that we don't prevent - * detection of a supported framework or library later (since a transaction - * can only have one framework). - */ - {"Aura1", NR_PSTR("aura/framework/system.php"), NULL}, - {"Aura2", NR_PSTR("aura/di/src/containerinterface.php"), NULL}, - {"Aura3", NR_PSTR("aura/di/src/containerconfiginterface.php"), NULL}, {"CakePHP3", NR_PSTR("cakephp/src/core/functions.php"), NULL}, - {"Fuel", NR_PSTR("fuel/core/classes/fuel.php"), NULL}, - {"Lithium", NR_PSTR("lithium/core/libraries.php"), NULL}, - {"Phpbb", NR_PSTR("phpbb/request/request.php"), NULL}, - {"Phpixie2", NR_PSTR("phpixie/core/classes/phpixie/pixie.php"), NULL}, - {"Phpixie3", NR_PSTR("phpixie/framework.php"), NULL}, - {"React", NR_PSTR("react/event-loop/src/loopinterface.php"), NULL}, - {"SilverStripe", NR_PSTR("injector/silverstripeinjectioncreator.php"), NULL}, - {"SilverStripe4", NR_PSTR("silverstripeserviceconfigurationlocator.php"), NULL}, - {"Typo3", NR_PSTR("classes/typo3/flow/core/bootstrap.php"), NULL}, - {"Typo3", NR_PSTR("typo3/sysext/core/classes/core/bootstrap.php"), NULL}, - - /* - * Other CMS (content management systems), detected only, but - * not specifically instrumented. - */ - {"Moodle", NR_PSTR("moodlelib.php"), NULL}, - /* - * It is likely that this will never be found, since the CodeIgniter.php - * will get loaded first, and as such mark this transaction as belonging to - * CodeIgniter, and not Expession Engine. - */ - {"ExpressionEngine", NR_PSTR("system/expressionengine/config/config.php"), NULL}, - /* - * ExpressionEngine 5, however, has a very obvious file we can look for. - */ - {"ExpressionEngine5", NR_PSTR("expressionengine/boot/boot.php"), NULL}, - /* - * DokuWiki uses doku.php as an entry point, but has other files that are - * loaded directly that this won't pick up. That's probably OK for - * supportability metrics, but we'll add the most common name for the - * configuration file as well just in case. - */ - {"DokuWiki", NR_PSTR("doku.php"), NULL}, - {"DokuWiki", NR_PSTR("conf/dokuwiki.php"), NULL}, - - /* - * SugarCRM no longer has a community edition, so this likely only works - * with older versions. - */ - {"SugarCRM", NR_PSTR("sugarobjects/sugarconfig.php"), NULL}, - - {"Xoops", NR_PSTR("class/xoopsload.php"), NULL}, - {"E107", NR_PSTR("e107_handlers/e107_class.php"), NULL}, }; // clang-format: on @@ -590,9 +538,6 @@ static nr_library_table_t logging_frameworks[] = { /* laminas-log - Logging for PHP */ {"laminas-log", NR_PSTR("laminas-log/src/logger.php"), NULL}, /* cakephp-log - Logging for PHP */ - {"cakephp-log", NR_PSTR("cakephp/log/log.php"), NULL}, - /* Analog - Logging for PHP */ - {"Analog", NR_PSTR("analog/analog.php"), NULL}, }; // clang-format: on diff --git a/tests/integration/logging/analog/test_supportability_metric.php b/tests/integration/logging/analog/test_supportability_metric.php deleted file mode 100644 index f3cfb4477..000000000 --- a/tests/integration/logging/analog/test_supportability_metric.php +++ /dev/null @@ -1,25 +0,0 @@ - Date: Tue, 15 Oct 2024 13:08:33 -0600 Subject: [PATCH 16/45] fix(installer): tarball installer checks for ini in mods-available (#971) Debian uses a directory called "mods-available" to house ini files. It's `phpenmod` and `phpdismod` allow command-line management of extensions in directory. It also creates a symlink ini file to this "mods-available" directory in the normal conf.d directory. The issue is that this symlink is prefixed with a number (for example 20-newrelic.ini). This prefix prevents our tarball installer from recognizing that a newrelic.ini already exists, resulting in 2 ini files: a newrelic.ini and a 20-newrelic.ini. Our .deb package installer scouts out the "mods-available" directory and installs therein when possible. That means that if a customer installs via package but upgrades via tarball (not recommended), then their system gets the duplicates. This change makes our tarball installer scout for a potential previous installation in mods-available. Fixes https://github.com/newrelic/newrelic-php-agent/issues/399 --------- Co-authored-by: Michal Nowacki --- agent/newrelic-install.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/agent/newrelic-install.sh b/agent/newrelic-install.sh index d0959b252..9ef1e45fd 100755 --- a/agent/newrelic-install.sh +++ b/agent/newrelic-install.sh @@ -1371,6 +1371,25 @@ EOF if [ -d "${cfg_pfx}/fpm/conf.d" ]; then pi_inidir_dso="${cfg_pfx}/fpm/conf.d" fi + + # + # Debian can use a mods-available directory to store the ini files. + # It creates a symlink from the ini file in the conf.d directory that + # our installer can fail to find (because the symlink is prefixed with + # "20-" (notably the number can change based on configurations). + # While this install script will not install into the mods-available + # directory, our .deb installer can. Therefore, we want to detect if + # newrelic has previously been installed in the mods-available directory + # so that we do not create an additional ini file -- which would result in + # the conf.d directory having both newrelic.ini and 20-newrelic.ini. + # + + if [ -d "${cfg_pfx}/mods-available" -a -f "${cfg_pfx}/mods-available/newrelic.ini" ]; then + pi_inidir_cli="${cfg_pfx}/mods-available" + if [ -n "${pi_inidir_dso}" ]; then + pi_inidir_dso="${cfg_pfx}/mods-available" + fi + fi fi done From dc162a4eb72fa9faad2d982350e5362ed7cdd83b Mon Sep 17 00:00:00 2001 From: Michal Nowacki Date: Fri, 18 Oct 2024 10:30:16 -0400 Subject: [PATCH 17/45] fix(agent): don't skip arguments when calling `mysqli::real_connect` (#976) Co-authored-by: Konstantin Kovshenin Co-authored-by: Amber Sistla --- agent/php_mysqli.c | 61 ++++--- docker-compose.yaml | 9 ++ .../mysqli/test_explain_connect_socket.php | 148 +++++++++++++++++ .../mysqli/test_explain_construct_socket.php | 150 ++++++++++++++++++ 4 files changed, 342 insertions(+), 26 deletions(-) create mode 100644 tests/integration/mysqli/test_explain_connect_socket.php create mode 100644 tests/integration/mysqli/test_explain_construct_socket.php diff --git a/agent/php_mysqli.c b/agent/php_mysqli.c index b17fd2491..24a321b02 100644 --- a/agent/php_mysqli.c +++ b/agent/php_mysqli.c @@ -398,42 +398,51 @@ static nr_status_t nr_php_mysqli_link_real_connect( zval* link, const nr_mysqli_metadata_link_t* metadata TSRMLS_DC) { zend_ulong argc = 0; + zend_ulong arg_required = 0; zval* argv[7] = {0}; zend_ulong i; zval* retval = NULL; -#define ADD_IF_INT_SET(args, argc, value) \ - if (value) { \ - args[argc] = nr_php_zval_alloc(); \ - ZVAL_LONG(args[argc], value); \ - argc++; \ - } - -#define ADD_IF_STR_SET(args, argc, value) \ - if (value) { \ - args[argc] = nr_php_zval_alloc(); \ - nr_php_zval_str(args[argc], value); \ - argc++; \ - } - - ADD_IF_STR_SET(argv, argc, +#define ADD_IF_INT_SET(null_ok, args, argc, value) \ + if (value) { \ + args[argc] = nr_php_zval_alloc(); \ + ZVAL_LONG(args[argc], value); \ + argc++; \ + } else if (true == null_ok) { \ + args[argc] = nr_php_zval_alloc(); \ + ZVAL_NULL(args[argc]); \ + argc++; \ + } + +#define ADD_IF_STR_SET(null_ok, args, argc, value) \ + if (value) { \ + args[argc] = nr_php_zval_alloc(); \ + nr_php_zval_str(args[argc], value); \ + argc++; \ + } else if (true == null_ok) { \ + args[argc] = nr_php_zval_alloc(); \ + ZVAL_NULL(args[argc]); \ + argc++; \ + } + + ADD_IF_STR_SET(false, argv, argc, nr_php_mysqli_strip_persistent_prefix(metadata->host)); - ADD_IF_STR_SET(argv, argc, metadata->user); - ADD_IF_STR_SET(argv, argc, metadata->password); + ADD_IF_STR_SET(false, argv, argc, metadata->user); + ADD_IF_STR_SET(false, argv, argc, metadata->password); /* * We can only add the remaining metadata fields if we already have three * arguments (host, user and password) above, lest we accidentally set the - * wrong positional argument to something it doesn't mean. + * wrong positional argument to something it doesn't mean. Note, prior + * to 7.4 not all args are nullable. */ + arg_required = argc; if (argc == 3) { - ADD_IF_STR_SET(argv, argc, metadata->database); - ADD_IF_INT_SET(argv, argc, metadata->port); - ADD_IF_STR_SET(argv, argc, metadata->socket); - ADD_IF_INT_SET(argv, argc, metadata->flags); - } - - retval = nr_php_call_user_func(link, "real_connect", argc, argv TSRMLS_CC); + ADD_IF_STR_SET(true, argv, argc, metadata->database); + ADD_IF_INT_SET(true, argv, argc, metadata->port); + ADD_IF_STR_SET(true, argv, argc, metadata->socket); + ADD_IF_INT_SET(false, argv, argc, metadata->flags); + } retval = nr_php_call_user_func(link, "real_connect", argc, argv TSRMLS_CC); for (i = 0; i < argc; i++) { nr_php_zval_free(&argv[i]); @@ -450,7 +459,7 @@ static nr_status_t nr_php_mysqli_link_real_connect( * If we didn't specify the database in the connection parameters, we need to * call mysqli::select_db here. */ - if (metadata->database && (argc < 4)) { + if (metadata->database && (arg_required < 3)) { zval* database = nr_php_zval_alloc(); nr_php_zval_str(database, metadata->database); diff --git a/docker-compose.yaml b/docker-compose.yaml index 4d57753ff..2ea1d11d3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -24,6 +24,8 @@ services: retries: 3 start_period: 20s container_name: mysqldb + volumes: + - var-run-mysqld:/var/run/mysqld redisdb: image: redis restart: always @@ -56,6 +58,7 @@ services: MYSQL_USER: admin MYSQL_PASSWD: admin MYSQL_HOST: mysqldb + MYSQL_SOCKET: /var/run/mysqld/mysqld.sock PG_HOST: postgres PG_PORT: 5432 @@ -67,6 +70,7 @@ services: volumes: - ${AGENT_CODE:-$PWD}:/usr/local/src/newrelic-php-agent + - var-run-mysqld:/var/run/mysqld entrypoint: tail command: -f /dev/null container_name: nr-php @@ -83,6 +87,7 @@ services: MYSQL_USER: admin MYSQL_PASSWD: admin MYSQL_HOST: mysqldb + MYSQL_SOCKET: /var/run/mysqld/mysqld.sock PG_HOST: postgres PG_PORT: 5432 @@ -97,8 +102,12 @@ services: NEWRELIC_LICENSE_KEY: ${NEW_RELIC_LICENSE_KEY} volumes: - ${PWD}:/usr/src/myapp + - var-run-mysqld:/var/run/mysqld working_dir: /usr/src/myapp stdin_open: true tty: true container_name: agent-devenv profiles: ["dev"] + +volumes: + var-run-mysqld: diff --git a/tests/integration/mysqli/test_explain_connect_socket.php b/tests/integration/mysqli/test_explain_connect_socket.php new file mode 100644 index 000000000..0c56e3bc9 --- /dev/null +++ b/tests/integration/mysqli/test_explain_connect_socket.php @@ -0,0 +1,148 @@ +", + "?? SQL ID", + "SELECT TABLE_NAME FROM information_schema.tables WHERE table_name=?", + "Datastore/statement/MySQL/tables/select", + 1, + "?? total time", + "?? min time", + "?? max time", + { + "explain_plan": [ + [ + "id", + "select_type", + "table", + "type", + "possible_keys", + "key", + "key_len", + "ref", + "rows", + "Extra" + ], + [ + [ + 1, + "SIMPLE", + "tables", + "ALL", + null, + "TABLE_NAME", + null, + null, + null, + "Using where; Skip_open_table; Scanned 1 database" + ] + ] + ], + "backtrace": [ + " in mysqli_stmt_execute called at __FILE__ (??)", + " in test_prepare called at __FILE__ (??)" + ] + } + ] + ] +] +*/ + +/*EXPECT_TRACED_ERRORS +null +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/config.php'); + +function test_prepare($link) +{ + $query = "SELECT TABLE_NAME FROM information_schema.tables WHERE table_name='STATISTICS'"; + + $stmt = mysqli_prepare($link, $query); + if (FALSE === $stmt) { + echo mysqli_error($link) . "\n"; + return; + } + + if (FALSE === mysqli_stmt_execute($stmt)) { + echo mysqli_stmt_error($stmt) . "\n"; + return; + } + + if (FALSE === mysqli_stmt_bind_result($stmt, $value)) { + echo mysqli_stmt_error($stmt) . "\n"; + return; + } + + while (mysqli_stmt_fetch($stmt)) { + echo $value . "\n"; + } + + mysqli_stmt_close($stmt); +} + +$link = mysqli_connect('localhost', $MYSQL_USER, $MYSQL_PASSWD, $MYSQL_DB, null, $MYSQL_SOCKET); +if (mysqli_connect_errno()) { + echo mysqli_connect_error() . "\n"; + exit(1); +} + +test_prepare($link); +mysqli_close($link); diff --git a/tests/integration/mysqli/test_explain_construct_socket.php b/tests/integration/mysqli/test_explain_construct_socket.php new file mode 100644 index 000000000..20c6e1481 --- /dev/null +++ b/tests/integration/mysqli/test_explain_construct_socket.php @@ -0,0 +1,150 @@ +", + "?? SQL ID", + "SELECT TABLE_NAME FROM information_schema.tables WHERE table_name=?", + "Datastore/statement/MySQL/tables/select", + 1, + "?? total time", + "?? min time", + "?? max time", + { + "explain_plan": [ + [ + "id", + "select_type", + "table", + "type", + "possible_keys", + "key", + "key_len", + "ref", + "rows", + "Extra" + ], + [ + [ + 1, + "SIMPLE", + "tables", + "ALL", + null, + "TABLE_NAME", + null, + null, + null, + "Using where; Skip_open_table; Scanned 1 database" + ] + ] + ], + "backtrace": [ + " in mysqli_stmt_execute called at __FILE__ (??)", + " in test_prepare called at __FILE__ (??)" + ] + } + ] + ] +] +*/ + +/*EXPECT_TRACED_ERRORS +null +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/config.php'); + +function test_prepare($link) +{ + + $query = "SELECT TABLE_NAME FROM information_schema.tables WHERE table_name='STATISTICS'"; + + $stmt = mysqli_prepare($link, $query); + if (FALSE === $stmt) { + echo mysqli_error($link) . "\n"; + return; + } + + if (FALSE === mysqli_stmt_execute($stmt)) { + echo mysqli_stmt_error($stmt) . "\n"; + return; + } + + if (FALSE === mysqli_stmt_bind_result($stmt, $value)) { + echo mysqli_stmt_error($stmt) . "\n"; + return; + } + + while (mysqli_stmt_fetch($stmt)) { + echo $value . "\n"; + } + + mysqli_stmt_close($stmt); +} + +$link = new mysqli('localhost', $MYSQL_USER, $MYSQL_PASSWD, $MYSQL_DB, null, $MYSQL_SOCKET); +if (mysqli_connect_errno()) { + echo mysqli_connect_error() . "\n"; + exit(1); +} + +test_prepare($link); +mysqli_close($link); From 2197a45266f476553ada0dcce241ecce3ae79c24 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 20 Mar 2024 14:53:01 -0600 Subject: [PATCH 18/45] refactor(agent): remove oapi 'clean' callback --- agent/fw_cakephp.c | 4 +- agent/fw_drupal.c | 39 +++----- agent/fw_drupal8.c | 24 ++--- agent/fw_laravel.c | 12 +-- agent/fw_laravel_queue.c | 16 ++-- agent/fw_lumen.c | 4 +- agent/fw_magento2.c | 16 ++-- agent/fw_slim.c | 12 +-- agent/fw_symfony4.c | 4 +- agent/fw_wordpress.c | 30 +++---- agent/fw_yii.c | 17 ++-- agent/lib_doctrine2.c | 10 +-- agent/lib_mongodb.c | 64 +++++++------- agent/lib_predis.c | 29 ++---- agent/php_execute.c | 13 +-- agent/php_newrelic.h | 2 +- agent/php_user_instrument.c | 16 ---- agent/php_user_instrument.h | 10 +-- agent/php_wrapper.c | 55 +++++------- agent/php_wrapper.h | 20 ++--- agent/tests/test_php_wrapper.c | 36 ++++---- .../frameworks/drupal/mock_module_handler.php | 5 ++ .../drupal/test_invoke_all_with.php | 5 +- tests/integration/test_simple.php | 88 +++++++++++++++++++ 24 files changed, 262 insertions(+), 269 deletions(-) create mode 100644 tests/integration/test_simple.php diff --git a/agent/fw_cakephp.c b/agent/fw_cakephp.c index 1dfe88231..c956b4de0 100644 --- a/agent/fw_cakephp.c +++ b/agent/fw_cakephp.c @@ -331,8 +331,8 @@ void nr_cakephp_enable_2(TSRMLS_D) { nr_cakephp_name_the_wt_2 TSRMLS_CC); #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("CakeException::__construct"), nr_cakephp_problem_2, NULL, NULL); + nr_php_wrap_user_function_before_after( + NR_PSTR("CakeException::__construct"), nr_cakephp_problem_2, NULL); #else nr_php_wrap_user_function(NR_PSTR("CakeException::__construct"), nr_cakephp_problem_2 TSRMLS_CC); diff --git a/agent/fw_drupal.c b/agent/fw_drupal.c index 3537f8b42..5a9b2ce4e 100644 --- a/agent/fw_drupal.c +++ b/agent/fw_drupal.c @@ -305,10 +305,10 @@ NR_PHP_WRAPPER(nr_drupal_http_request_after) { = nr_drupal_http_request_get_method(NR_EXECUTE_ORIG_ARGS); external_params.encoded_response_header - = nr_drupal_http_request_get_response_header(&func_return_value); + = nr_drupal_http_request_get_response_header(NR_GET_RETURN_VALUE_PTR); external_params.status - = nr_drupal_http_request_get_response_code(&func_return_value); + = nr_drupal_http_request_get_response_code(NR_GET_RETURN_VALUE_PTR); if (NRPRG(txn) && NRTXN(special_flags.debug_cat)) { nrl_verbosedebug( NRL_CAT, "CAT: outbound response: transport='Drupal 6-7' %s=" NRP_FMT, @@ -331,16 +331,6 @@ NR_PHP_WRAPPER(nr_drupal_http_request_after) { } NR_PHP_WRAPPER_END -NR_PHP_WRAPPER(nr_drupal_http_request_clean) { - NR_UNUSED_SPECIALFN; - NR_UNUSED_FUNC_RETURN_VALUE; - (void)wraprec; - - NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_DRUPAL); - - NRPRG(drupal_http_request_depth) -= 1; -} -NR_PHP_WRAPPER_END #else /* @@ -778,14 +768,6 @@ NR_PHP_WRAPPER(nr_drupal_wrap_module_invoke_all_after) { } NR_PHP_WRAPPER_END -NR_PHP_WRAPPER(nr_drupal_wrap_module_invoke_all_clean) { - NR_UNUSED_SPECIALFN; - NR_UNUSED_FUNC_RETURN_VALUE; - (void)wraprec; - nr_drupal_invoke_all_hook_stacks_pop(); -} -NR_PHP_WRAPPER_END - #else NR_PHP_WRAPPER(nr_drupal_wrap_module_invoke_all) { zval* hook = NULL; @@ -834,14 +816,14 @@ void nr_drupal_enable(TSRMLS_D) { nr_drupal_cron_run TSRMLS_CC); #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("QFormBase::Run"), nr_drupal_qdrupal_name_the_wt, NULL, NULL); - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( + NR_PSTR("QFormBase::Run"), nr_drupal_qdrupal_name_the_wt, NULL); + nr_php_wrap_user_function_before_after( NR_PSTR("drupal_page_cache_header"), nr_drupal_name_wt_as_cached_page, - NULL, NULL); - nr_php_wrap_user_function_before_after_clean( + NULL); + nr_php_wrap_user_function_before_after( NR_PSTR("drupal_http_request"), nr_drupal_http_request_before, - nr_drupal_http_request_after, nr_drupal_http_request_clean); + nr_drupal_http_request_after); #else nr_php_wrap_user_function(NR_PSTR("QFormBase::Run"), nr_drupal_qdrupal_name_the_wt TSRMLS_CC); @@ -860,10 +842,9 @@ void nr_drupal_enable(TSRMLS_D) { nr_drupal_wrap_module_invoke TSRMLS_CC); #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("module_invoke_all"), nr_drupal_wrap_module_invoke_all_before, - nr_drupal_wrap_module_invoke_all_after, - nr_drupal_wrap_module_invoke_all_clean); + nr_drupal_wrap_module_invoke_all_after); #else nr_php_wrap_user_function(NR_PSTR("module_invoke_all"), nr_drupal_wrap_module_invoke_all TSRMLS_CC); diff --git a/agent/fw_drupal8.c b/agent/fw_drupal8.c index 541cb8b87..41c53a2f9 100644 --- a/agent/fw_drupal8.c +++ b/agent/fw_drupal8.c @@ -76,13 +76,12 @@ static void nr_drupal8_add_method_callback(const zend_class_entry* ce, #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA -static void nr_drupal8_add_method_callback_before_after_clean( +static void nr_drupal8_add_method_callback_before_after( const zend_class_entry* ce, const char* method, size_t method_len, nrspecialfn_t before_callback, - nrspecialfn_t after_callback, - nrspecialfn_t clean_callback) { + nrspecialfn_t after_callback) { zend_function* function = NULL; if (NULL == ce) { @@ -106,9 +105,9 @@ static void nr_drupal8_add_method_callback_before_after_clean( "%.*s::%.*s", NRSAFELEN(nr_php_class_entry_name_length(ce)), nr_php_class_entry_name(ce), NRSAFELEN(method_len), method); - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( class_method, nr_strlen(class_method), - before_callback, after_callback, clean_callback); + before_callback, after_callback); nr_free(class_method); } @@ -542,12 +541,6 @@ NR_PHP_WRAPPER(nr_drupal94_invoke_all_with_after) { nr_drupal_invoke_all_hook_stacks_pop(); } NR_PHP_WRAPPER_END - -NR_PHP_WRAPPER(nr_drupal94_invoke_all_with_clean) { - (void)wraprec; - nr_drupal_invoke_all_hook_stacks_pop(); -} -NR_PHP_WRAPPER_END #endif // OAPI /* @@ -584,11 +577,10 @@ NR_PHP_WRAPPER(nr_drupal8_module_handler) { /* Drupal 9.4 introduced a replacement method for getImplentations */ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_drupal8_add_method_callback_before_after_clean( + nr_drupal8_add_method_callback_before_after( ce, NR_PSTR("invokeallwith"), nr_drupal94_invoke_all_with, - nr_drupal94_invoke_all_with_after, - nr_drupal94_invoke_all_with_clean); + nr_drupal94_invoke_all_with_after); #else nr_drupal8_add_method_callback(ce, NR_PSTR("invokeallwith"), nr_drupal94_invoke_all_with TSRMLS_CC); @@ -709,10 +701,10 @@ void nr_drupal8_enable(TSRMLS_D) { */ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Symfony\\Component\\HttpKernel\\EventListe" "ner\\RouterListener::onKernelRequest"), - nr_drupal8_name_the_wt_via_symfony, NULL, NULL); + nr_drupal8_name_the_wt_via_symfony, NULL); #else nr_php_wrap_user_function(NR_PSTR("Symfony\\Component\\HttpKernel\\EventListe" "ner\\RouterListener::onKernelRequest"), diff --git a/agent/fw_laravel.c b/agent/fw_laravel.c index bbac65b20..0c5496cdc 100644 --- a/agent/fw_laravel.c +++ b/agent/fw_laravel.c @@ -772,8 +772,8 @@ static void nr_laravel5_wrap_middleware(zval* app TSRMLS_DC) { Z_STRVAL_P(classname)); #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( - name, nr_strlen(name), nr_laravel5_middleware_handle, NULL, NULL); + nr_php_wrap_user_function_before_after( + name, nr_strlen(name), nr_laravel5_middleware_handle, NULL); #else nr_php_wrap_user_function(name, nr_strlen(name), nr_laravel5_middleware_handle TSRMLS_CC); @@ -833,8 +833,8 @@ static void nr_laravel_add_callback_method(const zend_class_entry* ce, #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( - class_method, nr_strlen(class_method), callback, NULL, NULL); + nr_php_wrap_user_function_before_after( + class_method, nr_strlen(class_method), callback, NULL); #else nr_php_wrap_user_function(class_method, nr_strlen(class_method), callback TSRMLS_CC); @@ -1231,9 +1231,9 @@ void nr_laravel_enable(TSRMLS_D) { */ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Illuminate\\Console\\Application::doRun"), - nr_laravel_console_application_dorun, NULL, NULL); + nr_laravel_console_application_dorun, NULL); #else nr_php_wrap_user_function(NR_PSTR("Illuminate\\Console\\Application::doRun"), nr_laravel_console_application_dorun TSRMLS_CC); diff --git a/agent/fw_laravel_queue.c b/agent/fw_laravel_queue.c index b4d5576e7..a972236f6 100644 --- a/agent/fw_laravel_queue.c +++ b/agent/fw_laravel_queue.c @@ -864,18 +864,18 @@ void nr_laravel_queue_enable(TSRMLS_D) { * took. */ - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Illuminate\\Queue\\Worker::raiseBeforeJobEvent"), NULL, - nr_laravel_queue_worker_raiseBeforeJobEvent_after, NULL); - nr_php_wrap_user_function_before_after_clean( + nr_laravel_queue_worker_raiseBeforeJobEvent_after); + nr_php_wrap_user_function_before_after( NR_PSTR("Illuminate\\Queue\\Worker::raiseAfterJobEvent"), - nr_laravel_queue_worker_raiseAfterJobEvent_before, NULL, NULL); - nr_php_wrap_user_function_before_after_clean( + nr_laravel_queue_worker_raiseAfterJobEvent_before, NULL); + nr_php_wrap_user_function_before_after( NR_PSTR("Illuminate\\Queue\\SyncQueue::raiseBeforeJobEvent"), - nr_laravel_queue_syncqueue_raiseBeforeJobEvent_before, NULL, NULL); - nr_php_wrap_user_function_before_after_clean( + nr_laravel_queue_syncqueue_raiseBeforeJobEvent_before, NULL); + nr_php_wrap_user_function_before_after( NR_PSTR("Illuminate\\Queue\\SyncQueue::raiseAfterJobEvent"), - nr_laravel_queue_worker_raiseAfterJobEvent_before, NULL, NULL); + nr_laravel_queue_worker_raiseAfterJobEvent_before, NULL); #else diff --git a/agent/fw_lumen.c b/agent/fw_lumen.c index 2d34551f0..028c2c71c 100644 --- a/agent/fw_lumen.c +++ b/agent/fw_lumen.c @@ -225,9 +225,9 @@ void nr_lumen_enable(TSRMLS_D) { nr_lumen_handle_found_route TSRMLS_CC); #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Laravel\\Lumen\\Application::sendExceptionToHandler"), - nr_lumen_exception, NULL, NULL); + nr_lumen_exception, NULL); #else nr_php_wrap_user_function( NR_PSTR("Laravel\\Lumen\\Application::sendExceptionToHandler"), diff --git a/agent/fw_magento2.c b/agent/fw_magento2.c index f96994f25..e67f2858b 100644 --- a/agent/fw_magento2.c +++ b/agent/fw_magento2.c @@ -440,9 +440,9 @@ void nr_magento2_enable(TSRMLS_D) { */ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Magento\\Framework\\App\\Action\\Action::dispatch"), - nr_magento2_action_dispatch, NULL, NULL); + nr_magento2_action_dispatch, NULL); #else nr_php_wrap_user_function( NR_PSTR("Magento\\Framework\\App\\Action\\Action::dispatch"), @@ -472,10 +472,10 @@ void nr_magento2_enable(TSRMLS_D) { */ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR( "Magento\\Webapi\\Controller\\Rest\\InputParamsResolver::resolve"), - nr_magento2_inputparamsresolver_resolve, NULL, NULL); + nr_magento2_inputparamsresolver_resolve, NULL); #else nr_php_wrap_user_function( NR_PSTR( @@ -497,14 +497,14 @@ void nr_magento2_enable(TSRMLS_D) { nr_magento2_soap_iswsdllistrequest TSRMLS_CC); #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Magento\\Webapi\\Controller\\Soap\\Request\\Handler::_" "prepareRequestData"), - nr_magento2_soap_handler_preparerequestdata, NULL, NULL); - nr_php_wrap_user_function_before_after_clean( + nr_magento2_soap_handler_preparerequestdata, NULL); + nr_php_wrap_user_function_before_after( NR_PSTR("Magento\\Webapi\\Controller\\Soap\\Request\\Handler::" "prepareOperationInput"), - nr_magento2_soap_handler_prepareoperationinput, NULL, NULL); + nr_magento2_soap_handler_prepareoperationinput, NULL); #else nr_php_wrap_user_function( diff --git a/agent/fw_slim.c b/agent/fw_slim.c index facaeecc1..b352debfe 100644 --- a/agent/fw_slim.c +++ b/agent/fw_slim.c @@ -181,17 +181,17 @@ void nr_slim_enable(TSRMLS_D) { && !defined OVERWRITE_ZEND_EXECUTE_DATA /* Slim 3 */ - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("Slim\\Route::run"), nr_slim3_4_route_run, NULL, NULL); + nr_php_wrap_user_function_before_after( + NR_PSTR("Slim\\Route::run"), nr_slim3_4_route_run, NULL); /* Slim 4 */ - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("Slim\\Routing\\Route::run"), nr_slim3_4_route_run, NULL, NULL); + nr_php_wrap_user_function_before_after( + NR_PSTR("Slim\\Routing\\Route::run"), nr_slim3_4_route_run, NULL); /* Slim 4 */ - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Slim\\Routing\\Dispatcher::dispatch"), nr_slim4_route_dispatch, - NULL, NULL); + NULL); #else /* Slim 4*/ nr_php_wrap_user_function(NR_PSTR("Slim\\Routing\\Route::run"), diff --git a/agent/fw_symfony4.c b/agent/fw_symfony4.c index 8592186ac..de5d00485 100644 --- a/agent/fw_symfony4.c +++ b/agent/fw_symfony4.c @@ -269,9 +269,9 @@ void nr_symfony4_enable(TSRMLS_D) { */ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Symfony\\Component\\Console\\Command\\Command::run"), - nr_symfony4_console_application_run, NULL, NULL); + nr_symfony4_console_application_run, NULL); #else nr_php_wrap_user_function( NR_PSTR("Symfony\\Component\\Console\\Command\\Command::run"), diff --git a/agent/fw_wordpress.c b/agent/fw_wordpress.c index 050750f6a..8503c5286 100644 --- a/agent/fw_wordpress.c +++ b/agent/fw_wordpress.c @@ -682,16 +682,6 @@ NR_PHP_WRAPPER(nr_wordpress_handle_tag_stack_after) { } NR_PHP_WRAPPER_END -NR_PHP_WRAPPER(nr_wordpress_handle_tag_stack_clean) { - NR_UNUSED_SPECIALFN; - NR_UNUSED_FUNC_RETURN_VALUE; - (void)wraprec; - if (0 != NRINI(wordpress_hooks)) { - clean_wordpress_tag_stack(auto_segment); - } -} -NR_PHP_WRAPPER_END - NR_PHP_WRAPPER(nr_wordpress_apply_filters_after) { /* using nr_php_get_user_func_arg() so that we don't perform another copy * when all we want to do is check the string length */ @@ -763,8 +753,8 @@ NR_PHP_WRAPPER(nr_wordpress_add_filter) { if (NULL != wordpress_plugin_theme || NRPRG(wordpress_core)) { #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - callback_wraprec = nr_php_wrap_callable_before_after_clean( - zf, NULL, nr_wordpress_wrap_hook, nr_wordpress_wrap_hook); + callback_wraprec = nr_php_wrap_callable_before_after( + zf, NULL, nr_wordpress_wrap_hook); #else callback_wraprec = nr_php_wrap_callable(zf, nr_wordpress_wrap_hook); #endif @@ -823,22 +813,22 @@ void nr_wordpress_version() { void nr_wordpress_enable(TSRMLS_D) { #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("apply_filters"), nr_wordpress_apply_filters, - nr_wordpress_apply_filters_after, nr_wordpress_handle_tag_stack_clean); + nr_wordpress_apply_filters_after); if (0 != NRINI(wordpress_hooks)) { - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("apply_filters_ref_array"), nr_wordpress_exec_handle_tag, - nr_wordpress_handle_tag_stack_after, nr_wordpress_handle_tag_stack_clean); + nr_wordpress_handle_tag_stack_after); - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("do_action"), nr_wordpress_exec_handle_tag, - nr_wordpress_handle_tag_stack_after, nr_wordpress_handle_tag_stack_clean); + nr_wordpress_handle_tag_stack_after); - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("do_action_ref_array"), nr_wordpress_exec_handle_tag, - nr_wordpress_handle_tag_stack_after, nr_wordpress_handle_tag_stack_clean); + nr_wordpress_handle_tag_stack_after); if (0 != NRPRG(wordpress_plugins)) { nr_php_wrap_user_function(NR_PSTR("add_filter"), nr_wordpress_add_filter); } diff --git a/agent/fw_yii.c b/agent/fw_yii.c index 0b1af7c96..6d7d08803 100644 --- a/agent/fw_yii.c +++ b/agent/fw_yii.c @@ -93,12 +93,11 @@ NR_PHP_WRAPPER_END void nr_yii1_enable(TSRMLS_D) { #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("CAction::runWithParams"), nr_yii1_runWithParams_wrapper, NULL, - NULL); - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( + NR_PSTR("CAction::runWithParams"), nr_yii1_runWithParams_wrapper, NULL); + nr_php_wrap_user_function_before_after( NR_PSTR("CInlineAction::runWithParams"), nr_yii1_runWithParams_wrapper, - NULL, NULL); + NULL); #else nr_php_wrap_user_function(NR_PSTR("CAction::runWithParams"), nr_yii1_runWithParams_wrapper TSRMLS_CC); @@ -196,12 +195,12 @@ NR_PHP_WRAPPER_END void nr_yii2_enable(TSRMLS_D) { #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("yii\\base\\Action::runWithParams"), - nr_yii2_runWithParams_wrapper, NULL, NULL); - nr_php_wrap_user_function_before_after_clean( + nr_yii2_runWithParams_wrapper, NULL); + nr_php_wrap_user_function_before_after( NR_PSTR("yii\\base\\InlineAction::runWithParams"), - nr_yii2_runWithParams_wrapper, NULL, NULL); + nr_yii2_runWithParams_wrapper, NULL); #else nr_php_wrap_user_function(NR_PSTR("yii\\base\\Action::runWithParams"), nr_yii2_runWithParams_wrapper TSRMLS_CC); diff --git a/agent/lib_doctrine2.c b/agent/lib_doctrine2.c index 8f8b8dfe3..b71e3b5ca 100644 --- a/agent/lib_doctrine2.c +++ b/agent/lib_doctrine2.c @@ -63,12 +63,6 @@ NR_PHP_WRAPPER_END #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA -NR_PHP_WRAPPER(nr_doctrine2_cache_dql_clean) { - (void)wraprec; - nr_free(NRPRG(doctrine_dql)); -} -NR_PHP_WRAPPER_END - NR_PHP_WRAPPER(nr_doctrine2_cache_dql_after) { (void)wraprec; nr_free(NRPRG(doctrine_dql)); @@ -99,9 +93,9 @@ nr_slowsqls_labelled_query_t* nr_doctrine2_lookup_input_query(TSRMLS_D) { void nr_doctrine2_enable(TSRMLS_D) { #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Doctrine\\ORM\\Query::_doExecute"), nr_doctrine2_cache_dql, - nr_doctrine2_cache_dql_after, nr_doctrine2_cache_dql_clean); + nr_doctrine2_cache_dql_after); #else nr_php_wrap_user_function(NR_PSTR("Doctrine\\ORM\\Query::_doExecute"), nr_doctrine2_cache_dql TSRMLS_CC); diff --git a/agent/lib_mongodb.c b/agent/lib_mongodb.c index 8d129ae9d..aa96d1b60 100644 --- a/agent/lib_mongodb.c +++ b/agent/lib_mongodb.c @@ -264,84 +264,84 @@ void nr_mongodb_enable() { #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\Aggregate::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "aggregate"); + "aggregate"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\BulkWrite::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "bulkWrite"); + "bulkWrite"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\Count::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "count"); + "count"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\CountDocuments::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "countDocuments"); + "countDocuments"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\CreateIndexes::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "createIndexes"); + "createIndexes"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\Delete::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "delete"); + "delete"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\Distinct::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "distinct"); + "distinct"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\DropCollection::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "dropCollection"); + "dropCollection"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\DropIndexes::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "dropIndexes"); + "dropIndexes"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\Find::execute"), nr_mongodb_operation_before, - nr_mongodb_operation_after, nr_mongodb_operation_after, "find"); + nr_mongodb_operation_after, "find"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\FindAndModify::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "findAndModify"); + "findAndModify"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\InsertMany::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "insertMany"); + "insertMany"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\InsertOne::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "insertOne"); + "insertOne"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\ListIndexes::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "listIndexes"); + "listIndexes"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\Update::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "update"); + "update"); - nr_php_wrap_user_function_before_after_clean_extra( + nr_php_wrap_user_function_before_after_extra( NR_PSTR("MongoDB\\Operation\\DatabaseCommand::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, - nr_mongodb_operation_after, "databaseCommand"); + "databaseCommand"); #else /* Non-OAPI */ diff --git a/agent/lib_predis.c b/agent/lib_predis.c index 607e11ed2..f25b3c505 100644 --- a/agent/lib_predis.c +++ b/agent/lib_predis.c @@ -755,12 +755,6 @@ NR_PHP_WRAPPER(nr_predis_pipeline_executePipeline_after) { } NR_PHP_WRAPPER_END -NR_PHP_WRAPPER(nr_predis_pipeline_executePipeline_clean) { - (void)wraprec; - predis_executePipeline_handle_stack(); -} -NR_PHP_WRAPPER_END - NR_PHP_WRAPPER(nr_predis_webdisconnection_executeCommand_before) { (void)wraprec; @@ -848,34 +842,29 @@ void nr_predis_enable(TSRMLS_D) { */ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Predis\\Pipeline\\Pipeline::executePipeline"), nr_predis_pipeline_executePipeline, - nr_predis_pipeline_executePipeline_after, - nr_predis_pipeline_executePipeline_clean); - nr_php_wrap_user_function_before_after_clean( + nr_predis_pipeline_executePipeline_after); + nr_php_wrap_user_function_before_after( NR_PSTR("Predis\\Pipeline\\Atomic::executePipeline"), nr_predis_pipeline_executePipeline, - nr_predis_pipeline_executePipeline_after, - nr_predis_pipeline_executePipeline_clean); - nr_php_wrap_user_function_before_after_clean( + nr_predis_pipeline_executePipeline_after); + nr_php_wrap_user_function_before_after( NR_PSTR("Predis\\Pipeline\\ConnectionErrorProof::executePipeline"), nr_predis_pipeline_executePipeline, - nr_predis_pipeline_executePipeline_after, - nr_predis_pipeline_executePipeline_clean); - nr_php_wrap_user_function_before_after_clean( + nr_predis_pipeline_executePipeline_after); + nr_php_wrap_user_function_before_after( NR_PSTR("Predis\\Pipeline\\FireAndForget::executePipeline"), nr_predis_pipeline_executePipeline, - nr_predis_pipeline_executePipeline_after, - nr_predis_pipeline_executePipeline_clean); + nr_predis_pipeline_executePipeline_after); /* * Instrument Webdis connections, since they don't use the same * writeRequest()/readResponse() pair as the other connection types. */ - nr_php_wrap_user_function_before_after_clean( + nr_php_wrap_user_function_before_after( NR_PSTR("Predis\\Connection\\WebdisConnection::executeCommand"), nr_predis_webdisconnection_executeCommand_before, - nr_predis_webdisconnection_executeCommand_after, nr_predis_webdisconnection_executeCommand_after); #else nr_php_wrap_user_function( diff --git a/agent/php_execute.c b/agent/php_execute.c index e8e0db9ca..ac5121412 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -2068,17 +2068,8 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { */ create_metric = wraprec->create_metric; - /* - * A NULL return value ptr means that there was an uncaught exception - * and therefore we want to call the 'clean' function type - */ - if (NULL != nr_php_get_return_value(NR_EXECUTE_ORIG_ARGS)) { - zcaught = nr_zend_call_orig_execute_special(wraprec, segment, - NR_EXECUTE_ORIG_ARGS); - } else { - zcaught = nr_zend_call_oapi_special_clean(wraprec, segment, - NR_EXECUTE_ORIG_ARGS); - } + zcaught = nr_zend_call_orig_execute_special(wraprec, segment, + NR_EXECUTE_ORIG_ARGS); if (nrunlikely(zcaught)) { zend_bailout(); } diff --git a/agent/php_newrelic.h b/agent/php_newrelic.h index 1860b5167..1e9b5453d 100644 --- a/agent/php_newrelic.h +++ b/agent/php_newrelic.h @@ -117,7 +117,7 @@ extern zend_module_entry newrelic_module_entry; #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA -#define NR_GET_RETURN_VALUE_PTR &func_return_value +#define NR_GET_RETURN_VALUE_PTR func_return_value ? &func_return_value : NULL #else #define NR_GET_RETURN_VALUE_PTR nr_php_get_return_value_ptr(TSRMLS_C) #endif diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index 65b078b71..c7fab76d0 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -78,22 +78,6 @@ int nr_zend_call_oapi_special_before(nruserfn_t* wraprec, return zcaught; } - -int nr_zend_call_oapi_special_clean(nruserfn_t* wraprec, - nr_segment_t* segment, - NR_EXECUTE_PROTO) { - volatile int zcaught = 0; - - if (wraprec && wraprec->special_instrumentation_clean) { - zend_try { - wraprec->special_instrumentation_clean(wraprec, segment, - NR_EXECUTE_ORIG_ARGS); - } - zend_catch { zcaught = 1; } - zend_end_try(); - } - return zcaught; -} #endif int nr_zend_call_orig_execute_special(nruserfn_t* wraprec, nr_segment_t* segment, diff --git a/agent/php_user_instrument.h b/agent/php_user_instrument.h index f71889751..15fba7bdf 100644 --- a/agent/php_user_instrument.h +++ b/agent/php_user_instrument.h @@ -72,13 +72,10 @@ typedef struct _nruserfn_t { nrspecialfn_t special_instrumentation; /* * Only used by OAPI, PHP 8+. Used to do any special instrumentation actions - * before a function is executed. special_instrumentation_clean will clean up - * any variables that were set in the before calledback but didn't get cleaned - * up when an exception circumvents the end callback. All callbacks can be - * set. Use the `nr_php_wrap_user_function_after_before_clean` to set. + * before a function is executed. All callbacks can be set using + * `nr_php_wrap_user_function_before_after`. */ nrspecialfn_t special_instrumentation_before; - nrspecialfn_t special_instrumentation_clean; nruserfn_declared_t declared_callback; @@ -213,9 +210,6 @@ extern int nr_zend_call_orig_execute_special(nruserfn_t* wraprec, extern int nr_zend_call_oapi_special_before(nruserfn_t* wraprec, nr_segment_t* segment, NR_EXECUTE_PROTO); -extern int nr_zend_call_oapi_special_clean(nruserfn_t* wraprec, - nr_segment_t* segment, - NR_EXECUTE_PROTO); #endif /* * Purpose : Destroy all user instrumentation records, freeing diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index 6631c06d2..21fa27f15 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -9,12 +9,11 @@ #include "util_logging.h" #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO -static void nr_php_wraprec_add_before_after_clean_callbacks( +static void nr_php_wraprec_add_before_after_callbacks( const char* name, size_t namelen, nruserfn_t* wraprec, nrspecialfn_t before_callback, - nrspecialfn_t after_callback, - nrspecialfn_t clean_callback) { + nrspecialfn_t after_callback) { if (NULL == wraprec) { return; } @@ -41,47 +40,33 @@ static void nr_php_wraprec_add_before_after_clean_callbacks( return; } - if (is_instrumentation_set_and_not_equal(wraprec->special_instrumentation_clean, - clean_callback)) { - nrl_verbosedebug(NRL_INSTRUMENT, - "%s: attempting to set special_instrumentation_clean " - "for %.*s, but " - "it is already set", - __func__, NRSAFELEN(namelen), NRBLANKSTR(name)); - return; - } - wraprec->special_instrumentation = after_callback; wraprec->special_instrumentation_before = before_callback; - wraprec->special_instrumentation_clean = clean_callback; } -nruserfn_t* nr_php_wrap_user_function_before_after_clean( +nruserfn_t* nr_php_wrap_user_function_before_after( const char* name, size_t namelen, nrspecialfn_t before_callback, - nrspecialfn_t after_callback, - nrspecialfn_t clean_callback) { + nrspecialfn_t after_callback) { nruserfn_t* wraprec = nr_php_add_custom_tracer_named(name, namelen); - nr_php_wraprec_add_before_after_clean_callbacks(name, namelen, wraprec, + nr_php_wraprec_add_before_after_callbacks(name, namelen, wraprec, before_callback, - after_callback, - clean_callback); + after_callback); return wraprec; } -nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra( +nruserfn_t* nr_php_wrap_user_function_before_after_extra( const char* name, size_t namelen, nrspecialfn_t before_callback, nrspecialfn_t after_callback, - nrspecialfn_t clean_callback, const char* extra) { - nruserfn_t* wraprec = nr_php_wrap_user_function_before_after_clean( - name, namelen, before_callback, after_callback, clean_callback); + nruserfn_t* wraprec = nr_php_wrap_user_function_before_after( + name, namelen, before_callback, after_callback); if (nrunlikely(NULL == wraprec)) { nrl_warning(NRL_INSTRUMENT, "%s: unable to wrap '%s'", __func__, @@ -94,11 +79,10 @@ nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra( return wraprec; } -nruserfn_t* nr_php_wrap_callable_before_after_clean( +nruserfn_t* nr_php_wrap_callable_before_after( zend_function* callable, nrspecialfn_t before_callback, - nrspecialfn_t after_callback, - nrspecialfn_t clean_callback) { + nrspecialfn_t after_callback) { char* name = NULL; /* creates a transient wraprec */ @@ -110,10 +94,9 @@ nruserfn_t* nr_php_wrap_callable_before_after_clean( if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT)) { name = nr_php_function_debug_name(callable); } - nr_php_wraprec_add_before_after_clean_callbacks(name, nr_strlen(name), wraprec, + nr_php_wraprec_add_before_after_callbacks(name, nr_strlen(name), wraprec, before_callback, - after_callback, - clean_callback); + after_callback); if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT) && NULL != name) { nr_free(name); } @@ -178,8 +161,8 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, * wraprec's internals be evaluated BEFORE for the callable's. As such, * for OAPI, this creates "before" wrappers, where normally the default * is to create "after" wrappers (see nr_php_wrap_user_function). Should - * "after"/"clean" wrappers ever be desired, it is suggested to create a - * separate nr_php_wrap_generic_callable_before_after_clean() function. + * "after" wrappers ever be desired, it is suggested to create a + * separate nr_php_wrap_generic_callable_before_after() function. * * This creates a transient wraprec that does NOT produce an * "InstrumentedFunction" metric. @@ -193,7 +176,7 @@ nruserfn_t* nr_php_wrap_generic_callable(zval* callable, if (NULL != zf) { #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - return nr_php_wrap_callable_before_after_clean(zf, callback, NULL, NULL); + return nr_php_wrap_callable_before_after(zf, callback, NULL); #else return nr_php_wrap_callable(zf, callback); #endif @@ -383,7 +366,11 @@ zval** nr_php_get_return_value_ptr(TSRMLS_D) { return NULL; } - return &EG(current_execute_data)->return_value; + if (NULL != EG(current_execute_data)->return_value) { + return &EG(current_execute_data)->return_value; + } else { + return NULL; + } #else return EG(return_value_ptr_ptr); #endif /* PHP7 */ diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index 058e06e08..b87a5d9c0 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -87,12 +87,11 @@ /* * OAPI updates: - * There are now before, after, and clean callbacks. + * There are now before and after callbacks. * 1) before_callback gets called when OAPI triggers the begin function hook. * 2) after_callback gets called when OAPI triggers the end function hook. - * 3) clean_callback gets called in the case of an exception, because the - * return value will be null, so the after_callback might not function - * correctly. Use clean_callback to reset any variables or states. + * if an exception occurs, return value will be null, so the after_callback + * must check for NULL correctly. * 4) unless explicitly setting any of the above callbacks, the default * callback is set to after_callback. * @@ -138,25 +137,22 @@ * see how it works with frameworks. */ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO -extern nruserfn_t* nr_php_wrap_user_function_before_after_clean( +extern nruserfn_t* nr_php_wrap_user_function_before_after( const char* name, size_t namelen, nrspecialfn_t before_callback, - nrspecialfn_t after_callback, - nrspecialfn_t clean_callback); + nrspecialfn_t after_callback); -extern nruserfn_t* nr_php_wrap_callable_before_after_clean( +extern nruserfn_t* nr_php_wrap_callable_before_after( zend_function* callable, nrspecialfn_t before_callback, - nrspecialfn_t after_callback, - nrspecialfn_t clean_callback); + nrspecialfn_t after_callback); -extern nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra( +extern nruserfn_t* nr_php_wrap_user_function_before_after_extra( const char* name, size_t namelen, nrspecialfn_t before_callback, nrspecialfn_t after_callback, - nrspecialfn_t clean_callback, const char* extra); #endif extern nruserfn_t* nr_php_wrap_user_function(const char* name, diff --git a/agent/tests/test_php_wrapper.c b/agent/tests/test_php_wrapper.c index 975a46904..9b09cdedd 100644 --- a/agent/tests/test_php_wrapper.c +++ b/agent/tests/test_php_wrapper.c @@ -110,12 +110,12 @@ static void execute_nested_framework_calls(nrspecialfn_t one_before, #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("one"), one_before, one_after, NULL); - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("two"), two_before, two_after, NULL); - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("three"), three_before, three_after, NULL); + nr_php_wrap_user_function_before_after( + NR_PSTR("one"), one_before, one_after); + nr_php_wrap_user_function_before_after( + NR_PSTR("two"), two_before, two_after); + nr_php_wrap_user_function_before_after( + NR_PSTR("three"), three_before, three_after); #else /* * This will pick up whichever one isn't null. @@ -574,31 +574,31 @@ static void test_add_arg(TSRMLS_D) { #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA /* PHP 8.0+ and OAPI */ tlib_php_request_eval("function arg0_def0() { return 4; }" TSRMLS_CC); - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("arg0_def0"), test_add_array, NULL, NULL TSRMLS_CC); + nr_php_wrap_user_function_before_after( + NR_PSTR("arg0_def0"), test_add_array, NULL TSRMLS_CC); tlib_php_request_eval("function arg1_def0($a) { return $a; }" TSRMLS_CC); - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("arg1_def0"), test_add_array, NULL, NULL TSRMLS_CC); + nr_php_wrap_user_function_before_after( + NR_PSTR("arg1_def0"), test_add_array, NULL TSRMLS_CC); tlib_php_request_eval( "function arg0_def1($a = null) { return $a; }" TSRMLS_CC); - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("arg0_def1"), test_add_array, NULL, NULL TSRMLS_CC); + nr_php_wrap_user_function_before_after( + NR_PSTR("arg0_def1"), test_add_array, NULL TSRMLS_CC); tlib_php_request_eval( "function arg1_def1($a, $b = null) { return $b; }" TSRMLS_CC); - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("arg1_def1"), test_add_array, NULL, NULL TSRMLS_CC); + nr_php_wrap_user_function_before_after( + NR_PSTR("arg1_def1"), test_add_array, NULL TSRMLS_CC); tlib_php_request_eval( "function arg1_def1_2($a, $b = null) { return $b; }" TSRMLS_CC); - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("arg1_def1_2"), test_add_2_arrays, NULL, NULL TSRMLS_CC); + nr_php_wrap_user_function_before_after( + NR_PSTR("arg1_def1_2"), test_add_2_arrays, NULL TSRMLS_CC); tlib_php_request_eval("function splat(...$a) { return $a[0]; }" TSRMLS_CC); - nr_php_wrap_user_function_before_after_clean( - NR_PSTR("splat"), test_add_array, NULL, NULL TSRMLS_CC); + nr_php_wrap_user_function_before_after( + NR_PSTR("splat"), test_add_array, NULL TSRMLS_CC); #else tlib_php_request_eval("function arg0_def0() { return 4; }" TSRMLS_CC); nr_php_wrap_user_function(NR_PSTR("arg0_def0"), test_add_array TSRMLS_CC); diff --git a/tests/integration/frameworks/drupal/mock_module_handler.php b/tests/integration/frameworks/drupal/mock_module_handler.php index 84d385f10..da1f2b1a6 100644 --- a/tests/integration/frameworks/drupal/mock_module_handler.php +++ b/tests/integration/frameworks/drupal/mock_module_handler.php @@ -10,6 +10,11 @@ interface ModuleHandlerInterface { public function invokeAllWith($hook_str, $callback); } class ModuleHandler implements ModuleHandlerInterface { + public function __construct(bool $except=false) { + if ($except) { + throw new Exception("Constructor told to except"); + } + } public function invokeAllWith($hook_str, $callback) { if ($hook_str == "hook_1") { $module = "module_a"; diff --git a/tests/integration/frameworks/drupal/test_invoke_all_with.php b/tests/integration/frameworks/drupal/test_invoke_all_with.php index 289d4e69a..74e448111 100644 --- a/tests/integration/frameworks/drupal/test_invoke_all_with.php +++ b/tests/integration/frameworks/drupal/test_invoke_all_with.php @@ -108,7 +108,10 @@ public function invoke(callable $hook, string $module) { // Create module handler $drupal = new Drupal(); -$handler = $drupal->moduleHandler(); +// Throw an exception during creation to test agent recovery +$handler = $drupal->moduleHandler(true); +// Create the actual handler +$handler = $drupal->moduleHandler(false); // Test lambda calback $handler->invokeAllWith("hook_1", function (callable $hook, string $module) { diff --git a/tests/integration/test_simple.php b/tests/integration/test_simple.php new file mode 100644 index 000000000..8fa700b5b --- /dev/null +++ b/tests/integration/test_simple.php @@ -0,0 +1,88 @@ + Date: Wed, 20 Mar 2024 15:20:57 -0600 Subject: [PATCH 19/45] fix: accidentally included file --- tests/integration/test_simple.php | 88 ------------------------------- 1 file changed, 88 deletions(-) delete mode 100644 tests/integration/test_simple.php diff --git a/tests/integration/test_simple.php b/tests/integration/test_simple.php deleted file mode 100644 index 8fa700b5b..000000000 --- a/tests/integration/test_simple.php +++ /dev/null @@ -1,88 +0,0 @@ - Date: Wed, 17 Apr 2024 10:45:25 -0600 Subject: [PATCH 20/45] fix drupal dangling segment --- agent/fw_drupal.c | 27 +++++++--- .../test_bad_params_integer_headers.php | 8 --- .../test_bad_params_integer_headers.php8.php | 49 ------------------- .../drupal7/test_bad_params_null_headers.php | 8 --- .../test_bad_params_null_headers.php8.php | 49 ------------------- 5 files changed, 19 insertions(+), 122 deletions(-) delete mode 100644 tests/integration/external/drupal7/test_bad_params_integer_headers.php8.php delete mode 100644 tests/integration/external/drupal7/test_bad_params_null_headers.php8.php diff --git a/agent/fw_drupal.c b/agent/fw_drupal.c index 5a9b2ce4e..dbc67a7ec 100644 --- a/agent/fw_drupal.c +++ b/agent/fw_drupal.c @@ -281,6 +281,10 @@ NR_PHP_WRAPPER(nr_drupal_http_request_after) { NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_DRUPAL); + nr_segment_external_params_t external_params + = {.library = "Drupal", + .uri = NULL}; + /* * Grab the URL for the external metric, which is the first parameter in all * versions of Drupal. @@ -290,6 +294,12 @@ NR_PHP_WRAPPER(nr_drupal_http_request_after) { goto end; } + if (NULL == NR_GET_RETURN_VALUE_PTR) { + goto end; + } + + external_params.uri = nr_strndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)); + /* * We only want to create a metric here if this isn't a recursive call to * drupal_http_request() caused by the original call returning a redirect. @@ -297,10 +307,6 @@ NR_PHP_WRAPPER(nr_drupal_http_request_after) { * checking a counter. */ if (1 == NRPRG(drupal_http_request_depth)) { - nr_segment_external_params_t external_params - = {.library = "Drupal", - .uri = nr_strndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1))}; - external_params.procedure = nr_drupal_http_request_get_method(NR_EXECUTE_ORIG_ARGS); @@ -315,17 +321,22 @@ NR_PHP_WRAPPER(nr_drupal_http_request_after) { X_NEWRELIC_APP_DATA, NRP_CAT(external_params.encoded_response_header)); } + } - nr_segment_external_end(&NRPRG(drupal_http_request_segment), - &external_params); +end: + if (1 == NRPRG(drupal_http_request_depth)) { + if (external_params.uri == NULL) { + nr_segment_discard(&NRPRG(drupal_http_request_segment)); + } else { + nr_segment_external_end(&NRPRG(drupal_http_request_segment), + &external_params); + } NRPRG(drupal_http_request_segment) = NULL; nr_free(external_params.encoded_response_header); nr_free(external_params.procedure); nr_free(external_params.uri); } - -end: nr_php_arg_release(&arg1); NRPRG(drupal_http_request_depth) -= 1; } diff --git a/tests/integration/external/drupal7/test_bad_params_integer_headers.php b/tests/integration/external/drupal7/test_bad_params_integer_headers.php index 1f0aec4c8..27beebb0c 100644 --- a/tests/integration/external/drupal7/test_bad_params_integer_headers.php +++ b/tests/integration/external/drupal7/test_bad_params_integer_headers.php @@ -14,9 +14,6 @@ /*SKIPIF =")) { - die("skip: PHP >= 8.0 not supported\n"); -} */ /*EXPECT_METRICS @@ -35,11 +32,6 @@ [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Supportability/framework/Drupal/forced"}, [1, 0, 0, 0, 0, 0]], - [{"name":"External/127.0.0.1/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"External/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"External/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"External/127.0.0.1/all", - "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] ] ] diff --git a/tests/integration/external/drupal7/test_bad_params_integer_headers.php8.php b/tests/integration/external/drupal7/test_bad_params_integer_headers.php8.php deleted file mode 100644 index 36cf6b0f1..000000000 --- a/tests/integration/external/drupal7/test_bad_params_integer_headers.php8.php +++ /dev/null @@ -1,49 +0,0 @@ - 22)); diff --git a/tests/integration/external/drupal7/test_bad_params_null_headers.php b/tests/integration/external/drupal7/test_bad_params_null_headers.php index fe9463431..bfb431e9b 100644 --- a/tests/integration/external/drupal7/test_bad_params_null_headers.php +++ b/tests/integration/external/drupal7/test_bad_params_null_headers.php @@ -14,9 +14,6 @@ /*SKIPIF =")) { - die("skip: PHP >= 8.0 not supported\n"); -} */ /*EXPECT_METRICS @@ -35,11 +32,6 @@ [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Supportability/framework/Drupal/forced"}, [1, 0, 0, 0, 0, 0]], - [{"name":"External/127.0.0.1/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"External/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"External/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"External/127.0.0.1/all", - "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] ] ] diff --git a/tests/integration/external/drupal7/test_bad_params_null_headers.php8.php b/tests/integration/external/drupal7/test_bad_params_null_headers.php8.php deleted file mode 100644 index 1189821b1..000000000 --- a/tests/integration/external/drupal7/test_bad_params_null_headers.php8.php +++ /dev/null @@ -1,49 +0,0 @@ - NULL)); From d03e5b2cfa1f7ea24e0a8a23baccffcaa714b6c5 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 17 Apr 2024 10:51:06 -0600 Subject: [PATCH 21/45] fix tests --- .../test_bad_params_integer_headers.php | 8 +++ .../test_bad_params_integer_headers.php8.php | 50 +++++++++++++++++++ .../drupal7/test_bad_params_null_headers.php | 8 +++ .../test_bad_params_null_headers.php8.php | 50 +++++++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 tests/integration/external/drupal7/test_bad_params_integer_headers.php8.php create mode 100644 tests/integration/external/drupal7/test_bad_params_null_headers.php8.php diff --git a/tests/integration/external/drupal7/test_bad_params_integer_headers.php b/tests/integration/external/drupal7/test_bad_params_integer_headers.php index 27beebb0c..1f0aec4c8 100644 --- a/tests/integration/external/drupal7/test_bad_params_integer_headers.php +++ b/tests/integration/external/drupal7/test_bad_params_integer_headers.php @@ -14,6 +14,9 @@ /*SKIPIF =")) { + die("skip: PHP >= 8.0 not supported\n"); +} */ /*EXPECT_METRICS @@ -32,6 +35,11 @@ [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Supportability/framework/Drupal/forced"}, [1, 0, 0, 0, 0, 0]], + [{"name":"External/127.0.0.1/all"}, [1, "??", "??", "??", "??", "??"]], + [{"name":"External/all"}, [1, "??", "??", "??", "??", "??"]], + [{"name":"External/allOther"}, [1, "??", "??", "??", "??", "??"]], + [{"name":"External/127.0.0.1/all", + "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] ] ] diff --git a/tests/integration/external/drupal7/test_bad_params_integer_headers.php8.php b/tests/integration/external/drupal7/test_bad_params_integer_headers.php8.php new file mode 100644 index 000000000..f1fba9355 --- /dev/null +++ b/tests/integration/external/drupal7/test_bad_params_integer_headers.php8.php @@ -0,0 +1,50 @@ + 22)); diff --git a/tests/integration/external/drupal7/test_bad_params_null_headers.php b/tests/integration/external/drupal7/test_bad_params_null_headers.php index bfb431e9b..fe9463431 100644 --- a/tests/integration/external/drupal7/test_bad_params_null_headers.php +++ b/tests/integration/external/drupal7/test_bad_params_null_headers.php @@ -14,6 +14,9 @@ /*SKIPIF =")) { + die("skip: PHP >= 8.0 not supported\n"); +} */ /*EXPECT_METRICS @@ -32,6 +35,11 @@ [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Supportability/framework/Drupal/forced"}, [1, 0, 0, 0, 0, 0]], + [{"name":"External/127.0.0.1/all"}, [1, "??", "??", "??", "??", "??"]], + [{"name":"External/all"}, [1, "??", "??", "??", "??", "??"]], + [{"name":"External/allOther"}, [1, "??", "??", "??", "??", "??"]], + [{"name":"External/127.0.0.1/all", + "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] ] ] diff --git a/tests/integration/external/drupal7/test_bad_params_null_headers.php8.php b/tests/integration/external/drupal7/test_bad_params_null_headers.php8.php new file mode 100644 index 000000000..9cde3d021 --- /dev/null +++ b/tests/integration/external/drupal7/test_bad_params_null_headers.php8.php @@ -0,0 +1,50 @@ + NULL)); From 8b2ec4b36696f78536b148a97f2ba2572255808a Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 17 Apr 2024 11:25:09 -0600 Subject: [PATCH 22/45] fix tests --- .../drupal/test_module_invoke_all.php | 3 - .../drupal/test_module_invoke_all.php8.php | 91 ------------------- 2 files changed, 94 deletions(-) delete mode 100644 tests/integration/frameworks/drupal/test_module_invoke_all.php8.php diff --git a/tests/integration/frameworks/drupal/test_module_invoke_all.php b/tests/integration/frameworks/drupal/test_module_invoke_all.php index c9b70e1cf..45ca084c8 100644 --- a/tests/integration/frameworks/drupal/test_module_invoke_all.php +++ b/tests/integration/frameworks/drupal/test_module_invoke_all.php @@ -15,9 +15,6 @@ /*SKIPIF =")) { - die("skip: PHP >= 8.0 uses other test\n"); -} */ /*EXPECT diff --git a/tests/integration/frameworks/drupal/test_module_invoke_all.php8.php b/tests/integration/frameworks/drupal/test_module_invoke_all.php8.php deleted file mode 100644 index fc694e039..000000000 --- a/tests/integration/frameworks/drupal/test_module_invoke_all.php8.php +++ /dev/null @@ -1,91 +0,0 @@ - Date: Tue, 30 Apr 2024 11:48:38 -0600 Subject: [PATCH 23/45] initial commit --- .gitignore | 3 +- agent/fw_drupal.c | 4 + agent/fw_drupal_common.c | 1 + agent/fw_wordpress.c | 68 ++++++- agent/lib_mongodb.c | 4 + agent/lib_predis.c | 4 + agent/php_execute.c | 179 +++++++++++++++++- agent/php_observer.c | 39 ++++ agent/php_observer.h | 14 ++ agent/php_user_instrument.c | 69 ++++++- agent/php_user_instrument.h | 5 + agent/php_wrapper.c | 29 +++ agent/php_wrapper.h | 64 ++++++- axiom/nr_segment.h | 5 +- daemon/cmd/integration_runner/main.go | 2 +- .../test_span_events_max_samples_stored1.php | 2 +- .../span_events/test_span_events_on_dt_on.php | 3 + 17 files changed, 475 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index d850b2fd0..aec099e8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Build artifacts -agent/configure.ac agent/newrelic.map +agent/configure.ac agent/*.dep axiom/tests/*.tmp bin/ @@ -13,3 +13,4 @@ php_agent.log # Dev artifacts .vscode +*.log diff --git a/agent/fw_drupal.c b/agent/fw_drupal.c index dbc67a7ec..c2628b636 100644 --- a/agent/fw_drupal.c +++ b/agent/fw_drupal.c @@ -268,7 +268,11 @@ NR_PHP_WRAPPER(nr_drupal_http_request_before) { * this new segment is now at the top of the segment stack. */ if (NULL != NRPRG(drupal_http_request_segment)) { +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO NRPRG(drupal_http_request_segment)->wraprec = auto_segment->wraprec; +#else + NRPRG(drupal_http_request_segment)->execute_data = auto_segment->execute_data; +#endif } } } diff --git a/agent/fw_drupal_common.c b/agent/fw_drupal_common.c index 894ff2270..5fe24f5e3 100644 --- a/agent/fw_drupal_common.c +++ b/agent/fw_drupal_common.c @@ -60,6 +60,7 @@ NR_PHP_WRAPPER(nr_drupal_wrap_module_hook) { * function such as a_b_c is ambiguous (is the module a or a_b?). Instead, * we'll see if they're defined in the wraprec. */ + wraprec = nr_php_get_wraprec(execute_data->func); if ((NULL != wraprec->drupal_hook) && (NULL != wraprec->drupal_module)) { nr_drupal_create_metric(auto_segment, NR_PSTR(NR_DRUPAL_MODULE_PREFIX), wraprec->drupal_module, wraprec->drupal_module_len); diff --git a/agent/fw_wordpress.c b/agent/fw_wordpress.c index 8503c5286..cd905fc69 100644 --- a/agent/fw_wordpress.c +++ b/agent/fw_wordpress.c @@ -329,6 +329,60 @@ static char* nr_wordpress_plugin_from_function(zend_function* func TSRMLS_DC) { return plugin; } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +NR_PHP_WRAPPER(nr_wordpress_wrap_hook_core) { + NR_UNUSED_SPECIALFN; + (void)wraprec; + + /* + * We only want to hook the function being called if this is a WordPress + * function, we're instrumenting hooks, and WordPress is currently executing + * hooks (denoted by the wordpress_tag being set). + */ + NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_WORDPRESS); + + char* tag = nr_stack_get_top(&NRPRG(wordpress_tags)); + if ((0 == NRINI(wordpress_hooks)) || (NULL == tag)) { + NR_PHP_WRAPPER_LEAVE; + } + + NR_PHP_WRAPPER_CALL; + if (NRPRG(wordpress_core)) { + nr_wordpress_create_metric(auto_segment, NR_WORDPRESS_HOOK_PREFIX, tag); + } +} +NR_PHP_WRAPPER_END + +NR_PHP_WRAPPER(nr_wordpress_wrap_hook_plugin) { + char* plugin = NULL; + + NR_UNUSED_SPECIALFN; + (void)wraprec; + + /* + * We only want to hook the function being called if this is a WordPress + * function, we're instrumenting hooks, and WordPress is currently executing + * hooks (denoted by the wordpress_tag being set). + */ + NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_WORDPRESS); + + char* tag = nr_stack_get_top(&NRPRG(wordpress_tags)); + if ((0 == NRINI(wordpress_hooks)) || (NULL == tag)) { + NR_PHP_WRAPPER_LEAVE; + } + plugin = nr_wordpress_plugin_from_function(execute_data->func); + + NR_PHP_WRAPPER_CALL; + if (NULL != plugin) { + nr_wordpress_create_metric(auto_segment, NR_WORDPRESS_HOOK_PREFIX, tag); + nr_wordpress_create_metric(auto_segment, NR_WORDPRESS_PLUGIN_PREFIX, + plugin); + } +} +NR_PHP_WRAPPER_END +#endif + +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO NR_PHP_WRAPPER(nr_wordpress_wrap_hook) { #if ZEND_MODULE_API_NO < ZEND_7_4_X_API_NO zend_function* func = NULL; @@ -375,6 +429,7 @@ NR_PHP_WRAPPER(nr_wordpress_wrap_hook) { } } NR_PHP_WRAPPER_END +#endif // = ZEND_8_0_X_API_NO \ +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + if (NULL != wordpress_plugin_theme) { + callback_wraprec = nr_php_wrap_callable_before_after( + zf, NULL, nr_wordpress_wrap_hook_plugin); + } else { + callback_wraprec = nr_php_wrap_callable_before_after( + zf, NULL, nr_wordpress_wrap_hook_core); + } +#elif ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA callback_wraprec = nr_php_wrap_callable_before_after( zf, NULL, nr_wordpress_wrap_hook); diff --git a/agent/lib_mongodb.c b/agent/lib_mongodb.c index aa96d1b60..4672187ee 100644 --- a/agent/lib_mongodb.c +++ b/agent/lib_mongodb.c @@ -184,7 +184,11 @@ NR_PHP_WRAPPER(nr_mongodb_operation_before) { nr_segment_t* segment = NULL; segment = nr_segment_start(NRPRG(txn), NULL, NULL); if (NULL != segment) { +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO segment->wraprec = auto_segment->wraprec; +#else + segment->execute_data = auto_segment->execute_data; +#endif } } NR_PHP_WRAPPER_END diff --git a/agent/lib_predis.c b/agent/lib_predis.c index f25b3c505..5d009a9df 100644 --- a/agent/lib_predis.c +++ b/agent/lib_predis.c @@ -761,7 +761,11 @@ NR_PHP_WRAPPER(nr_predis_webdisconnection_executeCommand_before) { nr_segment_t* segment = NULL; segment = nr_segment_start(NRPRG(txn), NULL, NULL); if (NULL != segment) { +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + segment->execute_data = auto_segment->execute_data; +#else segment->wraprec = auto_segment->wraprec; +#endif } } NR_PHP_WRAPPER_END diff --git a/agent/php_execute.c b/agent/php_execute.c index ac5121412..66601635a 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -1884,9 +1884,11 @@ static void nr_php_observer_attempt_call_cufa_handler(NR_EXECUTE_PROTO) { static void nr_php_instrument_func_begin(NR_EXECUTE_PROTO) { nr_segment_t* segment = NULL; - nruserfn_t* wraprec = NULL; +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO nrtime_t txn_start_time = 0; + nruserfn_t* wraprec = NULL; int zcaught = 0; +#endif NR_UNUSED_FUNC_RETURN_VALUE; if (NULL == NRPRG(txn)) { @@ -1894,7 +1896,9 @@ static void nr_php_instrument_func_begin(NR_EXECUTE_PROTO) { } NRTXNGLOBAL(execute_count) += 1; +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO txn_start_time = nr_txn_start_time(NRPRG(txn)); +#endif /* * Handle here, but be aware the classes might not be loaded yet. */ @@ -1918,15 +1922,21 @@ static void nr_php_instrument_func_begin(NR_EXECUTE_PROTO) { */ nr_php_observer_attempt_call_cufa_handler(NR_EXECUTE_ORIG_ARGS); } +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO wraprec = nr_php_get_wraprec(execute_data->func); +#endif segment = nr_segment_start(NRPRG(txn), NULL, NULL); +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + segment->execute_data = execute_data; +#endif if (nrunlikely(NULL == segment)) { nrl_verbosedebug(NRL_AGENT, "Error starting segment."); return; } +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO if (NULL == wraprec) { return; } @@ -1972,16 +1982,64 @@ static void nr_php_instrument_func_begin(NR_EXECUTE_PROTO) { * Check for, and handle, frameworks. */ if (wraprec->is_names_wt_simple) { - nr_txn_name_from_function(NRPRG(txn), wraprec->funcname, - wraprec->classname); + + nr_txn_name_from_function(NRPRG(txn), + nr_php_op_array_function_name(NR_OP_ARRAY); + nr_php_class_entry_name(NR_OP_ARRAY->scope)); + } +#endif +} + +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t txn_start_time) { + /* + * During nr_zend_call_oapi_special_before, the transaction may have been + * ended and/or a new transaction may have started. To detect this, we + * compare the currently active transaction's start time with the transaction + * start time we saved before. + * + * Just comparing the transaction pointer is not enough, as a newly + * started transaction might actually obtain the same address as a + * transaction freed before. + */ + if (nrunlikely(nr_txn_start_time(NRPRG(txn)) != txn_start_time)) { + nrl_verbosedebug(NRL_AGENT, + "%s txn ended and/or started while in a wrapped function", + __func__); + + return; + } + + if (NR_OP_ARRAY->scope) { + nr_txn_force_single_count(NRPRG(txn), nr_txn_create_fn_supportability_metric( + nr_php_op_array_function_name(NR_OP_ARRAY), + nr_php_class_entry_name(NR_OP_ARRAY->scope))); + } else { + nr_txn_force_single_count(NRPRG(txn), nr_txn_create_fn_supportability_metric( + nr_php_op_array_function_name(NR_OP_ARRAY), + NULL)); } + /* + * Check for, and handle, frameworks. + */ + //if (wraprec->is_names_wt_simple) { + + // nr_txn_name_from_function(NRPRG(txn), + // nr_php_op_array_function_name(NR_OP_ARRAY), + // nr_php_class_entry_name(NR_OP_ARRAY->scope)); + //} } +#endif +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { int zcaught = 0; - nr_segment_t* segment = NULL; nruserfn_t* wraprec = NULL; bool create_metric = false; +#else +static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric) { +#endif + nr_segment_t* segment = NULL; nr_php_execute_metadata_t metadata = {0}; nrtime_t txn_start_time = 0; @@ -2017,6 +2075,7 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { return; } +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO wraprec = segment->wraprec; if (segment->is_exception_handler) { @@ -2034,6 +2093,9 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { NRPRG(txn), exception, nr_php_error_get_priority(E_ERROR), false, "Uncaught exception ", &NRPRG(exception_filters) TSRMLS_CC); } else if (NULL == nr_php_get_return_value(NR_EXECUTE_ORIG_ARGS)) { +#else + if (NULL == nr_php_get_return_value(NR_EXECUTE_ORIG_ARGS)) { +#endif /* * Having no return value (and not being an exception handler) indicates * that this segment had an uncaught exception. We want to add that @@ -2050,7 +2112,6 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { __func__); } } - /* * Stop the segment time now so we don't add our additional processing on to * the segment's time. @@ -2062,6 +2123,7 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { * has specifically requested it. */ +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO if (NULL != wraprec) { /* * This is the case for specifically requested custom instrumentation. @@ -2081,6 +2143,7 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { nr_segment_discard(&segment); return; } +#endif /* * During nr_zend_call_orig_execute_special, the transaction may have been * ended and/or a new transaction may have started. To detect this, we @@ -2120,6 +2183,9 @@ void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { * nr_php_execute_show */ zval* func_return_value = NULL; + //if (execute_data->func && execute_data->func->common.function_name) { + // printf("BEGIN %s\n", ZSTR_VAL(execute_data->func->common.function_name)); + //} if (nrunlikely(NULL == execute_data)) { return; } @@ -2138,13 +2204,60 @@ void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { int show_executes = NR_PHP_PROCESS_GLOBALS(special_flags).show_executes; if (nrunlikely(show_executes)) { + nrl_verbosedebug(NRL_AGENT, + "Stack depth: %d after OAPI function beginning via %s", + NRPRG(php_cur_stack_depth), __func__); nr_php_show_exec(NR_EXECUTE_ORIG_ARGS); } + if (NULL == NRPRG(txn)) { + return; + } nr_php_instrument_func_begin(NR_EXECUTE_ORIG_ARGS); return; } +void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { + /* + * Instrument the function. + * This and any other needed helper functions will replace: + * nr_php_execute_enabled + * nr_php_execute + * nr_php_execute_show + */ + zval* func_return_value = NULL; + //if (execute_data->func && execute_data->func->common.function_name) { + // printf("BEGIN %s\n", ZSTR_VAL(execute_data->func->common.function_name)); + //} + if (nrunlikely(NULL == execute_data)) { + return; + } + + NRPRG(php_cur_stack_depth) += 1; + + if ((0 < ((int)NRINI(max_nesting_level))) + && (NRPRG(php_cur_stack_depth) >= (int)NRINI(max_nesting_level))) { + nr_php_max_nesting_level_reached(); + } + + if (nrunlikely(0 == nr_php_recording())) { + return; + } + + int show_executes = NR_PHP_PROCESS_GLOBALS(special_flags).show_executes; + + if (nrunlikely(show_executes)) { + nr_php_show_exec(NR_EXECUTE_ORIG_ARGS); + } + if (NULL == NRPRG(txn)) { + return; + } + nr_php_instrument_func_begin(NR_EXECUTE_ORIG_ARGS); + nr_php_observer_fcall_begin_late(execute_data, nr_txn_start_time(NRPRG(txn))); + + return; +} + void nr_php_observer_fcall_end(zend_execute_data* execute_data, zval* func_return_value) { /* @@ -2157,16 +2270,22 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, if (nrunlikely(NULL == execute_data)) { return; } + //if (execute_data->func && execute_data->func->common.function_name) { + // printf("END %s\n", ZSTR_VAL(execute_data->func->common.function_name)); + //} if (nrlikely(1 == nr_php_recording())) { int show_executes_return = NR_PHP_PROCESS_GLOBALS(special_flags).show_execute_returns; if (nrunlikely(show_executes_return)) { + nrl_verbosedebug(NRL_AGENT, + "Stack depth: %d before OAPI function exiting via %s", + NRPRG(php_cur_stack_depth), __func__); nr_php_show_exec_return(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); } - nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS); + nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, false); } NRPRG(php_cur_stack_depth) -= 1; @@ -2174,4 +2293,52 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, return; } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +// These empty functions (rather than NULL) are used to know if instrumentation +// has been added This is needed because the process for adding instrumentation +// with a transient wrapper differs depending on if the function has been +// previously called. These will only be used when tt_detail is 0. +void nr_php_observer_empty_fcall_begin(zend_execute_data* execute_data) { + (void)execute_data; +} + +void nr_php_observer_empty_fcall_end(zend_execute_data* execute_data, + zval* func_return_value) { + (void)execute_data; + (void)func_return_value; +} + +void nr_php_observer_fcall_end_create_metric(zend_execute_data* execute_data, + zval* func_return_value) { + /* + * Instrument the function. + * This and any other needed helper functions will replace: + * nr_php_execute_enabled + * nr_php_execute + * nr_php_execute_show + */ + if (nrunlikely(NULL == execute_data)) { + return; + } + //if (execute_data->func && execute_data->func->common.function_name) { + // printf("END %s\n", ZSTR_VAL(execute_data->func->common.function_name)); + //} + + if (nrlikely(1 == nr_php_recording())) { + int show_executes_return + = NR_PHP_PROCESS_GLOBALS(special_flags).show_execute_returns; + + if (nrunlikely(show_executes_return)) { + nr_php_show_exec_return(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); + } + + nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, true); + } + + NRPRG(php_cur_stack_depth) -= 1; + + return; +} +#endif + #endif diff --git a/agent/php_observer.c b/agent/php_observer.c index 5719be38e..50ebbad26 100644 --- a/agent/php_observer.c +++ b/agent/php_observer.c @@ -72,6 +72,7 @@ /* * Register the begin and end function handlers with the Observer API. */ +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO /* PHP8+ */ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( zend_execute_data* execute_data) { zend_observer_fcall_handlers handlers = {NULL, NULL}; @@ -86,6 +87,44 @@ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( handlers.end = nr_php_observer_fcall_end; return handlers; } +#else +static zend_observer_fcall_handlers nr_php_fcall_register_handlers( + zend_execute_data* execute_data) { + zend_observer_fcall_handlers handlers = {NULL, NULL}; + nruserfn_t* wraprec = NULL; + if (NULL == execute_data) { + return handlers; + } + if ((NULL == execute_data->func) + || (ZEND_INTERNAL_FUNCTION == execute_data->func->type)) { + return handlers; + } + //if (execute_data->func && execute_data->func->common.function_name) { + // printf("REGISTER %s\n", ZSTR_VAL(execute_data->func->common.function_name)); + //} + wraprec = nr_php_get_wraprec(execute_data->func); + if (wraprec == NULL) { + if (0 == NRINI(tt_detail)) { + handlers.begin = nr_php_observer_empty_fcall_begin; + handlers.end = nr_php_observer_empty_fcall_end; + return handlers; + } else { + handlers.begin = nr_php_observer_fcall_begin; + handlers.end = nr_php_observer_fcall_end; + return handlers; + } + } + handlers.begin = wraprec->special_instrumentation_before ? + (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : + nr_php_observer_fcall_begin_instrumented; + handlers.end = wraprec->special_instrumentation ? + (zend_observer_fcall_end_handler)wraprec->special_instrumentation : + wraprec->create_metric ? nr_php_observer_fcall_end_create_metric: + nr_php_observer_fcall_end; + return handlers; +} +#endif + void nr_php_observer_no_op(zend_execute_data* execute_data NRUNUSED){}; diff --git a/agent/php_observer.h b/agent/php_observer.h index 6a80873cb..69dbeb315 100644 --- a/agent/php_observer.h +++ b/agent/php_observer.h @@ -76,6 +76,20 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, zval* func_return_value); +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +// These empty functions (rather than NULL) are used to know if instrumentation +// has been added This is needed because the process for adding instrumentation +// with a transient wrapper differs depending on if the function has been +// previously called. These will only be used when tt_detail is 0. +void nr_php_observer_empty_fcall_begin(zend_execute_data* execute_data); +void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data); + +void nr_php_observer_empty_fcall_end(zend_execute_data* execute_data, + zval* func_return_value); +void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t txn_start_time); +void nr_php_observer_fcall_end_create_metric(zend_execute_data* execute_data, + zval* func_return_value); +#endif /* PHP 8.2+ */ #endif /* PHP8+ */ #endif // NEWRELIC_PHP_AGENT_PHP_OBSERVER_H diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index c7fab76d0..8ee97004c 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -62,6 +62,24 @@ int nr_zend_call_orig_execute(NR_EXECUTE_PROTO TSRMLS_DC) { return zcaught; } #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO /* PHP8+ */ +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO /* PHP8+ */ +int nr_zend_call_oapi_special_before(nruserfn_t* wraprec, + nr_segment_t* segment, + NR_EXECUTE_PROTO) { + volatile int zcaught = 0; + (void)segment; + + if (wraprec && wraprec->special_instrumentation_before) { + zend_try { + wraprec->special_instrumentation_before(NR_EXECUTE_ORIG_ARGS); + } + zend_catch { zcaught = 1; } + zend_end_try(); + } + + return zcaught; +} +#else int nr_zend_call_oapi_special_before(nruserfn_t* wraprec, nr_segment_t* segment, NR_EXECUTE_PROTO) { @@ -79,15 +97,23 @@ int nr_zend_call_oapi_special_before(nruserfn_t* wraprec, return zcaught; } #endif +#endif int nr_zend_call_orig_execute_special(nruserfn_t* wraprec, nr_segment_t* segment, NR_EXECUTE_PROTO TSRMLS_DC) { +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO /* PHP8+ */ + (void)segment; +#endif volatile int zcaught = 0; NR_UNUSED_FUNC_RETURN_VALUE; zend_try { if (wraprec && wraprec->special_instrumentation) { +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO /* PHP8+ */ + wraprec->special_instrumentation(NR_EXECUTE_ORIG_ARGS); +#else wraprec->special_instrumentation(wraprec, segment, NR_EXECUTE_ORIG_ARGS TSRMLS_CC); +#endif } else { NR_PHP_PROCESS_GLOBALS(orig_execute) (NR_EXECUTE_ORIG_ARGS_OVERWRITE TSRMLS_CC); @@ -228,21 +254,21 @@ static void nr_php_wrap_zend_function(zend_function* func, } } -static void nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSRMLS_DC) { +static zend_function* nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSRMLS_DC) { zend_function* orig_func = 0; if (0 == NR_PHP_PROCESS_GLOBALS(done_instrumentation)) { - return; + return NULL; } if (wraprec->is_wrapped) { - return; + return NULL; } #if ZEND_MODULE_API_NO < ZEND_8_0_X_API_NO \ && defined OVERWRITE_ZEND_EXECUTE_DATA /* PHP8+ */ if (nrunlikely(-1 == NR_PHP_PROCESS_GLOBALS(zend_offset))) { - return; + return NULL; } #endif if (0 == wraprec->classname) { @@ -256,7 +282,7 @@ static void nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSRMLS_DC) { if (NULL == orig_func) { /* It could be in a file not yet loaded, no reason to log anything. */ - return; + return NULL; } if (ZEND_USER_FUNCTION != orig_func->type) { @@ -269,9 +295,10 @@ static void nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSRMLS_DC) { * logs with this message. */ wraprec->is_disabled = 1; - return; + return NULL; } nr_php_wrap_zend_function(orig_func, wraprec TSRMLS_CC); + return orig_func; } static nruserfn_t* nr_php_user_wraprec_create(void) { @@ -423,6 +450,10 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, size_t namestrlen) { nruserfn_t* wraprec; nruserfn_t* p; + zend_function* orig_func; +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + zend_observer_fcall_begin_handler *begin_handler; +#endif wraprec = nr_php_user_wraprec_create_named(namestr, namestrlen); if (0 == wraprec) { @@ -452,12 +483,36 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, NRP_PHP(wraprec->classname), (0 == wraprec->classname) ? "" : "::", NRP_PHP(wraprec->funcname)); - nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); + orig_func = nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); /* non-transient wraprecs are added to both the hashmap and linked list. * At request shutdown, the hashmap will free transients, but leave * non-transients to be freed when the linked list is disposed of which is at * module shutdown */ nr_php_add_custom_tracer_common(wraprec); +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + if (orig_func) { + // Before messing with our handlers, we must ensure that the observer fields of the function are initialized + begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(orig_func)->common), zend_observer_fcall_op_array_extension); + // begin_handler will be NULL if the observer hasn't been installed yet. + // *begin_Handler will be NULL if the function has not yet been called. + if (begin_handler && *begin_handler) { + if (zend_observer_remove_begin_handler(orig_func, NRINI(tt_detail) ? + nr_php_observer_fcall_begin : + nr_php_observer_empty_fcall_begin)) { + zend_observer_add_begin_handler(orig_func, wraprec->special_instrumentation_before ? + (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : + nr_php_observer_fcall_begin_instrumented); + } + if (zend_observer_remove_end_handler(orig_func, NRINI(tt_detail) ? + nr_php_observer_fcall_end : + nr_php_observer_empty_fcall_end)) { + zend_observer_add_end_handler(orig_func, wraprec->special_instrumentation ? + (zend_observer_fcall_end_handler)wraprec->special_instrumentation : + nr_php_observer_fcall_end); + } + } + } +#endif return wraprec; /* return the new wraprec */ } diff --git a/agent/php_user_instrument.h b/agent/php_user_instrument.h index 15fba7bdf..acae87dbb 100644 --- a/agent/php_user_instrument.h +++ b/agent/php_user_instrument.h @@ -19,12 +19,17 @@ struct _nruserfn_t; * This is an unused structure that is used to ensure that a bare return won't * compile in a user wrapper. */ +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +typedef void (*nrspecialfn_t)(zend_execute_data*, ...); + +#else struct nrspecialfn_return_t { int zcaught; }; typedef struct nrspecialfn_return_t (*nrspecialfn_t)( NR_SPECIALFNPTR_PROTO TSRMLS_DC); +#endif typedef void (*nruserfn_declared_t)(TSRMLS_D); /* diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index 21fa27f15..16a2d7090 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -140,6 +140,9 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, nrspecialfn_t callback TSRMLS_DC) { /* creates a transient wraprec */ nruserfn_t* wraprec = nr_php_add_custom_tracer_callable(callable TSRMLS_CC); +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + zend_observer_fcall_begin_handler *begin_handler; +#endif if (wraprec && callback) { if ((NULL != wraprec->special_instrumentation) @@ -150,6 +153,32 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, __func__); } else { wraprec->special_instrumentation = callback; +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + if (callable) { + // Before messing with our handlers, we must ensure that the observer fields of the function are initialized + begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(callable)->common), zend_observer_fcall_op_array_extension); + // begin_handler will be NULL if the observer hasn't been installed yet. + // *begin_Handler will be NULL if the function has not yet been called. + if (begin_handler && *begin_handler) { + // It is okay to attempt to remove a handler that doesn't exist + // TODO this could remove nr_php_observer_fcall_begin/end and then re-add it :) + if (zend_observer_remove_begin_handler(callable, NRINI(tt_detail) ? + nr_php_observer_fcall_begin : + nr_php_observer_empty_fcall_begin)) { + zend_observer_add_begin_handler(callable, wraprec->special_instrumentation_before ? + (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : + nr_php_observer_fcall_begin); + } + if (zend_observer_remove_end_handler(callable, NRINI(tt_detail) ? + nr_php_observer_fcall_end : + nr_php_observer_empty_fcall_end)) { + zend_observer_add_end_handler(callable, wraprec->special_instrumentation ? + (zend_observer_fcall_end_handler)wraprec->special_instrumentation : + nr_php_observer_fcall_end); + } + } + } +#endif } } diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index b87a5d9c0..486ab0e5c 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -252,9 +252,17 @@ extern void nr_php_scope_release(zval** ppzv); */ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO #define NR_PHP_WRAPPER_PROTOTYPE(name) \ struct nrspecialfn_return_t name(NR_SPECIALFNPTR_PROTO TSRMLS_DC) +#else + +#define NR_PHP_WRAPPER_PROTOTYPE(name) \ + void name(zend_execute_data* execute_data, ...) +#endif + +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO #define NR_PHP_WRAPPER_START(name) \ NR_PHP_WRAPPER_PROTOTYPE(name) { \ int was_executed = 0; \ @@ -264,14 +272,40 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); \ (void)auto_segment; // auto_segment isn't generally used in wrapper // functions +#else +#define NR_PHP_WRAPPER_START(name) \ + NR_PHP_WRAPPER_PROTOTYPE(name) { \ + int was_executed = 0; \ + int zcaught = 0; \ + bool is_begin = false; \ + nruserfn_t* wraprec = NULL; \ + zval* func_return_value = NULL; \ + zval** func_return_value_ptr = NULL; \ + const nrtxn_t* txn = NRPRG(txn); \ + const nrtime_t txn_start_time = nr_txn_start_time(txn); \ + \ + nr_segment_t* auto_segment = nr_txn_get_current_segment(NRPRG(txn), NULL); \ + if (!auto_segment || auto_segment->execute_data != execute_data) { \ + nr_php_observer_fcall_begin(execute_data); \ + auto_segment = nr_txn_get_current_segment(NRPRG(txn), NULL); \ + is_begin = true; \ + } else { \ + func_return_value_ptr = nr_php_get_return_value_ptr(); \ + func_return_value = func_return_value_ptr ? *func_return_value_ptr : NULL;\ + } +#endif #define NR_PHP_WRAPPER(name) static NR_PHP_WRAPPER_START(name) +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO #define NR_PHP_WRAPPER_END \ callback_end: \ __attribute__((unused)); \ if (!was_executed) { \ NR_PHP_WRAPPER_CALL \ + } \ + if (!is_begin) { \ + nr_php_observer_fcall_end(execute_data, func_return_value); \ } \ \ if (zcaught) { \ @@ -281,7 +315,27 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); struct nrspecialfn_return_t _retval = {zcaught}; \ return _retval; \ } \ - } +} +#else +#define NR_PHP_WRAPPER_END \ + callback_end: \ + __attribute__((unused)); \ + if (!was_executed) { \ + NR_PHP_WRAPPER_CALL \ + } \ + if (!is_begin) { \ + func_return_value_ptr = nr_php_get_return_value_ptr(); \ + nr_php_observer_fcall_end(execute_data, \ + func_return_value_ptr ? *func_return_value_ptr : NULL); \ + } else { \ + nr_php_observer_fcall_begin_late(execute_data, txn_start_time);\ + } \ + if (zcaught) { \ + zend_bailout(); \ + } \ +} +#endif + #define NR_PHP_WRAPPER_CALL \ if (!was_executed) { \ @@ -326,11 +380,19 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); } \ } while (0) +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO #define NR_PHP_WRAPPER_DELEGATE(name) \ if (!was_executed) { \ zcaught = ((name)(NR_SPECIALFNPTR_ORIG_ARGS TSRMLS_CC)).zcaught; \ was_executed = 1; \ } +#else +#define NR_PHP_WRAPPER_DELEGATE(name) \ + if (!was_executed) { \ + ((name)(execute_data)); \ + was_executed = 1; \ + } +#endif static inline bool is_instrumentation_set_and_not_equal( nrspecialfn_t instrumentation, diff --git a/axiom/nr_segment.h b/axiom/nr_segment.h index 56d972579..70fd2d3c3 100644 --- a/axiom/nr_segment.h +++ b/axiom/nr_segment.h @@ -182,7 +182,10 @@ typedef struct _nr_segment_t { external or datastore segments. */ nr_segment_error_t* error; /* segment error attributes */ -#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + void* execute_data; + +#elif ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA /* PHP 8.0+ and OAPI */ /* diff --git a/daemon/cmd/integration_runner/main.go b/daemon/cmd/integration_runner/main.go index f1a65a402..6edea1394 100644 --- a/daemon/cmd/integration_runner/main.go +++ b/daemon/cmd/integration_runner/main.go @@ -532,7 +532,7 @@ func runTest(t *integration.Test) { if err != nil { t.Output = body - t.Fatal(fmt.Errorf("error executing skipif: %v", err)) + t.Fatal(fmt.Errorf("error executing skipif: %v %v", err, skipIf)) return } diff --git a/tests/integration/span_events/test_span_events_max_samples_stored1.php b/tests/integration/span_events/test_span_events_max_samples_stored1.php index 93807d478..e6937bab5 100644 --- a/tests/integration/span_events/test_span_events_max_samples_stored1.php +++ b/tests/integration/span_events/test_span_events_max_samples_stored1.php @@ -29,7 +29,7 @@ newrelic_add_custom_tracer('main'); function main() { - usleep(10); + usleep(1); } $sample_size = 10000; diff --git a/tests/integration/span_events/test_span_events_on_dt_on.php b/tests/integration/span_events/test_span_events_on_dt_on.php index e94a4570a..d89c0e65c 100644 --- a/tests/integration/span_events/test_span_events_on_dt_on.php +++ b/tests/integration/span_events/test_span_events_on_dt_on.php @@ -76,6 +76,9 @@ Hello */ +if (version_compare(PHP_VERSION, "7.0", "<")) { + die("skip: CLM for PHP 5 not supported\n"); +} newrelic_add_custom_tracer('main'); function main() { From 1a90428dc9b7cb11542b1c41adb1f0eb5a1c2649 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 1 May 2024 13:09:54 -0600 Subject: [PATCH 24/45] consistent timings --- agent/php_wrapper.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index 486ab0e5c..1b64e3360 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -292,6 +292,8 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); } else { \ func_return_value_ptr = nr_php_get_return_value_ptr(); \ func_return_value = func_return_value_ptr ? *func_return_value_ptr : NULL;\ + nr_php_observer_fcall_end(execute_data, \ + func_return_value_ptr ? *func_return_value_ptr : NULL); \ } #endif @@ -303,9 +305,6 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); __attribute__((unused)); \ if (!was_executed) { \ NR_PHP_WRAPPER_CALL \ - } \ - if (!is_begin) { \ - nr_php_observer_fcall_end(execute_data, func_return_value); \ } \ \ if (zcaught) { \ @@ -323,11 +322,7 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); if (!was_executed) { \ NR_PHP_WRAPPER_CALL \ } \ - if (!is_begin) { \ - func_return_value_ptr = nr_php_get_return_value_ptr(); \ - nr_php_observer_fcall_end(execute_data, \ - func_return_value_ptr ? *func_return_value_ptr : NULL); \ - } else { \ + if (is_begin) { \ nr_php_observer_fcall_begin_late(execute_data, txn_start_time);\ } \ if (zcaught) { \ From 942f0e2e2ebd6fd1f5ac84c634bb7d0ccddace00 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Thu, 2 May 2024 08:31:50 -0600 Subject: [PATCH 25/45] fixups --- agent/fw_wordpress.c | 4 ++++ agent/php_execute.c | 8 +++++++- agent/php_user_instrument.c | 6 +++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/agent/fw_wordpress.c b/agent/fw_wordpress.c index cd905fc69..55662f3d1 100644 --- a/agent/fw_wordpress.c +++ b/agent/fw_wordpress.c @@ -747,7 +747,11 @@ NR_PHP_WRAPPER(nr_wordpress_apply_filters_after) { nr_wordpress_name_the_wt(tag, retval_ptr TSRMLS_CC); } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO nr_wordpress_handle_tag_stack_after(execute_data); +#else + nr_wordpress_handle_tag_stack_after(NR_SPECIALFNPTR_ORIG_ARGS); +#endif } NR_PHP_WRAPPER_END #endif /* OAPI */ diff --git a/agent/php_execute.c b/agent/php_execute.c index 66601635a..6ef2a4c56 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -1984,7 +1984,7 @@ static void nr_php_instrument_func_begin(NR_EXECUTE_PROTO) { if (wraprec->is_names_wt_simple) { nr_txn_name_from_function(NRPRG(txn), - nr_php_op_array_function_name(NR_OP_ARRAY); + nr_php_op_array_function_name(NR_OP_ARRAY), nr_php_class_entry_name(NR_OP_ARRAY->scope)); } #endif @@ -2217,6 +2217,7 @@ void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { return; } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { /* * Instrument the function. @@ -2257,6 +2258,7 @@ void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { return; } +#endif void nr_php_observer_fcall_end(zend_execute_data* execute_data, zval* func_return_value) { @@ -2285,7 +2287,11 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, nr_php_show_exec_return(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, false); +#else + nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS); +#endif } NRPRG(php_cur_stack_depth) -= 1; diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index 8ee97004c..62b9fb942 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -450,8 +450,8 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, size_t namestrlen) { nruserfn_t* wraprec; nruserfn_t* p; - zend_function* orig_func; #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + zend_function* orig_func; zend_observer_fcall_begin_handler *begin_handler; #endif @@ -483,7 +483,11 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, NRP_PHP(wraprec->classname), (0 == wraprec->classname) ? "" : "::", NRP_PHP(wraprec->funcname)); +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO orig_func = nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); +#else + nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); +#endif /* non-transient wraprecs are added to both the hashmap and linked list. * At request shutdown, the hashmap will free transients, but leave * non-transients to be freed when the linked list is disposed of which is at From 8c97ad448a0bfcb12381d4b6ba3d40e590ba739f Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Thu, 2 May 2024 08:42:37 -0600 Subject: [PATCH 26/45] fixups --- agent/fw_drupal_common.c | 2 ++ agent/fw_wordpress.c | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/agent/fw_drupal_common.c b/agent/fw_drupal_common.c index 5fe24f5e3..c09aac3d6 100644 --- a/agent/fw_drupal_common.c +++ b/agent/fw_drupal_common.c @@ -60,7 +60,9 @@ NR_PHP_WRAPPER(nr_drupal_wrap_module_hook) { * function such as a_b_c is ambiguous (is the module a or a_b?). Instead, * we'll see if they're defined in the wraprec. */ +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO wraprec = nr_php_get_wraprec(execute_data->func); +#endif if ((NULL != wraprec->drupal_hook) && (NULL != wraprec->drupal_module)) { nr_drupal_create_metric(auto_segment, NR_PSTR(NR_DRUPAL_MODULE_PREFIX), wraprec->drupal_module, wraprec->drupal_module_len); diff --git a/agent/fw_wordpress.c b/agent/fw_wordpress.c index 55662f3d1..479f0e657 100644 --- a/agent/fw_wordpress.c +++ b/agent/fw_wordpress.c @@ -357,7 +357,6 @@ NR_PHP_WRAPPER(nr_wordpress_wrap_hook_plugin) { char* plugin = NULL; NR_UNUSED_SPECIALFN; - (void)wraprec; /* * We only want to hook the function being called if this is a WordPress @@ -370,7 +369,10 @@ NR_PHP_WRAPPER(nr_wordpress_wrap_hook_plugin) { if ((0 == NRINI(wordpress_hooks)) || (NULL == tag)) { NR_PHP_WRAPPER_LEAVE; } - plugin = nr_wordpress_plugin_from_function(execute_data->func); + // Use optimized wraprec hashmap over plugin hashmap + wraprec = nr_php_get_wraprec(execute_data->func); + plugin = wraprec->wordpress_plugin_theme; + //plugin = nr_wordpress_plugin_from_function(execute_data->func); NR_PHP_WRAPPER_CALL; if (NULL != plugin) { From d596581ce190722c814b4c82d89606eae1ed045a Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Fri, 3 May 2024 11:57:44 -0600 Subject: [PATCH 27/45] fix instrumented function metric --- agent/php_observer.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/agent/php_observer.c b/agent/php_observer.c index 50ebbad26..cb23d566f 100644 --- a/agent/php_observer.c +++ b/agent/php_observer.c @@ -115,12 +115,15 @@ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( } } handlers.begin = wraprec->special_instrumentation_before ? - (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : - nr_php_observer_fcall_begin_instrumented; + (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : + wraprec->is_transient ? + nr_php_observer_fcall_begin : + nr_php_observer_fcall_begin_instrumented; handlers.end = wraprec->special_instrumentation ? - (zend_observer_fcall_end_handler)wraprec->special_instrumentation : - wraprec->create_metric ? nr_php_observer_fcall_end_create_metric: - nr_php_observer_fcall_end; + (zend_observer_fcall_end_handler)wraprec->special_instrumentation : + wraprec->create_metric ? + nr_php_observer_fcall_end_create_metric : + nr_php_observer_fcall_end; return handlers; } #endif From b983eb7c6b1a1e15794aeb96b53f82ed81b5a73f Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Tue, 7 May 2024 09:41:53 -0600 Subject: [PATCH 28/45] fix transient wrapper creation --- agent/php_wrapper.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index 16a2d7090..f77e9e808 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -84,6 +84,9 @@ nruserfn_t* nr_php_wrap_callable_before_after( nrspecialfn_t before_callback, nrspecialfn_t after_callback) { char* name = NULL; +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + zend_observer_fcall_begin_handler *begin_handler; +#endif /* creates a transient wraprec */ nruserfn_t* wraprec = nr_php_add_custom_tracer_callable(callable TSRMLS_CC); @@ -100,6 +103,32 @@ nruserfn_t* nr_php_wrap_callable_before_after( if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT) && NULL != name) { nr_free(name); } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + if (callable) { + // Before messing with our handlers, we must ensure that the observer fields of the function are initialized + begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(callable)->common), zend_observer_fcall_op_array_extension); + // begin_handler will be NULL if the observer hasn't been installed yet. + // *begin_Handler will be NULL if the function has not yet been called. + if (begin_handler && *begin_handler) { + // It is okay to attempt to remove a handler that doesn't exist + // TODO this could remove nr_php_observer_fcall_begin/end and then re-add it :) + if (zend_observer_remove_begin_handler(callable, NRINI(tt_detail) ? + nr_php_observer_fcall_begin : + nr_php_observer_empty_fcall_begin)) { + zend_observer_add_begin_handler(callable, wraprec->special_instrumentation_before ? + (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : + nr_php_observer_fcall_begin); + } + if (zend_observer_remove_end_handler(callable, NRINI(tt_detail) ? + nr_php_observer_fcall_end : + nr_php_observer_empty_fcall_end)) { + zend_observer_add_end_handler(callable, wraprec->special_instrumentation ? + (zend_observer_fcall_end_handler)wraprec->special_instrumentation : + nr_php_observer_fcall_end); + } + } + } +#endif return wraprec; } From beede69ae7967147b316bd4df68b83a94cd55942 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 8 May 2024 11:44:23 -0600 Subject: [PATCH 29/45] fix double segments --- agent/fw_wordpress.c | 8 ++--- agent/php_execute.c | 73 +++++++++++++++++++++++++++++++++++++++++--- agent/php_newrelic.h | 5 +++ agent/php_observer.h | 3 ++ agent/php_rinit.c | 4 +++ agent/php_wrapper.h | 14 +++++++-- 6 files changed, 94 insertions(+), 13 deletions(-) diff --git a/agent/fw_wordpress.c b/agent/fw_wordpress.c index 479f0e657..af38e0be7 100644 --- a/agent/fw_wordpress.c +++ b/agent/fw_wordpress.c @@ -749,11 +749,9 @@ NR_PHP_WRAPPER(nr_wordpress_apply_filters_after) { nr_wordpress_name_the_wt(tag, retval_ptr TSRMLS_CC); } -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - nr_wordpress_handle_tag_stack_after(execute_data); -#else - nr_wordpress_handle_tag_stack_after(NR_SPECIALFNPTR_ORIG_ARGS); -#endif + if (0 != NRINI(wordpress_hooks)) { + clean_wordpress_tag_stack(auto_segment); + } } NR_PHP_WRAPPER_END #endif /* OAPI */ diff --git a/agent/php_execute.c b/agent/php_execute.c index 6ef2a4c56..a08f6f454 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -2031,16 +2031,40 @@ void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t } #endif +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +void nr_php_observer_fcall_end_late(zend_execute_data* execute_data, bool create_metric, nrtime_t txn_start_time) { + nr_segment_t* segment; + nr_php_execute_metadata_t metadata = {0}; + if (nrunlikely(nr_txn_start_time(NRPRG(txn)) != txn_start_time)) { + nrl_verbosedebug(NRL_AGENT, + "%s txn ended and/or started while in a wrapped function", + __func__); + + return; + } + + /* + * Reassign segment to the current segment, as some before/after wraprecs + * start and then stop a segment. If that happened, we want to ensure we + * get the now-current segment + */ + segment = nr_txn_get_current_segment(NRPRG(txn), NULL); + nr_php_execute_metadata_init(&metadata, NR_OP_ARRAY); + nr_php_execute_segment_end(segment, &metadata, create_metric); + nr_php_execute_metadata_release(&metadata); +} +#endif + #if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { int zcaught = 0; nruserfn_t* wraprec = NULL; bool create_metric = false; + nr_php_execute_metadata_t metadata = {0}; #else -static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric) { +static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, bool end_segment) { #endif nr_segment_t* segment = NULL; - nr_php_execute_metadata_t metadata = {0}; nrtime_t txn_start_time = 0; if (NULL == NRPRG(txn)) { @@ -2143,7 +2167,6 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric) { nr_segment_discard(&segment); return; } -#endif /* * During nr_zend_call_orig_execute_special, the transaction may have been * ended and/or a new transaction may have started. To detect this, we @@ -2171,6 +2194,11 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric) { nr_php_execute_metadata_init(&metadata, NR_OP_ARRAY); nr_php_execute_segment_end(segment, &metadata, create_metric); nr_php_execute_metadata_release(&metadata); +#else + if (end_segment) { + nr_php_observer_fcall_end_late(execute_data, create_metric, txn_start_time); + } +#endif return; } @@ -2288,7 +2316,7 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, } #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, false); + nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, false, true); #else nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS); #endif @@ -2304,6 +2332,41 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, // has been added This is needed because the process for adding instrumentation // with a transient wrapper differs depending on if the function has been // previously called. These will only be used when tt_detail is 0. +void nr_php_observer_fcall_end_keep_segment(zend_execute_data* execute_data, + zval* func_return_value) { + /* + * Instrument the function. + * This and any other needed helper functions will replace: + * nr_php_execute_enabled + * nr_php_execute + * nr_php_execute_show + */ + if (nrunlikely(NULL == execute_data)) { + return; + } + //if (execute_data->func && execute_data->func->common.function_name) { + // printf("END %s\n", ZSTR_VAL(execute_data->func->common.function_name)); + //} + + if (nrlikely(1 == nr_php_recording())) { + int show_executes_return + = NR_PHP_PROCESS_GLOBALS(special_flags).show_execute_returns; + + if (nrunlikely(show_executes_return)) { + nrl_verbosedebug(NRL_AGENT, + "Stack depth: %d before OAPI function exiting via %s", + NRPRG(php_cur_stack_depth), __func__); + nr_php_show_exec_return(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); + } + + nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, false, false); + } + + NRPRG(php_cur_stack_depth) -= 1; + + return; +} + void nr_php_observer_empty_fcall_begin(zend_execute_data* execute_data) { (void)execute_data; } @@ -2338,7 +2401,7 @@ void nr_php_observer_fcall_end_create_metric(zend_execute_data* execute_data, nr_php_show_exec_return(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); } - nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, true); + nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, true, true); } NRPRG(php_cur_stack_depth) -= 1; diff --git a/agent/php_newrelic.h b/agent/php_newrelic.h index 1e9b5453d..b507e18b4 100644 --- a/agent/php_newrelic.h +++ b/agent/php_newrelic.h @@ -450,6 +450,11 @@ int symfony1_in_dispatch; /* Whether we are currently within a int symfony1_in_error404; /* Whether we are currently within a sfError404Exception::printStackTrace() frame */ +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO \ + && !defined OVERWRITE_ZEND_EXECUTE_DATA +bool in_wrapper; +#endif + #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA bool check_cufa; diff --git a/agent/php_observer.h b/agent/php_observer.h index 69dbeb315..566a8c8fb 100644 --- a/agent/php_observer.h +++ b/agent/php_observer.h @@ -87,6 +87,9 @@ void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data); void nr_php_observer_empty_fcall_end(zend_execute_data* execute_data, zval* func_return_value); void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t txn_start_time); +void nr_php_observer_fcall_end_keep_segment(zend_execute_data* execute_data, + zval* func_return_value); +void nr_php_observer_fcall_end_late(zend_execute_data* execute_data, bool create_metric, nrtime_t txn_start_time); void nr_php_observer_fcall_end_create_metric(zend_execute_data* execute_data, zval* func_return_value); #endif /* PHP 8.2+ */ diff --git a/agent/php_rinit.c b/agent/php_rinit.c index d704f935e..5d8634292 100644 --- a/agent/php_rinit.c +++ b/agent/php_rinit.c @@ -126,6 +126,10 @@ PHP_RINIT_FUNCTION(newrelic) { NRPRG(predis_ctxs).dtor = str_stack_dtor; NRPRG(drupal_invoke_all_hooks).dtor = zval_stack_dtor; #endif +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO \ + && !defined OVERWRITE_ZEND_EXECUTE_DATA + NRPRG(in_wrapper) = false; +#endif NRPRG(mysql_last_conn) = NULL; NRPRG(pgsql_last_conn) = NULL; diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index 1b64e3360..a8dda89ba 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -283,16 +283,21 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); zval** func_return_value_ptr = NULL; \ const nrtxn_t* txn = NRPRG(txn); \ const nrtime_t txn_start_time = nr_txn_start_time(txn); \ + if (NRPRG(in_wrapper)) { \ + printf("AAHHHHHHHHHHH\n"); \ + } \ + NRPRG(in_wrapper) = true; \ \ nr_segment_t* auto_segment = nr_txn_get_current_segment(NRPRG(txn), NULL); \ - if (!auto_segment || auto_segment->execute_data != execute_data) { \ + if (!auto_segment || auto_segment->execute_data != execute_data || \ + auto_segment == NRPRG(txn)->segment_root) { \ nr_php_observer_fcall_begin(execute_data); \ auto_segment = nr_txn_get_current_segment(NRPRG(txn), NULL); \ is_begin = true; \ } else { \ func_return_value_ptr = nr_php_get_return_value_ptr(); \ func_return_value = func_return_value_ptr ? *func_return_value_ptr : NULL;\ - nr_php_observer_fcall_end(execute_data, \ + nr_php_observer_fcall_end_keep_segment(execute_data, \ func_return_value_ptr ? *func_return_value_ptr : NULL); \ } #endif @@ -324,7 +329,10 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); } \ if (is_begin) { \ nr_php_observer_fcall_begin_late(execute_data, txn_start_time);\ - } \ + } else { \ + nr_php_observer_fcall_end_late(execute_data, false, txn_start_time); \ + } \ + NRPRG(in_wrapper) = false; \ if (zcaught) { \ zend_bailout(); \ } \ From 9810417209742424a6fe591921b7df81784f578d Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Thu, 9 May 2024 07:56:35 -0600 Subject: [PATCH 30/45] fix guzzle --- agent/lib_guzzle4.c | 13 +++++++++++++ agent/lib_guzzle4.h | 4 ++++ agent/lib_guzzle6.c | 12 ++++++++++++ agent/lib_guzzle6.h | 4 ++++ agent/php_wrapper.h | 7 ++----- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/agent/lib_guzzle4.c b/agent/lib_guzzle4.c index c52ddffff..457054eb4 100644 --- a/agent/lib_guzzle4.c +++ b/agent/lib_guzzle4.c @@ -438,22 +438,33 @@ const zend_function_entry nr_guzzle4_subscriber_functions[] * Purpose : Registers an event subscriber for a newly instantiated * GuzzleHttp\Client object. */ + +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +void nr_guzzle4_client_construct(NR_EXECUTE_PROTO) { +#else NR_PHP_WRAPPER_START(nr_guzzle4_client_construct) { +#endif zval* emitter = NULL; zval* retval = NULL; zval* subscriber = NULL; zval* this_var = nr_php_scope_get(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO (void)wraprec; +#endif NR_UNUSED_SPECIALFN; /* This is how we distinguish Guzzle 4/5 from other versions. */ if (0 == nr_guzzle_does_zval_implement_has_emitter(this_var TSRMLS_CC)) { +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO NR_PHP_WRAPPER_CALL; +#endif goto end; } +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO NR_PHP_WRAPPER_CALL; +#endif /* * We can't have newrelic\Guzzle4\Subscriber implement @@ -505,7 +516,9 @@ NR_PHP_WRAPPER_START(nr_guzzle4_client_construct) { nr_php_zval_free(&emitter); nr_php_zval_free(&subscriber); } +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO NR_PHP_WRAPPER_END +#endif void nr_guzzle4_enable(TSRMLS_D) { if (0 == NRINI(guzzle_enabled)) { diff --git a/agent/lib_guzzle4.h b/agent/lib_guzzle4.h index b1f58de99..c9eef3d4c 100644 --- a/agent/lib_guzzle4.h +++ b/agent/lib_guzzle4.h @@ -26,6 +26,10 @@ extern void nr_guzzle4_rshutdown(TSRMLS_D); /* * Purpose : Client::__construct() wrapper for Guzzle 4. */ +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO extern NR_PHP_WRAPPER_PROTOTYPE(nr_guzzle4_client_construct); +#else +extern void nr_guzzle4_client_construct(NR_EXECUTE_PROTO); +#endif #endif /* LIB_GUZZLE4_HDR */ diff --git a/agent/lib_guzzle6.c b/agent/lib_guzzle6.c index e65a684b7..54fe17950 100644 --- a/agent/lib_guzzle6.c +++ b/agent/lib_guzzle6.c @@ -343,7 +343,11 @@ const zend_function_entry nr_guzzle6_requesthandler_functions[] /* }}} */ +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +void nr_guzzle6_client_construct(NR_EXECUTE_PROTO) { +#else NR_PHP_WRAPPER_START(nr_guzzle6_client_construct) { +#endif zval* config; zend_class_entry* guzzle_client_ce; zval* handler_stack; @@ -380,16 +384,22 @@ NR_PHP_WRAPPER_START(nr_guzzle6_client_construct) { version); nr_free(version); +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO (void)wraprec; +#endif NR_UNUSED_SPECIALFN; /* This is how we distinguish Guzzle 4/5. */ if (nr_guzzle_does_zval_implement_has_emitter(this_var TSRMLS_CC)) { +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO NR_PHP_WRAPPER_CALL; +#endif goto end; } +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO NR_PHP_WRAPPER_CALL; +#endif /* * Get our middleware callable (which is just a string), and make sure it's @@ -436,7 +446,9 @@ NR_PHP_WRAPPER_START(nr_guzzle6_client_construct) { nr_php_zval_free(&middleware); nr_php_scope_release(&this_var); } +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO NR_PHP_WRAPPER_END +#endif void nr_guzzle6_enable(TSRMLS_D) { int retval; diff --git a/agent/lib_guzzle6.h b/agent/lib_guzzle6.h index 67bff7ecf..e66c97915 100644 --- a/agent/lib_guzzle6.h +++ b/agent/lib_guzzle6.h @@ -20,6 +20,10 @@ extern void nr_guzzle6_minit(TSRMLS_D); /* * Purpose : Client::__construct() wrapper for Guzzle 6. */ +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO extern NR_PHP_WRAPPER_PROTOTYPE(nr_guzzle6_client_construct); +#else +extern void nr_guzzle6_client_construct(NR_EXECUTE_PROTO); +#endif #endif /* LIB_GUZZLE4_HDR */ diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index a8dda89ba..d374046f9 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -284,7 +284,7 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); const nrtxn_t* txn = NRPRG(txn); \ const nrtime_t txn_start_time = nr_txn_start_time(txn); \ if (NRPRG(in_wrapper)) { \ - printf("AAHHHHHHHHHHH\n"); \ + printf("AAHHHHHHHHHHH %s\n", #name); \ } \ NRPRG(in_wrapper) = true; \ \ @@ -391,10 +391,7 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); } #else #define NR_PHP_WRAPPER_DELEGATE(name) \ - if (!was_executed) { \ - ((name)(execute_data)); \ - was_executed = 1; \ - } + ((name)(execute_data, func_return_value)); #endif static inline bool is_instrumentation_set_and_not_equal( From 5c58d760766db139caacefb5e85398bffe82e035 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Fri, 10 May 2024 10:54:01 -0600 Subject: [PATCH 31/45] fix return value stuff --- agent/fw_wordpress.c | 2 -- agent/php_execute.c | 10 ++++++++-- agent/php_wrapper.h | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/agent/fw_wordpress.c b/agent/fw_wordpress.c index af38e0be7..1f3855e94 100644 --- a/agent/fw_wordpress.c +++ b/agent/fw_wordpress.c @@ -346,7 +346,6 @@ NR_PHP_WRAPPER(nr_wordpress_wrap_hook_core) { NR_PHP_WRAPPER_LEAVE; } - NR_PHP_WRAPPER_CALL; if (NRPRG(wordpress_core)) { nr_wordpress_create_metric(auto_segment, NR_WORDPRESS_HOOK_PREFIX, tag); } @@ -374,7 +373,6 @@ NR_PHP_WRAPPER(nr_wordpress_wrap_hook_plugin) { plugin = wraprec->wordpress_plugin_theme; //plugin = nr_wordpress_plugin_from_function(execute_data->func); - NR_PHP_WRAPPER_CALL; if (NULL != plugin) { nr_wordpress_create_metric(auto_segment, NR_WORDPRESS_HOOK_PREFIX, tag); nr_wordpress_create_metric(auto_segment, NR_WORDPRESS_PLUGIN_PREFIX, diff --git a/agent/php_execute.c b/agent/php_execute.c index a08f6f454..b20909707 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -2118,7 +2118,7 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, boo "Uncaught exception ", &NRPRG(exception_filters) TSRMLS_CC); } else if (NULL == nr_php_get_return_value(NR_EXECUTE_ORIG_ARGS)) { #else - if (NULL == nr_php_get_return_value(NR_EXECUTE_ORIG_ARGS)) { + if (NULL == func_return_value) { #endif /* * Having no return value (and not being an exception handler) indicates @@ -2130,6 +2130,9 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, boo nr_status_t status = nr_php_error_record_exception_segment( NRPRG(txn), &exception, &NRPRG(exception_filters)); + if (execute_data->func && execute_data->func->common.function_name) { + nrl_verbosedebug(NRL_AGENT, "END %s", ZSTR_VAL(execute_data->func->common.function_name)); + } if (NR_FAILURE == status) { nrl_verbosedebug(NRL_AGENT, "%s: unable to record exception on segment", @@ -2214,6 +2217,9 @@ void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { //if (execute_data->func && execute_data->func->common.function_name) { // printf("BEGIN %s\n", ZSTR_VAL(execute_data->func->common.function_name)); //} + if (execute_data->func && execute_data->func->common.function_name) { + nrl_verbosedebug(NRL_AGENT, "BEGIN %s", ZSTR_VAL(execute_data->func->common.function_name)); + } if (nrunlikely(NULL == execute_data)) { return; } @@ -2256,7 +2262,7 @@ void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { */ zval* func_return_value = NULL; //if (execute_data->func && execute_data->func->common.function_name) { - // printf("BEGIN %s\n", ZSTR_VAL(execute_data->func->common.function_name)); + // nrl_verbosedebug(NRL_AGENT, "BEGIN %s", ZSTR_VAL(execute_data->func->common.function_name)); //} if (nrunlikely(NULL == execute_data)) { return; diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index d374046f9..107df82de 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -280,7 +280,6 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); bool is_begin = false; \ nruserfn_t* wraprec = NULL; \ zval* func_return_value = NULL; \ - zval** func_return_value_ptr = NULL; \ const nrtxn_t* txn = NRPRG(txn); \ const nrtime_t txn_start_time = nr_txn_start_time(txn); \ if (NRPRG(in_wrapper)) { \ @@ -295,10 +294,11 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); auto_segment = nr_txn_get_current_segment(NRPRG(txn), NULL); \ is_begin = true; \ } else { \ - func_return_value_ptr = nr_php_get_return_value_ptr(); \ - func_return_value = func_return_value_ptr ? *func_return_value_ptr : NULL;\ + va_list ptr; \ + va_start(ptr, execute_data); \ + func_return_value = va_arg(ptr, zval*); \ nr_php_observer_fcall_end_keep_segment(execute_data, \ - func_return_value_ptr ? *func_return_value_ptr : NULL); \ + func_return_value); \ } #endif From 7ad1ab234727bdd5f531a7cd639822c5dd002f6d Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Tue, 14 May 2024 14:30:21 -0600 Subject: [PATCH 32/45] fix wrapper tracking --- agent/php_execute.c | 8 +------- agent/php_newrelic.h | 5 ----- agent/php_rinit.c | 5 +---- agent/php_wrapper.c | 3 +++ agent/php_wrapper.h | 22 ++++++++-------------- 5 files changed, 13 insertions(+), 30 deletions(-) diff --git a/agent/php_execute.c b/agent/php_execute.c index b20909707..1fc6afb85 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -2130,9 +2130,6 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, boo nr_status_t status = nr_php_error_record_exception_segment( NRPRG(txn), &exception, &NRPRG(exception_filters)); - if (execute_data->func && execute_data->func->common.function_name) { - nrl_verbosedebug(NRL_AGENT, "END %s", ZSTR_VAL(execute_data->func->common.function_name)); - } if (NR_FAILURE == status) { nrl_verbosedebug(NRL_AGENT, "%s: unable to record exception on segment", @@ -2217,9 +2214,6 @@ void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { //if (execute_data->func && execute_data->func->common.function_name) { // printf("BEGIN %s\n", ZSTR_VAL(execute_data->func->common.function_name)); //} - if (execute_data->func && execute_data->func->common.function_name) { - nrl_verbosedebug(NRL_AGENT, "BEGIN %s", ZSTR_VAL(execute_data->func->common.function_name)); - } if (nrunlikely(NULL == execute_data)) { return; } @@ -2262,7 +2256,7 @@ void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { */ zval* func_return_value = NULL; //if (execute_data->func && execute_data->func->common.function_name) { - // nrl_verbosedebug(NRL_AGENT, "BEGIN %s", ZSTR_VAL(execute_data->func->common.function_name)); + // printf("BEGIN %s", ZSTR_VAL(execute_data->func->common.function_name)); //} if (nrunlikely(NULL == execute_data)) { return; diff --git a/agent/php_newrelic.h b/agent/php_newrelic.h index b507e18b4..1e9b5453d 100644 --- a/agent/php_newrelic.h +++ b/agent/php_newrelic.h @@ -450,11 +450,6 @@ int symfony1_in_dispatch; /* Whether we are currently within a int symfony1_in_error404; /* Whether we are currently within a sfError404Exception::printStackTrace() frame */ -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO \ - && !defined OVERWRITE_ZEND_EXECUTE_DATA -bool in_wrapper; -#endif - #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ && !defined OVERWRITE_ZEND_EXECUTE_DATA bool check_cufa; diff --git a/agent/php_rinit.c b/agent/php_rinit.c index 5d8634292..922e4a884 100644 --- a/agent/php_rinit.c +++ b/agent/php_rinit.c @@ -124,12 +124,9 @@ PHP_RINIT_FUNCTION(newrelic) { nr_stack_init(&NRPRG(drupal_invoke_all_hooks), NR_STACK_DEFAULT_CAPACITY); nr_stack_init(&NRPRG(drupal_invoke_all_states), NR_STACK_DEFAULT_CAPACITY); NRPRG(predis_ctxs).dtor = str_stack_dtor; + NRPRG(wordpress_tags).dtor = str_stack_dtor; NRPRG(drupal_invoke_all_hooks).dtor = zval_stack_dtor; #endif -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO \ - && !defined OVERWRITE_ZEND_EXECUTE_DATA - NRPRG(in_wrapper) = false; -#endif NRPRG(mysql_last_conn) = NULL; NRPRG(pgsql_last_conn) = NULL; diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index f77e9e808..29bc98250 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -110,6 +110,9 @@ nruserfn_t* nr_php_wrap_callable_before_after( // begin_handler will be NULL if the observer hasn't been installed yet. // *begin_Handler will be NULL if the function has not yet been called. if (begin_handler && *begin_handler) { + name = nr_php_function_debug_name(callable); + php_printf("AHHHHHHH %s\n", name); + nr_free(name); // It is okay to attempt to remove a handler that doesn't exist // TODO this could remove nr_php_observer_fcall_begin/end and then re-add it :) if (zend_observer_remove_begin_handler(callable, NRINI(tt_detail) ? diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index 107df82de..a5ec68f62 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -276,27 +276,22 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); #define NR_PHP_WRAPPER_START(name) \ NR_PHP_WRAPPER_PROTOTYPE(name) { \ int was_executed = 0; \ + bool in_begin = true;\ int zcaught = 0; \ - bool is_begin = false; \ nruserfn_t* wraprec = NULL; \ zval* func_return_value = NULL; \ const nrtxn_t* txn = NRPRG(txn); \ const nrtime_t txn_start_time = nr_txn_start_time(txn); \ - if (NRPRG(in_wrapper)) { \ - printf("AAHHHHHHHHHHH %s\n", #name); \ - } \ - NRPRG(in_wrapper) = true; \ \ nr_segment_t* auto_segment = nr_txn_get_current_segment(NRPRG(txn), NULL); \ - if (!auto_segment || auto_segment->execute_data != execute_data || \ - auto_segment == NRPRG(txn)->segment_root) { \ + if (!auto_segment || auto_segment->execute_data != execute_data) { \ nr_php_observer_fcall_begin(execute_data); \ - auto_segment = nr_txn_get_current_segment(NRPRG(txn), NULL); \ - is_begin = true; \ } else { \ - va_list ptr; \ - va_start(ptr, execute_data); \ - func_return_value = va_arg(ptr, zval*); \ + va_list args; \ + va_start(args, execute_data); \ + func_return_value = va_arg(args, zval*); \ + va_end(args); \ + in_begin = false; \ nr_php_observer_fcall_end_keep_segment(execute_data, \ func_return_value); \ } @@ -327,12 +322,11 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); if (!was_executed) { \ NR_PHP_WRAPPER_CALL \ } \ - if (is_begin) { \ + if (in_begin) { \ nr_php_observer_fcall_begin_late(execute_data, txn_start_time);\ } else { \ nr_php_observer_fcall_end_late(execute_data, false, txn_start_time); \ } \ - NRPRG(in_wrapper) = false; \ if (zcaught) { \ zend_bailout(); \ } \ From bd0df11abe9dbfcc989ffe5305d9e869341dd466 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 15 May 2024 09:03:53 -0600 Subject: [PATCH 33/45] try not using zend lookup --- agent/php_user_instrument.c | 2 +- agent/php_wrapper.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index 62b9fb942..b607e24eb 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -493,7 +493,7 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, * non-transients to be freed when the linked list is disposed of which is at * module shutdown */ nr_php_add_custom_tracer_common(wraprec); -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO if (orig_func) { // Before messing with our handlers, we must ensure that the observer fields of the function are initialized begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(orig_func)->common), zend_observer_fcall_op_array_extension); diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index 29bc98250..6fd6f3c51 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -103,7 +103,7 @@ nruserfn_t* nr_php_wrap_callable_before_after( if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT) && NULL != name) { nr_free(name); } -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO if (callable) { // Before messing with our handlers, we must ensure that the observer fields of the function are initialized begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(callable)->common), zend_observer_fcall_op_array_extension); @@ -185,7 +185,7 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, __func__); } else { wraprec->special_instrumentation = callback; -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO if (callable) { // Before messing with our handlers, we must ensure that the observer fields of the function are initialized begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(callable)->common), zend_observer_fcall_op_array_extension); From 1cb4b168a42f718a1333c95c8285e52fa6a9c686 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 15 May 2024 09:07:48 -0600 Subject: [PATCH 34/45] fix --- agent/php_user_instrument.c | 4 ++-- agent/php_wrapper.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index b607e24eb..3e91d13f2 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -450,7 +450,7 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, size_t namestrlen) { nruserfn_t* wraprec; nruserfn_t* p; -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO zend_function* orig_func; zend_observer_fcall_begin_handler *begin_handler; #endif @@ -483,7 +483,7 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, NRP_PHP(wraprec->classname), (0 == wraprec->classname) ? "" : "::", NRP_PHP(wraprec->funcname)); -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO orig_func = nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); #else nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index 6fd6f3c51..62a230c47 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -84,7 +84,7 @@ nruserfn_t* nr_php_wrap_callable_before_after( nrspecialfn_t before_callback, nrspecialfn_t after_callback) { char* name = NULL; -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO zend_observer_fcall_begin_handler *begin_handler; #endif @@ -172,7 +172,7 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, nrspecialfn_t callback TSRMLS_DC) { /* creates a transient wraprec */ nruserfn_t* wraprec = nr_php_add_custom_tracer_callable(callable TSRMLS_CC); -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO zend_observer_fcall_begin_handler *begin_handler; #endif From cd274ada0e142c6d60688737b61f4b5ca7600770 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 15 May 2024 11:27:29 -0600 Subject: [PATCH 35/45] revert version change --- agent/php_user_instrument.c | 6 +++--- agent/php_wrapper.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index 3e91d13f2..62b9fb942 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -450,7 +450,7 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, size_t namestrlen) { nruserfn_t* wraprec; nruserfn_t* p; -#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO zend_function* orig_func; zend_observer_fcall_begin_handler *begin_handler; #endif @@ -483,7 +483,7 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, NRP_PHP(wraprec->classname), (0 == wraprec->classname) ? "" : "::", NRP_PHP(wraprec->funcname)); -#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO orig_func = nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); #else nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); @@ -493,7 +493,7 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, * non-transients to be freed when the linked list is disposed of which is at * module shutdown */ nr_php_add_custom_tracer_common(wraprec); -#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO if (orig_func) { // Before messing with our handlers, we must ensure that the observer fields of the function are initialized begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(orig_func)->common), zend_observer_fcall_op_array_extension); diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index 62a230c47..29bc98250 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -84,7 +84,7 @@ nruserfn_t* nr_php_wrap_callable_before_after( nrspecialfn_t before_callback, nrspecialfn_t after_callback) { char* name = NULL; -#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO zend_observer_fcall_begin_handler *begin_handler; #endif @@ -103,7 +103,7 @@ nruserfn_t* nr_php_wrap_callable_before_after( if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT) && NULL != name) { nr_free(name); } -#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO if (callable) { // Before messing with our handlers, we must ensure that the observer fields of the function are initialized begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(callable)->common), zend_observer_fcall_op_array_extension); @@ -172,7 +172,7 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, nrspecialfn_t callback TSRMLS_DC) { /* creates a transient wraprec */ nruserfn_t* wraprec = nr_php_add_custom_tracer_callable(callable TSRMLS_CC); -#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO zend_observer_fcall_begin_handler *begin_handler; #endif @@ -185,7 +185,7 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, __func__); } else { wraprec->special_instrumentation = callback; -#if ZEND_MODULE_API_NO >= ZEND_8_3_X_API_NO +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO if (callable) { // Before messing with our handlers, we must ensure that the observer fields of the function are initialized begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(callable)->common), zend_observer_fcall_op_array_extension); From a9656a8170fdb7bbd97823694d69ac08bf7f98a2 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 15 May 2024 14:42:59 -0600 Subject: [PATCH 36/45] TEMP remove metric --- agent/php_execute.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agent/php_execute.c b/agent/php_execute.c index 1fc6afb85..345c45c17 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -2010,6 +2010,7 @@ void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t return; } + if (false) { if (NR_OP_ARRAY->scope) { nr_txn_force_single_count(NRPRG(txn), nr_txn_create_fn_supportability_metric( nr_php_op_array_function_name(NR_OP_ARRAY), @@ -2019,6 +2020,7 @@ void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t nr_php_op_array_function_name(NR_OP_ARRAY), NULL)); } + } /* * Check for, and handle, frameworks. */ From 6515a43e10f155d8a52caed7948f17fb0ed9af2a Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Tue, 21 May 2024 13:48:49 -0600 Subject: [PATCH 37/45] cleanup dispatch functions --- agent/php_execute.c | 154 ++++++++++--------------------------------- agent/php_observer.h | 4 -- 2 files changed, 35 insertions(+), 123 deletions(-) diff --git a/agent/php_execute.c b/agent/php_execute.c index 345c45c17..564e7fc29 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -2002,6 +2002,7 @@ void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t * started transaction might actually obtain the same address as a * transaction freed before. */ + (void)execute_data; if (nrunlikely(nr_txn_start_time(NRPRG(txn)) != txn_start_time)) { nrl_verbosedebug(NRL_AGENT, "%s txn ended and/or started while in a wrapped function", @@ -2010,17 +2011,6 @@ void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t return; } - if (false) { - if (NR_OP_ARRAY->scope) { - nr_txn_force_single_count(NRPRG(txn), nr_txn_create_fn_supportability_metric( - nr_php_op_array_function_name(NR_OP_ARRAY), - nr_php_class_entry_name(NR_OP_ARRAY->scope))); - } else { - nr_txn_force_single_count(NRPRG(txn), nr_txn_create_fn_supportability_metric( - nr_php_op_array_function_name(NR_OP_ARRAY), - NULL)); - } - } /* * Check for, and handle, frameworks. */ @@ -2204,7 +2194,11 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, boo return; } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +static void nr_php_observer_fcall_begin_common(zend_execute_data* execute_data, bool call_late) { +#else void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { +#endif /* * Instrument the function. * This and any other needed helper functions will replace: @@ -2243,55 +2237,24 @@ void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { return; } nr_php_instrument_func_begin(NR_EXECUTE_ORIG_ARGS); - - return; -} - #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO -void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { - /* - * Instrument the function. - * This and any other needed helper functions will replace: - * nr_php_execute_enabled - * nr_php_execute - * nr_php_execute_show - */ - zval* func_return_value = NULL; - //if (execute_data->func && execute_data->func->common.function_name) { - // printf("BEGIN %s", ZSTR_VAL(execute_data->func->common.function_name)); - //} - if (nrunlikely(NULL == execute_data)) { - return; - } - - NRPRG(php_cur_stack_depth) += 1; - - if ((0 < ((int)NRINI(max_nesting_level))) - && (NRPRG(php_cur_stack_depth) >= (int)NRINI(max_nesting_level))) { - nr_php_max_nesting_level_reached(); + if (call_late) { + nr_php_observer_fcall_begin_late(execute_data, nr_txn_start_time(NRPRG(txn))); } - - if (nrunlikely(0 == nr_php_recording())) { - return; - } - - int show_executes = NR_PHP_PROCESS_GLOBALS(special_flags).show_executes; - - if (nrunlikely(show_executes)) { - nr_php_show_exec(NR_EXECUTE_ORIG_ARGS); - } - if (NULL == NRPRG(txn)) { - return; - } - nr_php_instrument_func_begin(NR_EXECUTE_ORIG_ARGS); - nr_php_observer_fcall_begin_late(execute_data, nr_txn_start_time(NRPRG(txn))); +#endif return; } -#endif +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +static void nr_php_observer_fcall_end_common(zend_execute_data* execute_data, + zval* func_return_value, + bool create_metric, + bool end_segment) { +#else void nr_php_observer_fcall_end(zend_execute_data* execute_data, zval* func_return_value) { +#endif /* * Instrument the function. * This and any other needed helper functions will replace: @@ -2318,7 +2281,7 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, } #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, false, true); + nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, create_metric, end_segment); #else nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS); #endif @@ -2330,45 +2293,29 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, } #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { + nr_php_observer_fcall_begin_common(execute_data, false); +} +void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { + nr_php_observer_fcall_begin_common(execute_data, true); +} +void nr_php_observer_fcall_end(zend_execute_data* execute_data, + zval* func_return_value) { + nr_php_observer_fcall_end_common(execute_data, func_return_value, false, true); +} +void nr_php_observer_fcall_end_create_metric(zend_execute_data* execute_data, + zval* func_return_value) { + nr_php_observer_fcall_end_common(execute_data, func_return_value, true, true); +} +void nr_php_observer_fcall_end_keep_segment(zend_execute_data* execute_data, + zval* func_return_value) { + nr_php_observer_fcall_end_common(execute_data, func_return_value, false, false); +} + // These empty functions (rather than NULL) are used to know if instrumentation // has been added This is needed because the process for adding instrumentation // with a transient wrapper differs depending on if the function has been // previously called. These will only be used when tt_detail is 0. -void nr_php_observer_fcall_end_keep_segment(zend_execute_data* execute_data, - zval* func_return_value) { - /* - * Instrument the function. - * This and any other needed helper functions will replace: - * nr_php_execute_enabled - * nr_php_execute - * nr_php_execute_show - */ - if (nrunlikely(NULL == execute_data)) { - return; - } - //if (execute_data->func && execute_data->func->common.function_name) { - // printf("END %s\n", ZSTR_VAL(execute_data->func->common.function_name)); - //} - - if (nrlikely(1 == nr_php_recording())) { - int show_executes_return - = NR_PHP_PROCESS_GLOBALS(special_flags).show_execute_returns; - - if (nrunlikely(show_executes_return)) { - nrl_verbosedebug(NRL_AGENT, - "Stack depth: %d before OAPI function exiting via %s", - NRPRG(php_cur_stack_depth), __func__); - nr_php_show_exec_return(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); - } - - nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, false, false); - } - - NRPRG(php_cur_stack_depth) -= 1; - - return; -} - void nr_php_observer_empty_fcall_begin(zend_execute_data* execute_data) { (void)execute_data; } @@ -2379,37 +2326,6 @@ void nr_php_observer_empty_fcall_end(zend_execute_data* execute_data, (void)func_return_value; } -void nr_php_observer_fcall_end_create_metric(zend_execute_data* execute_data, - zval* func_return_value) { - /* - * Instrument the function. - * This and any other needed helper functions will replace: - * nr_php_execute_enabled - * nr_php_execute - * nr_php_execute_show - */ - if (nrunlikely(NULL == execute_data)) { - return; - } - //if (execute_data->func && execute_data->func->common.function_name) { - // printf("END %s\n", ZSTR_VAL(execute_data->func->common.function_name)); - //} - - if (nrlikely(1 == nr_php_recording())) { - int show_executes_return - = NR_PHP_PROCESS_GLOBALS(special_flags).show_execute_returns; - - if (nrunlikely(show_executes_return)) { - nr_php_show_exec_return(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); - } - - nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, true, true); - } - - NRPRG(php_cur_stack_depth) -= 1; - - return; -} #endif #endif diff --git a/agent/php_observer.h b/agent/php_observer.h index 566a8c8fb..2e45ff0d1 100644 --- a/agent/php_observer.h +++ b/agent/php_observer.h @@ -77,10 +77,6 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO -// These empty functions (rather than NULL) are used to know if instrumentation -// has been added This is needed because the process for adding instrumentation -// with a transient wrapper differs depending on if the function has been -// previously called. These will only be used when tt_detail is 0. void nr_php_observer_empty_fcall_begin(zend_execute_data* execute_data); void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data); From 643009bc6b246062064d2c9185056dd33bcef156 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 22 May 2024 09:11:26 -0600 Subject: [PATCH 38/45] unify handler overwrite into function --- agent/php_observer.c | 30 ++++++++++++++++++ agent/php_observer.h | 4 +++ agent/php_user_instrument.c | 24 +-------------- agent/php_wrapper.c | 61 ++----------------------------------- 4 files changed, 38 insertions(+), 81 deletions(-) diff --git a/agent/php_observer.c b/agent/php_observer.c index cb23d566f..ee1f0e790 100644 --- a/agent/php_observer.c +++ b/agent/php_observer.c @@ -126,6 +126,36 @@ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( nr_php_observer_fcall_end; return handlers; } + +bool nr_php_observer_is_registered(zend_function* func) { + zend_observer_fcall_begin_handler *begin_handler; + if (func == NULL) { + return false; + } + begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(func)->common), zend_observer_fcall_op_array_extension); + // begin_handler will be NULL if the observer hasn't been installed yet. + // *begin_Handler will be NULL if the function has not yet been called. + return (begin_handler && *begin_handler); +} + +void nr_php_observer_overwrite_handlers(zend_function* func, nruserfn_t* wraprec) { + if (nr_php_observer_is_registered(func)) { + if (zend_observer_remove_begin_handler(func, NRINI(tt_detail) ? + nr_php_observer_fcall_begin : + nr_php_observer_empty_fcall_begin)) { + zend_observer_add_begin_handler(func, wraprec->special_instrumentation_before ? + (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : + nr_php_observer_fcall_begin); + } + if (zend_observer_remove_end_handler(func, NRINI(tt_detail) ? + nr_php_observer_fcall_end : + nr_php_observer_empty_fcall_end)) { + zend_observer_add_end_handler(func, wraprec->special_instrumentation ? + (zend_observer_fcall_end_handler)wraprec->special_instrumentation : + nr_php_observer_fcall_end); + } + } +} #endif diff --git a/agent/php_observer.h b/agent/php_observer.h index 2e45ff0d1..0da7749a1 100644 --- a/agent/php_observer.h +++ b/agent/php_observer.h @@ -17,6 +17,7 @@ #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO /* PHP8+ */ #include "Zend/zend_observer.h" +#include "php_user_instrument.h" /* * Purpose: There are a few various places, aside from the php_execute_* family @@ -77,6 +78,9 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO +bool nr_php_observer_is_registered(zend_function* func); +void nr_php_observer_overwrite_handlers(zend_function* func, nruserfn_t* wraprec); + void nr_php_observer_empty_fcall_begin(zend_execute_data* execute_data); void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data); diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index 62b9fb942..5e0e64069 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -452,7 +452,6 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, nruserfn_t* p; #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO zend_function* orig_func; - zend_observer_fcall_begin_handler *begin_handler; #endif wraprec = nr_php_user_wraprec_create_named(namestr, namestrlen); @@ -494,28 +493,7 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, * module shutdown */ nr_php_add_custom_tracer_common(wraprec); #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - if (orig_func) { - // Before messing with our handlers, we must ensure that the observer fields of the function are initialized - begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(orig_func)->common), zend_observer_fcall_op_array_extension); - // begin_handler will be NULL if the observer hasn't been installed yet. - // *begin_Handler will be NULL if the function has not yet been called. - if (begin_handler && *begin_handler) { - if (zend_observer_remove_begin_handler(orig_func, NRINI(tt_detail) ? - nr_php_observer_fcall_begin : - nr_php_observer_empty_fcall_begin)) { - zend_observer_add_begin_handler(orig_func, wraprec->special_instrumentation_before ? - (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : - nr_php_observer_fcall_begin_instrumented); - } - if (zend_observer_remove_end_handler(orig_func, NRINI(tt_detail) ? - nr_php_observer_fcall_end : - nr_php_observer_empty_fcall_end)) { - zend_observer_add_end_handler(orig_func, wraprec->special_instrumentation ? - (zend_observer_fcall_end_handler)wraprec->special_instrumentation : - nr_php_observer_fcall_end); - } - } - } + nr_php_observer_overwrite_handlers(orig_func, wraprec); #endif return wraprec; /* return the new wraprec */ diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index 29bc98250..ffdfdd74a 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -83,10 +83,7 @@ nruserfn_t* nr_php_wrap_callable_before_after( zend_function* callable, nrspecialfn_t before_callback, nrspecialfn_t after_callback) { - char* name = NULL; -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - zend_observer_fcall_begin_handler *begin_handler; -#endif + char* name = NULL; /* creates a transient wraprec */ nruserfn_t* wraprec = nr_php_add_custom_tracer_callable(callable TSRMLS_CC); @@ -104,33 +101,7 @@ nruserfn_t* nr_php_wrap_callable_before_after( nr_free(name); } #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - if (callable) { - // Before messing with our handlers, we must ensure that the observer fields of the function are initialized - begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(callable)->common), zend_observer_fcall_op_array_extension); - // begin_handler will be NULL if the observer hasn't been installed yet. - // *begin_Handler will be NULL if the function has not yet been called. - if (begin_handler && *begin_handler) { - name = nr_php_function_debug_name(callable); - php_printf("AHHHHHHH %s\n", name); - nr_free(name); - // It is okay to attempt to remove a handler that doesn't exist - // TODO this could remove nr_php_observer_fcall_begin/end and then re-add it :) - if (zend_observer_remove_begin_handler(callable, NRINI(tt_detail) ? - nr_php_observer_fcall_begin : - nr_php_observer_empty_fcall_begin)) { - zend_observer_add_begin_handler(callable, wraprec->special_instrumentation_before ? - (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : - nr_php_observer_fcall_begin); - } - if (zend_observer_remove_end_handler(callable, NRINI(tt_detail) ? - nr_php_observer_fcall_end : - nr_php_observer_empty_fcall_end)) { - zend_observer_add_end_handler(callable, wraprec->special_instrumentation ? - (zend_observer_fcall_end_handler)wraprec->special_instrumentation : - nr_php_observer_fcall_end); - } - } - } + nr_php_observer_overwrite_handlers(callable, wraprec); #endif return wraprec; @@ -172,9 +143,6 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, nrspecialfn_t callback TSRMLS_DC) { /* creates a transient wraprec */ nruserfn_t* wraprec = nr_php_add_custom_tracer_callable(callable TSRMLS_CC); -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - zend_observer_fcall_begin_handler *begin_handler; -#endif if (wraprec && callback) { if ((NULL != wraprec->special_instrumentation) @@ -186,30 +154,7 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, } else { wraprec->special_instrumentation = callback; #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - if (callable) { - // Before messing with our handlers, we must ensure that the observer fields of the function are initialized - begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(callable)->common), zend_observer_fcall_op_array_extension); - // begin_handler will be NULL if the observer hasn't been installed yet. - // *begin_Handler will be NULL if the function has not yet been called. - if (begin_handler && *begin_handler) { - // It is okay to attempt to remove a handler that doesn't exist - // TODO this could remove nr_php_observer_fcall_begin/end and then re-add it :) - if (zend_observer_remove_begin_handler(callable, NRINI(tt_detail) ? - nr_php_observer_fcall_begin : - nr_php_observer_empty_fcall_begin)) { - zend_observer_add_begin_handler(callable, wraprec->special_instrumentation_before ? - (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : - nr_php_observer_fcall_begin); - } - if (zend_observer_remove_end_handler(callable, NRINI(tt_detail) ? - nr_php_observer_fcall_end : - nr_php_observer_empty_fcall_end)) { - zend_observer_add_end_handler(callable, wraprec->special_instrumentation ? - (zend_observer_fcall_end_handler)wraprec->special_instrumentation : - nr_php_observer_fcall_end); - } - } - } + nr_php_observer_overwrite_handlers(callable, wraprec); #endif } } From 61fd2254ebb9baf716d559dc45350192ba2f07b9 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 22 May 2024 09:51:48 -0600 Subject: [PATCH 39/45] add exception handler fcall_end --- agent/php_execute.c | 24 ++++++++++++++++-------- agent/php_observer.c | 12 ++++++++---- agent/php_observer.h | 6 ++++-- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/agent/php_execute.c b/agent/php_execute.c index 564e7fc29..5b57a81fc 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -2054,7 +2054,7 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { bool create_metric = false; nr_php_execute_metadata_t metadata = {0}; #else -static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, bool end_segment) { +static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, bool end_segment, bool is_exception_handler) { #endif nr_segment_t* segment = NULL; nrtime_t txn_start_time = 0; @@ -2093,8 +2093,10 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, boo #if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO wraprec = segment->wraprec; - if (segment->is_exception_handler) { +#else + if (is_exception_handler) { +#endif /* * After running the exception handler segment, create an error from * the exception it handled, and save the error in the transaction. @@ -2108,9 +2110,10 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, boo nr_php_error_record_exception( NRPRG(txn), exception, nr_php_error_get_priority(E_ERROR), false, "Uncaught exception ", &NRPRG(exception_filters) TSRMLS_CC); +#if ZEND_MODULE_API_NO < ZEND_8_2_X_API_NO } else if (NULL == nr_php_get_return_value(NR_EXECUTE_ORIG_ARGS)) { #else - if (NULL == func_return_value) { + } else if (NULL == func_return_value) { #endif /* * Having no return value (and not being an exception handler) indicates @@ -2250,7 +2253,8 @@ void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { static void nr_php_observer_fcall_end_common(zend_execute_data* execute_data, zval* func_return_value, bool create_metric, - bool end_segment) { + bool end_segment, + bool is_exception_handler) { #else void nr_php_observer_fcall_end(zend_execute_data* execute_data, zval* func_return_value) { @@ -2281,7 +2285,7 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, } #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, create_metric, end_segment); + nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS, create_metric, end_segment, is_exception_handler); #else nr_php_instrument_func_end(NR_EXECUTE_ORIG_ARGS); #endif @@ -2301,15 +2305,19 @@ void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { } void nr_php_observer_fcall_end(zend_execute_data* execute_data, zval* func_return_value) { - nr_php_observer_fcall_end_common(execute_data, func_return_value, false, true); + nr_php_observer_fcall_end_common(execute_data, func_return_value, false, true, false); } void nr_php_observer_fcall_end_create_metric(zend_execute_data* execute_data, zval* func_return_value) { - nr_php_observer_fcall_end_common(execute_data, func_return_value, true, true); + nr_php_observer_fcall_end_common(execute_data, func_return_value, true, true, false); } void nr_php_observer_fcall_end_keep_segment(zend_execute_data* execute_data, zval* func_return_value) { - nr_php_observer_fcall_end_common(execute_data, func_return_value, false, false); + nr_php_observer_fcall_end_common(execute_data, func_return_value, false, false, false); +} +void nr_php_observer_fcall_end_exception_handler(zend_execute_data* execute_data, + zval* func_return_value) { + nr_php_observer_fcall_end_common(execute_data, func_return_value, false, true, true); } // These empty functions (rather than NULL) are used to know if instrumentation diff --git a/agent/php_observer.c b/agent/php_observer.c index ee1f0e790..ffea6aaca 100644 --- a/agent/php_observer.c +++ b/agent/php_observer.c @@ -121,9 +121,11 @@ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( nr_php_observer_fcall_begin_instrumented; handlers.end = wraprec->special_instrumentation ? (zend_observer_fcall_end_handler)wraprec->special_instrumentation : - wraprec->create_metric ? - nr_php_observer_fcall_end_create_metric : - nr_php_observer_fcall_end; + wraprec->is_exception_handler ? + nr_php_observer_fcall_end_exception_handler : + wraprec->create_metric ? + nr_php_observer_fcall_end_create_metric : + nr_php_observer_fcall_end; return handlers; } @@ -152,7 +154,9 @@ void nr_php_observer_overwrite_handlers(zend_function* func, nruserfn_t* wraprec nr_php_observer_empty_fcall_end)) { zend_observer_add_end_handler(func, wraprec->special_instrumentation ? (zend_observer_fcall_end_handler)wraprec->special_instrumentation : - nr_php_observer_fcall_end); + wraprec->is_exception_handler ? + nr_php_observer_fcall_end_exception_handler : + nr_php_observer_fcall_end); } } } diff --git a/agent/php_observer.h b/agent/php_observer.h index 0da7749a1..352d198ad 100644 --- a/agent/php_observer.h +++ b/agent/php_observer.h @@ -88,10 +88,12 @@ void nr_php_observer_empty_fcall_end(zend_execute_data* execute_data, zval* func_return_value); void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t txn_start_time); void nr_php_observer_fcall_end_keep_segment(zend_execute_data* execute_data, - zval* func_return_value); + zval* func_return_value); void nr_php_observer_fcall_end_late(zend_execute_data* execute_data, bool create_metric, nrtime_t txn_start_time); void nr_php_observer_fcall_end_create_metric(zend_execute_data* execute_data, - zval* func_return_value); + zval* func_return_value); +void nr_php_observer_fcall_end_exception_handler(zend_execute_data* execute_data, + zval* func_return_value); #endif /* PHP 8.2+ */ #endif /* PHP8+ */ From 1a9b95381265d2b5c39e3df290dd4c2211cd10c0 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 22 May 2024 12:15:46 -0600 Subject: [PATCH 40/45] InstrumentedFunction supp metric on first call only --- agent/php_observer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agent/php_observer.c b/agent/php_observer.c index ffea6aaca..a6d5f1195 100644 --- a/agent/php_observer.c +++ b/agent/php_observer.c @@ -115,17 +115,18 @@ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( } } handlers.begin = wraprec->special_instrumentation_before ? - (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : + (zend_observer_fcall_begin_handler) wraprec->special_instrumentation_before : wraprec->is_transient ? nr_php_observer_fcall_begin : nr_php_observer_fcall_begin_instrumented; handlers.end = wraprec->special_instrumentation ? - (zend_observer_fcall_end_handler)wraprec->special_instrumentation : + (zend_observer_fcall_end_handler) wraprec->special_instrumentation : wraprec->is_exception_handler ? nr_php_observer_fcall_end_exception_handler : wraprec->create_metric ? nr_php_observer_fcall_end_create_metric : nr_php_observer_fcall_end; + nr_txn_force_single_count(NRPRG(txn), wraprec->supportability_metric); return handlers; } From f01406bbf9de5239154dc2f77c2e688166a7105e Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Thu, 23 May 2024 10:12:44 -0600 Subject: [PATCH 41/45] add metric for ini instrumented --- agent/php_execute.c | 30 ++++++++++++++++++++---------- agent/php_observer.c | 4 +++- agent/php_observer.h | 3 ++- agent/php_wrapper.h | 2 +- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/agent/php_execute.c b/agent/php_execute.c index 5b57a81fc..cfb9be43f 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -1991,7 +1991,7 @@ static void nr_php_instrument_func_begin(NR_EXECUTE_PROTO) { } #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO -void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t txn_start_time) { +void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t txn_start_time, bool name_transaction) { /* * During nr_zend_call_oapi_special_before, the transaction may have been * ended and/or a new transaction may have started. To detect this, we @@ -2014,12 +2014,14 @@ void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t /* * Check for, and handle, frameworks. */ - //if (wraprec->is_names_wt_simple) { + if (name_transaction) { - // nr_txn_name_from_function(NRPRG(txn), - // nr_php_op_array_function_name(NR_OP_ARRAY), - // nr_php_class_entry_name(NR_OP_ARRAY->scope)); - //} + nr_txn_name_from_function(NRPRG(txn), + nr_php_op_array_function_name(NR_OP_ARRAY), + NR_OP_ARRAY->scope ? + nr_php_class_entry_name(NR_OP_ARRAY->scope) : + NULL); + } } #endif @@ -2198,7 +2200,7 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO, bool create_metric, boo } #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO -static void nr_php_observer_fcall_begin_common(zend_execute_data* execute_data, bool call_late) { +static void nr_php_observer_fcall_begin_common(zend_execute_data* execute_data, bool call_late, bool name_transaction) { #else void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { #endif @@ -2242,7 +2244,7 @@ void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { nr_php_instrument_func_begin(NR_EXECUTE_ORIG_ARGS); #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO if (call_late) { - nr_php_observer_fcall_begin_late(execute_data, nr_txn_start_time(NRPRG(txn))); + nr_php_observer_fcall_begin_late(execute_data, nr_txn_start_time(NRPRG(txn)), name_transaction); } #endif @@ -2298,10 +2300,13 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO void nr_php_observer_fcall_begin(zend_execute_data* execute_data) { - nr_php_observer_fcall_begin_common(execute_data, false); + nr_php_observer_fcall_begin_common(execute_data, false, false); } void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data) { - nr_php_observer_fcall_begin_common(execute_data, true); + nr_php_observer_fcall_begin_common(execute_data, true, false); +} +void nr_php_observer_fcall_begin_name_transaction(zend_execute_data* execute_data) { + nr_php_observer_fcall_begin_common(execute_data, true, true); } void nr_php_observer_fcall_end(zend_execute_data* execute_data, zval* func_return_value) { @@ -2332,6 +2337,11 @@ void nr_php_observer_empty_fcall_end(zend_execute_data* execute_data, zval* func_return_value) { (void)execute_data; (void)func_return_value; + + /* need a way to register framework info while tt_detail is 0 */ + if (nrunlikely(OP_ARRAY_IS_A_FILE(NR_OP_ARRAY))) { + nr_php_execute_file(NR_OP_ARRAY, NR_EXECUTE_ORIG_ARGS); + } } #endif diff --git a/agent/php_observer.c b/agent/php_observer.c index a6d5f1195..093078321 100644 --- a/agent/php_observer.c +++ b/agent/php_observer.c @@ -118,7 +118,9 @@ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( (zend_observer_fcall_begin_handler) wraprec->special_instrumentation_before : wraprec->is_transient ? nr_php_observer_fcall_begin : - nr_php_observer_fcall_begin_instrumented; + wraprec->is_names_wt_simple ? + nr_php_observer_fcall_begin_name_transaction : + nr_php_observer_fcall_begin_instrumented; handlers.end = wraprec->special_instrumentation ? (zend_observer_fcall_end_handler) wraprec->special_instrumentation : wraprec->is_exception_handler ? diff --git a/agent/php_observer.h b/agent/php_observer.h index 352d198ad..c789f1518 100644 --- a/agent/php_observer.h +++ b/agent/php_observer.h @@ -83,10 +83,11 @@ void nr_php_observer_overwrite_handlers(zend_function* func, nruserfn_t* wraprec void nr_php_observer_empty_fcall_begin(zend_execute_data* execute_data); void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data); +void nr_php_observer_fcall_begin_name_transaction(zend_execute_data* execute_data); void nr_php_observer_empty_fcall_end(zend_execute_data* execute_data, zval* func_return_value); -void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t txn_start_time); +void nr_php_observer_fcall_begin_late(zend_execute_data* execute_data, nrtime_t txn_start_time, bool name_transaction); void nr_php_observer_fcall_end_keep_segment(zend_execute_data* execute_data, zval* func_return_value); void nr_php_observer_fcall_end_late(zend_execute_data* execute_data, bool create_metric, nrtime_t txn_start_time); diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index a5ec68f62..23fa7539c 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -323,7 +323,7 @@ extern zval** nr_php_get_return_value_ptr(TSRMLS_D); NR_PHP_WRAPPER_CALL \ } \ if (in_begin) { \ - nr_php_observer_fcall_begin_late(execute_data, txn_start_time);\ + nr_php_observer_fcall_begin_late(execute_data, txn_start_time, false);\ } else { \ nr_php_observer_fcall_end_late(execute_data, false, txn_start_time); \ } \ From 5c966b29afd7b9bcf4bbb7ba1b92838132027b1f Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 29 May 2024 11:13:26 -0600 Subject: [PATCH 42/45] fixup --- agent/php_observer.c | 124 +++++++++++++++++++++++------------- agent/php_observer.h | 9 ++- agent/php_user_instrument.c | 52 ++++++++++----- agent/php_user_instrument.h | 3 + agent/php_wrapper.c | 60 ++++++++++++----- 5 files changed, 173 insertions(+), 75 deletions(-) diff --git a/agent/php_observer.c b/agent/php_observer.c index 093078321..9a60eea86 100644 --- a/agent/php_observer.c +++ b/agent/php_observer.c @@ -87,7 +87,55 @@ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( handlers.end = nr_php_observer_fcall_end; return handlers; } + #else + +static zend_observer_fcall_begin_handler nr_php_observer_determine_begin_handler(nruserfn_t* wraprec) { + zend_observer_fcall_begin_handler begin = NULL; + + if (NULL == wraprec) { + if (NRINI(tt_detail)) { + begin = nr_php_observer_fcall_begin; + } else { + begin = nr_php_observer_empty_fcall_begin; + } + } else { + if (wraprec->special_instrumentation_before) { + begin = (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before; + } else if (wraprec->is_names_wt_simple) { + begin = nr_php_observer_fcall_begin_name_transaction; + } else if (wraprec->is_transient) { + begin = nr_php_observer_fcall_begin; + } else { + begin = nr_php_observer_fcall_begin_instrumented; + } + } + return begin; +} + +static zend_observer_fcall_end_handler nr_php_observer_determine_end_handler(nruserfn_t* wraprec) { + zend_observer_fcall_end_handler end = NULL; + + if (NULL == wraprec) { + if (NRINI(tt_detail)) { + end = nr_php_observer_fcall_end; + } else { + end = nr_php_observer_empty_fcall_end; + } + } else { + if (wraprec->special_instrumentation) { + end = (zend_observer_fcall_end_handler)wraprec->special_instrumentation; + } else if (wraprec->is_exception_handler) { + end = nr_php_observer_fcall_end_exception_handler; + } else if (wraprec->create_metric) { + end = nr_php_observer_fcall_end_create_metric; + } else { + end = nr_php_observer_fcall_end; + } + } + return end; +} + static zend_observer_fcall_handlers nr_php_fcall_register_handlers( zend_execute_data* execute_data) { zend_observer_fcall_handlers handlers = {NULL, NULL}; @@ -103,32 +151,11 @@ static zend_observer_fcall_handlers nr_php_fcall_register_handlers( // printf("REGISTER %s\n", ZSTR_VAL(execute_data->func->common.function_name)); //} wraprec = nr_php_get_wraprec(execute_data->func); - if (wraprec == NULL) { - if (0 == NRINI(tt_detail)) { - handlers.begin = nr_php_observer_empty_fcall_begin; - handlers.end = nr_php_observer_empty_fcall_end; - return handlers; - } else { - handlers.begin = nr_php_observer_fcall_begin; - handlers.end = nr_php_observer_fcall_end; - return handlers; - } + handlers.begin = nr_php_observer_determine_begin_handler(wraprec); + handlers.end = nr_php_observer_determine_end_handler(wraprec); + if (wraprec) { + nr_txn_force_single_count(NRPRG(txn), wraprec->supportability_metric); } - handlers.begin = wraprec->special_instrumentation_before ? - (zend_observer_fcall_begin_handler) wraprec->special_instrumentation_before : - wraprec->is_transient ? - nr_php_observer_fcall_begin : - wraprec->is_names_wt_simple ? - nr_php_observer_fcall_begin_name_transaction : - nr_php_observer_fcall_begin_instrumented; - handlers.end = wraprec->special_instrumentation ? - (zend_observer_fcall_end_handler) wraprec->special_instrumentation : - wraprec->is_exception_handler ? - nr_php_observer_fcall_end_exception_handler : - wraprec->create_metric ? - nr_php_observer_fcall_end_create_metric : - nr_php_observer_fcall_end; - nr_txn_force_single_count(NRPRG(txn), wraprec->supportability_metric); return handlers; } @@ -143,25 +170,36 @@ bool nr_php_observer_is_registered(zend_function* func) { return (begin_handler && *begin_handler); } -void nr_php_observer_overwrite_handlers(zend_function* func, nruserfn_t* wraprec) { - if (nr_php_observer_is_registered(func)) { - if (zend_observer_remove_begin_handler(func, NRINI(tt_detail) ? - nr_php_observer_fcall_begin : - nr_php_observer_empty_fcall_begin)) { - zend_observer_add_begin_handler(func, wraprec->special_instrumentation_before ? - (zend_observer_fcall_begin_handler)wraprec->special_instrumentation_before : - nr_php_observer_fcall_begin); - } - if (zend_observer_remove_end_handler(func, NRINI(tt_detail) ? - nr_php_observer_fcall_end : - nr_php_observer_empty_fcall_end)) { - zend_observer_add_end_handler(func, wraprec->special_instrumentation ? - (zend_observer_fcall_end_handler)wraprec->special_instrumentation : - wraprec->is_exception_handler ? - nr_php_observer_fcall_end_exception_handler : - nr_php_observer_fcall_end); - } +bool nr_php_observer_remove_begin_handler(zend_function* func, nruserfn_t* wraprec) { + if (!nr_php_observer_is_registered(func)) { + return false; + } + zend_observer_fcall_begin_handler begin = nr_php_observer_determine_begin_handler(wraprec);; + return zend_observer_remove_begin_handler(func, begin); +} + +bool nr_php_observer_remove_end_handler(zend_function* func, nruserfn_t* wraprec) { + if (!nr_php_observer_is_registered(func)) { + return false; + } + zend_observer_fcall_end_handler end = nr_php_observer_determine_end_handler(wraprec); + return zend_observer_remove_end_handler(func, end); +} + +void nr_php_observer_add_begin_handler(zend_function* func, nruserfn_t* wraprec) { + if (!nr_php_observer_is_registered(func)) { + return; + } + zend_observer_fcall_begin_handler begin = nr_php_observer_determine_begin_handler(wraprec);; + zend_observer_add_begin_handler(func, begin); +} + +void nr_php_observer_add_end_handler(zend_function* func, nruserfn_t* wraprec) { + if (!nr_php_observer_is_registered(func)) { + return; } + zend_observer_fcall_end_handler end = nr_php_observer_determine_end_handler(wraprec); + zend_observer_add_end_handler(func, end); } #endif diff --git a/agent/php_observer.h b/agent/php_observer.h index c789f1518..f6069f7bd 100644 --- a/agent/php_observer.h +++ b/agent/php_observer.h @@ -79,8 +79,15 @@ void nr_php_observer_fcall_end(zend_execute_data* execute_data, #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO bool nr_php_observer_is_registered(zend_function* func); -void nr_php_observer_overwrite_handlers(zend_function* func, nruserfn_t* wraprec); +bool nr_php_observer_remove_begin_handler(zend_function* func, nruserfn_t* wraprec); +bool nr_php_observer_remove_end_handler(zend_function* func, nruserfn_t* wraprec); +void nr_php_observer_add_begin_handler(zend_function* func, nruserfn_t* wraprec); +void nr_php_observer_add_end_handler(zend_function* func, nruserfn_t* wraprec); +/* + * These different forms of fcall_begin and fcall_end are needed to properly utilize + * the fields in a wraprec without looking it up every call. +*/ void nr_php_observer_empty_fcall_begin(zend_execute_data* execute_data); void nr_php_observer_fcall_begin_instrumented(zend_execute_data* execute_data); void nr_php_observer_fcall_begin_name_transaction(zend_execute_data* execute_data); diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index 5e0e64069..6d2eb9a7c 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -254,21 +254,21 @@ static void nr_php_wrap_zend_function(zend_function* func, } } -static zend_function* nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSRMLS_DC) { +static void nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSRMLS_DC) { zend_function* orig_func = 0; if (0 == NR_PHP_PROCESS_GLOBALS(done_instrumentation)) { - return NULL; + return; } if (wraprec->is_wrapped) { - return NULL; + return; } #if ZEND_MODULE_API_NO < ZEND_8_0_X_API_NO \ && defined OVERWRITE_ZEND_EXECUTE_DATA /* PHP8+ */ if (nrunlikely(-1 == NR_PHP_PROCESS_GLOBALS(zend_offset))) { - return NULL; + return; } #endif if (0 == wraprec->classname) { @@ -282,8 +282,9 @@ static zend_function* nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSR if (NULL == orig_func) { /* It could be in a file not yet loaded, no reason to log anything. */ - return NULL; + return; } + wraprec->func = orig_func; if (ZEND_USER_FUNCTION != orig_func->type) { nrl_verbosedebug(NRL_INSTRUMENT, "%s%s%s is not a user function", @@ -295,10 +296,9 @@ static zend_function* nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSR * logs with this message. */ wraprec->is_disabled = 1; - return NULL; + return; } nr_php_wrap_zend_function(orig_func, wraprec TSRMLS_CC); - return orig_func; } static nruserfn_t* nr_php_user_wraprec_create(void) { @@ -429,6 +429,10 @@ nruserfn_t* nr_php_add_custom_tracer_callable(zend_function* func TSRMLS_DC) { nrl_verbosedebug(NRL_INSTRUMENT, "reusing custom wrapper for callable '%s'", name); nr_free(name); +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_remove_begin_handler(func, wraprec); + nr_php_observer_remove_end_handler(func, wraprec); +#endif return wraprec; } @@ -442,6 +446,10 @@ nruserfn_t* nr_php_add_custom_tracer_callable(zend_function* func TSRMLS_DC) { #if ZEND_MODULE_API_NO < ZEND_7_4_X_API_NO nr_php_add_custom_tracer_common(wraprec); #endif +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_remove_begin_handler(func, NULL); + nr_php_observer_remove_end_handler(func, NULL); +#endif return wraprec; } @@ -450,9 +458,6 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, size_t namestrlen) { nruserfn_t* wraprec; nruserfn_t* p; -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - zend_function* orig_func; -#endif wraprec = nr_php_user_wraprec_create_named(namestr, namestrlen); if (0 == wraprec) { @@ -472,6 +477,10 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, nr_php_user_wraprec_destroy(&wraprec); nr_php_wrap_user_function_internal(p TSRMLS_CC); +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_remove_begin_handler(p->func, wraprec); + nr_php_observer_remove_end_handler(p->func, wraprec); +#endif return p; /* return the wraprec we are duplicating */ } p = p->next; @@ -482,18 +491,15 @@ nruserfn_t* nr_php_add_custom_tracer_named(const char* namestr, NRP_PHP(wraprec->classname), (0 == wraprec->classname) ? "" : "::", NRP_PHP(wraprec->funcname)); -#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - orig_func = nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); -#else nr_php_wrap_user_function_internal(wraprec TSRMLS_CC); -#endif /* non-transient wraprecs are added to both the hashmap and linked list. * At request shutdown, the hashmap will free transients, but leave * non-transients to be freed when the linked list is disposed of which is at * module shutdown */ nr_php_add_custom_tracer_common(wraprec); #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - nr_php_observer_overwrite_handlers(orig_func, wraprec); + nr_php_observer_remove_begin_handler(wraprec->func, NULL); + nr_php_observer_remove_end_handler(wraprec->func, NULL); #endif return wraprec; /* return the new wraprec */ @@ -579,6 +585,10 @@ void nr_php_add_transaction_naming_function(const char* namestr, if (NULL != wraprec) { wraprec->is_names_wt_simple = 1; +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_add_begin_handler(wraprec->func, wraprec); + nr_php_observer_add_end_handler(wraprec->func, wraprec); +#endif } } @@ -588,6 +598,10 @@ void nr_php_add_custom_tracer(const char* namestr, int namestrlen TSRMLS_DC) { if (NULL != wraprec) { wraprec->create_metric = 1; wraprec->is_user_added = 1; +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_add_begin_handler(wraprec->func, wraprec); + nr_php_observer_add_end_handler(wraprec->func, wraprec); +#endif } } @@ -596,6 +610,10 @@ void nr_php_add_exception_function(zend_function* func TSRMLS_DC) { if (wraprec) { wraprec->is_exception_handler = 1; +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_add_begin_handler(func, wraprec); + nr_php_observer_add_end_handler(func, wraprec); +#endif } } @@ -651,6 +669,10 @@ void nr_php_user_function_add_declared_callback(const char* namestr, if (wraprec->is_wrapped && callback) { (callback)(TSRMLS_C); } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_add_begin_handler(wraprec->func, wraprec); + nr_php_observer_add_end_handler(wraprec->func, wraprec); +#endif } } diff --git a/agent/php_user_instrument.h b/agent/php_user_instrument.h index acae87dbb..f42e50cd4 100644 --- a/agent/php_user_instrument.h +++ b/agent/php_user_instrument.h @@ -110,6 +110,9 @@ typedef struct _nruserfn_t { #if ZEND_MODULE_API_NO >= ZEND_7_4_X_API_NO char* wordpress_plugin_theme; #endif +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + zend_function* func; /* the underlying function that this wraprec wraps */ +#endif } nruserfn_t; extern nruserfn_t* nr_wrapped_user_functions; /* a singly linked list */ diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index ffdfdd74a..c34482321 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -9,13 +9,13 @@ #include "util_logging.h" #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO -static void nr_php_wraprec_add_before_after_callbacks( +static bool nr_php_wraprec_can_add_before_after_callbacks( const char* name, size_t namelen, nruserfn_t* wraprec, nrspecialfn_t before_callback, nrspecialfn_t after_callback) { if (NULL == wraprec) { - return; + return false; } /* If any of the callbacks we are attempting to set are already set to @@ -27,7 +27,7 @@ static void nr_php_wraprec_add_before_after_callbacks( "%s: attempting to set special_instrumentation for %.*s, but " "it is already set", __func__, NRSAFELEN(namelen), NRBLANKSTR(name)); - return; + return false; } if (is_instrumentation_set_and_not_equal(wraprec->special_instrumentation_before, @@ -37,8 +37,19 @@ static void nr_php_wraprec_add_before_after_callbacks( "for %.*s, but " "it is already set", __func__, NRSAFELEN(namelen), NRBLANKSTR(name)); - return; + return false; } + if (wraprec->special_instrumentation_before == before_callback && + wraprec->special_instrumentation == after_callback) { + return false; + } + return true; +} + +static void nr_php_wraprec_add_before_after_callbacks( + nruserfn_t* wraprec, + nrspecialfn_t before_callback, + nrspecialfn_t after_callback) { wraprec->special_instrumentation = after_callback; wraprec->special_instrumentation_before = before_callback; @@ -51,10 +62,17 @@ nruserfn_t* nr_php_wrap_user_function_before_after( nrspecialfn_t after_callback) { nruserfn_t* wraprec = nr_php_add_custom_tracer_named(name, namelen); - - nr_php_wraprec_add_before_after_callbacks(name, namelen, wraprec, - before_callback, - after_callback); + if (nr_php_wraprec_can_add_before_after_callbacks(name, namelen, wraprec, + before_callback, + after_callback)) { + nr_php_wraprec_add_before_after_callbacks(wraprec, + before_callback, + after_callback); + } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_add_begin_handler(wraprec->func, wraprec); + nr_php_observer_add_end_handler(wraprec->func, wraprec); +#endif return wraprec; } @@ -83,7 +101,7 @@ nruserfn_t* nr_php_wrap_callable_before_after( zend_function* callable, nrspecialfn_t before_callback, nrspecialfn_t after_callback) { - char* name = NULL; + char* name = NULL; /* creates a transient wraprec */ nruserfn_t* wraprec = nr_php_add_custom_tracer_callable(callable TSRMLS_CC); @@ -94,15 +112,20 @@ nruserfn_t* nr_php_wrap_callable_before_after( if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT)) { name = nr_php_function_debug_name(callable); } - nr_php_wraprec_add_before_after_callbacks(name, nr_strlen(name), wraprec, - before_callback, - after_callback); - if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT) && NULL != name) { - nr_free(name); + if (nr_php_wraprec_can_add_before_after_callbacks(name, nr_strlen(name), wraprec, + before_callback, + after_callback)) { + nr_php_wraprec_add_before_after_callbacks(wraprec, + before_callback, + after_callback); } #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - nr_php_observer_overwrite_handlers(callable, wraprec); + nr_php_observer_add_begin_handler(callable, wraprec); + nr_php_observer_add_end_handler(callable, wraprec); #endif + if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT) && NULL != name) { + nr_free(name); + } return wraprec; } @@ -123,6 +146,10 @@ nruserfn_t* nr_php_wrap_user_function(const char* name, } else { wraprec->special_instrumentation = callback; } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO + nr_php_observer_add_begin_handler(wraprec->func, wraprec); + nr_php_observer_add_end_handler(wraprec->func, wraprec); +#endif } return wraprec; @@ -154,7 +181,8 @@ nruserfn_t* nr_php_wrap_callable(zend_function* callable, } else { wraprec->special_instrumentation = callback; #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO - nr_php_observer_overwrite_handlers(callable, wraprec); + nr_php_observer_add_begin_handler(callable, wraprec); + nr_php_observer_add_end_handler(callable, wraprec); #endif } } From 6845e9b169ba1b74819417a9012640b6ddc6aeae Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 29 May 2024 11:18:58 -0600 Subject: [PATCH 43/45] fix --- agent/php_user_instrument.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agent/php_user_instrument.c b/agent/php_user_instrument.c index 6d2eb9a7c..73047f7c5 100644 --- a/agent/php_user_instrument.c +++ b/agent/php_user_instrument.c @@ -284,7 +284,9 @@ static void nr_php_wrap_user_function_internal(nruserfn_t* wraprec TSRMLS_DC) { /* It could be in a file not yet loaded, no reason to log anything. */ return; } +#if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO wraprec->func = orig_func; +#endif if (ZEND_USER_FUNCTION != orig_func->type) { nrl_verbosedebug(NRL_INSTRUMENT, "%s%s%s is not a user function", From 0b74c04c775ea4bd506515a235a14d0e5d84b6e2 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 29 May 2024 13:59:42 -0600 Subject: [PATCH 44/45] cleanup --- agent/php_wrapper.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index c34482321..3306a7723 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -9,13 +9,13 @@ #include "util_logging.h" #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO -static bool nr_php_wraprec_can_add_before_after_callbacks( +static void nr_php_wraprec_add_before_after_callbacks( const char* name, size_t namelen, nruserfn_t* wraprec, nrspecialfn_t before_callback, nrspecialfn_t after_callback) { if (NULL == wraprec) { - return false; + return; } /* If any of the callbacks we are attempting to set are already set to @@ -27,7 +27,7 @@ static bool nr_php_wraprec_can_add_before_after_callbacks( "%s: attempting to set special_instrumentation for %.*s, but " "it is already set", __func__, NRSAFELEN(namelen), NRBLANKSTR(name)); - return false; + return; } if (is_instrumentation_set_and_not_equal(wraprec->special_instrumentation_before, @@ -37,19 +37,12 @@ static bool nr_php_wraprec_can_add_before_after_callbacks( "for %.*s, but " "it is already set", __func__, NRSAFELEN(namelen), NRBLANKSTR(name)); - return false; + return; } if (wraprec->special_instrumentation_before == before_callback && wraprec->special_instrumentation == after_callback) { - return false; + return; } - return true; -} - -static void nr_php_wraprec_add_before_after_callbacks( - nruserfn_t* wraprec, - nrspecialfn_t before_callback, - nrspecialfn_t after_callback) { wraprec->special_instrumentation = after_callback; wraprec->special_instrumentation_before = before_callback; @@ -62,13 +55,9 @@ nruserfn_t* nr_php_wrap_user_function_before_after( nrspecialfn_t after_callback) { nruserfn_t* wraprec = nr_php_add_custom_tracer_named(name, namelen); - if (nr_php_wraprec_can_add_before_after_callbacks(name, namelen, wraprec, + nr_php_wraprec_add_before_after_callbacks(name, namelen, wraprec, before_callback, - after_callback)) { - nr_php_wraprec_add_before_after_callbacks(wraprec, - before_callback, - after_callback); - } + after_callback); #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO nr_php_observer_add_begin_handler(wraprec->func, wraprec); nr_php_observer_add_end_handler(wraprec->func, wraprec); @@ -112,13 +101,9 @@ nruserfn_t* nr_php_wrap_callable_before_after( if (nrl_should_print(NRL_VERBOSEDEBUG, NRL_INSTRUMENT)) { name = nr_php_function_debug_name(callable); } - if (nr_php_wraprec_can_add_before_after_callbacks(name, nr_strlen(name), wraprec, + nr_php_wraprec_add_before_after_callbacks(name, nr_strlen(name), wraprec, before_callback, - after_callback)) { - nr_php_wraprec_add_before_after_callbacks(wraprec, - before_callback, - after_callback); - } + after_callback); #if ZEND_MODULE_API_NO >= ZEND_8_2_X_API_NO nr_php_observer_add_begin_handler(callable, wraprec); nr_php_observer_add_end_handler(callable, wraprec); From 6de619972fe2f18a4fb22f7ed3e4773a492f5d12 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Thu, 8 Aug 2024 11:30:10 -0600 Subject: [PATCH 45/45] fix ZEND_OBSERVER_NOT_OBSERVED is 2 instead of NULL --- agent/php_observer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/php_observer.c b/agent/php_observer.c index 9a60eea86..019063cb7 100644 --- a/agent/php_observer.c +++ b/agent/php_observer.c @@ -166,8 +166,8 @@ bool nr_php_observer_is_registered(zend_function* func) { } begin_handler = (zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(func)->common), zend_observer_fcall_op_array_extension); // begin_handler will be NULL if the observer hasn't been installed yet. - // *begin_Handler will be NULL if the function has not yet been called. - return (begin_handler && *begin_handler); + // *begin_Handler will be (void*)2 if the function has not yet been called. + return (begin_handler && *begin_handler != (void*)2); } bool nr_php_observer_remove_begin_handler(zend_function* func, nruserfn_t* wraprec) {