This is documented behaviour of gdb, and it is (supposed to be) configurable.
When I compile your code with gcc 4.7.2 (-O3), I get the following assembly:
_main:
LFB1:
movl _i.2134(%rip), %eax
cmpl $10, %eax
jle L6
xorl %eax, %eax
ret
L6:
addl $1, %eax
pushq %rdx
LCFI0:
movl %eax, _i.2134(%rip)
xorl %eax, %eax
call _main ; <=== recursive call
popq %rcx
LCFI1:
movl $101, %esi
xorl %eax, %eax
leaq LC0(%rip), %rdi
jmp _printf
LFE1:
This refutes the hypothesis that the recursive call is optimized away.
Now, if I load the binary into gdb and set a breakpoint on main(), it gets hit repeatedly. When I examine the registers, %rsp is getting decremented with each call, so there are clearly stack frames associated with each main().
Nonetheless, bt only shows a single frame:
(gdb) bt
#0 0x0000000100000f50 in main ()
(In this case, I know there are three main() frames and not just one.)
I therefore conclude that this has something to do with gdb itself.
Upon further investigation, it turns out that this behaviour is documented:
Most programs have a standard user entry point—a place where system libraries and startup code transition into user code. For C this is main. When gdb finds the entry function in a backtrace it will terminate the backtrace, to avoid tracing into highly system-specific (and generally uninteresting) code.
When I set the following in gdb:
set backtrace past-main on
set backtrace past-entry on
it starts showing two main() frames. For some reason, it's still not going any deeper.