While debugging one application that I was working with, I needed to have full application flow, I want to get a list of all functions calls, and I couldn’t find an automated way to do that within GDB itself, or within any other IDE like eclipse or netbeans or codeblocks. Although, I might have not tested different IDEs enough, since I found this more straight forward, and does the job nicely.
This script runs any given application, with the given arguments if any, and keep inputing ‘n’ to gdb and reading the output, the output is then dumped to both, the terminal and to a file myfile2.txt.
Download the script:
wget http://www.codeground.net/downloads/gdbwalkthrough.c |
This script can be compiled with:
gcc gdbwalkthrough.c -o gdbwalkthrough |
Usage:
./gdbwalkthrough <application full path> [application arguments] |
The script:
#include <stdio.h> #include <stdlib.h> #include <string.h> void startDebugger (); void stopDebugger (); char gdbRead (); void gdbWrite (char c); void readUntilPrompt (unsigned char *data); // If data is NULL copy to stdout void writeDebugger (unsigned char*cmd, unsigned char* answer); int fd_to_debugger, fd_from_debugger, fd_err_from_debugger; FILE *pFile2 = NULL; int fsync_ct = 0; char **argv_out; void gdbWrite (char c) { write (fd_to_debugger, &c, 1); fsync (fd_to_debugger); } char gdbRead () { char c; read (fd_from_debugger, &c, 1); return c; } void writeDebugger (unsigned char*cmd, unsigned char *answer) { unsigned char a[10000]; if (answer==NULL) answer = a; fprintf (stderr, " ---COMMAND>%s\n", cmd); write (fd_to_debugger, cmd, strlen (cmd)); write (fd_to_debugger, "\n", 1); fsync (fd_to_debugger); readUntilPrompt (answer); //fprintf (stderr, "******\n%s\n******\n", answer); int sln = strlen(answer); char * a4 = answer+sln-5; char * a5 = "(gdb)"; if (strcmp(a4 , a5)==0) { answer[sln-6] = '\0'; } fprintf (stderr, "%s\n", answer); if(pFile2==NULL) pFile2=fopen("myfile2.txt", "a"); if(pFile2==NULL) { perror("Error opening file."); } else { fprintf(pFile2, "%s\n", answer); fsync_ct++; fsync(pFile2); } } void readUntilPrompt (unsigned char *data) { char buf[256], c; char *str = "(gdb)"; int nof_read, n = strlen (str); if (n>255) exit(-1); memset (buf, 0, 256); while (1) { memmove(buf, buf+1, n-1); nof_read = read (fd_from_debugger, &c, 1); if (nof_read!=1) break; buf[n-1] = c; if (data!=NULL) { *data = c; data++; } else { printf ("%c", c); } if (buf[n-1]==0) exit(-1); if (!strcmp (str, buf)) break; } if (data!=NULL) *data = 0; } void stopDebugger () { close(fd_to_debugger); close(fd_err_from_debugger); close(fd_from_debugger); } void startDebugger () { unsigned char tmp_string[1024]; int stdin_pipe[2]; int stdout_pipe[2]; int stderr_pipe[2]; int fork_result; int n, i; if( (pipe(stdin_pipe) != 0) || (pipe(stdout_pipe) != 0) || (pipe(stderr_pipe) != 0)) { fprintf (stderr, "Could not create pipes!\n"); exit (-1); } fork_result = fork(); if(fork_result == -1) { fprintf(stderr, "Fork Failure\n"); exit(-1); } if(fork_result == 0) { // We are child close(0); // Close childs stdin dup(stdin_pipe[0]); // Child now has STDIN that is stdin_pipe[0] close(stdin_pipe[0]); close(stdin_pipe[1]); close(1); // Close childs stdout dup(stdout_pipe[1]); // Child now has STDOUT that is stdout_pipe[1] //setvbuf (stdout, (char*)NULL, _IONBF, 0); // Do not buffer stdout! close(stdout_pipe[0]); close(stdout_pipe[1]); close(2); // Close childs STDERR dup(stderr_pipe[1]); close(stderr_pipe[0]); close(stderr_pipe[1]); execvp("gdb",argv_out); // If we get here, the execlp failed! fprintf (stderr, "Execution of gdb failed!\n"); exit(-1); } else { fd_to_debugger = stdin_pipe[1]; // Write to stdin fd_from_debugger = stdout_pipe[0]; // Read from stdout fd_err_from_debugger = stderr_pipe[0]; // Read from stderr close(stdin_pipe[0]); // No reads from stdin close(stdout_pipe[1]); // No writes to stdout close(stderr_pipe[1]); // No writes to stderr readUntilPrompt (NULL); } } int main (int argc, char **argv) { if (argc<2) { fprintf (stdout, "Usage: %s <application full path> [application arguments]\n\n",argv[0]); exit(0); } argv_out = malloc(argc+3); int x=0; argv_out[x++] = "gdb"; argv_out[x++] = "--args"; int i=0; while (argc) { i++; argv_out[x++] = argv[i]; argc--; } argv_out[x++] = NULL; char ** argv_out2 = argv_out; fprintf (stdout, "Starting with arguments: \n"); while (*argv_out2) { fprintf (stdout, ">> %s\n", *argv_out2++); } fprintf (stdout, "\n\n"); sleep(2); startDebugger(); //writeDebugger("info shared", NULL); writeDebugger("b main", NULL); writeDebugger("run", NULL); while(1) writeDebugger("s",NULL); stopDebugger(); } |
This is a modified version of what is originally posted by “Fredrik Ohrstrom” on http://www.mail-archive.com/[email protected]/msg00031.html, the modifications includes automation of stepping in all functions and clean up the output, and output to txt file, among other minor modifications.
Tried but getting:
gdbwalkthrough: malloc.c:2392: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) – 1) * 2])) – __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 *(sizeof(size_t))) – 1)) & ~((2 *(sizeof(size_t))) – 1))) && ((old_top)->size & 0x1) && ((unsigned long) old_end & pagemask) == 0)’ failed.
Aborted
using
gcc –version
gcc (SUSE Linux) 4.8.5
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
and
qmake-qt5 CONFIG+=debug QMAKE_CXXFLAGS_DEBUG*=-pg QMAKE_LFLAGS_DEBUG*=-pg . && make && ./test
/home/user/gdbwalkthrough /home/user/test