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