Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(agent): improve exception handler instrumentation for PHPs 8.0+ #877

Merged
merged 9 commits into from
Apr 19, 2024

Conversation

lavarou
Copy link
Member

@lavarou lavarou commented Apr 12, 2024

When user exception handler is installed it can call restore_exception_handler,
and that will reset is_exception_handler flag in the wraprec which will cause
the agent not to use the correct code path after user exception handler returns.
The information that nr_php_instrument_func_end is handling return from user
exception handler needs to be stored in the segment associated with the user
exception handler.

This test reproduces the issue of the agent not recording the error when
exception handler calls restore_exception_handler.
When user exception handler is installed it can call restore_exception_handler,
and that will reset is_exception_handler flag in the wraprec which will cause
the agent not to use the correct code path after user exception handler returns.
The information that `nr_php_instrument_func_end` is handling return from user
exception handler needs to be stored in the segment associated with the user
exception handler.
@lavarou lavarou self-assigned this Apr 12, 2024
@newrelic-php-agent-bot
Copy link

newrelic-php-agent-bot commented Apr 12, 2024

Test Suite Status Result
Multiverse 9/9 passing
SOAK 51/51 passing

@codecov-commenter
Copy link

codecov-commenter commented Apr 12, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 78.76%. Comparing base (e0d2d12) to head (5013256).
Report is 2 commits behind head on dev.

Additional details and impacted files
@@           Coverage Diff           @@
##              dev     #877   +/-   ##
=======================================
  Coverage   78.76%   78.76%           
=======================================
  Files         193      193           
  Lines       27125    27129    +4     
=======================================
+ Hits        21365    21369    +4     
  Misses       5760     5760           
Flag Coverage Δ
agent-for-php-7.0 77.82% <ø> (+<0.01%) ⬆️
agent-for-php-7.1 77.58% <ø> (+<0.01%) ⬆️
agent-for-php-7.2 78.15% <ø> (+<0.01%) ⬆️
agent-for-php-7.3 78.17% <ø> (+<0.01%) ⬆️
agent-for-php-7.4 77.87% <ø> (+<0.01%) ⬆️
agent-for-php-8.0 77.90% <100.00%> (+<0.01%) ⬆️
agent-for-php-8.1 77.88% <100.00%> (+<0.01%) ⬆️
agent-for-php-8.2 77.48% <100.00%> (+<0.01%) ⬆️
agent-for-php-8.3 77.48% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

lavarou referenced this pull request Apr 12, 2024
Agent for PHPs 8.0+ uses observer API to hook into Zend Engine, which allows
offers better out-of-the-box instrumentation of user exception handler. When
observer API is used, there's no need to wrap exception handlers to record
errors.
agent/php_execute.c Outdated Show resolved Hide resolved
@lavarou lavarou marked this pull request as ready for review April 16, 2024 17:24
Co-authored-by: ZNeumann <zneumann@newrelic.com>
@zsistla
Copy link
Contributor

zsistla commented Apr 18, 2024

Do you have an idea what is causing the 8.3 failure?

FAIL - tests/integration/errors/test_oapi_uncaught_handled_exception_04.php
regex error:
expected:
Fatal error: Uncaught RuntimeException: Not able to handle, throwing another exception from handler

actual:
01 Handled uncaught exception

@lavarou
Copy link
Member Author

lavarou commented Apr 18, 2024

Do you have an idea what is causing the 8.3 failure?

FAIL - tests/integration/errors/test_oapi_uncaught_handled_exception_04.php
regex error:
expected:
Fatal error: Uncaught RuntimeException: Not able to handle, throwing another exception from handler

actual:
01 Handled uncaught exception

@zsistla Good eye! This means that PHP 8.3 started to behave like I would expect all PHPs to behave, that is honor the exception handler's stack. That was the purpose of this test! I remember reading about it somewhere... I need to split this test into two tests: for PHPs < 8.3 and for PHPs >= 8.3.

@lavarou
Copy link
Member Author

lavarou commented Apr 18, 2024

Do you have an idea what is causing the 8.3 failure?

