Hello yet again internals,
I'd like to formally propose a continuation of the original error
backtraces RFC (https://wiki.php.net/rfc/error_backtraces):
https://wiki.php.net/rfc/error_backtraces_v2
We recently had a production incident that might have been shortened,
had we been able to identify an errant infinite loop in our code from
the backtrace of a PHP timeout error. It's likely that there are
plenty of other error conditions that would be easier to debug with a
stacktrace, too.
The original RFC is inactive, but I've read the proposal and resulting
discussion and I believe that @nikic's suggestion is a viable way to
address the RFC's issues. I've implemented that suggestion here:
https://github.com/php/php-src/pull/17056
The RFC currently has two voting choices, the first is to accept an
INI setting to configure an error mask for backtraces, and the second
is to choose the default value for the error mask.
Please take a look at let me know what you think.
Thank you!
Hi
Thank you for your RFC. This time I'm making sure to comment already
during the discussion period :-)
https://wiki.php.net/rfc/error_backtraces_v2
The RFC currently has two voting choices, the first is to accept an
INI setting to configure an error mask for backtraces, and the second
is to choose the default value for the error mask.
You are not restricted to Yes/No votes for the voting widgets. I suggest
to rephrase the second voting widget to:
"What should the default value of the 'error_backtrace_recording' INI be?"
- E_FATAL_ERRORS
- 0
This avoids the complicated sentence above the voting widget.
The “php.ini Defaults” section should probably refer to the voting
widget as well.
The first voting widget would probably be better phrased as:
"Add the 'error_backtrace_recording' INI as described?"
As for the RFC contents, I had a look at the previous discussion. Dan
remarked that adding the backtrace to error_get_last()
would keep any
parameters to functions in the stack trace alive until the next error
occurs. This can easily break RAII patterns, such as this example:
https://3v4l.org/i6EOv
<?php
class Lock
{
public function __construct() { echo "Locked\n"; }
public function __destruct() { echo "Unlocked\n"; }
}
function do_while_locked(Lock $lock)
{
// The exception implicitly keeps the lock alive.
$GLOBALS['x'] = new Exception();
echo "Doing something\n";
}
function foo()
{
$lock = new Lock();
do_while_locked($lock);
echo "Done\n";
}
foo();
echo "After\n";
Normally one would expect the "Unlocked" to be printed between "Done"
and "After", but this is not the case, because the Exception in the
global keeps a reference to $lock within its backtrace. This would be
the same if the Exception would instead be a trigger_error()
that
remains available via error_get_last()
.
In other words, we would have a INI setting that changes application
behavior in subtle ways, which is a no-no.
For the previous RFC this apparently was a non-issue, because the stack
was stored as a string. But for your RFC this is an issue, because based
on the tests it's stored as an array.
At the very least this gotcha should clearly be noted in the RFC text.
Best regards
Tim Düsterhus
Hello,
Hi
Thank you for your RFC. This time I'm making sure to comment already
during the discussion period :-)
Haha, thank you! I appreciate the feedback as always.
https://wiki.php.net/rfc/error_backtraces_v2
The RFC currently has two voting choices, the first is to accept an
INI setting to configure an error mask for backtraces, and the second
is to choose the default value for the error mask.You are not restricted to Yes/No votes for the voting widgets. I suggest
to rephrase the second voting widget to:"What should the default value of the 'error_backtrace_recording' INI be?"
- E_FATAL_ERRORS
- 0
This avoids the complicated sentence above the voting widget.
Good point.
The “php.ini Defaults” section should probably refer to the voting
widget as well.The first voting widget would probably be better phrased as:
"Add the 'error_backtrace_recording' INI as described?"
I'm a little nervous about this change, because I was hoping to be a
little more open-ended. For example, I'm not tied to the name
'error_backtrace_recording', so I wouldn't want someone to vote 'no'
because they didn't like the setting name. Secondly, maybe we don't
want the zend global (which I'll actually get to in a bit), and
similarly I wouldn't want someone to vote 'no' because of that; I
don't actually care about whether it's a global or not, I only care
about getting backtraces for fatal errors.
Maybe this is a little bit of an overreaction from my previous RFC, in
that I'm trying to give myself more room for differing implementations
while achieving my primary goal. What are your thoughts here?
<snip> Normally one would expect the "Unlocked" to be printed between "Done" and "After", but this is not the case, because the Exception in the global keeps a reference to $lock within its backtrace. This would be the same if the Exception would instead be a `trigger_error()` that remains available via `error_get_last()`.In other words, we would have a INI setting that changes application
behavior in subtle ways, which is a no-no.
Great example, thank you. Since you are stating this is problematic -
and I don't necessarily disagree - does this make allowing backtraces
as implemented for non-fatal errors a non-starter? Presumably having a
backtrace for a fatal error doesn't matter since everything will soon
be destroyed in the process of shutting down.
For the previous RFC this apparently was a non-issue, because the stack
was stored as a string. But for your RFC this is an issue, because based
on the tests it's stored as an array.
That's a good point; I hadn't made that connection when I made the
change to use an array (which was an add-on suggestion by nikic,
separate from the error mask suggestion). I think this is
fundamentally an issue even without the change to error_get_last,
since I'm storing the backtrace in a zend global.
I would have preferred to make this a parameter to zend_error_cb, but
I thought that changing the signature of that method might be too much
of a backwards compatibility break (though this would only affect
extensions). Making it a parameter would shorten the lifetime of the
trace, and PHP's zend_error_cb could store that trace as a string for
error_get_last.
At the very least this gotcha should clearly be noted in the RFC text.
Right, but of course if that means the RFC will fail, I'd rather
implement it differently upfront.
Thinking about the options I see here:
- We could make error backtraces non-configurable and only for fatal
errors, which I believe means we can ignore the lifecycle issue, since
application code will switch into shutdown mode. We should still
mention the caveat, however, as it still means that the destructors
won't fire until after all of the application's shutdown code has run. - We could keep error backtraces configurable as it is now in the RFC,
but change zend_error_cb to take the trace as an optional parameter,
and not use a global. php_error_cb could store the trace as a string,
which could be used by error_get_last. This feels slightly better to
me than the next option. - We could keep error backtraces configurable as it is now in the RFC,
but store a string in the zend global. For some reason, this doesn't
feel "right" to me, since we'd be giving up the structured data in the
array. - We could remove arguments from the backtrace. This feels wrong as
well, since it would make them different from exceptions. - We could keep the status quo, and note the caveat.
Are there any other options people can think of? What are internals'
preferences for this?
Hi
Am 2024-12-06 02:08, schrieb Eric Norris:
The “php.ini Defaults” section should probably refer to the voting
widget as well.The first voting widget would probably be better phrased as:
"Add the 'error_backtrace_recording' INI as described?"
I'm a little nervous about this change, because I was hoping to be a
little more open-ended. For example, I'm not tied to the name
'error_backtrace_recording', so I wouldn't want someone to vote 'no'
because they didn't like the setting name. Secondly, maybe we don't
want the zend global (which I'll actually get to in a bit), and
similarly I wouldn't want someone to vote 'no' because of that; I
don't actually care about whether it's a global or not, I only care
about getting backtraces for fatal errors.
I believe RFCs should be sufficiently precise so that one is able to
implement the proposal reasonably unambiguously based on the RFC text
alone. Given that you are concerned about the INI setting causing the
RFC not to pass, it should be evident that this is such an important
topic that it needs the RFC to be clear about what is proposed for
voters to be able to vote on the full picture.
Normally one would expect the "Unlocked" to be printed between "Done"
and "After", but this is not the case, because the Exception in the
global keeps a reference to $lock within its backtrace. This would be
the same if the Exception would instead be atrigger_error()
that
remains available viaerror_get_last()
.In other words, we would have a INI setting that changes application
behavior in subtle ways, which is a no-no.Great example, thank you. Since you are stating this is problematic -
and I don't necessarily disagree - does this make allowing backtraces
as implemented for non-fatal errors a non-starter? Presumably having a
backtrace for a fatal error doesn't matter since everything will soon
be destroyed in the process of shutting down.
Yes, I agree that having a stacktracke for fatal errors is a non-issue.
For non-fatal errors the INI setting definitely is a problem.
Unconditionally having a stacktrace for non-fatal errors I'm am not yet
sure if this would be a problem or if it would be fine if it is a
RFC-agreed-upon (breaking) change. Given that it's common to turn
warnings and notices into Exceptions and thus to treat them as hard
errors, I'd be inclined to say that the value-add of having backtraces
for them is fairly small compared to the possible impact and that it
makes sense not to include this in the RFC at all.
For the previous RFC this apparently was a non-issue, because the
stack
was stored as a string. But for your RFC this is an issue, because
based
on the tests it's stored as an array.I would have preferred to make this a parameter to zend_error_cb, but
I thought that changing the signature of that method might be too much
of a backwards compatibility break (though this would only affect
extensions). Making it a parameter would shorten the lifetime of the
Making breaking changes to the internal API is generally fine if there
is a reason for making the change.
Best regards
Tim Düsterhus
Yes, I agree that having a stacktracke for fatal errors is a non-issue.
For non-fatal errors the INI setting definitely is a problem.
Unconditionally having a stacktrace for non-fatal errors I'm am not yet
sure if this would be a problem or if it would be fine if it is a
RFC-agreed-upon (breaking) change. Given that it's common to turn
warnings and notices into Exceptions and thus to treat them as hard
errors, I'd be inclined to say that the value-add of having backtraces
for them is fairly small compared to the possible impact and that it
makes sense not to include this in the RFC at all.
I've updated the RFC to v2.0: I'm now proposing an on-off switch to
enable backtraces for fatal errors only.
That said, after updating the RFC I've discussed an alternative with
my colleagues that I think is worth also discussing here. It occurred
to me that the main issue is including the backtrace in
error_get_last, but we want the backtrace in error_get_last as there
is no other way for user code to get the backtrace for a fatal error,
aside from calling error_get_last in shutdown code.
If instead we did call the error handler set up in set_error_handler -
or call something like that - users could gather the backtrace with
debug_backtrace themselves, and log it how they want, or send it
elsewhere, whatever. Internally PHP wouldn't need to do anything about
the backtrace.
Of course users should not be able to actually handle fatal errors,
and execution should immediately bail out after calling the handler as
it does today upon encountering a fatal error. This could be addressed
by either informing users that returning true or false makes no
difference for fatal errors, or by introducing a newer error handler
concept that had that behavior. There may be a backwards compatibility
issue with the former - users might not have written their error
handling code with the expectation that it could encounter fatal
errors - and that too could be addressed by either making this an INI
setting, or again, introducing a newer error handler concept.
Either way, exposing fatal errors to user-level code may be a better
solution to the problem of errors not having a backtrace. Passing
fatal errors to user handler code would potentially make error
handling more consistent, and rely less on global state like
error_get_last. Also note that calling user-level code after a fatal
error is not new; user code registered in register_shutdown_function
is called after a fatal error, which again is one source of a need for
something like error_get_last.
I'm curious if anyone has any immediate reactions - positive or
negative - to this line of thinking. Perfect is the enemy of good, so
I'd accept moving forward with the RFC as it is now if it is more
tenable; I think this idea is worth exploring a little bit before
doing so, however.
Hi,
Hello yet again internals,
I'd like to formally propose a continuation of the original error
backtraces RFC (https://wiki.php.net/rfc/error_backtraces):
I would prefer this to be disabled by default (even for fatal errors).
Users need to be aware that they are opting to multi line logging
potentially.
Cheers
Jakub
Hi,
On Thu, Dec 5, 2024 at 10:02 PM Eric Norris eric.t.norris@gmail.com
wrote:Hello yet again internals,
I'd like to formally propose a continuation of the original error
backtraces RFC (https://wiki.php.net/rfc/error_backtraces):I would prefer this to be disabled by default (even for fatal errors).
Users need to be aware that they are opting to multi line logging
potentially.
I know that there's a voting option for that - just saying my preference
for that vote basically.
Hello,
We're nearing the two-week mark, so I'm curious if anyone has any
other feedback or points of discussion for this RFC.
In particular, I'm interested to know if anyone has thoughts on a
suggestion I had in one of the reply threads - that we could consider
something to the effect of allowing a user-supplied function for fatal
errors - and if that is worth pursuing. If not, I'll continue with the
RFC as-is and plan to put it up for a vote when eligible.
Thanks!
Hello,
We're nearing the two-week mark, so I'm curious if anyone has any
other feedback or points of discussion for this RFC.In particular, I'm interested to know if anyone has thoughts on a
suggestion I had in one of the reply threads - that we could consider
something to the effect of allowing a user-supplied function for fatal
errors - and if that is worth pursuing. If not, I'll continue with the
RFC as-is and plan to put it up for a vote when eligible.Thanks!
I've added a voting choice, "If fatal_error_backtraces defaults to 1,
default value in run-tests.php", in response to discussion in the
GitHub PR.
Le 5 déc. 2024 à 21:57, Eric Norris eric.t.norris@gmail.com a écrit :
Hello yet again internals,
I'd like to formally propose a continuation of the original error
backtraces RFC (https://wiki.php.net/rfc/error_backtraces):
Hi,
Would be reasonable to be able to recover the stack trace of the error as a string, in addition to the proposed format? In general, it is the format I use in my error/exception handlers for convenience.
A utility function that stringify the trace would be sufficient, as the operation is non-trivial.
—Claude
Claude Pache claude.pache@gmail.com hat am 18.12.2024 22:59 CET geschrieben:
Le 5 déc. 2024 à 21:57, Eric Norris eric.t.norris@gmail.com a écrit :
Hello yet again internals,
I'd like to formally propose a continuation of the original error
backtraces RFC (https://wiki.php.net/rfc/error_backtraces):Hi,
Would be reasonable to be able to recover the stack trace of the error as a string, in addition to the proposed format? In general, it is the format I use in my error/exception handlers for convenience.
A utility function that stringify the trace would be sufficient, as the operation is non-trivial.
—Claude
Some years ago, we had another rfc on this topic:
https://wiki.php.net/rfc/error_handler_callback_parameters_passed_by_reference
Best Regards
Thomas
Hi,
Would be reasonable to be able to recover the stack trace of the error as a string, in addition to the proposed format? In general, it is the format I use in my error/exception handlers for convenience.
A utility function that stringify the trace would be sufficient, as the operation is non-trivial.
It is reasonable to provide such a utility function, though I could
imagine some might argue that a function like that is better left to
userland libraries. In any case, I think I would prefer to leave that
for a future RFC in order to keep the scope of this one small.
Hello yet again internals,
I'd like to formally propose a continuation of the original error
backtraces RFC (https://wiki.php.net/rfc/error_backtraces):https://wiki.php.net/rfc/error_backtraces_v2
We recently had a production incident that might have been shortened,
had we been able to identify an errant infinite loop in our code from
the backtrace of a PHP timeout error. It's likely that there are
plenty of other error conditions that would be easier to debug with a
stacktrace, too.The original RFC is inactive, but I've read the proposal and resulting
discussion and I believe that @nikic's suggestion is a viable way to
address the RFC's issues. I've implemented that suggestion here:https://github.com/php/php-src/pull/17056
The RFC currently has two voting choices, the first is to accept an
INI setting to configure an error mask for backtraces, and the second
is to choose the default value for the error mask.Please take a look at let me know what you think.
Thank you!
As it has now been two weeks and discussion has quieted down, I've now
opened the RFC for voting. Once again I've added an additional week
due to the holidays, so this will remain open until 2025-01-10
00:00:00 UTC.
Thank you for the feedback so far.