Use object models for crashdump generation

This commit is contained in:
Dylan K. Taylor 2021-11-04 16:55:04 +00:00
parent 4f8501ff34
commit 8ac999cbd4
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
5 changed files with 233 additions and 68 deletions

View File

@ -24,6 +24,9 @@ declare(strict_types=1);
namespace pocketmine;
use Composer\InstalledVersions;
use pocketmine\crash\CrashDumpData;
use pocketmine\crash\CrashDumpDataGeneral;
use pocketmine\crash\CrashDumpDataPluginEntry;
use pocketmine\errorhandler\ErrorTypeToStringMap;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\plugin\PluginBase;
@ -69,6 +72,7 @@ use const FILE_IGNORE_NEW_LINES;
use const JSON_UNESCAPED_SLASHES;
use const PHP_EOL;
use const PHP_OS;
use const PHP_VERSION;
use const SORT_STRING;
class CrashDump{
@ -91,13 +95,9 @@ class CrashDump{
private $fp;
/** @var float */
private $time;
/**
* @var mixed[]
* @phpstan-var array<string, mixed>
*/
private $data = [];
private CrashDumpData $data;
/** @var string */
private $encodedData = "";
private $encodedData;
/** @var string */
private $path;
@ -118,9 +118,10 @@ class CrashDump{
throw new \RuntimeException("Could not create Crash Dump");
}
$this->fp = $fp;
$this->data["format_version"] = self::FORMAT_VERSION;
$this->data["time"] = $this->time;
$this->data["uptime"] = $this->time - $this->server->getStartTime();
$this->data = new CrashDumpData();
$this->data->format_version = self::FORMAT_VERSION;
$this->data->time = $this->time;
$this->data->uptime = $this->time - $this->server->getStartTime();
$this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", (int) $this->time));
$this->addLine();
$this->baseCrash();
@ -142,11 +143,7 @@ class CrashDump{
return $this->encodedData;
}
/**
* @return mixed[]
* @phpstan-return array<string, mixed>
*/
public function getData() : array{
public function getData() : CrashDumpData{
return $this->data;
}
@ -173,22 +170,21 @@ class CrashDump{
$plugins = $this->pluginManager->getPlugins();
$this->addLine();
$this->addLine("Loaded plugins:");
$this->data["plugins"] = [];
ksort($plugins, SORT_STRING);
foreach($plugins 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" => mb_strtoupper($d->getOrder()->name()),
"website" => $d->getWebsite()
];
$this->data->plugins[$d->getName()] = new CrashDumpDataPluginEntry(
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: mb_strtoupper($d->getOrder()->name()),
website: $d->getWebsite()
);
$this->addLine($d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . " for API(s) " . implode(", ", $d->getCompatibleApis()));
}
}
@ -198,32 +194,26 @@ class CrashDump{
global $argv;
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-settings", true)){
$this->data["parameters"] = (array) $argv;
$this->data->parameters = (array) $argv;
if(($serverDotProperties = @file_get_contents(Path::join($this->server->getDataPath(), "server.properties"))) !== false){
$this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties);
}else{
$this->data["server.properties"] = $serverDotProperties;
$this->data->serverDotProperties = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties) ?? throw new AssumptionFailedError("Pattern is valid");
}
if(($pocketmineDotYml = @file_get_contents(Path::join($this->server->getDataPath(), "pocketmine.yml"))) !== false){
$this->data["pocketmine.yml"] = $pocketmineDotYml;
}else{
$this->data["pocketmine.yml"] = "";
$this->data->pocketmineDotYml = $pocketmineDotYml;
}
}else{
$this->data["pocketmine.yml"] = "";
$this->data["server.properties"] = "";
$this->data["parameters"] = [];
}
$extensions = [];
foreach(get_loaded_extensions() as $ext){
$extensions[$ext] = phpversion($ext);
$version = phpversion($ext);
if($version === false) throw new AssumptionFailedError();
$extensions[$ext] = $version;
}
$this->data["extensions"] = $extensions;
$this->data->extensions = $extensions;
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-phpinfo", true)){
ob_start();
phpinfo();
$this->data["phpinfo"] = ob_get_contents();
$this->data->phpinfo = ob_get_contents(); // @phpstan-ignore-line
ob_end_clean();
}
}
@ -255,18 +245,18 @@ class CrashDump{
if(isset($lastError["trace"])){
$lastError["trace"] = Utils::printableTrace($lastError["trace"]);
}
$this->data["lastError"] = $lastError;
$this->data->lastError = $lastError;
}
$this->data["error"] = $error;
unset($this->data["error"]["fullFile"]);
unset($this->data["error"]["trace"]);
$this->data->error = $error;
unset($this->data->error["fullFile"]);
unset($this->data->error["trace"]);
$this->addLine("Error: " . $error["message"]);
$this->addLine("File: " . $error["file"]);
$this->addLine("Line: " . $error["line"]);
$this->addLine("Type: " . $error["type"]);
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_NONE;
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_NONE;
if(!$this->determinePluginFromFile($error["fullFile"], true)){ //fatal errors won't leave any stack trace
foreach($error["trace"] as $frame){
if(!isset($frame["file"])){
@ -280,21 +270,20 @@ class CrashDump{
$this->addLine();
$this->addLine("Code:");
$this->data["code"] = [];
if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-code", true) and file_exists($error["fullFile"])){
$file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES);
if($file !== false){
for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){
$this->addLine("[" . ($l + 1) . "] " . $file[$l]);
$this->data["code"][$l + 1] = $file[$l];
$this->data->code[$l + 1] = $file[$l];
}
}
}
$this->addLine();
$this->addLine("Backtrace:");
foreach(($this->data["trace"] = Utils::printableTrace($error["trace"])) as $line){
foreach(($this->data->trace = Utils::printableTrace($error["trace"])) as $line){
$this->addLine($line);
}
$this->addLine();
@ -306,10 +295,10 @@ class CrashDump{
$this->addLine();
if($crashFrame){
$this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN");
$this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_DIRECT;
$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;
$this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_INDIRECT;
}
if(file_exists($filePath)){
@ -319,7 +308,7 @@ class CrashDump{
foreach($this->server->getPluginManager()->getPlugins() as $plugin){
$filePath = Filesystem::cleanPath($file->getValue($plugin));
if(strpos($frameCleanPath, $filePath) === 0){
$this->data["plugin"] = $plugin->getName();
$this->data->plugin = $plugin->getName();
$this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName());
break;
}
@ -341,19 +330,20 @@ class CrashDump{
);
}
$this->data["general"] = [];
$this->data["general"]["name"] = $this->server->getName();
$this->data["general"]["base_version"] = VersionInfo::BASE_VERSION;
$this->data["general"]["build"] = VersionInfo::BUILD_NUMBER;
$this->data["general"]["is_dev"] = VersionInfo::IS_DEVELOPMENT_BUILD;
$this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL;
$this->data["general"]["git"] = VersionInfo::GIT_HASH();
$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->data["general"]["composer_libraries"] = $composerLibraries;
$this->data->general = new CrashDumpDataGeneral(
name: $this->server->getName(),
base_version: VersionInfo::BASE_VERSION,
build: VersionInfo::BUILD_NUMBER,
is_dev: VersionInfo::IS_DEVELOPMENT_BUILD,
protocol: ProtocolInfo::CURRENT_PROTOCOL,
git: VersionInfo::GIT_HASH(),
uname: php_uname("a"),
php: PHP_VERSION,
zend: zend_version(),
php_os: PHP_OS,
os: Utils::getOS(),
composer_libraries: $composerLibraries,
);
$this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]");
$this->addLine("Git commit: " . VersionInfo::GIT_HASH());
$this->addLine("uname -a: " . php_uname("a"));

View File

@ -1504,8 +1504,8 @@ class Server{
}
@touch($stamp); //update file timestamp
$plugin = $dump->getData()["plugin"];
if(is_string($plugin)){
$plugin = $dump->getData()->plugin;
if($plugin !== ""){
$p = $this->pluginManager->getPlugin($plugin);
if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){
$this->logger->debug("Not sending crashdump due to caused by non-phar plugin");
@ -1513,7 +1513,7 @@ class Server{
}
}
if($dump->getData()["error"]["type"] === \ParseError::class){
if($dump->getData()->error["type"] === \ParseError::class){
$report = false;
}

View File

@ -0,0 +1,84 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crash;
final class CrashDumpData implements \JsonSerializable{
public int $format_version;
public float $time;
public float $uptime;
/** @var mixed[] */
public array $lastError = [];
/** @var mixed[] */
public array $error;
public string $plugin_involvement;
public string $plugin = "";
/** @var string[] */
public array $code = [];
/** @var string[] */
public array $trace;
/**
* @var CrashDumpDataPluginEntry[]
* @phpstan-var array<string, CrashDumpDataPluginEntry>
*/
public array $plugins = [];
/** @var string[] */
public array $parameters = [];
public string $serverDotProperties = "";
public string $pocketmineDotYml = "";
/**
* @var string[]
* @phpstan-var array<string, string>
*/
public array $extensions = [];
public string $phpinfo = "";
public CrashDumpDataGeneral $general;
/**
* @return mixed[]
*/
public function jsonSerialize() : array{
$result = (array) $this;
unset($result["serverDotProperties"]);
unset($result["pocketmineDotYml"]);
$result["pocketmine.yml"] = $this->pocketmineDotYml;
$result["server.properties"] = $this->serverDotProperties;
return $result;
}
}

View File

@ -0,0 +1,46 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crash;
final class CrashDumpDataGeneral{
/**
* @param string[] $composer_libraries
* @phpstan-param array<string, string> $composer_libraries
*/
public function __construct(
public string $name,
public string $base_version,
public int $build,
public bool $is_dev,
public int $protocol,
public string $git,
public string $uname,
public string $php,
public string $zend,
public string $php_os,
public string $os,
public array $composer_libraries,
){}
}

View File

@ -0,0 +1,45 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\crash;
final class CrashDumpDataPluginEntry{
/**
* @param string[] $authors
* @param string[] $api
* @param string[] $depends
* @param string[] $softDepends
*/
public function __construct(
public string $name,
public string $version,
public array $authors,
public array $api,
public bool $enabled,
public array $depends,
public array $softDepends,
public string $main,
public string $load,
public string $website,
){}
}