Imprimer la pile d'appels PHP

Réponses:

123

Si vous souhaitez générer une trace, vous recherchez debug_backtraceet / ou debug_print_backtrace.


Le premier vous donnera, par exemple, un tableau comme celui-ci (en citant le manuel) :

array(2) {
[0]=>
array(4) {
    ["file"] => string(10) "/tmp/a.php"
    ["line"] => int(10)
    ["function"] => string(6) "a_test"
    ["args"]=>
    array(1) {
      [0] => &string(6) "friend"
    }
}
[1]=>
array(4) {
    ["file"] => string(10) "/tmp/b.php"
    ["line"] => int(2)
    ["args"] =>
    array(1) {
      [0] => string(10) "/tmp/a.php"
    }
    ["function"] => string(12) "include_once"
  }
}


Apparemment, ils ne videront pas le tampon d'E / S, mais vous pouvez le faire vous-même, avec flushet / ou ob_flush.

(voir la page de manuel du premier pour savoir pourquoi le "et / ou" ;-))

Pascal MARTIN
la source
7
cela fait régulièrement manquer de mémoire à mon php. Je recommande la solution de Tobiasz.
peedee
Si vous avez du mal à lire / comprendre, je recommande également la solution de
Tobiasz
1
@peedee, il suffit de fournir l'un des DEBUG_BACKTRACE_IGNORE_ARGSparamètres facultatifs ; ce qui les rend fonctionnellement équivalents à(new \Exception())->getTraceAsString()
567

Plus lisible que debug_backtrace():

$e = new \Exception;
var_dump($e->getTraceAsString());

#2 /usr/share/php/PHPUnit/Framework/TestCase.php(626): SeriesHelperTest->setUp()
#3 /usr/share/php/PHPUnit/Framework/TestResult.php(666): PHPUnit_Framework_TestCase->runBare()
#4 /usr/share/php/PHPUnit/Framework/TestCase.php(576): PHPUnit_Framework_TestResult->run(Object(SeriesHelperTest))
#5 /usr/share/php/PHPUnit/Framework/TestSuite.php(757): PHPUnit_Framework_TestCase->run(Object(PHPUnit_Framework_TestResult))
#6 /usr/share/php/PHPUnit/Framework/TestSuite.php(733): PHPUnit_Framework_TestSuite->runTest(Object(SeriesHelperTest), Object(PHPUnit_Framework_TestResult))
#7 /usr/share/php/PHPUnit/TextUI/TestRunner.php(305): PHPUnit_Framework_TestSuite->run(Object(PHPUnit_Framework_TestResult), false, Array, Array, false)
#8 /usr/share/php/PHPUnit/TextUI/Command.php(188): PHPUnit_TextUI_TestRunner->doRun(Object(PHPUnit_Framework_TestSuite), Array)
#9 /usr/share/php/PHPUnit/TextUI/Command.php(129): PHPUnit_TextUI_Command->run(Array, true)
#10 /usr/bin/phpunit(53): PHPUnit_TextUI_Command::main()
#11 {main}"
Tobias Cudnik
la source
50
Merde, c'est tellement mieux, pourquoi ne pourraient-ils pas en faire la sortie par défaut pour debug_print_backtrace ()? Aurait pu ajouter un paramètre booléen "returnTrace" pour ceux qui le veulent dans une variable, pas en écho, et ça aurait été parfait!
jurchiks
1
Je ne sais pas combien de mois j'ai essayé de comprendre comment faire ça, je n'ai jamais pensé que ça marcherait
WojonsTech
Cette solution semble également prendre moins de mémoire que de capturer la sortie de debug_backtrace () sous forme de tableau, puis de l'imprimer à l'aide de print_r (), ce que j'avais fait jusqu'à ce que je voie cela!
Peter
5
Je cherchais un moyen de limiter debug_backtracepour ne renvoyer que le premier niveau dans le stacktrace - cette solution fait le travail pour moi. Je vous remercie!
ankr
3
@Andrew print_rconservera tous les messages.
mopo922
41

Pour enregistrer la trace

$e = new Exception;
error_log(var_export($e->getTraceAsString(), true));

Merci @Tobiasz

Sydwell
la source
35

Backtrace vide un tas de déchets dont vous n'avez pas besoin. Cela prend très longtemps, difficile à lire. Tout ce que vous voulez habituellement, c'est "ce qui a appelé quoi d'où?" Voici une solution de fonction statique simple. Je le mets généralement dans une classe appelée «débogage», qui contient toutes mes fonctions utilitaires de débogage.

class debugUtils {
    public static function callStack($stacktrace) {
        print str_repeat("=", 50) ."\n";
        $i = 1;
        foreach($stacktrace as $node) {
            print "$i. ".basename($node['file']) .":" .$node['function'] ."(" .$node['line'].")\n";
            $i++;
        }
    } 
}

Vous l'appelez comme ceci:

debugUtils::callStack(debug_backtrace());

Et il produit une sortie comme celle-ci:

