Cannot resume process using LLDB API

This problem is very strange, but I can’t resume execution using process.Continue().

If I write a simple program that create a debugger and a target, set a breakpoint on line 7 and launch a process. After stopping at line 7, I can use process.Continue() to resume it and next state is eStateExited. But if I apply the same strategy to a GUI program using Qt, it won’t resume execution. Regardless of how many times I click on the “Continue” button, the debuggee is always “stopped”.

You need to consume the process events that allow you to discover that a process is stopped.
You can see how to get process events by looking at the python example code:

svn cat http://llvm.org/svn/llvm-project/lldb/trunk/examples/python/process_events.py

Look at lines 174 - 245.

Basically your GUI should have a thread that is listening for process events so that it can update your GUI each time the process stops or resumes. In this thread you will also want to implement a stack of process control commands. Why? Lets say in your GUI you have F5 mapped to "step over" and F6 mapped to "step in". And the user hits F5 5 times really quickly and then hits F6. What we do in the Xcode IDE, is to stack up these process control events in a pending process control queue:

Process Control Queue:
[0] step over
[1] step over
[2] step over
[3] step over
[4] step over
[5] step in

Then as soon as we get a process event saying "eStateStopped" we know it is time to grab the next item in the process control queue. So we wait for eStateStopped, and we stay "is there anything on the process control queue? Yes? Ok, grab it off and since the process is stopped, you can now do the step.

If you just hook your GUI button presses and key pressed to functions that directly call the "process.Continue()" or "thread.StepOver()", you might be in the middle of actually doing the first "step over" but you are not yet stopped and if you call "thread.StepOver()" again without synchronizing with the state of the process you will get an error back saying that the process isn't stopped and your GUI won't do what the user wants...

We also go a step further by watching for process control events like "kill" and "halt" and we clear the current process control queue. So lets say you have a user that hits step 15 times really quickly and as they are watching the stepping go by they say "woops, I want to stop now", then when we get a halt or kill we clear any pending process control events in our queue so that it stops when the user requests it.

It is also a good idea to sync the fetching of local variables, stacks and other info in the place that responds to the process stopping. When you get a eStateStopped, it isn't a great idea to send a message out to your GUI objects to say "update your views anytime you want on any thread" unless you explicitly include the stop ID (an incrementing number you can get from the process via SBProcess::GetStopID()). Why? because if you hit step 10 times and you receive the eStateStopped from the first step, then send a message out to your GUI saying "update all views on any thread", those other threads might start asking for threads and frames and variables while the event thread has just said "hey, I have more process control events and I need to step since I have 9 more single steps to do. Now you have a case where your GUI is trying to update the variable and stack views while the process is running. So be sure to include some sort of synchronization, or do it all on one thread if possible.

Greg Clayton

Thank you for your detailed explanation.

My GUI front end is quite simple, and I set the SBDebugger to synchronous mode to make my life easier (debugger.SetAsync(false)). Besides, I only use single thread, so every time the API call returns, I can guarantee that I’m not in the middle of executing any other api function.

For example, the “Resume” button calls process.Continue(), when process.Continue() returns, we can guarantee that the debuggee is not active executing, so we call another function called handleDebugEvent to check its state, if the debugge is in eStateStopped, it grab the current value of local variables and print them in the gui.

Other buttons works similar, after doing their work, they call handleDebugEvent to check state and dispatch event.

Since I can’t find any tutorials on LLDB API besides generated docs, I can only come up with that strategy.

Take a look at:

svn cat http://llvm.org/svn/llvm-project/lldb/trunk/examples/python/process_events.py

This tells you how to handle events correctly. The problem with your approach is you won't be able to interrupt a running process because you will be stuck in process.Continue(), so you really do need to switch over to using an event loop as the python above will show you exactly how to do it.

Greg