为什么启用尾部优化时回溯可能不正确
Why the backtrace may be not correct when enable tail optimization
根据 https://www.gnu.org/software/libc/manual/html_node/Backtraces.html:
Note that certain compiler optimizations may interfere with obtaining a valid backtrace. Function inlining causes the inlined function to not have a stack frame; tail call optimization replaces one stack frame with another; frame pointer elimination will stop backtrace from interpreting the stack contents correctly.
我想知道为什么在启用尾部优化时会发生这种情况以及如何避免这种情况。
I want to know why it should happen when enabling tail optimization
尾调用优化会发生这种情况,因为您的递归函数在编译器完成后将不会递归,或者尾递归函数将从调用堆栈中完全丢失。
示例:
// t.c
int foo() { return 42; }
int bar() { return foo(); }
int main() { return bar(); }
gcc -O2 -fno-inline t.c && gdb -q ./a.out
(gdb) b foo
Breakpoint 1 at 0x1140
(gdb) run
Starting program: /tmp/a.out
Breakpoint 1, 0x0000555555555140 in foo ()
(gdb) bt
#0 0x0000555555555140 in foo ()
#1 0x00007ffff7dfad0a in __libc_start_main (main=0x555555555040 <main>, argc=1, argv=0x7fffffffdc08, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdbf8) at ../csu/libc-start.c:308
#2 0x000055555555507a in _start ()
main()
和bar()
去哪儿了?我们知道main()
一定还在栈上。
他们进行了尾调用优化:
(gdb) disas main
Dump of assembler code for function main:
0x0000555555555040 <+0>: xor %eax,%eax
0x0000555555555042 <+2>: jmpq 0x555555555150 <bar>
End of assembler dump.
(gdb) disas bar
Dump of assembler code for function bar:
0x0000555555555150 <+0>: xor %eax,%eax
0x0000555555555152 <+2>: jmp 0x555555555140 <foo>
End of assembler dump.
how to avoid it.
避免它的唯一方法是禁用尾调用优化(使用 -fno-optimize-sibling-calls
):
gcc -O2 -fno-inline t.c -fno-optimize-sibling-calls && gdb -q ./a.out
(gdb) b foo
Breakpoint 1 at 0x1140
(gdb) run
Starting program: /tmp/a.out
Breakpoint 1, 0x0000555555555140 in foo ()
(gdb) bt
#0 0x0000555555555140 in foo ()
#1 0x0000555555555157 in bar ()
#2 0x0000555555555047 in main ()
根据 https://www.gnu.org/software/libc/manual/html_node/Backtraces.html:
Note that certain compiler optimizations may interfere with obtaining a valid backtrace. Function inlining causes the inlined function to not have a stack frame; tail call optimization replaces one stack frame with another; frame pointer elimination will stop backtrace from interpreting the stack contents correctly.
我想知道为什么在启用尾部优化时会发生这种情况以及如何避免这种情况。
I want to know why it should happen when enabling tail optimization
尾调用优化会发生这种情况,因为您的递归函数在编译器完成后将不会递归,或者尾递归函数将从调用堆栈中完全丢失。
示例:
// t.c
int foo() { return 42; }
int bar() { return foo(); }
int main() { return bar(); }
gcc -O2 -fno-inline t.c && gdb -q ./a.out
(gdb) b foo
Breakpoint 1 at 0x1140
(gdb) run
Starting program: /tmp/a.out
Breakpoint 1, 0x0000555555555140 in foo ()
(gdb) bt
#0 0x0000555555555140 in foo ()
#1 0x00007ffff7dfad0a in __libc_start_main (main=0x555555555040 <main>, argc=1, argv=0x7fffffffdc08, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdbf8) at ../csu/libc-start.c:308
#2 0x000055555555507a in _start ()
main()
和bar()
去哪儿了?我们知道main()
一定还在栈上。
他们进行了尾调用优化:
(gdb) disas main
Dump of assembler code for function main:
0x0000555555555040 <+0>: xor %eax,%eax
0x0000555555555042 <+2>: jmpq 0x555555555150 <bar>
End of assembler dump.
(gdb) disas bar
Dump of assembler code for function bar:
0x0000555555555150 <+0>: xor %eax,%eax
0x0000555555555152 <+2>: jmp 0x555555555140 <foo>
End of assembler dump.
how to avoid it.
避免它的唯一方法是禁用尾调用优化(使用 -fno-optimize-sibling-calls
):
gcc -O2 -fno-inline t.c -fno-optimize-sibling-calls && gdb -q ./a.out
(gdb) b foo
Breakpoint 1 at 0x1140
(gdb) run
Starting program: /tmp/a.out
Breakpoint 1, 0x0000555555555140 in foo ()
(gdb) bt
#0 0x0000555555555140 in foo ()
#1 0x0000555555555157 in bar ()
#2 0x0000555555555047 in main ()