Jay-Pi
Jay-Pi

Reputation: 428

How to get the complete command history of gdb before exiting?

It is well documented in the manual, that gdb writes the command history after exiting (https://sourceware.org/gdb/onlinedocs/gdb/Command-History.html). However, I would like to obtain the complete command history without exiting to fill it on demand into a scratch buffer for editing or convenient re-running from within neovim with gdb -x FILE in another gdb instance.

What ways exist to get the info out from a running gdb instance?

Upvotes: 1

Views: 441

Answers (2)

Andrew
Andrew

Reputation: 4751

Since GDB 9 you can achieve what you want using the pipe command, which is documented here, along with some basic shell commands.

$ gdb -q 
(gdb) p 1
$1 = 1
(gdb) p 2
$2 = 2
(gdb) pipe show commands | sed -e 's/[[:space:]]\+[0-9]\+[[:space:]]\+//' | head -n -1 | tee /tmp/commands
p 1
p 2
(gdb) q
$ cat /tmp/commands
p 1
p 2

GDB's pipe command arranges to send the command output to a shell command. I pass the output through sed to strip off the command index, then through head to drop the last command, which is going to be the pipe command that is currently running, and finally, I use tee to send the output to a file.

It was pointed out in the comments that show commands only shows the last 10 commands. However, show commands also takes some arguments, these are documented here.

With this, we could add a new command to GDB using the Python API. Here's the contents of show-all-commands.py:

import re

class ShowAllCommands(gdb.Command):
  """
  show all-commands

  Show GDB's complete command history.  Unlike 'show commands' this
  lists everything in GDB's command history.
  """

  def __init__(self):
    super().__init__("show all-commands", gdb.COMMAND_OBSCURE)

  def invoke(self, arg, from_tty):
    start = 1
    last_command_number = 0
    all_commands = []
    get_more = True
    while get_more:
      found_new_line = False
      output = gdb.execute(f"show commands {start}", False, True)
      for line in output.splitlines():
        g = re.search(r'^\s+(\d+)', line).group(1)
        if not g:
          break
        if int(g) <= last_command_number:
          continue
        last_command_number = int(g)
        all_commands.append(line)
        found_new_line = True
      if not found_new_line:
        break
      start = "+"

    for line in all_commands:
      print(line)

ShowAllCommands()

Then in GDB (or from the .gdbinit file) we can do this:

source show-all-commands.py

You might need to add the path to that Python script in the source line so GDB can find it.

Then, just as before, we can use the new command:

pipe show all-commands | sed -e 's/[[:space:]]\+[0-9]\+[[:space:]]\+//' | head -n -1 | tee /tmp/commands

If you were feeling super keen then you could, of course, make the new Python command smarter, maybe it could write the commands directly to the output file, but I've left that as an exercise for the reader.

Upvotes: 2

Employed Russian
Employed Russian

Reputation: 213476

What ways exist to get the info out from a running gdb instance?

You can see the history with (gdb) show commands.
You could save the output into a file with:

(gdb) set logging file /tmp/gdb-history
(gdb) set logging on
(gdb) show commands
(gdb) set logging off

Unfortunately the history has sequence numbers, which you'll have to strip before you could use gdb -x /tmp/gdb-history.

Upvotes: 1

Related Questions