Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

debugging transformed code #7

Closed
bennylut opened this issue Sep 29, 2016 · 4 comments
Closed

debugging transformed code #7

bennylut opened this issue Sep 29, 2016 · 4 comments
Labels

Comments

@bennylut
Copy link

Hi, first I just want to say - great job!

I noticed that when I use this library I cannot debug a method that has been modified, when I try to I receive the following error:
JDWP exit error JVMTI_ERROR_NONE(0): getting frame location [stepControl.c:641]

is this a limitation that you planning to resolve?

@JoeHegarty
Copy link
Member

JoeHegarty commented Sep 30, 2016

Hi!
If you use the -javaagent JVM parameter as described in the README to run ea-async, you shouldn't run into any crashes, but it doesn't really solve the issue at hand.
Unfortunately, debugging is still not going to work as expected (you will get odd behaviour with breakpoints and stepping through your code). This makes sense when you think about what ea-async is doing by rewriting the bytecode underneath so it's no longer matching the input source.

I've had some thoughts about potentially allowing a way to disable async just for debugging. This would work by replacing calls to await with a basic join. That would likely fix the debugging issues, but then the runtime behaviour would be significantly different as the code would block the thread rather than rewriting your method to be a state machine returning a task immediately, so it's not ideal from that point of view.

If you (or anyone else) has any thoughts on how we might approach this problem I'm certainly open to ideas!

@bennylut
Copy link
Author

bennylut commented Sep 30, 2016

I see, What if you had a code transformation for async that only add bytecode to the existing method and don't completely modify the existing byte code - doesn't then the existing debug information will be kept relevant and therefore will allow debugging?

Let me try to explain myself: (and on this occasion also say sorry for the long post :) )

assume that you have the following class:

class Test {
  static CompletableFuture<Integer> doSomthingAsync() { ... }

  static CompletableFuture<Integer> transformMe(int k) {
    int sum=0; //line 1
    for (int i=0; i<k; i++)  //line 2
        sum+=await(doSomethingAsync()); //line 3
    return completedFuture(sum); //line 4
  }
}

If it will be compiled with debug information it will contain a LineNumberTable for the debugger that is in sync with the source code,
if the transformation of ea-async for the transformMe method will do something like:

static CompletableFuture<Integer> transformMe(int k) {
  AsyncState$transformMe state = AsyncState.get();
  if (state == null) {
    state = new AsyncState$transformMe(k);
  }

  switch (state.state) {
    case 0: goto START; //using the goto op code 
    case 1: goto STATE_1; //using the goto op code 
  }

  START:
  int sum=0;  //line 1
  for (int i=0; i<k; i++) { //line 2
    state.$local1 = sum; 
    state.$local2 = i;
    return state.bind(doSomethingAsync(), 1)
    STATE_1:
    sum = state.$local1;
    i = state$local2;
    sum+=(Integer) state.lastAwaitResult(); //line 3
  }
  return state.complete(completedFuture(sum)); //line 4
}

Where AsyncState is the following class:

abstract class AsyncState {
  private final static ThreadLocal<AsyncState> STATE_HOLDER = new ThreadLocal<>();

  public static <T extends AsyncState> T get() {
    T res =  (T) STATE_HOLDER.get();
    STATE_HOLDER.remove();
    return res;
  }
  public static void set(AsyncState state) {STATE_HOLDER.set(state); }

  private CompletableFuture result = new CompletableFuture();
  public int state = 0;
  public Object lastResult;

  abstract void doContinue();

  CompletableFuture  bind (CompletablFuture target, int nextState) {
    target.whenComplete((v,e) -> {
        if (e == null) { 
          this.state = nextState;
          AsyncState.set(this);
          this.lastResult = v;
          doContinue();
        }else {
           result.completeExceptionally(e);
        }
    });  
    return result;
  }

  CompletableFuture complete(CompletableFuture fin) {
     fin.whenComplete((v,e) -> {
        if (e == null) { 
          result.complete(v);
        }else {
           result.completeExceptionally(e);
        }
    });  
    return result;
  }
}

And AsyncState$transformMe is a class that is generated by the agent and look something like:

public class AsyncState$transformMe  extends AsyncState {
  public int $arg1;
  public int $local1;
  public int $local2;

  public AsyncState$transformMe(int $arg1) {this.$arg1 = $arg1;}

  void doContinue() {
    transformMe($arg1);
  }  

}

Under this transformation, the original source code is still inside the same method and with a little adjustments one can align the LineNumbersTable accordingly which (theoretically at least) should keep the debugger happy.. Do you think It can work?

BTW, I am sure that it is a huge change in the code and I not suggesting you should change the whole design - It is just an idea that I hope can help..

@bennylut bennylut reopened this Sep 30, 2016
@DanielSperry
Copy link
Contributor

@bennylut It is usually still possible to debug the instrumented code. the line information is not lost.

The only situation where we have problems debugging it is when the instrumentation is done in runtime without the java agent. Offline instrumentation; or runtime instrumentation with the java agent; seem to debug fine.

However, your issue might be something we haven't encountered before. Which IDE are you using?

@TheMulti0
Copy link

TheMulti0 commented Apr 11, 2019

Great work on the project!

I still encounter that problem in IntelliJ, I encountered using the Gradle instrumental tool, and the runtime option Async.init();. What do you guys suggest?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants