2014-09-12 62 views
0

我试图调试一个服务器应用程序,但我遇到了一些困难,需要打破我的位置。该应用程序分为两部分:GDB/DDD:使用多进程应用程序调试共享库C/C++

  • 一个服务器应用程序,它产生工作进程(不是线程)来处理传入的请求。服务器基本上派生出处理传入请求的先来先服务的进程。
  • 服务器还以共享库的形式加载插件。共享库定义了服务器能够处理的大部分服务,因此大部分实际处理都在这里完成。

作为喜悦的附加金块,工作人员处理“重生”(即退出并产生新的工作进程),因此孩子的PID会周期性变化。 -_-'

基本上我需要调试一个在共享库内调用的服务,但是我不知道要提前附加哪个进程,因为他们抓取临时请求。附加到主进程并设置断点似乎目前还没有工作。

有没有办法调试这个共享库代码,而无需预先附加到一个进程?基本上我想调试第一个调用函数的进程。

目前我可能会尝试将工作进程的数量限制为1而不重新生成,但是知道未来如何处理这种情况会更好,特别是如果我想要以确保它仍然在“发布”配置中工作。

我在一个试图用DDD和GDB进行调试的Linux平台上运行。

编辑:为了帮助说明我想要完成什么,让我提供一个关于概念的简短证明。

#include <iostream> 
#include <stdlib.h> 
#include <unistd.h> 

using namespace std; 

int important_function(const int child_id) 
{ 
    cout << "IMPORTANT(" << child_id << ")" << endl; 
} 

void child_task(const int child_id) 
{ 
    const int delay = 10 - child_id; 
    cout << "Child " << child_id << " started. Waiting " << delay << " seconds..." << endl; 
    sleep(delay); 
    important_function(child_id); 
    exit(0); 
} 

int main(void) 
{ 
    const int children = 10; 
    for (int i = 0; i < 10; ++i) 
    { 
     pid_t pid = fork(); 
     if (pid < 0) cout << "Fork " << i << "failed." << endl; 
     else if (pid == 0) child_task(i); 
    } 

    sleep(10); 
    return 0; 
} 

这一计划将叉掉10个进程,这将都睡10 - 调用important_function前ID秒钟,该功能中,我想在第一次调用子进程来调试(其中应,在这里,是最后一个我叉)。

将后续分叉模式设置为孩子会让我跟随分叉的第一个孩子,这不是我正在寻找的。我正在寻找称为重要功能的第一个孩子。

设置detach-on-fork关闭不起作用,因为它会暂停父进程,直到子进程分叉退出,然后继续fork其他进程(一次一个,最后一个退出之后)。

在真实场景中,能够附加到已经运行的服务器应用程序(已经产生线程)并暂停第一个调用该函数的应用程序也很重要。

我不确定这是否有可能,因为我没有看到太多的文档。基本上我想调试第一个应用程序来调用这行代码,不管它来自哪个进程。 (虽然只有我的应用程序会调用代码,但似乎我的问题可能更为一般:连接到调用代码的第一个进程,无论它是什么来源)。

回答

0

您可以在fork()中设置断点,然后发出“continue”命令,直到主进程的下一步是产生要调试的子进程。此时,在要调试的函数中设置一个断点,然后向gdb发出一个“set follow-fork-mode child”命令。当你继续时,gdb应该将你引入到处理断点的函数的子进程中。

如果您发出命令“set detach-on-fork off”,gdb将继续调试子进程。当到达该断点时,命中库中断点的进程应该停止。问题在于,当detach-on-fork关闭时,gdb会暂停所有启动时分叉的子进程。我不知道如何告诉它在分叉后继续执行这些过程。

我相信这个解决方案是编写一个gdb script来切换到每个进程并发出一个continue命令。应该停止使用断点处理函数的过程。

一位同事提出了另一种解决方案,让每个孩子都能继续下去。您可以打开“detach-on-fork”,在每个子进程的入口点插入一个打印语句,打印出它的进程ID,然后给它一个声明,告诉它等待变量的变化,如下所示:

{ 
    volatile int foo = 1; 
    printf("execute \"gdb -p %u\" in a new terminal\n", (unsigned)getpid()); 
    printf("once GDB is loaded, give it the following commands:\n"); 
    printf(" set variable foo = 0\n"); 
    printf(" c\n"); 
    while (foo == 1) __asm__ __volatile__ ("":::"memory"); 
} 

然后,启动gdb,启动主进程,并将输出传输到文件。使用bash脚本,您可以读取子进程ID,启动gdb的多个实例,将每个实例附加到不同子进程之一,并通过清除变量“foo”来指示每个实例继续运行。

+0

问题在于我不知道哪个孩子会接受并处理服务器请求。这不是以确定性的方式完成的;相反,进程等待传入连接,然后尝试接受它,所以哪个人获得空闲进程的驱动程序是由谁在请求进来时足够幸运地运行的。 – Anthony 2014-09-12 17:39:22

+0

请参阅我的答案的更新。 – plafratt 2014-09-12 21:40:19

+0

那么,关闭detach-on-fork的问题是使用follow-fork-mode子进程,它似乎暂停父进程,直到子进程停止。所以作为一个简单的概念证明,我写了一个应用程序,它分派10个子进程,每个进程在唤醒和调用我想要调试的函数之前睡10个秒,因此最后一个分支线程将(通常)首先到达那里。随着分离,我只能在第一个产生的线程上进行调试,关闭它后,我将不得不依次调试所有这些,这并不是我所需要的。 – Anthony 2014-09-16 00:33:55