Skip to content

Commit

Permalink
BlueScreen: displays real arguments and local variables using xdebug …
Browse files Browse the repository at this point in the history
…[WIP]

- requires xdebug 3.3 and xdebug.mode=develop
- stores information only for the last 8 exceptions, so we don't want to generate new exceptions during rendering
  • Loading branch information
dg committed Jan 17, 2024
1 parent a92e70a commit 123a8ef
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 18 deletions.
66 changes: 66 additions & 0 deletions examples/xdebug.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

require __DIR__ . '/../src/tracy.php';

use Tracy\Debugger;

// For security reasons, Tracy is visible only on localhost.
// You may force Tracy to run in development mode by passing the Debugger::Development instead of Debugger::Detect.
//Debugger::$strictMode = true;
Debugger::enable(Debugger::Detect, __DIR__ . '/log');

?>
<!DOCTYPE html><link rel="stylesheet" href="assets/style.css">

<h1>Tracy: exception demo</h1>

<?php

class DemoClass
{
public function first($arg1, $arg2)
{
$arg1 = 'new';
$arg3 = 'xxx';
$this->second();
}


public function second()
{
self::third([1, 2, 3]);
}


public static function third($arg5)
{
//require __DIR__ . '/assets/E_COMPILE_WARNING-1.php';
//require __DIR__ . '/assets/E_COMPILE_ERROR.php';
// trigger_error('jo', E_USER_ERROR);
// dump(new Exception);
// dumpe(xdebug_get_function_stack( [ 'local_vars' => true, 'params_as_values' => true ] ));
try {
throw new Exception('Original');
} catch (Exception $e) {
throw new Exception('The my exception', 123, $e);
}
$a++;
}
}



function demo($a, $b)
{
$demo = new DemoClass;
$demo->first($a, $b);
}


if (Debugger::$productionMode) {
echo '<p><b>For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.</b></p>';
}

demo(10, 'any string');
14 changes: 14 additions & 0 deletions src/Tracy/BlueScreen/BlueScreen.php
Original file line number Diff line number Diff line change
Expand Up @@ -499,4 +499,18 @@ private function findGeneratorsAndFibers(object $object): array
Helpers::traverseValue($object, $add);
return [$generators, $fibers];
}


public function getRealArgsAndVariables(\Throwable $exception): array
{
$args = $variables = [];
if (function_exists('xdebug_get_function_stack') && version_compare(phpversion('xdebug'), '3.3.0', '>=')) {
$stack = @xdebug_get_function_stack(['from_exception' => $exception]); // @ warning: Function must be enabled in php.ini by setting 'xdebug.mode' to 'develop'
foreach (array_reverse($stack) as $k => $row) {
$args[$k] = $row['params'] ?? [];
$variables[$k - 1] = $row['variables'] ?? [];
}
}
return [$args, $variables];
}
}
8 changes: 6 additions & 2 deletions src/Tracy/BlueScreen/assets/bluescreen.css
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,15 @@ html.tracy-bs-visible body {
grid-column-end: 3;
}

#tracy-bs .tracy-callstack-args tr:first-child > * {
#tracy-bs .tracy-callstack-args tr > :first-child {
width: 10em;
}

#tracy-bs .tracy-callstack-args-warning tr:first-child > * {
position: relative;
}

#tracy-bs .tracy-callstack-args tr:first-child td:before {
#tracy-bs .tracy-callstack-args-warning tr:first-child td:before {
position: absolute;
right: .3em;
content: 'may not be true';
Expand Down
34 changes: 31 additions & 3 deletions src/Tracy/BlueScreen/assets/section-stack-callStack.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace Tracy;
* @var callable $dump
* @var int $expanded
* @var array $stack
* @var ?array $realArgs
* @var ?array $variables
*/

