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
Travailler sur un projet US présente plusieurs défis. En premier lieu : Le décalage horaire. Afin d’atténuer cet obstacle, nous avons planifié les réunions en début d’après-midi en France, ce qui permet de trouver un compromis acceptable pour les deux parties. Cette approche assure une participation optimale des deux côtés et facilite la communication…
JoliCode accompagne l’équipe technique Dayuse dans l’optimisation des performances de sa plateforme. Nous sommes intervenus sur différents sujets : La fonctionnalité de recherche d’hôtels, en remplaçant MongoDB et Algolia par Redis et Elasticsearch. La mise en place d’un workflow de réservation, la migration d’un site en Twig vers une SPA à base de…
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…