Search code examples
pythonpython-3.xasynchronouspexpect

Understanding pexpect's async properly


I'm trying to write a gdb frontend using pexpect to communicate with gdb/mi. I'm new to pexpect and trying to figure out async property of expect_exact() function. I wrote a simple test which looks like this:

def attach(str):
    global p
    p=pexpect.spawnu('sudo gdb --interpreter=mi')
    p.expect_exact("(gdb) ")
    GDB_Engine.send_command("set target-async 1")
    GDB_Engine.send_command("set pagination off")
    GDB_Engine.send_command("set non-stop on")
    GDB_Engine.send_command("attach " + str + "&")

def test():
    for x in range(0,3):
        global p
        time.sleep(0.5)
        GDB_Engine.send_command("find 0x00400000,+500,1")

def send_command(str):
    global p
    p.sendline(str)
    p.expect_exact("(gdb) ",async=False)
    print(p.before)

In main I simply call attach() and then test() functions. The result is this:

find 0x00400000,+500,1
&"find 0x00400000,+500,1\n"
~"0x400006\n"
~"0x400014\n"
~"0x40002a\n"
~"0x400061\n"
~"0x400069\n"
~"0x4000a8\n"
~"0x4000b0\n"
~"0x4000d2\n"
~"0x4000da\n"
~"0x4000e8\n"
~"0x4000f2\n"
~"0x40012a\n"
~"0x40019a\n"
~"13 patterns found.\n"
^done


find 0x00400000,+500,1
&"find 0x00400000,+500,1\n"
~"0x400006\n"
~"0x400014\n"
~"0x40002a\n"
~"0x400061\n"
~"0x400069\n"
~"0x4000a8\n"
~"0x4000b0\n"
~"0x4000d2\n"
~"0x4000da\n"
~"0x4000e8\n"
~"0x4000f2\n"
~"0x40012a\n"
~"0x40019a\n"
~"13 patterns found.\n"
^done

find 0x00400000,+500,1
&"find 0x00400000,+500,1\n"
~"0x400006\n"
~"0x400014\n"
~"0x40002a\n"
~"0x400061\n"
~"0x400069\n"
~"0x4000a8\n"
~"0x4000b0\n"
~"0x4000d2\n"
~"0x4000da\n"
~"0x4000e8\n"
~"0x4000f2\n"
~"0x40012a\n"
~"0x40019a\n"
~"13 patterns found.\n"
^done

Works as expected, but if I pass the argument async as True in the function send_command(), the result output becomes something like this:

=thread-group-added,id="i1"
~"GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1\n"
~"Copyright (C) 2014 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.  Type \"show copying\"\nand \"show warranty\" for details.\n"
~"This GDB was configured as \"x86_64-linux-gnu\".\nType \"show configuration\" for configuration details."
~"\nFor bug reporting instructions, please see:\n"
~"<http://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n<http://www.gnu.org/software/gdb/documentation/>.\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
=cmd-param-changed,param="disassembly-flavor",value="intel"

=thread-group-added,id="i1"
~"GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1\n"
~"Copyright (C) 2014 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.  Type \"show copying\"\nand \"show warranty\" for details.\n"
~"This GDB was configured as \"x86_64-linux-gnu\".\nType \"show configuration\" for configuration details."
~"\nFor bug reporting instructions, please see:\n"
~"<http://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n<http://www.gnu.org/software/gdb/documentation/>.\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
=cmd-param-changed,param="disassembly-flavor",value="intel"

=thread-group-added,id="i1"
~"GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1\n"
~"Copyright (C) 2014 Free Software Foundation, Inc.\n"
~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.  Type \"show copying\"\nand \"show warranty\" for details.\n"
~"This GDB was configured as \"x86_64-linux-gnu\".\nType \"show configuration\" for configuration details."
~"\nFor bug reporting instructions, please see:\n"
~"<http://www.gnu.org/software/gdb/bugs/>.\n"
~"Find the GDB manual and other documentation resources online at:\n<http://www.gnu.org/software/gdb/documentation/>.\n"
~"For help, type \"help\".\n"
~"Type \"apropos word\" to search for commands related to \"word\".\n"
=cmd-param-changed,param="disassembly-flavor",value="intel"

It only prints the result of the first command executed(which is p=pexpect.spawnu('sudo gdb --interpreter=mi'). Why does this happen? What's the proper usage of async property?


Solution

  • From pexpect docs:

    On Python 3.4, or Python 3.3 with asyncio installed, passing async=True will make this return an asyncio coroutine, which you can yield from to get the same result that this method would normally give directly. So, inside a coroutine, you can replace this code:

    index = p.expect(patterns)
    

    With this non-blocking form:

    index = yield from p.expect(patterns, async=True)
    

    See coroutines.