Динамическое определение, где выполняется мошенническая инструкция AVX-512 - PullRequest
0 голосов
/ 24 августа 2018

У меня есть процесс, работающий на машине Intel, которая поддерживает AVX-512, но этот процесс напрямую не использует никаких инструкций AVX-512 (asm или intrinsics) и скомпилирован с -mno-avx512f, так что компилятор не вставьте любые инструкции AVX-512.

Тем не менее, он работает бесконечно на пониженной турбо частоте AVX. Без сомнения, где-то подкрадывается инструкция AVX-512 через библиотеку, (очень маловероятно) системный вызов или что-то в этом роде.

Вместо того, чтобы пытаться выполнить «бинарный поиск» там, откуда поступает инструкция AVX-512, есть ли какой-нибудь способ, которым я могу найти его немедленно, например, отловить такую ​​инструкцию?

ОС - это Ubuntu 16.04.

1 Ответ

0 голосов
/ 12 сентября 2018

Как предлагается в комментариях, вы можете искать все файлы ELF вашей системы и разбирать их, чтобы проверить, используют ли они инструкции AVX-512:

$ objdump -d /lib64/ld-linux-x86-64.so.2 | grep %zmm0
14922:       62 f1 fd 48 7f 44 24    vmovdqa64 %zmm0,0xc0(%rsp)
14a2d:       62 f1 fd 48 6f 44 24    vmovdqa64 0xc0(%rsp),%zmm0
14c2c:       62 f1 fd 48 7f 81 50    vmovdqa64 %zmm0,0x50(%rcx)
14ca0:       62 f1 fd 48 6f 84 24    vmovdqa64 0x50(%rsp),%zmm0

(Кстати, libc и ld.so включают инструкции AVX-512, они не те, которые вы ищете?)

Однако вы можете найти двоичный файл, который вы даже не выполняете, и пропускаете код, динамически несжатый и т. Д. ...

Если у вас есть сомнения по поводу процесса (поскольку perf сообщает CORE_POWER.LVL*_TURBO_LICENSE события), я предлагаю сгенерировать дамп ядра, если этот процесс, и дизассемблировать его (обратите внимание, что первая строка позволяет также выводить код):

$ echo 0xFF > /proc/<PID>/coredump_filter 
$ gdb --pid=<PID>
[...]
(gdb) gcore
Saved corefile core.19602
(gdb) quit
Detaching from program: ..., process ...
$ objdump -d core.19602 | grep %zmm0
7f73db8187cb:       62 f1 7c 48 10 06       vmovups (%rsi),%zmm0
7f73db818802:       62 f1 7c 48 11 07       vmovups %zmm0,(%rdi)
7f73db81883f:       62 f1 7c 48 10 06       vmovups (%rsi),%zmm0
[...]

Далее, вы можете легко написать небольшой скрипт на python для добавления точки останова (или точки трассировки) к каждой инструкции AVX-512. Что-то вроде

(gdb) python
>import os
>with os.popen('objdump -d core.19602 | grep %zmm0 | cut -f1 -d:') as pipe:
>    for line in pipe:
>         gdb.Breakpoint("*" + line)

Конечно, это создаст несколько сотен (или тысяч) точек останова. Однако накладные расходы на точку останова достаточно малы, чтобы GDB мог это поддерживать (я думаю, <1 КБ для каждой точки останова). </p>

Еще один способ - запустить ваш код на виртуальной машине. Особенно я предлагаю libvex. libvex используется для динамического инструментирования кода (утечка памяти, профилирование памяти и т. д.). libvex интерпретирует машинный код, переводит его в промежуточное представление и перекодирует машинный код для выполнения процессора. Самым известным проектом, использующим libvex, является valgrind (честно говоря, libvex является бэк-эндом valgrind).

Таким образом, вы можете запустить ваше приложение с libvex без каких-либо инструментариев:

$ valgrind --tool=none YOUR_APP

Теперь вам нужно написать инструмент для libvex, чтобы обнаружить использование AVX-512. Однако libVEX НЕ (пока) не поддерживает AVX-512. Таким образом, как только ему придется выполнить инструкцию AVX-512, он завершится неудачно с незаконной инструкцией.

$ valgrind --tool=none YOUR_APP
[...]   
vex amd64->IR: unhandled instruction bytes: 0x62 0xF1 0xFD 0x48 0x28 0x84 0x24 0x8 0x1 0x0
vex amd64->IR:   REX=0 REX.W=0 REX.R=0 REX.X=0 REX.B=0
vex amd64->IR:   VEX=0 VEX.L=0 VEX.nVVVV=0x0 ESC=NONE
vex amd64->IR:   PFX.66=0 PFX.F2=0 PFX.F3=0
==20061== valgrind: Unrecognised instruction at address 0x10913e.
==20061==    at 0x10913E: main (in ...)
==20061== Your program just tried to execute an instruction that Valgrind
==20061== did not recognise.  There are two possible reasons for this.
==20061== 1. Your program has a bug and erroneously jumped to a non-code
==20061==    location.  If you are running Memcheck and you just saw a
==20061==    warning about a bad jump, it's probably your program's fault.
==20061== 2. The instruction is legitimate but Valgrind doesn't handle it,
==20061==    i.e. it's Valgrind's fault.  If you think this is the case or
==20061==    you are not sure, please let us know and we'll try to fix it.
==20061== Either way, Valgrind will now raise a SIGILL signal which will
==20061== probably kill your program.
==20061== 
==20061== Process terminating with default action of signal 4 (SIGILL)
==20061==  Illegal opcode at address 0x10913E
==20061==    at 0x10913E: main (in ...)
==20061== 

Примечание: этот ответ был проверен с:

#include <immintrin.h>
int main(int argc, char *argv[]) {
    __m512d a, b, c;
    _mm512_fnmadd_pd(a, b, c);
}
...