Merge branch '3.5'

This commit is contained in:
Dylan K. Taylor 2019-01-06 20:19:18 +00:00
commit 3ae722867c
3 changed files with 63 additions and 23 deletions

View File

@ -87,7 +87,11 @@ class CrashDump{
* having their content changed, version format changing, etc. * having their content changed, version format changing, etc.
* It is not necessary to increase this when adding new fields. * It is not necessary to increase this when adding new fields.
*/ */
private const FORMAT_VERSION = 1; private const FORMAT_VERSION = 2;
private const PLUGIN_INVOLVEMENT_NONE = "none";
private const PLUGIN_INVOLVEMENT_DIRECT = "direct";
private const PLUGIN_INVOLVEMENT_INDIRECT = "indirect";
/** @var Server */ /** @var Server */
private $server; private $server;
@ -207,7 +211,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 +249,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_involvement"] = self::PLUGIN_INVOLVEMENT_NONE;
$this->addLine(); if(!$this->determinePluginFromFile($error["fullFile"], true)){ //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"], false)){
$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 +275,40 @@ 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 $crashFrame) : 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();
if($crashFrame){
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_DIRECT;
}else{
$this->addLine("A PLUGIN WAS INVOLVED IN THIS CRASH");
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_INDIRECT;
}
$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"] = [];

View File

@ -104,7 +104,6 @@ use pocketmine\tile\Tile;
use pocketmine\timings\Timings; use pocketmine\timings\Timings;
use pocketmine\timings\TimingsHandler; use pocketmine\timings\TimingsHandler;
use pocketmine\updater\AutoUpdater; use pocketmine\updater\AutoUpdater;
use pocketmine\utils\Binary;
use pocketmine\utils\Config; use pocketmine\utils\Config;
use pocketmine\utils\Internet; use pocketmine\utils\Internet;
use pocketmine\utils\MainLogger; use pocketmine\utils\MainLogger;
@ -151,6 +150,7 @@ use function pcntl_signal;
use function pcntl_signal_dispatch; use function pcntl_signal_dispatch;
use function preg_replace; use function preg_replace;
use function random_bytes; use function random_bytes;
use function random_int;
use function realpath; use function realpath;
use function register_shutdown_function; use function register_shutdown_function;
use function rename; use function rename;
@ -169,6 +169,8 @@ use function time;
use function touch; use function touch;
use function trim; use function trim;
use const DIRECTORY_SEPARATOR; use const DIRECTORY_SEPARATOR;
use const INT32_MAX;
use const INT32_MIN;
use const PHP_EOL; use const PHP_EOL;
use const PHP_INT_MAX; use const PHP_INT_MAX;
use const PTHREADS_INHERIT_NONE; use const PTHREADS_INHERIT_NONE;
@ -1120,7 +1122,7 @@ class Server{
return false; return false;
} }
$seed = $seed ?? Binary::readInt(random_bytes(4)); $seed = $seed ?? random_int(INT32_MIN, INT32_MAX);
if(!isset($options["preset"])){ if(!isset($options["preset"])){
$options["preset"] = $this->getConfigString("generator-settings", ""); $options["preset"] = $this->getConfigString("generator-settings", "");
@ -2273,7 +2275,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;
@ -2312,7 +2314,7 @@ class Server{
if(is_string($plugin)){ if(is_string($plugin)){
$p = $this->pluginManager->getPlugin($plugin); $p = $this->pluginManager->getPlugin($plugin);
if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){ if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){
$report = false; $this->logger->debug("Not sending crashdump due to caused by non-phar plugin");
} }
} }

View File

@ -578,7 +578,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;
} }
/** /**