| #!/usr/bin/env bash |
| |
| # Copyright (C) 2024-2025 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 $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" |