Cannot get information about local variables on a StackFrame

I'm trying to write a relatively simple debugger in java, but I'm having some trouble with StackFrame.visibleVariables(). It always throws an AbsentInformationException, yet I cannot see why. Can anybody explain why this happens?

I've tried searching for hints as to what is wrong, but there doesn't seem to be any. Most of the hits I get in google is from the JPDA API documentation.

Here follows the code that's not working. It consists of two files: Trace.java with the debugger implementation (a modified version of the example tracer that comes with JPDA), and Foo.java which is the class that will be traced.

// ****** Trace.java ******

import java.io.*;

import java.util.*;

import com.sun.jdi.*;

import com.sun.jdi.request.*;

import com.sun.jdi.event.*;

import com.sun.jdi.connect.*;

/**

* This program traces the execution of another program.

*/

class Trace{

// Running remote VM

privatefinal VirtualMachine vm;

// Thread transferring remote error stream to our error stream

private Thread errThread =null;

// Thread transferring remote output stream to our output stream

private Thread outThread =null;

/**

* main

*/

publicstaticvoid main(String[] args){

new Trace(args);

}

/**

* Launch target VM.

* Generate the trace.

*/

Trace(String[] args){

PrintWriter writer =new PrintWriter(System.out);

// Launch target VM - Foo is the class to be executed

vm = launchTarget("Foo");

generateTrace(writer);

}

/**

* Generate the trace.

* Enable events, start thread to display events,

* start threads to forward remote error and output streams,

* resume the remote VM, wait for the final event, and shutdown.

*/

void generateTrace(PrintWriter writer){

vm.setDebugTraceMode(VirtualMachine.TRACE_NONE);

EventThread eventThread =new EventThread(vm, writer);

eventThread.start();

redirectOutput();

vm.resume();

// Shutdown begins when event thread terminates

try{

eventThread.join();

errThread.join();// Make sure output is forwarded

outThread.join();// before we exit

}catch (InterruptedException exc){

// we don't interrupt

}

writer.close();

}

/**

* Launch target VM.

* Forward target's output and error.

*/

VirtualMachine launchTarget(String mainArgs){

LaunchingConnector connector = findLaunchingConnector();

Map arguments = connectorArguments(connector, mainArgs);

try{

return connector.launch(arguments);

}catch (IOException exc){

thrownew Error("Unable to launch target VM: " + exc);

}catch (IllegalConnectorArgumentsException exc){

thrownew Error("Internal error: " + exc);

}catch (VMStartException exc){

thrownew Error("Target VM failed to initialize: " +

exc.getMessage());

}

}

void redirectOutput(){

Process process = vm.process();

// Copy target's output and error to our output and error.

errThread =new StreamRedirectThread("error reader", process.getErrorStream(), System.err);

outThread =new StreamRedirectThread("output reader",process.getInputStream(), System.out);

errThread.start();

outThread.start();

}

/**

* Find a com.sun.jdi.CommandLineLaunch connector

*/

LaunchingConnector findLaunchingConnector(){

List<Connector> connectors = Bootstrap.virtualMachineManager().allConnectors();

for(Connector connector: connectors)

if (connector.name().equals("com.sun.jdi.CommandLineLaunch"))

return (LaunchingConnector)connector;

thrownew Error("No launching connector");

}

/**

* Return the launching connector's arguments.

*/

Map connectorArguments(LaunchingConnector connector, String mainArgs){

Map arguments = connector.defaultArguments();

Connector.Argument mainArg =

(Connector.Argument)arguments.get("main");

if (mainArg ==null){

thrownew Error("Bad launching connector");

}

mainArg.setValue(mainArgs);

// Need a VM that supports watchpoints

Connector.Argument optionArg = (Connector.Argument)arguments.get("options");

if (optionArg ==null){

thrownew Error("Bad launching connector");

}

optionArg.setValue("-classic");

return arguments;

}

}

