排查 “Illegal instruction (core dumped)”

现象

在一台服务器上无法运行程序 demo

./demo --server --config ../config/config.xml

提示直接core掉了:

Illegal instruction (core dumped)

通过 dmesg -T 检查系统日志可以看到:

[Fri Mar  3 17:52:34 2023] traps: demo[107300] trap invalid opcode ip:89813b3 sp:7fff24b82d98 error:0
[Fri Mar  3 17:52:34 2023]  in demo[400000+e49a000]

Illegal instruction

所谓 Illegal instruction (错误指令),表示处理器(CPU)收到了一条它不支持的指令:

大多数情况下,是因为程序采用了特定的优化编译,需要依赖一定(新型)的CPU指令集。例如,一些近期的tensorflow构建都是假设你的CPU支持 AVX 指令,而对于早于2011年的处理器或者低端x86 CPU(Pentium, Celeron, Atom)都不支持AVX指令集。 很巧,我这个案例就是这样

当出现 Illegal instruction (core dumped) 信息是,表示内核发送了一个 SIGILL ,也就是在 kernel/traps.c 中捕获 X86_TRAP_UD 定义的 CPU Trap 。那么,要如何找到这个程序触发的CPU指令是什么呢?

Debug Illegal instruction

使用 gdb 工具可以调试程序,找到导致非法指令,以及该指令从哪里来,或者找到程序所依赖的库:

  • 需要在能够重现错误的服务器上使用 gdb 调试才能发现导致 Illegal instruction 的指令

  • 如果没有重现环境,则可能需要采用反汇编来找出特定指令集

调试步骤

  • 首先用 gdb 加载程序:

    gdb demo
    

此时按照以下指令步骤:

run
bt   打印backtrace,这个指令需要debug symbols才能定位源码
display/i $pc  打印指令,也就是导致程序崩溃的最后一条指令

备注

编译程序时采用 debug 模式,可以添加 debugging symbols (调试符号表),这样使用 gdb 调试就更好了,能够通过汇编调试找到源代码,方便发现bug

调试信息如下:

通过 gdb 找出 Illegal instruction (core dumped) 指令
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-6.5.alios7
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from cheetah...Missing separate debuginfo for /home/admin/cheetah/9249a69/bin/cheetah
(no debugging symbols found)...done.
(gdb) run
Starting program: /home/admin/cheetah/9249a69/bin/cheetah
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Program received signal SIGILL, Illegal instruction.
0x00000000089813b3 in tensorflow::boosted_trees::learner::LearnerConfig::InitAsDefaultInstance() ()
Missing separate debuginfos, use: debuginfo-install cyrus-sasl-lib-2.1.26-19.2.1.alios7.x86_64 keyutils-libs-1.5.8-3.4.alios7.x86_64 krb5-libs-1.15.1-37.2.alios7.x86_64 libcom_err-1.43.5-8.alios7.x86_64 libcurl-7.29.0-59.1.alios7.1.x86_64 libidn-1.28-4.1.alios7.x86_64 libselinux-2.5-14.1.1.alios7.x86_64 libssh2-1.8.0-3.1.alios7.x86_64 ncurses-libs-5.9-13.20130511.1.alios7.x86_64 nspr-4.21.0-1.1.alios7.x86_64 nss-3.44.0-7.1.alios7.x86_64 nss-util-3.44.0-4.1.alios7.x86_64 openldap-2.4.44-5.alios7.x86_64 openssl-libs-1.0.2k-23.1.alios7.x86_64 pcre-8.32-15.1.alios7.x86_64 readline-6.2-9.1.alios7.x86_64 zlib-1.2.7-16.2.alios7.x86_64
(gdb) bt
#0  0x00000000089813b3 in tensorflow::boosted_trees::learner::LearnerConfig::InitAsDefaultInstance() ()
#1  0x000000000c531df8 in google::protobuf::internal::InitSCCImpl(google::protobuf::internal::SCCInfoBase*) ()
#2  0x000000000897ff49 in protobuf_tensorflow_2fcontrib_2fboosted_5ftrees_2fproto_2flearner_2eproto::AddDescriptorsImpl() ()
#3  0x00007ffff6e8620b in __pthread_once_slow (
    once_control=0xec65380 <protobuf_tensorflow_2fcontrib_2fboosted_5ftrees_2fproto_2flearner_2eproto::AddDescriptors()::once>,
    init_routine=0xc82e3d0 <__once_proxy>) at pthread_once.c:117
#4  0x000000000898017f in protobuf_tensorflow_2fcontrib_2fboosted_5ftrees_2fproto_2flearner_2eproto::AddDescriptors() ()
#5  0x000000000c8b1f9d in __libc_csu_init ()
#6  0x00007ffff63c64e5 in __libc_start_main (main=0x5062080 <main>, argc=1, argv=0x7fffffffdbf8, init=0xc8b1f50 <__libc_csu_init>, fini=<optimized out>,
    rtld_fini=<optimized out>, stack_end=0x7fffffffdbe8) at ../csu/libc-start.c:225
#7  0x0000000005166021 in _start ()
(gdb) display/i $pc
1: x/i $pc
=> 0x89813b3 <_ZN10tensorflow13boosted_trees7learner13LearnerConfig21InitAsDefaultInstanceEv+51>:	vinserti128 $0x1,%xmm1,%ymm0,%ymm0
(gdb)

通过 display/i $pc 我们可以看到:

=> 0x89813b3 <_ZN10tensorflow13boosted_trees7learner13LearnerConfig21InitAsDefaultInstanceEv+51>:    vinserti128 $0x1,%xmm1,%ymm0,%ymm0

这里就可以看到导致程序奔溃( Illegal instruction ) 的指令就是 vinserti128

从Intel开发手册,或者直接Google就能够找到 WikiPedia: Advanced Vector Extensions : vinserti128AVX2 指令集中的一条指令,以及对应的Intel/AMD CPU型号。

之所以遇到这个 Illegal instruction 是因为运行程序的那台老服务器恰好是只支持 AVX 的Intel Xeon E5-2650 v2 ;换了一台 Intel Xeon E5-2670 v3 就能够看到 AVX2 支持了(请检查 lscpu 输出的 Flags )

参考