Wydrukuj stos wywołań PHP

Odpowiedzi:

123

Jeśli chcesz wygenerować ślad, szukasz debug_backtracei / lub debug_print_backtrace.


Pierwszy, na przykład, da ci tablicę taką jak ta (cytując instrukcję) :

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"
  }
}


Najwyraźniej nie będą opróżniać bufora we / wy, ale możesz to zrobić samodzielnie, za pomocą flushi / lub ob_flush.

(zobacz stronę podręcznika pierwszego, aby dowiedzieć się, dlaczego „i / lub” ;-))

Pascal MARTIN
źródło
7
regularnie powoduje to brak pamięci w php. Polecam rozwiązanie Tobiasza.
peedee
Jeśli trudno ci przeczytać / zrozumieć, polecam również rozwiązanie
Tobiasza
1
@peedee wystarczy podać jeden z opcjonalnych DEBUG_BACKTRACE_IGNORE_ARGSparametrów; dzięki czemu są funkcjonalnie równoważne z(new \Exception())->getTraceAsString()
565

Bardziej czytelny niż 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
źródło
50
Cholera, jest o wiele lepiej, dlaczego nie mogliby zrobić z tego domyślnego wyjścia dla debug_print_backtrace ()? Mógłbym dodać parametr boolowski „returnTrace” dla tych, którzy chcą go w zmiennej, bez echa, i byłoby idealnie!
jurchiks
1
Nie wiem, ile miesięcy próbowałem wymyślić, jak to zrobić, nigdy nie myślałem, że to zadziała
WojonsTech
To rozwiązanie wydaje się również zajmować mniej pamięci niż przechwytywanie danych wyjściowych debug_backtrace () jako tablicy, a następnie drukowanie ich za pomocą print_r (), co robiłem, dopóki tego nie zobaczyłem!
Peter
5
Szukałem sposobu ograniczenia, debug_backtraceaby zwrócić tylko pierwszy poziom w stacktrace - to rozwiązanie działa dla mnie. Dziękuję Ci!
ankr
3
@Andrew print_rzachowa wszystkie wiadomości.
mopo922,
41

Aby zarejestrować ślad

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

Dzięki @Tobiasz

Sydwell
źródło
35

Backtrace zrzuca całe śmieci, których nie potrzebujesz. Trwa bardzo długo, trudny do odczytania. Wszystko, czego zwykle pragniesz, to „jak się nazywa, skąd?” Oto proste rozwiązanie funkcji statycznej. Zwykle umieszczam go w klasie o nazwie „debugowanie”, która zawiera wszystkie moje funkcje narzędzia do debugowania.

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++;
        }
    } 
}

Nazywasz to tak:

debugUtils::callStack(debug_backtrace());

I produkuje takie dane wyjściowe:

==================================================
 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
źródło
33

Dziwne, że nikt nie opublikował w ten sposób:

debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

To faktycznie drukuje ślad bez śmieci - tylko jaka metoda została wywołana i gdzie.

AbstractVoid
źródło
2
Rzeczywiście, naprawdę równoważne głównemu głosowanemu rozwiązaniu i krótsze. Dzięki
brunetton,
9

Jeśli chcesz śledzenia stosu, który wygląda bardzo podobnie do tego, jak php formatuje wyjątek śledzenia stosu, niż użyj tej funkcji, napisałem:

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;
} 

Zwróci to ślad stosu sformatowany w następujący sposób:

#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
źródło
2
lub po prostu$e = new Exception; echo $e->getTraceAsString();
Brad Kent
Brad, to rozwiązanie nie usuwa ostatniego elementu ze śledzenia stosu, więc nie wyświetlasz elementu śledzenia spowodowanego przez nowy wyjątek
TroySteven
8
var_dump(debug_backtrace());

Czy to robi co chcesz?

inkedmn
źródło
6

Zobaczyć debug_print_backtrace. Chyba możesz flushpóźniej zadzwonić, jeśli chcesz.

Martin Geisler
źródło
4

phptrace to świetne narzędzie do drukowania stosu PHP w dowolnym momencie, bez potrzeby instalowania żadnych rozszerzeń.

Istnieją dwie główne funkcje phptrace: po pierwsze, stos wywołań drukowania PHP, które nie muszą niczego instalować, po drugie, śledzenie przepływów wykonania php, które wymaga zainstalowania rozszerzenia, które dostarcza.

następująco:

$ ./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 
renenglish
źródło
Czy jest wersja Windows?
Johnny
Podoba mi się, że pokazany jest tutaj adres pamięci. Może to być pomocne
Tyler Miles,
3

Służy debug_backtracedo uzyskania informacji o tym, jakie funkcje i metody zostały wywołane oraz jakie pliki zostały zawarte, które doprowadziły do ​​momentu, w którym debug_backtracezostały wywołane.

Gumbo
źródło
1

debug_backtrace()

pix0r
źródło
1

Rozwiązanie Walltearer jest doskonałe, szczególnie jeśli jest umieszczone w tagu „pre”:

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

- który określa połączenia na osobnych liniach, starannie ponumerowanych

Geoff Kendall
źródło
0

Zaadaptowałem powyższą odpowiedź Dona Briggsa, aby używać wewnętrznego rejestrowania błędów zamiast drukowania publicznego, co może stanowić poważny problem podczas pracy na serwerze na żywo. Ponadto dodano kilka dodatkowych modyfikacji, takich jak opcja włączenia pełnej ścieżki pliku zamiast podstawowej nazwy (ponieważ mogą istnieć pliki o tej samej nazwie w różnych ścieżkach), a także (dla tych, którzy tego wymagają) kompletny plik wyjściowy stosu węzłów:

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
źródło