Как мне найти строку C ++, которая блокирует фьютекс Linux? - PullRequest
4 голосов
/ 14 июля 2020

У меня проблема с производительностью большого приложения, написанного на C ++. Программа использует только 150% ЦП, в то время как сервер представляет собой 24-ядерный гиперпотоковый EPY C, а другие аналогичные приложения могут надежно выдерживать ожидаемую нагрузку ЦП 4800%. iotop практически не показывает операций ввода-вывода, что ожидалось.

Поскольку программа явно не привязана ни к вводу-выводу, ни к ЦП, я проверил strace и обнаружил, что подавляющее большинство отслеживаемых вызовов ждут на сингле futex. То есть: 48 из 50 потоков в программе, похоже, блокируют один и тот же фьютекс, что довольно хорошо объясняет, почему загрузка ЦП едва превышает 100%.

Пример:

[pid 11581] futex(0x55acec47a900, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 11580] futex(0x55acec47a900, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 11579] futex(0x55acec47a900, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 11578] futex(0x55acec47a900, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 11577] futex(0x55acec47a900, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 11576] futex(0x55acec47a900, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>

Теперь для меня проблема: как найти проблемный код? Программа не тупиковая, просто медленная, поэтому обычные методы поиска тупиковых ситуаций не работают.

Ответы [ 2 ]

6 голосов
/ 14 июля 2020

Лучший способ, которым я оказался, - это запустить программу в GDB. Поскольку большинство потоков заблокировано, info threads покажет большинство потоков в одном и том же состоянии. Для меня это оказалось заблокировано в __lll_lock_wait. Переключение на любой из этих потоков дало мне трассировку стека, показывающую, как я оказался в __lll_lock_wait. На три уровня вверх по стеку я обнаружил свой оскорбительный код.

0 голосов
/ 15 июля 2020

Как мне найти строку C ++, которая блокирует Linux фьютекс?

Если вы примете изменение исходного кода C ++, вы можете скомпилировать это с g++ -O -g (то есть с DWARF отладочная информация) с использованием недавнего G CC компилятора и использования libbacktrace Яна Тейлора. Эта библиотека дает хорошую информацию об обратной трассировке во время выполнения, используя отладочную информацию DWARF в ваших ELF исполняемых файлах и общих библиотеках .

Тогда вы можете либо подклассифицировать блокировка классов C ++ (например, std::mutex или std::lock_guard) или добавить дополнительный код C ++ (возможно, с препроцессором X-макросами ), чтобы использовать эту библиотеку обратного отслеживания.

Также рассмотрите подход к профилированию с помощью GNU gprof .

Другой возможный подход (целесообразен только для большой базы кода, состоящей из более чем ста тысяч строк C ++, см. этот черновик отчет) может заключаться в использовании трюков с динамическим c компоновщиком (например, LD_PRELOAD, см. ld.so (8) ), чтобы переопределить вашу стандартную библиотеку C ++ или написать свой G CC плагин для изменения выданного кода, связанного с futex (7) или блокировкой классов C ++ .

Для небольшого кода base, подумайте также о написании вашей специализированной метапрограммы (в духе Qt mo c) для преобразования вашего кода C ++ (например, для автоматического добавления вызовов C ++ к libbacktrace функциям), затем обновите вашу автоматизацию сборки (например, ваш Makefile), чтобы использовать его.

Для конкретного примера посмотрите RefPerSys (или, конечно, исходный код G CC)

Однако имейте в виду, что ошибки, связанные с взаимоблокировкой или синхронизацией, являются типичными heisenbugs .

Итак, выделите несколько недель на отладку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...