How to use the C++ API? No useful documentation?

Hello,

I am currently working on an IDE for C++ and I would like to integrate lldb as a debugger using the C++ API but it has been difficult for me to understand the architecture because there is no documentation available (except doxygen which isn’t helpful at all).
I am at the point understanding the Event system? How are Events created?
How can I use SBListener and SBBroadcaster? (What’s the function of SBBroadvaster).

My current code looks something like this:

SBListener listener;
SBProcess process = target.Launch(listener, args, env, nullptr, nullptr,
nullptr, “/home/cynecx/dev/helloWorld”,
0, true, error);

process.Continue();

StateType state = process.GetState(); // is stopped

SBEvent event;

while(true) {
if(listener.WaitForEvent(0xFFFFFFFF, event)) {
// This branch is never hit
SBStream stream;
event.GetDescription(stream);
std::cout << stream.GetData() << std::endl;
} else {
break;
}
}

It would help developers (IDE) a lot if there might be some tutorials/documentation on how to use the API.

Regards,
Paul

The Python API is pretty much a mirror of the C++ API's. The process_events.py example: http://llvm.org/svn/llvm-project/lldb/trunk/examples/python/process_events.py should give you a sense of how the broadcasters & listeners are used. Translating that from Python to C++ is quite straightforward. There is some documentation in the SB API header files which can be helpful, though that is not complete. There have been discussions on some of these issues on the mailing list previously that also might be of some help.

But you are right, we don't have a how-to manual for writing a debugger with the SB API's. It would be great if somebody who was trying to use them for this purpose would draft up such a thing as they were going. I am sure the folks on this list would be happy to help out and suggest better ways to do things, etc.

Jim

I need to spend some time writing this up, but until then here is some info.

We created a python script that uses the LLDB public API to grab async events so people can see how to do things:

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

If you look in here you will see the correct way to do things.

I will answer you questions inlined into your email below and then add some extra tips at the end:

Hello,

