From d8cba4f0454c57c2c317ce1a567a7a05f9cdfe28 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Fri, 18 Jul 2014 11:54:11 +0200 Subject: [PATCH 1/6] Implemented new crash dumps --- src/pocketmine/CrashDump.php | 193 ++++++++++++++++++++++++++++++++++ src/pocketmine/PocketMine.php | 5 +- src/pocketmine/Server.php | 86 ++------------- 3 files changed, 204 insertions(+), 80 deletions(-) create mode 100644 src/pocketmine/CrashDump.php diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php new file mode 100644 index 000000000..2360e7be6 --- /dev/null +++ b/src/pocketmine/CrashDump.php @@ -0,0 +1,193 @@ +time = time(); + $this->server = $server; + $this->path = $this->server->getDataPath() . "CrashDump_" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log"; + $this->fp = fopen($this->path, "wb"); + $this->addLine("PocketMine-MP Crash Dump " . date("D M j H:i:s T Y", $this->time)); + $this->addLine(); + $this->baseCrash(); + $this->generalData(); + $this->pluginsData(); + + $this->extraData(); + + $this->encodeData(); + } + + public function getPath(){ + return $this->path; + } + + public function getEncodedData(){ + return $this->encodedData; + } + + public function getData(){ + return $this->data; + } + + private function encodeData(){ + $this->addLine(); + $this->addLine("----------------------REPORT THE DATA BELOW THIS LINE-----------------------"); + $this->addLine(); + $this->addLine("===BEGIN CRASH DUMP==="); + $this->encodedData = zlib_encode(json_encode($this->data, JSON_UNESCAPED_SLASHES), ZLIB_ENCODING_DEFLATE, 9); + foreach(str_split(base64_encode($this->encodedData), 76) as $line){ + $this->addLine($line); + } + $this->addLine("===END CRASH DUMP==="); + } + + private function pluginsData(){ + if(class_exists("pocketmine\\plugin\\PluginManager", false)){ + $this->addLine(); + $this->addLine("Loaded plugins:"); + $this->data["plugins"] = []; + foreach($this->server->getPluginManager()->getPlugins() as $p){ + $d = $p->getDescription(); + $this->data["plugins"][$d->getName()] = [ + "name" => $d->getName(), + "version" => $d->getVersion(), + "authors" => $d->getAuthors(), + "api" => $d->getCompatibleApis(), + "enabled" => $p->isEnabled(), + "depends" => $d->getDepend(), + "softDepends" => $d->getSoftDepend(), + "main" => $d->getMain(), + "load" => $d->getOrder() === PluginLoadOrder::POSTWORLD ? "POSTWORLD" : "STARTUP", + "website" => $d->getWebsite() + ]; + $this->addLine($d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors())." for API(s) ". implode(", ", $d->getCompatibleApis())); + } + } + } + + private function extraData(){ + global $arguments; + $this->data["parameters"] = (array) $arguments; + $this->data["server.properties"] = @file_get_contents($this->server->getDataPath() . "server.properties"); + $this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#", "rcon.password=******", $this->data["server.properties"]); + $this->data["pocketmine.yml"] = @file_get_contents($this->server->getDataPath() . "pocketmine.yml"); + $extensions = []; + foreach(get_loaded_extensions() as $ext){ + $extensions[$ext] = phpversion($ext); + } + $this->data["extensions"] = $extensions; + ob_start(); + phpinfo(); + $this->data["phpinfo"] = ob_get_contents(); + ob_end_clean(); + } + + private function baseCrash(){ + $error = (array) error_get_last(); + $errorConversion = array( + E_ERROR => "E_ERROR", + E_WARNING => "E_WARNING", + E_PARSE => "E_PARSE", + E_NOTICE => "E_NOTICE", + E_CORE_ERROR => "E_CORE_ERROR", + E_CORE_WARNING => "E_CORE_WARNING", + E_COMPILE_ERROR => "E_COMPILE_ERROR", + E_COMPILE_WARNING => "E_COMPILE_WARNING", + E_USER_ERROR => "E_USER_ERROR", + E_USER_WARNING => "E_USER_WARNING", + E_USER_NOTICE => "E_USER_NOTICE", + E_STRICT => "E_STRICT", + E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", + E_DEPRECATED => "E_DEPRECATED", + E_USER_DEPRECATED => "E_USER_DEPRECATED", + ); + $fullFile = $error["file"]; + $error["file"] = str_replace(["\\", ".php", str_replace("\\", "/", \pocketmine\PATH)], ["/", "", ""], $error["file"]); + $error["type"] = isset($errorConversion[$error["type"]]) ? $errorConversion[$error["type"]] : $error["type"]; + + $this->data["error"] = $error; + $this->addLine("Error: ". $error["message"]); + $this->addLine("File: ". $error["file"]); + $this->addLine("Line: ". $error["line"]); + $this->addLine("Type: ". $error["type"]); + + $this->addLine("Code:"); + $this->data["code"] = []; + $file = @file($fullFile, FILE_IGNORE_NEW_LINES); + for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10; ++$l){ + $this->addLine("[" . ($l + 1) . "] " . @$file[$l]); + $this->data["code"][$l + 1] = @$file[$l]; + } + $this->addLine(); + $this->addLine("Backtrace:"); + foreach(($this->data["trace"] = getTrace(3)) as $line){ + $this->addLine($line); + } + $this->addLine(); + } + + private function generalData(){ + $version = new VersionString(); + $this->data["general"] = []; + $this->data["general"]["version"] = $version->get(false); + $this->data["general"]["build"] = $version->getNumber(); + $this->data["general"]["protocol"] = Info::CURRENT_PROTOCOL; + $this->data["general"]["api"] = \pocketmine\API_VERSION; + $this->data["general"]["git"] = \pocketmine\GIT_COMMIT; + $this->data["general"]["uname"] = php_uname("a"); + $this->data["general"]["php"] = phpversion(); + $this->data["general"]["zend"] = zend_version(); + $this->data["general"]["php_os"] = PHP_OS; + $this->data["general"]["os"] = Utils::getOS(); + $this->addLine("PocketMine-MP version: " . $version->get(false). " #" . $version->getNumber() . " [Protocol " . Info::CURRENT_PROTOCOL . "; API " . API_VERSION . "]"); + $this->addLine("Git commit: " . GIT_COMMIT); + $this->addLine("uname -a: " . php_uname("a")); + $this->addLine("PHP Version: " . phpversion()); + $this->addLine("Zend version: " . zend_version()); + $this->addLine("OS : " . PHP_OS . ", " . Utils::getOS()); + } + + public function addLine($line = ""){ + fwrite($this->fp, $line . PHP_EOL); + } + + public function add($str){ + fwrite($this->fp, $str); + } + +} \ No newline at end of file diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 134f57ebc..f26925c86 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -184,7 +184,7 @@ namespace pocketmine { $params .= (is_object($value) ? get_class($value) . " " . (method_exists($value, "__toString") ? $value->__toString() : "object") : gettype($value) . " " . @strval($value)) . ", "; } } - $messages[] = "#$j " . (isset($trace[$i]["file"]) ? $trace[$i]["file"] : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . $trace[$i]["type"] : "") . $trace[$i]["function"] . "(" . substr($params, 0, -2) . ")"; + $messages[] = "#$j " . (isset($trace[$i]["file"]) ? str_replace(["\\", ".php", str_replace("\\", "/", \pocketmine\PATH)], ["/", "", ""], $trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . $trace[$i]["type"] : "") . $trace[$i]["function"] . "(" . substr($params, 0, -2) . ")"; } return $messages; @@ -214,7 +214,8 @@ namespace pocketmine { $type = ($errno === E_ERROR or $errno === E_WARNING or $errno === E_USER_ERROR or $errno === E_USER_WARNING) ? LogLevel::ERROR : LogLevel::NOTICE; $errno = isset($errorConversion[$errno]) ? $errorConversion[$errno] : $errno; $logger = MainLogger::getLogger(); - $logger->log($type, "A $errno error happened: \"$errstr\" in \"$errfile\" at line $errline"); + $errfile = str_replace(["\\", ".php", str_replace("\\", "/", \pocketmine\PATH)], ["/", "", ""], $errfile); + $logger->log($type, "An $errno error happened: \"$errstr\" in \"$errfile\" at line $errline"); foreach(getTrace() as $i => $line){ $logger->debug($line); } diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 526d7db65..fa4098849 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1747,7 +1747,7 @@ class Server{ $this->tickCounter = 0; - //register_shutdown_function(array($this, "dumpError")); + register_shutdown_function(array($this, "crashDump")); register_shutdown_function(array($this, "forceShutdown")); if(function_exists("pcntl_signal")){ pcntl_signal(SIGTERM, array($this, "shutdown")); @@ -1759,6 +1759,7 @@ class Server{ $this->logger->info("Done (" . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"'); + crash(); $this->tickProcessor(); $this->forceShutdown(); } @@ -1779,89 +1780,18 @@ class Server{ } } - public function dumpError(){ - //TODO - if($this->stop === true){ + public function crashDump(){ + if($this->isRunning === false){ return; } ini_set("memory_limit", "-1"); //Fix error dump not dumped on memory problems $this->logger->emergency("An unrecoverable has occurred and the server has crashed. Creating an error dump"); - $dump = "```\r\n# PocketMine-MP Error Dump " . date("D M j H:i:s T Y") . "\r\n"; - $er = error_get_last(); - $errorConversion = array( - E_ERROR => "E_ERROR", - E_WARNING => "E_WARNING", - E_PARSE => "E_PARSE", - E_NOTICE => "E_NOTICE", - E_CORE_ERROR => "E_CORE_ERROR", - E_CORE_WARNING => "E_CORE_WARNING", - E_COMPILE_ERROR => "E_COMPILE_ERROR", - E_COMPILE_WARNING => "E_COMPILE_WARNING", - E_USER_ERROR => "E_USER_ERROR", - E_USER_WARNING => "E_USER_WARNING", - E_USER_NOTICE => "E_USER_NOTICE", - E_STRICT => "E_STRICT", - E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", - E_DEPRECATED => "E_DEPRECATED", - E_USER_DEPRECATED => "E_USER_DEPRECATED", - ); - $er["type"] = isset($errorConversion[$er["type"]]) ? $errorConversion[$er["type"]] : $er["type"]; - $dump .= "Error: " . var_export($er, true) . "\r\n\r\n"; - if(stripos($er["file"], "plugin") !== false){ - $dump .= "THIS ERROR WAS CAUSED BY A PLUGIN. REPORT IT TO THE PLUGIN DEVELOPER.\r\n"; - } + $dump = new CrashDump($this); + $this->logger->emergency("Please submit the \"".$dump->getPath()."\" file to the Bug Reporting page. Give as much info as you can."); - $dump .= "Code: \r\n"; - $file = @file($er["file"], FILE_IGNORE_NEW_LINES); - for($l = max(0, $er["line"] - 10); $l < $er["line"] + 10; ++$l){ - $dump .= "[" . ($l + 1) . "] " . @$file[$l] . "\r\n"; - } - $dump .= "\r\n\r\n"; - $dump .= "Backtrace: \r\n"; - foreach(getTrace() as $line){ - $dump .= "$line\r\n"; - } - $dump .= "\r\n\r\n"; - $version = new VersionString(); - $dump .= "PocketMine-MP version: " . $version->get(false). " #" . $version->getNumber() . " [Protocol " . Info::CURRENT_PROTOCOL . "; API " . API_VERSION . "]\r\n"; - $dump .= "Git commit: " . GIT_COMMIT . "\r\n"; - $dump .= "uname -a: " . php_uname("a") . "\r\n"; - $dump .= "PHP Version: " . phpversion() . "\r\n"; - $dump .= "Zend version: " . zend_version() . "\r\n"; - $dump .= "OS : " . PHP_OS . ", " . Utils::getOS() . "\r\n"; - $dump .= "Debug Info: " . var_export($this->debugInfo(false), true) . "\r\n\r\n\r\n"; - global $arguments; - $dump .= "Parameters: " . var_export($arguments, true) . "\r\n\r\n\r\n"; - $p = $this->api->getProperties(); - if($p["rcon.password"] != ""){ - $p["rcon.password"] = "******"; - } - $dump .= "server.properties: " . var_export($p, true) . "\r\n\r\n\r\n"; - if(class_exists("pocketmine\\plugin\\PluginManager", false)){ - $dump .= "Loaded plugins:\r\n"; - foreach($this->getPluginManager()->getPlugins() as $p){ - $d = $p->getDescription(); - $dump .= $d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . "\r\n"; - } - $dump .= "\r\n\r\n"; - } + //$this->checkMemory(); + //$dump .= "Memory Usage Tracking: \r\n" . chunk_split(base64_encode(gzdeflate(implode(";", $this->memoryStats), 9))) . "\r\n"; - $extensions = []; - foreach(get_loaded_extensions() as $ext){ - $extensions[$ext] = phpversion($ext); - } - - $dump .= "Loaded Modules: " . var_export($extensions, true) . "\r\n"; - $this->checkMemory(); - $dump .= "Memory Usage Tracking: \r\n" . chunk_split(base64_encode(gzdeflate(implode(";", $this->memoryStats), 9))) . "\r\n"; - ob_start(); - phpinfo(); - $dump .= "\r\nphpinfo(): \r\n" . chunk_split(base64_encode(gzdeflate(ob_get_contents(), 9))) . "\r\n"; - ob_end_clean(); - $dump .= "\r\n```"; - $name = "Error_Dump_" . date("D_M_j-H.i.s-T_Y"); - //log($dump, $name, true, 0, true); - $this->logger->emergency("Please submit the \"{$name}.log\" file to the Bug Reporting page. Give as much info as you can.", true, true, 0); } private function tickProcessor(){ From 2e773a32ff008e972ee40083161e4819369157ca Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Fri, 18 Jul 2014 11:55:24 +0200 Subject: [PATCH 2/6] Removed crash() --- src/pocketmine/Server.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index fa4098849..5d9455076 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1759,7 +1759,6 @@ class Server{ $this->logger->info("Done (" . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"'); - crash(); $this->tickProcessor(); $this->forceShutdown(); } From 2448405e3aeda963acf28f39fe978994c8233267 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Fri, 18 Jul 2014 11:58:39 +0200 Subject: [PATCH 3/6] Fixed rcon password being shown --- src/pocketmine/CrashDump.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php index 2360e7be6..19d327af5 100644 --- a/src/pocketmine/CrashDump.php +++ b/src/pocketmine/CrashDump.php @@ -104,7 +104,7 @@ class CrashDump{ global $arguments; $this->data["parameters"] = (array) $arguments; $this->data["server.properties"] = @file_get_contents($this->server->getDataPath() . "server.properties"); - $this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#", "rcon.password=******", $this->data["server.properties"]); + $this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $this->data["server.properties"]); $this->data["pocketmine.yml"] = @file_get_contents($this->server->getDataPath() . "pocketmine.yml"); $extensions = []; foreach(get_loaded_extensions() as $ext){ From 7b45df680e9ee5265f181a268d8e7e236d04d224 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Sun, 27 Jul 2014 12:39:38 +0200 Subject: [PATCH 4/6] Added time and plugin cause --- src/pocketmine/CrashDump.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pocketmine/CrashDump.php b/src/pocketmine/CrashDump.php index 19d327af5..38e00e7d7 100644 --- a/src/pocketmine/CrashDump.php +++ b/src/pocketmine/CrashDump.php @@ -41,6 +41,7 @@ class CrashDump{ $this->server = $server; $this->path = $this->server->getDataPath() . "CrashDump_" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log"; $this->fp = fopen($this->path, "wb"); + $this->data["time"] = $this->time; $this->addLine("PocketMine-MP Crash Dump " . date("D M j H:i:s T Y", $this->time)); $this->addLine(); $this->baseCrash(); @@ -146,6 +147,15 @@ class CrashDump{ $this->addLine("Line: ". $error["line"]); $this->addLine("Type: ". $error["type"]); + if(strpos($error["file"], "src/pocketmine/") === false and strpos($error["file"], "src/raklib/") === false and file_exists($fullFile)){ + $this->addLine(); + $this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN"); + $this->data["plugin"] = true; + }else{ + $this->data["plugin"] = false; + } + + $this->addLine(); $this->addLine("Code:"); $this->data["code"] = []; $file = @file($fullFile, FILE_IGNORE_NEW_LINES); From 9d75baf212c3e541cc7efe418c09d9d1397a4b4e Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Sun, 27 Jul 2014 14:00:00 +0200 Subject: [PATCH 5/6] Added automatic reporting --- src/pocketmine/Server.php | 18 +++++++++++++++++- src/pocketmine/resources/pocketmine.yml | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 5d9455076..7e29fa286 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1784,8 +1784,24 @@ class Server{ return; } ini_set("memory_limit", "-1"); //Fix error dump not dumped on memory problems - $this->logger->emergency("An unrecoverable has occurred and the server has crashed. Creating an error dump"); + $this->logger->emergency("An unrecoverable has occurred and the server has crashed. Creating a crash dump"); $dump = new CrashDump($this); + + if($this->getProperty("settings.send-crash", true) !== false){ + $reply = Utils::postURL("http://crash.pocketmine.net/submit/api", [ + "report" => "yes", + "name" => "PocketMine-MP ".$this->getPocketMineVersion(), + "email" => "crash@pocketmine.net", + "reportPaste" => base64_encode($dump->getEncodedData()) + ]); + + if(($data = json_decode($reply)) !== false and isset($data->crashId)){ + $reportId = $data->crashId; + $reportUrl = $data->crashUrl; + $this->logger->emergency("The crash dump has ben automatically submitted to the Crash Archive. You can view it on $reportUrl or use the ID #$reportId."); + } + } + $this->logger->emergency("Please submit the \"".$dump->getPath()."\" file to the Bug Reporting page. Give as much info as you can."); //$this->checkMemory(); diff --git a/src/pocketmine/resources/pocketmine.yml b/src/pocketmine/resources/pocketmine.yml index 3b7926525..d1aecc4ba 100644 --- a/src/pocketmine/resources/pocketmine.yml +++ b/src/pocketmine/resources/pocketmine.yml @@ -10,6 +10,7 @@ settings: advanced-cache: false upnp-forwarding: false send-usage: true + send-crash: true async-workers: 4 debug: From e6e15ceffa2c57a77943358327b460a1ac4be259 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Sun, 27 Jul 2014 14:09:34 +0200 Subject: [PATCH 6/6] Fixed typo in crash text --- src/pocketmine/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 7e29fa286..861b5929e 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1784,7 +1784,7 @@ class Server{ return; } ini_set("memory_limit", "-1"); //Fix error dump not dumped on memory problems - $this->logger->emergency("An unrecoverable has occurred and the server has crashed. Creating a crash dump"); + $this->logger->emergency("An unrecoverable error has occurred and the server has crashed. Creating a crash dump"); $dump = new CrashDump($this); if($this->getProperty("settings.send-crash", true) !== false){