Программирование на C: отладка с помощью pthreads - PullRequest
27 голосов
/ 11 июня 2009

Одной из самых трудных вещей, к которым мне пришлось сначала привыкнуть, был мой первый интенсивный опыт программирования с использованием pthreads на C. Я привык точно знать, какой будет следующая строка кода, и большинство моих методов отладки были сосредоточены это ожидание.

Каковы хорошие методы отладки с помощью pthreads в C? Вы можете предложить персональные методологии без каких-либо дополнительных инструментов, инструментов, которые вы используете, или чего-либо еще, что поможет вам в отладке.

P.S. Я делаю свое программирование на C, используя gcc в linux, но не позволяйте этому обязательно сдерживать ваш ответ

Ответы [ 8 ]

29 голосов
/ 11 июня 2009

Valgrind - превосходный инструмент для поиска условий гонки и злоупотреблений API pthreads. Он сохраняет модель доступа к программной памяти (и, возможно, совместно используемым ресурсам) и обнаруживает отсутствующие блокировки, даже когда ошибка является доброкачественной (что, конечно, означает, что в какой-то момент она совершенно неожиданно станет менее благоприятной).

Чтобы использовать его, вы вызываете valgrind --tool=helgrind, , вот его руководство . Также есть valgrind --tool=drd ( руководство ). Helgrind и DRD используют разные модели, поэтому они обнаруживают наложение, но, возможно, различный набор ошибок. Также могут возникнуть ложные срабатывания.

В любом случае, valgrind сэкономил бесчисленные часы отладки (хотя не все из них :) для меня.

7 голосов
/ 11 июня 2009

Одна из вещей, которая удивит вас в отладке многопоточных программ, заключается в том, что вы часто будете находить изменения ошибок или даже исчезать, когда добавляете printf или запускаете программу в отладчике (в народе известный как Heisenbug ).

В многопоточной программе Heisenbug обычно означает, что у вас условие гонки . Хороший программист будет искать общие переменные или ресурсы, которые зависят от порядка. Дрянной программист попытается слепо исправить это с помощью операторов sleep ().

2 голосов
/ 13 февраля 2010

В фазе «мышления» перед началом кодирования используйте концепцию конечного автомата. Это может сделать дизайн намного понятнее.

printf может помочь вам понять динамику вашей программы. Но они загромождают исходный код, поэтому используйте макрос DEBUG_OUT () и в своем определении включите его с логическим флагом. Более того, установите / очистите этот флаг с помощью сигнала, который вы посылаете через 'kill -USR1'. Отправьте вывод в файл журнала с отметкой времени.

также рассмотрите возможность использования assert (), а затем проанализируйте дампы ядра, используя gdb и ddd.

2 голосов
/ 11 июня 2009

Отладка многопоточного приложения затруднена. Хороший отладчик, такой как GDB (с дополнительным DDD интерфейсом) для среды * nix или тот, который поставляется с Visual Studio для Windows, очень поможет.

1 голос
/ 17 июля 2014

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

Дизайн - лучшая оптимизация - лучший алгоритм:

1) Разбейте свои функции на ЛОГИЧЕСКИ отделимые части. Это означает, что вызов делает "A" и ТОЛЬКО "A" - не A, а B, а затем C ...
2) ОТСУТСТВИЕ ПОБОЧНЫХ ЭФФЕКТОВ: отменить все открытые глобальные переменные, статические или нет. Если вы не можете полностью устранить побочные эффекты, изолируйте их в нескольких местах (сконцентрируйте их в коде).
3) Сделайте как можно больше изолированных компонентов. Это означает, что они не имеют состояния - они принимают все свои входные данные как константы и манипулируют только ЗАЯВЛЕННЫМИ, логически постоянными параметрами для получения выходных данных. Передача по значению вместо ссылки, где вы можете.
4) Если у вас есть состояние, сделайте четкое разделение между узлами без состояния и действительным конечным автоматом. В идеале конечный автомат должен быть отдельной функцией или классом, управляющим компонентами без состояний.

Отладка:

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

1) Видите ли вы повреждение данных ?: ДА => Возможно, гонка.
2) Возникает ли ошибка при КАЖДОМ запуске или только при некоторых запусках ?: ДА => Вероятно, тупик (гонки обычно недетерминированы).
3) Процесс когда-нибудь зависает? ДА => Где-то тупик. Если он иногда зависает, возможно, вы тоже участвуете в гонке.

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

Как правило, это означает, что вы должны удалить все контрольные точки, определить тип ошибки, а затем повторно ввести их, чтобы попытаться исправить. Иначе они просто еще больше искажают вещи.

1 голос
/ 12 июня 2009

Мой подход к многопоточной отладке аналогичен однопоточному, но обычно на этапе мышления тратится больше времени:

  1. Разработка теории о том, что может быть причиной проблемы.

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

  3. При необходимости добавьте код, который может опровергнуть или проверить ваши результаты и теорию.

  4. Если ваша теория верна, устраните проблему.

Часто «эксперимент», подтверждающий теорию, заключается в добавлении критического участка или мьютекса вокруг подозрительного кода. Затем я попытаюсь сузить проблему, систематически сокращая критический раздел. Критические разделы не всегда являются лучшим решением (хотя часто могут быть быстрым решением). Тем не менее, они полезны для определения «дымящегося ружья».

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

Кроме того, hellgrind - отличный инструмент. Intel Thread Checker выполняет аналогичную функцию для Windows, но стоит намного дороже, чем hellgrind.

0 голосов
/ 20 сентября 2012

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

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

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

0 голосов
/ 11 июня 2009

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

...