mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
Add gstack script
This commit adds support for a `gstack' command which Fedora has been carrying for many years. gstack is a natural counterpart to the gcore command. Whereas gcore dumps a core file, gstack prints stack traces of a running process. There are many improvements over Fedora's version of this script. The dependency on procfs is gone; gstack will run anywhere gdb runs. The only runtime dependencies are bash and awk. The script includes suggestions from gdb/32325 to include versioning and help. [If this approach to gdb/32325 is acceptable, I could propagate the solution to gcore/gdb-add-index.] I've rewritten the documentation, integrating it into the User Manual. The manpage is now output using this one source. Example run (on x86_64 Fedora 40) $ gstack --help Usage: gstack [-h|--help] [-v|--version] PID Print a stack trace of a running program -h, --help Print this message then exit. -v, --version Print version information then exit. $ gstack -v GNU gstack (GDB) 16.0.50.20241119-git $ gstack 12345678 Process 12345678 not found. $ gstack $(pidof emacs) Thread 6 (Thread 0x7fd5ec1c06c0 (LWP 2491423) "pool-spawner"): #0 0x00007fd6015ca3dd in syscall () at /lib64/libc.so.6 #1 0x00007fd60b31eccd in g_cond_wait () at /lib64/libglib-2.0.so.0 #2 0x00007fd60b28a61b in g_async_queue_pop_intern_unlocked () at /lib64/libglib-2.0.so.0 #3 0x00007fd60b2f1a03 in g_thread_pool_spawn_thread () at /lib64/libglib-2.0.so.0 #4 0x00007fd60b2f0813 in g_thread_proxy () at /lib64/libglib-2.0.so.0 #5 0x00007fd6015486d7 in start_thread () at /lib64/libc.so.6 #6 0x00007fd6015cc60c in clone3 () at /lib64/libc.so.6 #7 0x0000000000000000 in ??? () Thread 5 (Thread 0x7fd5eb9bf6c0 (LWP 2491424) "gmain"): #0 0x00007fd6015be87d in poll () at /lib64/libc.so.6 #1 0x0000000000000001 in ??? () #2 0xffffffff00000001 in ??? () #3 0x0000000000000001 in ??? () #4 0x000000002104cfd0 in ??? () #5 0x00007fd5eb9be320 in ??? () #6 0x00007fd60b321c34 in g_main_context_iterate_unlocked.isra () at /lib64/libglib-2.0.so.0 Thread 4 (Thread 0x7fd5eb1be6c0 (LWP 2491425) "gdbus"): #0 0x00007fd6015be87d in poll () at /lib64/libc.so.6 #1 0x0000000020f9b558 in ??? () #2 0xffffffff00000003 in ??? () #3 0x0000000000000003 in ??? () #4 0x00007fd5d8000b90 in ??? () #5 0x00007fd5eb1bd320 in ??? () #6 0x00007fd60b321c34 in g_main_context_iterate_unlocked.isra () at /lib64/libglib-2.0.so.0 Thread 3 (Thread 0x7fd5ea9bd6c0 (LWP 2491426) "emacs"): #0 0x00007fd6015ca3dd in syscall () at /lib64/libc.so.6 #1 0x00007fd60b31eccd in g_cond_wait () at /lib64/libglib-2.0.so.0 #2 0x00007fd60b28a61b in g_async_queue_pop_intern_unlocked () at /lib64/libglib-2.0.so.0 #3 0x00007fd60b28a67c in g_async_queue_pop () at /lib64/libglib-2.0.so.0 #4 0x00007fd603f4d0d9 in fc_thread_func () at /lib64/libpangoft2-1.0.so.0 #5 0x00007fd60b2f0813 in g_thread_proxy () at /lib64/libglib-2.0.so.0 #6 0x00007fd6015486d7 in start_thread () at /lib64/libc.so.6 #7 0x00007fd6015cc60c in clone3 () at /lib64/libc.so.6 #8 0x0000000000000000 in ??? () Thread 2 (Thread 0x7fd5e9e6d6c0 (LWP 2491427) "dconf worker"): #0 0x00007fd6015be87d in poll () at /lib64/libc.so.6 #1 0x0000000000000001 in ??? () #2 0xffffffff00000001 in ??? () #3 0x0000000000000001 in ??? () #4 0x00007fd5cc000b90 in ??? () #5 0x00007fd5e9e6c320 in ??? () #6 0x00007fd60b321c34 in g_main_context_iterate_unlocked.isra () at /lib64/libglib-2.0.so.0 Thread 1 (Thread 0x7fd5fcc45280 (LWP 2491417) "emacs"): #0 0x00007fd6015c9197 in pselect () at /lib64/libc.so.6 #1 0x0000000000000000 in ??? () Since this is essentially a complete rewrite of the original script and documentation, I've chosen to only keep a 2024 copyright date. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Approved-By: Tom Tromey <tom@tromey.com>
This commit is contained in:
@@ -590,6 +590,7 @@ CONFIG_CLEAN = @CONFIG_CLEAN@
|
|||||||
CONFIG_INSTALL = @CONFIG_INSTALL@
|
CONFIG_INSTALL = @CONFIG_INSTALL@
|
||||||
CONFIG_UNINSTALL = @CONFIG_UNINSTALL@
|
CONFIG_UNINSTALL = @CONFIG_UNINSTALL@
|
||||||
HAVE_NATIVE_GCORE_TARGET = @HAVE_NATIVE_GCORE_TARGET@
|
HAVE_NATIVE_GCORE_TARGET = @HAVE_NATIVE_GCORE_TARGET@
|
||||||
|
HAVE_GSTACK = @HAVE_GSTACK@
|
||||||
|
|
||||||
CONFIG_SRC_SUBDIR = arch cli dwarf2 mi compile tui unittests guile python \
|
CONFIG_SRC_SUBDIR = arch cli dwarf2 mi compile tui unittests guile python \
|
||||||
target nat
|
target nat
|
||||||
@@ -1946,7 +1947,7 @@ generated_files = \
|
|||||||
# Flags needed to compile Python code
|
# Flags needed to compile Python code
|
||||||
PYTHON_CFLAGS = @PYTHON_CFLAGS@
|
PYTHON_CFLAGS = @PYTHON_CFLAGS@
|
||||||
|
|
||||||
all: gdb$(EXEEXT) $(CONFIG_ALL) gdb-gdb.py gdb-gdb.gdb gcore
|
all: gdb$(EXEEXT) $(CONFIG_ALL) gdb-gdb.py gdb-gdb.gdb gcore gstack
|
||||||
@$(MAKE) $(FLAGS_TO_PASS) DO=all "DODIRS=$(SUBDIRS)" subdir_do
|
@$(MAKE) $(FLAGS_TO_PASS) DO=all "DODIRS=$(SUBDIRS)" subdir_do
|
||||||
|
|
||||||
# Rule for compiling .c files in the top-level gdb directory.
|
# Rule for compiling .c files in the top-level gdb directory.
|
||||||
@@ -2105,6 +2106,19 @@ install-only: $(CONFIG_INSTALL)
|
|||||||
$(INSTALL_SCRIPT) gcore \
|
$(INSTALL_SCRIPT) gcore \
|
||||||
$(DESTDIR)$(bindir)/$$transformed_name; \
|
$(DESTDIR)$(bindir)/$$transformed_name; \
|
||||||
fi
|
fi
|
||||||
|
if test "x${HAVE_GSTACK}" != x; \
|
||||||
|
then \
|
||||||
|
transformed_name=`t='$(program_transform_name)'; \
|
||||||
|
echo gstack | sed -e "$$t"` ; \
|
||||||
|
if test "x$$transformed_name" = x; then \
|
||||||
|
transformed_name=gstack ; \
|
||||||
|
else \
|
||||||
|
true ; \
|
||||||
|
fi ; \
|
||||||
|
$(SHELL) $(srcdir)/../mkinstalldirs $(DESTDIR)$(bindir) ; \
|
||||||
|
$(INSTALL_SCRIPT) gstack \
|
||||||
|
$(DESTDIR)$(bindir)/$$transformed_name; \
|
||||||
|
fi
|
||||||
transformed_name=`t='$(program_transform_name)'; \
|
transformed_name=`t='$(program_transform_name)'; \
|
||||||
echo gdb-add-index | sed -e "$$t"` ; \
|
echo gdb-add-index | sed -e "$$t"` ; \
|
||||||
if test "x$$transformed_name" = x; then \
|
if test "x$$transformed_name" = x; then \
|
||||||
@@ -2149,6 +2163,17 @@ uninstall: force $(CONFIG_UNINSTALL)
|
|||||||
fi ; \
|
fi ; \
|
||||||
rm -f $(DESTDIR)$(bindir)/$$transformed_name; \
|
rm -f $(DESTDIR)$(bindir)/$$transformed_name; \
|
||||||
fi
|
fi
|
||||||
|
if test "x$(HAVE_GSTACK)" != x; \
|
||||||
|
then \
|
||||||
|
transformed_name=`t='$(program_transform_name)'; \
|
||||||
|
echo gstack | sed -e "$$t"` ; \
|
||||||
|
if test "x$$transformed_name" = x; then \
|
||||||
|
transformed_name=gstack ; \
|
||||||
|
else \
|
||||||
|
true ; \
|
||||||
|
fi ; \
|
||||||
|
rm -f $(DESTDIR)$(bindir)/$$transformed_name; \
|
||||||
|
fi
|
||||||
transformed_name=`t='$(program_transform_name)'; \
|
transformed_name=`t='$(program_transform_name)'; \
|
||||||
echo gdb-add-index | sed -e "$$t"` ; \
|
echo gdb-add-index | sed -e "$$t"` ; \
|
||||||
if test "x$$transformed_name" = x; then \
|
if test "x$$transformed_name" = x; then \
|
||||||
@@ -2260,7 +2285,7 @@ clean mostlyclean: $(CONFIG_CLEAN)
|
|||||||
# functionality described is if the distributed files are unmodified.
|
# functionality described is if the distributed files are unmodified.
|
||||||
distclean: clean
|
distclean: clean
|
||||||
@$(MAKE) $(FLAGS_TO_PASS) DO=distclean "DODIRS=$(CLEANDIRS)" subdir_do
|
@$(MAKE) $(FLAGS_TO_PASS) DO=distclean "DODIRS=$(CLEANDIRS)" subdir_do
|
||||||
rm -f nm.h config.status config.h stamp-h b jit-reader.h gcore stamp-nmh
|
rm -f nm.h config.status config.h stamp-h b jit-reader.h gcore gstack gstack.in stamp-nmh
|
||||||
rm -f gdb-gdb.py gdb-gdb.gdb
|
rm -f gdb-gdb.py gdb-gdb.gdb
|
||||||
rm -f y.output yacc.acts yacc.tmp y.tab.h
|
rm -f y.output yacc.acts yacc.tmp y.tab.h
|
||||||
rm -f config.log config.cache
|
rm -f config.log config.cache
|
||||||
@@ -2321,6 +2346,15 @@ jit-reader.h: $(srcdir)/jit-reader.in config.status
|
|||||||
gcore: $(srcdir)/gcore.in config.status
|
gcore: $(srcdir)/gcore.in config.status
|
||||||
$(ECHO_GEN) $(SHELL) config.status $(SILENT_FLAG) $@
|
$(ECHO_GEN) $(SHELL) config.status $(SILENT_FLAG) $@
|
||||||
|
|
||||||
|
gstack: gstack.in version.c
|
||||||
|
$(ECHO_GEN) \
|
||||||
|
vv=`grep 'version\[\] = ' version.c | grep -o '".*"' | tr -d \"`; \
|
||||||
|
sed -e "s,@VERSION@,$$vv," $< > $@
|
||||||
|
@chmod +x $@
|
||||||
|
|
||||||
|
gstack.in: $(srcdir)/gstack-1.in config.status
|
||||||
|
$(ECHO_GEN) $(SHELL) config.status $(SILENT_FLAG) $@
|
||||||
|
|
||||||
gdb-gdb.py: $(srcdir)/gdb-gdb.py.in config.status
|
gdb-gdb.py: $(srcdir)/gdb-gdb.py.in config.status
|
||||||
$(ECHO_GEN) $(SHELL) config.status $(SILENT_FLAG) $@
|
$(ECHO_GEN) $(SHELL) config.status $(SILENT_FLAG) $@
|
||||||
|
|
||||||
|
|||||||
2
gdb/NEWS
2
gdb/NEWS
@@ -62,6 +62,8 @@
|
|||||||
* Support for process record/replay and reverse debugging on loongarch*-linux*
|
* Support for process record/replay and reverse debugging on loongarch*-linux*
|
||||||
targets has been added.
|
targets has been added.
|
||||||
|
|
||||||
|
* New bash script gstack uses GDB to print stack traces of running processes.
|
||||||
|
|
||||||
* Python API
|
* Python API
|
||||||
|
|
||||||
** Added gdb.record.clear. Clears the trace data of the current recording.
|
** Added gdb.record.clear. Clears the trace data of the current recording.
|
||||||
|
|||||||
14
gdb/configure
vendored
14
gdb/configure
vendored
@@ -760,6 +760,7 @@ HAVE_NATIVE_GCORE_TARGET
|
|||||||
TARGET_OBS
|
TARGET_OBS
|
||||||
AMD_DBGAPI_LIBS
|
AMD_DBGAPI_LIBS
|
||||||
AMD_DBGAPI_CFLAGS
|
AMD_DBGAPI_CFLAGS
|
||||||
|
HAVE_GSTACK
|
||||||
ENABLE_BFD_64_BIT_FALSE
|
ENABLE_BFD_64_BIT_FALSE
|
||||||
ENABLE_BFD_64_BIT_TRUE
|
ENABLE_BFD_64_BIT_TRUE
|
||||||
subdirs
|
subdirs
|
||||||
@@ -11499,7 +11500,7 @@ else
|
|||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 11502 "configure"
|
#line 11503 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
@@ -11605,7 +11606,7 @@ else
|
|||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 11608 "configure"
|
#line 11609 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
@@ -25008,6 +25009,12 @@ if test x${all_targets} = xtrue; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
HAVE_GSTACK=0
|
||||||
|
if test $gdb_native = yes; then
|
||||||
|
HAVE_GSTACK=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# AMD debugger API support.
|
# AMD debugger API support.
|
||||||
|
|
||||||
|
|
||||||
@@ -33761,6 +33768,8 @@ fi
|
|||||||
|
|
||||||
ac_config_files="$ac_config_files gcore"
|
ac_config_files="$ac_config_files gcore"
|
||||||
|
|
||||||
|
ac_config_files="$ac_config_files gstack.in:gstack-1.in"
|
||||||
|
|
||||||
ac_config_files="$ac_config_files Makefile gdb-gdb.gdb gdb-gdb.py doc/Makefile data-directory/Makefile"
|
ac_config_files="$ac_config_files Makefile gdb-gdb.gdb gdb-gdb.py doc/Makefile data-directory/Makefile"
|
||||||
|
|
||||||
|
|
||||||
@@ -34861,6 +34870,7 @@ do
|
|||||||
"jit-reader.h") CONFIG_FILES="$CONFIG_FILES jit-reader.h:jit-reader.in" ;;
|
"jit-reader.h") CONFIG_FILES="$CONFIG_FILES jit-reader.h:jit-reader.in" ;;
|
||||||
"nm.h") CONFIG_LINKS="$CONFIG_LINKS nm.h:$GDB_NM_FILE" ;;
|
"nm.h") CONFIG_LINKS="$CONFIG_LINKS nm.h:$GDB_NM_FILE" ;;
|
||||||
"gcore") CONFIG_FILES="$CONFIG_FILES gcore" ;;
|
"gcore") CONFIG_FILES="$CONFIG_FILES gcore" ;;
|
||||||
|
"gstack.in") CONFIG_FILES="$CONFIG_FILES gstack.in:gstack-1.in" ;;
|
||||||
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
|
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
|
||||||
"gdb-gdb.gdb") CONFIG_FILES="$CONFIG_FILES gdb-gdb.gdb" ;;
|
"gdb-gdb.gdb") CONFIG_FILES="$CONFIG_FILES gdb-gdb.gdb" ;;
|
||||||
"gdb-gdb.py") CONFIG_FILES="$CONFIG_FILES gdb-gdb.py" ;;
|
"gdb-gdb.py") CONFIG_FILES="$CONFIG_FILES gdb-gdb.py" ;;
|
||||||
|
|||||||
@@ -261,6 +261,12 @@ if test x${all_targets} = xtrue; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
HAVE_GSTACK=0
|
||||||
|
if test $gdb_native = yes; then
|
||||||
|
HAVE_GSTACK=1
|
||||||
|
fi
|
||||||
|
AC_SUBST(HAVE_GSTACK)
|
||||||
|
|
||||||
# AMD debugger API support.
|
# AMD debugger API support.
|
||||||
|
|
||||||
AC_ARG_WITH([amd-dbgapi],
|
AC_ARG_WITH([amd-dbgapi],
|
||||||
@@ -2264,6 +2270,7 @@ GDB_AC_SELFTEST([
|
|||||||
GDB_AC_TRANSFORM([gdb], [GDB_TRANSFORM_NAME])
|
GDB_AC_TRANSFORM([gdb], [GDB_TRANSFORM_NAME])
|
||||||
GDB_AC_TRANSFORM([gcore], [GCORE_TRANSFORM_NAME])
|
GDB_AC_TRANSFORM([gcore], [GCORE_TRANSFORM_NAME])
|
||||||
AC_CONFIG_FILES([gcore], [chmod +x gcore])
|
AC_CONFIG_FILES([gcore], [chmod +x gcore])
|
||||||
|
AC_CONFIG_FILES([gstack.in:gstack-1.in])
|
||||||
AC_CONFIG_FILES([Makefile gdb-gdb.gdb gdb-gdb.py doc/Makefile data-directory/Makefile])
|
AC_CONFIG_FILES([Makefile gdb-gdb.gdb gdb-gdb.py doc/Makefile data-directory/Makefile])
|
||||||
|
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ TEXI2POD = perl $(srcdir)/../../etc/texi2pod.pl \
|
|||||||
POD2MAN = pod2man --center="GNU Development Tools"
|
POD2MAN = pod2man --center="GNU Development Tools"
|
||||||
|
|
||||||
# List of man pages generated from gdb.texi
|
# List of man pages generated from gdb.texi
|
||||||
MAN1S = gdb.1 gdbserver.1 gcore.1 gdb-add-index.1
|
MAN1S = gdb.1 gdbserver.1 gcore.1 gstack.1 gdb-add-index.1
|
||||||
MAN5S = gdbinit.5
|
MAN5S = gdbinit.5
|
||||||
MANS = $(MAN1S) $(MAN5S)
|
MANS = $(MAN1S) $(MAN5S)
|
||||||
|
|
||||||
@@ -199,6 +199,7 @@ POD_FILE_TMPS = $(patsubst %.1,%.pod,$(MAN1S)) \
|
|||||||
|
|
||||||
HAVE_NATIVE_GCORE_TARGET = @HAVE_NATIVE_GCORE_TARGET@
|
HAVE_NATIVE_GCORE_TARGET = @HAVE_NATIVE_GCORE_TARGET@
|
||||||
HAVE_NATIVE_GCORE_HOST = @HAVE_NATIVE_GCORE_HOST@
|
HAVE_NATIVE_GCORE_HOST = @HAVE_NATIVE_GCORE_HOST@
|
||||||
|
HAVE_GSTACK = @HAVE_GSTACK@
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
@@ -339,6 +340,10 @@ install-man1: $(MAN1S)
|
|||||||
-a "$$p" = gcore.1; then \
|
-a "$$p" = gcore.1; then \
|
||||||
continue; \
|
continue; \
|
||||||
fi; \
|
fi; \
|
||||||
|
if test "x$(HAVE_GSTACK)" = x \
|
||||||
|
-a "$$p" = gstack.1; then \
|
||||||
|
continue; \
|
||||||
|
fi; \
|
||||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||||
f=`echo $$p | sed -e 's|^.*/||' -e '$(transform)'`; \
|
f=`echo $$p | sed -e 's|^.*/||' -e '$(transform)'`; \
|
||||||
echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(man1dir)/$$f'"; \
|
echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(man1dir)/$$f'"; \
|
||||||
@@ -363,6 +368,10 @@ uninstall-man1:
|
|||||||
-a "$$i" = gcore.1; then \
|
-a "$$i" = gcore.1; then \
|
||||||
continue; \
|
continue; \
|
||||||
fi; \
|
fi; \
|
||||||
|
if test "x$(HAVE_GSTACK)" = x \
|
||||||
|
-a "$$i" = gstack.1; then \
|
||||||
|
continue; \
|
||||||
|
fi; \
|
||||||
echo "$$i"; \
|
echo "$$i"; \
|
||||||
done | \
|
done | \
|
||||||
sed -n '/\.1[a-z]*$$/p'; \
|
sed -n '/\.1[a-z]*$$/p'; \
|
||||||
|
|||||||
@@ -50584,6 +50584,7 @@ Show the current verbosity setting.
|
|||||||
* gdb man:: The GNU Debugger man page
|
* gdb man:: The GNU Debugger man page
|
||||||
* gdbserver man:: Remote Server for the GNU Debugger man page
|
* gdbserver man:: Remote Server for the GNU Debugger man page
|
||||||
* gcore man:: Generate a core file of a running program
|
* gcore man:: Generate a core file of a running program
|
||||||
|
* gstack man:: Print a stack trace of a running program
|
||||||
* gdbinit man:: gdbinit scripts
|
* gdbinit man:: gdbinit scripts
|
||||||
* gdb-add-index man:: Add index files to speed up GDB
|
* gdb-add-index man:: Add index files to speed up GDB
|
||||||
@end menu
|
@end menu
|
||||||
@@ -51266,6 +51267,75 @@ Richard M. Stallman and Roland H. Pesch, July 1991.
|
|||||||
@end ifset
|
@end ifset
|
||||||
@c man end
|
@c man end
|
||||||
|
|
||||||
|
@node gstack man
|
||||||
|
@heading gstack
|
||||||
|
|
||||||
|
@c man title gstack Print a stack trace of a running program
|
||||||
|
|
||||||
|
@format
|
||||||
|
@c man begin SYNOPSIS gstack
|
||||||
|
gstack [-h | --help] [-v | --version] @var{pid}
|
||||||
|
@c man end
|
||||||
|
@end format
|
||||||
|
|
||||||
|
@c man begin DESCRIPTION gstack
|
||||||
|
Print a stack trace of a running program with process ID @var{pid}. If the process
|
||||||
|
is multi-threaded, @command{gstack} outputs backtraces for every thread which exists
|
||||||
|
in the process.
|
||||||
|
|
||||||
|
The script invokes @value{GDBN}, attaches to the given process ID, prints the stack trace,
|
||||||
|
and detaches from the process.
|
||||||
|
|
||||||
|
@command{gstack} exits with non-zero status if @code{gdb} was unable to attach to
|
||||||
|
the given process ID for any reason, such as a non-existent process ID or
|
||||||
|
insufficient privileges to attach to the process.
|
||||||
|
@c man end
|
||||||
|
|
||||||
|
@c man begin OPTIONS gstack
|
||||||
|
@table @env
|
||||||
|
@item --help
|
||||||
|
@itemx -h
|
||||||
|
List all options, with brief explanations.
|
||||||
|
|
||||||
|
@item --version
|
||||||
|
@itemx -v
|
||||||
|
Print version information and then exit.
|
||||||
|
@end table
|
||||||
|
@c man end
|
||||||
|
|
||||||
|
@c man begin ENVIRONMENT gstack
|
||||||
|
@table @env
|
||||||
|
@item AWK
|
||||||
|
Full file name for an Awk interpreter to use. If not set, @env{PATH} will be
|
||||||
|
searched for an @code{awk} program.
|
||||||
|
|
||||||
|
@item GDB
|
||||||
|
Full file name for a @value{GDBN} executable to use to generate stack backtraces.
|
||||||
|
If not set, @env{PATH} will be searched for a @code{gdb} program.
|
||||||
|
|
||||||
|
@item GDBARGS
|
||||||
|
Optional arguments to be passed to the @code{gdb} program.
|
||||||
|
@end table
|
||||||
|
@c man end
|
||||||
|
|
||||||
|
@c man begin SEEALSO gstack
|
||||||
|
@ifset man
|
||||||
|
The full documentation for @value{GDBN} is maintained as a Texinfo manual.
|
||||||
|
If the @code{info} and @code{gdb} programs and @value{GDBN}'s Texinfo
|
||||||
|
documentation are properly installed at your site, the command
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
info gdb
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
should give you access to the complete manual.
|
||||||
|
|
||||||
|
@cite{Using GDB: A Guide to the GNU Source-Level Debugger},
|
||||||
|
Richard M. Stallman and Roland H. Pesch, July 1991.
|
||||||
|
@end ifset
|
||||||
|
@c man end
|
||||||
|
|
||||||
@node gdbinit man
|
@node gdbinit man
|
||||||
@heading gdbinit
|
@heading gdbinit
|
||||||
|
|
||||||
|
|||||||
147
gdb/gstack-1.in
Executable file
147
gdb/gstack-1.in
Executable file
@@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright (C) 2024 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Print a stack trace of a running process.
|
||||||
|
# Similar to the gcore command, but instead of creating a core file,
|
||||||
|
# we simply have gdb print out the stack backtrace to the terminal.
|
||||||
|
|
||||||
|
GDB=${GDB:-$(command -v gdb)}
|
||||||
|
GDBARGS=${GDBARGS:-}
|
||||||
|
AWK=${AWK:-}
|
||||||
|
PKGVERSION=@PKGVERSION@
|
||||||
|
VERSION=@VERSION@
|
||||||
|
|
||||||
|
# Find an appropriate awk interpreter if one was not specified
|
||||||
|
# via the environment.
|
||||||
|
awk_prog=""
|
||||||
|
if [ -z "$AWK" ]; then
|
||||||
|
for prog in gawk mawk nawk awk; do
|
||||||
|
awk_prog=$(command -v $prog)
|
||||||
|
test -n "$awk_prog" && break
|
||||||
|
done
|
||||||
|
AWK="$awk_prog"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$AWK" ]; then
|
||||||
|
echo "$0: could not find usable awk interpreter" 1>&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
function print_usage() {
|
||||||
|
echo "Usage: $0 [-h|--help] [-v|--version] PID"
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_try_help() {
|
||||||
|
echo "Try '$0 --help' for more information."
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_help() {
|
||||||
|
print_usage
|
||||||
|
echo "Print a stack trace of a running program"
|
||||||
|
echo
|
||||||
|
echo " -h, --help Print this message then exit."
|
||||||
|
echo " -v, --version Print version information then exit."
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_version() {
|
||||||
|
echo "GNU gstack (${PKGVERSION}) ${VERSION}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse options.
|
||||||
|
while getopts hv-: OPT; do
|
||||||
|
if [ "$OPT" = "-" ]; then
|
||||||
|
OPT="${OPTARG%%=*}"
|
||||||
|
OPTARG="${OPTARG#'$OPT'}"
|
||||||
|
OPTARG="${OPTARG#=}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$OPT" in
|
||||||
|
h | help)
|
||||||
|
print_help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
v | version)
|
||||||
|
print_version
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
\?)
|
||||||
|
# getopts has already output an error message.
|
||||||
|
print_try_help 1>&2
|
||||||
|
exit 2 ;;
|
||||||
|
*)
|
||||||
|
echo "$0: unrecognized option '--$OPT'" 1>&2
|
||||||
|
print_try_help 1>&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
# The sole remaining argument should be the PID of the process
|
||||||
|
# whose backtrace is desired.
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
print_usage 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PID=$1
|
||||||
|
|
||||||
|
awk_script=$(cat << EOF
|
||||||
|
BEGIN {
|
||||||
|
first=1
|
||||||
|
attach_okay=0
|
||||||
|
}
|
||||||
|
|
||||||
|
/ATTACHED/ {
|
||||||
|
attach_okay=1
|
||||||
|
}
|
||||||
|
|
||||||
|
/^#/ {
|
||||||
|
if (attach_okay) {
|
||||||
|
print \$0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/^Thread/ {
|
||||||
|
if (attach_okay) {
|
||||||
|
if (first == 0)
|
||||||
|
print ""
|
||||||
|
first=0
|
||||||
|
print \$0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
END {
|
||||||
|
if (attach_okay == 0)
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
# Run GDB and remove some unwanted noise.
|
||||||
|
"$GDB" --quiet -nx --readnever $GDBARGS <<EOF |
|
||||||
|
set width 0
|
||||||
|
set height 0
|
||||||
|
set pagination no
|
||||||
|
set debuginfod enabled off
|
||||||
|
define attach-bt
|
||||||
|
attach \$arg0
|
||||||
|
echo "ATTACHED"
|
||||||
|
thread apply all bt
|
||||||
|
end
|
||||||
|
attach-bt $PID
|
||||||
|
EOF
|
||||||
|
$AWK -- "$awk_script"
|
||||||
32
gdb/testsuite/gdb.base/gstack.c
Normal file
32
gdb/testsuite/gdb.base/gstack.c
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* This testcase is part of GDB, the GNU debugger.
|
||||||
|
|
||||||
|
Copyright 2024 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
const char msg[] = "looping\n";
|
||||||
|
|
||||||
|
/* Output a simple string for the expect script to monitor us. */
|
||||||
|
write (1, msg, strlen (msg));
|
||||||
|
|
||||||
|
for (;;) ;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
92
gdb/testsuite/gdb.base/gstack.exp
Normal file
92
gdb/testsuite/gdb.base/gstack.exp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# Copyright (C) 2024 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
require !gdb_protocol_is_remote
|
||||||
|
standard_testfile
|
||||||
|
|
||||||
|
if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}] == -1} {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
set command "$binfile"
|
||||||
|
set res [remote_spawn host $command]
|
||||||
|
if { ![gdb_assert { ![expr {$res < 0 || $res == ""}] } "spawn inferior"] } {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# The spawn id of the test inferior.
|
||||||
|
set test_spawn_id $res
|
||||||
|
|
||||||
|
# Wait for the spawned program to loop.
|
||||||
|
set test "wait for inferior to loop"
|
||||||
|
gdb_expect {
|
||||||
|
-re "looping\r\n" {
|
||||||
|
pass $test
|
||||||
|
}
|
||||||
|
eof {
|
||||||
|
fail "$test (eof)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timeout {
|
||||||
|
fail "$test (timeout)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# The test case uses a very simple notification not to get caught by attach on
|
||||||
|
# exiting the function.
|
||||||
|
|
||||||
|
set test "spawn gstack"
|
||||||
|
set pid [spawn_id_get_pid $test_spawn_id]
|
||||||
|
set gstack_cmd [findfile $base_dir/../../gdb/gstack]
|
||||||
|
set command "sh -c GDB=$GDB\\ GDBARGS=-data-directory\\\\\\ $GDB_DATA_DIRECTORY\\ $gstack_cmd\\ $pid\\;echo\\ GSTACK-END"
|
||||||
|
set res [remote_spawn host $command]
|
||||||
|
if { ![gdb_assert { ![expr {$res < 0 || $res == ""}] } $test] } {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
set test "got backtrace"
|
||||||
|
set saw_backtrace false
|
||||||
|
set no_awk false
|
||||||
|
gdb_test_multiple "" $test {
|
||||||
|
-i "$res" -re "#0 +(0x\[0-9a-f\]+ in )?main \(\).*\r\nGSTACK-END\r\n\$" {
|
||||||
|
set saw_backtrace true
|
||||||
|
pass $test
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
|
||||||
|
-i "$res" "could not find usable awk interpreter" {
|
||||||
|
set no_awk true
|
||||||
|
exp_continue
|
||||||
|
}
|
||||||
|
|
||||||
|
eof {
|
||||||
|
set result [wait -i $res]
|
||||||
|
verbose $result
|
||||||
|
|
||||||
|
gdb_assert { [lindex $result 2] == 0 } "gstack exits with no error"
|
||||||
|
gdb_assert { [lindex $result 3] == 0 } "gstack's exit status is 0"
|
||||||
|
|
||||||
|
remote_close host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if {$no_awk} {
|
||||||
|
unsupported "no awk interpreter found"
|
||||||
|
} elseif {!$saw_backtrace} {
|
||||||
|
fail $test
|
||||||
|
}
|
||||||
|
|
||||||
|
# Kill the test inferior.
|
||||||
|
kill_wait_spawned_process $test_spawn_id
|
||||||
Reference in New Issue
Block a user