Skip to content

Commit ef7200a

Browse files
authored
Merge pull request #944 from newrelic/dev
Release 11.0
2 parents 687e42d + bd4922f commit ef7200a

Some content is hidden

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

42 files changed

+812
-139
lines changed

.github/workflows/code-coverage-baseline.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
matrix:
7070
platform: [gnu, musl]
7171
arch: [amd64]
72-
php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
72+
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
7373
include:
7474
- codecov: 0
7575
- platform: gnu
@@ -147,7 +147,7 @@ jobs:
147147
matrix:
148148
platform: [gnu, musl]
149149
arch: [amd64]
150-
php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
150+
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
151151
include:
152152
- codecov: 0
153153
- platform: gnu

.github/workflows/test-agent.yml

+28-13
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,31 @@ on:
1919
pull_request:
2020

2121
jobs:
22+
gofmt-check:
23+
runs-on: ubuntu-latest
24+
continue-on-error: true
25+
steps:
26+
- name: Checkout newrelic-php-agent code
27+
uses: actions/checkout@v4
28+
with:
29+
path: php-agent
30+
- name: Setup go
31+
uses: actions/setup-go@v5
32+
with:
33+
go-version-file: ./php-agent/daemon/go.mod
34+
cache: false
35+
- name: Display go version
36+
run: |
37+
go version
38+
- name: Run gofmt
39+
run: |
40+
GOFMT_REPORTED_FILES="$(gofmt -l -e ./php-agent/daemon)"
41+
if [ ! -z "$GOFMT_REPORTED_FILES" ]; then
42+
gofmt -d -e ./php-agent/daemon
43+
echo "### gofmt violations found in $(echo "$GOFMT_REPORTED_FILES" | wc -l) files" >> $GITHUB_STEP_SUMMARY
44+
echo "$GOFMT_REPORTED_FILES" >> $GITHUB_STEP_SUMMARY
45+
exit 1
46+
fi
2247
daemon-unit-tests:
2348
runs-on: ubuntu-latest
2449
env:
@@ -72,12 +97,8 @@ jobs:
7297
matrix:
7398
platform: [gnu, musl]
7499
arch: [amd64, arm64]
75-
php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
100+
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
76101
exclude:
77-
- arch: arm64
78-
php: '7.0'
79-
- arch: arm64
80-
php: '7.1'
81102
- arch: arm64
82103
php: '7.2'
83104
- arch: arm64
@@ -117,9 +138,7 @@ jobs:
117138
echo "AGENT_CHECK_VARIANT=check" >> $GITHUB_OUTPUT
118139
elif [[ ${{ matrix.platform }} = 'gnu' ]]; then
119140
echo "AGENT_CHECK_VARIANT=valgrind" >> $GITHUB_OUTPUT
120-
elif [[ ${{matrix.php}} = '7.0' || ${{matrix.php}} = '7.1' ]]; then
121-
echo "AGENT_CHECK_VARIANT=check" >> $GITHUB_OUTPUT
122-
else
141+
else
123142
echo "AGENT_CHECK_VARIANT=valgrind" >> $GITHUB_OUTPUT
124143
fi
125144
- name: Build axiom
@@ -183,12 +202,8 @@ jobs:
183202
matrix:
184203
platform: [gnu, musl]
185204
arch: [amd64, arm64]
186-
php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
205+
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
187206
exclude:
188-
- arch: arm64
189-
php: '7.0'
190-
- arch: arm64
191-
php: '7.1'
192207
- arch: arm64
193208
php: '7.2'
194209
- arch: arm64

.github/workflows/trigger-test-suite.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ on:
88
pull_request:
99

1010
jobs:
11-
trigger-multiverse-tests:
11+
trigger-test-suite:
1212
runs-on: ubuntu-latest
1313
env:
1414
GH_TOKEN: ${{ secrets.TEST_SUITE_REPO_GH_TOKEN }}
1515
steps:
16-
- name: Trigger Multiverse Test Suite
16+
- name: Trigger Test Suite
1717
run: |
1818
gh workflow run -R ${{ secrets.TEST_SUITE_REPO }} ${{ secrets.TEST_SUITE_WORKFLOW }} -f agent_git_ref=${{ github.head_ref }} -f pr-number=${{ github.event.number }}

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
10.22.0
1+
11.0.0

