Detect and report stack traces in the PDE.

This commit is contained in:
jdf
2010-03-03 16:38:38 +00:00
parent 366f792fc1
commit ae6045f8cd
5 changed files with 129 additions and 29 deletions

View File

@@ -1,7 +1,10 @@
package processing.app.tools.android;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -12,6 +15,8 @@ class AndroidDevice implements AndroidDeviceProperties {
private final AndroidEnvironment env;
private final String id;
private final Set<Integer> activeProcesses = new HashSet<Integer>();
private final Set<DeviceListener> listeners = Collections
.synchronizedSet(new HashSet<DeviceListener>());
// mutable state
private Process logcat;
@@ -88,40 +93,65 @@ class AndroidDevice implements AndroidDeviceProperties {
private static final Pattern SIG = Pattern
.compile("PID:\\s+(\\d+)\\s+SIG:\\s+(\\d+)");
private final List<String> stackTrace = new ArrayList<String>();
private class LogLineProcessor implements LineProcessor {
public void processLine(final String line) {
final LogEntry entry = new LogEntry(line);
final String src = entry.source;
final String msg = entry.message;
final Severity sev = entry.severity;
if (msg.startsWith("PROCESSING")) {
if (msg.contains("onStart")) {
startProc(src, entry.sourcePid);
} else if (msg.contains("onStop")) {
endProc(entry.sourcePid);
if (entry.message.startsWith("PROCESSING")) {
if (entry.message.contains("onStart")) {
startProc(entry.source, entry.pid);
} else if (entry.message.contains("onStop")) {
endProc(entry.pid);
}
} else if (src.equals("Process")) {
final Matcher m = SIG.matcher(msg);
} else if (entry.source.equals("Process")) {
final Matcher m = SIG.matcher(entry.message);
if (m.find()) {
final int pid = Integer.parseInt(m.group(1));
final int signal = Integer.parseInt(m.group(2));
if (signal == 9) {
endProc(pid);
} else if (signal == 3) {
reportStackTrace(entry);
}
}
} else if (activeProcesses.contains(entry.sourcePid)
&& ((src.equals("AndroidRuntime") && sev == Severity.Error)
|| src.equals("System.out") || src.equals("System.err"))) {
if (sev.useErrorStream) {
System.err.println(msg);
} else {
System.out.println(msg);
}
} else if (activeProcesses.contains(entry.pid)) {
handleConsole(entry);
}
//System.err.println(entry.source + "/" + entry.message);
}
}
private void handleConsole(final LogEntry entry) {
final boolean isStackTrace = entry.source.equals("AndroidRuntime")
&& entry.severity == Severity.Error;
if (isStackTrace) {
if (!entry.message.startsWith("Uncaught handler")) {
stackTrace.add(entry.message);
System.err.println(entry.message);
}
} else if (entry.source.equals("System.out")
|| entry.source.equals("System.err")) {
if (entry.severity.useErrorStream) {
System.err.println(entry.message);
} else {
System.out.println(entry.message);
}
}
}
private void reportStackTrace(final LogEntry entry) {
if (stackTrace.isEmpty()) {
System.err.println("That's weird. Proc " + entry.pid
+ " got signal 3, but there's no stack trace.");
}
final List<String> stackCopy = Collections
.unmodifiableList(new ArrayList<String>(stackTrace));
for (final DeviceListener listener : listeners) {
listener.stacktrace(stackCopy);
}
stackTrace.clear();
}
void initialize() throws IOException, InterruptedException {
adb("logcat", "-c");
logcat = Runtime.getRuntime().exec(generateAdbCommand("logcat"));
@@ -135,6 +165,7 @@ class AndroidDevice implements AndroidDeviceProperties {
logcat.destroy();
}
env.deviceRemoved(this);
listeners.clear();
}
public String getId() {
@@ -155,6 +186,14 @@ class AndroidDevice implements AndroidDeviceProperties {
activeProcesses.remove(pid);
}
public void addListener(final DeviceListener listener) {
listeners.add(listener);
}
public void removeListener(final DeviceListener listener) {
listeners.remove(listener);
}
private ProcessResult adb(final String... cmd) throws InterruptedException,
IOException {
final String[] adbCmd = generateAdbCommand(cmd);

View File

@@ -1,10 +1,10 @@
package processing.app.tools.android;
import java.awt.Frame;
import java.io.File;
import java.io.IOException;
import javax.swing.JOptionPane;
import processing.app.Base;
import processing.app.Editor;
import processing.app.Platform;
import processing.app.Preferences;
@@ -76,7 +76,7 @@ class AndroidSDK {
* @throws BadSDKException
* @throws IOException
*/
public static AndroidSDK find(final Editor editor) throws BadSDKException,
public static AndroidSDK find(final Frame window) throws BadSDKException,
IOException {
final Platform platform = Base.getPlatform();
@@ -110,7 +110,7 @@ class AndroidSDK {
}
}
final int result = Base.showYesNoQuestion(editor, "Android SDK",
final int result = Base.showYesNoQuestion(window, "Android SDK",
ANDROID_SDK_PRIMARY, ANDROID_SDK_SECONDARY);
if (result == JOptionPane.CANCEL_OPTION) {
throw new BadSDKException("User cancelled attempt to find SDK.");
@@ -122,7 +122,7 @@ class AndroidSDK {
}
while (true) {
final File folder = Base.selectFolder(SELECT_ANDROID_SDK_FOLDER, null,
editor);
window);
if (folder == null) {
throw new BadSDKException("User cancelled attempt to find SDK.");
}
@@ -133,7 +133,7 @@ class AndroidSDK {
Preferences.set("android.sdk.path", selectedPath);
return androidSDK;
} catch (final BadSDKException nope) {
JOptionPane.showMessageDialog(editor, NOT_ANDROID_SDK);
JOptionPane.showMessageDialog(window, NOT_ANDROID_SDK);
}
}
}

View File

@@ -21,16 +21,23 @@
package processing.app.tools.android;
import java.awt.Frame;
import java.io.File;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.ProgressMonitor;
import processing.app.Base;
import processing.app.Editor;
import processing.app.Sketch;
import processing.app.debug.Runner;
import processing.app.debug.RunnerException;
import processing.app.tools.Tool;
import processing.core.PApplet;
@@ -38,7 +45,7 @@ import processing.core.PApplet;
// http://dl.google.com/android/android-sdk_r3-mac.zip
// http://dl.google.com/android/repository/tools_r03-macosx.zip
public class AndroidTool implements Tool {
public class AndroidTool implements Tool, DeviceListener {
private AndroidSDK sdk;
private Editor editor;
private Build build;
@@ -61,7 +68,7 @@ public class AndroidTool implements Tool {
editor.statusNotice("Loading Android tools.");
try {
sdk = AndroidSDK.find(editor);
sdk = AndroidSDK.find((editor instanceof Frame) ? (Frame) editor : null);
} catch (final Exception e) {
Base.showWarning("Android Tools Error", e.getMessage(), null);
editor.statusNotice("Android mode canceled.");
@@ -208,6 +215,8 @@ public class AndroidTool implements Tool {
return;
}
device.addListener(this);
if (monitor.isCanceled()) {
throw new Cancelled();
}
@@ -230,6 +239,51 @@ public class AndroidTool implements Tool {
}
}
private static final Pattern LOCATION = Pattern
.compile("\\(([^:]+):(\\d+)\\)");
private static final Pattern EXCEPTION_PARSER = Pattern.compile(
"^([a-z]+(?:\\.[a-z]+)+)(?:: (.+))?$", Pattern.CASE_INSENSITIVE);
/**
* Currently figures out the first relevant stack trace line
* by looking for the telltale presence of "processing.android"
* in the package. If the packaging for droid sketches changes,
* this method will have to change too.
*/
public void stacktrace(final List<String> trace) {
final Iterator<String> frames = trace.iterator();
final String exceptionLine = frames.next();
final Matcher m = EXCEPTION_PARSER.matcher(exceptionLine);
if (!m.matches()) {
System.err.println("Can't parse this exception line:");
System.err.println(exceptionLine);
editor.statusError("Unknown exception");
return;
}
final String exceptionClass = m.group(1);
final String message = m.group(2);
if (Runner.handleCommonErrors(exceptionClass, message, editor)) {
return;
}
while (frames.hasNext()) {
final String line = frames.next();
if (line.contains("processing.android")) {
final Matcher lm = LOCATION.matcher(line);
if (lm.find()) {
final String filename = lm.group(1);
final int lineNumber = Integer.parseInt(lm.group(2));
final RunnerException rex = editor.getSketch().placeException(
message, filename, lineNumber);
editor.statusError(rex == null ? new RunnerException(message, false)
: rex);
}
}
}
}
/**
* Build the sketch and run it inside an emulator with the debugger.
*/

View File

@@ -0,0 +1,7 @@
package processing.app.tools.android;
import java.util.List;
public interface DeviceListener {
void stacktrace(final List<String> trace);
}

View File

@@ -35,7 +35,7 @@ class LogEntry {
public final Severity severity;
public final String source;
public final int sourcePid;
public final int pid;
public final String message;
private static final Pattern PARSER = Pattern
@@ -48,12 +48,12 @@ class LogEntry {
}
this.severity = Severity.fromChar(m.group(1).charAt(0));
this.source = m.group(2);
this.sourcePid = Integer.parseInt(m.group(3));
this.pid = Integer.parseInt(m.group(3));
this.message = m.group(4);
}
@Override
public String toString() {
return severity + "/" + source + "(" + sourcePid + "): " + message;
return severity + "/" + source + "(" + pid + "): " + message;
}
}