Debugging in Rider on Ubuntu Linux (gnome) causes editor or game to freeze on breakpoints, causing no mouse to appear

!!! Seamless solution - 5.3.2 tested !!!

I have found a decent workaround using .lldbinit script. It makes LLDB execute a function in the LinuxPlatform when a breakpoint is hit.

Simple (1 minute, global config)

This solution applies the hook globally (to all lldb runs). It might not be always desirable. The advanced one doesn’t. If you don’t use lldb anywhere except for unreal, this should suffice.

  1. You need to create a .lldbinit file in your home directory (~/.lldbinit) and paste the following:

    File: .lldbinit:
    target stop-hook add --one-liner "p ::UngrabAllInputImpl()"
    
  2. That’s it. The mouse should be released when a breakpoint is hit.

Explanation

The .lldbinit file is executed every time lldb is started (duh). It will hook to the stopping event (like a breakpoint) and call an unreal internal function - lldb command print (p) will execute the specified C++ code (with some caveats).

::UngrabAllInputImpl() is defined in one of the Unreal source files related to unix platform and should gracefully release the input without much side-effects.

Under the hood this calls some SDL functions, which in turn talk to X11 and do the actual job.

Advanced (5 minutes, Unreal-specific)

To address the globality issue I’ve prepared a slightly more complex solution to filter for Unreal presence, one that requires a custom python lldb extension.

  1. Create a python file ue_dbg_hook.py in a reasonable location. I’ve used my Rider installation directory; feel free to choose a different folder.

    File: ue_dbg_hook.py:
    import lldb
    
    class UnrealBreakpointHook:
        def __init__(self, target, extra_args, internal_dict):
            pass
    
        running_unreal = None
    
        @staticmethod
        def check_module_loaded(debugger, module_name):
            # Get the current target
            target = debugger.GetSelectedTarget()
    
            # Iterate over all modules in the target
            for module in target.module_iter():
                # Check if the module name matches
                if module.GetFileSpec().GetFilename() == module_name:
                    return True
    
            return False
    
        def handle_stop(self, exe_ctx, stream):
            tgt = exe_ctx.GetTarget()
            dbg = tgt.GetDebugger()
    
            if UnrealBreakpointHook.running_unreal is None:
                UnrealBreakpointHook.running_unreal = self.check_module_loaded(dbg, "UnrealEditor")
    
            if UnrealBreakpointHook.running_unreal:
                dbg.HandleCommand("p ::UngrabAllInputImpl()")
            return True
    
    
    def __lldb_init_module(debugger, internal_dict):
        print("ue_dbg_hook loaded")
    
  2. Change the .lldbinit slightly to use the python hook instead. Don’t forget to use the path where you actually placed the python script.

    NOTE: Notice that in the second line ue_dbg_hook module name is used - if for some reason you decided to name your python script differently, remember to apply this change here as well.

    File: .lldbinit:
    command script import "~/JetBrains Rider-2023.3.2/ue_dbg_hook.py"
    target stop-hook add -P ue_dbg_hook.UnrealBreakpointHook
    
  3. That’s it. The mouse should be released when a breakpoint is hit, but only in Unreal.

Disclaimers, notes

  • I haven’t tested this thoroughly yet, nor I have used this for an extended period of time - the solution appears to be “clean” in a sense that it doesn’t interfere with much of the engine/system, but I cannot give any guarantees for no side effects or derived issues.

  • I haven’t tested that with packaged builds either. I’m pretty sure the simple solution will work right away and for the advanced one a different module name to look for should be selected (UnrealEditor is the current choice).

  • The python hook in the advanced solution caches the unreal presence to reduce performance impact - it might not be desired when debugging low-level engine, but I honestly cannot think of any situation where one would need to break earlier. Just keep in mind that such feature exists in case some troubleshooting is in order.

  • It should work with different Unreal versions, provided the ::UngrabAllInputImpl exists. If for some reason this convenient function goes the dodo way, there is plenty of room to find a different route.

Cheers.