Created
April 3, 2013 05:56
-
-
Save cibyr/5298770 to your computer and use it in GitHub Desktop.
Bash script to attach GDB to a process and print a stack trace, from http://root.cern.ch/svn/root/tags/v5-26-00b/etc/gdb-backtrace.sh Useful on stupid distros (like Ubuntu) where pstack doesn't work and gstack isn't included in the GDB package. Set /proc/sys/kernel/yama/ptrace_scope to 0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This script is almost identical to /usr/bin/gstack. | |
# It is used by TUnixSystem::StackTrace() on Linux and MacOS X. | |
tempname=`basename $0 .sh` | |
messfile=`dirname $0`/gdb-message.sh | |
OUTFILE=`mktemp -q /tmp/${tempname}.XXXXXX` | |
if test $? -ne 0; then | |
OUTFILE=/dev/stdout | |
fi | |
if [ `uname -s` = "Darwin" ]; then | |
if test $# -lt 2; then | |
echo "Usage: ${tempname} <executable> <process-id> [gdb-mess-file]" 1>&2 | |
exit 1 | |
fi | |
if test $# -eq 3; then | |
messfile=$3 | |
fi | |
if test ! -x $1; then | |
echo "${tempname}: process $1 not found." 1>&2 | |
exit 1 | |
fi | |
TMPFILE=`mktemp -q /tmp/${tempname}.XXXXXX` | |
if test $? -ne 0; then | |
echo "${tempname}: can't create temp file, exiting..." 1>&2 | |
exit 1 | |
fi | |
backtrace="thread apply all bt" | |
echo $backtrace > $TMPFILE | |
GDB=${GDB:-gdb} | |
# Run GDB, strip out unwanted noise. | |
$GDB -q -batch -n -x $TMPFILE $1 $2 2>&1 | | |
/usr/bin/sed -n \ | |
-e 's/^(gdb) //' \ | |
-e '/^#/p' \ | |
-e 's/\(^Thread.*\)/@\1/p' | tr "@" "\n" > $OUTFILE | |
rm -f $TMPFILE | |
else | |
if test $# -lt 1; then | |
echo "Usage: ${tempname} <process-id> [gdb-mess-file]" 1>&2 | |
exit 1 | |
fi | |
if test $# -eq 2; then | |
messfile=$2 | |
fi | |
if test ! -r /proc/$1; then | |
echo "${tempname}: 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. | |
# The leading spaces are needed for have_eval_command's replacement. | |
backtrace="bt" | |
if test -d /proc/$1/task ; then | |
# Newer kernel; has a task/ directory. | |
if test `ls /proc/$1/task | 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 grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; then | |
backtrace="thread apply all bt" | |
fi | |
fi | |
GDB=${GDB:-gdb} | |
# Run GDB, strip out unwanted noise. | |
have_eval_command=`gdb --help 2>&1 |grep eval-command` | |
if ! test "x$have_eval_command" = "x"; then | |
$GDB --batch --eval-command="$backtrace" /proc/$1/exe $1 2>&1 | | |
/bin/sed -n \ | |
-e 's/^(gdb) //' \ | |
-e '/^#/p' \ | |
-e '/^ /p' \ | |
-e 's/\(^Thread.*\)/@\1/p' | tr '@' '\n' > $OUTFILE | |
else | |
$GDB -q -n /proc/$1/exe $1 <<EOF 2>&1 | | |
$backtrace | |
EOF | |
/bin/sed -n \ | |
-e 's/^(gdb) //' \ | |
-e '/^#/p' \ | |
-e '/^ /p' \ | |
-e 's/\(^Thread.*\)/@\1/p' | tr '@' '\n' > $OUTFILE | |
fi | |
fi | |
# Analyze the stack. | |
# The recommendations are based on the following assumptions: | |
# * More often than not, the crash is caused by user code. | |
# * The crash can be caused by the user's interpreted code, | |
# in which case sighandler() is called from CINT (G__...) | |
# * The crash can be called by the user's library code, | |
# in which case sighandler() is called from non-CINT and | |
# it's worth dumping the stack frames. | |
# * The user doesn't call CINT directly, so whenever we reach | |
# a stack frame with "G__" we can stop. | |
# * The crash is caused by only one thread, the one which | |
# invokes sighandler() | |
if ! test "x$OUTFILE" = "x/dev/stdout"; then | |
frames="" | |
wantthread="" | |
skip="" | |
ininterp="" | |
signal="" | |
while IFS= read line; do | |
case $line in | |
*'<signal handler called>'* ) | |
# this frame doesn't exist | |
skip="yes" | |
frames="" | |
continue | |
;; | |
Thread* ) | |
if test "x$wantthread" = "xyes"; then | |
break | |
fi | |
skip="" | |
frames="" | |
;; | |
'#'* ) | |
skip="" | |
;; | |
esac | |
if test "x${skip}" = "x"; then | |
tag=`echo $line|sed 's,^.*[0-9a-fA-F] in ,,'` | |
if test "x$ininterp" = "xcheck next"; then | |
ininterp="check this" | |
fi | |
case $tag in | |
SigHandler* ) | |
signal=`echo $line | sed 's/^.*\(kSig[^)]*\).*$/\1/'` | |
wantthread="yes" | |
ininterp="check next" | |
frames="" | |
skip="yes" | |
;; | |
sighandler* | TUnixSystem::DispatchSignals* ) | |
wantthread="yes" | |
ininterp="check next" | |
frames="" | |
skip="yes" | |
;; | |
G__* | Cint::G__* ) | |
if test "x$ininterp" = "xcheck this"; then | |
ininterp="yes" | |
break | |
elif test "x$ininterp" = "xno"; then | |
break | |
fi | |
skip="yes" | |
;; | |
*::ProcessLine* | TRint::HandleTermInput* ) | |
# frames to ignore (upper end) | |
if test "x$ininterp" = "xcheck this"; then | |
ininterp="no" | |
break | |
fi | |
if test "x$wantthread" = "xyes"; then | |
break; | |
fi | |
skip="yes" | |
;; | |
* ) | |
if test "x${skip}" = "x"; then | |
if test "x$frames" = "x"; then | |
frames="$line" | |
else | |
frames="$frames | |
$line" | |
fi | |
fi | |
if test "x$ininterp" = "xcheck this"; then | |
ininterp="no" | |
fi | |
;; | |
esac | |
fi | |
done < $OUTFILE | |
# Only print the informative text if we actually have a crash | |
# but not when TSystem::Stacktrace() was called. | |
if ! test "x$ininterp" = "x"; then | |
if ! test "x$signal" = "x"; then | |
signal=' ('${signal}')' | |
fi | |
echo "" | |
echo "" | |
echo "" | |
echo "===========================================================" | |
echo "There was a crash${signal}." | |
echo "This is the entire stack trace of all threads:" | |
echo "===========================================================" | |
fi | |
cat $OUTFILE | |
if ! test "x$ininterp" = "x"; then | |
echo "===========================================================" | |
if test "x$ininterp" = "xyes"; then | |
echo "" | |
echo "" | |
if test -f $messfile; then | |
cat $messfile | tr '%' '\n' | |
else | |
echo 'The crash is most likely caused by a problem in your script.' | |
echo 'Try to compile it (.L myscript.C+g) and fix any errors.' | |
echo 'If that does not help then please submit a bug report at' | |
echo 'http://root.cern.ch/bugs. Please post the ENTIRE stack trace' | |
echo 'from above as an attachment in addition to anything else' | |
echo 'that might help us fixing this issue.' | |
fi | |
elif ! test "x$frames" = "x"; then | |
echo "" | |
echo "" | |
if test -f $messfile; then | |
cat $messfile | tr '%' '\n' | |
else | |
echo 'The lines below might hint at the cause of the crash.' | |
echo 'If they do not help you then please submit a bug report at' | |
echo 'http://root.cern.ch/bugs. Please post the ENTIRE stack trace' | |
echo 'from above as an attachment in addition to anything else' | |
echo 'that might help us fixing this issue.' | |
fi | |
echo "===========================================================" | |
echo "$frames" | |
echo "===========================================================" | |
fi | |
echo "" | |
echo "" | |
fi | |
rm -f $OUTFILE $messfile | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment