2
Overview
source program
machine program
compiler
source level
machine level
source state
debugger
machine statestackheapglobal dataregistersload listcode
reference info
Reference info (symbol info)• contains names and addresses of variables and methods• is often part of the object file
Debugger decodes the machine state using the reference infoand shows the program state in source code
Debugger = inverse of a compiler
3
Machine state
PC
code stack(one per thread)
FP
SP
FP'PC'
FP''PC''
0
heap globaldata
registers
load list
Memory areas are unstructured byte arrays!They need to be "interpreted" using the reference info
4
Load listList of all loaded modules or classes
• built by the loader• used to find out to which module (class) a PC value belongs
e.g. PC = 2500 => module B
name Ainfo ...startPC 1000endPC 1800
B...22003500
C...40004900
5
Reference infoPer module or classserialized symbol table with names, addresses and types of all variables and methods
methods
name startPC endPCfoo 1000 1299bar 1300 1650baz 1651 1800
name offset typex -4y -8z -12
variables / fields
a -4b -8
name infoT1T2
types
positionsPC pos
1000130121522016526
......
for every statementits starting position in the source codeand the corresponding PC position
f1 0f2 8
The debugger parses the reference info and builds a symbol table (like in a compiler)
6
Problem: optimizing compilers
Variables are temporarily in registers and then in memory again
x
ECX
y
z
x: -4y: -8z: -12
x: ECXy: -8z: -12
x: -4y: -8z: ECX
The reference info must contain multiple regionsper method;for every region it must record the positions ofthe local variables
Program transfomations• Common subexpression elimination• Loop invariant code motion• Loop unrolling
Solution: before debugging, compile the program without optimizations
7
Reference information in OberonFor every module there is a reference file with the following structure
RefInfo = {Procedure} {Record}.
Procedure = ProcTag address Name {Variable}.Name = char {char} 0.Variable = (VarTag | VarParTag) Name address Type.Type = ByteTag | BoolTag | CharTag
| SIntTag | IntTag | LIntTag | RealTag | LRealTag| SetTag | StringTag | NilTag | NoTypeTag| PointerTag Type| ArrayTag length elemsize Type| RecTag mod typeDescAdr.
Record = RecTag typeDescAdr Name {Field} 0.Field = Name address Type.
NoTypeTag = 0ByteTag = 1BoolTag = 2CharTag = 3SIntTag = 4IntTag = 5LIntTag = 6RealTag = 7LRealTag = 8SetTag = 9StringTag = 10NilTag = 11ProcTag = 12RecTag = 13VarTag = 14VarParTag = 15PointerTag = 16ArrayTag = 17
PROCEDURE Foo(VAR x: INTEGER);VAR a: REAL;
BEGIN...
END Foo; 12 00 00 F o o 0 15 x 0 5 14 a 0 ff
Proc
Tag
Var
ParT
ag
IntT
agV
arTa
g
Rea
lTag
fc 700 08
8
Example: stack trace with local variables
stack
FP
SP
FP'PC'
FP''PC''
null while (FP != null) {mod = findModule(PC);proc = mod.findProc(PC);println(mod.Name + "." proc.Name);foreach (local v in proc.locals) {
print(v.name);print value at mem[FP+v.adr] according to v.type;
}PC = mem[FP+4];FP = mem[FP];
}
xyz
x -4name adr
inttype
y -8 floatz -12 int
Reference information of the current stack frame
9
Example: display of the error position
PC
...x = 2 * x + 1;y = x - 5;z = foo(x, y);...
code source programPC pos
1000130121522016526
......
PC/pos mapping table
pos
mod = findModule(PC);pos = mod.findPos(PC);markPos(sourceFile(mod), pos);
10
Kinds of debuggers
Post mortem debuggers• Analyze a program after it "crashed"• No program continuation possible• When a program crashes it dumps its machine state to a file.
This file is then analyzed by the debugger
Tracing debuggers (dynamic debuggers)• Analyze a running program• Typical operations:
- run to the next breakpoint- single step (step into, step over)- show/modify local variables
• Work with the real machine state(often a separate process, so that the machine state is not destroyed)
11
Process structure of debuggersDebugger and debuggee run as separate processes• every process has its own address space• every process has its own state (registers, stack, ...)
machinestate
back
• user interface• commands• symbol table
debuggee debugger
front
socket connection
commands• setBreakpoint• getWord• getRegister• ...
events• breakpoint• method entry• method exit• ...
back end • runs in the debuggee (in the run-time system or VM)• can access registers and memory; can set breakpoints, ...
front end • runs in the debugger• performs high-level operations to access the machine state
end
end
12
BreakpointsSetting a breakpoint
...MOV EAX, ...CMP EAX, ...JLE ......
instruction isreplaced by INT3
...INT3CMP EAX, ...JLE ......
overwritten instructionhas to be saved
adrinstr MOV EAX, ...next
breakpoint list
What happens if a breakpoint is reached?
INT3
OScontext switchto the debugger
• show machine state
• if continuation is desired:- restore the patched instruction- do a context switch back
debuggee process debugger process
Context switches are expensive• all registers have to be saved• virtual memory has to be swapped in and out
13
Conditional breakpoints
stop if x < 0
Breakpoint command
What happens?
...INT3...
debuggee
x = getWord(adr(x));if (x < 0) {
show program state;...
}restore instruction;switch back;
debugger
• Condition is evaluated in thecontext of the debugger
• Continues immediately if thecondition evaluates to false
14
Implicit breakpointsSet by the debugger implicitly (e.g. for single step)
...x = x + 1;y = x - 2;...
source program
PC = 1000PC = 1007
remove breakpoint at current PC;PC1 = address of next statement;set breakpoint at PC1;switch back;
debugger
...MOV EAX, xADD EAX, 1MOV x, EAXMOV EAX, xSUB EAX, 2MOV y, EAX...
1000
1007
INT3...MOV EAX, xADD EAX, 1MOV x, EAXMOV EAX, xSUB EAX, 2MOV y, EAX...
1000
1007 INT3
command
step
step over Set breakpoint to the beginning of the next statement
step into Set breakpoint to the beginning of the called methodProblem: dynamic binding => set breakpoints to the beginnings of all possibly invoked methods
15
Breakpoints in recursive methodsvoid Foo(int n) {
if (n == 0) {...
} else {...Foo(n-1);...
}}
Problem: program stops in deeper recursion level!
PCPC1
// command "step over"FP1 = fpremove breakpoint at PC;PC1 = address of next instruction;set breakpoint at PC1;switch back;
// breakpoint eventif (pc == PC1) {
if (fp == FP1) show program state;switch back;
}
Debugger
Foo(3)...Foo(2)
...Foo(1)
...Foo(0)...
16
TracingExecute an action when the program reaches a certain location• increment a counter• print a log message
With normal breakpoints
...MOV EAX, ......
execute action;switch back to saved instruction;INT3
MOV EAX, ...JMP ...
With "fast" breakpoints
MOV EAX, ......
JMP, ...
save registers;execute action;restore registers;MOV EAX, ...JMP ...
• no context switch• but code must be generated
for executing the action in thecontext of the debuggee
18
JPDA - Java Platform Debugger Architecture
JVMTI JDWP
debuggee debugger
VM VM
application debugger GUI
JDI Java Debug Interface (front end)Java API to access frames, threads, variables, types, ...
JVMTI Java VM Tools Interface (back end)C++ methods for accessing the machine state
JDWP Java Debug Wire Protocolcontrols the communication between JDI and JVMTI (e.g. sockets)
JDI packagescom.sun.jdi VirtualMachine, StackFrame, LocalVariable, Value, ReferenceType, ...com.sun.jdi.connect AttachingConnector, LaunchingConnector, ...com.sun.jdi.event BreakpointEvent, MethodEntryEvent, StepEvent, ...com.sun.jdi.request BreakpointRequest, MethodEntryRequest, StepRequest, ...
requestsrequestseventsJDI
Can only debug interpreted programs (compiled programs are de-optimized)
19
JDB - Command Line Java Debugger
class Test {public static void main(String[] args) {
int i = 1;int j = i * 2;System.out.println(j);
}}
Sample programmust be compiled with -g (generate reference info)
javac -g Test.java
Debug sessionjdb Test myArg> stop in Test.main> runBreakpoint hit: "thread=main", Test.main(), line=3 bci=03 int i = 1;> nextStep completed: "thread=main", Test.main(), line=4 bci=24 int j = 2 * i;> localsargs = instance of java.lang.String[0] (id=280)i = 1> print args[0]args[0] = "myArg"> quit
JDB uses JDI
reference info is written to the class file(as a special code attribute)
20
JDI classes for making a connection
BootstrapVirtualMachineManager virtualMachineManager()
VirtualMachineManagerList<Connector> allConnectors()LaunchingConnector defaultConnector()...
LaunchingConnectorVirtualMachine launch(Map arguments)
AttachingConnectorVirtualMachine attach(Map arguments)
VirtualMachineList<ReferenceType> allClasses()List<ThreadReference> allThreads()EventRequestManager eventRequestManager()EventQueue eventQueue()Process process()void suspend()void resume()...
21
Launching - start a Java-VM in debug mode
LaunchingConnector con = Bootstrap.virtualMachineManager().defaultConnector();Map args = con.defaultArguments();((Connector.Argument) args.get("main")).setValue("Test");try {
VirtualMachine vm = con.launch(args);Process proc = vm.process();new Redirection(proc.getErrorStream(), System.err).start();new Redirection(proc.getInputStream(), System.out).start();...
} catch (Exception e) { /* error */ }
SampleDebugger.java
Starting the debuggerjava -cp "...\tools.jar" SampleDebugger
(starts Test in the target VM)
class Redirection extends Thread {Reader in; Writer out;Redirection(InputStream is, OutputStream os) {
in = new InputStreamReader(is); out = new OutputStreamWriter(os);}public void run() {
try {char[] buf = new char[1024];while ((n = in.read(buf, 0, 1024)) >= 0) out.write(buf, 0, n);out.flush();
} catch(IOException e) {}}
}
Compiling the debuggerjavac -cp "...\tools.jar" SampleDebugger.java
the data that this process wrote to stdout
22
Attaching - connect to a running VM
AttachingConnector con = null;Iterator iter = Bootstrap.virtualMachineManager().allConnectors().iterator();while (iter.hasNext()) {
Connector x = (Connector) iter.next();if (x.name().equals("com.sun.jdi.SocketAttach"))
con = (AttachingConncetor)x;}Map args = con.defaultArguments();((Connector.Argument) args.get("port")).setValue("8000");try {
VirtualMachine vm = con.attach(args);...
} catch (Exception e) { /* error */ }
Starting the debuggee VMjava -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000 Test
Starting the debugger VMjava -cp "...\tools.jar" SampleDebugger
SampleDebugger.java
arbitrary port numberto which you want toconnect later
search for Attaching-Connector namedcom.sun.jdi.SocketAttach
23
Requests and events1. Debugger sends a request (MethodEntryRequest, BreakpointRequest, ...)2. Debuggee raises an event (MethodEntryEvent, BreakpointEvent, ...)
puts it into the debugger's event queue and suspends itself3. Debugger polls its event queue and reacts to the event4. Debugger asks the debuggee to resume
//--- post requestEventRequestManager reqManager = vm.eventRequestManager();MethodEntryRequest req = reqManager.createMethodEntryRequest();req.addClassFilter("Test");req.enable();//--- listen for eventsEventQueue q = vm.eventQueue();while (true) {
try {EventSet events = q.remove();EventIterator iter = events.eventIterator();while (iter.hasNext()) {
Event e = iter.nextEvent();if (e instanceof MethodEntryEvent) {
...vm.resume();
}} catch (Exception e) { break;}
}
24
Symbol information (overview)
Mirror
LocalVariableLocation Type
PrimitiveType ReferenceType
ArrayTypeClassTypeInterfaceType
BooleanTypeByteTypeCharTypeDoubleTypeFloatTypeIntegerTypeLongTypeShortType
TypeComponent
FieldMethod
Value
PrimitiveValue ObjectReference
BooleanValueByteValueCharValueDoubleValueFloatValueIntegerValueLongValueShortValue
ArrayReferenceClassObjectReferenceStringReferenceThreadReference
VirtualMachineStackFrame
Proxy for an element of the machine state
25
Symbol information (details)ThreadReferenceList<StackFrame> frames()StackFrame frame(int i)...
StackFrameList<LocalVariable> visibleVariables()LocalVariable visibleVariableByName(String name)Value getValue(LocalVariable v)void setValue(LocalVariable v, Value x)Location location()ThreadReference thread()...
*
Locationint lineNumber()long codeIndex()Method method()ReferenceType declaringType()...
LocalVariableString name()Type type()boolean isArgument()void isVisible(StackFrame f)...
ValueType type()
IntegerValueint value()
TypeString name()String signature()
ArrayTypeType componentType()
MethodList<LocalVariable> arguments()List<LocalVariable> variables()Type returnType()List<Location> allLineLocations()...
ClassTypeList<Field> fields()List<Method> methods()...
26
Example: printing the local variablesif (e instanceof MethodEntryEvent) {
MethodEntryEvent me = (MethodEntryEvent)e;System.out.println("call of " + me.method().toString());printVars(me.thread().frame(0));...
}
static void printVars(StackFrame frame) {try {
Iterator iter = frame.visibleVariables().iterator();while (iter.hasNext()) {
LocalVariable v = (LocalVariable)iter.next();Out.print(v.name() + ": " + v.type().name() + " = ");printValue(frame.getValue(v)); Out.println();
}} catch (Exception e) { e.printStackTrace(); }
}
static void printValue(Value val) {if (val instanceof IntegerValue) {
Out.print(((IntegerValue)val).value() + " ");} else if (val instanceof StringReference) {
Out.print(((StringReference)val).value() + " ");} else if (val instanceof ArrayReference) {
Iterator iter = ((ArrayReference)val).getValues().iterator();while (iter.hasNext()) {
printValue((Value)iter.next());}
} else...}
27
Example: setting a breakpoint
static void setBreakpoint(Method m, int line) {try {
List<Location> locs = m.locationsOfLine(line);if (locs.size() > 0) {
Location loc = locs.get(0);EventRequestManager reqManager = vm.eventRequestManager();BreakpointRequest req = reqManager.createBreakpointRequest(loc);req.enable();
}} catch (Exception e) { e.printStackTrace(); }
}
if (e instanceof BreakpointEvent) {BreakpointEvent be = (BreakpointEvent)e;System.out.println("breakpoint at " + be.location().lineNumber() + " in " +
be.location().method().toString());printVars(be.thread().frame(0));
}
Handling the breakpoint event
28
Example: single stepstatic void stepOver(ThreadReference thread) {
try {StepRequest req = reqManager.createStepRequest(thread,
StepRequest.STEP_LINE, StepRequest.STEP_OVER);req.addClassFilter("*Test"); // create step requests only in class Testreq.addCountFilter(1); // create step event after 1 stepreq.enable();vm.resume();
} catch (Exception e) { e.printStackTrace(); }}
Handling the step eventif (e instanceof StepEvent) {
StepEvent se = (StepEvent)e;Out.print("step halted in " + se.location().method().name() + " at ");printLocation(se.location());printVars(se.thread().frame(0));reqManager.deleteEventRequest(se.request());
}
static void printLocation(Location loc) {Out.println(loc.lineNumber() + ", " + loc.codeIndex());
}