mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-07 02:08:21 +00:00
Allow thread errors and their traces to be properly recorded in crashdumps (#5910)
until now, any thread crash would show as a generic crash since we aren't able to get the trace from the crashed thread directly. This uses some dirty tricks to export a partially serialized stack trace to the main thread, where it can be written into a crashdump. This enables us to see proper crash information for async tasks in the crash archive (finally!!!) as well as being able to capture RakLib errors properly.
This commit is contained in:
@ -29,11 +29,13 @@ use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\plugin\PluginBase;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\thread\ThreadCrashInfoFrame;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\VersionInfo;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_map;
|
||||
use function base64_encode;
|
||||
use function error_get_last;
|
||||
use function file;
|
||||
@ -186,7 +188,7 @@ class CrashDump{
|
||||
if($error === null){
|
||||
throw new \RuntimeException("Crash error information missing - did something use exit()?");
|
||||
}
|
||||
$error["trace"] = Utils::currentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$error["trace"] = Utils::printableTrace(Utils::currentTrace(3)); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$error["fullFile"] = $error["file"];
|
||||
$error["file"] = Filesystem::cleanPath($error["file"]);
|
||||
try{
|
||||
@ -201,9 +203,6 @@ class CrashDump{
|
||||
$error["message"] = mb_scrub($error["message"], 'UTF-8');
|
||||
|
||||
if(isset($lastError)){
|
||||
if(isset($lastError["trace"])){
|
||||
$lastError["trace"] = Utils::printableTrace($lastError["trace"]);
|
||||
}
|
||||
$this->data->lastError = $lastError;
|
||||
$this->data->lastError["message"] = mb_scrub($this->data->lastError["message"], 'UTF-8');
|
||||
}
|
||||
@ -215,10 +214,11 @@ class CrashDump{
|
||||
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_NONE;
|
||||
if(!$this->determinePluginFromFile($error["fullFile"], true)){ //fatal errors won't leave any stack trace
|
||||
foreach($error["trace"] as $frame){
|
||||
if(!isset($frame["file"])){
|
||||
$frameFile = $frame->getFile();
|
||||
if($frameFile === null){
|
||||
continue; //PHP core
|
||||
}
|
||||
if($this->determinePluginFromFile($frame["file"], false)){
|
||||
if($this->determinePluginFromFile($frameFile, false)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -233,7 +233,8 @@ class CrashDump{
|
||||
}
|
||||
}
|
||||
|
||||
$this->data->trace = Utils::printableTrace($error["trace"]);
|
||||
$this->data->trace = array_map(array: $error["trace"], callback: fn(ThreadCrashInfoFrame $frame) => $frame->getPrintableFrame());
|
||||
$this->data->thread = $error["thread"];
|
||||
}
|
||||
|
||||
private function determinePluginFromFile(string $filePath, bool $crashFrame) : bool{
|
||||
|
@ -37,6 +37,8 @@ final class CrashDumpData implements \JsonSerializable{
|
||||
/** @var mixed[] */
|
||||
public array $error;
|
||||
|
||||
public string $thread;
|
||||
|
||||
public string $plugin_involvement;
|
||||
|
||||
public string $plugin = "";
|
||||
|
@ -64,6 +64,7 @@ final class CrashDumpRenderer{
|
||||
|
||||
$this->addLine();
|
||||
|
||||
$this->addLine("Thread: " . $this->data->thread);
|
||||
$this->addLine("Error: " . $this->data->error["message"]);
|
||||
$this->addLine("File: " . $this->data->error["file"]);
|
||||
$this->addLine("Line: " . $this->data->error["line"]);
|
||||
|
Reference in New Issue
Block a user