mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-05 17:41:46 +00:00
CrashDump: Scan full stack trace to determine plugin involvement
This commit is contained in:
parent
836cb67850
commit
2cdf97b7b5
@ -207,7 +207,7 @@ class CrashDump{
|
|||||||
$error = $lastExceptionError;
|
$error = $lastExceptionError;
|
||||||
}else{
|
}else{
|
||||||
$error = (array) error_get_last();
|
$error = (array) error_get_last();
|
||||||
$error["trace"] = Utils::printableCurrentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
$error["trace"] = Utils::currentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||||
$errorConversion = [
|
$errorConversion = [
|
||||||
E_ERROR => "E_ERROR",
|
E_ERROR => "E_ERROR",
|
||||||
E_WARNING => "E_WARNING",
|
E_WARNING => "E_WARNING",
|
||||||
@ -245,24 +245,16 @@ class CrashDump{
|
|||||||
$this->addLine("Line: " . $error["line"]);
|
$this->addLine("Line: " . $error["line"]);
|
||||||
$this->addLine("Type: " . $error["type"]);
|
$this->addLine("Type: " . $error["type"]);
|
||||||
|
|
||||||
if(strpos($error["file"], "src/pocketmine/") === false and strpos($error["file"], "vendor/pocketmine/") === false and file_exists($error["fullFile"])){
|
$this->data["plugin"] = false;
|
||||||
$this->addLine();
|
if(!$this->determinePluginFromFile($error["fullFile"])){ //fatal errors won't leave any stack trace
|
||||||
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
foreach($error["trace"] as $frame){
|
||||||
$this->data["plugin"] = true;
|
if(!isset($frame["file"])){
|
||||||
|
continue; //PHP core
|
||||||
$reflection = new \ReflectionClass(PluginBase::class);
|
}
|
||||||
$file = $reflection->getProperty("file");
|
if($this->determinePluginFromFile($frame["file"])){
|
||||||
$file->setAccessible(true);
|
|
||||||
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
|
||||||
$filePath = Utils::cleanPath($file->getValue($plugin));
|
|
||||||
if(strpos($error["file"], $filePath) === 0){
|
|
||||||
$this->data["plugin"] = $plugin->getName();
|
|
||||||
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
|
||||||
$this->data["plugin"] = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->addLine();
|
$this->addLine();
|
||||||
@ -279,12 +271,35 @@ class CrashDump{
|
|||||||
|
|
||||||
$this->addLine();
|
$this->addLine();
|
||||||
$this->addLine("Backtrace:");
|
$this->addLine("Backtrace:");
|
||||||
foreach(($this->data["trace"] = $error["trace"]) as $line){
|
foreach(($this->data["trace"] = Utils::printableTrace($error["trace"])) as $line){
|
||||||
$this->addLine($line);
|
$this->addLine($line);
|
||||||
}
|
}
|
||||||
$this->addLine();
|
$this->addLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function determinePluginFromFile(string $filePath) : bool{
|
||||||
|
$frameCleanPath = Utils::cleanPath($filePath); //this will be empty in phar stub
|
||||||
|
if($frameCleanPath !== "" and strpos($frameCleanPath, "src/pocketmine/") === false and strpos($frameCleanPath, "vendor/pocketmine/") === false and file_exists($filePath)){
|
||||||
|
$this->addLine();
|
||||||
|
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
|
||||||
|
$this->data["plugin"] = true;
|
||||||
|
|
||||||
|
$reflection = new \ReflectionClass(PluginBase::class);
|
||||||
|
$file = $reflection->getProperty("file");
|
||||||
|
$file->setAccessible(true);
|
||||||
|
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
|
||||||
|
$filePath = Utils::cleanPath($file->getValue($plugin));
|
||||||
|
if(strpos($frameCleanPath, $filePath) === 0){
|
||||||
|
$this->data["plugin"] = $plugin->getName();
|
||||||
|
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private function generalData(){
|
private function generalData(){
|
||||||
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
$version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER);
|
||||||
$this->data["general"] = [];
|
$this->data["general"] = [];
|
||||||
|
@ -2241,7 +2241,7 @@ class Server{
|
|||||||
"fullFile" => $e->getFile(),
|
"fullFile" => $e->getFile(),
|
||||||
"file" => $errfile,
|
"file" => $errfile,
|
||||||
"line" => $errline,
|
"line" => $errline,
|
||||||
"trace" => Utils::printableTrace($trace)
|
"trace" => $trace
|
||||||
];
|
];
|
||||||
|
|
||||||
global $lastExceptionError, $lastError;
|
global $lastExceptionError, $lastError;
|
||||||
|
@ -642,7 +642,21 @@ class Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function cleanPath($path){
|
public static function cleanPath($path){
|
||||||
return str_replace(["\\", ".php", "phar://", str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PATH), str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PLUGIN_PATH)], ["/", "", "", "", ""], $path);
|
$result = str_replace(["\\", ".php", "phar://"], ["/", "", ""], $path);
|
||||||
|
|
||||||
|
//remove relative paths
|
||||||
|
//TODO: make these paths dynamic so they can be unit-tested against
|
||||||
|
static $cleanPaths = [
|
||||||
|
\pocketmine\PLUGIN_PATH => "plugins", //this has to come BEFORE \pocketmine\PATH because it's inside that by default on src installations
|
||||||
|
\pocketmine\PATH => ""
|
||||||
|
];
|
||||||
|
foreach($cleanPaths as $cleanPath => $replacement){
|
||||||
|
$cleanPath = rtrim(str_replace(["\\", "phar://"], ["/", ""], $cleanPath), "/");
|
||||||
|
if(strpos($result, $cleanPath) === 0){
|
||||||
|
$result = ltrim(str_replace($cleanPath, $replacement, $result), "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user