package jaci.openrio.toast.lib.crash;
import jaci.openrio.toast.core.Toast;
import jaci.openrio.toast.core.ToastBootstrap;
import jaci.openrio.toast.core.io.usb.MassStorageDevice;
import jaci.openrio.toast.core.io.usb.USBMassStorage;
import jaci.openrio.toast.lib.Assets;
import jaci.openrio.toast.lib.log.SplitStream;
import jaci.openrio.toast.lib.log.SysLogProxy;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Vector;
/**
* Handles crashes when the robot encounters an uncaught-exception. This simply adds details to the Logger
* and reports the StackTrace to a separate file under toast/crash/. Classes implementing
* {@link jaci.openrio.toast.lib.crash.CrashInfoProvider} are able to add custom data to the log
*
* @author Jaci
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
static Vector<CrashInfoProvider> providers;
static File crashDir;
static DateFormat dateFormat;
static CrashHandler instance;
/**
* Initialize the handler. This is handled by Toast.
*/
public static void init() {
try {
providers = new Vector<CrashInfoProvider>();
crashDir = new File(ToastBootstrap.toastHome, "crash");
crashDir.mkdirs();
} catch (Exception e) {}
dateFormat = new SimpleDateFormat("yyyy-MM-dd_hh-mm-ss");
instance = new CrashHandler();
Thread.setDefaultUncaughtExceptionHandler(instance);
Thread.currentThread().setUncaughtExceptionHandler(instance);
providers.add(new CrashInfoToast());
providers.add(new CrashInfoEnvironment());
providers.add(new CrashInfoModules());
}
/**
* Register a provider for the Crash Handler
*/
public static void registerProvider(CrashInfoProvider prov) {
providers.add(prov);
}
/**
* Handle an uncaught exception
*/
public static void handle(Throwable t) {
try {
String fn = "crash-" + dateFormat.format(new Date());
File file = new File(crashDir, fn + ".txt");
SplitStream split = new SplitStream(System.err, new FileOutputStream(file));
PrintStream out = new PrintStream(split);
out.println("**** CRASH LOG ****");
out.println("Your robot has crashed. Following is a crash log and more details.");
out.println("This log has been saved to: " + file.getCanonicalPath());
out.println("This log will also be duplicated to USB devices, with the filename: " + fn + ".txt");
out.println(Assets.getAscii("crash"));
for (CrashInfoProvider provider : providers) {
String info = provider.getCrashInfoPre(t);
if (info != null)
out.println("\t" + provider.getCrashInfoPre(t) + "\n");
}
t.printStackTrace(out);
out.println();
out.println("Crash Information: ");
for (CrashInfoProvider provider : providers) {
out.println("\t" + provider.getName() + ": ");
List<String> info = provider.getCrashInfo(t);
if (info != null)
for (String s : info) {
out.println("\t\t" + s);
}
out.println();
}
out.println();
out.println("*******************");
out.flush();
out.close();
File recentLog = SysLogProxy.recentOut;
File cpFile = new File(crashDir, fn + "-FULL.txt");
Files.copy(recentLog.toPath(), cpFile.toPath());
duplicate(cpFile);
duplicate(file);
Toast.getToast().shutdownCrash();
} catch (Exception e) {
}
}
/**
* Duplicates the given Crash File across all USB Mass Storage devices
*/
public static void duplicate(File f) {
String fn = f.getName();
boolean copied = false;
for (MassStorageDevice device : USBMassStorage.connectedDevices) {
File crash = new File(device.toast_directory, "crash");
crash.mkdirs();
copied = true;
try {
Files.copy(f.toPath(), new File(crash, fn).toPath());
} catch (IOException e) { }
}
if (copied) {
f.delete();
}
}
/**
* Handles an UncaughtException by logging the crash and shutting down the Robot safely. THis is
* automatically called by the JVM when an exception is uncaught on any thread.
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
handle(e);
}
}