Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to control gdb using Python subprocess.Popen?

So I'm writing (or at least trying to) a program to compare the outputs of two gdb runs in python. This is what I have so far:

from subprocess import *
import subprocess

file = raw_input('enter program name (with a ./ if in local directory): ')

p1 = Popen(['gdb', file], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = Popen(['gdb', file], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

p1.communicate(input='break main')
p2.communicate(input='break main')

args1 = raw_input('args for running program (from file) (ie r < input.txt): ')
args2 = raw_input('args for running program (from file) (for program 2...): ')

p1.communicate(input=args1)
p2.communicate(input=args2)

while True:
    p1out = p1.communicate(input='continue')[0]
    p2out = p2.communicate(input='continue')[0]

    if p1out != p2out:
        print 'difference: '
        print 'p1: ' + p1out
        print 'p2: ' + p2out
        cont = raw_input('continue (y/n): ')
        if cont != 'y':
            break

Now the problem is that this doesn't seem to be working. Any ideas on where I might be going wrong?

More detail: The point of the program is to take in an executable, break at the main function, and then run through each until the output varies between the two. This is intended as a debugging tool (that I would use, even if no one else would!). Then when you find a difference it gives you the choice of whether to end the program, or to continue. In theory, this should work, but I'm just not sure what's messing up.

like image 844
higgs241 Avatar asked Oct 21 '22 06:10

higgs241


1 Answers

.communicate waits for a Popen object to finish execution. Since you are trying to talk to gdb while it's running, this will just hang forever. gdb isn't going to exit without any input. Additionally you need to write the newlines yourself to emulate the user hitting enter.

what you want to do is write into and read from gdb while it is executing. For this, use p1.stdin.write('break main\n') (note the '\n') when sending input, and p1.stdout.readline() when reading output. This applies to the break in the beginning, the args you are sending, and the continues.

On sending the arguments and beggining executing, you should also be sure to start gdb.

p1.stdin.write('start ' + args1 + '\n')
p2.stdin.write('start ' + args2 + '\n')

You also want to handle the case where one process terminates before another. You can use Popen.poll to check if a process has completed yet, it will return None if it has not. Although this may not be exactly how you want to handle it, you can change the top of your loop to something like this:

while True:
    if p1.poll() is not None and p2.poll() is not None:
        print 'p1 and p2 have both finished'
        break
    elif p1.poll() is not None:
        print 'p1 finished before p2'
        break
    elif p2.poll() is not None:
        print 'p2 finished before p1'
        break

    p1.stdin.write('continue\n')
    p2.stdin.write('continue\n')
    p1out = p1.stdout.readline()
    p2out = p2.stdout.readline()

Reading a single line will likely not be correct, and you will have to calibrate the number of lines you read in order to get the correct output.

You should either add reads to stderr, or send it to /dev/null if you don't care about it. If you don't do either of these, the PIPE buffer can fill and cause it to hang.

like image 150
Ryan Haining Avatar answered Oct 29 '22 03:10

Ryan Haining