121 lines
3.8 KiB
C#
121 lines
3.8 KiB
C#
using PCL.Core.Logging;
|
||||
|
|
|
|||
|
|
namespace PCL;
|
|||
|
|
|
|||
|
|
internal sealed class CrashDetector
|
|||
|
|
{
|
|||
|
|
private readonly CrashEvidenceExtractor _extractor = new();
|
|||
|
|
private readonly CrashStackAnalyzer _stackAnalyzer = new();
|
|||
|
|
|
|||
|
|
public CrashAnalysisResult Analyze(CrashLogSet logs, McInstance? instance)
|
|||
|
|
{
|
|||
|
|
LogWrapper.Info("Crash", "步骤 3:分析崩溃原因");
|
|||
|
|
var result = new CrashAnalysisResult();
|
|||
|
|
if (!logs.HasAnalyzableLog)
|
|||
|
|
{
|
|||
|
|
result.Add(new CrashFinding(CrashCause.NoAnalyzableFile, CrashConfidence.High) { ShouldStop = true });
|
|||
|
|
_LogSummary(result);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var normalizedLogs = _Normalize(logs);
|
|||
|
|
var modIndex = CrashModIndex.Create(normalizedLogs, instance);
|
|||
|
|
|
|||
|
|
_RunPhase(DetectionPhase.Fatal, normalizedLogs, modIndex, result);
|
|||
|
|
if (_ShouldStop(result))
|
|||
|
|
{
|
|||
|
|
_LogSummary(result);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_RunPhase(DetectionPhase.Primary, normalizedLogs, modIndex, result);
|
|||
|
|
if (_ShouldStop(result))
|
|||
|
|
{
|
|||
|
|
_LogSummary(result);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var stackFinding = _stackAnalyzer.Analyze(normalizedLogs, modIndex);
|
|||
|
|
if (stackFinding is not null)
|
|||
|
|
result.Add(stackFinding);
|
|||
|
|
if (_ShouldStop(result))
|
|||
|
|
{
|
|||
|
|
_LogSummary(result);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_RunPhase(DetectionPhase.Secondary, normalizedLogs, modIndex, result);
|
|||
|
|
_LogSummary(result);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static CrashLogSet _Normalize(CrashLogSet logs)
|
|||
|
|
{
|
|||
|
|
var all = logs.All;
|
|||
|
|
if (all.Contains("quilt", StringComparison.OrdinalIgnoreCase) &&
|
|||
|
|
all.Contains("Mod Table Version", StringComparison.Ordinal))
|
|||
|
|
{
|
|||
|
|
LogWrapper.Info("Crash", "处理 Quilt Mod Table 后再继续分析");
|
|||
|
|
all = CrashText.BeforeFirst(all, "| Index") + CrashText.AfterFirst(all, "Mod Table Version:");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new CrashLogSet
|
|||
|
|
{
|
|||
|
|
Game = logs.Game,
|
|||
|
|
Debug = logs.Debug,
|
|||
|
|
CrashReport = logs.CrashReport,
|
|||
|
|
HsErr = logs.HsErr,
|
|||
|
|
All = all
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void _RunPhase(
|
|||
|
|
DetectionPhase phase,
|
|||
|
|
CrashLogSet logs,
|
|||
|
|
CrashModIndex modIndex,
|
|||
|
|
CrashAnalysisResult result)
|
|||
|
|
{
|
|||
|
|
var input = new CrashRuleInput { Logs = logs, ModIndex = modIndex };
|
|||
|
|
foreach (var rule in CrashRuleCatalog.Rules.Where(rule => rule.Phase == phase))
|
|||
|
|
{
|
|||
|
|
var finding = rule.Evaluate(input);
|
|||
|
|
if (finding is not null)
|
|||
|
|
_AddAndLog(result, finding);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
foreach (var finding in _extractor.Extract(phase, logs, modIndex))
|
|||
|
|
_AddAndLog(result, finding);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void _AddAndLog(CrashAnalysisResult result, CrashFinding finding)
|
|||
|
|
{
|
|||
|
|
result.Add(finding);
|
|||
|
|
_LogFinding(finding);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool _ShouldStop(CrashAnalysisResult result)
|
|||
|
|
{
|
|||
|
|
return result.Findings.Any(finding => finding.ShouldStop);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void _LogFinding(CrashFinding finding)
|
|||
|
|
{
|
|||
|
|
var evidence = string.Join(";", finding.Details);
|
|||
|
|
LogWrapper.Info(
|
|||
|
|
"Crash",
|
|||
|
|
$"可能的崩溃原因:{finding.Cause}{(string.IsNullOrEmpty(evidence) ? "" : "(" + evidence + ")")}");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void _LogSummary(CrashAnalysisResult result)
|
|||
|
|
{
|
|||
|
|
if (!result.Any)
|
|||
|
|
{
|
|||
|
|
LogWrapper.Info("Crash", "步骤 3:分析崩溃原因完成,未找到可能的原因");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
LogWrapper.Info("Crash", $"步骤 3:分析崩溃原因完成,找到 {result.Findings.Count} 条可能的原因");
|
|||
|
|
foreach (var finding in result.Findings)
|
|||
|
|
_LogFinding(finding);
|
|||
|
|
}
|
|||
|
|
}
|