Catch uncatched thrown Exceptions to get useful backtraces

This commit is contained in:
Shoghi Cervantes 2014-08-01 17:04:16 +02:00
parent 915443b508
commit 7fcaa78de4
3 changed files with 60 additions and 37 deletions

View File

@ -130,32 +130,36 @@ class CrashDump{
} }
private function baseCrash(){ private function baseCrash(){
global $lastError; global $lastExceptionError, $lastError;
$error = (array) error_get_last(); if(isset($lastExceptionError)){
$error["trace"] = getTrace(4); $error = $lastExceptionError;
$errorConversion = array( }else{
E_ERROR => "E_ERROR", $error = (array) error_get_last();
E_WARNING => "E_WARNING", $error["trace"] = getTrace(4);
E_PARSE => "E_PARSE", $errorConversion = array(
E_NOTICE => "E_NOTICE", E_ERROR => "E_ERROR",
E_CORE_ERROR => "E_CORE_ERROR", E_WARNING => "E_WARNING",
E_CORE_WARNING => "E_CORE_WARNING", E_PARSE => "E_PARSE",
E_COMPILE_ERROR => "E_COMPILE_ERROR", E_NOTICE => "E_NOTICE",
E_COMPILE_WARNING => "E_COMPILE_WARNING", E_CORE_ERROR => "E_CORE_ERROR",
E_USER_ERROR => "E_USER_ERROR", E_CORE_WARNING => "E_CORE_WARNING",
E_USER_WARNING => "E_USER_WARNING", E_COMPILE_ERROR => "E_COMPILE_ERROR",
E_USER_NOTICE => "E_USER_NOTICE", E_COMPILE_WARNING => "E_COMPILE_WARNING",
E_STRICT => "E_STRICT", E_USER_ERROR => "E_USER_ERROR",
E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", E_USER_WARNING => "E_USER_WARNING",
E_DEPRECATED => "E_DEPRECATED", E_USER_NOTICE => "E_USER_NOTICE",
E_USER_DEPRECATED => "E_USER_DEPRECATED", E_STRICT => "E_STRICT",
); E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR",
$error["fullFile"] = $error["file"]; E_DEPRECATED => "E_DEPRECATED",
$error["file"] = cleanPath($error["file"]); E_USER_DEPRECATED => "E_USER_DEPRECATED",
$error["type"] = isset($errorConversion[$error["type"]]) ? $errorConversion[$error["type"]] : $error["type"]; );
if(($pos = strpos($error["message"], "\n")) !== false){ $error["fullFile"] = $error["file"];
$error["message"] = substr($error["message"], 0, $pos); $error["file"] = cleanPath($error["file"]);
$error["type"] = isset($errorConversion[$error["type"]]) ? $errorConversion[$error["type"]] : $error["type"];
if(($pos = strpos($error["message"], "\n")) !== false){
$error["message"] = substr($error["message"], 0, $pos);
}
} }
if(isset($lastError)){ if(isset($lastError)){

View File

@ -172,13 +172,16 @@ namespace pocketmine {
} }
} }
function getTrace($start = 1){ function getTrace($start = 1, $trace = null){
if(function_exists("xdebug_get_function_stack")){ if($trace === null){
$trace = array_reverse(xdebug_get_function_stack()); if(function_exists("xdebug_get_function_stack")){
}else{ $trace = array_reverse(xdebug_get_function_stack());
$e = new \Exception(); }else{
$trace = $e->getTrace(); $e = new \Exception();
$trace = $e->getTrace();
}
} }
$messages = []; $messages = [];
$j = 0; $j = 0;
for($i = (int) $start; isset($trace[$i]); ++$i, ++$j){ for($i = (int) $start; isset($trace[$i]); ++$i, ++$j){
@ -203,7 +206,7 @@ namespace pocketmine {
return rtrim(str_replace(["\\", ".php", "phar://", rtrim(str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PATH), "/"), rtrim(str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PLUGIN_PATH), "/")], ["/", "", "", "", ""], $path), "/"); return rtrim(str_replace(["\\", ".php", "phar://", rtrim(str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PATH), "/"), rtrim(str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PLUGIN_PATH), "/")], ["/", "", "", "", ""], $path), "/");
} }
function error_handler($errno, $errstr, $errfile, $errline){ function error_handler($errno, $errstr, $errfile, $errline, $trace = null){
global $lastError; global $lastError;
if(error_reporting() === 0){ //@ error-control if(error_reporting() === 0){ //@ error-control
return false; return false;
@ -234,7 +237,8 @@ namespace pocketmine {
$oldFile = $errfile; $oldFile = $errfile;
$errfile = cleanPath($errfile); $errfile = cleanPath($errfile);
$logger->log($type, "An $errno error happened: \"$errstr\" in \"$errfile\" at line $errline"); $logger->log($type, "An $errno error happened: \"$errstr\" in \"$errfile\" at line $errline");
foreach(($trace = getTrace(3)) as $i => $line){
foreach(($trace = getTrace($trace === null ? 3 : 0, $trace)) as $i => $line){
$logger->debug($line); $logger->debug($line);
} }
$lastError = [ $lastError = [

View File

@ -1053,7 +1053,7 @@ class Server{
} }
if(($provider = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "mcregion"))) === null){ if(($provider = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "mcregion"))) === null){
$provider = "pocketmine\\level\\format\\mcregion\\McRegion"; $provider = LevelProviderManager::getProviderByName("mcregion");
} }
$path = $this->getDataPath() . "worlds/" . $name . "/"; $path = $this->getDataPath() . "worlds/" . $name . "/";
@ -1450,7 +1450,7 @@ class Server{
$this->setConfigInt("difficulty", 3); $this->setConfigInt("difficulty", 3);
} }
define("pocketmine\\DEBUG", $this->getProperty("debug.level", 1)); define("pocketmine\\DEBUG", (int) $this->getProperty("debug.level", 1));
if($this->logger instanceof MainLogger){ if($this->logger instanceof MainLogger){
$this->logger->setLogDebug(\pocketmine\DEBUG > 1); $this->logger->setLogDebug(\pocketmine\DEBUG > 1);
} }
@ -1490,8 +1490,9 @@ class Server{
$this->pluginManager->setUseTimings($this->getProperty("settings.enable-profiling", false)); $this->pluginManager->setUseTimings($this->getProperty("settings.enable-profiling", false));
$this->pluginManager->registerInterface("pocketmine\\plugin\\PharPluginLoader"); $this->pluginManager->registerInterface("pocketmine\\plugin\\PharPluginLoader");
register_shutdown_function(array($this, "crashDump")); set_exception_handler([$this, "exceptionHandler"]);
register_shutdown_function(array($this, "forceShutdown")); register_shutdown_function([$this, "crashDump"]);
register_shutdown_function([$this, "forceShutdown"]);
$this->pluginManager->loadPlugins($this->pluginPath); $this->pluginManager->loadPlugins($this->pluginPath);
@ -1805,6 +1806,20 @@ class Server{
} }
} }
public function exceptionHandler(\Exception $e){
if($e === null){
return;
}
error_handler(E_ERROR, $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTrace());
global $lastExceptionError, $lastError;
$lastExceptionError = $lastError;
$this->crashDump();
$this->forceShutdown();
kill(getmypid());
exit(1);
}
public function crashDump(){ public function crashDump(){
if($this->isRunning === false){ if($this->isRunning === false){
return; return;