diff --git a/hockeysdk/src/androidTest/java/net/hockeyapp/android/CrashManagerTest.java b/hockeysdk/src/androidTest/java/net/hockeyapp/android/CrashManagerTest.java index e2ef3fc7a..aed29fbbf 100644 --- a/hockeysdk/src/androidTest/java/net/hockeyapp/android/CrashManagerTest.java +++ b/hockeysdk/src/androidTest/java/net/hockeyapp/android/CrashManagerTest.java @@ -12,6 +12,8 @@ import org.junit.runner.RunWith; import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.UUID; @@ -140,4 +142,28 @@ public void invalidStackTrace() throws Exception { verify(listener).onCrashesNotSent(); assertEquals(0, CrashManager.stackTracesCount); } + + @Test + public void largeStackTrace() throws Exception { + String stackTrace = "java.lang.OutOfMemoryError: Failed to allocate a 37657308 byte allocation with 16776928 free bytes and 27MB until OOM\n" + + "\tat java.lang.String.(String.java:400)\n" + + "\tat java.lang.AbstractStringBuilder.toString(AbstractStringBuilder.java:633)\n" + + "\tat java.lang.StringBuilder.toString(StringBuilder.java:663)\n" + + "\tat net.hockeyapp.android.CrashManager.contentsOfFile(CrashManager.java:772)\n" + + "\tat net.hockeyapp.android.CrashManager.submitStackTrace(CrashManager.java:379)\n" + + "\tat net.hockeyapp.android.CrashManager.access$500(CrashManager.java:47)\n" + + "\tat net.hockeyapp.android.CrashManager$8.doInBackground(CrashManager.java:647)\n" + + "\tat net.hockeyapp.android.CrashManager$8.doInBackground(CrashManager.java:639)\n"; + + String filename = UUID.randomUUID().toString() + ".stacktrace"; + File file = new File(filesDirectory, filename); + PrintWriter writer = new PrintWriter(file); + writer.print(stackTrace); + writer.flush(); + writer.close(); + + WeakReference weakContext = new WeakReference<>(InstrumentationRegistry.getTargetContext()); + String result = CrashManager.contentsOfFile(weakContext, filename, 500); + assertTrue(result.endsWith("submitStackTrace(CrashManager.java:379)\n")); + } } diff --git a/hockeysdk/src/main/java/net/hockeyapp/android/CrashManager.java b/hockeysdk/src/main/java/net/hockeyapp/android/CrashManager.java index cf7224da2..64a6b3dad 100644 --- a/hockeysdk/src/main/java/net/hockeyapp/android/CrashManager.java +++ b/hockeysdk/src/main/java/net/hockeyapp/android/CrashManager.java @@ -26,6 +26,8 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.ref.WeakReference; import java.net.HttpURLConnection; @@ -406,7 +408,7 @@ public static synchronized void submitStackTraces(final WeakReference w private static void submitStackTrace(final WeakReference weakContext, String filename, CrashManagerListener listener, CrashMetaData crashMetaData) { String stacktrace = null; try { - stacktrace = contentsOfFile(weakContext, filename); + stacktrace = contentsOfFile(weakContext, filename, HttpURLConnectionBuilder.FORM_FIELD_LIMIT); } catch (Exception e) { HockeyLog.error("Failed to read crash data", e); } @@ -424,14 +426,10 @@ private static void submitStackTrace(final WeakReference weakContext, S try { // Transmit stack trace with POST request HockeyLog.debug("Transmitting crash data: \n" + stacktrace); - if (stacktrace.length() > HttpURLConnectionBuilder.FORM_FIELD_LIMIT) { - HockeyLog.info("The stack trace is too large, truncate a bit"); - stacktrace = stacktrace.substring(0, stacktrace.lastIndexOf('\n', HttpURLConnectionBuilder.FORM_FIELD_LIMIT - 1) + 1); - } // Retrieve user ID and contact information if given - String userID = contentsOfFile(weakContext, filename.replace(".stacktrace", ".user")); - String contact = contentsOfFile(weakContext, filename.replace(".stacktrace", ".contact")); + String userID = contentsOfFile(weakContext, filename.replace(".stacktrace", ".user"), 0); + String contact = contentsOfFile(weakContext, filename.replace(".stacktrace", ".contact"), 0); if (crashMetaData != null) { final String crashMetaDataUserID = crashMetaData.getUserID(); @@ -445,7 +443,7 @@ private static void submitStackTrace(final WeakReference weakContext, S } // Append application log to user provided description if present, if not, just send application log - final String applicationLog = contentsOfFile(weakContext, filename.replace(".stacktrace", ".description")); + final String applicationLog = contentsOfFile(weakContext, filename.replace(".stacktrace", ".description"), 0); String description = crashMetaData != null ? crashMetaData.getUserDescription() : ""; if (!TextUtils.isEmpty(applicationLog)) { if (!TextUtils.isEmpty(description)) { @@ -794,21 +792,29 @@ private static void deleteStackTrace(final WeakReference weakContext, S /** * Returns the content of a file as a string. */ - private static String contentsOfFile(final WeakReference weakContext, String filename) { + static String contentsOfFile(final WeakReference weakContext, String filename, int maxLength) { Context context = weakContext != null ? weakContext.get() : null; if (context != null) { File file = context.getFileStreamPath(filename); if(file == null || !file.exists()) { return ""; } - StringBuilder contents = new StringBuilder(); + final StringWriter result = new StringWriter(); + final PrintWriter writer = new PrintWriter(result); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(context.openFileInput(filename))); String line; while ((line = reader.readLine()) != null) { - contents.append(line); - contents.append(System.getProperty("line.separator")); + + // Break if the line is too long + if (maxLength > 0 && result.getBuffer().length() + line.length() + 1 >= maxLength) { + HockeyLog.info(filename + " is too large, truncate a bit"); + break; + } + + // Write line + line separator + writer.println(line); } } catch (IOException e) { HockeyLog.error("Failed to read content of " + filename, e); @@ -820,7 +826,7 @@ private static String contentsOfFile(final WeakReference weakContext, S } } } - return contents.toString(); + return result.toString(); } return ""; }