001package net.minecraft.crash; 002 003import cpw.mods.fml.common.FMLCommonHandler; 004import cpw.mods.fml.relauncher.Side; 005import cpw.mods.fml.relauncher.SideOnly; 006import java.io.File; 007import java.io.FileWriter; 008import java.io.IOException; 009import java.io.PrintWriter; 010import java.io.StringWriter; 011import java.text.SimpleDateFormat; 012import java.util.ArrayList; 013import java.util.Date; 014import java.util.Iterator; 015import java.util.List; 016import net.minecraft.logging.ILogAgent; 017import net.minecraft.util.ReportedException; 018 019public class CrashReport 020{ 021 /** Description of the crash report. */ 022 private final String description; 023 024 /** The Throwable that is the "cause" for this crash and Crash Report. */ 025 private final Throwable cause; 026 private final CrashReportCategory field_85061_c = new CrashReportCategory(this, "System Details"); 027 028 /** Holds the keys and values of all crash report sections. */ 029 private final List crashReportSections = new ArrayList(); 030 031 /** File of crash report. */ 032 private File crashReportFile = null; 033 private boolean field_85059_f = true; 034 private StackTraceElement[] field_85060_g = new StackTraceElement[0]; 035 036 public CrashReport(String par1Str, Throwable par2Throwable) 037 { 038 this.description = par1Str; 039 this.cause = par2Throwable; 040 this.populateEnvironment(); 041 } 042 043 /** 044 * Populates this crash report with initial information about the running server and operating system / java 045 * environment 046 */ 047 private void populateEnvironment() 048 { 049 this.field_85061_c.addCrashSectionCallable("Minecraft Version", new CallableMinecraftVersion(this)); 050 this.field_85061_c.addCrashSectionCallable("Operating System", new CallableOSInfo(this)); 051 this.field_85061_c.addCrashSectionCallable("Java Version", new CallableJavaInfo(this)); 052 this.field_85061_c.addCrashSectionCallable("Java VM Version", new CallableJavaInfo2(this)); 053 this.field_85061_c.addCrashSectionCallable("Memory", new CallableMemoryInfo(this)); 054 this.field_85061_c.addCrashSectionCallable("JVM Flags", new CallableJVMFlags(this)); 055 this.field_85061_c.addCrashSectionCallable("AABB Pool Size", new CallableCrashMemoryReport(this)); 056 this.field_85061_c.addCrashSectionCallable("Suspicious classes", new CallableSuspiciousClasses(this)); 057 this.field_85061_c.addCrashSectionCallable("IntCache", new CallableIntCache(this)); 058 FMLCommonHandler.instance().enhanceCrashReport(this, this.field_85061_c); 059 } 060 061 /** 062 * Returns the description of the Crash Report. 063 */ 064 public String getDescription() 065 { 066 return this.description; 067 } 068 069 /** 070 * Returns the Throwable object that is the cause for the crash and Crash Report. 071 */ 072 public Throwable getCrashCause() 073 { 074 return this.cause; 075 } 076 077 @SideOnly(Side.CLIENT) 078 public String func_90021_c() 079 { 080 StringBuilder stringbuilder = new StringBuilder(); 081 this.getSectionsInStringBuilder(stringbuilder); 082 return stringbuilder.toString(); 083 } 084 085 /** 086 * Gets the various sections of the crash report into the given StringBuilder 087 */ 088 public void getSectionsInStringBuilder(StringBuilder par1StringBuilder) 089 { 090 if (this.field_85060_g != null && this.field_85060_g.length > 0) 091 { 092 par1StringBuilder.append("-- Head --\n"); 093 par1StringBuilder.append("Stacktrace:\n"); 094 StackTraceElement[] astacktraceelement = this.field_85060_g; 095 int i = astacktraceelement.length; 096 097 for (int j = 0; j < i; ++j) 098 { 099 StackTraceElement stacktraceelement = astacktraceelement[j]; 100 par1StringBuilder.append("\t").append("at ").append(stacktraceelement.toString()); 101 par1StringBuilder.append("\n"); 102 } 103 104 par1StringBuilder.append("\n"); 105 } 106 107 Iterator iterator = this.crashReportSections.iterator(); 108 109 while (iterator.hasNext()) 110 { 111 CrashReportCategory crashreportcategory = (CrashReportCategory)iterator.next(); 112 crashreportcategory.func_85072_a(par1StringBuilder); 113 par1StringBuilder.append("\n\n"); 114 } 115 116 this.field_85061_c.func_85072_a(par1StringBuilder); 117 } 118 119 /** 120 * Gets the stack trace of the Throwable that caused this crash report, or if that fails, the cause .toString(). 121 */ 122 public String getCauseStackTraceOrString() 123 { 124 StringWriter stringwriter = null; 125 PrintWriter printwriter = null; 126 String s = this.cause.toString(); 127 128 try 129 { 130 stringwriter = new StringWriter(); 131 printwriter = new PrintWriter(stringwriter); 132 this.cause.printStackTrace(printwriter); 133 s = stringwriter.toString(); 134 } 135 finally 136 { 137 try 138 { 139 if (stringwriter != null) 140 { 141 stringwriter.close(); 142 } 143 144 if (printwriter != null) 145 { 146 printwriter.close(); 147 } 148 } 149 catch (IOException ioexception) 150 { 151 ; 152 } 153 } 154 155 return s; 156 } 157 158 /** 159 * Gets the complete report with headers, stack trace, and different sections as a string. 160 */ 161 public String getCompleteReport() 162 { 163 StringBuilder stringbuilder = new StringBuilder(); 164 stringbuilder.append("---- Minecraft Crash Report ----\n"); 165 stringbuilder.append("// "); 166 stringbuilder.append(getWittyComment()); 167 stringbuilder.append("\n\n"); 168 stringbuilder.append("Time: "); 169 stringbuilder.append((new SimpleDateFormat()).format(new Date())); 170 stringbuilder.append("\n"); 171 stringbuilder.append("Description: "); 172 stringbuilder.append(this.description); 173 stringbuilder.append("\n\n"); 174 stringbuilder.append(this.getCauseStackTraceOrString()); 175 stringbuilder.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n"); 176 177 for (int i = 0; i < 87; ++i) 178 { 179 stringbuilder.append("-"); 180 } 181 182 stringbuilder.append("\n\n"); 183 this.getSectionsInStringBuilder(stringbuilder); 184 return stringbuilder.toString(); 185 } 186 187 @SideOnly(Side.CLIENT) 188 189 /** 190 * Gets the file this crash report is saved into. 191 */ 192 public File getFile() 193 { 194 return this.crashReportFile; 195 } 196 197 /** 198 * Saves the complete crash report to the given File. 199 */ 200 public boolean saveToFile(File par1File, ILogAgent par2ILogAgent) 201 { 202 if (this.crashReportFile != null) 203 { 204 return false; 205 } 206 else 207 { 208 if (par1File.getParentFile() != null) 209 { 210 par1File.getParentFile().mkdirs(); 211 } 212 213 try 214 { 215 FileWriter filewriter = new FileWriter(par1File); 216 filewriter.write(this.getCompleteReport()); 217 filewriter.close(); 218 this.crashReportFile = par1File; 219 return true; 220 } 221 catch (Throwable throwable) 222 { 223 par2ILogAgent.logSevereException("Could not save crash report to " + par1File, throwable); 224 return false; 225 } 226 } 227 } 228 229 public CrashReportCategory func_85056_g() 230 { 231 return this.field_85061_c; 232 } 233 234 /** 235 * Creates a CrashReportCategory 236 */ 237 public CrashReportCategory makeCategory(String par1Str) 238 { 239 return this.makeCategoryDepth(par1Str, 1); 240 } 241 242 /** 243 * Creates a CrashReportCategory for the given stack trace depth 244 */ 245 public CrashReportCategory makeCategoryDepth(String par1Str, int par2) 246 { 247 CrashReportCategory crashreportcategory = new CrashReportCategory(this, par1Str); 248 249 if (this.field_85059_f) 250 { 251 int j = crashreportcategory.func_85073_a(par2); 252 StackTraceElement[] astacktraceelement = this.cause.getStackTrace(); 253 StackTraceElement stacktraceelement = null; 254 StackTraceElement stacktraceelement1 = null; 255 256 if (astacktraceelement != null && astacktraceelement.length - j < astacktraceelement.length) 257 { 258 stacktraceelement = astacktraceelement[astacktraceelement.length - j]; 259 260 if (astacktraceelement.length + 1 - j < astacktraceelement.length) 261 { 262 stacktraceelement1 = astacktraceelement[astacktraceelement.length + 1 - j]; 263 } 264 } 265 266 this.field_85059_f = crashreportcategory.func_85069_a(stacktraceelement, stacktraceelement1); 267 268 if (j > 0 && !this.crashReportSections.isEmpty()) 269 { 270 CrashReportCategory crashreportcategory1 = (CrashReportCategory)this.crashReportSections.get(this.crashReportSections.size() - 1); 271 crashreportcategory1.func_85070_b(j); 272 } 273 else if (astacktraceelement != null && astacktraceelement.length >= j) 274 { 275 this.field_85060_g = new StackTraceElement[astacktraceelement.length - j]; 276 System.arraycopy(astacktraceelement, 0, this.field_85060_g, 0, this.field_85060_g.length); 277 } 278 else 279 { 280 this.field_85059_f = false; 281 } 282 } 283 284 this.crashReportSections.add(crashreportcategory); 285 return crashreportcategory; 286 } 287 288 /** 289 * Gets a random witty comment for inclusion in this CrashReport 290 */ 291 private static String getWittyComment() 292 { 293 String[] astring = new String[] {"Who set us up the TNT?", "Everything\'s going to plan. No, really, that was supposed to happen.", "Uh... Did I do that?", "Oops.", "Why did you do that?", "I feel sad now :(", "My bad.", "I\'m sorry, Dave.", "I let you down. Sorry :(", "On the bright side, I bought you a teddy bear!", "Daisy, daisy...", "Oh - I know what I did wrong!", "Hey, that tickles! Hehehe!", "I blame Dinnerbone.", "You should try our sister game, Minceraft!", "Don\'t be sad. I\'ll do better next time, I promise!", "Don\'t be sad, have a hug! <3", "I just don\'t know what went wrong :(", "Shall we play a game?", "Quite honestly, I wouldn\'t worry myself about that.", "I bet Cylons wouldn\'t have this problem.", "Sorry :(", "Surprise! Haha. Well, this is awkward.", "Would you like a cupcake?", "Hi. I\'m Minecraft, and I\'m a crashaholic.", "Ooh. Shiny.", "This doesn\'t make any sense!", "Why is it breaking :(", "Don\'t do that.", "Ouch. That hurt :(", "You\'re mean.", "This is a token for 1 free hug. Redeem at your nearest Mojangsta: [~~HUG~~]", "There are four lights!"}; 294 295 try 296 { 297 return astring[(int)(System.nanoTime() % (long)astring.length)]; 298 } 299 catch (Throwable throwable) 300 { 301 return "Witty comment unavailable :("; 302 } 303 } 304 305 /** 306 * Creates a crash report for the exception 307 */ 308 public static CrashReport makeCrashReport(Throwable par0Throwable, String par1Str) 309 { 310 CrashReport crashreport; 311 312 if (par0Throwable instanceof ReportedException) 313 { 314 crashreport = ((ReportedException)par0Throwable).getCrashReport(); 315 } 316 else 317 { 318 crashreport = new CrashReport(par1Str, par0Throwable); 319 } 320 321 return crashreport; 322 } 323}