From 8b6ee9aeb3f0508ed2a41381cde13bdb8707b7be Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 23 Mar 2015 15:29:31 +0000 Subject: checkpatch: complain about ffs(3) calls The ffs(3) family of functions is not portable. MinGW doesn't always provide the function. Use ctz32() or ctz64() instead. Signed-off-by: Stefan Hajnoczi Message-id: 1427124571-28598-10-git-send-email-stefanha@redhat.com Signed-off-by: Kevin Wolf --- scripts/checkpatch.pl | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 5df61f9aa9..7f0aae977d 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2911,6 +2911,17 @@ sub process { if ($rawline =~ /\b(?:Qemu|QEmu)\b/) { WARN("use QEMU instead of Qemu or QEmu\n" . $herecurr); } + +# check for non-portable ffs() calls that have portable alternatives in QEMU + if ($line =~ /\bffs\(/) { + ERROR("use ctz32() instead of ffs()\n" . $herecurr); + } + if ($line =~ /\bffsl\(/) { + ERROR("use ctz32() or ctz64() instead of ffsl()\n" . $herecurr); + } + if ($line =~ /\bffsll\(/) { + ERROR("use ctz64() instead of ffsll()\n" . $herecurr); + } } # If we have no input at all, then there is nothing to report on -- cgit v1.2.3-55-g7522 From 9eddd6a4b3b187ba50038800b6e4aeda4973b365 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 26 Mar 2015 22:42:34 +0000 Subject: scripts: add 'qemu coroutine' command to qemu-gdb.py The 'qemu coroutine ' GDB command prints the backtrace for a CoroutineUContext. This is useful for peeking inside yielded coroutines that are waiting for file descriptor events, timers, etc. For example: $ gdb tests/test-coroutine (gdb) b test_yield (gdb) r (gdb) b qemu_coroutine_enter (gdb) c (gdb) c Continuing. Breakpoint 2, qemu_coroutine_enter (co=0x555555c66520, opaque=0x0) at qemu-coroutine.c:103 103 { (gdb) source scripts/qemu-gdb.py (gdb) qemu coroutine 0x555555c66520 #0 0x000055555557a740 in qemu_coroutine_switch (from_=, to_=0x7ffff7f90a70, action=COROUTINE_YIELD) at coroutine-ucontext.c:177 #1 0x0000555555566af9 in yield_5_times (opaque=0x7fffffffdbb7) at tests/test-coroutine.c:107 #2 0x000055555557a7aa in coroutine_trampoline (i0=, i1=) at coroutine-ucontext.c:80 #3 0x00007ffff08de000 in __start_context () at /lib64/libc.so.6 Signed-off-by: Stefan Hajnoczi Message-id: 1427409754-8556-1-git-send-email-stefanha@redhat.com Signed-off-by: Kevin Wolf --- scripts/qemu-gdb.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'scripts') diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py index 8a0f30534f..6c7f4fbe53 100644 --- a/scripts/qemu-gdb.py +++ b/scripts/qemu-gdb.py @@ -22,12 +22,86 @@ def isnull(ptr): def int128(p): return long(p['lo']) + (long(p['hi']) << 64) +def get_fs_base(): + '''Fetch %fs base value using arch_prctl(ARCH_GET_FS)''' + # %rsp - 120 is scratch space according to the SystemV ABI + old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') + gdb.execute('call arch_prctl(0x1003, $rsp - 120)', False, True) + fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') + gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) + return fs_base + +def get_glibc_pointer_guard(): + '''Fetch glibc pointer guard value''' + fs_base = get_fs_base() + return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base) + +def glibc_ptr_demangle(val, pointer_guard): + '''Undo effect of glibc's PTR_MANGLE()''' + return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard)) + +def bt_jmpbuf(jmpbuf): + '''Backtrace a jmpbuf''' + JB_RBX = 0 + JB_RBP = 1 + JB_R12 = 2 + JB_R13 = 3 + JB_R14 = 4 + JB_R15 = 5 + JB_RSP = 6 + JB_PC = 7 + + old_rbx = gdb.parse_and_eval('(uint64_t)$rbx') + old_rbp = gdb.parse_and_eval('(uint64_t)$rbp') + old_rsp = gdb.parse_and_eval('(uint64_t)$rsp') + old_r12 = gdb.parse_and_eval('(uint64_t)$r12') + old_r13 = gdb.parse_and_eval('(uint64_t)$r13') + old_r14 = gdb.parse_and_eval('(uint64_t)$r14') + old_r15 = gdb.parse_and_eval('(uint64_t)$r15') + old_rip = gdb.parse_and_eval('(uint64_t)$rip') + + pointer_guard = get_glibc_pointer_guard() + gdb.execute('set $rbx = %s' % jmpbuf[JB_RBX]) + gdb.execute('set $rbp = %s' % glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard)) + gdb.execute('set $rsp = %s' % glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard)) + gdb.execute('set $r12 = %s' % jmpbuf[JB_R12]) + gdb.execute('set $r13 = %s' % jmpbuf[JB_R13]) + gdb.execute('set $r14 = %s' % jmpbuf[JB_R14]) + gdb.execute('set $r15 = %s' % jmpbuf[JB_R15]) + gdb.execute('set $rip = %s' % glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard)) + + gdb.execute('bt') + + gdb.execute('set $rbx = %s' % old_rbx) + gdb.execute('set $rbp = %s' % old_rbp) + gdb.execute('set $rsp = %s' % old_rsp) + gdb.execute('set $r12 = %s' % old_r12) + gdb.execute('set $r13 = %s' % old_r13) + gdb.execute('set $r14 = %s' % old_r14) + gdb.execute('set $r15 = %s' % old_r15) + gdb.execute('set $rip = %s' % old_rip) + class QemuCommand(gdb.Command): '''Prefix for QEMU debug support commands''' def __init__(self): gdb.Command.__init__(self, 'qemu', gdb.COMMAND_DATA, gdb.COMPLETE_NONE, True) +class CoroutineCommand(gdb.Command): + '''Display coroutine backtrace''' + def __init__(self): + gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + argv = gdb.string_to_argv(arg) + if len(argv) != 1: + gdb.write('usage: qemu coroutine \n') + return + + coroutine_pointer = gdb.parse_and_eval(argv[0]).cast(gdb.lookup_type('CoroutineUContext').pointer()) + bt_jmpbuf(coroutine_pointer['env']['__jmpbuf']) + class MtreeCommand(gdb.Command): '''Display the memory tree hierarchy''' def __init__(self): @@ -86,4 +160,5 @@ class MtreeCommand(gdb.Command): subregion = subregion['subregions_link']['tqe_next'] QemuCommand() +CoroutineCommand() MtreeCommand() -- cgit v1.2.3-55-g7522 From 7898f74e78a5900fc079868e255b65d807fa8a8f Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 17 Apr 2015 19:50:06 -0400 Subject: iotests: add QMP event waiting queue A filter is added to allow callers to request very specific events to be pulled from the event queue, while leaving undesired events still in the stream. This allows us to poll for completion data for multiple asynchronous events in any arbitrary order. A new timeout context is added to the qmp pull_event method's wait parameter to allow tests to fail if they do not complete within some expected period of time. Also fixed is a bug in qmp.pull_event where we try to retrieve an event from an empty list if we attempt to retrieve an event with wait=False but no events have occurred. Signed-off-by: John Snow Reviewed-by: Max Reitz Reviewed-by: Stefan Hajnoczi Message-id: 1429314609-29776-19-git-send-email-jsnow@redhat.com Signed-off-by: Stefan Hajnoczi Signed-off-by: Kevin Wolf --- scripts/qmp/qmp.py | 95 +++++++++++++++++++++++++++++-------------- tests/qemu-iotests/iotests.py | 38 +++++++++++++++++ 2 files changed, 103 insertions(+), 30 deletions(-) (limited to 'scripts') diff --git a/scripts/qmp/qmp.py b/scripts/qmp/qmp.py index 20b6ec795e..1d38e3e9e7 100644 --- a/scripts/qmp/qmp.py +++ b/scripts/qmp/qmp.py @@ -21,6 +21,9 @@ class QMPConnectError(QMPError): class QMPCapabilitiesError(QMPError): pass +class QMPTimeoutError(QMPError): + pass + class QEMUMonitorProtocol: def __init__(self, address, server=False): """ @@ -72,6 +75,44 @@ class QEMUMonitorProtocol: error = socket.error + def __get_events(self, wait=False): + """ + Check for new events in the stream and cache them in __events. + + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be retrieved + or if some other error occurred. + """ + + # Check for new events regardless and pull them into the cache: + self.__sock.setblocking(0) + try: + self.__json_read() + except socket.error, err: + if err[0] == errno.EAGAIN: + # No data available + pass + self.__sock.setblocking(1) + + # Wait for new events, if needed. + # if wait is 0.0, this means "no wait" and is also implicitly false. + if not self.__events and wait: + if isinstance(wait, float): + self.__sock.settimeout(wait) + try: + ret = self.__json_read(only_event=True) + except socket.timeout: + raise QMPTimeoutError("Timeout waiting for event") + except: + raise QMPConnectError("Error while reading from socket") + if ret is None: + raise QMPConnectError("Error while reading from socket") + self.__sock.settimeout(None) + def connect(self, negotiate=True): """ Connect to the QMP Monitor and perform capabilities negotiation. @@ -140,43 +181,37 @@ class QEMUMonitorProtocol: """ Get and delete the first available QMP event. - @param wait: block until an event is available (bool) + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be retrieved + or if some other error occurred. + + @return The first available QMP event, or None. """ - self.__sock.setblocking(0) - try: - self.__json_read() - except socket.error, err: - if err[0] == errno.EAGAIN: - # No data available - pass - self.__sock.setblocking(1) - if not self.__events and wait: - self.__json_read(only_event=True) - event = self.__events[0] - del self.__events[0] - return event + self.__get_events(wait) + + if self.__events: + return self.__events.pop(0) + return None def get_events(self, wait=False): """ Get a list of available QMP events. - @param wait: block until an event is available (bool) - """ - self.__sock.setblocking(0) - try: - self.__json_read() - except socket.error, err: - if err[0] == errno.EAGAIN: - # No data available - pass - self.__sock.setblocking(1) - if not self.__events and wait: - ret = self.__json_read(only_event=True) - if ret == None: - # We are in blocking mode, if don't get anything, something - # went wrong - raise QMPConnectError("Error while reading from socket") + @param wait (bool): block until an event is available. + @param wait (float): If wait is a float, treat it as a timeout value. + @raise QMPTimeoutError: If a timeout float is provided and the timeout + period elapses. + @raise QMPConnectError: If wait is True but no events could be retrieved + or if some other error occurred. + + @return The list of available QMP events. + """ + self.__get_events(wait) return self.__events def clear_events(self): diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 14028540b3..e93e62387b 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -78,6 +78,23 @@ def create_image(name, size): i = i + 512 file.close() +# Test if 'match' is a recursive subset of 'event' +def event_match(event, match=None): + if match is None: + return True + + for key in match: + if key in event: + if isinstance(event[key], dict): + if not event_match(event[key], match[key]): + return False + elif event[key] != match[key]: + return False + else: + return False + + return True + class VM(object): '''A QEMU VM''' @@ -92,6 +109,7 @@ class VM(object): '-machine', 'accel=qtest', '-display', 'none', '-vga', 'none'] self._num_drives = 0 + self._events = [] # This can be used to add an unused monitor instance. def add_monitor_telnet(self, ip, port): @@ -202,14 +220,34 @@ class VM(object): def get_qmp_event(self, wait=False): '''Poll for one queued QMP events and return it''' + if len(self._events) > 0: + return self._events.pop(0) return self._qmp.pull_event(wait=wait) def get_qmp_events(self, wait=False): '''Poll for queued QMP events and return a list of dicts''' events = self._qmp.get_events(wait=wait) + events.extend(self._events) + del self._events[:] self._qmp.clear_events() return events + def event_wait(self, name='BLOCK_JOB_COMPLETED', timeout=60.0, match=None): + # Search cached events + for event in self._events: + if (event['event'] == name) and event_match(event, match): + self._events.remove(event) + return event + + # Poll for new events + while True: + event = self._qmp.pull_event(wait=timeout) + if (event['event'] == name) and event_match(event, match): + return event + self._events.append(event) + + return None + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') class QMPTestCase(unittest.TestCase): -- cgit v1.2.3-55-g7522