From 0597620f5605c4874271f2a421545e4ac92dbe80 Mon Sep 17 00:00:00 2001 From: Aaron Piotrowski Date: Mon, 10 Feb 2020 12:33:55 -0600 Subject: [PATCH] Serialize exception trace arguments in child processes and threads --- composer.json | 2 + lib/Context/functions.php | 33 +++++++++++++ lib/Sync/ExitFailure.php | 6 +-- lib/Sync/functions.php | 75 +++++++++++++++++++++++++++++ lib/Worker/Internal/TaskFailure.php | 9 ++-- 5 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 lib/Context/functions.php create mode 100644 lib/Sync/functions.php diff --git a/composer.json b/composer.json index 9676b62..f7bf817 100755 --- a/composer.json +++ b/composer.json @@ -38,6 +38,8 @@ "Amp\\Parallel\\": "lib" }, "files": [ + "lib/Context/functions.php", + "lib/Sync/functions.php", "lib/Worker/functions.php" ] }, diff --git a/lib/Context/functions.php b/lib/Context/functions.php new file mode 100644 index 0000000..7344a53 --- /dev/null +++ b/lib/Context/functions.php @@ -0,0 +1,33 @@ + + */ +function run($script): Promise +{ + if (Parallel::isSupported()) { + return Parallel::run($script); + } + + return Process::run($script); +} diff --git a/lib/Sync/ExitFailure.php b/lib/Sync/ExitFailure.php index f067bac..eb78b19 100644 --- a/lib/Sync/ExitFailure.php +++ b/lib/Sync/ExitFailure.php @@ -13,7 +13,7 @@ final class ExitFailure implements ExitResult /** @var int|string */ private $code; - /** @var array */ + /** @var string[] */ private $trace; /** @var self|null */ @@ -24,7 +24,7 @@ final class ExitFailure implements ExitResult $this->type = \get_class($exception); $this->message = $exception->getMessage(); $this->code = $exception->getCode(); - $this->trace = $exception->getTraceAsString(); + $this->trace = flattenThrowableBacktrace($exception); if ($previous = $exception->getPrevious()) { $this->previous = new self($previous); @@ -53,7 +53,7 @@ final class ExitFailure implements ExitResult $this->code, PanicError::class ), - $this->trace, + \implode("\n", $this->trace), $previous ); } diff --git a/lib/Sync/functions.php b/lib/Sync/functions.php new file mode 100644 index 0000000..e113f58 --- /dev/null +++ b/lib/Sync/functions.php @@ -0,0 +1,75 @@ +getTrace(); + + foreach ($trace as $call) { + if (isset($call['class'])) { + $name = $call['class'] . $call['type'] . $call['function']; + } else { + $name = $call['function']; + } + + $args = \implode(', ', \array_map(__NAMESPACE__ . '\\flattenArgument', $call['args'])); + + $output[] = \sprintf( + '#%d %s(%d): %s(%s)', + $counter++, + $call['file'] ?? '[internal function]', + $call['line'] ?? 0, + $name, + $args + ); + } + + return $output; +} + +/** + * @param mixed $value + * + * @return string Serializable string representation of $value for backtraces. + */ +function flattenArgument($value): string +{ + if ($value instanceof \Closure) { + $closureReflection = new \ReflectionFunction($value); + return \sprintf( + 'Closure(%s:%s)', + $closureReflection->getFileName(), + $closureReflection->getStartLine() + ); + } + + if (\is_object($value)) { + return \sprintf('Object(%s)', \get_class($value)); + } + + if (\is_array($value)) { + return 'Array([' . \implode(', ', \array_map(__FUNCTION__, $value)) . '])'; + } + + if (\is_resource($value)) { + return \sprintf('Resource(%s)', \get_resource_type($value)); + } + + if (\is_string($value)) { + return '"' . $value . '"'; + } + + if (\is_null($value)) { + return 'null'; + } + + return (string) $value; +} diff --git a/lib/Worker/Internal/TaskFailure.php b/lib/Worker/Internal/TaskFailure.php index 5d4091e..32e2fbf 100644 --- a/lib/Worker/Internal/TaskFailure.php +++ b/lib/Worker/Internal/TaskFailure.php @@ -3,6 +3,7 @@ namespace Amp\Parallel\Worker\Internal; use Amp\Failure; +use Amp\Parallel\Sync; use Amp\Parallel\Worker\TaskError; use Amp\Parallel\Worker\TaskException; use Amp\Promise; @@ -25,7 +26,7 @@ final class TaskFailure extends TaskResult /** @var int|string */ private $code; - /** @var array */ + /** @var string[] */ private $trace; /** @var self|null */ @@ -38,7 +39,7 @@ final class TaskFailure extends TaskResult $this->parent = $exception instanceof \Error ? self::PARENT_ERROR : self::PARENT_EXCEPTION; $this->message = $exception->getMessage(); $this->code = $exception->getCode(); - $this->trace = $exception->getTraceAsString(); + $this->trace = Sync\flattenThrowableBacktrace($exception); if ($previous = $exception->getPrevious()) { $this->previous = new self($id, $previous); @@ -61,7 +62,7 @@ final class TaskFailure extends TaskResult return new TaskError( $this->type, \sprintf($format, $this->type, $this->message, $this->code, TaskError::class), - $this->trace, + \implode("\n", $this->trace), $previous ); } @@ -69,7 +70,7 @@ final class TaskFailure extends TaskResult return new TaskException( $this->type, \sprintf($format, $this->type, $this->message, $this->code, TaskException::class), - $this->trace, + \implode("\n", $this->trace), $previous ); }