LINUX.ORG.RU

Отладка SIGSEGV: поделитесь мудростью пожалуйста!


0

0

Проблема такая: платформа ARM9, самописный медиа плеер, слизанный с madplay. Сегфолтится при проигрывании интернет радио почти всегда, при запуске отдельно. Как только запускаю его под gdb, сразу работает без сбоев.

Функции backtrace_symbols или backtrace_symbols_fd в моём SDK нет. Ошибка плавающая, т.е. при запуске некоего тестирующего скрипта она может проявиться сразу, а может не сразу (видимо зависит от данных, которые идут с интернет-радиостанций или от еще чего-то), а может вообще не проявиться.

Собственно зацепка только одна: с gdb работает всегда. Принтами отлаживать пробовал, но без особого успеха...

Буду благодарен любым соображениям.

Скомпилировать с отладочной информацией, сделать так, чтобы при sigsegv оставлялся core-файл, и с его помощью посмотреть стектрейс и значения переменных в момент падения.

Legioner ★★★★★
()

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

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

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

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

В обычном линуксе с башем ulimit -c unlimited набрать перед запуском программы, он сам останется. Как в вашем окружении - не знаю.

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

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

SIGSEGV - нефатальный сигнал. Можно его ловить и что-то делать дальше.

mv ★★★★★
()

Очень похоже на проблемы с многопоточностью.
gdb снижает скорость выполнения, пожтому зачастую такие проблемы
с ним вылезают не синхронно с обычным запуском.
Соответственно я бы проверил, что все необходимые ресурсы успели
инициализироваться перед работой с ними из других потоков.
А потом проверил обращения к общим ресурсам без локинга.

Все вышеописанное исходит из многопоточности приложения.

Если однопоточное поискал бы то что может быть завязано на endiannes.

Svoloch ★★★
()
Ответ на: комментарий от no-dashi

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

smplayer так делает.

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

core - файл получил, в нём не густо, весь стек состоит из одного уровня, если я правильно понял:

# /www/cignias/service/gdb /usr/bin/cigplay /tmp/core.1986

......................................... (gdb) bt #0 0x0000e7e4 in ?? () Cannot access memory at address 0x4 (gdb) quit

вот как теперь узнать, чему соответствует адрес 0x0000e7e4 ?

nm не помогает, она только с библиотеками работает, если я правильно понял...

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

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

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

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

Я лично против неё потому, что плеер - приложение реал-таймовое всё-таки, в нем фактически есть одна задача: брать данные, декодировать и воспроизводить, поэтому, как мне кажется, многопоточность в нём не к чему особо...

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

> у меня приложение как раз многопоточное

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

no-dashi ★★★★★
()
Ответ на: комментарий от FatherTorque

Такое счастье как правило значит, что стек сломан.
Сие крайне печально, с учетом невоспроизводимости проблем с gdb.
Можно попробовать поймать стрим которй идет на вход,
и послать его из симуляции сервера. Это позволит исключить падение
из-за входных данных. Хотя я склоняюсь к проблемам многопоточности,
тем более что по не опытности их можно схватить много.
Имеет смылсл расставить локи вокруг всех общих ресурсов.
Что делают разные потоки?
И да, на gdb свет клином не сошелся, есть strace, ltrace и прочие.
Может с какими-то из них упадет. Хоть что-то еще узнаешь?

P.S. скомпилено ведь с дебагом? (если что -g -ggdb)
P.P.S. Многопоточность смысл имеет если есть две несвязанные задачи.
Например GUI и проигрывание.

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

да, скомпилено с опцией -g

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

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

ладно, как говорил Семён Семёныч Горбунков, «будем искать»...

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

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


P.S. И strace и ltrace под arm есть.

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

К сожалению, никак не отключить...

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

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

Внутри него сложно что-то комментировать (я пробовал, но начинают лезть уже другие ошибки) - этот кусок он самодостаточен и целостен. Но если его отключаю - ошибка исчезает напрочь.

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

Показывай бэктрейс упавшего треда, регистры и дизассемблированную функцию в районе program counter.

