我必须实现这样的事情:
a.out
)
a.out
a.out
这些步骤可以在shell下以交互方式执行,但我需要在程序中自动执行。我正在考虑使用fork为 GDB 创建子进程并使用popen
执行初始 GDB 启动命令,但是我如何定期发送 > GDB 子命令(bt,continue)到该子进程并让它执行它们?
我陷入了困境,任何想法都会受到高度赞赏。谢谢你。
答案 0 :(得分:1)
这是一个非常简单的实现。它在没有管道的情况下分配目标进程,我们只需要学习它pid
。然后它使用-p <PID>
选项分叉gdb以附加到我们的目标。在执行之前,GDB的fork为stdin
/ stdout
/ stderr
设置管道,以便我们可以远程控制GDB。
一些有趣的说明:
SIGINT
。您必须将SIGINT
发送到调试目标。这就是我分叉两次而不是启动gdb --args <target>
的原因。我需要调试进程的PID,以便发送SIGINT
。stdout
和stderr
时,必须读取它们,否则目标流程最终会阻止(当它们填充管道的缓冲区时)。我的实现在这里很愚蠢,因为我不想花时间使用线程或进行适当的select
调用。 read
/ write
而不是fread
,fwrite
,因为当他们无法读取我请求的金额时,他们的行为就会出现。'追踪'计划是:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/select.h>
char gdb_pid_buf[20];
char *gdb_argv[] =
{
"gdb",
"-p",
gdb_pid_buf,
NULL
};
char *child_argv[] =
{
"./looper",
NULL
};
const char GDB_PROMPT[] = "(gdb)";
int wait_for_prompt(const char *prefix, int fd)
{
char readbuf[4096];
size_t used = 0;
while(1)
{
ssize_t amt;
char *prompt;
char *end;
amt = read(fd, readbuf+used, sizeof(readbuf)-used-1);
if(amt == -1)
{
return 1;
}
else if(amt == 0)
{ }
else
{
used += amt;
readbuf[used] = '\0';
for(end = strstr(readbuf, "\n"); end; end= strstr(readbuf, "\n"))
{
size_t consumed;
size_t remaining;
*end = '\0';
printf("%s: %s\n", prefix, readbuf);
consumed = (end-readbuf) + strlen("\n");
remaining = used - consumed;
memmove(readbuf, readbuf+consumed, remaining);
used -= consumed;
}
prompt = strstr(readbuf, GDB_PROMPT);
if(prompt)
{
*prompt = '\0';
printf("%s: %s", prefix, readbuf);
printf("[PROMPT]\n");
fflush(stdout);
break;
}
}
}
return 0;
}
int main(int argc, char *argv)
{
int i;
int stdin_pipe[2];
int stdout_pipe[2];
int stderr_pipe[2];
pipe(stdin_pipe);
pipe(stdout_pipe);
pipe(stderr_pipe);
int gdb_pid;
int child_pid;
//Launch child
child_pid = fork();
if(child_pid == 0)
{
close(stdin_pipe[0]);
close(stdout_pipe[0]);
close(stderr_pipe[0]);
close(stdin_pipe[1]);
close(stdout_pipe[1]);
close(stderr_pipe[1]);
execvp(child_argv[0], child_argv);
return 0;
}
sprintf(gdb_pid_buf, "%d", child_pid);
//Launch gdb with command-line args to attach to child.
gdb_pid = fork();
if(gdb_pid == 0)
{
close(stdin_pipe[1]);
close(stdout_pipe[0]);
close(stderr_pipe[0]);
dup2(stdin_pipe[0],0);
dup2(stdout_pipe[1],1);
dup2(stderr_pipe[1],2);
execvp(gdb_argv[0], gdb_argv);
return 0;
}
close(stdin_pipe[0]);
close(stdout_pipe[1]);
close(stderr_pipe[1]);
//Wait for GDB to reach its prompt
if(wait_for_prompt("GDB", stdout_pipe[0]))
{fprintf(stderr,"child died\n");return 1;}
printf("[SENDING \"continue\\n\"]\n");
fflush(stdout);
write(stdin_pipe[1], "continue\n", strlen("continue\n"));
sleep(4);
printf("[SENDING \"CTRL+C\"]\n");
fflush(stdout);
kill(child_pid, SIGINT);
//Then read through all the output until we reach a prompt.
if(wait_for_prompt("POST SIGINT", stdout_pipe[0]))
{fprintf(stderr,"child died\n");return 1;}
//Ask for the stack trace
printf("[SENDING \"where\\n\"]\n");
fflush(stdout);
write(stdin_pipe[1], "where\n", strlen("where\n"));
//read through the stack trace output until the next prompt
if(wait_for_prompt("TRACE", stdout_pipe[0]))
{fprintf(stderr,"child died\n");return 1;}
kill(child_pid, SIGKILL);
kill(gdb_pid, SIGKILL);
}
目标计划looper
只是:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
while(1)
{
printf(".");
fflush(stdout);
sleep(1);
}
}
示例输出是:
$ ./a.out
.GDB: GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7
GDB: Copyright (C) 2013 Free Software Foundation, Inc.
GDB: License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
GDB: This is free software: you are free to change and redistribute it.
GDB: There is NO WARRANTY, to the extent permitted by law. Type "show copying"
GDB: and "show warranty" for details.
GDB: This GDB was configured as "x86_64-redhat-linux-gnu".
GDB: For bug reporting instructions, please see:
GDB: <http://www.gnu.org/software/gdb/bugs/>.
GDB: Attaching to process 8057
GDB: Reading symbols from /home/<nope>/temp/remotecontrol/looper...(no debugging symbols found)...done.
GDB: Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
GDB: Loaded symbols for /lib64/libc.so.6
GDB: Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
GDB: Loaded symbols for /lib64/ld-linux-x86-64.so.2
GDB: 0x00007f681b4f9480 in __nanosleep_nocancel () from /lib64/libc.so.6
GDB: Missing separate debuginfos, use: debuginfo-install glibc-2.17- 106.el7_2.4.x86_64
GDB: [PROMPT]
[SENDING "continue\n"]
....[SENDING "CTRL+C"]
POST SIGINT: Continuing.
POST SIGINT:
POST SIGINT: Program received signal SIGINT, Interrupt.
POST SIGINT: 0x00007f681b4f9480 in __nanosleep_nocancel () from /lib64/libc.so.6
POST SIGINT: [PROMPT]
[SENDING "where\n"]
TRACE: #0 0x00007f681b4f9480 in __nanosleep_nocancel () from /lib64/libc.so.6
TRACE: #1 0x00007f681b4f9334 in sleep () from /lib64/libc.so.6
TRACE: #2 0x0000000000400642 in main ()
TRACE: [PROMPT]
你可以从....
看到目标确实继续运行,即使GDB的输出是“继续”。直到后来当我读到它的stdout管道时才出现。