Skip to content

IoManager._get_responses_windows mangles token when reading from stdout #54

@leonardopsantos

Description

@leonardopsantos

Describe the bug

The IoManager._get_responses_windows() method sometimes doesn't read the MI token.

To Reproduce

I'm using pygdbmi to drive arm-none-eabi-gdb. I'm issuing commands using a token:

0-gdb-set trace-commands on
1-gdb-set logging on

And so forth. The reply from GDB is

0^done
1^done

The problem is that when issuing the command 2-gdb-set print repeats 0, GDB replies with 2^done, but IoManager._get_responses_windows() only reads ^done. As I'm using the tokens to match the commands with the replies, my SW fails.

To really check that it's a problem with pygdbmi, I tried to directly call arm-none-eabi-gdb with the following script:

gdb_args = [arm-none-eabi-gdb]
gdb_args.append('--init-command=.gdbinit')
gdb_args.append('--quiet')
gdb_args.append('--interpreter=mi3')

with open('gdb_stdout.txt', 'w') as f_out:

    gdb_process = subprocess.Popen(
        gdb_args,
        shell=False,
        stdout=f_out,
        stdin=subprocess.PIPE,
        stderr=subprocess.PIPE,
        bufsize=0,
    )
    gdb_process.stdin.write('0-gdb-set trace-commands on\n'.encode())
    time.sleep(0.5)
    gdb_process.stdin.write('1-gdb-set logging on\n'.encode())
    time.sleep(0.5)
    gdb_process.stdin.write('2-gdb-set print repeats 0\n'.encode())
    time.sleep(0.5)
    gdb_process.stdin.write('3-gdb-set print elements 0\n'.encode())
    time.sleep(0.5)

The gdb_stdout.txt file is

(gdb) 
~"+set trace-commands on\n"
0^done
(gdb) 
~"+set logging on\n"
~"Already logging to gdb.txt.\n"
1^done
(gdb) 
~"+set print repeats 0\n"
2^done
(gdb) 
~"+set print elements 0\n"
3^done
(gdb) 
~"Exception condition detected on fd 0\n"
~"error detected on stdin\n"

So, clearly, GDB is indeed replying 2^done

I've enabled debugging in IOManager with and also added some more debug statements:

logging.basicConfig(filename='example.log', level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
    def _get_responses_windows(self, timeout_sec):
        """Get responses on windows. Assume no support for select and use a while loop."""
        timeout_time_sec = time.time() + timeout_sec
        responses = []
        while True:
            responses_list = []
            try:
                self.stdout.flush()
                raw_output = self.stdout.readline().replace(b"\r", b"\n")
                logger.debug("raw stdout {}".format(repr(raw_output)))
                responses_list = self._get_responses_list(raw_output, "stdout")
            except IOError:
                pass

            try:
                self.stderr.flush()
                raw_output = self.stderr.readline().replace(b"\r", b"\n")
                logger.debug("raw stderr {}".format(repr(raw_output)))
                responses_list += self._get_responses_list(raw_output, "stderr")
            except IOError:
                pass

And got:

DEBUG:pygdbmi.IoManager:writing: 2-gdb-set print repeats 0
DEBUG:pygdbmi.IoManager:raw stdout b'"+set print repeats 0\\n"\n\n'
DEBUG:pygdbmi.IoManager:{'message': None,
 'payload': '"+set print repeats 0\\n"',
 'stream': 'stdout',
 'type': 'output'}
DEBUG:pygdbmi.IoManager:raw stdout b'^done\n\n'
DEBUG:pygdbmi.IoManager:{'message': 'done',
 'payload': None,
 'stream': 'stdout',
 'token': None,
 'type': 'result'}

Looking at the gdb log, it's definitively printing the token:

~"+set print repeats 0\n"
2^done
(gdb) 

Please complete the following information:

  • OS: Windows 10 64 bits
  • pygdbmi version (pip freeze output): 0.10.0.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions