All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands
@ 2026-08-04 15:17 Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 1/9] scripts/qemugdb: coroutine: Fix selected frame leak on error path Andrey Drobyshev
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

A bunch of fixes for functional bugs, cosmetic improvements and fixes
for annoying failures and corner cases which I encounter when using this
script in gdb sessions.  Most of them are basically fixups for my previous
commit 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining
detailed trace in coredump").  Still I did split them up cause it'd be a
mess putting them all together.

Andrey Drobyshev (9):
  scripts/qemugdb: coroutine: Fix selected frame leak on error path
  scripts/qemugdb: coroutine: Get rid of fallback pattern when dumping
    backtrace
  scripts/qemugdb: coroutine: Only attempt to restore regs in coredump
  scripts/qemugdb: coroutine: Don't unwind past the outermost frame
  scripts/qemugdb: coroutine: Fix patching pt_regs in the coredump
  scripts/qemugdb: coroutine: Fix '--detailed' description
  scripts/qemugdb: coroutine: Handle target absence gracefully
  scripts/qemugdb: coroutine: Speed up coroutine lookup in a coredump
  scripts/qemugdb: coroutine: Don't touch the coredump for a plain
    backtrace

 scripts/qemugdb/coroutine.py | 135 ++++++++++++++++++++++-------------
 1 file changed, 86 insertions(+), 49 deletions(-)

-- 
2.47.1



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/9] scripts/qemugdb: coroutine: Fix selected frame leak on error path
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 2/9] scripts/qemugdb: coroutine: Get rid of fallback pattern when dumping backtrace Andrey Drobyshev
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

Currently, whenever gdb.execute() fails and throws an exception,
previously selected frame gets leaked.  I.e.:

  (gdb) frame N
  (gdb) qemu bt # throws exception
  (gdb) frame   # shows 0, not N

As a fix, wrap gdb.execute() commands in try/finally, so that selected
frame gets restored no matter what.

Fixes: 4cbf8efc5b89 ("scripts/gdb: fix 'qemu coroutine' when users selects a non topmost stack frame")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index 29f57ae84ed..d1e6722d14d 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -265,18 +265,21 @@ def dump_backtrace_live(regs):
     selected_frame = gdb.selected_frame()
     gdb.newest_frame().select()
 
-    for i in regs:
-        old[i] = gdb.parse_and_eval('(uint64_t)$%s' % i)
-
-    for i in regs:
-        gdb.execute('set $%s = %s' % (i, regs[i]))
-
-    gdb.execute('bt')
+    try:
+        for i in regs:
+            old[i] = gdb.parse_and_eval('(uint64_t)$%s' % i)
 
-    for i in regs:
-        gdb.execute('set $%s = %s' % (i, old[i]))
+        for i in regs:
+            gdb.execute('set $%s = %s' % (i, regs[i]))
 
-    selected_frame.select()
+        gdb.execute('bt')
+    finally:
+        try:
+            for i in old:
+                gdb.execute('set $%s = %s' % (i, old[i]))
+        finally:
+            # restore previously selected frame in any case
+            selected_frame.select()
 
 def bt_jmpbuf(jmpbuf, detailed=False):
     '''Backtrace a jmpbuf'''
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/9] scripts/qemugdb: coroutine: Get rid of fallback pattern when dumping backtrace
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 1/9] scripts/qemugdb: coroutine: Fix selected frame leak on error path Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 3/9] scripts/qemugdb: coroutine: Only attempt to restore regs in coredump Andrey Drobyshev
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

Currently upon invocation of 'qemu bt' / 'qemu coroutine' we try live
process backtrace dump first, and if it fails - fallback to coredump.
That causes us needless work and catching an exception.  In my previous
commit 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining
detailed trace in coredump") class Coredump was introduced, and each
invocation already calls init_coredump().  Let's just pass the result of
this call further as a bool param and make a decision based upon it.
Apply the same pattern to obtaining coroutine pointer.

Note that we deliberately lose a safety net here: the bare except used to
swallow any failure of the live dump, not just the absence of a live
process, and silently fall back to the raw unwind.  Such a failure is now
reported to the user instead.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index d1e6722d14d..d3f31bab9fe 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -281,20 +281,19 @@ def dump_backtrace_live(regs):
             # restore previously selected frame in any case
             selected_frame.select()
 
