0

GDB version: GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1

I can't define a command with arbitrary number of arguments in gdb.

I have found some workaround, for example a command with up to three arguments support:

define test_1

if $argc == 1
    python args = "$arg0"
end
if $argc == 2
    python args = "$arg0 $arg1"
end
if $argc == 3
    python args = "$arg0 $arg1 $arg2"
end

python print(gdb.execute(args, to_string=True))

end

Usage

test_1 info breakpoints

I have tried another approach but it doesn't work, because gdb parses $arg<n> arguments before python execution, so "$arg0 $arg1 $arg2" in the args variable are not replaced by their values:

define test_1

python args = ' '.join(f"$arg{i}" for i in range($argc))
python print(gdb.execute(args, to_string=True))

end

Question: How to do it right? Python is not mandatory, other solutions (like pure gdb script) are allowable.

MiniMax
  • 983
  • 2
  • 8
  • 24
  • 1
    Have you tried [gdb python commands](https://sourceware.org/gdb/current/onlinedocs/gdb/Commands-In-Python.html)? – ssbssa Mar 11 '21 at 11:16
  • @ssbssa I have glanced this page several times, but I have not read it carefully... It turned out, this is exactly what I need. Thanks for the link. – MiniMax Mar 11 '21 at 21:13

1 Answers1

1

The easiest and most robust way to do this is to use GDB's Python extensions. Making a subclass of gdb.Command will give you access to the unsplit argument string, as the 2nd argument to invoke. In your example use case, this string can be passed as-is to gdb.execute.

You can also split it into arguments using gdb.string_to_argv. And if you’re going to pass just a part of the argument string to gdb.execute, you can use string.split, as in this example:

class Repeat(gdb.Command):
  """repeat count command - run the given command count times"""

  def __init__(self):
    super (Repeat, self).__init__ ("repeat", gdb.COMMAND_USER, gdb.COMPLETE_COMMAND)

  def invoke(self, argstr, from_tty):
      try:
        (count, command) = argstr.split(maxsplit = 1)
      except ValueError:
        raise Exception("Usage: repeat count command")
      if not count.isdigit():
        raise Exception("missing or garbled repeat count: " + count)
      for _ in range(0, int(count)):
        gdb.execute(command, from_tty = False)

Repeat()

Example:

(gdb) repeat 3 run $(expr $RANDOM % 20)
16! = 20922789888000
[Inferior 1 (process 259) exited normally]
9! = 362880
[Inferior 1 (process 262) exited normally]
13! = 6227020800
[Inferior 1 (process 265) exited normally]

If you can't use Python, a user-defined command can still concatenate its arguments, using eval, but it's not nearly as robust (see note at the end.)

(This requires GDB 9.1 or later; its C-like expression evaluator will concatenate adjacent string literals. )

define repeat
  if $argc < 2
    printf "Usage: repeat count command\n"
  else
    # first arg goes in $count, rest are concatenated and put in $command
    set $count=$arg0
    set $i=1
    set $command=""
    while $i < $argc
      eval "set $command = \"%s\" \"$arg%d\"", $command, $i
      # add one blank space after every arg except for the last
      if $i < $argc - 1
        eval "set $command = \"%s\" \" \"", $command
      end
      set $i++
    end
    printf "About to run `%s' %d times.\n", $command, $count
    set $i=0
    while $i < $count
      eval "%s", $command
      set $i++
    end
  end
end

Using eval to surround strings in double quotes when those strings contain double quotes is problematic, though.

(gdb) repeat 3 set $a="foo"
A syntax error in expression, near `foo""'.
Mark Plotnick
  • 9,598
  • 1
  • 24
  • 40
  • Thanks. This approach allowed me to do what I wanted - [Using less as gdb pager](https://stackoverflow.com/a/66590807/2913477). – MiniMax Mar 11 '21 at 21:35