Example usage

FZF is an awesome tool for fuzzy string searching. I use it for searching my Bash history with the Ctrl+R shortcut, and honestly I can’t live without it anymore. It’s so much better than readline’s default reverse search!

One day, I decided I’d try to get it working for searching my command history in GDB. I visited bing.com, and searched for “google.com”. Once I was at google.com, I ran a quick search for “GDB FZF history”, where I came across this issue on the FZF github repo:

Github issue

The author of FZF expressed that they weren’t aware of any way to get FZF working with GDB’s command history. This was a little puzzling for me. I knew that both Bash and GDB used readline, and I was using FZF with Bash… so why wouldn’t it work with GDB? I thought to myself.

So I set out to answer the question: What does Bash have, that GDB doesn’t have, that allows it to integrate FZF?

For this, I went digging through FZF’s install scripts for setting up history search in Bash. I quickly came across this:

bind -m emacs-standard -x '"\C-r": __fzf_history__'

Well, it turned out that Bash has a builtin command bind, that GDB does not have. The bind command in Bash allows one to bind keyboard shortcuts to execute custom shell commands. Although GDB does come with readline’s default keybinding mechanism, .inputrc, the default interface is too weak, and doesn’t support binding keyboard shortcuts to shell commands. The Bash developers had to implement the bind builtin in order to get around this limitation of .inputrc.

man bash was very helpful here.

Since I roughly knew what Bash was doing under the hood with bind -x (just by reading the docs), I figured a good place to start would be to poke around GDB’s source code and see if I could find any reference to history searching.

ag "history.*search|search.*history" readline/

Search result

That command displayed some useful results! Turned out rl_reverse_search_history was exactly the function I needed to mess with.

I replaced it with a function that prints “hello world” to test if pressing Ctrl+R would cause the message to print.

Hello world!

Ok, but what I really wanted was to call out to FZF with the command history.

I knew what I needed to do, just not how yet. I needed some mechanism to retrieve the history list. And I needed a mechanism to populate the readline prompt with the selected result from FZF.

I took a couple directions here. One was just static reverse engineering of GDB and the other was running a debug build of Bash under GDB and stopping it at critical moments to see what it was doing.

First, following the call chain from rl_reverse_search_history in GDB’s source code lead me to _rl_isearch_init.

rl_isearch_init

Which nicely answered one of my questions. I now had the history list.

Next I built a debug build of Bash with optimizations turned off so I could get a nice backtrace in GDB with source code mapping.

I was interested in understanding how Bash implements the bind -x builtin command. I probably could’ve done this by reading the code, but setting catch points in GDB is also great :)

strace showed that when I pressed Ctrl+R, Bash made the pipe and clone syscalls. These calls were responsible for calling out to FZF. Using the nifty GDB trick catch syscall clone, I was able to stop execution of Bash exactly at the point where the FZF was being called. Then by examining the backtrace with bt, I got a nice print out of the call stack at the moment the FZF process was being created. This led me to the C function bash_execute_unix_command. Exactly what I was looking for!

There were a few useful readline functions I discovered there.

parse_and_execute

At this point I knew everything I needed in order to write the patch and to get awesome FZF fuzziness in GDB.

Pseudo code:

parent_read, parent_write = pipe()
child_read, child_write = pipe()
 
if fork() == 0:
   dup2(child_read, 0)
   dup2(child_write, 1)
   close(parent_read)
   close(parent_write)
   execve("fzf")
 
parent_write.write(history_list())
 
result = parent_read.read()
 
set_current_prompt_line(result)

I posted the full implementation here github.com/filipkilibarda/gdb_fzf_patch.

I also posted a notice in github.com/junegunn/fzf/issues/1516, letting people know about it. If you google gdb fzf, that github issue is the first link, so anyone who is actually interested, will come across it.

It’s unlikely anyone will actually use it though… particularly because it requires building GDB from source. Sad :(