10

My software uses the SIGUSR2 signal and I am using LLDB (under Xcode 4.6.2) as my debugger. I would like to disable LLDB from stoping at SIGUSR2 and have been doing so using the command:

process handle --pass true --stop false --notify true SIGUSR2

I am looking for a way to have LLDB always execute this command at startup. I have looked into adding something along the lines of settings append target.process.extra-startup-command process in my .lldbinit, but while this changes the value of the target.process.extra-startup-command setting (as evidenced by the settings show command), I am uncertain if/how I can use this setting to always execute the process handle command to disable the SIGUSR2 signal.

I am aware of the "solution" posted here: Permanently configuring LLDB (in Xcode 4.3.2) not to stop on signals. I am looking however for a more elegant solution, if one exists.

Daniel Heilper
  • 1,182
  • 2
  • 17
  • 34

3 Answers3

6

At present the suggestion of doing this in a breakpoint command on main is the most elegant solution available.

gdb had this view of the world where all processes, no matter what system they might be on, magically responded to UNIX signals. So it made sense to say what was going to happen when the process got a SIGINT, say, before you even had a process. In lldb, the process, when it gets created, will tell us what its signals are and their default behaviors. That's lovely, except it means now there is no natural place to store configuration options for signal behaviors before you have a process. This is just something that has to get added.

The ability to trigger off of "process life-cycle events", not just "process launch" but "process exit" and "shared library load" etc would be a great addition. This feature is something it would be great to file an enhancement request (http://bugreport.apple.com/) for, since bugs like that act as votes for features.

BTW, target.process.extra-startup-command does something entirely different. It allows you to prepend some commands to the sequence lldb sends to its debug agent (e.g. debugserver) before we start running. Its main use is to turn on more debugserver logging.

Jason Molenda
  • 14,835
  • 1
  • 59
  • 61
Jim Ingham
  • 25,260
  • 2
  • 55
  • 63
5

Since I regularly return to this question in order to configure this, I finally ended up creating a small script to do it automatically:

import lldb
import threading

class ProcessEventListener(threading.Thread):
    def __init__(self, debugger):
        super(ProcessEventListener, self).__init__()
        self._listener = debugger.GetListener()
        self._debugger = debugger
        self._interpreter = debugger.GetCommandInterpreter()
        self._handled = set()

    def _suppress_signals(self, process):
        signals = process.GetUnixSignals()
        signals.SetShouldStop(11, False)

    def run(self):
        while True:
            event = lldb.SBEvent()
            if not self._listener.PeekAtNextEvent(event):
                continue                
            process = self._interpreter.GetProcess()
            if process and not process.GetUniqueID() in self._handled:
                self._suppress_signals(process)
                self._handled.add(process.GetUniqueID())

def __lldb_init_module(debugger, *rest):
    listener_thread = ProcessEventListener(debugger)
    listener_thread.start()

To use, put it in something like ignore_signals.py and reference it from .lldbinit:

command script import ~/ignore_signals.py

I suspect this can be improved further, so I've put it up on GitHub as well in case anyone would like to contribute.

rwestberg
  • 81
  • 1
  • 3
  • The downside of this script is that one thread in LLDB will be at 100% CPU at all times. This is because `PeekAtNextEvent` doesn't wait for event. Unfortunately, this can't be improved, because LLDB itself reads events from the same `debugger.GetListener()`, so script and LLDB are competing for events. If script uses a waiting function, then it also removes event from queue. I tried putting it back, but then it seems that events are returned out of order and CTRL+C will sometimes fail to work. – Codeguard Dec 01 '21 at 00:52
  • I tried to convince LLDB to put events to a different `SBListener`, but after a few hours of going through LLDB sources, I didn't find a way to do that. – Codeguard Dec 01 '21 at 00:54
  • I finally managed to compose a script that doesn't hog 100% CPU (in the other answer) – Codeguard Dec 11 '21 at 04:38
0

Inspired by rwestberg's answer, I was able to compose a similar script that doesn't consume 100% CPU. In rwestberg's script, that was necessary because script tried to read event from listener before lldb itself removes it from listener permanently.

Here's the improved script:

import lldb
import threading
import time

class UnixSignalDisabler(threading.Thread):
    def __init__(self, debugger):
        super(UnixSignalDisabler, self).__init__()
        self._debugger = debugger
        self._handled = set()

    def _suppress_signals(self, process):
        print("UnixSignalDisabler: disabling SIGBUS, SIGSEGV in process #" + str(process.GetUniqueID()))
        signals = process.GetUnixSignals()
        signals.SetShouldStop(10, False) # SIGBUS
        signals.SetShouldStop(11, False) # SIGSEGV

    def run(self):
        while True:
            for target in self._debugger:
                if target:
                    process = target.GetProcess()
                    if process and not process.GetUniqueID() in self._handled:
                        self._suppress_signals(process)
                        self._handled.add(process.GetUniqueID())
            # Don't hog CPU
            time.sleep(0.03)

def __lldb_init_module(debugger, *rest):
    # Can't use 'debugger' reference directly because it gets deleted after calling '__lldb_init_module'
    debugger = lldb.SBDebugger.FindDebuggerWithID(debugger.GetID())
    listener_thread = UnixSignalDisabler(debugger)
    listener_thread.start()

To use, put it in something like ignore_signals.py and reference it from .lldbinit:

command script import ~/ignore_signals.py
Codeguard
  • 7,787
  • 2
  • 38
  • 41