agent/Makefile.frag

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ TEST_BINARIES = \
8888
tests/test_globals \
8989
tests/test_internal_instrument \
9090
tests/test_hash \
91+
tests/test_lib_aws_sdk_php \
9192
tests/test_mongodb \
9293
tests/test_monolog \
9394
tests/test_mysql \

agent/config.m4

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ if test "$PHP_NEWRELIC" = "yes"; then
228228
fw_silex.c fw_slim.c fw_support.c fw_symfony4.c fw_symfony2.c \
229229
fw_symfony.c fw_symfony_common.c fw_wordpress.c fw_yii.c \
230230
fw_zend2.c fw_zend.c"
231-
LIBRARIES="lib_monolog.c lib_doctrine2.c lib_guzzle3.c \
231+
LIBRARIES="lib_aws_sdk_php.c lib_monolog.c lib_doctrine2.c lib_guzzle3.c \
232232
lib_guzzle4.c lib_guzzle6.c lib_guzzle_common.c \
233233
lib_mongodb.c lib_phpunit.c lib_predis.c lib_zend_http.c"
234234
PHP_NEW_EXTENSION(newrelic, $FRAMEWORKS $LIBRARIES $NEWRELIC_AGENT, $ext_shared,, \\$(NEWRELIC_CFLAGS))

agent/fw_hooks.h

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ extern void nr_zend_enable(TSRMLS_D);
4545
extern void nr_fw_zend2_enable(TSRMLS_D);
4646

4747
/* Libraries. */
48+
extern void nr_aws_sdk_php_enable();
4849
extern void nr_doctrine2_enable(TSRMLS_D);
4950
extern void nr_guzzle3_enable(TSRMLS_D);
5051
extern void nr_guzzle4_enable(TSRMLS_D);