FAIL - tests/integration/errors/test_oapi_uncaught_handled_exception_04.php
regex error:
expected:
Fatal error: Uncaught RuntimeException: Not able to handle, throwing another exception from handler

actual:
01 Handled uncaught exception

@zsistla Good eye! This means that PHP 8.3 started to behave like I would expect all PHPs to behave, that is honor the exception handler's stack. That was the purpose of this test! I remember reading about it somewhere... I need to split this test into two tests: for PHPs < 8.3 and for PHPs >= 8.3.

@zsistla This is really broken in PHP. Even not all versions of PHP 8.3 behave the same: https://3v4l.org/ARJ9A

PHP 8.3 introduced changes to handling exceptions and honoring exception handler
stack. Add tests that illustrate those behavior differences.
@lavarou
Copy link
Member Author

lavarou commented Apr 18, 2024

Do you have an idea what is causing the 8.3 failure?

FAIL - tests/integration/errors/test_oapi_uncaught_handled_exception_04.php
regex error:
expected:
Fatal error: Uncaught RuntimeException: Not able to handle, throwing another exception from handler

actual:
01 Handled uncaught exception

@zsistla Good eye! This means that PHP 8.3 started to behave like I would expect all PHPs to behave, that is honor the exception handler's stack. That was the purpose of this test! I remember reading about it somewhere... I need to split this test into two tests: for PHPs < 8.3 and for PHPs >= 8.3.

@zsistla This is really broken in PHP. Even not all versions of PHP 8.3 behave the same: https://3v4l.org/ARJ9A

Tried to address this with 4646c9c.


/*SKIPIF
<?php
if (version_compare(PHP_VERSION, "8.3", "<")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test is named php835, but this just checks for 8.3?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how to properly name the test files that need to run for PHPs 8.3.0-8.3.4 (currently the test is named tests/integration/errors/test_oapi_uncaught_handled_exception_04.php83.php) vs test files that run correctly for all 8.3.x PHPs. I'm open to suggestions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 835 naming convention was perfectly fine, but the 835 test only had this check:

<?php
if (version_compare(PHP_VERSION, "8.3", "<")) {
  die("skip: PHP < 8.3.0 not supported\n");
}

shouldn't it be

if (version_compare(PHP_VERSION, "8.3.5", "<")) {
  die("skip: PHP < 8.3.5 not supported\n");
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed 835 meant php 8.3.5 and above.

Copy link
Contributor

@zsistla zsistla Apr 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of in the name, update the description inside the test explaining why the skipif and which versions we expect it to run on.
Then you can have an 83a, 83b, etc to indicate it is the same test but differences are needed due to variants 8.3?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestions. See 5013256.

@zsistla
Copy link
Contributor

zsistla commented Apr 18, 2024

Not a blocker, but why do the text cases need _oapi_ in the name? It doesn't seem to meaningfully add information about the test case.

@lavarou
Copy link
Member Author

lavarou commented Apr 18, 2024

Not a blocker, but why do the text cases need _oapi_ in the name? It doesn't seem to meaningfully add information about the test case.

I included _oapi_ in the name because they only exercise _oapi_ codepaths. I'm open to suggestions how to better convey that information without including _oapi_ in the name.

Explain why different tests are needed in test description rather than test
filename.
@lavarou
Copy link
Member Author

lavarou commented Apr 18, 2024

Not a blocker, but why do the text cases need _oapi_ in the name? It doesn't seem to meaningfully add information about the test case.

I included _oapi_ in the name because they only exercise _oapi_ codepaths. I'm open to suggestions how to better convey that information without including _oapi_ in the name.

For 8+ there are only oapi code paths, we didn't include oapi on any other 8+ only tests or non-oapi on any pre8 tests. just seemed redundant.

Removed _oapi part from test filename in e8669dd.

@zsistla
Copy link
Contributor

zsistla commented Apr 18, 2024

LGTM!

@lavarou lavarou merged commit eecccd5 into dev Apr 19, 2024
62 checks passed
@lavarou lavarou deleted the fix/agent/oapi-exception-handler-instrumentation branch April 19, 2024 13:19
@lavarou lavarou added this to the next-release milestone Apr 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants