Подтвердить что ты не робот

C Программирование: отладка с помощью pthreads

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

  • Разработайте теорию о том, что может вызвать проблему.

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

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

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

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

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

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

Ответ 6

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

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

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

Отладка:

Ошибки Threading, как правило, входят в 2 широких разногласия и тупики. Как правило, тупики гораздо более детерминированы.

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

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

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

Ответ 7

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

Ответ 8

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

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

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