lldb

How can I script LLDB to update an external source code view?


In GDB I have a Python script which uses gdb.prompt_hook to send a command to an open vim session whenever the current file/line number changes. Effectively giving me a "live updating" source view. The script I use is publicly visible here.

I would like to port this script to LLDB, but I can't figure out an equivalent to gdb.prompt_hook. I.e. some way to execute a Python script whenever control is returned to LLDB when execution stops, OR the current frame is changed (e.g. executing f N).

Can someone suggest which APIs I might be able to use to implement this?

(NB: I am already aware of LLDB's gui view.)


Solution

  • Jim's answer is probably the "correct" way of doing this so all cases are covered. However, after spending quite some time struggling with LLDB's Python APIs for event handling I stumbled across LLDB's target stop-hook ... command. This can run a Python callback whenever execution pauses, and so covers most of what I want.

    Unfortunately, target stop-hook doesn't cover the need for updates as the user manually navigates the stack e.g. using commands like up, down, etc. To deal with this I re-implemented the commands I regularly use: up, down, and f.

    My implementation looks roughly like this:

    class LLDBStopHandler:
      def __init__(self, _target, _extra_args, _dict):
        pass
    
      def handle_stop(self, _exe_ctx, _stream):
        MY_STOP_HOOK()
        return True
    
    
    def lldb_f_command(debugger, command, result, dict):
      debugger.HandleCommand(f'frame select {args}')
      MY_STOP_HOOK()
    
    
    def lldb_down_command(debugger, command, result, dict):
      frame_id = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().GetFrameID()
      debugger.HandleCommand(f'frame select {frame_id - 1}')
      MY_STOP_HOOK()
    
    
    def lldb_up_command(debugger, command, result, dict):
      frame_id = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame().GetFrameID()
      debugger.HandleCommand(f'frame select {frame_id + 1}')
      MY_STOP_HOOK()
    
    
    def __lldb_init_module(debugger, _dict):
        debugger.HandleCommand(f'target stop-hook add -P {__name__}.LLDBStopHandler')
        debugger.HandleCommand(f'command script add -f {__name__}.lldb_f_command f')
        debugger.HandleCommand(f'command script add -f {__name__}.lldb_down_command down')
        debugger.HandleCommand(f'command script add -f {__name__}.lldb_up_command up')
    
    

    Note the frame command cannot be overridden directly as it's a built-in. I don't tend to use it though. Also the above is missing anything to cover switching threads, and probably some other things I haven't had to worry about yet.