"Необъяснимый" дамп ядра
Я видел много свалков в моей жизни, но у меня есть я в тупике.
Context:
- многопоточная программа Linux/x86_64, работающая на кластере процессоров AMD Barcelona.
- код, который выходит из строя, выполняется много.
- запуск 1000 экземпляров программы (точно такой же оптимизированный двоичный файл) под нагрузкой вызывает 1-2 сбоя в час
- аварии происходят на разных машинах (но сами машины довольно идентичны)
- все сбои выглядят одинаково (одинаковый точный адрес, тот же стек вызовов)
Ниже приведены сведения об аварии:
Program terminated with signal 11, Segmentation fault.
#0 0x00000000017bd9fd in Foo()
(gdb) x/i $pc
=> 0x17bd9fd <_Z3Foov+349>: rex.RB orb $0x8d,(%r15)
(gdb) x/6i $pc-12
0x17bd9f1 <_Z3Foov+337>: mov (%rbx),%eax
0x17bd9f3 <_Z3Foov+339>: mov %rbx,%rdi
0x17bd9f6 <_Z3Foov+342>: callq *0x70(%rax)
0x17bd9f9 <_Z3Foov+345>: cmp %eax,%r12d
0x17bd9fc <_Z3Foov+348>: mov %eax,-0x80(%rbp)
0x17bd9ff <_Z3Foov+351>: jge 0x17bd97e <_Z3Foov+222>
Вы заметите, что авария произошла в середине инструкции на 0x17bd9fc
, которая после возврата из вызова 0x17bd9f6
к виртуальной функции.
Когда я просматриваю виртуальную таблицу, я вижу, что она не повреждена:
(gdb) x/a $rbx
0x2ab094951f80: 0x3f8c550 <_ZTI4Foo1+16>
(gdb) x/a 0x3f8c550+0x70
0x3f8c5c0 <_ZTI4Foo1+128>: 0x2d3d7b0 <_ZN4Foo13GetEv>
и что он указывает на эту тривиальную функцию (как и ожидалось, глядя на источник):
(gdb) disas 0x2d3d7b0
Dump of assembler code for function _ZN4Foo13GetEv:
0x0000000002d3d7b0 <+0>: push %rbp
0x0000000002d3d7b1 <+1>: mov 0x70(%rdi),%eax
0x0000000002d3d7b4 <+4>: mov %rsp,%rbp
0x0000000002d3d7b7 <+7>: leaveq
0x0000000002d3d7b8 <+8>: retq
End of assembler dump.
Далее, когда я смотрю на обратный адрес, который Foo1::Get()
должен был бы вернуться:
(gdb) x/a $rsp-8
0x2afa55602048: 0x17bd9f9 <_Z3Foov+345>
Я вижу, что он указывает на правильную инструкцию, поэтому, как бы во время возврата из Foo1::Get()
, какой-то гремлин пришел и увеличил %rip
на 4.
Возможные объяснения?
Ответы
Ответ 1
Итак, маловероятно, как это может показаться, мы, похоже, попали в реальную ошибку bona-fide CPU.
http://support.amd.com/us/Processor_TechDocs/41322_10h_Rev_Gd.pdf имеет erratum # 721:
721 процессор может неправильно корректировать указатель стека
Описание
Under a highly specific and detailed set of internal timing conditions,
the processor may incorrectly update the stack pointer after a long series
of push and/or near-call instructions, or a long series of pop
and/or near-return instructions. The processor must be in 64-bit mode for
this erratum to occur.
Потенциальный эффект для системы
The stack pointer value jumps by a value of approximately 1024, either in
the positive or negative direction.
This incorrect stack pointer causes unpredictable program or system behavior,
usually observed as a program exception or crash (for example, a #GP or #UD).
Ответ 2
Я когда-то видел "незаконный код операции" в середине инструкции. Я работал над портом Linux. Короче говоря, Linux вычитает из указателя инструкции, чтобы перезапустить syscall, и в моем случае это происходило дважды (если два сигнала поступали одновременно).
Итак, один из возможных виновников: ядро, играющее с указателем инструкции. В вашем случае может быть и другая причина.
Помните, что иногда процессор будет понимать данные, которые он обрабатывает как инструкцию, даже если это не должно быть. Таким образом, процессор, возможно, выполнил "инструкцию" в 0x17bd9fa, а затем перешел на 0x17bd9fd и затем сгенерировал незаконное исключение кода операции. (Я только что сделал это число, но эксперимент с дизассемблером может показать вам, где процессор мог "ввести" поток команд.)
Счастливая отладка!