Как отладить редкий тупик

Я пытаюсь отлаживать пользовательскую реализацию пула потоков, которая редко срабатывает. Поэтому я не могу использовать отладчик вроде gdb, потому что у меня есть клик, как 100-кратный отладчик "запуска", перед тем, как зайти в тупик.

В настоящее время я запускаю тест threadpool в бесконечном цикле в оболочке script, но это означает, что я не вижу переменных и т.д. Я пытаюсь использовать данные std::cout, но это замедляет поток и уменьшает риск взаимоблокировок, что означает, что я могу подождать как 1 час с моим бесконечным до получения сообщений. Тогда я не получаю ошибку, и мне нужно больше сообщений, что означает ожидание еще одного часа...

Как эффективно отлаживать программу, чтобы ее перезапуск снова и снова до тех пор, пока она не будет заблокирована? (Или, может быть, я должен открыть другой вопрос со всем кодом для некоторой помощи?)

Заранее благодарю вас!

Бонусный вопрос: как проверить, все ли в порядке с std::condition_variable? Вы не можете сказать, какой поток спал или если условие гонки происходит в состоянии wait.

Ответ 1

Существует два основных способа:

  • Автоматизация работы программы в отладчике. Использование gdb program -ex 'run <args>' -ex 'quit' должно запускать программу под отладчиком, а затем выйти. Если программа все еще жива в той или иной форме (segfault или вы ее рубили вручную), вас попросят подтвердить.
  • Прикрепите отладчик после воспроизведения тупика. Например, gdb может быть запущен как gdb <program> <pid> для присоединения к запущенной программе - просто подождите тупик и прикрепите его. Это особенно полезно, когда прикрепленный отладчик приводит к изменению времени, и вы больше не можете повторять ошибку.

Таким образом, вы можете просто запустить его в цикле и ждать результата, пока вы пьете кофе. BTW - мне легче найти второй вариант.

Ответ 2

Если это какая-то домашняя работа - перезапуск снова и снова с большим отлаживанием будет разумным подходом.

Если кто-то платит деньги за каждый час, который вы ждете, они могут предпочесть инвестировать в программное обеспечение, поддерживающее отладку на основе воспроизведения, то есть программное обеспечение, которое записывает все программа выполняет каждую инструкцию и позволяет повторять ее снова и снова, отлаживая назад и вперед. Таким образом, вместо добавления дополнительной отладки вы записываете сеанс, в течение которого происходит взаимоблокировка, и затем начинаете отладку непосредственно перед тем, как произойдет тупик. Вы можете шагнуть туда и обратно так часто, как хотите, пока не найдете виновника.

Программное обеспечение, упомянутое в ссылке, фактически поддерживает Linux и многопоточность.

Ответ 3

Вы можете запустить тестовый пример под GDB в цикле, используя команду, показанную в fooobar.com/questions/400595/...: gdb --eval-command=run --eval-command=quit --args ./a.out.

Я сам использовал это: (while gdb --eval-command=run --eval-command=quit --args ./thread_testU ; do echo . ; done).

Как только он блокируется и не выходит, вы можете просто прервать его с помощью CTRL+C, чтобы войти в отладчик.

Ответ 4

Легкая быстрая отладка для поиска взаимоблокировок заключается в том, чтобы изменить некоторые глобальные переменные там, где вы хотите отлаживать, а затем распечатать их в обработчике сигналов. Вы можете использовать SIGINT (отправляется при прерывании с помощью ctrl+c) или SIGTERM (отправляется, когда вы убиваете программу):

int dbg;

int multithreaded_function()
{
  signal(SIGINT, dbg_sighandler);
  ...
  dbg = someVar;
  ...  
}

void  dbg_sighandler(int)
{
  std::cout << dbg1 << std::endl;
  std::exit(EXIT_FAILURE);
}

Так же вы видите состояние всех ваших отладочных переменных при прерывании программы с помощью ctrl+c.

Кроме того, вы можете запустить его в оболочке во время цикла:

$> while [ $? -eq 0 ]
   do
   ./my_program
   done

который будет запускать вашу программу навсегда до тех пор, пока не сработает ($? - это статус выхода вашей программы, и вы выходите с EXIT_FAILURE в обработчике сигнала).

Это сработало для меня, особенно для того, чтобы узнать, сколько потоков прошло до и после каких блокировок.

Он довольно деревенский, но вам не нужен дополнительный инструмент, и он быстро реализуется.