PageRenderTime 23ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/pocketmine/CrashDump.php

https://gitlab.com/matthww/Elywing
PHP | 259 lines | 208 code | 30 blank | 21 comment | 13 complexity | fe5a6472b59578506e6811edeaaee56d MD5 | raw file
  1. <?php
  2. /*
  3. *
  4. * ____ _ _ __ __ _ __ __ ____
  5. * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
  6. * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
  7. * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
  8. * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Lesser General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * @author PocketMine Team
  16. * @link http://www.pocketmine.net/
  17. *
  18. *
  19. */
  20. namespace pocketmine;
  21. use pocketmine\network\protocol\Info;
  22. use pocketmine\plugin\PluginBase;
  23. use pocketmine\plugin\PluginLoadOrder;
  24. use pocketmine\utils\Utils;
  25. use pocketmine\utils\VersionString;
  26. use raklib\RakLib;
  27. class CrashDump{
  28. /** @var Server */
  29. private $server;
  30. private $fp;
  31. private $time;
  32. private $data = [];
  33. private $encodedData = null;
  34. private $path;
  35. public function __construct(Server $server){
  36. $this->time = time();
  37. $this->server = $server;
  38. $this->path = $this->server->getCrashPath() . "CrashDump_" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log";
  39. $this->fp = @fopen($this->path, "wb");
  40. if(!is_resource($this->fp)){
  41. throw new \RuntimeException("Could not create Crash Dump");
  42. }
  43. $this->data["time"] = $this->time;
  44. $this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $this->time));
  45. $this->addLine();
  46. try{
  47. $this->baseCrash();
  48. }catch(\Exception $e){
  49. //Attempt to fix incomplete crashdumps
  50. $this->addLine("CrashDump crashed while generating base crash data");
  51. $this->addLine();
  52. }
  53. $this->generalData();
  54. $this->pluginsData();
  55. $this->extraData();
  56. //$this->encodeData();
  57. }
  58. public function getPath(){
  59. return $this->path;
  60. }
  61. public function getEncodedData(){
  62. return $this->encodedData;
  63. }
  64. public function getData(){
  65. return $this->data;
  66. }
  67. private function encodeData(){
  68. $this->addLine();
  69. $this->addLine("----------------------REPORT THE DATA BELOW THIS LINE-----------------------");
  70. $this->addLine();
  71. $this->addLine("===BEGIN CRASH DUMP===");
  72. $this->encodedData = zlib_encode(json_encode($this->data, JSON_UNESCAPED_SLASHES), ZLIB_ENCODING_DEFLATE, 9);
  73. foreach(str_split(base64_encode($this->encodedData), 76) as $line){
  74. $this->addLine($line);
  75. }
  76. $this->addLine("===END CRASH DUMP===");
  77. }
  78. private function pluginsData(){
  79. if(class_exists("pocketmine\\plugin\\PluginManager", false)){
  80. $this->addLine();
  81. $this->addLine("Loaded plugins:");
  82. $this->data["plugins"] = [];
  83. foreach($this->server->getPluginManager()->getPlugins() as $p){
  84. $d = $p->getDescription();
  85. $this->data["plugins"][$d->getName()] = [
  86. "name" => $d->getName(),
  87. "version" => $d->getVersion(),
  88. "authors" => $d->getAuthors(),
  89. "api" => $d->getCompatibleApis(),
  90. "enabled" => $p->isEnabled(),
  91. "depends" => $d->getDepend(),
  92. "softDepends" => $d->getSoftDepend(),
  93. "main" => $d->getMain(),
  94. "load" => $d->getOrder() === PluginLoadOrder::POSTWORLD ? "POSTWORLD" : "STARTUP",
  95. "website" => $d->getWebsite()
  96. ];
  97. $this->addLine($d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . " for API(s) " . implode(", ", $d->getCompatibleApis()));
  98. }
  99. }
  100. }
  101. private function extraData(){
  102. global $arguments;
  103. if($this->server->getProperty("auto-report.send-settings", true) !== false){
  104. $this->data["parameters"] = (array) $arguments;
  105. $this->data["server.properties"] = @file_get_contents($this->server->getDataPath() . "server.properties");
  106. $this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $this->data["server.properties"]);
  107. $this->data["pocketmine.yml"] = @file_get_contents($this->server->getDataPath() . "pocketmine.yml");
  108. }else{
  109. $this->data["pocketmine.yml"] = "";
  110. $this->data["server.properties"] = "";
  111. $this->data["parameters"] = [];
  112. }
  113. $extensions = [];
  114. foreach(get_loaded_extensions() as $ext){
  115. $extensions[$ext] = phpversion($ext);
  116. }
  117. $this->data["extensions"] = $extensions;
  118. if($this->server->getProperty("auto-report.send-phpinfo", true) !== false){
  119. ob_start();
  120. phpinfo();
  121. $this->data["phpinfo"] = ob_get_contents();
  122. ob_end_clean();
  123. }
  124. }
  125. private function baseCrash(){
  126. global $lastExceptionError, $lastError;
  127. if(isset($lastExceptionError)){
  128. $error = $lastExceptionError;
  129. }else{
  130. $error = (array) error_get_last();
  131. $error["trace"] = @getTrace(3);
  132. $errorConversion = [
  133. E_ERROR => "E_ERROR",
  134. E_WARNING => "E_WARNING",
  135. E_PARSE => "E_PARSE",
  136. E_NOTICE => "E_NOTICE",
  137. E_CORE_ERROR => "E_CORE_ERROR",
  138. E_CORE_WARNING => "E_CORE_WARNING",
  139. E_COMPILE_ERROR => "E_COMPILE_ERROR",
  140. E_COMPILE_WARNING => "E_COMPILE_WARNING",
  141. E_USER_ERROR => "E_USER_ERROR",
  142. E_USER_WARNING => "E_USER_WARNING",
  143. E_USER_NOTICE => "E_USER_NOTICE",
  144. E_STRICT => "E_STRICT",
  145. E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR",
  146. E_DEPRECATED => "E_DEPRECATED",
  147. E_USER_DEPRECATED => "E_USER_DEPRECATED",
  148. ];
  149. $error["fullFile"] = $error["file"];
  150. $error["file"] = cleanPath($error["file"]);
  151. $error["type"] = isset($errorConversion[$error["type"]]) ? $errorConversion[$error["type"]] : $error["type"];
  152. if(($pos = strpos($error["message"], "\n")) !== false){
  153. $error["message"] = substr($error["message"], 0, $pos);
  154. }
  155. }
  156. if(isset($lastError)){
  157. $this->data["lastError"] = $lastError;
  158. }
  159. $this->data["error"] = $error;
  160. unset($this->data["error"]["fullFile"]);
  161. unset($this->data["error"]["trace"]);
  162. $this->addLine("Error: " . $error["message"]);
  163. $this->addLine("File: " . $error["file"]);
  164. $this->addLine("Line: " . $error["line"]);
  165. $this->addLine("Type: " . $error["type"]);
  166. if(strpos($error["file"], "src/pocketmine/") === false and strpos($error["file"], "src/raklib/") === false and strpos($error["file"], "src/synapse/") === false and file_exists($error["fullFile"])){
  167. $this->addLine();
  168. $this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
  169. $this->data["plugin"] = true;
  170. $reflection = new \ReflectionClass(PluginBase::class);
  171. $file = $reflection->getProperty("file");
  172. $file->setAccessible(true);
  173. foreach($this->server->getPluginManager()->getPlugins() as $plugin){
  174. $filePath = \pocketmine\cleanPath($file->getValue($plugin));
  175. if(strpos($error["file"], $filePath) === 0){
  176. $this->data["plugin"] = $plugin->getName();
  177. $this->addLine("BAD PLUGIN : " . $plugin->getDescription()->getFullName());
  178. break;
  179. }
  180. }
  181. }else{
  182. $this->data["plugin"] = false;
  183. }
  184. $this->addLine();
  185. $this->addLine("Code:");
  186. $this->data["code"] = [];
  187. if($this->server->getProperty("auto-report.send-code", true) !== false){
  188. $file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
  189. for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10; ++$l){
  190. $this->addLine("[" . ($l + 1) . "] " . @$file[$l]);
  191. $this->data["code"][$l + 1] = @$file[$l];
  192. }
  193. }
  194. $this->addLine();
  195. $this->addLine("Backtrace:");
  196. foreach(($this->data["trace"] = $error["trace"]) as $line){
  197. $this->addLine($line);
  198. }
  199. $this->addLine();
  200. }
  201. private function generalData(){
  202. $version = new VersionString();
  203. $this->data["general"] = [];
  204. $this->data["general"]["protocol"] = Info::CURRENT_PROTOCOL;
  205. $this->data["general"]["api"] = \pocketmine\API_VERSION;
  206. $this->data["general"]["git"] = \pocketmine\GIT_COMMIT;
  207. $this->data["general"]["raklib"] = RakLib::VERSION;
  208. $this->data["general"]["uname"] = php_uname("a");
  209. $this->data["general"]["php"] = phpversion();
  210. $this->data["general"]["zend"] = zend_version();
  211. $this->data["general"]["php_os"] = PHP_OS;
  212. $this->data["general"]["os"] = Utils::getOS();
  213. $this->addLine("Elywing version: " . \pocketmine\GIT_COMMIT . " [Protocol " . Info::CURRENT_PROTOCOL . "; API " . API_VERSION . "]");
  214. $this->addLine("uname -a: " . php_uname("a"));
  215. $this->addLine("PHP version: " . phpversion());
  216. $this->addLine("Zend version: " . zend_version());
  217. $this->addLine("OS : " . PHP_OS . ", " . Utils::getOS());
  218. $this->addLine();
  219. $this->addLine("Server uptime: " . $this->server->getUptime());
  220. $this->addLine("Number of loaded worlds: " . count($this->server->getLevels()));
  221. $this->addLine("Players online: ".count($this->server->getOnlinePlayers())."/".$this->server->getMaxPlayers());
  222. }
  223. public function addLine($line = ""){
  224. fwrite($this->fp, $line . PHP_EOL);
  225. }
  226. public function add($str){
  227. fwrite($this->fp, $str);
  228. }
  229. }