Find Segfaults in PHP like a boss
Section intitulée a-bit-of-historyA bit of history
Sometimes, a segfault happens, but you don’t know where, and your PHP installation does not have tools to find it. Or sometime, you think PHP is hanging, but you don’t know where. You may use xdebug, but you don’t want to click so many times on the « next call » button.
To address theses issues, I used to use this hack.
register_tick_function(function() {
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
$last = reset($bt);
$info = sprintf("%s +%d\n", $last['file'], $last['line']);
file_put_contents('/tmp/segfault.txt', $info, FILE_APPEND);
// or
// file_put_contents('php://output', $info, FILE_APPEND);
});
declare(ticks=1);
The code is pretty small but it could appear really weird. Don’t worry, I will explain it.
- We register a tick function. A tick is an event emitted by PHP when a very low-level (tickable) statements is executed. This function will be executed on each PHP Tick and will print the last executed line.
- We tell PHP to fire an event for all possible tick.
- Profit… Thanks to that, it’s possible to find the last successfully executed line.
But, what I did not know, is that it worked because of a PHP Bug: declare(ticks=1)
is not supposed
to leak to other files. This has been fixed in PHP 7.0 and so my hack does not
work anymore.
Section intitulée let-s-use-a-strike-bigger-strike-cleaner-hackLet’s use a bigger cleaner hack
So If I want to continue to use this debug method, I need to put by hand
declare(ticks=1)
on every PHP files… Boring! I could write a simple tool
that will do that for me but I don’t want to modify all my vendors.
So I decided to use PHP Stream Wrapper and Stream Filter. Theses PHP features are not really well known, but they are very powerful. I encourage you to read more about it.
This new implementation replaces the default file
and phar
stream wrapper
implementation of PHP to be able to automatically add declare(ticks=1)
on each
PHP file. But this is done only in memory, not physically on the disk.
Section intitulée usageUsage
To use it, copy the HardCoreDebugLogger.php
file somewhere on your disk and
then add the following line in your code:
require '/path/to/HardCoreDebugLogger.php'
HardCoreDebugLogger::register();
By default, the traces will be displayed on STDOUT, but you can change it to save it a file:
require '/path/to/HardCoreDebugLogger.php'
HardCoreDebugLogger::register('/tmp/trace.txt');
Section intitulée demoDemo?
First, you will learn how to generate a segfault with PHP. You should not try to reproduce it at home!
// require __DIR__.'/HardCoreDebugLogger.php';
// declare(ticks=1); // We need tick in this file
// HardCoreDebugLogger::register();
function a()
{
b();
}
function b()
{
c();
}
function c()
{
$a = 1 + 2;
"".(new Crash());
}
class Crash
{
public function __tostring()
{
return "".$this;
}
}
a();
If you try to execute this code, you will get the following:
$ php segfault.php
Segmentation fault (core dumped)
Not easy to find the segfault, isn’t it? And now, imagine you have 100 000 lines of codes 😱
We need to uncomment the 3 first lines to get the following:
$ php segfault.php
1555494674.7385 /tmp/56dfc48fb7e807dd2a229813da89a0dc/segfault.php +5
1555494674.7385 /tmp/56dfc48fb7e807dd2a229813da89a0dc/segfault.php +10
1555494674.7385 /tmp/56dfc48fb7e807dd2a229813da89a0dc/segfault.php +16
1555494674.7385 /tmp/56dfc48fb7e807dd2a229813da89a0dc/segfault.php +23
1555494674.7385 /tmp/56dfc48fb7e807dd2a229813da89a0dc/segfault.php +25
1555494674.7386 /tmp/56dfc48fb7e807dd2a229813da89a0dc/segfault.php +20
Segmentation fault (core dumped)
And now, we know the last successful executed line is the line 20. So the segfault should be triggered by:
"".(new Crash());
Section intitulée creditCredit
Writing a Stream Wrapper is boring, so I would like to credit Anthony Ferrara for his work on php-preprocessor.
Commentaires et discussions
Ces clients ont profité de notre expertise
Canal+ a sollicité l’expertise technique de JoliCode dans le cadre d’un audit technique du framework PHP employé par l’entreprise pour ses développements. À l’aide de notre outillage projet, nous avons évalué la qualité du framework et son adéquation avec l’écosystème PHP actuel, et émis une série de recommandations en vue de la modernisation du socle…
L’équipe de Finarta a fait appel à JoliCode pour le développement de leur plateforme Web. Basée sur le framework Symfony 2, l’application est un réseau privé de galerie et se veut être une place de communication et de vente d’oeuvres d’art entre ses membres. Pour cela, de nombreuses règles de droits ont été mises en places et une administration poussée…
Dans le cadre d’une refonte complète de son architecture Web, Expertissim a sollicité l’expertise de JoliCode afin de tenir les délais et le niveau de qualité attendus. Le domaine métier d’Expertissim n’est pas trivial : les spécificités du marché de l’art apportent une logique métier bien particulière et un processus complexe. La plateforme propose…