I am currently working on an IDE for C++ and I would like to integrate lldb as a debugger using the C++ API but it has been difficult for me to understand the architecture because there is no documentation available (except doxygen which isn't helpful at all).
I am at the point understanding the Event system? How are Events created?

You need to be able to listen for events from your debug session. In order to do this, SBBroadcaster objects can broadcast events as SBEvent objects. You need to listen for events using a SBListener. Each SBBroadcaster will broadcast events where each different kind of event is represented by one bit in a 32 bit uint32_t.

How can I use SBListener and SBBroadcaster? (What's the function of SBBroadvaster).

Yes you can. You might want to use a SBBroadcaster to send events to your main event loop in the debugger:

using namespace lldb;
SBBroadcaster gui_event_broadcaster("gui-events");

// Define the event bits we will use for gui_event_broadcaster
enum
{
  eGUIEventBitLaunch = (1u << 0),
  eGUIEventBitKill = (1u << 1),
  eGUIEventBitStepOver = (1u << 2),
  eGUIEventBitStepOut = (1u << 3),
  eGUIEventBitStepInto = (1u << 4),
  eGUIEventBitContinue = (1u << 5),
  eGUIEventBitHalt = (1u << 6),
  eGUIEventBitQuit = (1u << 7),
  eGUIEventAll = UINT32_MAX
};

SBListener run_loop_listener("run-loop-listener");
// Listen for any event from gui_event_broadcaster by listening to all event bits
run_loop_listener.StartListeningForEvents(gui_event_broadcaster, eGUIEventAll);

You can then run an event loop on a thread:

void
RunLoop()
{
  SBEvent event;
  bool done = false
  while (!done)
  {
    if (listener.WaitForEvent(UINT32_MAX, event))
    {
      const uint32_t event_type = event.GetType();
      if (event.BroadcasterMatchesRef(gui_event_broadcaster))
      {
        switch (event_type)
        {
        case eGUIEventBitLaunch:
  case eGUIEventBitKill:
  case eGUIEventBitQuit:
        }
      }
    }
  }
}

Then on another thread, you can broadcast events. Lets say the user clicked the "kill" button in your IDE, you could broadcast an event:

gui_event_broadcaster.BroadcastEventByType(eGUIEventBitKill);

Then the event loop would receive the event, as long as it is listening for these events. So you can use SBBroadcaster and SBListener yourself, but many objects (like SBTarget, SBProcess and SBThread) are already broadcasters and will broadcast events to you and you can sign up to listen to the events they send out. I would recommend starting with SBProcess, and you will see how to listen to events by looking at the python code.

My current code looks something like this:

SBListener listener;
SBProcess process = target.Launch(listener, args, env, nullptr, nullptr,
                                  nullptr, "/home/cynecx/dev/helloWorld",
                                  0, true, error);

process.Continue();

StateType state = process.GetState(); // is stopped

SBEvent event;

while(true) {
  if(listener.WaitForEvent(0xFFFFFFFF, event)) {
    // This branch is never hit
    SBStream stream;
    event.GetDescription(stream);
    std::cout << stream.GetData() << std::endl;
  } else {
    break;
  }
}

It would help developers (IDE) a lot if there might be some tutorials/documentation on how to use the API.

I agree. We will try to get this up on the web at some point in the near future.

Some more pointers:

- When launching, use a SBLaunchInfo:

const char *argv = { "/bin/ls", "-l", "-A", "-F", nullptr };
SBLaunchInfo launch_info(argv);
launch_info.SetWorkingDirectory("/tmp");
SBError error;
SBProcess process = target.Launch (launch_info, error);

This will allow you to fill in a SBLaunchInfo from your IDE and possibly keep it around for re-use on next launch.

- When attaching, use a SBAttachInfo. Same reason as launch info.

pid_t pid = 123;
SBAttachInfo attach_info(pid)

or

const bool wait_for = true;
SBAttachInfo attach_info("my_program", wait_for);

Then do:

SBProcess process = target.Attach (attach_info, error);

- I would recommend having one thread that is the main LLDB event loop and have this event loop also do process control. You can usually use the SBDebugger's listener as it will be hooked up by default to the process if you don't specify a listener:

SBListener listener = debugger.GetListener();
SBEvent event;
const uint32_t infinite_timeout = UINT32_MAX;
StateType process_state = eStateInvalid;
while (!done)
{
  if (listener.WaitForEvent(infinite_timeout, event))
  {
    if (SBProcess::EventIsProcessEvent (event))
    {
      process_state = SBProcess::GetStateFromEvent (event);
      switch (process_state)
      {
      case eStateStopped:
      case eStateRunning:
      case eStateExited:
      case eStateDetached:
      ....
      }
    }
    else if (event.BroadcasterMatchesRef(gui_event_broadcaster))
    {
      switch (event_type)
      {
        case eGUIEventBitLaunch:
  case eGUIEventBitKill:
    case eGUIEventBitStepOver:
    case eGUIEventBitStepOut:
    case eGUIEventBitStepInto:
        case eGUIEventBitContinue:
  case eGUIEventBitHalt:
  case eGUIEventBitQuit:
      }
    }
  }
}

Why do all process control on one thread? Because you don't want one thread telling the process to run and another telling the process to stop. So the easiest way to do this ensure only one thread actually controls the process. Since the event loop the place that will know if the process is stopped, it is the perfect place to also do the process control.

What we did in Xcode was we keep a stack of process control requests. So lets say we are currently stopped and the user presses the step over button 3 times really quickly. We might receive 10 eGUIEventBitStepOver events, and make a dequeue of process control requests:

eGUIEventBitStepOver
eGUIEventBitStepOver
eGUIEventBitStepOver

Now in the event handler for eGUIEventBitStepOver in your event loop you can do:

      switch (event_type)
      {
    case eGUIEventBitStepOver:
          if (process_state == eStateStopped)
            process.GetSelectedThread().StepOver();
          else
            process_control_dequeue.push_back(eGUIEventBitStepOver);
        break;
      }

So this means, if nothing else is going on, then you can issue the step over right away, else you need to wait until the process is stopped. Then when responding to the process stopped event:

      process_state = SBProcess::GetStateFromEvent (event);
      switch (process_state)
      {
      case eStateStopped:
        if (SBProcess::GetRestartedFromEvent(event) == false)
        {
          UpdateGUI(); // Update your IDE so we see where we just stopped
          if (!process_control_dequeue.empty())
          {
             // pop the next process control event off of the process_control_dequeue
             // and make the process resume/step/stop/continue
          }
        }
        break;

In Xcode we also did something a bit fancy: if the user ever presses the halt or kill button, we clear the process_control_dequeue. Why? Because most often someone is stepping faster than the debugger can keep up with and as the GUI is updating and showing them where their program is going, they say "yikes! Stop the program now". So any halt, kill or detach will clear any steps/continues that might have been saved up...

Hope this helps. For now, just keep asking questions on this list and we will help you out as much as possible.

Greg Clayton

Thank you very much for that detailed description on explaining the 'basics'.

I got it working so far but there is a small issue with the code I am currently running. When the main loop (there is only one) receives a breakpoint event and I am trying to get the thread description (After the breakpoint I issue the thread.StepOver command to the next line of code), the result is not 'deterministic' in terms of it is always showing me different current instruction location. Sometimes it's still on the same breakpoint line and sometimes it's on the proper next line. How is it that the thread is still changing even if the stopped state event got hit?

The code:

Sorry about pastebin, I am sending this from my phone and apparently it discards the code style while copy and paste.

This is a very useful read! I am also building a IDE debugger using lldb, and I found the initial attach/launch API and eventing system has a lot quirks. After getting to stopped state, querying info is relative trivial. Thanks again.

Here is the fixed code:

SBListener listener = debugger.GetListener();

SBLaunchInfo launch_info(args);
launch_info.SetEnvironmentEntries(env, true);
launch_info.SetWorkingDirectory("/home/dev/helloWorld");

SBProcess process = target.Launch(launch_info, error);

process.GetBroadcaster().AddListener(listener, SBProcess::eBroadcastBitStateChanged | SBProcess::eBroadcastBitSTDOUT);

while(true)
{
    SBEvent event;

    if(listener.WaitForEvent(6, event))
    {
        if(!event.IsValid())
        break;

        const uint32_t event_type = event.GetType();
    
        if (SBProcess::EventIsProcessEvent (event))
        {
            switch (event_type)
            {
            case SBProcess::eBroadcastBitStateChanged:
                {
                    const StateType state = SBProcess.GetStateFromEvent(event);
                    switch (state)
                    {
                    case eStateStopped:
                        {
                            const uint32_t num_threads = process.GetNumThreads();
                            SBThread thread = process.GetThreadAtIndex(0);
                            SBStream stream;
                            thread.GetStatus(stream);
                            event.GetDescription(stream);
                            std::cout << stream.GetData() << std::endl;
                            auto threadStopReason = thread.GetStopReason();
                            if (threadStopReason == eStopReasonBreakpoint)
                            {
                                uint64_t bpId = thread.GetStopReasonDataAtIndex(0);

                                if(bpId == static_cast<uint64_t>(bp1.GetID()))
                                {
                                    std::cout << "Stopped at breakpoint" << std::endl;
                                    thread.StepOver();
                                }
                            }
                            else if (threadStopReason == eStopReasonPlanComplete)
                            {
                                std::cout << "Stopped at step" << std::endl;
                            }
                        }
                        break;
                    }
                }
                break;
            case SBProcess::eBroadcastBitSTDOUT:
                {
                    char buffer[1024];
                    size_t num_bytes = 0;
                    do
                    {
                        num_bytes = process.GetSTDOUT(buffer, sizeof(buffer));
                        // Print exactly num_bytes bytes of the string data in buffer
                        if (num_bytes > 0)
                            printf("%*s", (int)num_bytes, buffer);
                    } while (num_bytes == sizeof(buffer);
                }
                break;
        }
    }
}

Your main issue was you were grabbing threads even when you weren't stopped, possibly when you were getting process STDOUT...

The main differences in the code above are:
1 - use SBLaunchInfo to launch
1 - Only try to grab stuff from the process and threads when you are stopped (only do process.GetThreadAtIndex(...) when you are stopped, not for every event. What would process return if the process is running for process.GetThreadAtIndex(0)? Sometimes an invalid thread, sometimes a valid thread that might be in the middle of a step. When you are stopped, you are guaranteed to get good results. So make sure you are stopped before you ask for threads, frames, variables, etc...
2 - Make sure the event is a process event with SBProcess::EventIsProcessEvent() as you might get other events if you use the debugger's listener (target events, thread events, etc)
3 - Switch off of the event type so you know what kind of event you are getting.

Let me know if this fixes things.

Greg Clayton

I am still getting "non determinism".

This is my current code:

SBListener listener = debugger.GetListener();
process.GetBroadcaster().AddListener(listener,
                                     SBProcess::eBroadcastBitStateChanged |
                                     SBProcess::eBroadcastBitSTDOUT);

if(!process.IsValid() || !error.Success()) {
  return 1;
}

error = process.Continue();
if(!error.Success()) {
  return 1;
}

while(true) {
  SBEvent event;

  if(listener.WaitForEvent(6, event)) {
    if(!event.IsValid()) {
      break;
    }

    const uint32_t event_type = event.GetType();

    if (!SBProcess::EventIsProcessEvent(event)) {
      continue;
    }

    if(event_type == SBProcess::eBroadcastBitStateChanged) {
      const StateType state = SBProcess::GetStateFromEvent(event);

      switch(state) {
        default:
          continue;
        case eStateStopped: {
          SBThread thread = process.GetThreadAtIndex(0);
          SBStream stream;

          thread.GetStatus(stream);
          event.GetDescription(stream);

          std::cout << stream.GetData() << std::endl;

          auto threadStopReason = thread.GetStopReason();
          if(threadStopReason == eStopReasonBreakpoint) {
            uint64_t bpId = thread.GetStopReasonDataAtIndex(0);

            if(bpId == static_cast<uint64_t>(bp1.GetID())) {
              std::cout << "Stopped at breakpoint" << std::endl;
              thread.StepOver();
            }
          } else if (threadStopReason == eStopReasonPlanComplete) {
            std::cout << "Stopped at step" << std::endl;
          }

          break;
        }
      }
    } else if (SBProcess::eBroadcastBitSTDOUT) {
      char buffer[1024];
      size_t num_bytes = 0;
      do
      {
        num_bytes = process.GetSTDOUT(buffer, sizeof(buffer));
        if (num_bytes > 0)
          printf("%*s", (int)num_bytes, buffer);
      } while (num_bytes == sizeof(buffer));
    }

    std::cout << "----------" << std::endl;
  } else {
    break;
  }
}

It worked at the first try but then I got some strange console output.
It's not stepping over the instruction anymore. Also the rip is
basically the same and it's not displaying proper source information
like it did at the first try.

Current output:

* thread #1: tid = 2535, 0x0000000000400830 main`_start, name = 'main'
    frame #0: 0x0000000000400830 main`_start
main`_start:
    0x400830 <+0>: xorl %ebp, %ebp
    0x400832 <+2>: movq %rdx, %r9
    0x400835 <+5>: popq %rsi
    0x400836 <+6>: movq %rsp, %rdx
0x7ffaac000930 Event: broadcaster = 0xcdefa8 (lldb.process), type =
0x00000001 (state-changed), data = { process = 0xcdef70 (pid = 2535),
state = stopped}

Not sure. First off, you don't need to do:

process.GetBroadcaster().AddListener(...)

The debugger's listener is already listening to all the events. If you don't specify a listener during launch the process will use the debugger's listener automatically. If you end up making your own process listener, you would actually tell your listener to listen to a broadcaster:

SBListener listener("process-listener");
launch_info.SetListener (listener);
SBProcess process = target.Launch(launch_info, error);

Everything else looks good.

What happens if you try running using the process_events.py python script? You might need to specify the PYTHONPATH environment variable to point to your lldb.so

% PYTHONPATH=/path/to/lldb/build/directory python lldb/examples/python/process_events.py --breakpoint main.c:12 -- ./a.out arg1 arg2

Does this work if you replace a.out with the path and args for your program? If it does, I would start porting the code from process_events.py and seeing what works and what doesn't. The process_events.py will call "process.Continue()" after it stops. Feel free to edit and play with this script.

Yes, the process listener is a little bit special.

When you are running a process in the debugger, the process is likely going to be stopping and starting for all sorts of reasons. For instance, stepping through a source line is actually going to:

step over the breakpoint at the start pc - if you had stopped due to a breakpoint
then set a breakpoint at the next branch instruction it sees and continue to there
then step over that breakpoint, and if that caused a step in, decide if it wants to stop there, and if not step out
etc...

But none of these stops are relevant at the user level. The thread plans have control over these implementation stops, and it would only cause trouble if code outside the thread plans tried to intervene here.

So lldb_private has the notion of a public state, and a private state for the process. While all the above is going on, the private state is tracking all the changes described above, but the public state will remain "running".

Now eventually, whatever you asked the process to do will end, and the private state will go to stopped, and the stepping machinery will decide that its plan is complete and we should report a public stop. So it sends an event to the process listener.

Another wrinkle is that we also want to keep the state you would get from SBProcess::GetState sync'ed with the event queue. It would be weird if one thread calling into lldb saw the process stopped, but the event queue thought the last event it saw was "running". So the public state won't change to stopped till somebody fetches the stopped event off the queue.

That means the process listener is privileged. You have to have one when you create a process and since it is driving the state changes, there can be only one. We didn't actually put in code to enforce this because I was planning to add the ability to have multiple listeners, but have one the "driving" listener, and the others passive observers. Or have some kind of control baton that could be passed from listener to listener. That way you could have one thread run the process for a while, then pass control to another listener. But up to now nobody has really needed this complexity, so I never got around to doing that.

Anyway, so in the current state of things, you just have to know that there can only be one process listener, and it has to be the one you pass in when you launch the process. And if you don't provide a listener, then we will set the Debugger's listener to be the process listener. The documentation for launch hints at this but we could make it more explicit.

Hope this helps,

Jim

I updated my code and removed the call to AddListener and as I can
tell nothing changed. It is still behaving "strange". It also didn't
work with the python example. I couldn't step over the next
instruction, so I am getting similar behavior like the c++ code.

The strange thing is that at the first try it worked (c++ code):

* thread #1: tid = 4502, 0x0000000000400953 main`main + 35 at
main.cpp:7, name = 'main', stop reason = breakpoint 1.1
    frame #0: 0x0000000000400953 main`main + 35 at main.cpp:7
   4
   5 int main() {
   6
-> 7 std::cout << "Hello World" << std::endl;
   8
   9 int i = 0;
   10 std::cin >> i;
0x7f022c000930 Event: broadcaster = 0x56348a831fa8 (lldb.process),
type = 0x00000001 (state-changed), data = { process = 0x56348a831f70
(pid = 4502), state = stopped}

Hey,

Just to let you know that I think I made some progress in determine the problem.
I've basically setup an vm (archlinux, linux 4.4, lldb 3.7.1) and
tried the code on it. To my surprise it gave me proper output without
non-determinism. YEY.
I still don't have any idea why it's not working on my host system. I
might try testing linux 4.4 like I did on the vm.

Do you have any idea/suspicion why it might not work on my system. (I
can provide additional information if needed).

I don't. Maybe some of the linux experts out there might be able to help you. Are are working with top of tree LLDB sources right? You might post the exact linux setup you have in case that might allow people to help you out...

Greg

Sorry to bring this up again, but I am not sure if this is really a
linux kernel issue anymore, see the following code:

if(event_type == SBProcess::eBroadcastBitStateChanged) {
  const StateType state = SBProcess::GetStateFromEvent(event);

  switch(state) {
    default:
      continue;
    case eStateStopped: {
      static bool runOnce = false;
      if(runOnce == false) {
        sleep(1); // sleep a second
        runOnce = true;
      }

      SBThread thread = process.GetThreadAtIndex(0);
      SBStream stream;

As I said before that strangely it worked on the vm with kernel 4.4 so
I tried using kernel 4.4 and it still didn't work.

Next thing I did was enabling lldb's logging and what I noticed was
that when the first thread plan is being made it had wrong instruction
information that's probably because the thread was still running. So
what I tried was this runOnce with a sleep(1) and the result was as I
expected, the thread plan contained the correct instruction
information and the following breakpoints and step-overs were
correctly made.

Any chance that this issue lies deep in lldb? Would the lldb log help
to trace back the issue?

Hi Paul,

I haven't followed this discussion from the start, and I am now having
trouble understanding what is the issue at hand here. Could you just
briefly repeat what is the problem, and maybe send the code for
reproducing the problem again? Maybe I'll be able to help...

pl

Hi Pavel,

This is the code:

int main() {
  using namespace lldb;

  SBDebugger::Initialize();
  SBDebugger debugger = SBDebugger::Create(true);

  if(!debugger.IsValid()) {
    return 1;
  }

  SBTarget target = debugger.CreateTarget("/home/cynecx/dev/helloWorld/main");

  if(!target.IsValid()) {
    return 1;
  }

  SBBreakpoint bp1 = target.BreakpointCreateByLocation(
    "/home/cynecx/dev/helloWorld/main.cpp", 7);

  if(!bp1.IsValid()) {
    return 1;
  }

  bp1.SetEnabled(true);

  const char* args = { "/home/cynecx/dev/helloWorld/main", 0 };
  const char* env = { 0 };

  SBLaunchInfo launch_info(args);
  launch_info.SetEnvironmentEntries(env, true);
  launch_info.SetWorkingDirectory("/home/cynecx/dev/helloWorld");
  launch_info.SetLaunchFlags(eLaunchFlagStopAtEntry);

  SBError error;
  SBProcess process = target.Launch(launch_info, error);

  if(!process.IsValid() || !error.Success()) {
    return 1;
  }

  error = process.Continue();
  if(!error.Success()) {
    return 1;
  }

  while(true) {
    SBEvent event;
    SBListener listener = debugger.GetListener();

    if(listener.WaitForEvent(6, event)) {
      if(!event.IsValid()) {
        break;
      }

      const uint32_t event_type = event.GetType();

      if (!SBProcess::EventIsProcessEvent(event)) {
        continue;
      }

      if(event_type == SBProcess::eBroadcastBitStateChanged) {
        const StateType state = SBProcess::GetStateFromEvent(event);

        switch(state) {
          default:
            continue;
          case eStateStopped: {
// static bool runOnce = false;
//
// if(runOnce == false) {
// sleep(1);
// runOnce = true;
// }

            SBThread thread = process.GetThreadAtIndex(0);
            SBStream stream;

            thread.GetStatus(stream);
            event.GetDescription(stream);

            std::cout << stream.GetData() << std::endl;

            auto threadStopReason = thread.GetStopReason();
            if(threadStopReason == eStopReasonBreakpoint) {
              uint64_t bpId = thread.GetStopReasonDataAtIndex(0);

              if(bpId == static_cast<uint64_t>(bp1.GetID())) {
                std::cout << "Stopped at breakpoint" << std::endl;
                thread.StepOver();
              }
            } else if (threadStopReason == eStopReasonPlanComplete) {
              std::cout << "Stopped at step" << std::endl;
            }

            break;
          }
        }
      } else if (SBProcess::eBroadcastBitSTDOUT) {
        char buffer[1024];
        size_t num_bytes = 0;
        do
        {
          num_bytes = process.GetSTDOUT(buffer, sizeof(buffer));
          if (num_bytes > 0)
            printf("%*s", (int)num_bytes, buffer);
        } while (num_bytes == sizeof(buffer));
      }

      std::cout << "----------" << std::endl;
    } else {
      break;
    }
  }

  SBDebugger::Terminate();

  return 0;
}

main.cpp:

#include <cstddef>
#include <cstdint>
#include <iostream>

int main() {

  std::cout << "Hello World" << std::endl;

  int i = 0;
  std::cin >> i;

  if (i == 1) {
    return 1;
  }

  return 0;
}

So the problem is that I am not getting correct thread information
when receiving an eStateStopped event. It kinda seems that when
"thread.GetStatus(stream);", the thread hasn't stopped yet so I always
getting non-determine thread information.
I found out that on my linux vm, it works properly so I thought it
might be an issue with the kernel and stuff but I kinda found a
workaround by simply putting a sleep(1) before querying the thread
information. As expected it gave me correct thread information, also
the step-over worked properly because the thread plan used correct
line information.

To compare both outputs:

Correct one (with sleep(1)):

* thread #1: tid = 5862, 0x0000000000400953 main`main + 35 at
main.cpp:7, name = 'main', stop reason = breakpoint 1.1
    frame #0: 0x0000000000400953 main`main + 35 at main.cpp:7
   4
   5 int main() {
   6
-> 7 std::cout << "Hello World" << std::endl;
   8
   9 int i = 0;
   10 std::cin >> i;
0x7f1dc4000930 Event: broadcaster = 0x55da93268068 (lldb.process),
type = 0x00000001 (state-changed), data = { process = 0x55da93268030
(pid = 5862), state = stopped}

Thanks for the explanation. I'll look this over tomorrow.

cheers,
pl

Hi Paul,

I believe you are experiencing the same problem as a couple of other
people before you (see
<http://lists.llvm.org/pipermail/lldb-dev/2016-January/009293.html&gt;,
<http://lists.llvm.org/pipermail/lldb-dev/2016-January/009453.html&gt;\).
Basically, you need to ignore the events with the "restarted" flag by
checking the GetRestartedFromEvent value for Stopped events. Linux
broadcasts this event on initial start, but other targets don't. This
is not a particularly well documented feature, and we would welcome
any improvements to the documentation. :slight_smile:

Let me know if you still have problems after adding the check for the
restarted flag.

cheers,
pl

Hi Pavel,

First of all, thank you for looking into this. I really appreciate it.
Secondly, the check with GetRestartedFromEvent did the trick.
I am finally getting correct information. Thank You very much.

About the documentation, I really think it can be much better and I
would also help in this case but my english isn't my first language as
I am also not good at expressing the intention of every lldb feature
because obviously I am also just a beginner.

Although I think it would be helpful for other users, that if there
would have been a valid C++ debugging example like my issue
reproduction code.

:slight_smile:

~Paul

Hi Pavel,

First of all, thank you for looking into this. I really appreciate it.
Secondly, the check with GetRestartedFromEvent did the trick.
I am finally getting correct information. Thank You very much.

I would like to note that the initial Python implementation I pointed you at does check the restarted bit. Glad this fixes your issues.

About the documentation, I really think it can be much better and I
would also help in this case but my english isn't my first language as
I am also not good at expressing the intention of every lldb feature
because obviously I am also just a beginner.

Although I think it would be helpful for other users, that if there
would have been a valid C++ debugging example like my issue
reproduction code.

We, and by we I mean Jim Ingham and myself, need to document this better. If you have a summary of the issues you ran into and what wasn't clear about the APIs, please let us know so we don't miss something when we write up the documentation.

Greg Clayton

I was planning to take a stab at writing documentation, since I just finished my own C++ app on top of lldb. (Code Medic)

Attached is my attempt to add docs to SBAddress. Am I on the right track? If so, I’ll tackle additional files.

Thanks,
John

SBAddress.h (9.49 KB)