#!/bin/sh
# Launch debugger on a core file.
# Copyright (C) 1999-2019 FARGOS Development, LLC
# $Id$
# If the core file is not specified as an argument, which is typical usage,
# the most recent one as determined by the "last_core" utility is used.
# On some systems, such as MacOS and those making use of coredumpctl,
# the core files are maintained in separate directories and are potentially
# compressed.  The --extract option can be used to request retrieval of
# a core file.
# The debug_core script will first attempt to use the "size" command
# to retrieve the command line used when the core file was generated.
# If unsuccessful (as will be the case if the path names are too long),
# it will attempt to fallback on fexe, if available.
# The next fallback is to attempt to locate the executable in the PATH,
# which may fail if really long absolute path names were used and only
# a fragment of the program name is found in the core file.
# As a last resort, it attempts to locate a executable that begins with
# the retrieved path fragment.
# The debugger to be used can be specified by the DEFAULT_debugger
# environment variable, or SYSTEMD_DEBUGGER, which is used by coredumpctl.
originDir=`dirname ${0}`
PATH="${PATH}:${originDir}:${SB_TOOLS_BIN_DIR:+:}${SB_TOOLS_BIN_DIR}"
export PATH
osName=`uname -s`

getCompilerVerProg=`which getCompilerVersion 2>/dev/null`

if test -n "${getCompilerVerProg}"
then
	libcDir=`getCompilerVersion --showlib`
	LD_LIBRARY_PATH=`pathedit -p ${LD_LIBRARY_PATH} ${libcDir}`
	if test -n "${LD_LIBRARY_PATH}"
	then
		export LD_LIBRARY_PATH
	fi
fi
defaultDebugger="${DEFAULT_debugger:-${SYSTEMD_DEBUGGER}}"
case "${osName}" in
Darwin)
	debugger="${defaultDebugger:-lldb}"
	;;
OpenBSD)
	debugger="${defaultDebugger:-egdb --quiet}"
	;;
*)
	debugger="${defaultDebugger:-gdb --quiet}"
;;
esac

extractCoreFile=0
debugArgList=""
utilArgList=""
execArg="--exec"
coreArg="--core"
colorProg=`which colorText 2>/dev/null`
if test -n "${colorProg}"
then
	redColor="-red"
	greenColor="-green"
	blueColor="-blue"
	resetColor="-reset"
	foreground="-fg"
else
	colorProg="echo"
fi
while test $# -gt 0
do
        case $1 in
	-h | -help | --help)
		printf "usage: %s [--gdb | --gdbgui | --ddd | --lldb] [coreFile]\n" "${0}" >&2
		${colorProg} The default debugger can be specified by the ${greenColor} DEFAULT_debugger ${resetColor} environment variable >&2
		${colorProg} On this ${osName} system, it defaults to ${blueColor} ${debugger} ${resetColor} >&2
		gdbGUI=`which gdbgui 2>/dev/null`
		if test -z "${gdbGUI}"
		then
			${colorProg} "  " ${blueColor} gdbgui ${redColor} not installed. ${resetColor}  Try: ${foreground} ${greenColor} pip install gdbgui ${resetColor} >&2
			printf "The system packages redhat-rpm-config- and python2-devel are dependencies\n" >&2
			printf "that might need to be installed to permit the gdbgui to be built.\n" >&2
		fi
		dddGUI=`which ddd 2>/dev/null`
		if test -z "${dddGUI}"
		then
			${colorProg} "  " ${blueColor} ddd ${redColor} not installed. ${resetColor} >&2
		fi
		exit 1
		;;
	-extract | --extract)
		extractCoreFile=1
		;;
	-use | --use)
		debugger="${2}"
		shift
		;;
        -gdb | --gdb)
                debugger="gdb"
                ;;
        -gdbgui | --gdbgui) # Python web server front-end
                debugger="gdbgui"
		utilArgListStart='-r --gdb-args "'
		utilArgListEnd='"'
                ;;
        -ddd | --ddd) # DDD GUI frontend
                debugger="ddd"
		execArg=""
		coreArg=""
                ;;
	lldb) # use LLVM debugger
		debugger="lldb"
		;;
	-arg) # used to pass argument without leading "-"
		debugArgList="${debugArgList}${debugArgList:+ }${2}"
		shift
		;;
	-*) # pass argument on as-is
		debugArgList="${debugArgList}${debugArgList:+ }${1}"
		;;
        *)
                recentCore="${1}"
                ;;
        esac
        shift
