Skip to content

Instantly share code, notes, and snippets.

@tripleee
Last active July 21, 2022 01:43
Show Gist options
  • Save tripleee/0aea6984632c30956329 to your computer and use it in GitHub Desktop.
Save tripleee/0aea6984632c30956329 to your computer and use it in GitHub Desktop.
make a shell command show a call trace on failure
#!/bin/sh
:<<'=cut'
=head1 NAME
traceback - make a shell command show a call trace on failure
=head1 SYNOPSIS
ln -s /path/to/traceback bin/problematiccommand
=head1 DESCRIPTION
B<traceback> is an aid
for debugging an error message from a shell script which is
too uninformative and/or comes from an unknown source.
As long as you know which individual command has an error,
and as long as it properly sets the errorlevel on error,
you can obtain a traceback when the error happens to see
what was going on in the calling script's little mind.
=head1 USAGE EXAMPLES
sh$ mkdir $HOME/bin/traceback
sh$ PATH=$HOME/bin/traceback:$PATH
sh$ ln -s /path/to/traceback $HOME/bin/traceback/sed
sh$ hash -r sed
sh$ type sed
sed is hashed (/home/you/bin/traceback/sed)
sh$ sed 's%foo/bar/'
/bin/sed: -e expression #1, char 9: unterminated `s' command
traceback for process 1163:
* 1163 /bin/sh /home/you/bin/traceback/sed s%foo/bar/ (cwd: /tmp)
* 4245 /bin/bash (cwd: /home/you/project)
* 4239 SCREEN -e ^Ll (cwd: denied)
... or, perhaps more realistically
bash$ cd problematicproject
bash$ ./problematicscript
/usr/bin/expr: non-numeric argument
./problematicscript: catastrophic failure, aborting at line 93845
bash$ !? fsck
!?: event not found
bash$ mkdir bin
bash$ ln -s /usr/local/bin/traceback bin/expr
bash$ PATH=`pwd`/bin:$PATH ./problematicscript
/usr/bin/expr: non-numeric argument
traceback for process 3908:
3908 /bin/sh /home/you/work/problematicproject/bin/expr 1 + var (cwd: ...)
3905 /bin/sh /home/you/bin/calculatestuff /tmp/subscript.7Fg49Ab (cwd: ...)
3901 /bin/sh subscript fortytwo (cwd: /home/you/work/problematicproject)
3887 /bin/sh problematicscript
1122 -bash (cwd: /home/you/work/problematicproject)
./problematicscript: catastrophic failure, aborting at line 93845
=head1 PORTABILITY
This relies on specific L<ps(1)> options,
so is not portable.
Currently, this is Linux only
(or possibly even Debian only).
Also, requires L<lsof(1)>.
=head1 AUTHOR
tripleee
L<tripleee@users.noreply.github.com>
=head1 LICENSE
There is no explicit license at this point in time.
If it bothers you, get in touch and I'll add the necessary
copyright notices etc.
My intent is to distribute this under the
BSD license (without advertising clause, of course).
=head1 SEE ALSO
L<sh(1)>,
L<python(1)>;
L<ps(1)>,
L<lsof(1)>
=cut
cmd=$(basename "$0")
pth=$(type -a -p "$cmd" | fgrep -vx "$0" | head -n 1)
"$pth" "$@"
rc=$?
case $rc in
0) exit 0;;
esac
# ps options for Linux:
# -o field1= -o field2= ... -- output these fields, with no header line
# -p -- (needs to be last) show process with pid ...
psopts='-o ppid= -o pid= -o args= -p'
# terminating condition: stop looping when comm= matches one of these
toplvl='-bash | SCREEN | init'
p=$$
echo "traceback for process $p:" >&2
while true; do
set -- `ps $psopts $p`
p=$1
shift
######## FIXME: horrid
cwd=$(set -- $(lsof -d cwd -a -p $1 | tail -n 1); echo "$9")
echo '*' "$@" "(cwd: $cwd)" >&2
#lsof -p $1 | sed 's/^/** /'
case $1 in 0|1) break;; esac
eval case \$2 in $toplvl\) break\;\; esac
done
exit $rc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment