Skip to content

Commit

Permalink
Add analyze some crash automaticly
Browse files Browse the repository at this point in the history
  • Loading branch information
xfl03 committed Aug 20, 2019
1 parent 5862650 commit c960745
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 13 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Display more info in crash report.
## Why make it?
Forge 1.13.2 - 1.14.3 removed mod list and coremod list in crash report, while users need them to analyze what caused crash.
Forge 1.14.4 added mod list back, but it is not formatted. It's hard to read when plenty of mods loaded. Meanwhile, coremods list is still not shown.
Mod list added in Forge 1.14.4 crash report can be shown below:
Mod list added in Forge 1.14.4 crash report:
```
Mod List:
CustomSkinLoader_Forge-14.11-SNAPSHOT-89.jar CustomSkinLoader {customskinloader@14.11-SNAPSHOT-89 DONE}
Expand All @@ -13,7 +13,7 @@ Mod List:
## What added?
This mod is designed to take mod list and coremod list back with pretty printing.
Feel free to open an issue if other info needed.
What added in crash report is shown below:
What added in crash report:
```
Forge Mods:
| ID | Name | Version | Source | Status |
Expand All @@ -28,3 +28,20 @@ Forge CoreMods:
| customskinloader | transformers | transformers.js | Loaded |
| forge | fieldtomethodtransformers | fieldtomethodtransformers.js | Loaded |
```
## What's more?
We are trying to analyze some crash automaticly.
Now, we can help you to solve `java.lang.VerifyError`.
You can open an issue to submit crash report, I will try my best to find the way to solve.
What added for some crash:
```
Possible Reason:
Bytecode in class 'me.xfl03.morecrashinfo.util.CrashMaker' failed to verify.
CoreMod 'MoreCrashInfo' modified that class, which may cause crash.
Possible Solution:
Please remove or update 'MoreCrashInfo(MoreCrashInfo-1.0.3.jar)' and try again.
Error Info:
Class: me.xfl03.morecrashinfo.util.CrashMaker
Owner: MoreCrashInfo
Audit: xf:fml:morecrashinfo:CrashMakerTransformer
```
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ buildscript {
}
apply plugin: 'net.minecraftforge.gradle'

version = '1.0.2'
version = '1.0.3'
group = 'me.xfl03'
archivesBaseName = 'MoreCrashInfo'

Expand Down Expand Up @@ -47,7 +47,7 @@ minecraft {
workingDirectory project.file('run')
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
property 'forge.logging.console.level', 'debug'
args '--mod', 'examplemod', '--all', '--output', file('src/generated/resources/')
args '--mod', 'morecrashinfo', '--all', '--output', file('src/generated/resources/')

mods {
examplemod {
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/me/xfl03/morecrashinfo/MoreCrashInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

import me.xfl03.morecrashinfo.crash.CoreModList;
import me.xfl03.morecrashinfo.crash.ModList;
import me.xfl03.morecrashinfo.handler.CrashHandler;
import me.xfl03.morecrashinfo.handler.exception.VerifyErrorHandler;
import me.xfl03.morecrashinfo.util.CrashMaker;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.CrashReportExtender;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand All @@ -16,6 +22,7 @@ public class MoreCrashInfo

public MoreCrashInfo() {
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
MinecraftForge.EVENT_BUS.register(this);
}

private void setup(final FMLCommonSetupEvent event)
Expand All @@ -25,6 +32,17 @@ private void setup(final FMLCommonSetupEvent event)
CrashReportExtender.registerCrashCallable(new ModList());
CrashReportExtender.registerCrashCallable(new CoreModList());

CrashHandler.registerHandler(VerifyError.class, VerifyErrorHandler::new);

LOGGER.debug("MoreCrashInfo: PreInit End.");
}

@SubscribeEvent
public void onServerStarting(FMLServerStartingEvent event) {
// Uncomment to cause java.lang.RuntimeException
//CrashMaker.makeCrash();

// Uncomment in crashtransformers.js can cause special crash
CrashMaker.doNothing();
}
}
34 changes: 34 additions & 0 deletions src/main/java/me/xfl03/morecrashinfo/handler/CrashHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package me.xfl03.morecrashinfo.handler;

import cpw.mods.modlauncher.log.TransformingThrowablePatternConverter;
import me.xfl03.morecrashinfo.handler.exception.*;
import net.minecraft.crash.CrashReport;

import java.util.*;
import java.util.function.Function;

public class CrashHandler {

private static Map<Class, Function<Throwable, ExceptionHandler>> handlers = new HashMap<>();
private static ExceptionHandler handler;

public static void registerHandler(Class exception, Function<Throwable, ExceptionHandler> handler) {
handlers.put(exception, handler);
}

// net.minecraftforge.fml.CrashReportExtender.addCrashReportHeader
public static void addCrashReportHeader(StringBuilder stringbuilder, CrashReport crashReport) {
Throwable cause = crashReport.getCrashCause();
handler = Optional.ofNullable(handlers.get(cause.getClass()))
.orElse(ExceptionHandler::new).apply(cause);
handler.handleHeader(stringbuilder);
}

// net.minecraftforge.fml.CrashReportExtender.generateEnhancedStackTrace
public static String generateEnhancedStackTrace(final Throwable throwable) {
StringBuilder stringbuilder = new StringBuilder();
handler.handleException(stringbuilder);
stringbuilder.append(TransformingThrowablePatternConverter.generateEnhancedStackTrace(throwable));
return stringbuilder.toString();
}
}
15 changes: 15 additions & 0 deletions src/main/java/me/xfl03/morecrashinfo/handler/ExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package me.xfl03.morecrashinfo.handler;

public class ExceptionHandler {
protected Throwable cause;

public ExceptionHandler(Throwable cause) {
this.cause = cause;
}

public void handleHeader(StringBuilder sb) {
}

public void handleException(StringBuilder sb) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package me.xfl03.morecrashinfo.handler.exception;

import me.xfl03.morecrashinfo.handler.ExceptionHandler;
import me.xfl03.morecrashinfo.util.ModHelper;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.forgespi.language.IModInfo;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class VerifyErrorHandler extends ExceptionHandler {
private String className;
private List<? extends ModContainer> transformers;
private Optional<? extends ModContainer> owner;

public VerifyErrorHandler(Throwable cause) {
super(cause);
className = getClass(cause.getMessage());
transformers = ModHelper.getTransformers(className);
owner = ModHelper.getModByClass(className);
}

@Override
public void handleHeader(StringBuilder sb) {
sb.append("Possible Reason:\n\tBytecode in class '").append(className).append("' failed to verify. ")
.append(getReason()).append("\n\n");
}

private String getReason() {
if (!transformers.isEmpty()) {

return String.format(
"\n\tCoreMod '%s' modified that class, which may cause crash.\n" +
"Possible Solution:\n\tPlease remove or update %s'%s' and try again.",
transformers.stream().map(it -> it.getModInfo().getDisplayName())
.collect(Collectors.joining(",")),
transformers.size() > 1 ? "one of " : "",
transformers.stream().map(it -> it.getModInfo().getDisplayName() +
"(" + ModHelper.getSource(it.getModInfo()) + ")")
.collect(Collectors.joining(","))
);
} else if (owner.isPresent()) {
IModInfo info = owner.get().getModInfo();
return String.format(
"\n\tMod '%s' might has been broken.\n" +
"Possible Solution:\n\tPlease remove or update '%s' and try again.",
info.getDisplayName(),
info.getDisplayName() + "(" + ModHelper.getSource(info) + ")"
);
}
return "";
}

@Override
public void handleException(StringBuilder sb) {
sb.append("Error Info:\n\tClass: ").append(className)
.append("\n\tOwner: ").append(owner.map(it -> it.getModInfo().getDisplayName()).orElse("Unknown"))
.append("\n\tAudit: ").append(ModHelper.getAuditLine(className)).append("\n\n");
}

private String getClass(String message) {
String[] t = message.split("Location:");
if (t.length < 2) return null;
t = t[1].split("\\.");
return t[0].trim().replace('/', '.');
}
}
11 changes: 11 additions & 0 deletions src/main/java/me/xfl03/morecrashinfo/util/CrashMaker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package me.xfl03.morecrashinfo.util;

public class CrashMaker {
public static void makeCrash() {
throw new RuntimeException(new RuntimeException(new RuntimeException("Crashed for test.")));
}

public static void doNothing() {

}
}
49 changes: 43 additions & 6 deletions src/main/java/me/xfl03/morecrashinfo/util/ModHelper.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package me.xfl03.morecrashinfo.util;

import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.IEnvironment;
import net.minecraftforge.coremod.CoreMod;
import net.minecraftforge.coremod.CoreModEngine;
import net.minecraftforge.coremod.CoreModProvider;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.moddiscovery.CoreModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.forgespi.coremod.ICoreModFile;
import net.minecraftforge.forgespi.coremod.ICoreModProvider;
import net.minecraftforge.forgespi.language.IModInfo;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

public class ModHelper {
public static List<CoreMod> getCoreModList() throws Exception {
Expand All @@ -27,6 +31,11 @@ public static List<CoreMod> getCoreModList() throws Exception {
return list;
}

public static String getSource(IModInfo it) {
if (it instanceof ModInfo) return getSource((ModInfo) it);
return "Not Found";
}

public static String getSource(ModInfo it) {
if (it.getOwningFile() == null || it.getOwningFile().getFile() == null) return "Not Found";
return it.getOwningFile().getFile().getFileName();
Expand All @@ -47,9 +56,37 @@ public static String getName(ICoreModFile file) {
return name;
}

public static Optional<? extends ModContainer> getModContainer(String modId) {
return ModList.get().getModContainerById(modId);
}

public static String getStatus(String modId) {
return ModList.get()
.getModContainerById(modId).map(ModContainer::getCurrentState).map(Objects::toString)
.orElse("NONE");
return getModContainer(modId)
.map(ModContainer::getCurrentState).map(Objects::toString).orElse("NONE");
}

public static String getAuditLine(String className) {
String name = className.replace('/', '.');
return Launcher.INSTANCE.environment().getProperty(IEnvironment.Keys.AUDITTRAIL.get())
.map(it -> it.getAuditString(name)).orElse("");
}

public static List<? extends ModContainer> getTransformers(String className) {
return Arrays.stream(
getAuditLine(className).split(","))
.filter(it -> it.startsWith("xf:"))
.map(it -> it.split(":"))
.filter(it -> it.length > 2)
.map(it -> it[1].equals("fml") ? it[2] : it[1])
.map(ModHelper::getModContainer)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}

public static Optional<? extends ModContainer> getModByClass(String className) {
String path = className.replace('.', '/') + ".class";
URL url = ModHelper.class.getClassLoader().getResource(path);
return url == null ? Optional.empty() : getModContainer(url.getHost());
}
}
3 changes: 3 additions & 0 deletions src/main/resources/META-INF/coremods.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"crashtransformers": "crashtransformers.js"
}
6 changes: 3 additions & 3 deletions src/main/resources/META-INF/mods.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ loaderVersion="[25,)"
issueTrackerURL="http://github.com/xfl03/MoreCrashInfo/issues"
[[mods]]
modId="morecrashinfo"
version="1.0.2"
version="1.0.3"
displayName="MoreCrashInfo"
displayURL="http://github.com/xfl03/MoreCrashInfo" #optional
displayURL="http://github.com/xfl03/MoreCrashInfo"
credits=""
authors="xfl03"
description='''
Expand All @@ -14,7 +14,7 @@ Display more info in crash report.
[[dependencies.morecrashinfo]]
modId="forge"
mandatory=true
versionRange="[25,)"
versionRange="[25.0.216,)"
ordering="NONE"
side="BOTH"
[[dependencies.morecrashinfo]]
Expand Down
52 changes: 52 additions & 0 deletions src/main/resources/crashtransformers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
var InsnNode = Java.type('org.objectweb.asm.tree.InsnNode');
var MethodInsnNode = Java.type('org.objectweb.asm.tree.MethodInsnNode');
var VarInsnNode = Java.type('org.objectweb.asm.tree.VarInsnNode');

var Opcodes = Java.type('org.objectweb.asm.Opcodes');

function initializeCoreMod() {
return {
'CrashReportExtenderTransformer': {
'target': {
'type': 'CLASS',
'name': 'net/minecraftforge/fml/CrashReportExtender'
},
'transformer': function (cn) {
cn.methods.forEach(function (mn) {
if (mn.name === 'addCrashReportHeader') {
mn.instructions.clear();
mn.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
mn.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
mn.instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"me/xfl03/morecrashinfo/handler/CrashHandler",
"addCrashReportHeader",
"(Ljava/lang/StringBuilder;Lnet/minecraft/crash/CrashReport;)V", false));
mn.instructions.add(new InsnNode(Opcodes.RETURN));
} else if (mn.name === 'generateEnhancedStackTrace') {
mn.instructions.clear();
mn.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
mn.instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"me/xfl03/morecrashinfo/handler/CrashHandler",
"generateEnhancedStackTrace",
"(Ljava/lang/Throwable;)Ljava/lang/String;", false));
mn.instructions.add(new InsnNode(Opcodes.ARETURN));
}
});
return cn;
}
},
'CrashMakerTransformer': {
'target': {
'type': 'CLASS',
'name': 'me/xfl03/morecrashinfo/util/CrashMaker'
},
'transformer': function (cn) {
// Uncomment to cause java.lang.ClassFormatError
//cn.methods.add(cn.methods.get(0));

//Uncomment to cause java.lang.VerifyError
//cn.methods.forEach(function (mn) { mn.instructions.clear(); mn.instructions.add(new InsnNode(Opcodes.ARETURN)); });
}
}
}
}

0 comments on commit c960745

Please sign in to comment.