The currently accepted answer includes a lot of file io and does only stop on breakpoints, but watchpoints, signals and possibly even the program end is ignored.
Using the python api this can be handled nicely:
- define a user command (with additional argument to say how fast to auto-step)
- optional: define a parameter for the default (both variants below)
- do the while loop within python, handle the "expected" keyboard interrupt of CTRL-C
- register a stopevent handler that checks for the stop reason and store the kind of step there
- adjust the while loop to stop for a "not simple" stop (breakpoint/watchpoint/signal/...)
The following code may be placed in a gdb-auto-step.py which can be made active with source gdb-auto-step.py whenever you want that (or include in the .gdbinit file to make it always available):
import gdb
import time
import traceback
class CmdAutoStep (gdb.Command):
    """Auto-Step through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (1-19, default 5)."""
    def __init__(self):
        print('Registering command auto-step')
        super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
        gdb.events.stop.connect(stop_handler_auto_step)
    def invoke(self, argument, from_tty):
        # sanity check - are we even active, prevents a spurious "no registers" exception
        try:
            gdb.newest_frame()
        except gdb.error:
            raise gdb.GdbError("The program is not being run.")
        # calculate sleep time
        if argument:
            if not argument.isdigit():
                raise gdb.GdbError("argument must be a digit, not " + argument)
            number = int(argument)
            if number == 0 or number > 19:
                raise gdb.GdbError("argument must be a digit between 1 and 19")   
        sleep_time = 3.0 / (1.4 ** number)
        # activate GDB scrolling, otherwise we'd auto-step only one page
        pagination = gdb.parameter("pagination")
        if pagination:
            gdb.execute("set pagination off", False, False)
        # recognize the kind of stop via stop_handler_auto_step 
        global last_stop_was_simple
        last_stop_was_simple = True
        # actual auto-stepping
        try:
            while last_stop_was_simple:
                gdb.execute("step")
                time.sleep(sleep_time)
        # we just quit the loop as requested
        # pass keyboard and user errors unchanged
        except (KeyboardInterrupt, gdb.GdbError):
            raise
        # that exception is unexpected, but we never know...
        except Exception:
            traceback.print_exc()
        # never leave without cleanup...
        finally:
            if pagination:
                gdb.execute("set pagination on", False, False)
def stop_handler_auto_step(event):
    # check the type of stop, the following is the common one after step/next,
    # a more complex one would be a subclass (for example breakpoint or signal)
    global last_stop_was_simple
    last_stop_was_simple = type(event) is gdb.StopEvent
CmdAutoStep()
To specify the default with a parameter (aka "the gdb way") add a new parameter via api and use it as follows (comes also with 0 = unlimited, handling process exit, an additional auto-next command and more class wrapping):
import gdb
import time
import traceback
class ParameterAutoSpeed (gdb.Parameter):
    """default speed for auto-step and auto-next commands (0-19, default 5)"""
    def __init__(self):
        self.set_doc = """Set speed for "auto-step" and "auto-next",
internally used to calculate sleep time between iterations of "step" / "next";
set "auto-speed 0" causes there to be no sleeping."""
        self.show_doc = "Speed value for auto-step/auto-next."
        super(ParameterAutoSpeed, self).__init__("auto-speed", gdb.COMMAND_RUNNING, gdb.PARAM_UINTEGER)
        self.value = 5
        self.backup = self.value
    def get_set_string (self):
        try:
            self.value = int(self.validate(self.value))
        except gdb.GdbError:
            self.value = int (self.backup)
            raise
        self.backup = self.value
        return ""
    def validate (self, argument):
        """validation for auto-step/auto-next speed"""
        try:
            speed = int(argument)
            if speed < 0 or speed > 19:
                raise ValueError()
        except (TypeError, ValueError):
            raise gdb.GdbError("speed argument must be an integer between 1 and 19, or 0")
        return speed
class CmdAutoNext (gdb.Command):
    """Auto-Next through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (see parameter "auto-speed")."""
    def __init__(self, worker):
        self.worker = worker
        super(CmdAutoNext, self).__init__("auto-next", gdb.COMMAND_RUNNING)
    def invoke(self, argument, from_tty):
        self.worker.invoke (argument)
class CmdAutoStep (gdb.Command):
    """Auto-Step through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (see parameter "auto-speed").
Note: To be usable you likely need several "skip" setup for not stepping into functions of
      the C library and other system libraries which may have no debug symbols available
      or are of no interest.
      You may press [CTRL]+[C] and execute "skip file", then "finish" to leave those."""
    def __init__(self, worker):
        self.worker = worker
        super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
    def invoke(self, argument, from_tty):
        self.worker.invoke (argument, do_step=True)
class AutoWorker ():
    def __init__(self):
        print('Registering parameter auto-speed and commands auto-step, auto-next')
        self.speed = ParameterAutoSpeed()
        CmdAutoStep(self)
        CmdAutoNext(self)
        gdb.events.stop.connect(self.stop_handler_auto)
        gdb.events.exited.connect(self.exit_handler_auto)
    def invoke(self, argument, do_step=False):
        # calculate sleep time
        if argument:
            number = self.speed.validate(argument) # raises an error if not valid
        else:
            number = self.speed.value
        if number:
            sleep_time = 3.0 / (1.4 ** number)
        else:
            sleep_time = 0
        # activate GDB scrolling, otherwise we'd auto-step/next only one page
        pagination = gdb.parameter("pagination")
        if pagination:
            gdb.execute("set pagination off", False, False)
        # recognize the kind of stop via stop_handler_auto_step
        self.last_stop_was_simple = True
        # actual auto-stepping
        try:
            while self.last_stop_was_simple:
                if do_step:
                    gdb.execute ("step")
                else:
                    gdb.execute ("next")
                time.sleep(sleep_time)
        # we just quit the loop as requested
        # pass keyboard and user errors unchanged
        except (KeyboardInterrupt, gdb.GdbError):
            raise
        # wrap GDB errors like "the program is not being run" to let them be
        # handled via default invoke error reporting, not as a python error
        except gdb.error as err:
            raise gdb.GdbError(err)
        # that exception is unexpected, but we never know...
        except Exception:
            traceback.print_exc()
        # never leave without cleanup...
        finally:
            if pagination:
                gdb.execute("set pagination on", False, False)
    def stop_handler_auto(self, event):
        # check the type of stop, the following is the common one after step/next,
        # a more complex one would be a subclass (for example breakpoint or signal)
        self.last_stop_was_simple = type(event) is gdb.StopEvent
    def exit_handler_auto(self, event):
        self.last_stop_was_simple = False
AutoWorker()