首页 文章

GDB / DDD:使用多进程应用程序C / C调试共享库

提问于
浏览
0

我正在尝试调试服务器应用程序,但我遇到了一些困难,我需要在哪里 . 该应用程序分为两部分:

  • 一个服务器应用程序,它生成工作进程(而不是线程)来处理传入的请求 . 服务器基本上会产生进程,这些进程将先处理先前服务的传入请求 .

  • 服务器还以共享库的形式加载插件 . 共享库定义了服务器能够处理的大多数服务,因此大多数实际处理都在此处完成 .

作为一个额外的快乐, Worker 处理“重生”(即退出并产生新的 Worker 流程),因此孩子的PID会定期更改 . -_-”

基本上我需要调试在共享库中调用的服务,但我不知道哪个进程要提前附加,因为它们会临时获取请求 . 到目前为止,附加到主进程并设置断点似乎不起作用 .

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

暂时我可能会尝试将工作进程的数量限制为1而没有重生,但是知道将来如何处理这样的情况会很好,特别是如果我想确定它仍然在“发布”配置中工作 .

我正在Linux平台上运行,尝试使用DDD和GDB进行调试 .

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

#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个进程,这些进程将在调用 important_function 之前全部休眠10-id秒,这是我想在第一个调用子进程中调试的函数(这里应该是我最后一个调用的进程) .

将follow-fork-mode设置为child将让我跟进分叉的第一个孩子,这不是我正在寻找的 . 我正在寻找第一个称之为重要功能的孩子 .

设置分离分离没有帮助,因为它会暂停父进程,直到子进程分叉退出,然后继续分叉其他进程(一次一个,在最后退出之后) .

在实际场景中,我能够连接已经运行的已经生成线程的服务器应用程序并停止调用该函数的第一个服务器应用程序也很重要 .

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

1 回答

  • 0

    您可以在fork()中设置断点,然后发出“continue”命令,直到主进程的下一步是生成您要调试的子进程 . 此时,在要调试的函数上设置断点,然后向gdb发出“set follow-fork-mode child”命令 . 当你继续时,gdb应该把你挂钩到断点所在的函数的子进程 .

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

    我认为解决这个问题的方法是写一个gdb script来切换到每个进程并发出一个continue命令 . 使用断点命中函数的过程应该停止 .

    一位同事为让每个孩子继续下去的问题提供了另一种解决方案 . 你可以打开“detach-on-fork”,在每个子进程的入口点插入一个print语句,打印出它的进程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”来发送每个实例的信号 .

相关问题