diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php index 8cefb4912..f83b9b650 100644 --- a/src/pocketmine/CrashDump.php +++ b/src/pocketmine/CrashDump.php @@ -207,7 +207,7 @@ class CrashDump{ $error = $lastExceptionError; }else{ $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 = [ E_ERROR => "E_ERROR", E_WARNING => "E_WARNING", @@ -245,24 +245,16 @@ class CrashDump{ $this->addLine("Line: " . $error["line"]); $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->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($error["file"], $filePath) === 0){ - $this->data["plugin"] = $plugin->getName(); - $this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName()); + $this->data["plugin"] = false; + if(!$this->determinePluginFromFile($error["fullFile"])){ //fatal errors won't leave any stack trace + foreach($error["trace"] as $frame){ + if(!isset($frame["file"])){ + continue; //PHP core + } + if($this->determinePluginFromFile($frame["file"])){ break; } } - }else{ - $this->data["plugin"] = false; } $this->addLine(); @@ -279,12 +271,35 @@ class CrashDump{ $this->addLine(); $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(); } + 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(){ $version = new VersionString(\pocketmine\BASE_VERSION, \pocketmine\IS_DEVELOPMENT_BUILD, \pocketmine\BUILD_NUMBER); $this->data["general"] = []; diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index f5ec53406..926285250 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -2241,7 +2241,7 @@ class Server{ "fullFile" => $e->getFile(), "file" => $errfile, "line" => $errline, - "trace" => Utils::printableTrace($trace) + "trace" => $trace ]; global $lastExceptionError, $lastError; diff --git a/src/pocketmine/utils/Utils.php b/src/pocketmine/utils/Utils.php index 93e3322b1..5e69755b5 100644 --- a/src/pocketmine/utils/Utils.php +++ b/src/pocketmine/utils/Utils.php @@ -642,7 +642,21 @@ class Utils{ } 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; } /**