Почему "pstack" печатает только содержимое одного потока?
Мой OS
является RHEL 7
и я бегу простой Go
программа:
package main
import (
"time"
)
func main() {
time.Sleep(1000 * time.Second)
}
Во время его работы я проверяю количество потоков процесса:
# cat /proc/13858/status | grep Thread
Threads: 5
При использовании pstack
команда отправлена на RHEL
, он печатает только стек одного потока:
# pstack 13858
Thread 1 (process 13858):
#0 runtime.futex () at /usr/local/go/src/runtime/sys_linux_amd64.s:307
#1 0x0000000000422580 in runtime.futexsleep (addr=0x4c7af8 <runtime.timers+24>, val=0, ns=999999997446) at /usr/local/go/src/runtime/os1_linux.go:57
#2 0x000000000040b07b in runtime.notetsleep_internal (n=0x4c7af8 <runtime.timers+24>, ns=999999997446, ~r2=255) at /usr/local/go/src/runtime/lock_futex.go:174
#3 0x000000000040b1e6 in runtime.notetsleepg (n=0x4c7af8 <runtime.timers+24>, ns=999999997446, ~r2=false) at /usr/local/go/src/runtime/lock_futex.go:206
#4 0x000000000043e5de in runtime.timerproc () at /usr/local/go/src/runtime/time.go:209
#5 0x0000000000451001 in runtime.goexit () at /usr/local/go/src/runtime/asm_amd64.s:1998
#6 0x0000000000000000 in ?? ()
Почему pstack
печатать только содержимое одной темы?
PS: pstack
Сценарий здесь:
#!/bin/sh
if test $# -ne 1; then
echo "Usage: `basename $0 .sh` <process-id>" 1>&2
exit 1
fi
if test ! -r /proc/$1; then
echo "Process $1 not found." 1>&2
exit 1
fi
# GDB doesn't allow "thread apply all bt" when the process isn't
# threaded; need to peek at the process to determine if that or the
# simpler "bt" should be used.
backtrace="bt"
if test -d /proc/$1/task ; then
# Newer kernel; has a task/ directory.
if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; then
backtrace="thread apply all bt"
fi
elif test -f /proc/$1/maps ; then
# Older kernel; go by it loading libpthread.
if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; then
backtrace="thread apply all bt"
fi
fi
GDB=${GDB:-/usr/bin/gdb}
# Run GDB, strip out unwanted noise.
# --readnever is no longer used since .gdb_index is now in use.
$GDB --quiet -nx $GDBARGS /proc/$1/exe $1 <<EOF 2>&1 |
set width 0
set height 0
set pagination no
$backtrace
EOF
/bin/sed -n \
-e 's/^\((gdb) \)*//' \
-e '/^#/p' \
-e '/^Thread/p'
2 ответа
pstack использует GDB. Это цитата из Голанга Док ( https://golang.org/doc/gdb):
GDB плохо понимает программы Go. Управление стеком, многопоточность и среда выполнения содержат аспекты, которые достаточно отличаются от модели исполнения. GDB ожидает, что они могут запутать отладчик, даже когда программа скомпилирована с помощью gccgo. Как следствие, хотя GDB может быть полезен в некоторых ситуациях, он не является надежным отладчиком для программ Go, особенно для сильно параллельных.
4 из 5 потоков, которые вы видите в /proc, создаются еще до того, как ваша программа войдет в main. Я предполагаю, что среда выполнения Голанга создает их.
Почему pstack печатает только содержимое одного потока?
Судя по выводу strace для GDB, я вижу, что gdb
на самом деле пытается присоединиться к ним, но после того, как что-то идет не так и gdb
не пытается проверять эти темы. Это системные вызовы, которые GDB выпустил для этих потоков времени выполнения, но по неизвестной причине решил немедленно прекратить их расследование:
5072 ptrace(PTRACE_ATTACH, 5023, 0, 0) = 0
5072 --- SIGCHLD (Child exited) @ 0 (0) ---
5072 rt_sigreturn(0x11) = 0
5072 ptrace(PTRACE_ATTACH, 5024, 0, 0) = 0
5072 --- SIGCHLD (Child exited) @ 0 (0) ---
5072 rt_sigreturn(0x11) = 0
5072 ptrace(PTRACE_ATTACH, 5025, 0, 0) = 0
5072 --- SIGCHLD (Child exited) @ 0 (0) ---
5072 rt_sigreturn(0x11) = 0
Однако вы можете проверить их самостоятельно. Кажется, что эти темы принадлежат Golang Runtime
$ pstack 5094
Thread 1 (process 5094):
#0 0x0000000000459243 in runtime.futex ()
#1 0x00000000004271e0 in runtime.futexsleep ()
#2 0x000000000040d55b in runtime.notetsleep_internal ()
#3 0x000000000040d64b in runtime.notetsleep ()
#4 0x0000000000435677 in runtime.sysmon ()
#5 0x000000000042e6cc in runtime.mstart1 ()
#6 0x000000000042e5d2 in runtime.mstart ()
#7 0x00000000004592b7 in runtime.clone ()
#8 0x0000000000000000 in ?? ()
$ pstack 5095
Thread 1 (process 5095):
#0 0x0000000000459243 in runtime.futex ()
#1 0x0000000000427143 in runtime.futexsleep ()
#2 0x000000000040d3f4 in runtime.notesleep ()
#3 0x000000000042f6eb in runtime.stopm ()
#4 0x0000000000430a79 in runtime.findrunnable ()
#5 0x00000000004310ff in runtime.schedule ()
#6 0x000000000043139b in runtime.park_m ()
#7 0x0000000000455acb in runtime.mcall ()
#8 0x000000c820021500 in ?? ()
#9 0x0000000000000000 in ?? ()
$ pstack 5096
Thread 1 (process 5096):
#0 0x0000000000459243 in runtime.futex ()
#1 0x0000000000427143 in runtime.futexsleep ()
#2 0x000000000040d3f4 in runtime.notesleep ()
#3 0x000000000042f6eb in runtime.stopm ()
#4 0x000000000042fff7 in runtime.startlockedm ()
#5 0x0000000000431147 in runtime.schedule ()
#6 0x000000000043139b in runtime.park_m ()
#7 0x0000000000455acb in runtime.mcall ()
#8 0x000000c820020000 in ?? ()
Обновление для GDB 8.0
pstack, использующий gdb 8.0, правильно печатает обратные трассировки для всех цепочек. Команда выглядит так:
$ GDB=$HOME/bin/gdb pstack $(pidof main)
И вот его вывод (сокращенный):
$ GDB=$HOME/bin/gdb pstack $(pidof main) | egrep "^Thread"
Thread 4 (LWP 18335):
Thread 3 (LWP 18334):
Thread 2 (LWP 18333):
Thread 1 (LWP 18332):
Когда вы передаете LWP/ идентификатор потока в pstack
Вы получаете стек только из этого потока. Попробуйте передать PID процесса pstack
и вы получите стеки всех его потоков. Вы можете получить PID или Tgid (идентификатор группы потоков) процесса: cat /proc/13858/status | grep Tgid
, Чтобы получить все LWP, созданные вашим процессом, вы можете запустить ps -L <PID>