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:

> On Feb 20, 2016, at 3:04 PM, Paul Peet via lldb-dev <lldb-dev@lists.llvm.org> 
> wrote:
> 
> 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

_______________________________________________
lldb-dev mailing list
lldb-dev@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-dev

Reply via email to