Методы отладки и тестирования многопоточных приложений

Методы отладки и тестирования многопоточных приложений

19.07.2012 11:15:27 Просмотров 27 Источник

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

Есть ли у вас какие-то любимые, проверенные методики, может утилиты, которые будут полезны (исключая уже много раз мною упомянутый Valgrind)?

На что при тестировании следует обратить особое внимание?

В общем всё, что подойдет относительно начинающему. Работаю в Freebsd с posix_thread.

У вопроса есть решение - Посмотреть?

Ответы - Методы отладки и тестирования многопоточных приложений / Методы отладки и тестирования многопоточных приложений

Является ответом!
avp

19.07.2012 03:15:13

@margosh, я тупо ставлю printf-ы в которых обязательно печатаю еще и thread id. Если прога падает (SIGSEGV и т.п.) смотрю gdb. Он сообщает в каком потоке свалилось.

Вообще, все более-менее нетривиальные функции стараюсь сначала отладить в однопоточном варианте (просто из main).

Многопоточную логику сначала выделяю в некие тестовые куски (без реального наполнения данными задачи) и просто играюсь с ней (опять printf-ами, слипами, где-то ввод с клавиатуры и запись в какой-нибудь пайп и т.д.).

Ну, и черкаю ручкой на бумаге линии потоков, какие события и когда происходят и т.п.

--

В целом так, но реально я иногда просто вижу алгоритм в виде каких-то цветных и объемных фигурок, которые двигаются, сливаются, меняются ... и тогда мне становится ясно, как это можно программировать.

