Enhanced crash report interface
- CrashReports.report now throws an exception instead of invoking System.exit() - Reduced chance of deadlocks - Improved code readability - added CrashReports.crash to handle exceptions thrown by report() in top-level catch blocks
This commit is contained in:
parent
00773d4f8b
commit
bedc9fc729
@ -44,7 +44,7 @@ public class ProgressiaLauncher {
|
||||
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler((Thread thread, Throwable t)-> {
|
||||
CrashReports.report(t,"Uncaught exception in thread %s", thread.getName());
|
||||
CrashReports.crash(t, "Uncaught exception in thread %s", thread.getName());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ public class ClientProxy implements Proxy {
|
||||
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||
RenderTaskQueue.waitAndInvoke(() -> Typefaces.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz"))));
|
||||
} catch (InterruptedException e) {
|
||||
CrashReports.report(e, "ClientProxy failed");
|
||||
throw CrashReports.report(e, "ClientProxy failed");
|
||||
}
|
||||
|
||||
TestContent.registerContent();
|
||||
|
@ -35,7 +35,7 @@ public class DefaultClientCommsListener implements CommsListener {
|
||||
|
||||
@Override
|
||||
public void onIOError(IOException reason) {
|
||||
CrashReports.report(reason, "An IOException has occurred in communications");
|
||||
throw CrashReports.report(reason, "An IOException has occurred in communications");
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ public class Program implements OpenGLDeletable {
|
||||
glLinkProgram(handle);
|
||||
|
||||
if (glGetProgrami(handle, GL_LINK_STATUS) == GL_FALSE) {
|
||||
CrashReports.report(null, "Bad program:\n%s", glGetProgramInfoLog(handle));
|
||||
throw CrashReports.report(null, "Bad program:\n%s", glGetProgramInfoLog(handle));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ public class Shader implements OpenGLDeletable {
|
||||
if (glGetShaderi(handle, GL_COMPILE_STATUS) == GL_FALSE) {
|
||||
System.out.println("***************** ERROR ******************");
|
||||
System.out.println(source);
|
||||
CrashReports.report(null, "Bad shader:\n %s", glGetShaderInfoLog(handle));
|
||||
throw CrashReports.report(null, "Bad shader:\n %s", glGetShaderInfoLog(handle));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ public class Attribute {
|
||||
|
||||
public Attribute(int handle, Program program) {
|
||||
if (handle < 0) {
|
||||
CrashReports.report(null, "Bad handle: %d", handle);
|
||||
throw CrashReports.report(null, "Bad handle: %d", handle);
|
||||
}
|
||||
|
||||
this.handle = handle;
|
||||
|
@ -27,7 +27,7 @@ public class Uniform {
|
||||
|
||||
public Uniform(int handle, Program program) {
|
||||
if (handle < 0) {
|
||||
CrashReports.report(null, "Bad handle: %d", handle);
|
||||
throw CrashReports.report(null, "Bad handle: %d", handle);
|
||||
}
|
||||
|
||||
this.handle = handle;
|
||||
|
@ -57,8 +57,7 @@ public class GNUUnifontLoader {
|
||||
return createStream(reader).map(GNUUnifontLoader::parse).map(GNUUnifontLoader::addToAtlas)
|
||||
.collect(Collectors.collectingAndThen(createMapper(), GNUUnifont::new));
|
||||
} catch (IOException | UncheckedIOException e) {
|
||||
CrashReports.report(e, "Could not load GNUUnifont");
|
||||
return null;
|
||||
throw CrashReports.report(e, "Could not load GNUUnifont");
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,8 +92,7 @@ public class GNUUnifontLoader {
|
||||
return new ParsedGlyph(c, editor);
|
||||
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar);
|
||||
return null;
|
||||
throw CrashReports.report(e, "Could not load GNUUnifont: could not load character \"%s\"", declar);
|
||||
}
|
||||
}
|
||||
|
||||
|
10
src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java
Executable file → Normal file
10
src/main/java/ru/windcorp/progressia/client/graphics/gui/Component.java
Executable file → Normal file
@ -235,7 +235,7 @@ public class Component extends Named {
|
||||
|
||||
valid = true;
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not layout Component %s", this);
|
||||
throw CrashReports.report(e, "Could not layout Component %s", this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,7 +248,7 @@ public class Component extends Named {
|
||||
try {
|
||||
return getLayout().calculatePreferredSize(this);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not calculate preferred size for Component %s", this);
|
||||
throw CrashReports.report(e, "Could not calculate preferred size for Component %s", this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,7 +517,7 @@ public class Component extends Named {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not dispatch input to Component %s", this);
|
||||
throw CrashReports.report(e, "Could not dispatch input to Component %s", this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,7 +609,7 @@ public class Component extends Named {
|
||||
try {
|
||||
assembleSelf(target);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not assemble Component %s", this);
|
||||
throw CrashReports.report(e, "Could not assemble Component %s", this);
|
||||
}
|
||||
|
||||
assembleChildren(target);
|
||||
@ -617,7 +617,7 @@ public class Component extends Named {
|
||||
try {
|
||||
postAssembleSelf(target);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Post-assembly failed for Component %s", this);
|
||||
throw CrashReports.report(e, "Post-assembly failed for Component %s", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ public class Keys {
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
CrashReports.report(e, "Cannot access GLFW constants");
|
||||
throw CrashReports.report(e, "Cannot access GLFW constants");
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ public class Keys {
|
||||
}
|
||||
|
||||
if (CODES_TO_NAMES.containsKey(value)) {
|
||||
CrashReports.report(null, "Duplicate keys: %s and %s both map to %d(0x%s)",
|
||||
throw CrashReports.report(null, "Duplicate keys: %s and %s both map to %d(0x%s)",
|
||||
CODES_TO_NAMES.get(value), name, value, Integer.toHexString(value));
|
||||
}
|
||||
|
||||
|
@ -157,8 +157,7 @@ public class Atlases {
|
||||
|
||||
return loadSprite(data.getData(), group);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group);
|
||||
return null;
|
||||
throw CrashReports.report(e, "Could not load sprite %s into atlas group %s", resource, group);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +173,7 @@ public class Atlases {
|
||||
Atlas newAtlas = new Atlas(group);
|
||||
|
||||
if (!newAtlas.canAddSprite(data)) {
|
||||
CrashReports.report(null, "Could not fit texture into atlas of size %d", newAtlas.getSize());
|
||||
throw CrashReports.report(null, "Could not fit texture into atlas of size %d", newAtlas.getSize());
|
||||
}
|
||||
|
||||
atlases.add(newAtlas);
|
||||
|
@ -31,8 +31,7 @@ public class SimpleTextures {
|
||||
new Sprite(new TexturePrimitive(data.getData()))
|
||||
);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not load texture %s", resource);
|
||||
return null;
|
||||
throw CrashReports.report(e, "Could not load texture %s", resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ public class TexturePrimitive implements OpenGLDeletable {
|
||||
OpenGLObjectTracker.register(this, GL11::glDeleteTextures);
|
||||
|
||||
if (handle < 0) {
|
||||
CrashReports.report(null, "Failed to allocate texture");
|
||||
throw CrashReports.report(null, "Failed to allocate texture");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class Localizer {
|
||||
data = new Parser(langFolder + this.language + ".lang").parse();
|
||||
pokeListeners(language);
|
||||
} else {
|
||||
CrashReports.report(null, "Language not found: %s", language);
|
||||
throw CrashReports.report(null, "Language not found: %s", language);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,8 +81,7 @@ public class Parser {
|
||||
}
|
||||
|
||||
} catch (IOException | EscapeException e) {
|
||||
CrashReports.report(e, "Could not parse language file %s", filePath);
|
||||
return null;
|
||||
throw CrashReports.report(e, "Could not parse language file %s", filePath);
|
||||
}
|
||||
return parsedData;
|
||||
}
|
||||
|
@ -29,8 +29,7 @@ public class EntityRenderRegistry extends NamespacedInstanceRegistry<EntityRende
|
||||
).getData()
|
||||
);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not load entity texture %s", name);
|
||||
return null;
|
||||
throw CrashReports.report(e, "Could not load entity texture %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,7 +304,7 @@ public class Units {
|
||||
try {
|
||||
registerUnits(Units.class);
|
||||
} catch (IllegalAccessException e) {
|
||||
CrashReports.report(e, "Could not register units declared in {}", Units.class.getName());
|
||||
throw CrashReports.report(e, "Could not register units declared in {}", Units.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,11 +39,10 @@ public class ChunkIO {
|
||||
} catch (IOException | DecodingException e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
CrashReports.report(
|
||||
throw CrashReports.report(
|
||||
t, "Codec %s has failed to decode chunk (%d; %d; %d)",
|
||||
codec.getId(), position.x, position.y, position.z
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +57,7 @@ public class ChunkIO {
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
CrashReports.report(
|
||||
throw CrashReports.report(
|
||||
t, "Codec %s has failed to encode chunk (%d; %d; %d)",
|
||||
codec.getId(), chunk.getPosition().x, chunk.getPosition().y, chunk.getPosition().z
|
||||
);
|
||||
|
@ -51,8 +51,7 @@ public class Resource extends Named {
|
||||
try (Reader reader = getReader()) {
|
||||
return CharStreams.toString(reader);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not read resource %s as text", this);
|
||||
return null;
|
||||
throw CrashReports.report(e, "Could not read resource %s as text", this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,8 +60,7 @@ public class Resource extends Named {
|
||||
try (InputStream stream = getInputStream()) {
|
||||
byteArray = ByteStreams.toByteArray(stream);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not read resource %s as bytes", this);
|
||||
return null;
|
||||
throw CrashReports.report(e, "Could not read resource %s as bytes", this);
|
||||
}
|
||||
|
||||
if (output == null || output.remaining() < byteArray.length) {
|
||||
|
@ -30,17 +30,33 @@ public class CrashReports {
|
||||
private static final Collection<Analyzer> ANALYZERS = Collections.synchronizedCollection(new ArrayList<>());
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("crash");
|
||||
|
||||
public static ReportedException report(Throwable throwable, String messageFormat, Object... args) {
|
||||
if (throwable instanceof ReportedException) return (ReportedException) throwable;
|
||||
|
||||
return new ReportedException(throwable, messageFormat, args);
|
||||
}
|
||||
|
||||
public static RuntimeException crash(Throwable throwable, String messageFormat, Object... args) {
|
||||
if (throwable instanceof ReportedException) {
|
||||
throw crash((ReportedException) throwable);
|
||||
} else {
|
||||
throw crash(report(throwable, messageFormat, args));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <em>This method never returns.</em>
|
||||
* <p>
|
||||
* TODO document
|
||||
*
|
||||
* @param throwable
|
||||
* @param messageFormat
|
||||
* @param args
|
||||
* @param reportException
|
||||
*/
|
||||
public static void report(Throwable throwable, String messageFormat, Object... args) {
|
||||
public static RuntimeException crash(ReportedException reportException) {
|
||||
Throwable throwable = reportException.getCause();
|
||||
String messageFormat = reportException.getMessageFormat();
|
||||
Object[] args = reportException.getArgs();
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
try {
|
||||
@ -76,6 +92,7 @@ public class CrashReports {
|
||||
export(output.toString());
|
||||
|
||||
System.exit(0);
|
||||
return reportException;
|
||||
}
|
||||
|
||||
private static void appendContextProviders(StringBuilder output) {
|
||||
@ -233,6 +250,27 @@ public class CrashReports {
|
||||
private static void addSeparator(StringBuilder sb) {
|
||||
sb.append(StringUtil.sequence('-', 80)).append("\n");
|
||||
}
|
||||
|
||||
public static class ReportedException extends RuntimeException {
|
||||
|
||||
private String messageFormat;
|
||||
private Object[] args;
|
||||
|
||||
public ReportedException(Throwable throwable, String messageFormat, Object... args){
|
||||
super(throwable);
|
||||
this.messageFormat = messageFormat;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public String getMessageFormat() {
|
||||
return messageFormat;
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class StringUtilsTemp {
|
||||
@ -254,4 +292,4 @@ class StringUtilsTemp {
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@ public class PacketSendChunk extends PacketChunkChange {
|
||||
try {
|
||||
world.addChunk(ChunkIO.load(world, position, data.getInputStream()));
|
||||
} catch (DecodingException | IOException e) {
|
||||
CrashReports.report(e, "Could not load chunk");
|
||||
throw CrashReports.report(e, "Could not load chunk");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ public class PacketEntityChange extends PacketWorldChange {
|
||||
try {
|
||||
entity.write(this.buffer.getWriter(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Entity could not be written");
|
||||
throw CrashReports.report(e, "Entity could not be written");
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,13 +71,13 @@ public class PacketEntityChange extends PacketWorldChange {
|
||||
EntityData entity = world.getEntity(getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
CrashReports.report(null, "Entity with ID %d not found", getEntityId());
|
||||
throw CrashReports.report(null, "Entity with ID %d not found", getEntityId());
|
||||
}
|
||||
|
||||
try {
|
||||
entity.read(getReader(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Entity could not be read");
|
||||
throw CrashReports.report(e, "Entity could not be read");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ public class PacketSendEntity extends PacketWorldChange {
|
||||
try {
|
||||
entity.write(this.buffer.getWriter(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not write an entity into an internal buffer");
|
||||
throw CrashReports.report(e, "Could not write an entity into an internal buffer");
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ public class PacketSendEntity extends PacketWorldChange {
|
||||
try {
|
||||
entity.read(this.buffer.getReader(), IOContext.COMMS);
|
||||
} catch (IOException e) {
|
||||
CrashReports.report(e, "Could not read an entity from an internal buffer");
|
||||
throw CrashReports.report(e, "Could not read an entity from an internal buffer");
|
||||
}
|
||||
|
||||
world.addEntity(entity);
|
||||
|
@ -44,8 +44,7 @@ public class PlayerManager {
|
||||
EntityData entity = spawnPlayerEntity(login);
|
||||
return entity;
|
||||
} else {
|
||||
CrashReports.report(null, "Unknown login %s, javahorse stupid", login);
|
||||
return null;
|
||||
throw CrashReports.report(null, "Unknown login %s, javahorse stupid", login);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ public class ServerThread implements Runnable {
|
||||
server.tick();
|
||||
ticker.runOneTick();
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Got an exception in the server thread");
|
||||
throw CrashReports.crash(e, "Got an exception in the server thread");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ public class TickAndUpdateUtil {
|
||||
try {
|
||||
block.tick(context);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not tick block {}", block);
|
||||
throw CrashReports.report(e, "Could not tick block {}", block);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public class TickAndUpdateUtil {
|
||||
try {
|
||||
tile.tick(context);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not tick tile {}", tile);
|
||||
throw CrashReports.report(e, "Could not tick tile {}", tile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ public class TickAndUpdateUtil {
|
||||
try {
|
||||
block.update(context);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not update block {}", block);
|
||||
throw CrashReports.report(e, "Could not update block {}", block);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ public class TickAndUpdateUtil {
|
||||
try {
|
||||
tile.update(context);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not update tile {}", tile);
|
||||
throw CrashReports.report(e, "Could not update tile {}", tile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ public class TickAndUpdateUtil {
|
||||
try {
|
||||
logic.tick(data, context);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not tick entity {}", logic);
|
||||
throw CrashReports.report(e, "Could not tick entity {}", logic);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ class Ticker {
|
||||
try {
|
||||
task.run(srv);
|
||||
} catch (Exception e) {
|
||||
CrashReports.report(e, "Could not run %s task %s", task.getClass().getSimpleName(), task);
|
||||
throw CrashReports.report(e, "Could not run %s task %s", task.getClass().getSimpleName(), task);
|
||||
}
|
||||
|
||||
tasksCompleted++;
|
||||
|
@ -260,8 +260,8 @@ public class TickerCoordinator {
|
||||
if (t instanceof ConcurrentModificationException) {
|
||||
logger.debug("javahorse kill urself");
|
||||
}
|
||||
|
||||
CrashReports.report(
|
||||
|
||||
throw CrashReports.crash(
|
||||
t,
|
||||
"Something has gone horribly wrong in server ticker code "
|
||||
+ "(thread %s) and it is (probably) not related to mods or devils.",
|
||||
|
Reference in New Issue
Block a user