minecraft-src/net/minecraft/CrashReport.java
2025-07-04 01:41:11 +03:00

261 lines
7.8 KiB
Java

package net.minecraft;
import com.google.common.collect.Lists;
import com.mojang.logging.LogUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletionException;
import net.minecraft.util.MemoryReserve;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
public class CrashReport {
private static final Logger LOGGER = LogUtils.getLogger();
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ROOT);
private final String title;
private final Throwable exception;
private final List<CrashReportCategory> details = Lists.<CrashReportCategory>newArrayList();
@Nullable
private Path saveFile;
private boolean trackingStackTrace = true;
private StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0];
private final SystemReport systemReport = new SystemReport();
public CrashReport(String title, Throwable exception) {
this.title = title;
this.exception = exception;
}
/**
* Returns the description of the Crash Report.
*/
public String getTitle() {
return this.title;
}
/**
* Returns the Throwable object that is the cause for the crash and Crash Report.
*/
public Throwable getException() {
return this.exception;
}
public String getDetails() {
StringBuilder stringBuilder = new StringBuilder();
this.getDetails(stringBuilder);
return stringBuilder.toString();
}
/**
* Gets the various sections of the crash report into the given StringBuilder
*/
public void getDetails(StringBuilder builder) {
if ((this.uncategorizedStackTrace == null || this.uncategorizedStackTrace.length <= 0) && !this.details.isEmpty()) {
this.uncategorizedStackTrace = ArrayUtils.subarray(((CrashReportCategory)this.details.get(0)).getStacktrace(), 0, 1);
}
if (this.uncategorizedStackTrace != null && this.uncategorizedStackTrace.length > 0) {
builder.append("-- Head --\n");
builder.append("Thread: ").append(Thread.currentThread().getName()).append("\n");
builder.append("Stacktrace:\n");
for (StackTraceElement stackTraceElement : this.uncategorizedStackTrace) {
builder.append("\t").append("at ").append(stackTraceElement);
builder.append("\n");
}
builder.append("\n");
}
for (CrashReportCategory crashReportCategory : this.details) {
crashReportCategory.getDetails(builder);
builder.append("\n\n");
}
this.systemReport.appendToCrashReportString(builder);
}
/**
* Gets the stack trace of the Throwable that caused this crash report, or if that fails, the cause {@code .toString()}.
*/
public String getExceptionMessage() {
StringWriter stringWriter = null;
PrintWriter printWriter = null;
Throwable throwable = this.exception;
if (throwable.getMessage() == null) {
if (throwable instanceof NullPointerException) {
throwable = new NullPointerException(this.title);
} else if (throwable instanceof StackOverflowError) {
throwable = new StackOverflowError(this.title);
} else if (throwable instanceof OutOfMemoryError) {
throwable = new OutOfMemoryError(this.title);
}
throwable.setStackTrace(this.exception.getStackTrace());
}
String var4;
try {
stringWriter = new StringWriter();
printWriter = new PrintWriter(stringWriter);
throwable.printStackTrace(printWriter);
var4 = stringWriter.toString();
} finally {
IOUtils.closeQuietly(stringWriter);
IOUtils.closeQuietly(printWriter);
}
return var4;
}
public String getFriendlyReport(ReportType type, List<String> links) {
StringBuilder stringBuilder = new StringBuilder();
type.appendHeader(stringBuilder, links);
stringBuilder.append("Time: ");
stringBuilder.append(DATE_TIME_FORMATTER.format(ZonedDateTime.now()));
stringBuilder.append("\n");
stringBuilder.append("Description: ");
stringBuilder.append(this.title);
stringBuilder.append("\n\n");
stringBuilder.append(this.getExceptionMessage());
stringBuilder.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n");
for (int i = 0; i < 87; i++) {
stringBuilder.append("-");
}
stringBuilder.append("\n\n");
this.getDetails(stringBuilder);
return stringBuilder.toString();
}
public String getFriendlyReport(ReportType type) {
return this.getFriendlyReport(type, List.of());
}
@Nullable
public Path getSaveFile() {
return this.saveFile;
}
public boolean saveToFile(Path path, ReportType type, List<String> links) {
if (this.saveFile != null) {
return false;
} else {
try {
if (path.getParent() != null) {
FileUtil.createDirectoriesSafe(path.getParent());
}
Writer writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8);
try {
writer.write(this.getFriendlyReport(type, links));
} catch (Throwable var8) {
if (writer != null) {
try {
writer.close();
} catch (Throwable var7) {
var8.addSuppressed(var7);
}
}
throw var8;
}
if (writer != null) {
writer.close();
}
this.saveFile = path;
return true;
} catch (Throwable var9) {
LOGGER.error("Could not save crash report to {}", path, var9);
return false;
}
}
}
public boolean saveToFile(Path path, ReportType type) {
return this.saveToFile(path, type, List.of());
}
public SystemReport getSystemReport() {
return this.systemReport;
}
/**
* Creates a CrashReportCategory
*/
public CrashReportCategory addCategory(String name) {
return this.addCategory(name, 1);
}
/**
* Creates a CrashReportCategory for the given stack trace depth
*/
public CrashReportCategory addCategory(String categoryName, int stacktraceLength) {
CrashReportCategory crashReportCategory = new CrashReportCategory(categoryName);
if (this.trackingStackTrace) {
int i = crashReportCategory.fillInStackTrace(stacktraceLength);
StackTraceElement[] stackTraceElements = this.exception.getStackTrace();
StackTraceElement stackTraceElement = null;
StackTraceElement stackTraceElement2 = null;
int j = stackTraceElements.length - i;
if (j < 0) {
LOGGER.error("Negative index in crash report handler ({}/{})", stackTraceElements.length, i);
}
if (stackTraceElements != null && 0 <= j && j < stackTraceElements.length) {
stackTraceElement = stackTraceElements[j];
if (stackTraceElements.length + 1 - i < stackTraceElements.length) {
stackTraceElement2 = stackTraceElements[stackTraceElements.length + 1 - i];
}
}
this.trackingStackTrace = crashReportCategory.validateStackTrace(stackTraceElement, stackTraceElement2);
if (stackTraceElements != null && stackTraceElements.length >= i && 0 <= j && j < stackTraceElements.length) {
this.uncategorizedStackTrace = new StackTraceElement[j];
System.arraycopy(stackTraceElements, 0, this.uncategorizedStackTrace, 0, this.uncategorizedStackTrace.length);
} else {
this.trackingStackTrace = false;
}
}
this.details.add(crashReportCategory);
return crashReportCategory;
}
/**
* Creates a crash report for the exception
*/
public static CrashReport forThrowable(Throwable cause, String description) {
while (cause instanceof CompletionException && cause.getCause() != null) {
cause = cause.getCause();
}
CrashReport crashReport;
if (cause instanceof ReportedException reportedException) {
crashReport = reportedException.getReport();
} else {
crashReport = new CrashReport(description, cause);
}
return crashReport;
}
public static void preload() {
MemoryReserve.allocate();
new CrashReport("Don't panic!", new Throwable()).getFriendlyReport(ReportType.CRASH);
}
}