LINUX.ORG.RU

printf по памяти


0

3

Добрый день! Интересная проблема нарисовалась.

Есть большой и сложный проект, практически SCADA система. Вдруг (как обычно) обнаружилась ошибка - при разыменовании указателя (глобальная переменная) получаем ошибку сегментации. Ну вроде понятно, перетирается указатель. Однако перетирается интересным образом - функцией printf(). То есть в нескольких потоках (printf потокобезопасна) есть вызовы printf(«бла бла бла»), и в какой-то момент это самое «бла бла бла» начинает выводится прямо по глобальным переменным, т. е. если было глобально определено

char a=1,b=2,c=3; 
то получится a='б', b='л', c='а', и т. д. В какой-то момент времени таким образом портится и указатель.

Посмотрел в файле stdio.h, там stdout определен как &__iob[1], где __iob[] - массив FILE. При этом указатель на позицию в буффере в структуре stdout как раз приходится на адреса глобальных переменных, причем сам буффер (base addr) расположен дальше в памяти, количество свободных элементов в буффере порядка 15тыс.

В общем-то вопрос и заключается в следующем - как может изменится (перетереться?) адрес в указателе позиции буффера в stdout? Переменные, располагающиеся в памяти вокруг stdout в полном порядке. И как можно отследить эту ситуацию? Запустить под отладчиком - проблема.

ОС - solaris 2.5.1 на SPARCstation-10

>При этом указатель на позицию в буффере в структуре stdout как раз приходится на адреса глобальных переменных, причем сам буффер (base addr) расположен дальше в памяти, количество свободных элементов в буффере порядка 15тыс.

Не распарсил.

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

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

> Не распарсил.

Указатель внутри структуры FILE указывает на область, где глобальные переменные программы вместо того, чтобы указывать на реальный файловый буфер. ТС спрашивает, как выяснить, что записало в указатель ошибочный адрес.

geekless ★★
()

>В какой-то момент времени таким образом портится и указатель.

Что значит портится указатель?

mopsene ★★★
()
Ответ на: комментарий от mopsene

Очевидно, в него записывается неверное значение.

Gvidon ★★★★
()

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

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

ksv82
()

тебе нужен valgrind или аналог чтобы понять на каком этапе облом происходит. Очень маловероятно что проблема в printf. Это больше похоже на использование высвобожденной free() памяти.

true_admin ★★★★★
()

если это из-за printf-а, то похоже на гонки в работе с полями структуры FILE. а это значит, что printf не потокобезопасна.

xydo ★★
()

в принципе, что тут думать, сделать mutex или что там используется для синхронизации и выяснишь printf это или нет.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Кстати, может глупый вопрос, но задать его все таки стоит. А если закоментить printf в потоках? Баг исчезает?

ksv82
()
Ответ на: комментарий от Reset

>В glibc точно. Внутри там делается flockfile.

а... тогда да. надо ман читать... так-ли это. ладно, ман читать лень, верю :-)

drBatty ★★
()

printf() не потокобезопасна. В солярисе - уж точно. Возможны даже дедлоки, сам когда-то натыкался.

Sorcerer ★★★★★
()
Ответ на: комментарий от Sorcerer

И да, с дедлоками при выводе сталкивался, если использовать write() вместо printf(), при замене на printf() все как раз исправлялось... В какой версии solaris Вы сталкивались с потоконебезопасностью?

StirliZ
() автор топика
Ответ на: комментарий от ksv82

> Кстати, может глупый вопрос, но задать его все таки стоит. А если закоментить printf в потоках? Баг исчезает?

Баг возникает достаточно редко, убирать вывод придется надолго, поэтому такая проверка еще не проводилась. И если верить ману, то результат не должен измениться. Программа-то падать перестанет, но память все равно будет портится.

anonymous
()
Ответ на: комментарий от StirliZ

> В какой версии solaris Вы сталкивались с потоконебезопасностью?

В 8 по-моему. Проблема решилась только записью внутри mutex.

Sorcerer ★★★★★
()
Ответ на: комментарий от StirliZ

В мане английским по черному написано, что printf - MT-safe

Если точнее, то:

http://download.oracle.com/docs/cd/E19695-01/802-1930-03/802-1930-03.pdf

sprintf( ) is MT-Safe in multi-thread applications. printf and fprintf can be used safely in a multi-thread application, as long as setlocale(3C) is not being called to change the locale.

Плюс там еще имеет место быть printf в библиотеках совместимости с BSD, который нифига не потокобезопасен.

mukoh
()
Ответ на: комментарий от mukoh

Вторую часть фразы я конечно же читал, только вот setlocate() нигде не используется, поэтому и принял за точку отсчета, что printf безопасен.

Про библиотеки совместимости - спасибо, этот момент я никак не учитывал, проверю.

StirliZ
() автор топика
Ответ на: комментарий от StirliZ

Запускать на каком-нибудь более новом релизе, в котором есть libumem пробовали? Можно подсунуть libumem, активировать отладку (man umem_debug) и поймать наглеца за руку.

mukoh
()
Ответ на: комментарий от StirliZ

>только вот setlocate() нигде не используется

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

legolegs ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.