done

if test -z "${recentCore}"
then
        recentCore=`last_core`
	if test -z "${recentCore}"
	then
		if test ${extractCoreFile} -eq 0
		then
			printf "%s: cannot locate a core file\n" "${0}" >&2
			coreDumpControl=`which coredumpctl 2>/dev/null`
			if test -n "${coreDumpControl}"
			then # has coredumpctl
				printf "    Try using --extract option to retrieve core file using coredumpctl\n" >&2
			fi
			exit 1
		fi
		recentCore=`last_core --extract`
		if test -z "${recentCore}"
		then
			printf "%s: cannot locate nor extract a saved core file\n" "${0}" >&2
			exit 1
		fi
	fi
fi
execFile=`size ${recentCore} | awk '/core file invoked as/ { p = "core file invoked as "; i = index($0, p); f = substr($0, i + length(p)); i = index(f, " "); f = substr(f, 1, i - 1); print f; }'`
if test -z "${execFile}"
then # could not get file name from size program (common on many systems)
	# try using Linux fexe utility.....if it has been installed
	fexe_prog=`which fexe 2>/dev/null`
	if test -n "${fexe_prog}"
	then
		execFile=`${fexe_prog} -noexe ${recentCore}`
	fi
else
	${colorProg} ${greenColor} 'Obtained program path as' ${execFile} ${resetColor}
fi
if test -z "${execFile}"
then
	# look for progName.core (e.g., OpenBSD)
	case "${recentCore}" in
	*.core)
		execFile=`basename ${recentCore} .core`
		;;
	esac
fi
if test -n "${execFile}"
then
	if test ! -r "${execFile}"
	then
		${colorProg} ${0}':' ${redColor} 'retrieved executable name does not exist:' ${resetColor} ${execFile}
		# get program name and search path
		progName=`basename ${execFile}`
		fullPath=`which ${progName} 2>/dev/null`
		if test -x "${fullPath}"
		then
			execFile="${fullPath}"
			${colorProg} ${greenColor} 'Inferred program path as' ${execFile} ${resetColor}
		else
			tryLookingInDir="`dirname ${execFile}`/"
			${colorProg} ${greenColor} 'Inferred directory:' ${tryLookingInDir} ${resetColor}
			execFile=""
		fi
	fi
fi
if test -z "${execFile}"
then # fall back to using file utility...
	# This suffers from having a limit on the length of the file name
        progName=`file ${recentCore} | awk '{ print substr($NF, 2, length($NF)-2); }'`
        execFile=`which ${tryLookingInDir}${progName} 2>/dev/null`
	if test -z "${execFile}" -a -n "${tryLookingInDir}"
	then # no exact match...but maybe the real file name is longer
		# try generating a filename with the fragment retrieved
		# as its prefix
		newExecFile=`echo ${tryLookingInDir}${progName}* 2>/dev/null`
		if test -r ${newExecFile}
		then # we came up with something
			${colorProg} ${greenColor} 'File name' ${execFile} 'appears to be truncated, assuming' ${newExecFile} ${resetColor}
			execFile="${newExecFile}"
		fi
	fi
fi
if test -z "${execFile}"
then
	# ultimate fallback, try crazy heuristic of using strings on core file,
	# pull out first word of second line after first 2 CORE lines.
	progName=`strings ${recentCore} | awk 'BEGIN { c1=0; c2=0 } /CORE/ { c1+=1; next } { if (c1 == 2) { c2 += 1; if (c2 == 2) { print $1; exit } } }'`
        execFile=`which ${progName} 2>/dev/null`
fi
if test -z "${execFile}"
then
        ${colorProg} ${redColor} ${0}': cannot locate executable for core file' ${recentCore} ${resetColor} >&2
        exit 1
fi

${colorProg} ${blueColor} 'Invoking' ${greenColor} ${debugger} ${utilArgListStart}${execArg} ${execFile} ${coreArg} ${recentCore}${utilArgListEnd} ${debugArgList} ${resetColor}
eval ${debugger} ${utilArgListStart}${execArg} ${execFile} ${coreArg} ${recentCore}${utilArgListEnd} ${debugArgList}