class EventThreadextends Thread{

privatefinal VirtualMachine vm;// Running VM

privatefinal PrintWriter writer;// Where output goes

privateboolean connected =true;// Connected to VM

privateboolean vmDied =true;// VMDeath occurred

// Maps ThreadReference to ThreadTrace instances

private Map traceMap =new HashMap();

EventThread(VirtualMachine vm, PrintWriter writer){

super("event-handler");

this.vm = vm;

this.writer = writer;

}

/**

* Run the event handling thread.

* As long as we are connected, get event sets off

* the queue and dispatch the events within them.

*/

publicvoid run(){

EventQueue queue = vm.eventQueue();

while (connected){

try{

EventSet eventSet = queue.remove();

for(Event e: eventSet)

handleEvent(e);

eventSet.resume();

}catch (InterruptedException exc){

// Ignore

}catch (VMDisconnectedException discExc){

break;

}

}

}

/**

* This class keeps context on events in one thread.

* In this implementation, context is the indentation prefix.

*/

class ThreadTrace{

final ThreadReference thread;

ThreadTrace(ThreadReference thread){

this.thread = thread;

// Create step event which will step through all lines

EventRequestManager mgr = vm.eventRequestManager();

StepRequest req = mgr.createStepRequest(thread,

StepRequest.STEP_LINE,

StepRequest.STEP_INTO);

req.setSuspendPolicy(EventRequest.SUSPEND_ALL);

req.addClassFilter("Foo");

req.enable();

}

privatevoid println(String str){

writer.println(str);

}

void printStackFrame(StackFrame sf){

try{

for(LocalVariable localvar: sf.visibleVariables()){

println("Local variable: " + localvar.name());

}

}catch(AbsentInformationException e){

println("Got AbsentInformationException");

}

}

void stepEvent(StepEvent event){

try{

if(!event.thread().frame(0).location().sourceName().equals("Foo.java"))

return;

}catch(Exception e){

return;

}

try{

StackFrame sf = event.thread().frame(0);

println("\n**** " + sf.location().sourceName() +

":" + sf.location().lineNumber() +

" in method " + sf.location().method().name() +"() ****");

for(StackFrame s: event.thread().frames()){

printStackFrame(s);

}

}catch(Exception e){

//foo

}

}

}

/**

* Returns the ThreadTrace instance for the specified thread,

* creating one if needed.

*/

ThreadTrace threadTrace(ThreadReference thread){

return (ThreadTrace)traceMap.get(thread);

}

/**

* Dispatch incoming events

*/

privatevoid handleEvent(Event event){

if (eventinstanceof StepEvent){

stepEvent((StepEvent)event);

}elseif(eventinstanceof VMStartEvent){

startEvent((VMStartEvent)event);

}

}

// Create entry in traceMap

privatevoid startEvent(VMStartEvent event){

traceMap.put(event.thread(),new ThreadTrace(event.thread()));

}

// Forward event for thread specific processing

privatevoid stepEvent(StepEvent event){

threadTrace(event.thread()).stepEvent(event);

}

}

/**

* StreamRedirectThread is a thread which copies it's input to

* it's output and terminates when it completes.

*/

class StreamRedirectThreadextends Thread{

privatefinal Reader in;

privatefinal Writer out;

privatestaticfinalint BUFFER_SIZE = 2048;

/**

* Set up.

* @param name Name of the thread

* @param inStream to copy from

* @param outStream to copy to

*/

StreamRedirectThread(String name, InputStream in, OutputStream out){

super(name);

this.in =new InputStreamReader(in);

this.out =new OutputStreamWriter(out);

setPriority(Thread.MAX_PRIORITY-1);

}

publicvoid run(){

try{

char[] cbuf =newchar[BUFFER_SIZE];

int count;

while ((count = in.read(cbuf, 0, BUFFER_SIZE)) >= 0){

out.write(cbuf, 0, count);

}

out.flush();

}catch(IOException exc){

System.err.println("Child I/O Transfer - " + exc);

}

}

}

// ****** Trace.java ******

class Foo{

int a = 0, b = 1, c = 2;

publicstaticvoid main(String[] args){

int e = 4;

int g = 5;

Foo f =new Foo();

for(int i=0; i<2; i++)

f.foo();

}

publicstaticvoid foo(){

int d = 3;

System.out.println("Foo");

d = 4;

}

}

When Trace is run, the following is outputted:

Foo

Foo

**** Foo.java:6 in method main() ****

Got AbsentInformationException

**** Foo.java:7 in method main() ****

Got AbsentInformationException

**** Foo.java:8 in method main() ****

Got AbsentInformationException

**** Foo.java:1 in method <init>() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:2 in method <init>() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:8 in method main() ****

Got AbsentInformationException

**** Foo.java:9 in method main() ****

Got AbsentInformationException

**** Foo.java:10 in method main() ****

Got AbsentInformationException

**** Foo.java:15 in method foo() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:16 in method foo() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:17 in method foo() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:18 in method foo() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:9 in method main() ****

Got AbsentInformationException

**** Foo.java:10 in method main() ****

Got AbsentInformationException

**** Foo.java:15 in method foo() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:16 in method foo() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:17 in method foo() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:18 in method foo() ****

Got AbsentInformationException

Got AbsentInformationException

**** Foo.java:9 in method main() ****

Got AbsentInformationException

**** Foo.java:11 in method main() ****

Got AbsentInformationException

[20454 byte] By [Carbonizea] at [2007-11-26 19:57:28]
# 1
Just a quick answer (I haven't looked into the code in detail): are you sure that the Virtual Machine under watch is suspended? When it is running, consulting a stackframe will result into an error.Kees
keesa at 2007-7-9 22:52:13 > top of Java-index,Developer Tools,Debugging and Profiling Tool APIs...
# 2
Thanks for your feedback. I just found the error, and it's quite embarrasing. I acctually forgot to include the -g option when compiling Foo.java :-) Now it seems to work fine.
Carbonizea at 2007-7-9 22:52:13 > top of Java-index,Developer Tools,Debugging and Profiling Tool APIs...
# 3

Hello,

I try to execute your code by netbeans, but I find this error:

java.lang.NoClassDefFoundError: Foo

Warning: classic VM not supported; client VM will be used

Exception in thread "main

"

I am trying to launching an application using jdi, it is okay by enter the class like argument in the command line 搄ava <options>..<Arguments>? but I don't succeed in entering the class name in the program like you.

Please help me?

asmainfa at 2007-7-9 22:52:13 > top of Java-index,Developer Tools,Debugging and Profiling Tool APIs...