Friday, April 28, 2017

Eclipse - break on user code when unhandled exception is raised on Android App

Leave a Comment

My problem is simple :

  • I use Ecplise (Luna or Neon) to develop on Android and I don't want to use Android Studio

  • I wish to debug breaks on ALL unhandled exceptions only on the last user code call of the stack that cause the exception (So, for example, I don't want to break in an unuseful ZygonteInit&MethodAndArgsCaller.run() when an exception in caused by passing a null reference to a native Android SDK method).

I know that I can set a break point for a particular exception in the breakpoint view (NullPointerException..Throwable...) but I want to break on ALL unhandled. I know that I can filter debug by setting "step filters" in Java debug option, but in my case this doesn't work for all exception.

EDIT

In the image below my stack in debug View when an exception is raised (a division by zero in my code)

enter image description here

And the stack of the main thread if I set a default Uncaught Exception Handler after exception is raised.

enter image description here

2 Answers

Answers 1

You can first verify if this setting in Eclipse is enabled.

Window -> Preferences -> Java -> Debug -> Suspend execution on uncaught exceptions

If this setting is enabled, any uncaught exception will suspend the JVM exactly at the point its thrown, including classes invoked using reflection. This is without adding any breakpoint, but provided its unhandled, i.e. your code is not even invoked by an external code from a try-catch.

For e.g.

int a = 0, b= 0; System.out.println(a/b); // ArithmeticException 

Even if this code is called from a reflection invoked code, eclipse will suspend at sysout with all variables still available on the stack.

However in Android's startup class ZygoteInit there is this line :

    catch (Throwable t) {                 Log.e(TAG, "Error preloading " + line + ".", t);                 if (t instanceof Error) {                     throw (Error) t;                 }                 if (t instanceof RuntimeException) {                     throw (RuntimeException) t;                 }                 throw new RuntimeException(t);             } 

The reason why such code would break Eclipse debugging is, the RuntimeException is now no more unhandled. Your UncaughtExceptionHandler may actually be catching the startup class instead of your user code. This is for regular Eclipse.

Solution 1 :

  1. Goto Run -> Add Java Exception Breakpoint -> Throwable
  2. Click on Throwable in the Breakpoint view
  3. Right click -> Breakpoint properties -> Add package -> OK
  4. Check on the option Subclasses of this exception

enter image description here

Note : This can marginally catch a java.lang.OutOfMemoryError but definitely cannot catch a java.lang.StackOverflowError.

Solution 2 : (Only if too many caught exceptions, NOT recommended otherwise)

  1. Copy the source code of com.android.internal.os.ZygoteInit to a new project say MyBootstrap
  2. Modify the catch (Throwable t) block to catch only Error

        } catch (Error t) {         Log.e(TAG, "Error preloading " + line + ".", t);         throw t;     } 
  3. Go-to debug configurations -> Classpath -> Click Bootstrap Entries -> Add projects -> MyBootstrap. Move this project to the top

enter image description here

Answers 2

Basically, if I understand you correctly, you want to set a breakpoint that will trigger at the point where an exception is thrown if that exception is not / would not be handled subsequently.

If that is what you mean, then what you are asking for is basically impossible.

  1. At the point the exception is thrown the debugger cannot tell if the exception is going to be caught.

  2. At the point where the exception is caught, the state (i.e. stack frames, variables, etc) from the throw point ... and up to the catch point ... will have been discarded.

  3. The Java debugger APIs don't support a "rewind and replay" mechanism that a debugger could use for this.


To my mind, the best you can do is to 1) identify the exception that you suspect is not being caught, 2) set a breakpoint on its constructor or on a suitable superclass constructor, 3) figure out some conditions to filter out the cases that are not interesting, and 4) step through the code to see if the exception is caught or not.

Note: an exception may be thrown or rethrown at a different point to where it was instantiated, so an exception constructor breakpoint won't always help. But it usually will.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment