GDB step into all lines to get full application flow

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.

One thought on “GDB step into all lines to get full application flow”

  1. 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

Leave a Reply

Your email address will not be published. Required fields are marked *

(Your message will only be visible after moderation)