cs01 / cs01/pygdbmi

IoManager._get_responses_windows mangles token when reading from stdout

Open
#54 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
Python
Stars
260
Forks
53
PR merge metrics
No merged PRs in 30d

Description

**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](https://ftp.gnu.org/old-gnu/Manuals/gdb/html_chapter/gdb_22.html#SEC220):

```
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:

```py
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:

```py
logging.basicConfig(filename='example.log', level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
```

```py
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

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.