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

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

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


Обновить

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


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

7 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

Разве UML не может помочь вам здесь?

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

Это также покажет "объекты данных", которые являются общими и объекты, которые являются специфичными для потока.

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

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

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

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

Другие вопросы по тегам