1 / 14

Extending managed debugger with backstepping facilities

Extending managed debugger with backstepping facilities. Evgeny Vigdorchik, Alexey Nikitin St.Petersburg State University. Synopsis. Debugging is usually a most labor-intensive work Stepping back eliminates multiple debugger runs

vernon
Download Presentation

Extending managed debugger with backstepping facilities

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Extending managed debugger with backstepping facilities Evgeny Vigdorchik, Alexey Nikitin St.Petersburg State University

  2. Synopsis • Debugging is usually a most labor-intensive work • Stepping back eliminates multiple debugger runs • The conventional way is to create new process using Unix fork call, the old process stores the state of a program for the backstepping • More efficient checkpointing requires the modification of VM sources

  3. Watchpoints • Watchpoints are atomic events that the user is able to backstep. Each watchpoint has a unique counter value associated with it • No unified high-level language semantics for watchpoints is possible: e.g. associating source code line with watchpoint is absurd with following ML code List.fold (fun el sum -> el + sum) 0 li • Use CIL basic blocks instead • Calls to watchpoint code are emitted by FJIT

  4. Checkpoints • Registers are copied • Copy-on-write strategy is used for saving memory that is implemented using write protection • Memory regions that need to be saved are enumerated explicitly (i.e. heap, large heap, class loader heap, handle table…) • Stack is not write protected and is saved eagerly

  5. Saving registers • Watchpoints are implemented using FCALL helper calling conventions in Rotor • When frames are placed, the registers are captured • Execution may be simulated to provide register state after the exit from the helper: LazyMachState::getState() • Allows to restore to point after the call to respective watchpoint

  6. Saving stack • Stack is saved to common memory history, i.e. restored like any other memory region • The whole thread stack is copied • Stack bounds are found using stack walk routine static StackWalkAction StackBottomWalker( CrawlFrame *pCF, VOID* pData // Caller's private data ) { void * pTop = alloca(2); if(pCF && pCF->IsFrameless() && pCF->GetRegisterSet() && pCF-> GetRegisterSet()->pEbp) PStackBounds(pData)->pBottom = (const BYTE *)*pCF->GetRegisterSet()->pEbp; PStackBounds(pData)->pTop = (const BYTE *)&pTop; … }

  7. Saving GC Heap • Invoke minor GC during checkpoint : • No protection is needed for gen0 – more frequently used generation • Decreases overall memory usage, unlike in fork based debuggers • Survivers are promoted to gen1 where they live forever unless a major collection is forced by the user

  8. Debugging similarity issues • Due to periodic minor GC and no automatic major GC behavior of the program may change when debugging (finalization, weak refs, …) • The user may issue major GC explicitly from managed code at cost of higher memory usage • An option to perform major GC automatically may be given on startup

  9. cordbg architecture • Debugger resides out of debuggee process • Runtime controller thread is listening to debugger events • Restoration of context is done by RTC

  10. Restoring to checkpoint • Decrement watchpoint counter by desired value • Restore the context and run forward until user break is issued: void Debugger::EventCounterChange (Thread *thread){ CheckpointInterface * ci; LONG ulTime = thread->IncrementEventCount (); _ASSERTE (JanusGetCheckpointInterface(&ci) == S_OK); if (thread->IsTargetEventReached ()) { DebuggerExitFrame __def; SendUserBreakpoint (thread); __def.Pop(); thread->ResetTargetEventCount (); } else if (ulTime % ci->GetDensity () == 0) {/* checkpoint */} }

  11. Restoring to checkpoint (cont) • Need to recover from previous debugger event processing: • substitute return address to SuspendThread and resume execution • Win32 SuspendThread places return value on stack after thread is resumed • need to ensure new stack won’t be corrupted • know new stack bounds • grow the stack before suspension

  12. Results • The framework was implemented on top of rotor EE and managed debugger API • For computational programs with small basic blocks (computing digits of pi accumulating result in StringBuilder) the slowdown is 4 times compared to cordbg, almostall this time spent in watchpoint helper calls • For doing 225 checkpoints memory used increased only 10 times

  13. Future Work • The replay of the external calls, e.g. file I/O, networking etc. • Correct and deterministic replay of multithreaded programs

  14. That’s all! Q&A {ven,lex}@{tercom.ru, intellij.com}

More Related