https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128944#comment128955_128944
@printf-ами с thread id тоже пользуюсь... > вижу алгоритм в виде каких-то цветных и объемных фигурок, которые двигаются здорово, но до подобного понимания мне пока далеко - это мой первый серьезный многопоточный проект, все время кажется, что что-то упускаю :)
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128944#comment128984_128944
@margosh - когда работаешь с тредами нужно проверять все, даже, на первый взгляд, невозможное. Такого рода приложения могут работать даже год безбажно, а потом - бух и все... :(
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128944#comment128985_128944
у меня такое было, когда на защите работы, которая проверялась мной примерно месяц, я поймал дедлок. а если бы это было реально работающие приложение...именно из-за этого я всё больше смотрю в сторону асинхронности
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128944#comment128993_128944
@AlexWindHope, я и пытаюсь по мере сил, потому и прошу советов по методикам тестирования.
Asen2

19.07.2012 03:27:44

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


Единственный минус - поднять более-менее адекватную тестовую среду для мультитредового приложения или его мультитредовых блоков - это задача на порядок сложнее аналогичной задачи для single-core environment.

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

https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment128959_128951
@margosh DRD даже расшифровывается как Data Race Detector. Собственно, насколько мне известно, он покрывает большой набор кейсов типа дедлоков и pthreads misusage, что, разумеется, бывает полезно. Когда я говорил про Windows, имелось ввиду, что, скажем, встроенные средства visual studio, лично мне кажутся более удобными и к конкретно вашей задаче данное высказывание не относилось =)
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment128961_128951
@margosh Если вы говорите, что "тестирование - разумеется", то это означает, что некоторый тест сьют под ваши кейсы у вас уже имеется. Значит, если предположить, что тест сьют достаточно полон, чтобы покрыть большую часть обычных багов, то Valgrind DRD даст вам полезную информацию сугубо по мультитредовой - pthread'овой части вашего приложения.
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment128963_128951
пардон, спутала с другой утилиткой. на Ваш взгляд DRD лучше Helgrind? В любом случае. с Valgrind-утилитками у меня пока плохо складывается. так что я ищу способ подойти к отладке с другого бока. > Valgrind DRD даст вам полезную информацию сугубо по мультитредовой - pthread'овой части вашего приложения в том, что дал, пока не разобралась(одна из моих неотвеченных тем), так что еще варианты приветствуются.
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment128970_128951
@margosh До Hellgrind'a в свое время руки не дошли (на основании ответов в треде stackoverflow.com/questions/8157669/…). Хотя, чтобы увеличить вероятность нахождения ошибки, возможно, стоит использовать все тулзы по очереди, включая и рекомендуемый на stackoverflow ThreadSanitizer.
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment128981_128951
@margosh, у Вас был вопрос "Как выжать максимум информации из gdb при анализе стека вызовов?" и там непонятная ошибка: Program received signal SIGILL, Illegal instruction. 0x380410bd in ?? () (gdb) bt 0 0x380410bd in ?? () ..... 5 0xbebf4570 in ?? () 6 0x000f988c in EC_toByte () from /usr/local/lib/libfcrypt.so.3 7 0x0804b44c in main (argc=3, argv=0xbebfec74) at test.c:462 Вы нашли ее ? Насколько помню, у Вас MIPS, это конечно несколько усложняет ситуацию (базу практиков).
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment128991_128951
@avp, нет, пока не нашла, потому и возникают подобные этому вопросы. Однако, поэкспериментировав с кодом, при вылете получаю некоторые вариации этого стека, поэтому считаю. что проблема имеет неспосредственное отношение к многопоточке. К примеру, время от времени демон вылетал на memchr, поковыряла исходник - не вижу там использования static-переменных, может что-то еще не учитываю...
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment129013_128951
@margosh, к сожалению банальное предположение об испорченном стеке (как адресе возврата, так и списка фреймов вызова (очевидно Вы и сами это видите)) практически бесполезно. Если в FreeBSD потоки реализуются библиотекой (не ядром), то можно предположить порчу памяти в куче. А вообще (пришла мысль) посмотрите в каких диапазонах адресов находятся стеки всех потоков. -- Сразу продолжу. Увидел SIGCHLD. Значит Ваша многопоточная программа делает fork()-и. А что в новом процессе происходит ? Как в нем потоки живут (ведь сокеты, например, будут унаследованы)? Это очень тонкое место.
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment129019_128951
> посмотрите в каких диапазонах адресов находятся стеки всех потоков как это сделать и, помимо сравнения с адресом падения, это еще что-то даст? Да, прога форкается - в дочернем процессе устанавливаются переменные среды, все дескрипторы кроме одного (половинка из socketpair()) закрываются, дескрипторы ввода-вывода дублируются на этот один, для связи с родительским процессом, в родительском же закрывается этот единственный, и слушается второй дескриптор пары. После установления переменных среды - exec()-ом вызывается другая программа.
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment129039_128951
Просто посмотрите, стеки потоков в тех же (примерно) адресах, что и адреса из malloc()? Если да, смотрите в сторону динамических структур. Два free, ошибки в аргументах realloc, банальное переполнение в allocated памяти. Посмотрите внимательно, что может произойти в разных потоках во время между fork и exec в обоих процессах. Не может ли, например, recv() отработать в потоке дочернего процесса (до exec). В таком случае логика работы потока в родителе может нарушиться. Откровенно, я не знаю, что происходит с системным вызовом при fork-e в другом потоке. Надо бы поэкспериментировать. (limi
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment129477_128951
@margosh, для linux мои опасения насчет fork() и "наследуемых" потоков (SYSCALLS) оказались напрасными. Я где-то читал о таком поведении. Видимо это так в системах, которые делают pthreads на уровне библиотеки и обработки сигналов. В linux fork() делит только один поток, тот в котором он (fork) вызван. Т.е. тут pid = 3680, 4 потока tid = [3680, 3681, 3682, 3683] if (fork()) { // это в tid 3682, pid 3680 процесс родитель 3680 те же 4 потока } else { процесс потомок pid 3684 tid 3684 ppip 3680 (!!!) } Вам надо просто проверить, как обстоит дело в FreeBSD.
https://ru.stackoverflow.com/questions/128719/%d0%9c%d0%b5%d1%82%d0%be%d0%b4%d1%8b-%d0%be%d1%82%d0%bb%d0%b0%d0%b4%d0%ba%d0%b8-%d0%b8-%d1%82%d0%b5%d1%81%d1%82%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d1%8f-%d0%bc%d0%bd%d0%be%d0%b3%d0%be%d0%bf%d0%be%d1%82%d0%be%d1%87%d0%bd%d1%8b%d1%85-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b9/128951#comment129493_128951
@avp, абсолютно аналогично обстоит, т.к. я как раз fork() использую. Есть еще вызов rfork(), он аналогичен линуксовскому clone(). Сегодня смотрела опять - в дочернем процессе производится обращение к переменной данного потока, но поковырявшись поняла, что это - копия той переменной, т.к. при попытке записи ядро создает дубликат для дочернего процесса, так что не страшно. А вообще, еще не совсем разобралась, когда стоит, а когда нет применять pthread_atfork(), по идее, так как я не пытаюсь каких-то блокировок захватить до exec(), она не нужна... не сталкивались?
Михаил М

22.06.2013 10:26:43

Могу в дополнение порекомендовать

  • ставить побольше assert'ов (прямо максимум, что удастся выдумать, пусть даже некоторые окажутся перебором),
  • при необходимости - делать очень подробное логирование в память, а в файл выводить только наиболее важные сообщения. При ошибке, соответственно, сливать лог из памяти в файл,
  • всю логику, связанную с многопоточностью стараться делать максимально просто!
Закрыть X