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



> On Feb 24, 2016, at 4:05 PM, Greg Clayton via lldb-dev 
> <lldb-dev@lists.llvm.org> wrote:
> 
> 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.
> 
> 
>> On Feb 24, 2016, at 3:45 PM, Paul Peet <paulpee...@gmail.com> wrote:
>> 
>> 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}
>> ----------
>> * thread #1: tid = 2535, 0x0000000000400830 main`_start, name =
>> 'main', stop reason = breakpoint 1.1
>>   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
>> 0x7ffaac001640 Event: broadcaster = 0xcdefa8 (lldb.process), type =
>> 0x00000001 (state-changed), data = { process = 0xcdef70 (pid = 2535),
>> state = stopped}
>> Stopped at breakpoint
>> ----------
>> Hello World
>> ----------
>> 
>> It is basically missing the "Stepped at step" part which means lldb
>> isn't emitting the PlanCompleteEvent.
>> 
>> Is it a problem on my system? (Linux?).
>> 
>> 2016-02-24 19:49 GMT+01:00 Greg Clayton <gclay...@apple.com>:
>>> 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
>>> 
>>> 
>>>> On Feb 23, 2016, at 7:10 PM, paulpee...@gmail.com wrote:
>>>> 
>>>> 
>>>> 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:
>>>> http://pastebin.com/0arNea9m
>>>> 
>>>> Sorry about pastebin, I am sending this from my phone and apparently it 
>>>> discards the code style while copy and paste.
>>>> 
>>>>> On 23 Feb 2016, at 23:40, Greg Clayton <gclay...@apple.com> 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