Is it possible to set a gdb watchpoint programmatically?

后端 未结 5 1756

I want to set a watchpoint (break on hardware write) temporarily in my C++ program to find memory corruption.

I\'ve seen all the ways to do it manually through gdb,

5条回答
  •  广开言路
    2020-12-04 20:47

    The program itself can supply commands to the GDB. You'll need a special shell script to run GDB though.

    Copy this code into the file named untee, and execute chmod 755 untee

    #!/bin/bash
    
    if [ -z "$1" ]; then
        echo "Usage: $0 PIPE | COMMAND"
        echo "This script will read the input from both stdin and PIPE, and supply it to the COMMAND."
        echo "If PIPE does not exist it will be created with mkfifo command."
        exit 0
    fi
    
    PIPE="$1"
    
    if [ \! -e "$PIPE" ]; then
        mkfifo "$PIPE"
    fi
    
    if [ \! -p "$PIPE" ]; then
        echo "File $PIPE does not exist or is not a named pipe" > /dev/stderr
        exit 1
    fi
    
    # Open the pipe as a FD 3
    echo "Waiting for $PIPE to be opened by another process" > /dev/stderr
    exec 3<"$PIPE"
    echo "$PIPE opened" > /dev/stderr
    OPENED=true
    
    while true; do
        read -t 1 INPUT
        RET=$?
        if [ "$RET" = 0 ]; then
            echo "$INPUT"
        elif [ "$RET" -lt 128 ]; then
            echo "stdin closed, exiting" > /dev/stderr
            break
        fi
    
        if $OPENED; then
            while read -t 1 -u 3 INPUT; do
                RET=$?
                if [ "$RET" = 0 ]; then
                    echo "$INPUT"
                else
                    if [ "$RET" -lt 128 ]; then
                        echo "$PIPE closed, ignoring" > /dev/stderr
                        OPENED=false
                    fi
                    break
                fi
            done
        fi
    done
    

    And now the C code:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    void gdbCommand(const char *c)
    {
        static FILE * dbgpipe = NULL;
        static const char * dbgpath = "/tmp/dbgpipe";
        struct stat st;
    
        if( !dbgpipe && stat(dbgpath, &st) == 0 && S_ISFIFO(st.st_mode) )
                dbgpipe = fopen(dbgpath, "w");
        if( !dbgpipe )
            return;
        fprintf(dbgpipe, "%s\n", c);
        fflush(dbgpipe);
    }
    
    void gdbSetWatchpoint(const char *var)
    {
        char buf[256];
        snprintf(buf, sizeof(buf), "watch %s", var);
    
        gdbCommand("up"); /* Go up the stack from the kill() system call - this may vary by the OS, you may need to walk the stack more times */
        gdbCommand("up"); /* Go up the stack from the gdbSetWatchpoint() function */
        gdbCommand(buf);
        gdbCommand("continue");
        kill(getpid(), SIGINT); /* Make GDB pause our process and execute commands */
    }
    
    int subfunc(int *v)
    {
        *v += 5; /* GDB should pause after this line, and let you explore stack etc */
        return v;
    }
    
    int func()
    {
        int i = 10;
        printf("Adding GDB watch for var 'i'\n");
        gdbSetWatchpoint("i");
    
        subfunc(&i);
        return i;
    }
    
    int func2()
    {
        int j = 20;
        return j + func();
    }
    
    
    int main(int argc, char ** argv)
    {
        func();
        func2();
        return 0;
    }
    

    Copy that to the file named test.c, compile with command gcc test.c -O0 -g -o test then execute ./untee /tmp/dbgpipe | gdb -ex "run" ./test

    This works on my 64-bit Ubuntu, with GDB 7.3 (older GDB versions might refuse to read commands from non-terminal)

提交回复
热议问题