-def bt_jmpbuf(jmpbuf, detailed=False):
+def bt_jmpbuf(jmpbuf, is_coredump, detailed=False):
     '''Backtrace a jmpbuf'''
     regs = get_jmpbuf_regs(jmpbuf)
-    try:
+    if not is_coredump:
         # This reuses gdb's "bt" command, which can be slightly prettier
         # but only works with live sessions.
         dump_backtrace_live(regs)
-    except:
-        if detailed:
-            # Obtain detailed trace by patching regs in copied coredump
-            dump_backtrace_patched(regs)
-        else:
-            # If above doesn't work, fallback to poor man's unwind
-            dump_backtrace(regs)
+    elif detailed:
+        # Obtain detailed trace by patching regs in copied coredump
+        dump_backtrace_patched(regs)
+    else:
+        # Obtain a non-detailed trace by poor man's unwind
+        dump_backtrace(regs)
 
 def co_cast(co):
     return co.cast(gdb.lookup_type('CoroutineUContext').pointer())
@@ -353,7 +352,7 @@ def invoke(self, arg, from_tty):
 
         try:
             bt_jmpbuf(coroutine_to_jmpbuf(gdb.parse_and_eval(argv[0])),
-                      detailed=detailed)
+                      is_coredump, detailed=detailed)
         finally:
             coredump.restore_regs()
 
@@ -390,10 +389,10 @@ def invoke(self, arg, from_tty):
 
         gdb.execute("bt")
 
-        try:
+        if not is_coredump:
             # This only works with a live session
             co_ptr = gdb.parse_and_eval("qemu_coroutine_self()")
-        except:
+        else:
             # Fallback to use hard-coded ucontext vars if it's coredump
             co_ptr = gdb.parse_and_eval("co_tls_current")
 
@@ -407,7 +406,8 @@ def invoke(self, arg, from_tty):
                 if co_ptr == 0:
                     break
                 gdb.write("\nCoroutine at " + str(co_ptr) + ":\n")
-                bt_jmpbuf(coroutine_to_jmpbuf(co_ptr), detailed=detailed)
+                bt_jmpbuf(coroutine_to_jmpbuf(co_ptr), is_coredump,
+                          detailed=detailed)
         finally:
             coredump.restore_regs()
 
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/9] scripts/qemugdb: coroutine: Only attempt to restore regs in coredump
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 1/9] scripts/qemugdb: coroutine: Fix selected frame leak on error path Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 2/9] scripts/qemugdb: coroutine: Get rid of fallback pattern when dumping backtrace Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 4/9] scripts/qemugdb: coroutine: Don't unwind past the outermost frame Andrey Drobyshev
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

When calling 'qemu bt' / 'qemu coroutine' in a live process, we get:

    AttributeError: 'NoneType' object has no attribute 'restore_regs'

That's because for both commands finally: blocks attempt to restore
patched regs unconditionally.  Fix this by guarding regs restoration by
'if is_coredump'.

Fixes: 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining detailed trace in coredump")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index d3f31bab9fe..649579378db 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -354,7 +354,8 @@ def invoke(self, arg, from_tty):
             bt_jmpbuf(coroutine_to_jmpbuf(gdb.parse_and_eval(argv[0])),
                       is_coredump, detailed=detailed)
         finally:
