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.

Sent from my iPad

> On Feb 23, 2016, at 2:40 PM, Greg Clayton via lldb-dev 
> <lldb-dev@lists.llvm.org> wrote:
> 
> 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
_______________________________________________
lldb-dev mailing list
lldb-dev@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-dev

Reply via email to