Странные сегменты с использованием fprintf - PullRequest
4 голосов
/ 20 октября 2010

Мне очень тяжело отлаживать многопоточное C-приложение, в которое я внес несколько изменений. Я не смог использовать GDB, чтобы помочь идентифицировать проблему (см. Код ниже для получения дополнительной информации).

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

void tskProcessTenMinuteTables(void *input)
{
    /* Check the minute as soon as we start.  If we're started on a ten min
     * boundary, sleep for one minute.
     */
    time_t now;
    time_t wakeup;
    struct tm *next_tick_ptr;

    now = time(NULL);
    next_tick_ptr = localtime(&now);

    /* returns a time struct populated w/ next ten min boundary */
    GetNextTenMinBoundary(next_tick_ptr); 
    wakeup = mktime(next_tick_ptr);

    sleep(2); /* Without this sleep, the following if() was always true. */ 


    if(next_tick_ptr->tm_min % 10 == 0)   
    {
     fprintf(stderr, "On tenmin boundary on initialization.. task sleeping for 60 seconds.\n");

        /*  debug statements to test the cause of segfault.  */ 
     fprintf(stderr, "NOM NOM NOM\n"); 
     printf( "Test%d\n", 1);
     fprintf(stderr, "Test%d\n", 2);  /* <~~~ This statement is the guilty party */

        sleep(60);
    }

    /*  Main loop.  Every loop besides the tick itself will consist only 
    *   of a call to time and a comparison of current stamp with wakeup.
    *   this should be pretty light on the processing side.
    *
    *   Re-implement this as a sleep/awake with a signal in the future.
    */
    while(1)
    {
        now = time(NULL);

        if( now >= wakeup )
        {
            fprintf(stderr, "Triggered 1.\n");
            fprintf(stderr, "Triggered 2.\n");  

            char statement[150];

            fprintf(stderr, "Triggered 3.\n");      
            sprintf(statement, "SELECT ten_min_end(%d::int2)",GetTenMinPeriodNumber());
            fprintf(stderr, "Triggered 4.\n");
            DBCallStoredProcedure(statement);
            fprintf(stderr, "Triggered 5.\n");
    }

}

Причина в попытке использовать fprintf с переменными (?) Аргументами. Вызов его без чего-либо, кроме шаблона работает. Функции printf с аргументами или без них.

fprintf(stderr, "Hi #%d.\n", 1); <~~ segfault
fprintf(stderr, "Hi #1.\n"); <~~ works
printf("Hi #%d.\n", 1); <~~ works
printf("Hi #1.\n"); <~~ works

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

$gdb ir_client
(gdb) r
Starting program: /home/ziop/Experimental_IR_Clients/ir-10-20/IR_Client/obj-linux-x86/ir_client 
[Thread debugging using libthread_db enabled]
[New Thread 0xb7fe5b70 (LWP 32269)]
[New Thread 0xb7fc4b70 (LWP 32270)]
(032266 - -1208067216) 20-Oct-2010 10:56:19.59 - IR_Client_ConnectCmdPort - Socket connected.
[New Thread 0xb7ffdb70 (LWP 32272)]
(032266 - main thread) 20-Oct-2010 10:56:19.59 - sl_exit - Exiting thread with code 0.
On tenmin boundary on initialization.. task sleeping for 60 seconds.
NOM NOM NOM 
Test1

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

Спасибо заранее.

Ответы [ 2 ]

3 голосов
/ 20 октября 2010

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

Кроме того, если вы используете gcc, вы должны скомпилировать с:

-fstack-protector-all -Wstack-protector -fno-omit-frame-pointer

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

Когда вы находитесь в GDB и все выглядит так, как будто онизаблокирован или программа занимает много времени, чтобы сделать что-то, вы обычно можете нажать CTRL Z, чтобы вернуться к (gdb), не убивая программу.Это подает сигнал остановки программе и позволяет вам снова взаимодействовать с GDB, чтобы вы могли узнать, что на самом деле делает программа.комментарии обсуждение, поэтому я напишу, в чем здесь проблема.

Быстрый взгляд на код не выявил проблемы, которая привела бы к ошибке сегментации (незаконный доступ к памяти), и Zypsy (OP) сказал мне, что функция работала нормально, когда вызывался напрямую из main, а не через отдельный поток.

Valgrind сообщил, что пространство стека потока не может быть расширено до определенного адреса.В Linux стек основного потока отображается в приложении таким образом, что он может легко расти, но это часто не делается, когда память выделяется для стеков потоков.

Я попросил Zypsy (OP):вставьте некоторый код, который будет распечатывать адрес чего-то, что известно как низкий в стеке потоков (printf("thread stk = %p\n", &input);), чтобы это значение можно было сравнить с адресом, указанным в сообщении об ошибке.Исходя из этого, я мог предположить размер стека.Это не предполагало, что между началом функции потока и ее ошибкой потреблялось очень много стекового пространства, но пространство также не выглядело слишком маленьким для рассматриваемого кода (хотя, по-видимому, оно оказалось слишком маленьким).

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

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

0 голосов
/ 20 октября 2010

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

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

...