-            coredump.restore_regs()
+            if is_coredump:
+                coredump.restore_regs()
 
 class CoroutineBt(gdb.Command):
     __doc__ = textwrap.dedent("""\
@@ -409,7 +410,8 @@ def invoke(self, arg, from_tty):
                 bt_jmpbuf(coroutine_to_jmpbuf(co_ptr), is_coredump,
                           detailed=detailed)
         finally:
-            coredump.restore_regs()
+            if is_coredump:
+                coredump.restore_regs()
 
 class CoroutineSPFunction(gdb.Function):
     def __init__(self):
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/9] scripts/qemugdb: coroutine: Don't unwind past the outermost frame
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (2 preceding siblings ...)
  2026-08-04 15:17 ` [PATCH 3/9] scripts/qemugdb: coroutine: Only attempt to restore regs in coredump Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 5/9] scripts/qemugdb: coroutine: Fix patching pt_regs in the coredump Andrey Drobyshev
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

dump_backtrace() walks the frame pointer chain until rbp is NULL.
However, when glibc is built with no frame pointers, rbp is never NULL
during unwind.  Instead, we see garbage in the outermost frame.  This
results into:

  #17  0x5610b6e9f2d9 in main<+1720> () at ../qemu-io.c:674
  #18  0x7f26cc10230e in __libc_start_call_main<+125> () at
      ../sysdeps/nptl/libc_start_call_main.h:58
  Traceback (most recent call last):
    File "/.../scripts/qemugdb/coroutine.py", line 409, in invoke
      bt_jmpbuf(coroutine_to_jmpbuf(co_ptr), is_coredump,
    File "/.../scripts/qemugdb/coroutine.py", line 296, in bt_jmpbuf
      dump_backtrace(regs)
    File "/.../scripts/qemugdb/coroutine.py", line 247, in dump_backtrace
      while rbp:
            ^^^
  gdb.MemoryError: Cannot access memory at address 0x4
  Error occurred in Python: Cannot access memory at address 0x4

As a fix, stop on the first link that can't be read.  Since
gdb.parse_and_eval() returns a lazy value, force the actual memory read
by wrapping it in int() and immediately catching potential gdb.MemoryError.

Fixes: 772f86839f77 ("scripts/qemu-gdb: Support coroutine dumps in coredumps")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index 649579378db..da395a1a13e 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -235,13 +235,24 @@ def dump_backtrace_patched(regs):
     out = run_with_pty(cmd).split('----split----')[1]
     gdb.write(out)
 
+def read_word(addr):
+    '''
+    Read a 64-bit word, None if that memory isn't accessible.
+    '''
+    try:
+        # gdb.parse_and_eval() returns lazy values.  Force memory access
+        # by wrapping in int()
+        return int(gdb.parse_and_eval(f"*(uint64_t *){hex(addr)}"))
+    except gdb.MemoryError:
+        return None
+
 def dump_backtrace(regs):
     '''
     Backtrace dump with raw registers, mimic GDB command 'bt'.
     '''
     # Here only rbp and rip that matter..
-    rbp = regs['rbp']
-    rip = regs['rip']
+    rbp = int(regs['rbp'])
+    rip = int(regs['rip'])
     i = 0
 
     while rbp:
@@ -250,8 +261,14 @@ def dump_backtrace(regs):
         # instruction instead of the CALL.  Here -1 would work for any
         # sized CALL instruction.
         print(f"#{i}  {hex(rip)} in {symbol_lookup(rip if i == 0 else rip-1)}")
-        rip = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)} + 8)")
-        rbp = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)})")
+
+        # The 'rbp != NULL' condition is insufficient: the outermost glibc
+        # frames might leave garbage in rbp if built without frame pointers.
+        # Break the loop on the frame that leads nowhere.
+        rip, rbp = read_word(rbp + 8), read_word(rbp)
+        if rip is None or rbp is None:
+            break
+
         i += 1
 
 def dump_backtrace_live(regs):
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/9] scripts/qemugdb: coroutine: Fix patching pt_regs in the coredump
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (3 preceding siblings ...)
  2026-08-04 15:17 ` [PATCH 4/9] scripts/qemugdb: coroutine: Don't unwind past the outermost frame Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 6/9] scripts/qemugdb: coroutine: Fix '--detailed' description Andrey Drobyshev
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

patch_regs() and restore_regs() open the coredump with 'ab', i.e. with
O_APPEND.  This repositions the file offset to the end of the file before
every write.  The seek() to pt_regs therefore has no effect: instead of
patching the saved registers, we just append the binary blob with patched
regs at the end of the file.

Nothing fails, and the gdb subprocess still prints a plausible backtrace.
However it's a backtrace of the crashed thread rather than of the
requested coroutine.  The .ptregs blob kept for recovery is dead for the
same reason as there's never anything to restore.

Open the file with 'r+b' so that the writes actually land where seek()
points.

Fixes: 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining detailed trace in coredump")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index da395a1a13e..4803914dde0 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -90,7 +90,7 @@ def patch_regs(self, regs):
         int_regs = {k: int(v) for k, v in regs.items()}
         patched_ptregs.update(int_regs)
 
-        with open(self.coredump, 'ab') as f:
+        with open(self.coredump, 'r+b') as f:
             gdb.write(f'assume pt_regs at 0x{self._ptregs_offset:x}\n')
             f.seek(self._ptregs_offset, 0)
             gdb.write('writing regs:\n')
@@ -106,7 +106,7 @@ def restore_regs(self):
             return
 
         gdb.write(f'\nrestoring original regs in core file {self.coredump}\n')
-        with open(self.coredump, 'ab') as f:
+        with open(self.coredump, 'r+b') as f:
             gdb.write(f'assume pt_regs at 0x{self._ptregs_offset:x}\n')
             f.seek(self._ptregs_offset, 0)
             f.write(struct.pack(f"={len(PT_REGS)}q",
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/9] scripts/qemugdb: coroutine: Fix '--detailed' description
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (4 preceding siblings ...)
  2026-08-04 15:17 ` [PATCH 5/9] scripts/qemugdb: coroutine: Fix patching pt_regs in the coredump Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 7/9] scripts/qemugdb: coroutine: Handle target absence gracefully Andrey Drobyshev
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

Both commands claim the coredump is copied before its registers get
patched, but patch_regs() writes to the coredump itself.  Describe what
actually happens, so that nobody expects their coredump to be left alone.
Fix a typo in "runing" while at it.

Fixes: 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining detailed trace in coredump")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index 4803914dde0..9148264de3c 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -306,7 +306,7 @@ def bt_jmpbuf(jmpbuf, is_coredump, detailed=False):
         # but only works with live sessions.
         dump_backtrace_live(regs)
     elif detailed:
-        # Obtain detailed trace by patching regs in copied coredump
+        # Obtain detailed trace by patching regs in the coredump in place
         dump_backtrace_patched(regs)
     else:
         # Obtain a non-detailed trace by poor man's unwind
@@ -342,9 +342,9 @@ class CoroutineCommand(gdb.Command):
         Usage: qemu coroutine COROPTR [--detailed]
         Show backtrace for a coroutine specified by COROPTR
 
-          --detailed       obtain detailed trace by copying coredump, patching
-                           regs in it, and runing gdb subprocess to get
-                           backtrace from the patched coredump
+          --detailed       obtain detailed trace by patching regs in the
+                           coredump in place, and running gdb subprocess to
+                           get backtrace from the patched coredump
         """)
 
     def __init__(self):
