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 <[email protected]> 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 <[email protected]>:
>> 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, [email protected] 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 <[email protected]> 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
>>>>> <[email protected]> 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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-dev