Анализ многопоточных программ

У нас есть кодовая база, которой несколько лет, а все оригинальные разработчики давно ушли. Он использует много-много потоков, но без явного дизайна или общих архитектурных принципов. У каждого разработчика был свой стиль многопоточного программирования, поэтому некоторые потоки взаимодействуют друг с другом с помощью очередей, некоторые блокируют данные с помощью мьютексов, некоторые блокируют семафоры, некоторые используют механизмы IPC операционной системы для внутрипроцессного взаимодействия. Проектной документации нет, комментарии немногочисленны. Это беспорядок, и кажется, что всякий раз, когда мы пытаемся реорганизовать код или добавить новые функции, мы вводим тупиковые ситуации или другие проблемы.

Итак, знает ли кто-нибудь какие-либо инструменты или методы, которые помогли бы анализировать и документировать все взаимодействия между потоками? FWIW, кодовая база - C++ для Linux, но мне было бы интересно услышать об инструментах для других сред.


Обновлять

Я ценю полученные на данный момент ответы, но я надеялся на что-то более сложное или систематическое, чем совет, который по сути сводится к «добавлению сообщений журнала, выяснению того, что происходит, и исправлению». Существует множество инструментов для анализа и документирования потока управления в однопоточных программах; нет ничего доступного для многопоточных программ?


См. Также Отладка многопоточных приложений.

Ответов (7)

Решение

Приобретите копию Intel VTune и ее инструменты для профилирования потоков. Это даст вам представление о поведении потока как на системном, так и на исходном уровне. Это, конечно, не будет автоматически документировать вещь для вас, но должно быть реальным подспорьем, по крайней мере, в визуализации того, что происходит в различных обстоятельствах.

Я думаю, что есть пробная версия, которую вы можете скачать, так что, возможно, стоит попробовать. Я использовал только версию для Windows, но, глядя на веб-страницу VTune, у нее также есть версия для Linux.

При использовании log4net или аналогичного инструмента следует помнить, что они изменяют время работы приложения и часто могут скрыть основные условия гонки. У нас был плохо написанный код для отладки и введено ведение журнала, и это фактически устранило состояния гонки и взаимоблокировки (или значительно снизило их частоту).

Это действительно сложная проблема для автоматизированных инструментов. Возможно, вы захотите изучить модель, проверив свой код. Не ждите волшебных результатов: средства проверки моделей очень ограничены в объеме кода и количестве потоков, которые они могут эффективно проверить.

Инструмент, который может сработать для вас, - это ШАХМАТЫ (хотя, к сожалению, он доступен только для Windows). BLAST - еще один довольно мощный инструмент, но его очень сложно использовать и он может не работать с C++. В Википедии также есть список StEAM , о котором я раньше не слышал, но похоже, что он может сработать для вас:

StEAM - это средство проверки моделей для C++. Он обнаруживает взаимоблокировки, сбои сегментации, выходящие за пределы диапазона переменные и непрерывные циклы.

В качестве альтернативы, вероятно, будет очень полезно попытаться привести код к небольшому количеству четко определенных (и, предпочтительно, высокоуровневых) схем синхронизации. Смешивание блокировок, семафоров и мониторов в одной базе кода вызывает проблемы.

Неужели UML здесь не поможет?

Если вы перепроектируете свою кодовую базу в UML , вы сможете рисовать диаграммы классов, которые показывают отношения между вашими классами. Начиная с классов, методы которых являются точками входа в поток, можно было увидеть, какой поток какой класс использует. Исходя из моего опыта работы с Rational Rose , этого можно было достичь с помощью перетаскивания; если нет связи между добавленным классом и предыдущими, то добавленный класс не используется напрямую потоком, начавшимся с помощью метода, с которого вы начали диаграмму. Это должно дать вам представление о роли каждого потока.

Это также покажет общие «объекты данных» и объекты, зависящие от потока.

Если вы нарисуете большую диаграмму классов и удалите все «объекты данных», тогда вы сможете разместить эту диаграмму в виде облаков, каждое из которых будет потоком - или группой потоков, если только связь и согласованность базы кода не нарушены. ужасный.

Это даст вам только одну часть головоломки, но может быть полезно; Я просто надеюсь, что ваша кодовая база не слишком грязная или слишком "процедурная", и в этом случае ...

В качестве отправной точки я бы хотел добавить сообщения журнала трассировки в стратегические точки вашего приложения. Это позволит вам анализировать, как ваши потоки взаимодействуют, без опасности того, что процесс наблюдения за потоками изменит их поведение (как это может быть в случае пошаговой отладки). Мой опыт работы с платформой .NET, и моим любимым средством ведения журнала будет log4net, поскольку он бесплатный, имеет обширные параметры конфигурации и, если вы разумны в том, как реализовать ведение журнала, он не будет заметно снижать производительность вашего приложения. В качестве альтернативы в пространстве имен System.Diagnostics есть встроенный класс .NET Debug (или Trace).

Я бы сначала сосредоточился на блокировках разделяемой памяти (мьютексы и семафоры), поскольку они, скорее всего, вызовут проблемы. Посмотрите, какое состояние защищено блокировками, а затем определите, какое состояние находится под защитой нескольких блокировок. Это даст вам представление о потенциальных конфликтах. Посмотрите на ситуации, когда код, содержащий блокировку, вызывает методы (не забывайте виртуальные методы). Постарайтесь по возможности исключить эти вызовы (сократив время удержания блокировки).

Учитывая список удерживаемых мьютексов и приблизительное представление о состоянии, которое они защищают, назначьте порядок блокировки (т. Е. Мьютекс A всегда следует брать перед мьютексом B). Попробуйте закрепить это в коде.

Посмотрите, сможете ли вы объединить несколько блокировок в одну, если параллелизм не пострадает. Например, если кажется, что у мьютексов A и B могут быть взаимоблокировки, а схему упорядочения сделать нелегко, сначала объедините их в одну блокировку.

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

В Java у вас есть выбор, например FindBugs (для статического анализа байт-кода), чтобы найти определенные виды несогласованной синхронизации, или множество динамических анализаторов потоков от таких компаний, как Coverity, JProbe, OptimizeIt и т. Д.