261 lines
7.8 KiB
Java
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);
|
|
}
|
|
}
|