public inbox for gdb@sourceware.org
 help / color / mirror / Atom feed
From: Stefan Hajnoczi <stefanha@redhat.com>
To: Tom Tromey <tom@tromey.com>
Cc: gdb@sourceware.org, qemu-devel@nongnu.org, pedro@palves.net,
	"Dr. David Alan Gilbert" <dgilbert@redhat.com>
Subject: Re: How to backtrace an separate stack?
Date: Tue, 15 Mar 2022 14:17:20 +0000	[thread overview]
Message-ID: <YjCf8BPW+jITJain@stefanha-x1.localdomain> (raw)
In-Reply-To: <87r174gtuv.fsf@tromey.com>

[-- Attachment #1: Type: text/plain, Size: 6226 bytes --]

On Mon, Mar 14, 2022 at 02:30:16PM -0600, Tom Tromey wrote:
> Tom> IMO this is just a longstanding hole in GDB.  Green threads exist,
> Tom> so it would be good for GDB to have a way to inspect them.
> 
> I took a stab at implementing this recently.  It's still very rough but
> it's good enough to discuss whether it's something I should try to
> polish.
> 
> For testing the proof of concept, I used vireo, a simple user-space
> thread setup based on makecontext.
> 
> https://github.com/geofft/vireo
> 
> I've appended the Python code that teaches gdb how to find vireo
> threads.  It's incomplete, as in, if you re-'run', it will fail.
> 
> Here's what a session looks like:
> 
>     (gdb) cont
>     Continuing.
>     [New Vireo Thread 1]
>     [New Vireo Thread 2]
>     send 0 from 0 to 1
> 
>     Thread 1 "pingpong" hit Breakpoint 2, pingpong () at examples/pingpong.c:27
>     27			int i = vireo_recv(&who);
>     (gdb) info thread
>       Id   Target Id                                    Frame 
>     * 1    Thread 0x7ffff7cb2b80 (LWP 42208) "pingpong" pingpong () at examples/pingpong.c:27
>       2    Vireo Thread 1 "pingpong"                    pingpong () at examples/pingpong.c:27
>       3    Vireo Thread 2 "pingpong"                    pingpong () at examples/pingpong.c:27
>     (gdb) thread 3
>     [Switching to thread 3 (Vireo Thread 2)]
>     #0  pingpong () at examples/pingpong.c:27
>     27			int i = vireo_recv(&who);
>     (gdb) bt
>     #0  pingpong () at examples/pingpong.c:27
>     #1  0x00007ffff7d329c0 in ?? () from /lib64/libc.so.6
>     #2  0x00007ffff7fc20e0 in ?? () from /home/tromey/gdb/vireo/examples/../libvireo.so
>     #3  0x0000000000000000 in ?? ()
> 
> I realize now, writing this, that the approach to underlying threads
> should be improved.  These need to be tracked more actively, so that
> breakpoint stops can report the corresponding green thread.  You can see
> above that this isn't done.  Also I think the "Frame" info is wrong
> right now.
> 
> Anyway, the basic idea is to let Python tell gdb about the existence of
> green threads, and let gdb mostly treat them identically to OS threads.
> Under the hood, things like 'continue' will use the underlying OS
> thread.
> 
> You can play with this if you want.  It's on 'submit/green-threads' on
> my github.  Be warned that I rebase a lot.

This looks cool! Would it be useful to see a port of QEMU's coroutine.py
script to your green threads API?

> Some things to work out:
> 
> - Exactly how should the 'underlying thread' concept work?
>   Hooking into the inferior's scheduler seems slow, and also
>   like it could present a chicken/egg problem.
>   Maybe it needs a "green thread provider" object so that on
>   a stop we can query that to see if the green thread corresponding
>   to an OS thread is already known.

QEMU's coroutines aren't in a scheduler list so there is no way to
enumerate all coroutines. The Python script can register a GDB command
(e.g. "qemu coroutine 0x12345678") that makes GDB aware of the
coroutine.

The only automatic way of cleaning up coroutines that I can think of is
by placing a breakpoint on QEMU's internal coroutine deletion function
from the Python script and then making GDB aware that the coroutine no
longer exists. It looks like this is the approach your vireo script
takes.

> - Do we need a special hook to stop unwinding per-green-thread.
>   You may not want to allow unwinding through the scheduler.

When we've used 'bt' on coroutine stacks in the past, reaching the end
of the stack wasn't a problem for the user. There is no error message
from GDB.

> 
> Tom
> 
> 
> import gdb
> 
> thread_map = {}
> 
> main_thread = None
> 
> # From glibc/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
> x8664_regs = [ 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14',
>                'r15', 'rdi', 'rsi', 'rbp', 'rbx', 'rdx', 'rax',
>                'rcx', 'rsp', 'rip', 'efl', 'csgsfs', 'err',
>                'trapno', 'oldmask', 'cr2' ]
> 
> def vireo_current():
>     return int(gdb.parse_and_eval('curenv')) + 1
> 
> class VireoGreenThread:
>     def __init__(self, tid):
>         self.tid = tid
> 
>     def _get_state(self):
>         return gdb.parse_and_eval('envs')[self.tid]['state']
> 
>     def fetch(self, reg):
>         """Fetch REG from memory."""
>         global x8664_regs
>         global thread_map
>         thread = thread[self.tid]
>         state = self._get_state()
>         gregs = state['uc_mcontext']['gregs']
>         for i in range(0, len(x8664_regs)):
>             if reg is None or reg == x8664_regs[i]:
>                 thread.write_register(x8664_regs[i], gregs[i])
> 
>     def store(self, reg):
>         global x8664_regs
>         global thread_map
>         thread = thread[self.tid]
>         state = self._get_state()
>         gregs = state['uc_mcontext']['gregs']
>         for i in range(0, len(x8664_regs)):
>             if reg is None or reg == x8664_regs[i]:
>                 gregs[i] = thread.read_register(x8664_regs[i])
> 
>     def name(self):
>         return "Vireo Thread " + str(self.tid)
> 
>     def underlying_thread(self):
>         if vireo_current() == self.tid:
>             global main_thread
>             return main_thread
>         return None
> 
> class VFinish(gdb.FinishBreakpoint):
>     def stop(self):
>         tid = int(self.return_value) + 1
>         global thread_map
>         thread_map[tid] = gdb.create_green_thread(tid, VireoGreenThread(tid))
>         return False
> 
> class VCreate(gdb.Breakpoint):
>     def stop(self):
>         VFinish(gdb.newest_frame(), True)
>         return False
> 
> class VExit(gdb.Breakpoint):
>     def stop(self):
>         global main_thread
>         if main_thread is None:
>             main_thread = gdb.selected_thread()
>         global thread_map
>         tid = vireo_current()
>         if tid in thread_map:
>             thread_map[tid].set_exited()
>             del thread_map[tid]
> 
> VCreate('vireo_create', internal=True)
> VExit('vireo_exit', internal=True)
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

  reply	other threads:[~2022-03-15 14:17 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-03 11:22 Stefan Hajnoczi
2022-03-07 10:49 ` Pedro Alves
2022-03-08  9:47   ` Stefan Hajnoczi
2022-03-07 14:49 ` Florian Weimer
2022-03-07 17:30   ` Tom Tromey
2022-03-09 10:06     ` Florian Weimer
2022-03-09 19:50       ` Tom Tromey
2022-03-07 16:58 ` Tom Tromey
2022-03-07 17:18   ` Pedro Alves
2022-03-08  8:43     ` Stefan Hajnoczi
2022-03-14 20:30   ` Tom Tromey
2022-03-15 14:17     ` Stefan Hajnoczi [this message]
2022-03-18 21:13       ` Tom Tromey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YjCf8BPW+jITJain@stefanha-x1.localdomain \
    --to=stefanha@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=gdb@sourceware.org \
    --cc=pedro@palves.net \
    --cc=qemu-devel@nongnu.org \
    --cc=tom@tromey.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).