为什么 gdb mi 将 &"\n" 作为 return 提供给我的 -gdb-exit 命令?
Why does gdb mi give me &"\n" as return to my -gdb-exit command?
我正在编写一些必须与 gdb mi 通信的代码。所以我分叉了我的程序,放置了两个管道并在 child 中启动了 gdb mi,这样我就可以从 parent 中与 gdb mi 进行通信。
gdb 总是 returns "(gdb) \n" 当它完成时,所以我寻找它并编写我的下一个命令。这是我的代码的最小示例:
int main(){
printf("Starting Test\n");
int fromGDB[2], toGDB[2], nbytes;
pid_t childpid;
char readbuffer[80] = "";
pipe(fromGDB);
pipe(toGDB);
if((childpid = fork())==-1)
{
perror("fork");
exit(1);
}
if(childpid == 0){
close(toGDB[1]);
close(fromGDB[0]);
int backup = dup(1); //store stdout
if (dup2(fromGDB[1],1) < 0){puts("hat nicht geklappt");}
int backupStdin = dup(0); //store stdin
if (dup2(toGDB[0],0) < 0){puts("hat nicht geklappt");}
system("gdb -q --interpreter=mi2"); // start gdb mi
dup2(backup,1); // restore stdout
puts("child fertig");
exit(0);
}else{
close(toGDB[0]);
close(fromGDB[1]);
char* writeCommand = "";
int commandCounter = 0;
while (commandCounter <3){
nbytes = read(fromGDB[0],readbuffer,sizeof(readbuffer));
printf("parent recived: %s", readbuffer);
if (strncmp(readbuffer+strlen(readbuffer)-strlen("(gdb) \n"),"(gdb) \n", strlen("(gdb) \n")) == 0){
switch (commandCounter){
case 0: writeCommand = "-file-exec-and-symbols /home/dev/spielwiese/build/main\n"; break;
case 1: writeCommand = "-gdb-exit\n"; break;
default: writeCommand = "you should never reach here\n"; break;
}
write(toGDB[1],writeCommand,strlen(writeCommand)+1);
printf("wrote: %s", writeCommand);
commandCounter++;
}else if(strncmp(readbuffer,"^exit", sizeof("^exit")-1) == 0){
break;
}
memset(readbuffer,'[=12=]',strlen(readbuffer)); //clear the readbuffer
}
puts("parent fertig");
sleep(5);
}
return 0;
}
如果我手动调用相同的命令,这就是我得到的输出(-> 表示我的输入)
-> gdb -q --interpreter=mi2
=thread-group-added,id="i1"
(gdb)
-> -file-exec-and-symbols /home/dev/spielwiese/build/main
^done
(gdb)
-> -gdb-exit
^exit
但是如果我 运行 我的代码(本质上应该是一样的),我会得到这个输出:
Starting Test
parent recived: =thread-group-added,id="i1"
parent recived: (gdb)
wrote: -file-exec-and-symbols /home/dev/spielwiese/build/main
parent recived: ^done
(gdb)
wrote: -gdb-exit
parent recived: &"\n"
parent recived: ^done
(gdb)
wrote: you should never reach here
parent fertig
根据 gdb mi 手册,& 在日志条目之前,但是这个日志条目是空的,换行符除外。另外,我不知道为什么应该有一个退出日志条目,或者为什么它无法退出,但不会产生错误。
此外,如果您知道 gdb mi 的任何更好的来源:https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html#GDB_002fMI,请告诉我。
您的示例代码存在多个问题,其中许多已在评论中进行了总结:
read()
和 write()
仅传输指定的字节,或这些字节的前导子序列。他们不会将空字节附加到传输中。
read()
和 write()
不能保证在任何给定调用上传输请求的全部字节数。
- 您的缓冲区可能不够长,无法在一个
read()
. 中容纳来自 child 的一些回复
According to the gdb mi manual, an & preceeds a log entry, but this log entry is empty, exept for the newline. Also, I don't know why there should be a log entry for exiting, or why it fails to exit, but doesen't produce an error.
上面的 None 解释了日志条目或其他行为差异,但这可能是:
write(toGDB[1],writeCommand,strlen(writeCommand)+1);
假设传输了请求的全部字节数,您不仅要编写命令,还要编写字符串终止符。终止符不是 MI 协议的一部分,因此您的程序的行为与交互式会话不同。此外,特定的错误——一个额外的空字节——是一个特别有可能产生神秘输出的错误。我只能推测具体细节,但我可以相信 gdb
正在处理换行符后的额外空字节,就好像它终止了 zero-byte 命令一样。如果是这样,那么您实际上是在向 gdb 发出三个命令,而不是两个,并且 gdb 日志输出是关于空字节/空命令的。
您可能会发现流 I/O 比原始 I/O 对于您的目的更方便。这将使您免受 raw I/O 的许多特性的影响,并且会使 fgets()
可供您输入,这对您有利。 fdopen()
函数可以将流环绕在您的文件描述符周围:
#define BUFFER_SIZE 1024
// Parent
FILE *fFromGDB = fdopen(fromGDB[0], "r");
FILE *fToGDB = fdopen(fToGDB[1], "w");
if (!fFromGDB || ! fToGDB) // ...
// You probably want fToGDB to be line-buffered, not block buffered:
setvbuf(fToGDB, NULL, _IOLBF, BUFFER_SIZE);
然后使用fputs()
、fgets()
、fprintf()
、等。与 child.
互动
Also, if you know any better sources for gdb mi than this: https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html#GDB_002fMI , please let me know.
外部资源请求在off-topic这里。
无论如何,您参考的是相应的手册,而没有分析源代码,这是有关该问题的最终知识来源。
我正在编写一些必须与 gdb mi 通信的代码。所以我分叉了我的程序,放置了两个管道并在 child 中启动了 gdb mi,这样我就可以从 parent 中与 gdb mi 进行通信。 gdb 总是 returns "(gdb) \n" 当它完成时,所以我寻找它并编写我的下一个命令。这是我的代码的最小示例:
int main(){
printf("Starting Test\n");
int fromGDB[2], toGDB[2], nbytes;
pid_t childpid;
char readbuffer[80] = "";
pipe(fromGDB);
pipe(toGDB);
if((childpid = fork())==-1)
{
perror("fork");
exit(1);
}
if(childpid == 0){
close(toGDB[1]);
close(fromGDB[0]);
int backup = dup(1); //store stdout
if (dup2(fromGDB[1],1) < 0){puts("hat nicht geklappt");}
int backupStdin = dup(0); //store stdin
if (dup2(toGDB[0],0) < 0){puts("hat nicht geklappt");}
system("gdb -q --interpreter=mi2"); // start gdb mi
dup2(backup,1); // restore stdout
puts("child fertig");
exit(0);
}else{
close(toGDB[0]);
close(fromGDB[1]);
char* writeCommand = "";
int commandCounter = 0;
while (commandCounter <3){
nbytes = read(fromGDB[0],readbuffer,sizeof(readbuffer));
printf("parent recived: %s", readbuffer);
if (strncmp(readbuffer+strlen(readbuffer)-strlen("(gdb) \n"),"(gdb) \n", strlen("(gdb) \n")) == 0){
switch (commandCounter){
case 0: writeCommand = "-file-exec-and-symbols /home/dev/spielwiese/build/main\n"; break;
case 1: writeCommand = "-gdb-exit\n"; break;
default: writeCommand = "you should never reach here\n"; break;
}
write(toGDB[1],writeCommand,strlen(writeCommand)+1);
printf("wrote: %s", writeCommand);
commandCounter++;
}else if(strncmp(readbuffer,"^exit", sizeof("^exit")-1) == 0){
break;
}
memset(readbuffer,'[=12=]',strlen(readbuffer)); //clear the readbuffer
}
puts("parent fertig");
sleep(5);
}
return 0;
}
如果我手动调用相同的命令,这就是我得到的输出(-> 表示我的输入)
-> gdb -q --interpreter=mi2
=thread-group-added,id="i1"
(gdb)
-> -file-exec-and-symbols /home/dev/spielwiese/build/main
^done
(gdb)
-> -gdb-exit
^exit
但是如果我 运行 我的代码(本质上应该是一样的),我会得到这个输出:
Starting Test
parent recived: =thread-group-added,id="i1"
parent recived: (gdb)
wrote: -file-exec-and-symbols /home/dev/spielwiese/build/main
parent recived: ^done
(gdb)
wrote: -gdb-exit
parent recived: &"\n"
parent recived: ^done
(gdb)
wrote: you should never reach here
parent fertig
根据 gdb mi 手册,& 在日志条目之前,但是这个日志条目是空的,换行符除外。另外,我不知道为什么应该有一个退出日志条目,或者为什么它无法退出,但不会产生错误。 此外,如果您知道 gdb mi 的任何更好的来源:https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html#GDB_002fMI,请告诉我。
您的示例代码存在多个问题,其中许多已在评论中进行了总结:
read()
和write()
仅传输指定的字节,或这些字节的前导子序列。他们不会将空字节附加到传输中。read()
和write()
不能保证在任何给定调用上传输请求的全部字节数。- 您的缓冲区可能不够长,无法在一个
read()
. 中容纳来自 child 的一些回复
上面的According to the gdb mi manual, an & preceeds a log entry, but this log entry is empty, exept for the newline. Also, I don't know why there should be a log entry for exiting, or why it fails to exit, but doesen't produce an error.
None 解释了日志条目或其他行为差异,但这可能是:
write(toGDB[1],writeCommand,strlen(writeCommand)+1);
假设传输了请求的全部字节数,您不仅要编写命令,还要编写字符串终止符。终止符不是 MI 协议的一部分,因此您的程序的行为与交互式会话不同。此外,特定的错误——一个额外的空字节——是一个特别有可能产生神秘输出的错误。我只能推测具体细节,但我可以相信 gdb
正在处理换行符后的额外空字节,就好像它终止了 zero-byte 命令一样。如果是这样,那么您实际上是在向 gdb 发出三个命令,而不是两个,并且 gdb 日志输出是关于空字节/空命令的。
您可能会发现流 I/O 比原始 I/O 对于您的目的更方便。这将使您免受 raw I/O 的许多特性的影响,并且会使 fgets()
可供您输入,这对您有利。 fdopen()
函数可以将流环绕在您的文件描述符周围:
#define BUFFER_SIZE 1024
// Parent
FILE *fFromGDB = fdopen(fromGDB[0], "r");
FILE *fToGDB = fdopen(fToGDB[1], "w");
if (!fFromGDB || ! fToGDB) // ...
// You probably want fToGDB to be line-buffered, not block buffered:
setvbuf(fToGDB, NULL, _IOLBF, BUFFER_SIZE);
然后使用fputs()
、fgets()
、fprintf()
、等。与 child.
Also, if you know any better sources for gdb mi than this: https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html#GDB_002fMI , please let me know.
外部资源请求在off-topic这里。
无论如何,您参考的是相应的手册,而没有分析源代码,这是有关该问题的最终知识来源。