diff --git a/callygraphs.sh b/callygraphs.sh new file mode 100644 index 0000000..80a745f --- /dev/null +++ b/callygraphs.sh @@ -0,0 +1,126 @@ +#! /usr/bin/env bash +# Reserved for future comments ... +# +# +# +# -------------------------------- + +prog_name="callygraphs" + +if [ $# -lt 1 ]; then + echo "Usage: $prog_name EXECUTABLE [ARGS...]" + echo + echo "Example: $prog_name ~/bin/test-program foo 23" + exit 1 +fi + +# Sanity checks. +FILE=$1 + +if [ ! -x $FILE ]; then + echo "$prog_name: Unable to find executable '$FILE'" + exit 1 +fi + +LANG="" gdb --eval-command=quit $FILE 2>&1 \ + | grep -E '(no\ debugging\ symbols\ found|not\ in\ executable\ format)' 2>&1 > /dev/null +if [ $? -eq 0 ]; then + echo -n "$prog_name: Can't print call graph for '$FILE' because it's not a " + echo "binary executable compiled with debugging symbols." + exit 1; +fi + +shift + +# Set up temporary files. +TRACE="`mktemp -t $prog_name.XXXXXXXXXX`" || exit +GETFUNCS="`mktemp -t $prog_name.XXXXXXXXXX`" || exit +trap 'rm -f -- "$TRACE" "$GETFUNCS"' EXIT +trap 'trap - EXIT; rm -f -- "$TRACE" "$GETFUNCS"; exit 1' HUP INT QUIT TERM + +# Take control of GDB and print call graph. +cat > $GETFUNCS </dev/null | awk ' +function get_func_name(str) +{ + split(str, part, "("); + len = split(part[1], part, " "); + len = split(part[len], part, "*"); + + return part[len]; +} + +BEGIN { + total = 0; + print "set width 0"; + print "set height 0"; + print "set verbose off"; +} + +/[a-zA-Z_][a-zA-Z0-9_]*\(/ { + fn = get_func_name($0); + printf("break %s\n", fn); + ++total; +} + +END { + for (i = 1; i <= total; i++) { + print "commands", i; + /* print "info args"; */ + print "backtrace 2"; + print "continue"; + print "end"; + } + + print "run" +} +' > $TRACE + +gdb --batch --command=$TRACE --tty=/dev/null --args $FILE $@ 2>/dev/null | awk ' +function get_callee(s) +{ + split(s, info, ","); + split(info[2], fn, " "); + callee = fn[1]; + + return callee; +} + +function get_params(s, n) +{ + split(s, par, n); + split(par[2], par, " at "); + sub(/ \(/, "(", par[1]); + + return par[1]; +} + +BEGIN { + isrecord = 0; + callee = ""; + caller = "*INITIAL*"; + params = ""; +} + +/^Breakpoint [0-9]+,/ { + isrecord = 1; + + callee = get_callee($0); + params = get_params($0, callee); +} + +/^#1[ \t]+/ { + if (isrecord) + caller = $4; +} + +/^$/ { + if (isrecord && (caller != "*INITIAL*")) { + printf("%s %s %s\n", caller, callee, params); + callee = caller = params = ""; + } +}