@@ -380,9 +380,9 @@ class CoroutineBt(gdb.Command):
 
         Usage: qemu bt [--detailed]
 
-          --detailed       obtain detailed trace by copying coredump, patching
-                           regs in it, and runing gdb subprocess to get
-                           backtrace from the patched coredump
+          --detailed       obtain detailed trace by patching regs in the
+                           coredump in place, and running gdb subprocess to
+                           get backtrace from the patched coredump
         """)
 
     def __init__(self):
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 7/9] scripts/qemugdb: coroutine: Handle target absence gracefully
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (5 preceding siblings ...)
  2026-08-04 15:17 ` [PATCH 6/9] scripts/qemugdb: coroutine: Fix '--detailed' description Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 8/9] scripts/qemugdb: coroutine: Speed up coroutine lookup in a coredump Andrey Drobyshev
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

init_coredump() picks the core dump and the executable out of fixed lines
of 'info files' output.  With no target loaded that output is empty, and
'qemu bt' dies with:

    IndexError: list index out of range

Search the whole output for the core dump file instead, and take the
executable from gdb.current_progspace(), which doesn't need parsing at
all.  Neither can raise if the pattern isn't there, so an unexpected
output format now just means "not a coredump" rather than a traceback.

That alone only moves the problem to gdb.execute("bt"), whose gdb.error
escapes invoke() and still gets reported as a python traceback.  At the
same time plain 'bt' just prints 'No stack.'.  Let's mimick this
behaviour and simply report an error if there's no selected thread.

Fixes: 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining detailed trace in coredump")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index 9148264de3c..3e0b37b73ea 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -322,16 +322,14 @@ def coroutine_to_jmpbuf(co):
 def init_coredump():
     global coredump
 
-    files = gdb.execute('info files', False, True).split('\n')
+    files = gdb.execute('info files', False, True)
 
-    if not 'core dump' in files[1]:
+    match = re.search(r"core dump file:\s*`([^']+)'", files)
+    if match is None:
         return False
 
-    core_path = re.search("`(.*)'", files[2]).group(1)
-    exec_path = re.match('^Symbols from "(.*)".$', files[0]).group(1)
-
     if coredump is None:
-        coredump = Coredump(core_path, exec_path)
+        coredump = Coredump(match.group(1), gdb.current_progspace().filename)
 
     return True
 
@@ -362,6 +360,9 @@ def invoke(self, arg, from_tty):
             return self._usage()
         detailed = True if argc == 2 else False
 
+        if gdb.selected_thread() is None:
+            raise gdb.GdbError('No stack.')
+
         is_coredump = init_coredump()
         if detailed and not is_coredump:
             gdb.write('--detailed is only valid when debugging core dumps\n')
@@ -400,6 +401,9 @@ def invoke(self, arg, from_tty):
             return self._usage()
         detailed = True if argc == 1 else False
 
+        if gdb.selected_thread() is None:
+            raise gdb.GdbError('No stack.')
+
         is_coredump = init_coredump()
         if detailed and not is_coredump:
             gdb.write('--detailed is only valid when debugging core dumps\n')
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 8/9] scripts/qemugdb: coroutine: Speed up coroutine lookup in a coredump
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (6 preceding siblings ...)
  2026-08-04 15:17 ` [PATCH 7/9] scripts/qemugdb: coroutine: Handle target absence gracefully Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-04 15:17 ` [PATCH 9/9] scripts/qemugdb: coroutine: Don't touch the coredump for a plain backtrace Andrey Drobyshev
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

'qemu bt' on a coredump takes significant time before the first
coroutine frame is even printed.  The bulk of the delay is resolving a
name.

co_tls_current is a file static variable, and CoroutineUContext is defined
in the same compilation unit, so resolving either of them by name makes gdb
expand every symtab in the binary.

Look the static up with gdb.lookup_static_symbol(), which is served from
the gdb's in-memory index, and scope the type lookup to the symtab that
comes with it.  This significantly speeds up 'qemu bt' processing.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index 3e0b37b73ea..21ed7cbaab8 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -313,7 +313,16 @@ def bt_jmpbuf(jmpbuf, is_coredump, detailed=False):
         dump_backtrace(regs)
 
 def co_cast(co):
-    return co.cast(gdb.lookup_type('CoroutineUContext').pointer())
+    # Unscoped type lookup expands every symtab in the binary.
+    # Better scope it to the symtab of the ucontext backend
+    sym = gdb.lookup_static_symbol('co_tls_current')
+    if sym is not None:
+        co_type = gdb.lookup_type('CoroutineUContext',
+                                  sym.symtab.static_block())
+    else:
+        co_type = gdb.lookup_type('CoroutineUContext')
+
+    return co.cast(co_type.pointer())
 
 def coroutine_to_jmpbuf(co):
     coroutine_pointer = co_cast(co)
@@ -416,7 +425,8 @@ def invoke(self, arg, from_tty):
             co_ptr = gdb.parse_and_eval("qemu_coroutine_self()")
         else:
             # Fallback to use hard-coded ucontext vars if it's coredump
-            co_ptr = gdb.parse_and_eval("co_tls_current")
+            sym = gdb.lookup_static_symbol("co_tls_current")
+            co_ptr = sym.value() if sym else gdb.parse_and_eval("co_tls_current")
 
         if co_ptr == False:
             return
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 9/9] scripts/qemugdb: coroutine: Don't touch the coredump for a plain backtrace
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (7 preceding siblings ...)
  2026-08-04 15:17 ` [PATCH 8/9] scripts/qemugdb: coroutine: Speed up coroutine lookup in a coredump Andrey Drobyshev
@ 2026-08-04 15:17 ` Andrey Drobyshev
  2026-08-26 15:02 ` [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Fabiano Rosas
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-08-04 15:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

init_coredump() creates a Coredump object for every coredump session.  That
implies opening and scanning the actual coredump file.   For non-detailed
'qemu bt' invocation we don't really need it.

Les't only create the object when the core is about to be patched, i.e.
for '--detailed'.

Fixes: 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining detailed trace in coredump")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 scripts/qemugdb/coroutine.py | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index 21ed7cbaab8..edc1f3cc69f 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -328,7 +328,7 @@ def coroutine_to_jmpbuf(co):
     coroutine_pointer = co_cast(co)
     return coroutine_pointer['env']['__jmpbuf']
 
-def init_coredump():
+def init_coredump(detailed=False):
     global coredump
 
     files = gdb.execute('info files', False, True)
@@ -337,7 +337,8 @@ def init_coredump():
     if match is None:
         return False
 
-    if coredump is None:
+    # The object is only needed to patch the coredump
+    if detailed and coredump is None:
         coredump = Coredump(match.group(1), gdb.current_progspace().filename)
 
     return True
@@ -372,7 +373,7 @@ def invoke(self, arg, from_tty):
         if gdb.selected_thread() is None:
             raise gdb.GdbError('No stack.')
 
-        is_coredump = init_coredump()
+        is_coredump = init_coredump(detailed)
         if detailed and not is_coredump:
             gdb.write('--detailed is only valid when debugging core dumps\n')
             return
@@ -381,7 +382,7 @@ def invoke(self, arg, from_tty):
             bt_jmpbuf(coroutine_to_jmpbuf(gdb.parse_and_eval(argv[0])),
                       is_coredump, detailed=detailed)
         finally:
-            if is_coredump:
+            if coredump is not None:
                 coredump.restore_regs()
 
 class CoroutineBt(gdb.Command):
@@ -413,7 +414,7 @@ def invoke(self, arg, from_tty):
         if gdb.selected_thread() is None:
             raise gdb.GdbError('No stack.')
 
-        is_coredump = init_coredump()
+        is_coredump = init_coredump(detailed)
         if detailed and not is_coredump:
             gdb.write('--detailed is only valid when debugging core dumps\n')
             return
@@ -441,7 +442,7 @@ def invoke(self, arg, from_tty):
                 bt_jmpbuf(coroutine_to_jmpbuf(co_ptr), is_coredump,
                           detailed=detailed)
         finally:
-            if is_coredump:
+            if coredump is not None:
                 coredump.restore_regs()
 
 class CoroutineSPFunction(gdb.Function):
-- 
2.47.1



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (8 preceding siblings ...)
  2026-08-04 15:17 ` [PATCH 9/9] scripts/qemugdb: coroutine: Don't touch the coredump for a plain backtrace Andrey Drobyshev
@ 2026-08-26 15:02 ` Fabiano Rosas
  2026-09-23 15:28 ` Andrey Drobyshev
  2026-09-25 14:03 ` Kevin Wolf
  11 siblings, 0 replies; 13+ messages in thread
From: Fabiano Rosas @ 2026-08-26 15:02 UTC (permalink / raw)
  To: Andrey Drobyshev, qemu-devel; +Cc: stefanha, kwolf, peterx, andrey.drobyshev

Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> writes:

> A bunch of fixes for functional bugs, cosmetic improvements and fixes
> for annoying failures and corner cases which I encounter when using this
> script in gdb sessions.  Most of them are basically fixups for my previous
> commit 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining
> detailed trace in coredump").  Still I did split them up cause it'd be a
> mess putting them all together.
>
> Andrey Drobyshev (9):
>   scripts/qemugdb: coroutine: Fix selected frame leak on error path
>   scripts/qemugdb: coroutine: Get rid of fallback pattern when dumping
>     backtrace
>   scripts/qemugdb: coroutine: Only attempt to restore regs in coredump
>   scripts/qemugdb: coroutine: Don't unwind past the outermost frame
>   scripts/qemugdb: coroutine: Fix patching pt_regs in the coredump
>   scripts/qemugdb: coroutine: Fix '--detailed' description
>   scripts/qemugdb: coroutine: Handle target absence gracefully
>   scripts/qemugdb: coroutine: Speed up coroutine lookup in a coredump
>   scripts/qemugdb: coroutine: Don't touch the coredump for a plain
>     backtrace
>
>  scripts/qemugdb/coroutine.py | 135 ++++++++++++++++++++++-------------
>  1 file changed, 86 insertions(+), 49 deletions(-)

I used this series for debugging both a live process and a coredump. It
didn't show any regressions. I can confirm that the exception fixed by
patch 4 no longer happens.

Tested-by: Fabiano Rosas <farosas@suse.de>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (9 preceding siblings ...)
  2026-08-26 15:02 ` [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Fabiano Rosas
@ 2026-09-23 15:28 ` Andrey Drobyshev
  2026-09-25 14:03 ` Kevin Wolf
  11 siblings, 0 replies; 13+ messages in thread
From: Andrey Drobyshev @ 2026-09-23 15:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: stefanha, kwolf, peterx, farosas

On 8/4/26 6:17 PM, Andrey Drobyshev wrote:
> A bunch of fixes for functional bugs, cosmetic improvements and fixes
> for annoying failures and corner cases which I encounter when using this
> script in gdb sessions.  Most of them are basically fixups for my previous
> commit 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining
> detailed trace in coredump").  Still I did split them up cause it'd be a
> mess putting them all together.
> 
> Andrey Drobyshev (9):
>   scripts/qemugdb: coroutine: Fix selected frame leak on error path
>   scripts/qemugdb: coroutine: Get rid of fallback pattern when dumping
>     backtrace
>   scripts/qemugdb: coroutine: Only attempt to restore regs in coredump
>   scripts/qemugdb: coroutine: Don't unwind past the outermost frame
>   scripts/qemugdb: coroutine: Fix patching pt_regs in the coredump
>   scripts/qemugdb: coroutine: Fix '--detailed' description
>   scripts/qemugdb: coroutine: Handle target absence gracefully
>   scripts/qemugdb: coroutine: Speed up coroutine lookup in a coredump
>   scripts/qemugdb: coroutine: Don't touch the coredump for a plain
>     backtrace
> 
>  scripts/qemugdb/coroutine.py | 135 ++++++++++++++++++++++-------------
>  1 file changed, 86 insertions(+), 49 deletions(-)

Friendly ping

If someone else is using this tool for debugging - there're some genuine
bug fixes in this series.  The bugs were also introduced by me, so
that's more of a cleanup :)

Andrey


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands
  2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
                   ` (10 preceding siblings ...)
  2026-09-23 15:28 ` Andrey Drobyshev
@ 2026-09-25 14:03 ` Kevin Wolf
  11 siblings, 0 replies; 13+ messages in thread
From: Kevin Wolf @ 2026-09-25 14:03 UTC (permalink / raw)
  To: Andrey Drobyshev; +Cc: qemu-devel, stefanha, peterx

Am 04.08.2026 um 17:17 hat Andrey Drobyshev geschrieben:
> A bunch of fixes for functional bugs, cosmetic improvements and fixes
> for annoying failures and corner cases which I encounter when using this
> script in gdb sessions.  Most of them are basically fixups for my previous
> commit 42f3c143c3a0 ("scripts/qemugdb: coroutine: Add option for obtaining
> detailed trace in coredump").  Still I did split them up cause it'd be a
> mess putting them all together.
> 
> Andrey Drobyshev (9):
>   scripts/qemugdb: coroutine: Fix selected frame leak on error path
>   scripts/qemugdb: coroutine: Get rid of fallback pattern when dumping
>     backtrace
>   scripts/qemugdb: coroutine: Only attempt to restore regs in coredump
>   scripts/qemugdb: coroutine: Don't unwind past the outermost frame
>   scripts/qemugdb: coroutine: Fix patching pt_regs in the coredump
>   scripts/qemugdb: coroutine: Fix '--detailed' description
>   scripts/qemugdb: coroutine: Handle target absence gracefully
>   scripts/qemugdb: coroutine: Speed up coroutine lookup in a coredump
>   scripts/qemugdb: coroutine: Don't touch the coredump for a plain
>     backtrace
> 
>  scripts/qemugdb/coroutine.py | 135 ++++++++++++++++++++++-------------
>  1 file changed, 86 insertions(+), 49 deletions(-)

Thanks, applied to the block branch.

Kevin



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-09-25 14:04 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04 15:17 [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 1/9] scripts/qemugdb: coroutine: Fix selected frame leak on error path Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 2/9] scripts/qemugdb: coroutine: Get rid of fallback pattern when dumping backtrace Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 3/9] scripts/qemugdb: coroutine: Only attempt to restore regs in coredump Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 4/9] scripts/qemugdb: coroutine: Don't unwind past the outermost frame Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 5/9] scripts/qemugdb: coroutine: Fix patching pt_regs in the coredump Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 6/9] scripts/qemugdb: coroutine: Fix '--detailed' description Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 7/9] scripts/qemugdb: coroutine: Handle target absence gracefully Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 8/9] scripts/qemugdb: coroutine: Speed up coroutine lookup in a coredump Andrey Drobyshev
2026-08-04 15:17 ` [PATCH 9/9] scripts/qemugdb: coroutine: Don't touch the coredump for a plain backtrace Andrey Drobyshev
2026-08-26 15:02 ` [PATCH 0/9] scripts/qemugdb: some fixes for 'qemu bt' / 'qemu coroutine' commands Fabiano Rosas
2026-09-23 15:28 ` Andrey Drobyshev
2026-09-25 14:03 ` Kevin Wolf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.