Reputation: 27
I am writing some code that has to communicate with gdb mi. So I forked my programm, put two pipes in place and started gdb mi in the child, so that I can communicate with gdb mi from the parent. gdb always returns "(gdb) \n" when it is finished, so I look for that and write my next command. This is a minimum example of my code:
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,'\0',strlen(readbuffer)); //clear the readbuffer
}
puts("parent fertig");
sleep(5);
}
return 0;
}
If I call the same commands by hand, thats the output I get (-> means input from me)
-> gdb -q --interpreter=mi2
=thread-group-added,id="i1"
(gdb)
-> -file-exec-and-symbols /home/dev/spielwiese/build/main
^done
(gdb)
-> -gdb-exit
^exit
But if I run my code, which should be essentialy the same, I get this output:
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
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. 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.
Upvotes: 0
Views: 177
Reputation: 180306
There are several issues with your example code, many of which have already been summarized in comments:
read()
and write()
transfer only the bytes specified, or a leading subsequence of those. They do not append null bytes to the transfer.read()
and write()
are not guaranteed to transfer the full number of bytes requested on any given call.read()
.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 of the above explain the log entry or other behavior differences, but this probably does:
write(toGDB[1],writeCommand,strlen(writeCommand)+1);
Supposing that the full number of bytes requested is transferred, you are writing not only the command, but also a string terminator. The terminator is not part of the MI protocol, so your program is not behaving the same as your interactive session. Moreover, the particular error -- an extra null byte -- is one that is especially likely to produce mysterious output. I can only speculate about the specifics, but I could believe that gdb
is treating the extra null byte immediately following a newline as if it terminated a zero-byte command. If so, then you are effectively issuing three commands to gdb, not two, and the gdb log output is about the null byte / empty command.
You would probably find stream I/O to be more convenient for your purposes than raw I/O. That would insulate you from many of the idiosyncrasies of raw I/O, and it would make fgets()
available to you for input, which would be to your advantage. The fdopen()
function can wrap streams around your file descriptors:
#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);
Then use fputs()
, fgets()
, fprintf()
, etc. to interact with the 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.
Requests for external resources are off-topic here.
In any event, you are referencing the appropriate manual, and short of analyzing the source code, that is the ultimate source of knowledge on the matter.
Upvotes: 1