mv ★★★★★
()
Ответ на: комментарий от mv
Core was generated by `cigplay'.
Program terminated with signal 11, Segmentation fault.
#0  0x0000e7e4 in ?? ()
(gdb) backtrace full
#0  0x0000e7e4 in ?? ()
No symbol table info available.
Cannot access memory at address 0x4
(gdb) info registers
r0             0x6c549  443721
r1             0xff6a149f       4285142175
r2             0x6c549f 7099551
r3             0x1024000        16924672
r4             0x117158 1143128
r5             0x4      4
r6             0xb545c  742492
r7             0xb5458  742488
r8             0xfeaa0  1043104
r9             0xb545c  742492
r10            0xc7598  816536
r11            0x4      4
r12            0xf      15
sp             0xbe5ffcdc       0xbe5ffcdc
lr             0x0      0
pc             0xe7e4   0xe7e4
fps            0x0      0
cpsr           0x10     16
(gdb) x/16i $pc
0xe7e4: str     r1, [lr]
0xe7e8: ldr     r2, [r6, #64]
0xe7ec: ldr     r3, [r7, #56]
0xe7f0: add     r2, r2, r3
0xe7f4: add     r1, r2, #128    ; 0x80
0xe7f8: bic     r3, r1, #-268435456     ; 0xf0000000
0xe7fc: bic     r3, r3, #255    ; 0xff
0xe800: cmp     r3, #0  ; 0x0
0xe804: andeq   r2, r1, #-268435456     ; 0xf0000000
0xe808: cmn     r2, #-268435455 ; 0xf0000001
0xe80c: str     r2, [r6, #64]
0xe810: ble     0xe7b0
0xe814: ldr     r3, [r9, #64]
0xe818: add     r3, r3, #-268435456     ; 0xf0000000
0xe81c: str     r3, [r9, #64]
0xe820: b       0xe56c
(gdb) thread apply all backtrace

Thread 3 (process 1984):
#0  0x00000000 in ?? ()
#1  0x00000000 in ?? ()

Thread 2 (process 1985):
#0  0x00000000 in ?? ()
#1  0x00000000 in ?? ()

Thread 1 (process 1986):
#0  0x0000e7e4 in ?? ()
Cannot access memory at address 0x4
(gdb) 

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

думаю, что не проще... потому что там она оптимизирована под особенности этой платформы, да и уверен я, что проблема не в коде, что код правилен, так как он нормально работает на всех без исключения реальных файлах и всех поддерживаемых интернет-радиостанциях (для которых у нас есть кодеки (mp3, m4a и wma)), но тестовый скрипт (на тестирование одной станции уходит около 1-2 секунд, весь он исполняется больше суток) заставляет работать с какими-то непонятными радиостанциями, которые даже не играют толком... т.е. похоже что и даже стрима там как-такового нет (не знаю, что там вообще декодируется), ну вот, этот тестовый скрипт валит плеер.

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

Погляжу еще на исходники, может чего придёт в голову... там уж очень всё лихо. Даже не вникал, как работает эта интерполяция, просто скопипастил, указатели подставил и всё... Наверно лучше разобраться.

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

Ага, только тормозят по разному, и в отличие от корки,
можно глянуть что до падения было.

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

Кстати, второй поток ресемплирует, а данные для него откуда
берутся? Не из другого потока?
А то может пока он ресемплирует, в буфер данные новые пишутся,
и меняется переменная количества данных или что подобное из
другого потока?
Ну и опять же классика стэка, нет локаяльных для ф-ции массивов?

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

И еще. Если он указатели принимает, как быстрая диагностика -
увеличить размер буферов на которые указатели указывают.
Если регулярная +-1 ошибка, то падать прекратит.

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

Точно портабельный, его даже под винды можно собрать! (не мой плеер, а madplay)...

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

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

там размеры буферов вычисляются точно, и после этого прибавляется 100 (на всякий пожарный) - это всё уже проходили, и отлажено... я же говорю, воспроизводит все стримы, которые мы планировали...

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

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

Ну как бы очевидно, живет до возникновения сигнала.
А дальше зависит от его хэндлинга.
Только если стэк сломан, извлечь какую либо пользу из этого сложно.

Svoloch ★★★
()

Cannot access memory at address 0x4 очень часто падает при попытке читать писать по таким адресам 0x4 || 0x8 || 0xc || 0x10 когда есть уазатель на структуру и такой код structp->field когда указатель оказывается нулевой такие адреса отладчик показывает тоесть смещения полей

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

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

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

Тогда бы стэк был цел и бэктрэйс был бы нормальным. Разьве нет?

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

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

сэмплы для правого и левого каналов хранятся в двух массивах, соответственно структура mad_pcm_t такая:

typedef struct
{
  unsigned int   samplerate;            //sampling frequency (Hz)
  unsigned short nchannels;             // number of channels
  unsigned short length;                // actual number of samples
  unsigned short max_length;            // max number of samples per channel
  int            bit_per_samp;
  mad_fixed_t    *samples[2];           /* PCM output samples [ch][sample] */
  mad_fixed_t    buffer[0];             /* variable length buffer for samples*/
                                        /* its actual length is max_length*2 */
                                        /* THIS FIELD SHOULD ALWAYS BE THE LAST*/
}mad_pcm_t;

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

Смысл фикса был в том, что для моно сигнала поле samples[1] теперь устанавливается в NULL. После этого начало падать. При этом как моно, так и стерео стримы сейчас начали играться нормально (до этого, если моно игрался после стерео, то в правом канале был постоянный шум (проигрывались данные от предыдущего стрима забуференные).

Я так подробно не описывал, потому что хотел только получить совет, какие вообще есть приёмы по отладке SIGSEGV и не хотел задавать глупых вопросов (a la решите за меня проблему).

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

(Сразу говорю, больше козырей никаких в рукаве нет, теперь всё рассказал).

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

И еще вопрос к вам, osox, вы сказали, что линкер может мне выдать адреса всех функций и переменных, я так понимаю, это аналог map-файла в некоторых компиляторах. Т.е. по адресу я смогу понять, где проблема. Вот вы не подскажете, как эту информацию из gcc выпросить? Я щас читаю Гриффитса, пробовал генерировать ассемблерные листинги, листинги с подробными комментариями, пробовал опцию -M (как раз вроде для мап-файла) - но ничего полезного не получил. В листингах нет адресов (и вообще они генерятся для отдельных модулей, а не для всего приложения), с -M вообще отказывался вроде бы линковаться (точно не помню, может не линковаться, а компилиться, короче не собиралась программа, а дальше я не вникал).

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

Есть вариант с провернкой на разыменования и т.д.
Можно поставить бряк на чтение памяти по адресу samples[1].
Только или NULL тогда присваивать динамически или
отключать все оптимизации (что само по себе не плохо для отладки).
А то компилятор пожет предвычислить смещение, не используя samples[1].
Но это из рассчета что под дебагом тоже грохнется.

Да и еще. А нет ли до структур массивов? А то может просто в структуре
указатели перетираются при записи в массив больше его длинны?

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

Еще electric-fence есть и под arm вроде работает.

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

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

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

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

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

в gcc не знаю какая опция за это отвечает но да это обычный .map файл карта памяти так называемая генерируется линкером там указан относительный виртуальный адрес каждой функции + ее имя тоесть если там адрес некоей функции под именем к примеру flush_dns равен 0x0000C0DE и адрес базы вашего модуля 0x00400000 то получится 0x0040C0DE этот адрес вам и покажет отладчик если программа упадет в ней

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

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

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

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

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

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

спасибо за совет, mky, попробую наверно... а вот последнюю реплику не понял:

«s/отладочной программы/отладочной печати/»

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

Важное дополнение: в map-файл не включаются функции, объявленные как static (потому что компилятор не экспортирует статик-символы, соответственно линкер просто не видит их)

при этом надо помнить, что если функция была статик, и у неё просто убрать этот модификатор, то может остаться еще где-нибудь в файле прототип этой функции со словом static. Т.о. функция для компилятора всё равно останется статической и он её символ не будет экспортировать, и в мап-файл эта функция не будет включена.

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

Это в смысле, что я опечатался. Так как здесь нельзя править комментарии, то так на ЛОРе делают исправления комментариев :)

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

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

Ошибку нашёл, программа падать перестала.

Osox, вы были правы, но почему рушился стек (и рушился ли) я так и не понял.

Происходило следующее: имелось реально две структуры mad_pcm_t, одна хранила сэмплы до ресемплирования, а вторая - после. Время жизни первой структуры было чётко определено, она создавалась при открытии файла и уничтожалась при закрытии. Вторая структура создавалась и инициализировалась при открытии плеера и уничтожалась при закрытии. При этом она могла уничтожаться и создаваться заново входе работы, если недостаточно было отведено памяти под PCM-данные.

После воспроизведения монофонического стрима, когда указатель на массив сэмплов для правого канала становился нулевым, воспроизведение стереофонического стрима без переинициализации приводило к попытке записать ресемплированные данные правого канала начиная с NULL.

Огромное спасибо также Sjinks-у, за совет по командам gdb, «дающим пиццу для размышления», Legioner-у, за совет сосредоточить усилия на использовании core-файла и вообще всем, кто принял участие в обсуждении и помог советами.

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

ааа... теперь понял... здесь же одни линуксоиды... я просто редко пользуюсь sed-ом, поэтому не дошло до меня сразу :)

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