python3 provides gdb extensions. Using
them, gdb can attach to a running program, select a thread and print its
python backtrace.
On Debian (since at least Buster) the extensions are part of the
python3.x-dbg package (ex. python3.10-dbg installs
/usr/share/gdb/auto-load/usr/bin/python3.10-gdb.py) and gdb auto-loads them.
Example with a simple threaded python script:
#!/usr/bin/env python3
import signal
import threading
def a():
    while True:
        pass
def b():
    while True:
        signal.pause()
threading.Thread(target=a).start()
threading.Thread(target=b).start()
Running gdb:
user@vsid:~$ ps -C python3 -L
    PID     LWP TTY          TIME CMD
   1215    1215 pts/0    00:00:00 python3
   1215    1216 pts/0    00:00:19 python3
   1215    1217 pts/0    00:00:00 python3
user@vsid:~$ gdb -p 1215
GNU gdb (Debian 10.1-2+b1) 10.1.90.20210103-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
[...]
(gdb) info auto-load python-scripts
Loaded  Script
Yes     /usr/share/gdb/auto-load/usr/bin/python3.10-gdb.py
(gdb) info threads
  Id   Target Id                                  Frame
* 1    Thread 0x7f2f034b4740 (LWP 1215) "python3" 0x00007f2f036a60fa in __futex_abstimed_wait_common64 (futex_word=futex_word@entry=0x7f2ef4000b60, expected=expected@entry=0,
    clockid=clockid@entry=0, abstime=abstime@entry=0x0, private=<optimized out>,
    cancel=cancel@entry=true) at ../sysdeps/nptl/futex-internal.c:74
  2    Thread 0x7f2f02ea7640 (LWP 1216) "python3" 0x000000000051b858 in _PyEval_EvalFrameDefault
    (tstate=<optimized out>, f=<optimized out>, throwflag=<optimized out>)
    at ../Python/ceval.c:3850
  3    Thread 0x7f2f026a6640 (LWP 1217) "python3" 0x00007f2f036a3932 in __libc_pause ()
    at ../sysdeps/unix/sysv/linux/pause.c:29
(gdb) thread 2
(gdb) py-bt
Traceback (most recent call first):
  File "/root/./threaded.py", line 7, in a
    while True:
  File "/usr/lib/python3.10/threading.py", line 946, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/lib/python3.10/threading.py", line 1009, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.10/threading.py", line 966, in _bootstrap
    self._bootstrap_inner()
(gdb)
We can confirm that thread 1216 which used the most cpu time according to
ps is indeed the thread running function a() that is busy-looping.