agent/lib_aws_sdk_php.c

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright 2024 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
/*
7+
* Functions relating to instrumenting the AWS-SDK-PHP.
8+
* https://github.com/aws/aws-sdk-php
9+
*/
10+
#include "php_agent.h"
11+
#include "php_call.h"
12+
#include "php_hash.h"
13+
#include "php_wrapper.h"
14+
#include "fw_hooks.h"
15+
#include "fw_support.h"
16+
#include "util_logging.h"
17+
#include "lib_aws_sdk_php.h"
18+
19+
#define PHP_PACKAGE_NAME "aws/aws-sdk-php"
20+
21+
/*
22+
* In a normal course of events, the following line will always work
23+
* zend_eval_string("Aws\\Sdk::VERSION;", &retval, "Get AWS Version")
24+
* By the time we have detected the existence of the aws-sdk-php and with
25+
* default composer profject settings, it callable even from
26+
* nr_aws_sdk_php_enable which will automatically load the class if it isn't
27+
* loaded yet and then evaluate the string. However, in the rare case that files
28+
* are not loaded via autoloader and/or have non-default composer classload
29+
* settings, if the class is not found, PHP 8.2+ will generate a fatal
30+
* unrecoverable uncatchable error error whenever it cannot find a class. While
31+
* calling this from nr_aws_sdk_php_enable would have been great and would allow
32+
* the sdk version value to be set only once, to avoid the very unlikely but not
33+
* impossible fatal error, this will be called from the
34+
* "Aws\\ClientResolver::_apply_user_agent" wrapper which GUARANTEES that
35+
* aws/sdk exists and is already loaded.
36+
*
37+
*
38+
* Additionally given that aws-sdk-php is currently detected from the
39+
* AwsClient.php file, this method will always be called when a client is
40+
* created unlike Sdk::construct which doesn't show with PHP 8.2+.
41+
*
42+
* Using Aws/Sdk::__construct for version is currently nonviable as it is
43+
* unreliable as a version determiner.
44+
* Having separate functionality to extract from Aws/Sdk::__construct
45+
* is both not required and is redundant and causes additional overhead and
46+
* so only one function is needed to extract version.
47+
*
48+
* Aws\\ClientResolver::_apply_user_agent a reliable function as it is
49+
* always called on client initialization since it is key to populating
50+
* the request headers, and it loads Sdk by default.
51+
*
52+
* Concerns about future/past proofing to the checking prioritized the following
53+
* implementation vs using the eval method.
54+
*/
55+
void nr_lib_aws_sdk_php_handle_version() {
56+
zval* zval_version = NULL;
57+
zend_class_entry* class_entry = NULL;
58+
char* version = NULL;
59+
60+
class_entry = nr_php_find_class("aws\\sdk");
61+
if (NULL != class_entry) {
62+
zval_version = nr_php_get_class_constant(class_entry, "VERSION");
63+
64+
if (nr_php_is_zval_non_empty_string(zval_version)) {
65+
version = Z_STRVAL_P(zval_version);
66+
}
67+
}
68+
if (NRINI(vulnerability_management_package_detection_enabled)) {
69+
/* Add php package to transaction */
70+
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version);
71+
}
72+
nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
73+
version);
74+
nr_php_zval_free(&zval_version);
75+
}
76+
77+
void nr_lib_aws_sdk_php_add_supportability_service_metric(
78+
const char* service_name) {
79+
/* total MAX metric name length per agent-specs */
80+
char buf[MAX_METRIC_NAME_LEN];
81+
char* cp = NULL;
82+
83+
if (nr_strempty(service_name)) {
84+
return;
85+
}
86+
if (NULL == NRPRG(txn)) {
87+
return;
88+
}
89+
90+
cp = buf;
91+
strcpy(cp, PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX);
92+
cp += PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX_LEN - 1;
93+
strlcpy(cp, service_name, MAX_AWS_SERVICE_NAME_LEN);
94+
nrm_force_add(NRPRG(txn) ? NRTXN(unscoped_metrics) : 0, buf, 0);
95+
}
96+
97+
NR_PHP_WRAPPER(nr_create_aws_sdk_version_metrics) {
98+
(void)wraprec;
99+
NR_PHP_WRAPPER_CALL;
100+
nr_lib_aws_sdk_php_handle_version();
101+
}
102+
NR_PHP_WRAPPER_END
103+
104+
/*
105+
* AwsClient::parseClass
106+
* This is called from the base AwsClient class for every client associated
107+
* with a service during client initialization.
108+
* parseClass already computes the service name for internal use, so we don't
109+
* need to store it, we just need to snag it from the return value as it goes
110+
* through the client initialization process.
111+
*/
112+
NR_PHP_WRAPPER(nr_create_aws_service_metric) {
113+
(void)wraprec;
114+
115+
zval** ret_val_ptr = NULL;
116+
ret_val_ptr = NR_GET_RETURN_VALUE_PTR;
117+
118+
NR_PHP_WRAPPER_CALL;
119+
120+
if (NULL != ret_val_ptr && nr_php_is_zval_valid_array(*ret_val_ptr)) {
121+
/* obtain ret_val_ptr[0] which contains the service name */
122+
zval* service_name
123+
= nr_php_zend_hash_index_find(Z_ARRVAL_P(*ret_val_ptr), 0);
124+
if (nr_php_is_zval_non_empty_string(service_name)) {
125+
nr_lib_aws_sdk_php_add_supportability_service_metric(
126+
Z_STRVAL_P(service_name));
127+
}
128+
}
129+
}
130+
NR_PHP_WRAPPER_END
131+
132+
/*
133+
* The ideal file to begin immediate detection of the aws-sdk is:
134+
* aws-sdk-php/src/functions.php
135+
* Unfortunately, Php8.2+ and composer autoload leads to the
136+
* file being optimized directly and not loaded.
137+
*
138+
* Options considered:
139+
*
140+
* 1. for PHP8.2, and only optimizable libraries, when encountering autoload.php
141+
* files, ask the file what includes it added and check against only the
142+
* optimizable library. Small overhead incurred when encountering an autoload
143+
* file, but detects aws-sdk-php immediately before any sdk code executes
144+
* (changes needed for this are detailed in the original PR)
145+
* 2. use a file that gets called later and only when AwsClient.php file file is
146+
* called. It's called later and we'll miss some instrumentation, but if we're
147+
* only ever going to be interested in Client calls anyway, maybe that's ok?
148+
* Doesn't detect Sdk.php (optimized out) so when customers only use that or
149+
* when they use it first, we will not instrument it. This only detects when a
150+
* Client is called to use a service so potentially misses out on other
151+
* instrumentation and misses out when customers use the aws-sdk-php but use
152+
* non-SDK way to interact with the service (possibly with redis/memcached).
153+
* This way is definitely the least complex and lowest overhead and less
154+
* complexity means lower risk as well.
155+
* 3. Directly add the wrappers to the hash map. With potentially 50ish clients
156+
* to wrap, this will add overhead to every hash map lookup. Currently
157+
* implemented option is 2, use the AwsClient.php as this is our main focus.
158+
* This means until a call to an Aws/AwsClient function,
159+
* all calls including aws\sdk calls are ignored. Version detection will be
160+
* tied to Aws/ClientResolver::_apply_user_agent which is ALWAYS called when
161+
* dealing with aws clients. It will not be computed from
162+
* Aws/Sdk::__constructor which would at best be duplicate info and worst would
163+
* never be ignored until a client is called.
164+
*/
165+
void nr_aws_sdk_php_enable() {
166+
/* This will be used to extract the version. */
167+
nr_php_wrap_user_function(NR_PSTR("Aws\\ClientResolver::_apply_user_agent"),
168+
nr_create_aws_sdk_version_metrics);
169+
/* Called when initializing all other Clients */
170+
nr_php_wrap_user_function(NR_PSTR("Aws\\AwsClient::parseClass"),
171+
nr_create_aws_service_metric);
172+
173+
if (NRINI(vulnerability_management_package_detection_enabled)) {
174+
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME,
175+
PHP_PACKAGE_VERSION_UNKNOWN);
176+
}
177+
}

