Using LLDB C++ API for automated debugging sessions

Dear LLDB developers,

I am currently stuck while using the C++ API of LLDB. I am unable to correctly launch a process.
The code I have written looks as follows:

int main(int argc, char *argv){

LLDBSentry senty;
SBDebugger dbg(SBDebugger::Create());

const char *exeFilePath = “./target”;
const char *arch = “x86_64”;
const char *platform = “”;
const char *dependentLibs = “”;
SBError error;
SBTarget tgt = dbg.CreateTarget(exeFilePath, arch, platform, dependentLibs, error);

SBListener listen;
SBProcess proc = tgt.Launch(

listen,
nullptr,
nullptr,
nullptr,
“targetout.txt”,
nullptr,
“./”,
eLaunchFlagExec | eLaunchFlagDebug,
false,
error

);

SBThread thread = proc.GetSelectedThread(); // (1)


}

The complete code (usr.cpp) is added as an attachment to this email.
Its output is also added as a text file (stdout.txt) to this email.

The problem I have is, that thread.IsValid() returns null after line (1). Furthermore, the process says, that its state is eStateStopped, when asked via proc.IsStopped() it answers “false”, however.

The debugging target is a simple file that writes a hexadecimal number every 10us to stdout. I can see that the target is running, because targetout.txt is growing in size and its content is valid output from “target”.
Can you tell me what my mistake is?

Kind Regards
Jayvee

usr.cpp (4.34 KB)

stdout.txt (695 Bytes)

By default, the SB API's run in "asynchronous" mode, where calls that cause the target to run (launch, continue, etc) return when the target has started running, and then you have to wait on the listener that you registered with the process when you launched it to get notified of state changes. If you don't wait then you'll be asking questions of a process that only make sense when it's stopped, but it likely is still running...

Here's an example of handling events in async mode:

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

Otherwise, you can set the debugger into "synchronous" mode (using SBDebugger.SetAsync(False)) and then all the calls that cause the target to proceed will block till the target stops again.

Which mode you want to use really depends on whether you want to have control while the process is running or not.

Jim

You need to call SBDebugger::Initialize() before calling SBDebugger::Create().

Also, please use the SBLaunchInfo method for launching if possible where you create a SBLaunchInfo, then call tgt.Launch() with the instance.

Other than that, as Jim said, you need to setup an event loop if you don’t use synchronous mode where you wait for events and respond to events.

Greg

Thank you for your help.

What is the reason for using SBLaunchInfo over calling tgt.Launch with the launch configuration as a set of parameters? Furthermore I do not see a way to set stdout, stdin, and stderr for the launched process by using SBLaunchInfo.

I have a question regarding the event loop.
Is lldb thread-safe enough such that I can run that loop in another std::thread while simultaneously using other debugger functions?

Kind regards,
Jayvee

Thank you for your help.

What is the reason for using SBLaunchInfo over calling tgt.Launch with the launch configuration as a set of parameters?

The code is much clearer on what arguments mean what. In your current launch call, it is hard to know what all arguments mean and would be easy to put

SBProcess proc = tgt.Launch(

listen,
nullptr,
nullptr,
nullptr,
“targetout.txt”,
nullptr,
“./”,
eLaunchFlagExec | eLaunchFlagDebug,
false,
error

);

New code:

SBError error;

SBListener listen;
const char argv[] = { “arg1”, “arg2” };
SBLaunchInfo launch_info(argv);
launch_info.SetListener(listen);
launch_info.SetWorkingDirectory(getcwd());
launch_info.AddOpenFileAction(STDOUT_FILENO,
“**targetout.txt”, false, true);
launch_info.SetLaunchFlags(eLaunchFlagExec | eLaunchFlagDebug);
SBProcess proc = tgt.Launch(info, error);

You can see what each argument means above a bit more clearly. This also means you can create a SBLaunchInfo object before parsing the options, and using the options you could lazily populate the launch info object, then use it to launch.

Furthermore I do not see a way to set stdout, stdin, and stderr for the launched process by using SBLaunchInfo.

bool SBLaunchInfo::AddOpenFileAction(int fd, const char *path, bool read, bool write);

I have a question regarding the event loop.
Is lldb thread-safe enough such that I can run that loop in another std::thread while simultaneously using other debugger functions?

Yes you can. I would suggest being careful though as you wouldn’t want to try and display variables from a current frame while another thread is resuming the process. The code will work, but some calls might fail when the process does resume. We usually run a thread that listens for events, and when handling these events, it might do things like display all local variables in the IDE when a eStateStopped event is detected.

Some IDEs maintain a stack of work items to do. Say the user hits step 5 times really quickly by pressing F11 5 times, it is a good idea to make a queue of things to do for the debugger, and the stack now contains 5 “step over” commands. The IDE in the event loop will wait for the process to stop, check the command queue, and if there is another command in there, it could skip displaying the variables and updating all of the views and instead just pop one of the “step over” commands from the queue and do that. Once you stop and the queue is empty, you can show variables and stack traces, etc. Also, you might implement a timeout of .5 seconds after a stop before you show the variables in case the user continues or steps again really quickly. The user might also hit step a bunch of times and then click stop. Some IDEs flush the command queue to ensure we stop when the user hits the stop button to ensure we don’t keep stepping too long.

So just be careful to think about the logic you want. If you stop your process, it is a good idea to grab the variables and get their values on the same thread that is controlling the process so you don’t end up with your variable view trying to display variables as the process resumes. It is also a good idea to take the user input (step, continue, kill) and queue them up and consume those events in your event loop.

Let me know if that makes sense,

Greg Clayton

The main problem with trying to run a debug session multithreaded is coordinating execution control. Thread A gets a variable list and iterates over it to get child values. Thread B issues a "step" command. The API's thread A calls will either fail (because the target is running) or maybe fetch data that is now one step too new for some of the calls, if the step completed quickly. LLDB doesn't have any built-in mechanism for a thread to start a session which locks out other threads from running the target. So if you want to do your work on multiple threads you'll need to add something like that. And as Greg says, for many purposes you will need to have a cancellation mechanism as well, since people often want to "step really quickly" and don't want to wait for fresh values of the local variables to be fetched before the next step can start.

Jim

Thank you Jim and Greg, for your help. I will be careful with threads and I will also switch to using launch info.

Kind regards

Jayvee