Handle some pts errors

This commit is contained in:
Daniil Gentili 2019-09-18 16:01:27 +02:00
parent 1f33dc9e33
commit f1eecff25d
6 changed files with 198 additions and 86 deletions

View File

@ -44,7 +44,9 @@ use ReflectionGenerator;
*/
final class Coroutine implements Promise, \ArrayAccess
{
use Internal\Placeholder;
use Internal\Placeholder {
fail as internalFail;
}
/** @var \Generator */
private $generator;
/** @var callable(\Throwable|null $exception, mixed $value): void */
@ -56,41 +58,20 @@ final class Coroutine implements Promise, \ArrayAccess
/** @var mixed Promise success value when executing next coroutine step, null at all other times. */
private $value;
private $frames = [];
/**
* Generator trace.
*
* @var Trace
*/
private $trace;
/**
* @param \Generator $generator
*/
public function __construct(\Generator $generator)
public function __construct(\Generator $generator, Trace $trace = null)
{
/*
$this->generator = new class($generator) {
private $s = '';
private $g;
private $trace;
public function __construct($g) {
$this->g = $g;
$this->s .= spl_object_hash($this).', ';
}
public function __call($a, $args) {
$this->s .= "$a, ";
try {
$res = $this->g->{$a}(...$args);
if (is_array($res) && isset($res['my_trace'])) {
$this->trace = $res;
$res = $this->g->{$a}(...$args);
}
return $res;
} catch (\Throwable $e) {
$this->s .= $e->getMessage();
$this->s .= ', ';
var_dump($this->s, $this->trace);
throw $e;
}
}
//public function __destruct() { var_dump($this->s); }
};*/
$this->generator = $generator;
//$this->trace = $trace ?? new Trace(\debug_backtrace());
try {
$yielded = $this->generator->current();
@ -111,6 +92,20 @@ final class Coroutine implements Promise, \ArrayAccess
return;
}
if ($yielded instanceof \Generator) {
/*if ($this->generator->valid()) {
$reflection = new ReflectionGenerator($this->generator);
$trace = new Trace(
[[
'file' => $reflection->getExecutingFile(),
'line' => $reflection->getExecutingLine(),
'function' => $reflection->getFunction()->getName(),
]],
$this->trace
);
} else {
$trace = $this->trace;
}
$yielded = new self($yielded, $trace);*/
$yielded = new self($yielded);
} else {
$yielded = $this->generator->send($yielded);
@ -183,6 +178,17 @@ final class Coroutine implements Promise, \ArrayAccess
$yielded->onResolve($this->onResolve);
}
/**
* @param \Throwable $reason Failure reason.
*/
public function fail(\Throwable $reason)
{
//if (isset(\class_uses($reason)[TL\PrettyException::class])) {
//$reason->updateTLTrace($this->getTrace());
//}
$this->resolve(new Failure($reason));
}
public function offsetExists($offset): bool
{
throw new Exception('Not supported!');
@ -214,12 +220,10 @@ final class Coroutine implements Promise, \ArrayAccess
/**
* Get stacktrace from when the generator was started.
*
* @param integer $options Backtrace options
*
* @return array
*/
public function getTrace(int $options = \DEBUG_BACKTRACE_PROVIDE_OBJECT): array
public function getTrace(): array
{
return (new ReflectionGenerator($this->generator))->getTrace($options);
return $this->trace->getTrace();
}
}

View File

@ -93,9 +93,12 @@ trait CallHandler
$deferred->fail($e);
} else {
if (\is_array($read_deferred)) {
$read_deferred = \array_map(function ($value) {
return $value->promise();
}, $read_deferred);
$read_deferred = \array_map(
function ($value) {
return $value->promise();
},
$read_deferred
);
$deferred->resolve(all($read_deferred));
} else {
$deferred->resolve($read_deferred->promise());

View File

@ -21,6 +21,7 @@ namespace danog\MadelineProto\MTProtoSession;
use Amp\Loop;
use danog\MadelineProto\MTProto;
use danog\MadelineProto\TL\PrettyException;
/**
* Manages responses.
@ -307,7 +308,15 @@ trait ResponseHandler
return $only_updates;
}
public function handle_reject(&$request, $data)
/**
* Reject request with exception
*
* @param array $request Request
* @param \Throwable $data Exception
*
* @return void
*/
public function handle_reject(array &$request, \Throwable $data)
{
if (isset($request['promise']) && \is_object($request['promise'])) {
Loop::defer(function () use (&$request, $data) {
@ -350,12 +359,15 @@ trait ResponseHandler
$this->shared->getTempAuthKey()->init(true);
}
if (\in_array($response['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) {
if (\in_array($response['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_INVALID'])) {
$this->got_response_for_outgoing_message_id($request_id);
$this->handle_reject($request, new \danog\MadelineProto\PTSException($response['error_message']));
return;
}
if ($response['error_message'] === 'PERSISTENT_TIMESTAMP_OUTDATED') {
$response['error_code'] = 500;
}
if (\strpos($response['error_message'], 'FILE_REFERENCE_') === 0) {
$this->logger->logger("Got {$response['error_message']}, refreshing file reference and repeating method call...");
@ -377,7 +389,7 @@ trait ResponseHandler
return;
}
if (\in_array($response['error_message'], ['MSGID_DECREASE_RETRY', 'RPC_CALL_FAIL', 'RPC_MCGET_FAIL', 'no workers running'])) {
if (\in_array($response['error_message'], ['MSGID_DECREASE_RETRY', 'RPC_CALL_FAIL', 'PERSISTENT_TIMESTAMP_OUTDATED', 'RPC_MCGET_FAIL', 'no workers running'])) {
Loop::delay(1 * 1000, [$this, 'method_recall'], ['message_id' => $request_id, ]);
return;
}

View File

@ -19,23 +19,77 @@
namespace danog\MadelineProto\TL;
use danog\MadelineProto\Tools;
/**
* Handle async stack traces.
*/
trait PrettyException
{
public $tl_trace;
/**
* TL trace.
*
* @var string
*/
public $tl_trace = '';
public function getTLTrace()
/**
* Method name.
*
* @var string
*/
private $method = '';
/**
* Whether the TL trace was updated.
*
* @var boolean
*/
private $updated = false;
/**
* Update TL trace.
*
* @param array $trace
*
* @return void
*/
public function updateTLTrace(array $trace)
{
if (!$this->updated) {
$this->updated = true;
$this->prettify_tl($this->method, $trace);
}
}
/**
* Get TL trace.
*
* @return string
*/
public function getTLTrace(): string
{
return $this->tl_trace;
}
public function prettify_tl($init = '')
/**
* Generate async trace.
*
* @param string $init Method name
* @param array $trace Async trace
*
* @return void
*/
public function prettify_tl(string $init = '', array $trace = null)
{
$this->method = $init;
$previous_trace = $this->tl_trace;
$this->tl_trace = '';
$eol = PHP_EOL;
if (PHP_SAPI !== 'cli') {
$eol = '<br>'.PHP_EOL;
}
$tl = false;
foreach (\array_reverse($this->getTrace()) as $k => $frame) {
foreach (\array_reverse($trace ?? $this->getTrace()) as $k => $frame) {
if (isset($frame['function']) && \in_array($frame['function'], ['serialize_params', 'serialize_object'])) {
if ($frame['args'][2] !== '') {
$this->tl_trace .= $tl ? "['".$frame['args'][2]."']" : "While serializing: \t".$frame['args'][2];
@ -58,5 +112,11 @@ trait PrettyException
}
$this->tl_trace .= $init !== '' ? "['".$init."']" : '';
$this->tl_trace = \implode($eol, \array_reverse(\explode($eol, $this->tl_trace)));
if ($previous_trace) {
$this->tl_trace .= $eol.$eol;
$this->tl_trace .= "Previous TL trace$eol";
$this->tl_trace .= $previous_trace;
}
}
}

View File

@ -448,46 +448,4 @@ trait Tools
$var instanceof Traversable &&
$var instanceof Countable);
}
/**
* Custom backtrace for working with generators.
*
* @param boolean $ignoreArgs Whether to ignore method arguments
* @param array $trace Trace to work with
*
* @return array
*/
public static function backtrace(bool $ignoreArgs = false, array $trace = []): array
{
return \iterator_to_array(self::backtraceGenerator($ignoreArgs, $trace));
}
/**
* Custom backtrace for working with generators.
*
* @param boolean $ignoreArgs Whether to ignore method arguments
* @param array $trace Trace to work with
*
* @return \Generator
*/
public static function backtraceGenerator(bool $ignoreArgs = false, array $trace = []): \Generator
{
$flags = DEBUG_BACKTRACE_PROVIDE_OBJECT;
if ($ignoreArgs) {
$flags |= DEBUG_BACKTRACE_IGNORE_ARGS;
}
if (!$trace) {
$trace = \debug_backtrace($flags);
\array_shift($trace);
}
foreach ($trace as $frame) {
if (isset($frame['object']) && $frame['object'] instanceof Coroutine) {
yield from self::backtrace($ignoreArgs, $frame['object']->getTrace($flags));
return;
}
//var_dump(get_class($frame['object'] ?? new class {}));
unset($frame['object']);
yield $frame;
}
}
}

View File

@ -0,0 +1,75 @@
<?php
/**
* ResponseHandler module.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
*
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto;
/**
* Represents a piece of a coroutine stack trace
*/
class Trace
{
/**
* Next piece of the stack trace
*
* @var Trace
*/
private $next;
/**
* Current stack trace frames
*
* @var array
*/
private $frames = [];
/**
* Create trace
*
* @param array $frames Current frames
* @param self $next Next trace
*/
public function __construct(array $frames, self $next = null)
{
$this->frames = $frames;
$this->next = $next;
}
/**
* Get stack trace
*
* @return array
*/
public function getTrace(): array
{
return iterator_to_array($this->getTraceGenerator());
}
/**
* Get stack trace
*
* @return \Generator
*/
private function getTraceGenerator(): \Generator
{
foreach ($this->frames as $frame) {
yield $frame;
}
if ($this->next) {
yield from $this->next->getTraceGenerator();
}
}
}