mirror of
https://github.com/mauiaaron/apple2.git
synced 2025-03-06 00:29:34 +00:00
Zip up crash and log files on Droid to email to developer
This commit is contained in:
parent
310825e2cc
commit
f5899e74d6
@ -19,14 +19,11 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.deadc0de.apple2ix.basic.BuildConfig;
|
||||
import org.deadc0de.apple2ix.basic.R;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
@ -34,10 +31,13 @@ import java.io.FileWriter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class Apple2CrashHandler {
|
||||
|
||||
public final static String ALL_CRASH_FILE = "apple2ix_crash.zip";
|
||||
|
||||
public final static String javaCrashFileName = "jcrash.txt";
|
||||
|
||||
public static Apple2CrashHandler getInstance() {
|
||||
@ -241,8 +241,8 @@ public class Apple2CrashHandler {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink java crash : " + javaCrashFile);
|
||||
}
|
||||
|
||||
// remove previous log file
|
||||
File allCrashFile = _getCrashFile(activity);
|
||||
// remove previous crash files
|
||||
File allCrashFile = _getCrashFile(activity, ALL_CRASH_FILE);
|
||||
Apple2Utils.writeFile(new StringBuilder(), allCrashFile);
|
||||
allCrashFile.delete();
|
||||
return;
|
||||
@ -280,7 +280,6 @@ public class Apple2CrashHandler {
|
||||
final int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/true);
|
||||
|
||||
StringBuilder summary = new StringBuilder();
|
||||
StringBuilder allCrashData = new StringBuilder();
|
||||
|
||||
// prepend information about this device
|
||||
summary.append("BRAND: ").append(Build.BRAND).append("\n");
|
||||
@ -302,8 +301,6 @@ public class Apple2CrashHandler {
|
||||
// ...
|
||||
}
|
||||
|
||||
allCrashData.append(summary);
|
||||
|
||||
File[] nativeCrashes = _nativeCrashFiles(activity);
|
||||
if (nativeCrashes == null) {
|
||||
nativeCrashes = new File[0];
|
||||
@ -320,7 +317,7 @@ public class Apple2CrashHandler {
|
||||
}
|
||||
});
|
||||
|
||||
if (len > 0) {
|
||||
if (nativeCrashes.length > 0) {
|
||||
Apple2Utils.exposeSymbols(activity);
|
||||
}
|
||||
|
||||
@ -333,95 +330,41 @@ public class Apple2CrashHandler {
|
||||
}
|
||||
});
|
||||
|
||||
boolean summarizedHeader = false;
|
||||
|
||||
// iteratively process native crashes
|
||||
for (File crash : nativeCrashes) {
|
||||
ArrayList<File> allCrashFiles = new ArrayList<File>();
|
||||
if (nativeCrashes.length > 0) {
|
||||
for (File crash : nativeCrashes) {
|
||||
|
||||
String crashPath = crash.getAbsolutePath();
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Processing crash : " + crashPath);
|
||||
String crashPath = crash.getAbsolutePath();
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Processing crash : " + crashPath);
|
||||
|
||||
String processedPath = _dumpPath2ProcessedPath(crashPath);
|
||||
try {
|
||||
nativeProcessCrash(crashPath, processedPath); // Run Breakpad minidump_stackwalk
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
/* could happen on early lifecycle crashes */
|
||||
}
|
||||
String processedPath = _dumpPath2ProcessedPath(crashPath);
|
||||
try {
|
||||
nativeProcessCrash(crashPath, processedPath); // Run Breakpad minidump_stackwalk
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
/* could happen on early lifecycle crashes */
|
||||
}
|
||||
|
||||
StringBuilder crashData = new StringBuilder();
|
||||
if (!Apple2Utils.readEntireFile(new File(processedPath), crashData)) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error processing crash : " + crashPath);
|
||||
}
|
||||
allCrashData.append(">>>>>>> NATIVE CRASH [").append(crashPath).append("]\n");
|
||||
allCrashData.append(crashData);
|
||||
summary.append("NATIVE CRASH:\n");
|
||||
allCrashFiles.add(new File(processedPath));
|
||||
|
||||
// append succinct information about crashing thread
|
||||
String[] lines = crashData.toString().split("[\\n\\r][\\n\\r]*");
|
||||
for (int i = 0, j = 0; i < lines.length; i++) {
|
||||
|
||||
// 2 lines of minidump summary
|
||||
if (i < 2) {
|
||||
if (!summarizedHeader) {
|
||||
summary.append(lines[i]);
|
||||
summary.append("\n");
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (bar != null) {
|
||||
bar.setProgress(bar.getProgress() + 1);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1 line of crashing thread and reason
|
||||
if (i == 2) {
|
||||
summarizedHeader = true;
|
||||
summary.append(lines[i]);
|
||||
summary.append("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// whole lotta modules
|
||||
if (lines[i].startsWith("Module")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// one apparently empty line
|
||||
if (lines[i].matches("^[ \\t]*$")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// append crashing thread backtrace
|
||||
|
||||
summary.append(lines[i]);
|
||||
summary.append("\n");
|
||||
final int maxSummaryBacktrace = 8;
|
||||
if (j++ >= maxSummaryBacktrace) {
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (bar != null) {
|
||||
bar.setProgress(bar.getProgress() + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
summary.append("" + nativeCrashes.length + " native crashes\n");
|
||||
}
|
||||
|
||||
StringBuilder javaCrashData = new StringBuilder();
|
||||
File javaCrashFile = _javaCrashFile(activity);
|
||||
if (javaCrashFile.exists()) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Reading java crashes file");
|
||||
if (!Apple2Utils.readEntireFile(javaCrashFile, javaCrashData)) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error processing java crash : " + javaCrashFileName);
|
||||
}
|
||||
summary.append("Java Crash File\n");
|
||||
allCrashFiles.add(javaCrashFile);
|
||||
}
|
||||
|
||||
allCrashData.append(">>>>>>> JAVA CRASH DATA\n");
|
||||
allCrashData.append(javaCrashData);
|
||||
|
||||
summary.append("JAVA CRASH:\n");
|
||||
summary.append(javaCrashData);
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -431,23 +374,12 @@ public class Apple2CrashHandler {
|
||||
}
|
||||
});
|
||||
|
||||
StringBuilder jsonData = new StringBuilder();
|
||||
if (Apple2Utils.readEntireFile(new File(homeDir, Apple2Preferences.PREFS_FILE), jsonData)) {
|
||||
JSONObject obj = null;
|
||||
try {
|
||||
obj = new JSONObject(jsonData.toString());
|
||||
} catch (JSONException e) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error reading preferences : " + e);
|
||||
}
|
||||
if (obj != null) {
|
||||
summary.append("PREFS:\n");
|
||||
summary.append(obj.toString());
|
||||
allCrashData.append(">>>>>>> PREFS\n");
|
||||
allCrashData.append(obj.toString());
|
||||
}
|
||||
allCrashFiles.add(new File(homeDir, Apple2Preferences.PREFS_FILE));
|
||||
|
||||
if (nativeCrashes.length > 0) {
|
||||
Apple2Utils.unexposeSymbols(activity);
|
||||
}
|
||||
|
||||
Apple2Utils.unexposeSymbols(activity);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -460,8 +392,18 @@ public class Apple2CrashHandler {
|
||||
}
|
||||
});
|
||||
|
||||
// Add all the log files ...
|
||||
{
|
||||
File[] nativeLogs = _nativeLogs(activity);
|
||||
for (File logFile : nativeLogs) {
|
||||
allCrashFiles.add(logFile);
|
||||
}
|
||||
}
|
||||
|
||||
File[] allCrashesAry = new File[allCrashFiles.size()];
|
||||
File nativeCrashesZip = Apple2Utils.zipFiles(allCrashFiles.toArray(allCrashesAry), _getCrashFile(activity, ALL_CRASH_FILE));
|
||||
// send report with all the data
|
||||
_sendEmailToDeveloperWithCrashData(activity, summary, allCrashData);
|
||||
_sendEmailToDeveloperWithCrashData(activity, summary, nativeCrashesZip);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
@ -527,6 +469,21 @@ public class Apple2CrashHandler {
|
||||
return new File(homeDir, javaCrashFileName);
|
||||
}
|
||||
|
||||
private File[] _nativeLogs(Apple2Activity activity) {
|
||||
FilenameFilter logFilter = new FilenameFilter() {
|
||||
public boolean accept(File dir, String name) {
|
||||
File file = new File(dir, name);
|
||||
if (file.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return name.startsWith("apple2ix_log");
|
||||
}
|
||||
};
|
||||
|
||||
return new File(homeDir).listFiles(logFilter);
|
||||
}
|
||||
|
||||
private File[] _nativeCrashFiles(Apple2Activity activity) {
|
||||
FilenameFilter dmpFilter = new FilenameFilter() {
|
||||
public boolean accept(File dir, String name) {
|
||||
@ -556,19 +513,18 @@ public class Apple2CrashHandler {
|
||||
return crashPath.substring(0, crashPath.length() - 4) + ".txt";
|
||||
}
|
||||
|
||||
private File _getCrashFile(Apple2Activity activity) {
|
||||
File allCrashFile;
|
||||
private File _getCrashFile(Apple2Activity activity, String fileName) {
|
||||
File file;
|
||||
String storageState = Environment.getExternalStorageState();
|
||||
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
|
||||
allCrashFile = new File(Environment.getExternalStorageDirectory(), "apple2ix_crash.txt");
|
||||
file = new File(Environment.getExternalStorageDirectory(), fileName);
|
||||
} else {
|
||||
allCrashFile = new File(Apple2Utils.getDataDir(activity), "apple2ix_crash.txt");
|
||||
file = new File(Apple2Utils.getDataDir(activity), fileName);
|
||||
}
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Writing all crashes to temp file : " + allCrashFile.getAbsolutePath());
|
||||
return allCrashFile;
|
||||
return file;
|
||||
}
|
||||
|
||||
private void _sendEmailToDeveloperWithCrashData(Apple2Activity activity, StringBuilder summary, StringBuilder allCrashData) {
|
||||
private void _sendEmailToDeveloperWithCrashData(Apple2Activity activity, StringBuilder summary, File nativeCrashesZip) {
|
||||
mAlreadySentReport.set(true);
|
||||
|
||||
// <sigh> ... the disaster that is early Android ... there does not appear to be a reliable way to start an
|
||||
@ -585,13 +541,11 @@ public class Apple2CrashHandler {
|
||||
String summaryData = summary.substring(0, len);
|
||||
emailIntent.putExtra(Intent.EXTRA_TEXT, "The app crashed, please help!\n\n" + summaryData);
|
||||
|
||||
File allCrashFile = _getCrashFile(activity);
|
||||
Apple2Utils.writeFile(allCrashData, allCrashFile);
|
||||
if (!allCrashFile.setReadable(true, /*ownerOnly:*/false)) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Oops, could not set file data readable!");
|
||||
if (!nativeCrashesZip.setReadable(true, /*ownerOnly:*/false)) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Oops, could not set crash file data readable!");
|
||||
}
|
||||
|
||||
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(allCrashFile));
|
||||
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(nativeCrashesZip));
|
||||
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "STARTING CHOOSER FOR EMAIL ...");
|
||||
activity.startActivity(Intent.createChooser(emailIntent, "Send email"));
|
||||
|
@ -22,6 +22,8 @@ import android.widget.ProgressBar;
|
||||
import org.deadc0de.apple2ix.basic.BuildConfig;
|
||||
import org.deadc0de.apple2ix.basic.R;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
@ -35,6 +37,8 @@ import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class Apple2Utils {
|
||||
|
||||
@ -103,6 +107,93 @@ public class Apple2Utils {
|
||||
return attempts < maxAttempts;
|
||||
}
|
||||
|
||||
public static File zipFiles(File[] files, File zipFile) {
|
||||
|
||||
zipFile.delete();
|
||||
ZipOutputStream out = null;
|
||||
|
||||
do {
|
||||
try {
|
||||
zipFile.createNewFile();
|
||||
} catch (IOException ioe) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could not create zipfile " + zipFile.getAbsolutePath() + " : " + ioe.getMessage());
|
||||
break;
|
||||
}
|
||||
|
||||
final int BUF_SIZ = 4096;
|
||||
BufferedInputStream origin = null;
|
||||
|
||||
try {
|
||||
out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
|
||||
} catch (IOException ioe) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could not create zip outputStream : " + ioe.getMessage());
|
||||
break;
|
||||
}
|
||||
|
||||
byte data[] = new byte[BUF_SIZ];
|
||||
|
||||
for (File file : files) {
|
||||
FileInputStream fi = null;
|
||||
|
||||
try {
|
||||
fi = new FileInputStream(file);
|
||||
} catch (IOException ioe) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could not create file input stream : " + ioe.getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
origin = new BufferedInputStream(fi, BUF_SIZ);
|
||||
|
||||
ZipEntry entry = new ZipEntry(file.getName());
|
||||
try {
|
||||
out.putNextEntry(entry);
|
||||
} catch (IOException ioe) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could not put next zip entry : " + ioe.getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
final int maxAttempts = 5;
|
||||
int attempts = 0;
|
||||
do {
|
||||
int count;
|
||||
try {
|
||||
while ((count = origin.read(data, 0, BUF_SIZ)) != -1) {
|
||||
out.write(data, 0, count);
|
||||
}
|
||||
break;
|
||||
} catch (InterruptedIOException ie) {
|
||||
/* EINTR, EAGAIN ... */
|
||||
} catch (IOException ioe) {
|
||||
Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Could read/write zip data : " + ioe.getMessage());
|
||||
break;
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
|
||||
try {
|
||||
origin.close();
|
||||
} catch (IOException ioe) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ioe) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
if (zipFile.exists()) {
|
||||
return zipFile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void migrateToExternalStorage(Apple2Activity activity) {
|
||||
|
||||
do {
|
||||
|
Loading…
x
Reference in New Issue
Block a user