agent/lib_aws_sdk_php.h

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2024 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Functions relating to instrumenting AWS-SDK-PHP.
6+
*/
7+
#ifndef LIB_AWS_SDK_PHP_HDR
8+
#define LIB_AWS_SDK_PHP_HDR
9+
10+
#define PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX \
11+
"Supportability/PHP/AWS/Services/"
12+
#define MAX_METRIC_NAME_LEN 256
13+
#define PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX_LEN \
14+
sizeof(PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX)
15+
#define MAX_AWS_SERVICE_NAME_LEN \
16+
(MAX_METRIC_NAME_LEN - PHP_AWS_SDK_SERVICE_NAME_METRIC_PREFIX_LEN)
17+
18+
extern void nr_aws_sdk_php_enable();
19+
extern void nr_lib_aws_sdk_php_handle_version();
20+
extern void nr_lib_aws_sdk_php_add_supportability_service_metric(
21+
const char* service_name);
22+
23+
#endif /* LIB_AWS_SDK_PHP_HDR */

agent/lib_mongodb.c

+8
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,14 @@ void nr_mongodb_enable() {
356356
nr_php_wrap_user_function_extra(NR_PSTR("MongoDB\\Operation\\Count::execute"),
357357
nr_mongodb_operation, "count" TSRMLS_CC);
358358

359+
/*
360+
* `count` has been deprecated in later versions of mongodb and replaced by
361+
* `countDocuments`
362+
*/
363+
nr_php_wrap_user_function_extra(
364+
NR_PSTR("MongoDB\\Operation\\CountDocuments::execute"),
365+
nr_mongodb_operation, "countDocuments");
366+
359367
/*
360368
* This also catches MongoDB\Collection::createIndex
361369
*/

0 commit comments

Comments
 (0)