diff --git a/agent/php_execute.c b/agent/php_execute.c index 7bb875ec1..f7ea011e5 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -1946,6 +1946,14 @@ static void nr_php_instrument_func_begin(NR_EXECUTE_PROTO) { if (NULL == wraprec) { return; } + + /* Store information that the segment is exception handler segment directly in + * the segment, because exception handler can call restore_exception_handler, + * and that will reset is_exception_handler flag in the wraprec */ + if (wraprec->is_exception_handler) { + segment->is_exception_handler = 1; + } + /* * If a function needs to have arguments modified, do so in * nr_zend_call_oapi_special_before. @@ -2027,7 +2035,7 @@ static void nr_php_instrument_func_end(NR_EXECUTE_PROTO) { wraprec = segment->wraprec; - if (wraprec && wraprec->is_exception_handler) { + if (segment->is_exception_handler) { /* * After running the exception handler segment, create an error from * the exception it handled, and save the error in the transaction. diff --git a/axiom/nr_segment.h b/axiom/nr_segment.h index 5d68e9226..56d972579 100644 --- a/axiom/nr_segment.h +++ b/axiom/nr_segment.h @@ -192,6 +192,8 @@ typedef struct _nr_segment_t { */ void* wraprec; /* wraprec, if one is associated with this segment, to reduce wraprec lookups */ + int is_exception_handler; /* 1 if segment is associated with exception + handler, 0 otherwise */ #endif } nr_segment_t; diff --git a/tests/integration/errors/test_uncaught_handled_exception_01.php b/tests/integration/errors/test_uncaught_handled_exception_01.php new file mode 100644 index 000000000..75b6ee3f4 --- /dev/null +++ b/tests/integration/errors/test_uncaught_handled_exception_01.php @@ -0,0 +1,170 @@ +=")) { + die("skip: PHP >= 8.3.0 not supported\n"); +} +*/ + + +/*EXPECT_ERROR_EVENTS*/ +/* +[ + "?? agent run id", + { + "reservoir_size": "??", + "events_seen": 1 + }, + [ + [ + { + "type": "TransactionError", + "timestamp": "??", + "error.class": "RuntimeException", + "error.message": "Uncaught exception 'RuntimeException' with message 'Expected unexpected happened' in __FILE__:??", + "transactionName": "OtherTransaction\/php__FILE__", + "duration": "??", + "nr.transactionGuid": "??", + "guid": "??", + "sampled": true, + "priority": "??", + "traceId": "??", + "spanId": "??" + }, + {}, + "??" + ] + ] +] +*/ + +/*EXPECT_SPAN_EVENTS*/ +/* +[ + "?? agent run id", + { + "reservoir_size": 10000, + "events_seen": 4 + }, + [ + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "OtherTransaction\/php__FILE__", + "timestamp": "??", + "duration": "??", + "priority": "??", + "sampled": true, + "nr.entryPoint": true, + "transaction.name": "OtherTransaction\/php__FILE__" + }, + {}, + {} + ], + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "Custom\/call_throw_it", + "timestamp": "??", + "duration": "??", + "priority": "??", + "sampled": true, + "parentId": "??" + }, + {}, + { + "error.message": "Uncaught exception 'RuntimeException' with message 'Expected unexpected happened' in __FILE__:??", + "error.class": "RuntimeException" + } + ], + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "Custom\/throw_it", + "timestamp": "??", + "duration": "??", + "priority": "??", + "sampled": true, + "parentId": "??" + }, + {}, + { + "error.message": "Uncaught exception 'RuntimeException' with message 'Expected unexpected happened' in __FILE__:??", + "error.class": "RuntimeException" + } + ], + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "Custom\/user_exception_handler_02", + "timestamp": "??", + "duration": "??", + "priority": "??", + "sampled": true, + "parentId": "??" + }, + {}, + {} + ] + ] +] +*/ + + +/*EXPECT_REGEX +Fatal error: Uncaught RuntimeException: Not able to handle, throwing another exception from handler +*/ + +function user_exception_handler_01(Throwable $ex) { + echo "01 Handled uncaught exception"; +} + +function user_exception_handler_02(Throwable $ex) { + restore_exception_handler(); + throw new RuntimeException("Not able to handle, throwing another exception from handler"); + echo "Should never see this"; +} + +function throw_it() { + throw new RuntimeException('Expected unexpected happened'); +} + +function call_throw_it() { + throw_it(); +} + +set_exception_handler('user_exception_handler_01'); +set_exception_handler('user_exception_handler_02'); + +call_throw_it(); diff --git a/tests/integration/errors/test_uncaught_handled_exception_04.php83a.php b/tests/integration/errors/test_uncaught_handled_exception_04.php83a.php new file mode 100644 index 000000000..b71d37150 --- /dev/null +++ b/tests/integration/errors/test_uncaught_handled_exception_04.php83a.php @@ -0,0 +1,184 @@ +=")) { + die("skip: PHP >= 8.3.5 not supported\n"); +} +*/ + + +/*EXPECT_ERROR_EVENTS*/ +/* +[ + "?? agent run id", + { + "reservoir_size": "??", + "events_seen": 1 + }, + [ + [ + { + "type": "TransactionError", + "timestamp": "??", + "error.class": "RuntimeException", + "error.message": "Uncaught exception 'RuntimeException' with message 'Expected unexpected happened' in __FILE__:??", + "transactionName": "OtherTransaction\/php__FILE__", + "duration": "??", + "nr.transactionGuid": "??", + "guid": "??", + "sampled": true, + "priority": "??", + "traceId": "??", + "spanId": "??" + }, + {}, + "??" + ] + ] +] +*/ + +/*EXPECT_SPAN_EVENTS*/ +/* +[ + "?? agent run id", + { + "reservoir_size": 10000, + "events_seen": 4 + }, + [ + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "OtherTransaction\/php__FILE__", + "timestamp": "??", + "duration": "??", + "priority": "??", + "sampled": true, + "nr.entryPoint": true, + "transaction.name": "OtherTransaction\/php__FILE__" + }, + {}, + {} + ], + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "Custom\/call_throw_it", + "timestamp": "??", + "duration": "??", + "priority": "??", + "sampled": true, + "parentId": "??" + }, + {}, + { + "error.message": "Uncaught exception 'RuntimeException' with message 'Expected unexpected happened' in __FILE__:??", + "error.class": "RuntimeException" + } + ], + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "Custom\/throw_it", + "timestamp": "??", + "duration": "??", + "priority": "??", + "sampled": true, + "parentId": "??" + }, + {}, + { + "error.message": "Uncaught exception 'RuntimeException' with message 'Expected unexpected happened' in __FILE__:??", + "error.class": "RuntimeException" + } + ], + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "Custom\/user_exception_handler_02", + "timestamp": "??", + "duration": "??", + "priority": "??", + "sampled": true, + "parentId": "??" + }, + {}, + {} + ] + ] +] +*/ + + +/*EXPECT_REGEX +01 Handled uncaught exception +*/ + +function user_exception_handler_01(Throwable $ex) { + echo "01 Handled uncaught exception"; +} + +function user_exception_handler_02(Throwable $ex) { + restore_exception_handler(); + throw new RuntimeException("Not able to handle, throwing another exception from handler"); + echo "Should never see this"; +} + +function throw_it() { + throw new RuntimeException('Expected unexpected happened'); +} + +function call_throw_it() { + throw_it(); +} + +set_exception_handler('user_exception_handler_01'); +set_exception_handler('user_exception_handler_02'); + +call_throw_it(); diff --git a/tests/integration/errors/test_uncaught_handled_exception_04.php83b.php b/tests/integration/errors/test_uncaught_handled_exception_04.php83b.php new file mode 100644 index 000000000..54f949619 --- /dev/null +++ b/tests/integration/errors/test_uncaught_handled_exception_04.php83b.php @@ -0,0 +1,177 @@ +