mirror of
https://github.com/processing/processing4.git
synced 2026-02-02 13:21:07 +01:00
Detect and report stack traces in the PDE.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package processing.app.tools.android;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DeviceListener {
|
||||
void stacktrace(final List<String> trace);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user