Динамическое определение места выполнения несанкционированной инструкции AVX-512

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

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

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

ОС - Ubuntu 16.04.


person BeeOnRope    schedule 24.08.2018    source источник
comment
-mno-avx512f должен автоматически отключать avx512cd / pf / er / etc, верно? Вы пробовали использовать objdump -d grep для своего исполняемого файла и его библиотечных зависимостей?   -  person that other guy    schedule 24.08.2018
comment
Вы могли бы, возможно, попросить ядро ​​очистить бит регистра управления, который включает AVX512, и обещает, что полное состояние ZMM будет сохранено / восстановлено при переключении контекста. Но вы уверены в том, что устойчивые 256-битные FMA или что-то еще не снижают частоту до той же частоты, что и случайные 512-битные инструкции? Полагаю, вы исключили код в другом процессе, который замедляет работу ядра, на котором вы работаете?   -  person Peter Cordes    schedule 24.08.2018
comment
Ubuntu 16.04 достаточно стара, чтобы я не ожидал использования ZMM в функциях glibc memset / memcpy / strchr. Тем не менее, они выполняют обнаружение ЦП во время выполнения.   -  person Peter Cordes    schedule 24.08.2018
comment
Как проверить, требует ли двоичный файл SSE4 или AVX в Linux. Один из ответов включает сценарий bash. Возможно, вам потребуется запустить сценарий в зависимых библиотеках. ldd <your prog> должен вернуть список имен библиотек. Имена должны быть в порядке, но пути могут быть отключены в зависимости от вашей среды.   -  person jww    schedule 24.08.2018
comment
Из болезненного любопытства, как вы можете иметь двоичный файл, который поддерживает AVX-512, но не использует инструкции ISA?   -  person jww    schedule 24.08.2018
comment
@thatotherguy - -mno-avx512f отключает только AVX-512 в коде, который я компилирую, и, похоже, он работает (нет AVX-512 в сгенерированном коде). Однако библиотеки, статически или динамически связанные, могут иметь AVX-512. Проблема с grepping заключается в том, что он дает только статическое представление о том, что там находится, а не о том, где / почему путь фактически выполняется. Например, было бы нормально иметь memcpy где-нибудь, где используется AVX-512, но не ожидать, что ваша программа действительно вызовет его.   -  person BeeOnRope    schedule 24.08.2018
comment
@PeterCordes - этот ЦП не имеет HT, поэтому не должно быть других процессов, работающих параллельно, и я также не ожидаю, что другие процессы будут запланированы на этом ЦП, поскольку машина простаивает. Другие процессы работают так, как ожидалось (т. Е. Работают с полной скалярной частотой).   -  person BeeOnRope    schedule 24.08.2018
comment
В некоторых процессорах все ядра привязаны к одной и той же частоте. Может, не какой-нибудь SKX? Но если другие процессы надежно выходят на полную тактовую частоту, мы можем исключить вмешательство со стороны другого процесса.   -  person Peter Cordes    schedule 24.08.2018
comment
На этом процессоре все работает, как и ожидалось, но определенный процесс, похоже, работает на частоте AVX-512, хотя в нем не должно быть инструкций AVX-512. Я не проверял, все ли ядра заблокированы на одинаковой частоте, но для SKX это маловероятно (это W-2401).   -  person BeeOnRope    schedule 24.08.2018
comment
Это весьма примечательно, потому что частота AVX-512 активна только с тяжелым кодом AVX-512, который содержит инструкции FP и / или int-mul, см. здесь. Я бы не ожидал этих инструкций, например, в функции memcpy. Легкий код AVX-512 должен работать на частотах AVX2.   -  person wim    schedule 25.08.2018
comment
@PeterCordes Относительно Ubuntu 16.04: Некоторое время назад я скомпилировал часть кода, отличного от AVX-512, с -static на Ubuntu 16.04. Действительно, objdump показал регистры zmm и код AVX-512 (vmov-s), хотя 16.04 довольно старый.   -  person wim    schedule 25.08.2018
comment
Но обратите внимание, что поведение турбо-частоты может немного отличаться между Skylake-SP, Skylake-X и Skylake-W. Ссылка в моем предыдущем комментарии относилась к Skylake-SP. Я не знаю, применимо ли это здесь.   -  person wim    schedule 25.08.2018
comment
@wim - я оговорился выше: этот процесс работает на среднем уровне скорости, он же AVX2 turbo, но я считаю, что это плохо названо, потому что на самом деле он включает в себя несколько тяжелых инструкций AVX / AVX2 и подавляющее большинство инструкций AVX-512.   -  person BeeOnRope    schedule 25.08.2018
comment
@jww - спасибо за ссылку, но это про статический анализ. На самом деле я прошу подхода во время выполнения, то есть. определение фактического выполнения инструкции AVX-512 во время выполнения. В этом случае статический анализ дает как ложные срабатывания, так и ложноотрицательные: многие двоичные файлы содержат AVX-512, но они могут фактически не выполняться при любом данном вызове, а статический анализ может пропускать инструкции AVX-512, которые поступают из динамически загружаемых библиотек, созданных во время выполнения. код или другие вещи, например, распакованный во время выполнения код.   -  person BeeOnRope    schedule 26.08.2018
comment
Кстати, разгон AVX (512) может быть вызван спекуляциями. Таким образом, вам даже не нужно выполнять инструкцию AVX. Таким образом, код, который пытается грамотно запускать тяжелый AVX, чтобы избежать спада часов, может быть побежден плохими предположениями. Излишне говорить, что это один из эксплойтов Spectre.   -  person Mysticial    schedule 26.08.2018
comment
Возможно, вам стоит прочитать: realworldtech.com/forum/?threadid=179700&curpostid=179700   -  person Mysticial    schedule 26.08.2018
comment
@BeeOnRope Маленький мир. ржу не могу   -  person Mysticial    schedule 26.08.2018
comment
@Mysticial Это так! Я создал этот вопрос, чтобы иметь простой способ найти инструкции AVX-512, которые могут, так сказать, загрязнять верхнюю часть.   -  person BeeOnRope    schedule 26.08.2018
comment
@BeeOnRope Оглядываясь назад, я никогда с этим не сталкивался. И MSVC, и ICC безоговорочно вставляют vzerouppers в каждую функцию, имеющую какой-либо AVX. Кроме того, большая часть кода в любом случае будет работать на скорости AVX512.   -  person Mysticial    schedule 27.08.2018
comment
Я предлагаю использовать perf record для подсчета следующих трех событий: CORE_POWER.LVL0_TURBO_LICENSE, CORE_POWER.LVL1_TURBO_LICENSE и CORE_POWER.LVL2_TURBO_LICENSE. Затем perf report разбивает его на изображения в формате ELF. Выполнение чего-то подобного позволит вам закрепить изображение в формате ELF. Затем следует статический бинарный анализ. Хотя раньше я не использовал эти события perf.   -  person Hadi Brais    schedule 28.08.2018
comment
@HadiBrais - Я попробую, но это не выглядит многообещающим. Это только сообщает вам места, где вы работаете в различных лицензиях, а не инструкцию, которая запустила это, если, возможно, вы не можете запустить его по краю.   -  person BeeOnRope    schedule 28.08.2018
comment
@BeeOnRope Да, но я надеюсь, что абсолютные подсчеты будут полезны. Я также предполагаю, что количество выборок может коррелировать с приращениями счетчика. Другое предложение, которое у меня есть, может потребовать немного усилий, а именно использование динамических двоичных инструментов в вашем процессе. Это расскажет вам все о процессе.   -  person Hadi Brais    schedule 28.08.2018
comment
Возможно, выполните поиск в своих библиотеках и установите инструкции AVX-512 как точки останова или точки трассировки. Затем запустите программу с помощью отладчика и посмотрите, какие из них вам удастся.   -  person Bobby Durrett    schedule 30.08.2018
comment
Обратите внимание, что аналогичная проблема грязных верхних битов регистров ymm, которые вызывают плохую производительность SSE на Skylake, сообщается здесь: Почему этот код SSE В 6 раз медленнее без VZEROUPPER на Skylake?, существовало в Ubuntu 16.04. В Ubuntu 18.04.1 эта проблема вроде бы решена. По крайней мере, после обновления до 18.04.1 я больше не могу его воспроизвести.   -  person wim    schedule 30.08.2018
comment
@wim - да, я в конечном итоге нашел ту же проблему. Это исправлено в исходной версии glibc 2.23, которая является версией, которую использует Ubuntu, но Ubuntu (вероятно, Debian), по-видимому, еще не внес исправления.   -  person BeeOnRope    schedule 01.09.2018
comment
Можете ли вы заставить GDB производить динамическую трассировку инструкций, выполняемых в пошаговом режиме? Затем найдите zmm[0-3].   -  person Peter Cordes    schedule 01.09.2018
comment
Не уверен, связано ли это с stackoverflow.com/q/43256496/2542702   -  person Z boson    schedule 03.09.2018


Ответы (1)


Как предлагается в комментариях, вы можете выполнить поиск во всех файлах 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 КБ для каждой точки останова).

Еще один способ - запустить ваш код на виртуальной машине. Особенно рекомендую 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);
}
person Jérôme Pouiller    schedule 12.09.2018
comment
libvex виртуализирует CPUID, чтобы не сообщать о поддержке AVX512? Я думаю, OP понадобится виртуальная машина, которая действительно сообщила о поддержке AVX512, поэтому библиотеки по-прежнему могут свободно использовать AVX512 (и оставлять его в загрязненном состоянии). - person Peter Cordes; 12.09.2018
comment
@Peter - да, libvex сообщает об отсутствии поддержки AVX-512 через could. - person BeeOnRope; 13.09.2018
comment
РЕДАКТИРОВАТЬ: как только у вас есть список адресов инструкций AVX512, вы можете разместить точку останова на каждом из них. Я обновил ответ этой идеей. - person Jérôme Pouiller; 17.09.2018