奇怪的SEGFAULTS使用fprintf

时间:2010-10-20 17:36:10

标签: c multithreading gdb pthreads printf

我在调试多线程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与variadic(?)args一起使用。除了模式之外没有任何东西调用它。 Printf函数有或没有args。

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无响应之前收到以下消息。终止需要kill -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 funkiness仍然是我的头号嫌疑人。不幸的是,我暂时停留在架构上。

提前致谢。

2 个答案:

答案 0 :(得分:3)

第一步是尝试在不引入线程的情况下运行该函数。只需编写一个具有main的.c文件,该文件尽可能地为启动线程做好准备,然后只是调用该函数而不是这样做。如果只用一个线程重新创建问题,则调试起来要容易得多。

此外,如果您使用的是gcc,则应编译:

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

除了正常的标志(至少在你发现问题之前)。这些将有助于调试,并可能在编译时发出更多警告。我假设您知道-O标志如何影响调试能力和功能(特别是如果您已经在C代码中做错了或未定义的话)。

当您在GDB中并且事情看起来已经锁定或者程序需要很长时间才能执行某些操作时,您通常可以按CTRL Z返回(gdb)而不会终止程序。这会向程序发出停止信号并让您再次与GDB交互,这样您就可以找到程序实际执行的操作。

修改

我显然在 comments 讨论中解决了这个问题,所以我会在这里写下问题所在。

快速浏览代码并没有提出导致分段错误的问题(非法内存访问),而Zypsy(OP)告诉我,直接从main调用而不是运行时函数运行正常通过一个单独的线程。

Valgrind报告说,线程的堆栈空间无法扩展到某个地址。在Linux中,主线程的堆栈以一种容易增长的方式映射到应用程序中,但是当为线程堆栈分配内存时,通常不会这样做。

我让Zypsy(OP)插入一些代码,这些代码会打印出线程堆栈(printf("thread stk = %p\n", &input);)上已知低的地址,以便可以将该值与该地址中给出的地址进行比较。失败的消息。从这里我可以猜到堆栈大小。这并没有暗示在线程函数的开头和它的失败之间消耗了很多堆栈空间,但是对于问题中的代码来说空间似乎也不是太小(尽管它显然太小了)。

因为pthread_create函数允许您接受线程属性的设置(传入NULL)或传入一个参数,指定线程的各种设置我询问是否调用了代码可以发布pthread_create以便我可以查看是否存在任何可疑设置。

在查看此代码之后(围绕各种pthread_函数的特定于应用程序的包装器)我看到实际上已经设置了一些与堆栈相关的属性。我要求OP查看对此函数的调用,并查找与如何分配堆栈有关的可疑事项(确保大小值和分配的内存大小实际上是相同的)。事实证明,OP然后发现该线程的堆栈被分配的小于其他线程的堆栈。毕竟堆栈太小了。

答案 1 :(得分:0)

通常 - 这些问题与内存损坏有关。

是一个很好的例子。当你稍微改变代码时,不同行上的段错误等症状就是一个很好的例子。

尝试通过valgrind等工具运行程序,可以保证看到一些非法的内存访问。解决这些问题,我怀疑事情会有效。