#!/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 .
# 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 <