if (!$stack) {
Expand Down Expand Up @@ -66,15 +68,27 @@ if (!$stack) {
<?php endif ?>


<?php if (!empty($row['args'])): ?>
<?php if (!empty($realArgs[$key])): ?>
<table class="tracy-callstack-args">
<?php
try {
foreach ($realArgs[$key] as $argName => $v) {
echo '<tr><th>', Helpers::escapeHtml((is_string($argName) ? '$' : '#') . $argName), '</th><td>';
echo $dump($v, $argName);
echo "</td></tr>\n";
}
?>
</table>

<?php elseif (!empty($row['args'])): ?>
<table class="tracy-callstack-args tracy-callstack-args-warning">
<?php
if (isset($row['class']) ? method_exists($row['class'], $row['function']) : function_exists($row['function'])) {
$r = isset($row['class']) ? new \ReflectionMethod($row['class'], $row['function']) : new \ReflectionFunction($row['function']);
$params = $r->getParameters();
} catch (\Exception) {
} else {
$params = [];
}

foreach ($row['args'] as $k => $v) {
$argName = isset($params[$k]) && !$params[$k]->isVariadic() ? $params[$k]->name : $k;
echo '<tr><th>', Helpers::escapeHtml((is_string($argName) ? '$' : '#') . $argName), '</th><td>';
Expand All @@ -84,6 +98,20 @@ if (!$stack) {
?>
</table>
<?php endif ?>

<?php if (!empty($variables[$key])): ?>
<h3>Local Variables</h3>

<table class="tracy-callstack-args">
<?php
foreach ($variables[$key] as $k => $v) {
echo '<tr><th>$', Helpers::escapeHtml($k), '</th><td>';
echo $dump($v, $k);
echo "</td></tr>\n";
}
?>
</table>
<?php endif ?>
</div>
<?php endif ?>
<?php endforeach ?>
Expand Down
2 changes: 2 additions & 0 deletions src/Tracy/BlueScreen/assets/section-stack-exception.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ if (($stack[0]['class'] ?? null) === Debugger::class && in_array($stack[0]['func
}
$file = $ex->getFile();
$line = $ex->getLine();
[$realArgs, $variables] = $this->getRealArgsAndVariables($ex);

require __DIR__ . '/section-stack-sourceFile.phtml';
require __DIR__ . '/section-stack-variables.phtml';
require __DIR__ . '/section-stack-callStack.phtml';
30 changes: 30 additions & 0 deletions src/Tracy/BlueScreen/assets/section-stack-variables.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Tracy;

/**
* @var ?array $variables
*/

if (empty($variables[-1])) {
return;
}
?>

<section class="tracy-section">
<h2 class="tracy-section-label"><a href="#" data-tracy-ref="^+" class="tracy-toggle tracy-collapsed">Local variables</a></h2>

<div class="tracy-section-panel tracy-collapsed">
<table class="tracy-callstack-args">
<?php
foreach ($variables[-1] as $k => $v) {
echo '<tr><th>$', Helpers::escapeHtml($k), '</th><td>';
echo $dump($v, $k);
echo "</td></tr>\n";
}
?>
</table>
</div>
</section>
26 changes: 13 additions & 13 deletions src/Tracy/Dumper/Describer.php
Original file line number Diff line number Diff line change
Expand Up @@ -326,19 +326,19 @@ private static function findLocation(): ?array
if (isset($item['class']) && ($item['class'] === self::class || $item['class'] === Tracy\Dumper::class)) {
$location = $item;
continue;
} elseif (isset($item['function'])) {
try {
$reflection = isset($item['class'])
? new \ReflectionMethod($item['class'], $item['function'])
: new \ReflectionFunction($item['function']);
if (
$reflection->isInternal()
|| preg_match('#\s@tracySkipLocation\s#', (string) $reflection->getDocComment())
) {
$location = $item;
continue;
}
} catch (\ReflectionException) {
} elseif (
isset($item['function'])
&& (isset($item['class']) ? method_exists($item['class'], $item['function']) : function_exists($item['function']))
) {
$reflection = isset($item['class'])
? new \ReflectionMethod($item['class'], $item['function'])
: new \ReflectionFunction($item['function']);
if (
$reflection->isInternal()
|| preg_match('#\s@tracySkipLocation\s#', (string) $reflection->getDocComment())
) {
$location = $item;
continue;
}
}

Expand Down

0 comments on commit 123a8ef

Please sign in to comment.