Skip to content

Commit c497191

Browse files
authored
fix(agent): Add missing errors decode and prioritization (#850)
Add remaining error codes listed in https://github.com/php/php-src/blob/master/Zend/zend_errors.h for completeness in decoding and prioritizing errors. Added/modified tests accordingly.
1 parent 86f7753 commit c497191

15 files changed

+202
-41
lines changed

agent/Makefile.frag

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ TEST_BINARIES = \
9797
tests/test_pdo_mysql \
9898
tests/test_pdo_pgsql \
9999
tests/test_pgsql \
100+
tests/test_php_error \
100101
tests/test_php_execute \
101102
tests/test_php_minit \
102103
tests/test_php_stack \

agent/php_error.c

+45-13
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,9 @@ PHP_FUNCTION(newrelic_exception_handler) {
287287
#endif /* PHP7 */
288288
}
289289

290+
/* PHP Fatal errors: E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR |
291+
* E_COMPILE_ERROR | E_RECOVERABLE_ERROR */
292+
290293
int nr_php_error_get_priority(int type) {
291294
switch (type) {
292295
case E_PARSE:
@@ -299,6 +302,8 @@ int nr_php_error_get_priority(int type) {
299302
return 50;
300303
case E_ERROR:
301304
return 50;
305+
case E_RECOVERABLE_ERROR:
306+
return 50;
302307
case E_COMPILE_WARNING:
303308
return 40;
304309
case E_CORE_WARNING:
@@ -307,6 +312,12 @@ int nr_php_error_get_priority(int type) {
307312
return 40;
308313
case E_WARNING:
309314
return 40;
315+
case E_DEPRECATED:
316+
return 30;
317+
case E_USER_DEPRECATED:
318+
return 30;
319+
case E_STRICT: /* Included for backward compatibility */
320+
return 10;
310321
case E_USER_NOTICE:
311322
return 0;
312323
case E_NOTICE:
@@ -466,30 +477,51 @@ static char* nr_php_error_exception_message(zval* exception TSRMLS_DC) {
466477
return message;
467478
}
468479

469-
static const char* get_error_type_string(int type) {
480+
const char* nr_php_error_get_type_string(int type) {
481+
/* NOTE: PHP 7 makes E_STRICT irrelevant, reclassifying most of the errors as
482+
* proper warnings, notices or E_DEPRECATED:
483+
* https://wiki.php.net/rfc/reclassify_e_strict The E_STRICT constant will be
484+
* retained for better compatibility, it will simply no longer have meaning in
485+
* PHP 7. While PHP 7 was backward compatible, PHP 8 does not use E_STRICT.
486+
*/
487+
488+
/* Note:
489+
* With PHP7.4+ we should never actually be getting E_ERROR, or
490+
* E_RECOVERABLE_ERROR here. Both are fatal errors and are handled by the
491+
* agent's uncaught exceptions logic (unless E_RECOVERABLE_ERROR in which case
492+
* there is no error to see)
493+
*/
470494
switch (type) {
471-
case E_ERROR:
495+
case E_ERROR: /* 1 */
472496
return "E_ERROR";
473-
case E_WARNING:
497+
case E_WARNING: /* 2 */
474498
return "E_WARNING";
475-
case E_PARSE:
499+
case E_PARSE: /* 4 */
476500
return "E_PARSE";
477-
case E_NOTICE:
501+
case E_NOTICE: /* 8 */
478502
return "E_NOTICE";
479-
case E_CORE_ERROR:
503+
case E_CORE_ERROR: /* 16 */
480504
return "E_CORE_ERROR";
481-
case E_CORE_WARNING:
505+
case E_CORE_WARNING: /* 32 */
482506
return "E_CORE_WARNING";
483-
case E_COMPILE_ERROR:
507+
case E_COMPILE_ERROR: /* 64 */
484508
return "E_COMPILE_ERROR";
485-
case E_COMPILE_WARNING:
509+
case E_COMPILE_WARNING: /* 128 */
486510
return "E_COMPILE_WARNING";
487-
case E_USER_ERROR:
511+
case E_USER_ERROR: /* 256 */
488512
return "E_USER_ERROR";
489-
case E_USER_WARNING:
513+
case E_USER_WARNING: /* 512 */
490514
return "E_USER_WARNING";
491-
case E_USER_NOTICE:
515+
case E_USER_NOTICE: /* 1024 */
492516
return "E_USER_NOTICE";
517+
case E_STRICT: /* 2048 */
518+
return "E_STRICT";
519+
case E_RECOVERABLE_ERROR: /* 4096 */
520+
return "E_RECOVERABLE_ERROR";
521+
case E_DEPRECATED: /* 8192 */
522+
return "E_DEPRECATED";
523+
case E_USER_DEPRECATED: /* 16348 */
524+
return "E_USER_DEPRECATED";
493525
default:
494526
return "Error";
495527
}
@@ -597,7 +629,7 @@ void nr_php_error_cb(int type,
597629
#endif /* PHP < 8.0 */
598630

599631
stack_json = nr_php_backtrace_to_json(0 TSRMLS_CC);
600-
errclass = get_error_type_string(type);
632+
errclass = nr_php_error_get_type_string(type);
601633

602634
nr_txn_record_error(NRPRG(txn), nr_php_error_get_priority(type), true,
603635
msg, errclass, stack_json);

agent/php_error.h

+9
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ extern PHP_FUNCTION(newrelic_exception_handler);
112112
*/
113113
extern int nr_php_error_get_priority(int type);
114114

115+
/*
116+
* Purpose : Converts a PHP error type into a readable string.
117+
*
118+
* Params : 1. The error type.
119+
*
120+
* Returns : The PHP error type as constant, static string (must not be freed).
121+
*/
122+
extern const char* nr_php_error_get_type_string(int type);
123+
115124
/*
116125
* Purpose : Install newrelic_exception_handler as the user exception handler
117126
* in PHP.

agent/tests/test_php_error.c

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2020 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
#include "tlib_php.h"
6+
#include "tlib_main.h"
7+
8+
#include "php_agent.h"
9+
#include "php_call.h"
10+
#include "php_globals.h"
11+
#include "php_error.h"
12+
13+
tlib_parallel_info_t parallel_info
14+
= {.suggested_nthreads = -1, .state_size = 0};
15+
16+
static void test_error_get_priority() {
17+
/*
18+
* Test : Unknown type.
19+
*/
20+
tlib_pass_if_int_equal("Unknown error type", 20,
21+
nr_php_error_get_priority(-1));
22+
tlib_pass_if_int_equal("Unknown error type", 20,
23+
nr_php_error_get_priority(3));
24+
25+
/*
26+
* Test : Normal operation.
27+
*/
28+
29+
tlib_pass_if_int_equal("Known error type", 0,
30+
nr_php_error_get_priority(E_NOTICE));
31+
tlib_pass_if_int_equal("Known error type", 0,
32+
nr_php_error_get_priority(E_USER_NOTICE));
33+
tlib_pass_if_int_equal("Known error type", 10,
34+
nr_php_error_get_priority(E_STRICT));
35+
tlib_pass_if_int_equal("Known error type", 30,
36+
nr_php_error_get_priority(E_USER_DEPRECATED));
37+
tlib_pass_if_int_equal("Known error type", 30,
38+
nr_php_error_get_priority(E_DEPRECATED));
39+
tlib_pass_if_int_equal("Known error type", 40,
40+
nr_php_error_get_priority(E_USER_WARNING));
41+
tlib_pass_if_int_equal("Known error type", 40,
42+
nr_php_error_get_priority(E_WARNING));
43+
tlib_pass_if_int_equal("Known error type", 40,
44+
nr_php_error_get_priority(E_CORE_WARNING));
45+
tlib_pass_if_int_equal("Known error type", 40,
46+
nr_php_error_get_priority(E_COMPILE_WARNING));
47+
tlib_pass_if_int_equal("Known error type", 50,
48+
nr_php_error_get_priority(E_RECOVERABLE_ERROR));
49+
tlib_pass_if_int_equal("Known error type", 50,
50+
nr_php_error_get_priority(E_ERROR));
51+
tlib_pass_if_int_equal("Known error type", 50,
52+
nr_php_error_get_priority(E_USER_ERROR));
53+
tlib_pass_if_int_equal("Known error type", 50,
54+
nr_php_error_get_priority(E_CORE_ERROR));
55+
tlib_pass_if_int_equal("Known error type", 50,
56+
nr_php_error_get_priority(E_COMPILE_ERROR));
57+
tlib_pass_if_int_equal("Known error type", 50,
58+
nr_php_error_get_priority(E_PARSE));
59+
}
60+
61+
static void test_get_error_type_string() {
62+
/*
63+
* Test : Unknown type.
64+
*/
65+
tlib_pass_if_str_equal("Unknown error type", "Error",
66+
nr_php_error_get_type_string(-1));
67+
tlib_pass_if_str_equal("Unknown error type", "Error",
68+
nr_php_error_get_type_string(3));
69+
70+
/*
71+
* Test : Normal operation.
72+
*/
73+
74+
tlib_pass_if_str_equal("Known error type", "E_NOTICE",
75+
nr_php_error_get_type_string(E_NOTICE));
76+
tlib_pass_if_str_equal("Known error type", "E_USER_NOTICE",
77+
nr_php_error_get_type_string(E_USER_NOTICE));
78+
tlib_pass_if_str_equal("Known error type", "E_STRICT",
79+
nr_php_error_get_type_string(E_STRICT));
80+
tlib_pass_if_str_equal("Known error type", "E_USER_NOTICE",
81+
nr_php_error_get_type_string(E_USER_NOTICE));
82+
tlib_pass_if_str_equal("Known error type", "E_USER_DEPRECATED",
83+
nr_php_error_get_type_string(E_USER_DEPRECATED));
84+
tlib_pass_if_str_equal("Known error type", "E_DEPRECATED",
85+
nr_php_error_get_type_string(E_DEPRECATED));
86+
tlib_pass_if_str_equal("Known error type", "E_USER_WARNING",
87+
nr_php_error_get_type_string(E_USER_WARNING));
88+
tlib_pass_if_str_equal("Known error type", "E_WARNING",
89+
nr_php_error_get_type_string(E_WARNING));
90+
tlib_pass_if_str_equal("Known error type", "E_CORE_WARNING",
91+
nr_php_error_get_type_string(E_CORE_WARNING));
92+
tlib_pass_if_str_equal("Known error type", "E_COMPILE_WARNING",
93+
nr_php_error_get_type_string(E_COMPILE_WARNING));
94+
tlib_pass_if_str_equal("Known error type", "E_RECOVERABLE_ERROR",
95+
nr_php_error_get_type_string(E_RECOVERABLE_ERROR));
96+
tlib_pass_if_str_equal("Known error type", "E_ERROR",
97+
nr_php_error_get_type_string(E_ERROR));
98+
tlib_pass_if_str_equal("Known error type", "E_USER_ERROR",
99+
nr_php_error_get_type_string(E_USER_ERROR));
100+
tlib_pass_if_str_equal("Known error type", "E_CORE_ERROR",
101+
nr_php_error_get_type_string(E_CORE_ERROR));
102+
tlib_pass_if_str_equal("Known error type", "E_COMPILE_ERROR",
103+
nr_php_error_get_type_string(E_COMPILE_ERROR));
104+
tlib_pass_if_str_equal("Known error type", "E_PARSE",
105+
nr_php_error_get_type_string(E_PARSE));
106+
}
107+
108+
void test_main(void* p NRUNUSED) {
109+
tlib_php_engine_create("" PTSRMLS_CC);
110+
111+
test_error_get_priority();
112+
test_get_error_type_string();
113+
114+
tlib_php_engine_destroy(TSRMLS_C);
115+
}

tests/integration/errors/test_E_DEPRECATED_2.php7.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"?? when",
3737
"OtherTransaction/php__FILE__",
3838
"mktime(): You should be using the time() function instead",
39-
"Error",
39+
"E_DEPRECATED",
4040
{
4141
"stack_trace": [
4242
" in mktime called at __FILE__ (??)"
@@ -62,7 +62,7 @@
6262
{
6363
"type": "TransactionError",
6464
"timestamp": "??",
65-
"error.class": "Error",
65+
"error.class": "E_DEPRECATED",
6666
"error.message": "mktime(): You should be using the time() function instead",
6767
"transactionName": "OtherTransaction\/php__FILE__",
6868
"duration": "??",

tests/integration/errors/test_E_DEPRECATED_2.php8.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"?? when",
3737
"OtherTransaction/php__FILE__",
3838
"Required parameter $b follows optional parameter $a",
39-
"Error",
39+
"E_DEPRECATED",
4040
{
4141
"stack_trace": "??",
4242
"agentAttributes": "??",
@@ -60,7 +60,7 @@
6060
{
6161
"type": "TransactionError",
6262
"timestamp": "??",
63-
"error.class": "Error",
63+
"error.class": "E_DEPRECATED",
6464
"error.message": "Required parameter $b follows optional parameter $a",
6565
"transactionName": "OtherTransaction\/php__FILE__",
6666
"duration": "??",

tests/integration/errors/test_E_DEPRECATED_2.php81.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"?? when",
3434
"OtherTransaction/php__FILE__",
3535
"Optional parameter $a declared before required parameter $b is implicitly treated as a required parameter",
36-
"Error",
36+
"E_DEPRECATED",
3737
{
3838
"stack_trace": "??",
3939
"agentAttributes": "??",
@@ -57,7 +57,7 @@
5757
{
5858
"type": "TransactionError",
5959
"timestamp": "??",
60-
"error.class": "Error",
60+
"error.class": "E_DEPRECATED",
6161
"error.message": "Optional parameter $a declared before required parameter $b is implicitly treated as a required parameter",
6262
"transactionName": "OtherTransaction\/php__FILE__",
6363
"duration": "??",

tests/integration/errors/test_E_DEPRECATED_payload1.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"??",
2828
"OtherTransaction/php__FILE__",
2929
"Function newrelic_accept_distributed_trace_payload() is deprecated. Please see https://docs.newrelic.com/docs/agents/php-agent/features/distributed-tracing-php-agent#manual for more details.",
30-
"Error",
30+
"E_DEPRECATED",
3131
{
3232
"stack_trace": [
3333
" in newrelic_accept_distributed_trace_payload called at __FILE__ (??)"
@@ -53,7 +53,7 @@
5353
{
5454
"type": "TransactionError",
5555
"timestamp": "??",
56-
"error.class": "Error",
56+
"error.class": "E_DEPRECATED",
5757
"error.message": "Function newrelic_accept_distributed_trace_payload() is deprecated. Please see https://docs.newrelic.com/docs/agents/php-agent/features/distributed-tracing-php-agent#manual for more details.",
5858
"transactionName": "OtherTransaction\/php__FILE__",
5959
"duration": "??",

tests/integration/errors/test_E_DEPRECATED_payload2.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"??",
2929
"OtherTransaction/php__FILE__",
3030
"Function newrelic_accept_distributed_trace_payload_httpsafe() is deprecated. Please see https://docs.newrelic.com/docs/agents/php-agent/features/distributed-tracing-php-agent#manual for more details.",
31-
"Error",
31+
"E_DEPRECATED",
3232
{
3333
"stack_trace": [
3434
" in newrelic_accept_distributed_trace_payload_httpsafe called at __FILE__ (??)"
@@ -54,7 +54,7 @@
5454
{
5555
"type": "TransactionError",
5656
"timestamp": "??",
57-
"error.class": "Error",
57+
"error.class": "E_DEPRECATED",
5858
"error.message": "Function newrelic_accept_distributed_trace_payload_httpsafe() is deprecated. Please see https://docs.newrelic.com/docs/agents/php-agent/features/distributed-tracing-php-agent#manual for more details.",
5959
"transactionName": "OtherTransaction\/php__FILE__",
6060
"duration": "??",

tests/integration/errors/test_E_DEPRECATED_payload3.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"??",
2929
"OtherTransaction/php__FILE__",
3030
"Function newrelic_create_distributed_trace_payload() is deprecated. Please see https://docs.newrelic.com/docs/agents/php-agent/features/distributed-tracing-php-agent#manual for more details.",
31-
"Error",
31+
"E_DEPRECATED",
3232
{
3333
"stack_trace": [
3434
" in newrelic_create_distributed_trace_payload called at __FILE__ (??)"
@@ -54,7 +54,7 @@
5454
{
5555
"type": "TransactionError",
5656
"timestamp": "??",
57-
"error.class": "Error",
57+
"error.class": "E_DEPRECATED",
5858
"error.message": "Function newrelic_create_distributed_trace_payload() is deprecated. Please see https://docs.newrelic.com/docs/agents/php-agent/features/distributed-tracing-php-agent#manual for more details.",
5959
"transactionName": "OtherTransaction\/php__FILE__",
6060
"duration": "??",

tests/integration/errors/test_E_ERROR.php7.4.php

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
/*DESCRIPTION
88
The agent should capture and report fatal errors.
9+
With PHP 7.4+, E_ERROR are fatal errors triggered by exceptions and are no longer handled
10+
using nr_php_error_cb with uses the error type as the error.class. Instead, they are handled
11+
by newrelic_exception_handler which uses the exception name as the error.class.
912
*/
1013

1114
/*SKIPIF

tests/integration/errors/test_E_RECOVERABLE.php7.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"?? when",
3131
"OtherTransaction/php__FILE__",
3232
"Object of class stdClass could not be converted to string",
33-
"Error",
33+
"E_RECOVERABLE_ERROR",
3434
{
3535
"stack_trace": [
3636
" in run_test_in_a_function called at __FILE__ (??)"
@@ -56,7 +56,7 @@
5656
{
5757
"type": "TransactionError",
5858
"timestamp": "??",
59-
"error.class": "Error",
59+
"error.class": "E_RECOVERABLE_ERROR",
6060
"error.message": "Object of class stdClass could not be converted to string",
6161
"transactionName": "OtherTransaction\/php__FILE__",
6262
"duration": "??",

0 commit comments

Comments
 (0)