==================================================
 1. DatabaseDriver.php::getSequenceTable(169)
 2. ClassMetadataFactory.php::loadMetadataForClass(284)
 3. ClassMetadataFactory.php::loadMetadata(177)
 4. ClassMetadataFactory.php::getMetadataFor(124)
 5. Import.php::getAllMetadata(188)
 6. Command.php::execute(187)
 7. Application.php::run(194)
 8. Application.php::doRun(118)
 9. doctrine.php::run(99)
 10. doctrine::include(4)
==================================================
Don Briggs
la source
33

Étrange que personne n'ait publié de cette façon:

debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

Cela imprime en fait la trace sans les ordures - juste quelle méthode a été appelée et où.

Abstrait
la source
2
En effet, vraiment équivalent à la principale solution votée, et plus court. Merci
brunetton
9

Si vous voulez une trace de pile qui ressemble beaucoup à la façon dont php formate la trace de pile d'exception, utilisez cette fonction, j'ai écrit:

function debug_backtrace_string() {
    $stack = '';
    $i = 1;
    $trace = debug_backtrace();
    unset($trace[0]); //Remove call to this function from stack trace
    foreach($trace as $node) {
        $stack .= "#$i ".$node['file'] ."(" .$node['line']."): "; 
        if(isset($node['class'])) {
            $stack .= $node['class'] . "->"; 
        }
        $stack .= $node['function'] . "()" . PHP_EOL;
        $i++;
    }
    return $stack;
} 

Cela retournera une trace de pile formatée comme ceci:

#1 C:\Inetpub\sitename.com\modules\sponsors\class.php(306): filePathCombine()
#2 C:\Inetpub\sitename.com\modules\sponsors\class.php(294): Process->_deleteImageFile()
#3 C:\Inetpub\sitename.com\VPanel\modules\sponsors\class.php(70): Process->_deleteImage()
#4 C:\Inetpub\sitename.com\modules\sponsors\process.php(24): Process->_delete() 
TroySteven
la source
2
ou tout simplement$e = new Exception; echo $e->getTraceAsString();
Brad Kent
Brad, cette solution ne supprime pas le dernier élément de la trace de la pile, donc vous n'affichez pas l'élément de trace provoqué par la nouvelle exception
TroySteven
8
var_dump(debug_backtrace());

Est-ce que ça fait ce que vous voulez?

inkedmn
la source
6

Tu vois debug_print_backtrace. Je suppose que vous pouvez appeler flushaprès si vous le souhaitez.

Martin Geisler
la source
4

phptrace est un excellent outil pour imprimer la pile PHP à tout moment quand vous le souhaitez sans installer d'extensions.

Il y a deux fonctions principales de phptrace: premièrement, imprimer la pile d'appels de PHP qui n'a pas besoin d'installer quoi que ce soit, deuxièmement, tracer les flux d'exécution de php qui doivent installer l'extension qu'il fournit.

comme suit:

$ ./phptrace -p 3130 -s             # phptrace -p <PID> -s
phptrace 0.2.0 release candidate, published by infra webcore team
process id = 3130
script_filename = /home/xxx/opt/nginx/webapp/block.php
[0x7f27b9a99dc8]  sleep /home/xxx/opt/nginx/webapp/block.php:6
[0x7f27b9a99d08]  say /home/xxx/opt/nginx/webapp/block.php:3
[0x7f27b9a99c50]  run /home/xxx/opt/nginx/webapp/block.php:10 
renégat
la source
Existe-t-il une version Windows?
johnny
J'aime que l'adresse mémoire soit indiquée ici .. Cela peut être utile
Tyler Miles
3

Utilisez debug_backtracepour obtenir une trace des fonctions et méthodes qui ont été appelées et des fichiers qui ont été inclus et qui ont conduit au point où ils debug_backtraceont été appelés.

Gombo
la source
1

debug_backtrace()

pix0r
la source
1

La solution de Walltearer est excellente, en particulier si elle est enfermée dans une étiquette «pré»:

<pre>
<?php debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); ?>
</pre>

- qui établit les appels sur des lignes distinctes, soigneusement numérotées

Geoff Kendall
la source
0

J'ai adapté la réponse de Don Briggs ci-dessus pour utiliser la journalisation des erreurs internes au lieu de l'impression publique, ce qui peut être votre grande préoccupation lorsque vous travaillez sur un serveur en direct. En outre, quelques modifications supplémentaires comme l'option pour inclure le chemin de fichier complet au lieu du nom de base (car il pourrait y avoir des fichiers avec le même nom dans des chemins différents) et également (pour ceux qui en ont besoin) une sortie de pile de nœuds complète:

class debugUtils {
    public static function callStack($stacktrace) {
        error_log(str_repeat("=", 100));
        $i = 1;
        foreach($stacktrace as $node) {
            // uncomment next line to debug entire node stack
            // error_log(print_r($node, true));
            error_log( $i . '.' . ' file: ' .$node['file'] . ' | ' . 'function: ' . $node['function'] . '(' . ' line: ' . $node['line'] . ')' );
            $i++;
        }
        error_log(str_repeat("=", 100));
    } 
}

// call debug stack
debugUtils::callStack(debug_backtrace());
dev101
la source