mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +00:00 
			
		
		
		
	In order to get a dump of the running process, we need to find the pid of the main asterisk process. This can be tricky if there are also instances of "asterisk -r" running or if an alternate location for asterisk.conf was specified on the command line with the -C option that also specified an alternation location for the pid file. So now... 1. We find the asterisk executable with "which" or the --asterisk-bin command line option. 2. If there's only 1 process with an executable path that matches, we use that pid. If not... 3. We try "<asterisk-bin> -rx 'core show settings'" and parse the output to find the pidfile, then read that for the pid. If that didn't work... 4. We get a list of all the pids matching <asterisk-bin> and look in /proc/<pid>/cmdline for a -C argument and retry the "core show settings" using the same -C option. We can't parse the output of "ps" to get the -C path because it may contain spaces. The contents of /proc/<pid>/cmdline are delimited by NULLs. For BSDs we may have to mount /proc first. :( ASTERISK-28221 Reported by: Andrew Nagy Change-Id: I8aa1f3f912f949df2b5348908803c636bde1d57c
		
			
				
	
	
		
			704 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			704 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env bash
 | 
						|
# Turn on extended globbing
 | 
						|
shopt -s extglob
 | 
						|
# Bail on any error
 | 
						|
set -e
 | 
						|
 | 
						|
prog=$(basename $0)
 | 
						|
 | 
						|
print_help() {
 | 
						|
cat <<EOF
 | 
						|
NAME
 | 
						|
	$prog - Dump and/or format asterisk coredump files
 | 
						|
 | 
						|
SYNOPSIS
 | 
						|
	$prog [ --help ] [ --running | --RUNNING ] [ --latest ]
 | 
						|
		[ --tarball-coredumps ] [ --delete-coredumps-after ]
 | 
						|
		[ --tarball-results ] [ --delete-results-after ]
 | 
						|
		[ --tarball-config ] [ --tarball-uniqueid="<uniqueid>" ]
 | 
						|
		[ --no-default-search ] [ --append-coredumps ]
 | 
						|
		[ --asterisk-bin="path" ]
 | 
						|
		[ <coredump> | <pattern> ... ]
 | 
						|
 | 
						|
DESCRIPTION
 | 
						|
 | 
						|
	Extracts backtraces and lock tables from Asterisk coredump files.
 | 
						|
	For each coredump found, 4 new result files are created:
 | 
						|
	- <coredump>.brief.txt: The output of "thread apply all bt".
 | 
						|
 | 
						|
	- <coredump>.thread1.txt: The output of "thread apply 1 bt full".
 | 
						|
 | 
						|
	- <coredump>.full.txt: The output of "thread apply all bt full".
 | 
						|
 | 
						|
	- <coredump>.locks.txt: If asterisk was compiled with
 | 
						|
		"DEBUG_THREADS", this file will contain a dump of the locks
 | 
						|
		table similar to doing a "core show locks" from the asterisk
 | 
						|
		CLI.
 | 
						|
 | 
						|
	Optional features:
 | 
						|
	- The running asterisk process can be suspended and dumped.
 | 
						|
	- The coredumps can be merged into a tarball.
 | 
						|
	- The coredumps can be deleted after processing.
 | 
						|
	- The results files can be merged into a tarball.
 | 
						|
	- The results files can be deleted after processing.
 | 
						|
 | 
						|
	Options:
 | 
						|
 | 
						|
	--help
 | 
						|
		Print this help.
 | 
						|
 | 
						|
	--running
 | 
						|
		Create a coredump from the running asterisk instance and
 | 
						|
		process it along with any other coredumps found (if any).
 | 
						|
		WARNING: This WILL interrupt call processing.  You will be
 | 
						|
		asked to confirm.  The coredump will be written to /tmp if
 | 
						|
		$OUTPUTDIR is not defined.
 | 
						|
 | 
						|
	--RUNNING
 | 
						|
		Same as --running but without the confirmation prompt.
 | 
						|
		DANGEROUS!!
 | 
						|
 | 
						|
	--latest
 | 
						|
		Process only the latest coredump from those specified (based
 | 
						|
		on last-modified time).  If a dump of the running process was
 | 
						|
		requested, it is always included in addition to the latest
 | 
						|
		from the existing coredumps.
 | 
						|
 | 
						|
	--tarball-coredumps
 | 
						|
		Creates a gzipped tarball of coredumps processed, their
 | 
						|
		results txt files and copies of /etc/os-release,
 | 
						|
		/usr/sbin/asterisk, /usr/lib(64)/libasterisk* and
 | 
						|
		/usr/lib(64)/asterisk as those files are needed to properly
 | 
						|
		examine the coredump.  The file will be named
 | 
						|
		$OUTPUTDIR/asterisk.<timestamp>.coredumps.tar.gz or
 | 
						|
		$OUTPUTDIR/asterisk-<uniqueid>.coredumps.tar.gz if
 | 
						|
		--tarball-uniqueid was specified.
 | 
						|
		WARNING:  This file could 1gb in size!
 | 
						|
		Mutually exclusive with --tartball-results
 | 
						|
 | 
						|
	--delete-coredumps-after
 | 
						|
		Deletes all processed coredumps regardless of whether
 | 
						|
		a tarball was created.
 | 
						|
 | 
						|
	--tarball-results
 | 
						|
		Creates a gzipped tarball of all result files produced.
 | 
						|
		The tarball name will be:
 | 
						|
		$OUTPUTDIR/asterisk.<timestamp>.results.tar.gz
 | 
						|
		Mutually exclusive with --tartball-coredumps
 | 
						|
 | 
						|
	--delete-results-after
 | 
						|
		Deletes all processed results regardless of whether
 | 
						|
		a tarball was created.  It probably doesn't make sense
 | 
						|
		to use this option unless you have also specified
 | 
						|
		--tarball-results.
 | 
						|
 | 
						|
	--tarball-config
 | 
						|
		Adds the contents of /etc/asterisk to the tarball created
 | 
						|
		with --tarball-coredumps or --tarball-results.
 | 
						|
 | 
						|
	--tarball-uniqueid="<uniqueid>"
 | 
						|
		Normally DATEFORMAT is used to make the tarballs unique
 | 
						|
		but you can use your own unique id in the tarball names
 | 
						|
		such as the Jira issue id.
 | 
						|
 | 
						|
	--no-default-search
 | 
						|
		Ignore COREDUMPS from the config files and process only
 | 
						|
		coredumps listed on the command line (if any) and/or
 | 
						|
		the running asterisk instance (if requested).
 | 
						|
 | 
						|
	--append-coredumps
 | 
						|
		Append any coredumps specified on the command line to the
 | 
						|
		config file specified ones instead of overriding them.
 | 
						|
 | 
						|
	--asterisk-binary
 | 
						|
		Path to the asterisk binary. Default: look for asterisk
 | 
						|
		in the PATH.
 | 
						|
 | 
						|
	<coredump> | <pattern>
 | 
						|
		A list of coredumps or coredump search patterns.  Unless
 | 
						|
		--append-coredumps was specified, these entries will override
 | 
						|
		those specified in the config files.
 | 
						|
 | 
						|
		Any resulting file that isn't actually a coredump is silently
 | 
						|
		ignored.  If your patterns contains spaces be sure to only
 | 
						|
		quote the portion of the pattern that DOESN'T contain wildcard
 | 
						|
		expressions.  If you quote the whole pattern, it won't be
 | 
						|
		expanded.
 | 
						|
 | 
						|
		If --no-default-search is specified and no files are specified
 | 
						|
		on the command line, then the only the running asterisk process
 | 
						|
		will be dumped (if requested).  Otherwise if no files are
 | 
						|
		specified on the command line the value of COREDUMPS from
 | 
						|
		ast_debug_tools.conf will be used.  Failing that, the following
 | 
						|
		patterns will be used:
 | 
						|
		/tmp/core[-._]asterisk!(*.txt)
 | 
						|
		/tmp/core[-._]\$(hostname)!(*.txt)
 | 
						|
 | 
						|
NOTES
 | 
						|
	You must be root to use $prog.
 | 
						|
 | 
						|
	$OUTPUTDIR can be read from the current environment or from the
 | 
						|
	ast_debug_tools.conf file described below.  If not specified,
 | 
						|
	work products are placed in the same directory as the core file.
 | 
						|
 | 
						|
	The script relies on not only bash, but also recent GNU date and
 | 
						|
	gdb with python support.  *BSD operating systems may require
 | 
						|
	installation of the 'coreutils' and 'devel/gdb' packagess and minor
 | 
						|
	tweaking of the ast_debug_tools.conf file.
 | 
						|
 | 
						|
	Any files output will have ':' characters changed to '-'.  This is
 | 
						|
	to facilitate uploading those files to Jira which doesn't like the
 | 
						|
	colons.
 | 
						|
 | 
						|
FILES
 | 
						|
	/etc/asterisk/ast_debug_tools.conf
 | 
						|
	~/ast_debug_tools.conf
 | 
						|
	./ast_debug_tools.conf
 | 
						|
 | 
						|
	#
 | 
						|
	# This file is used by the Asterisk debug tools.
 | 
						|
	# Unlike other Asterisk config files, this one is
 | 
						|
	# "sourced" by bash and must adhere to bash semantics.
 | 
						|
	#
 | 
						|
 | 
						|
	# A list of coredumps and/or coredump search patterns.
 | 
						|
	# Bash extended globs are enabled and any resulting files
 | 
						|
	# that aren't actually coredumps are silently ignored
 | 
						|
	# so you can be liberal with the globs.
 | 
						|
	#
 | 
						|
	# If your patterns contains spaces be sure to only quote
 | 
						|
	# the portion of the pattern that DOESN'T contain wildcard
 | 
						|
	# expressions.  If you quote the whole pattern, it won't
 | 
						|
	# be expanded and the glob characters will be treated as
 | 
						|
	# literals.
 | 
						|
	#
 | 
						|
	# The exclusion of files ending ".txt" is just for
 | 
						|
	# demonstration purposes as non-coredumps will be ignored
 | 
						|
	# anyway.
 | 
						|
	COREDUMPS=(/tmp/core[-._]asterisk!(*.txt) /tmp/core[-._]\$(hostname)!(*.txt))
 | 
						|
 | 
						|
	# The directory to contain output files and work directories.
 | 
						|
	# For output from existing core files, the default is the
 | 
						|
	# directory that the core file is found in.  For core files
 | 
						|
	# produced from a running process, the default is /tmp.
 | 
						|
	OUTPUTDIR=/some/directory
 | 
						|
 | 
						|
	# Date command for the "running" coredump and tarballs.
 | 
						|
	# DATEFORMAT will be executed to get the timestamp.
 | 
						|
	# Don't put quotes around the format string or they'll be
 | 
						|
	# treated as literal characters.  Also be aware of colons
 | 
						|
	# in the output as you can't upload files with colons in
 | 
						|
	# the name to Jira.
 | 
						|
	#
 | 
						|
	# Unix timestamp
 | 
						|
	#DATEFORMAT='date +%s.%N'
 | 
						|
	#
 | 
						|
	# *BSD/MacOS doesn't support %N but after installing GNU
 | 
						|
	# coreutils...
 | 
						|
	#DATEFORMAT='gdate +%s.%N'
 | 
						|
	#
 | 
						|
	# Readable GMT
 | 
						|
	#DATEFORMAT='date -u +%FT%H-%M-%S%z'
 | 
						|
	#
 | 
						|
	# Readable Local time
 | 
						|
	DATEFORMAT='date +%FT%H-%M-%S%z'
 | 
						|
 | 
						|
EOF
 | 
						|
	exit 1
 | 
						|
}
 | 
						|
 | 
						|
if [ $EUID -ne 0 ] ; then
 | 
						|
	echo "You must be root to use $prog."
 | 
						|
	exit 1
 | 
						|
fi
 | 
						|
 | 
						|
running=false
 | 
						|
RUNNING=false
 | 
						|
latest=false
 | 
						|
tarball_coredumps=false
 | 
						|
tarball_config=false
 | 
						|
delete_coredumps_after=false
 | 
						|
tarball_results=false
 | 
						|
delete_results_after=false
 | 
						|
append_coredumps=false
 | 
						|
 | 
						|
declare -a COREDUMPS
 | 
						|
declare -a ARGS_COREDUMPS
 | 
						|
 | 
						|
# readconf reads a bash-sourceable file and sets variables
 | 
						|
# that havn't already been set.  This allows variables set
 | 
						|
# on the command line or that are already in the environment
 | 
						|
# to take precedence over those read from the file.
 | 
						|
#
 | 
						|
# Setting the values can't be done in a subshell so you can't
 | 
						|
# just pipe the output of sed into the while.
 | 
						|
 | 
						|
readconf() {
 | 
						|
	while read line ; do
 | 
						|
		v=${line%%=*}
 | 
						|
		[ -z "${!v}" ] && eval $line || :
 | 
						|
	done <<EOF
 | 
						|
$( sed -r -e "/\s*#/d" -e "/^\s*$/d" $1 )
 | 
						|
EOF
 | 
						|
}
 | 
						|
 | 
						|
# Read config files from most important to least important.
 | 
						|
# Variable set on the command line or environment always take precedence.
 | 
						|
[ -f ./ast_debug_tools.conf ] && readconf ./ast_debug_tools.conf
 | 
						|
[ -f ~/ast_debug_tools.conf ] && readconf ~/ast_debug_tools.conf
 | 
						|
[ -f /etc/asterisk/ast_debug_tools.conf ] && readconf /etc/asterisk/ast_debug_tools.conf
 | 
						|
 | 
						|
# For *BSD, the preferred gdb may be in /usr/local/bin so we
 | 
						|
# need to search for one that supports python.
 | 
						|
for g in $(which -a gdb) ; do
 | 
						|
	result=$($g --batch --ex "python print('hello')" 2>/dev/null || : )
 | 
						|
	if [[ "$result" =~ ^hello$ ]] ; then
 | 
						|
		GDB=$g
 | 
						|
		break
 | 
						|
	fi
 | 
						|
done
 | 
						|
 | 
						|
if [ -z "$GDB" ] ; then
 | 
						|
	echo "No suitable gdb was found in $PATH"
 | 
						|
	exit 1
 | 
						|
fi
 | 
						|
 | 
						|
if [ -n "$OUTPUTDIR" ] ; then
 | 
						|
	if [ ! -d "$OUTPUTDIR" ] ; then
 | 
						|
		echo "OUTPUTDIR $OUTPUTDIR doesn't exists or is not a directory"
 | 
						|
		exit 1
 | 
						|
	fi
 | 
						|
fi
 | 
						|
 | 
						|
if [ ${#COREDUMPS[@]} -eq 0 ] ; then
 | 
						|
	COREDUMPS+=(/tmp/core[-._]asterisk!(*.txt) /tmp/core[-._]$(hostname)!(*.txt))
 | 
						|
fi
 | 
						|
 | 
						|
DATEFORMAT=${DATEFORMAT:-'date +%FT%H-%M-%S%z'}
 | 
						|
 | 
						|
# Use "$@" (with the quotes) so spaces in patterns or
 | 
						|
# file names are preserved.
 | 
						|
# Later on when we have to iterate over COREDUMPS, we always
 | 
						|
# use the indexes rather than trying to expand the values of COREDUMPS
 | 
						|
# just in case.
 | 
						|
 | 
						|
for a in "$@" ; do
 | 
						|
	case "$a" in
 | 
						|
	--running)
 | 
						|
		running=true
 | 
						|
		;;
 | 
						|
	--RUNNING)
 | 
						|
		RUNNING=true
 | 
						|
		;;
 | 
						|
	--no-default-search)
 | 
						|
		# Clean out COREDUMPS from config files
 | 
						|
		COREDUMPS=()
 | 
						|
		;;
 | 
						|
	--latest)
 | 
						|
		latest=true
 | 
						|
		;;
 | 
						|
	--tarball-coredumps)
 | 
						|
		tarball_coredumps=true
 | 
						|
		;;
 | 
						|
	--tarball-config)
 | 
						|
		tarball_config=true
 | 
						|
		;;
 | 
						|
	--delete-coredumps-after)
 | 
						|
		delete_coredumps_after=true
 | 
						|
		;;
 | 
						|
	--tarball-results)
 | 
						|
		tarball_results=true
 | 
						|
		;;
 | 
						|
	--delete-results-after)
 | 
						|
		delete_results_after=true
 | 
						|
		;;
 | 
						|
	--append-coredumps)
 | 
						|
		append_coredumps=true
 | 
						|
		;;
 | 
						|
	--tarball-uniqueid=*)
 | 
						|
		tarball_uniqueid=${a#*=}
 | 
						|
		;;
 | 
						|
	--asterisk-bin=*)
 | 
						|
		asterisk_bin=${a#*=}
 | 
						|
		;;
 | 
						|
	--help|-*)
 | 
						|
		print_help
 | 
						|
		;;
 | 
						|
	*)
 | 
						|
		ARGS_COREDUMPS+=("$a")
 | 
						|
		# If any files are specified on the command line, ignore those
 | 
						|
		# specified in the config files unless append-coredumps was specified.
 | 
						|
		if ! $append_coredumps ; then
 | 
						|
			COREDUMPS=()
 | 
						|
		fi
 | 
						|
	esac
 | 
						|
done
 | 
						|
 | 
						|
# append coredumps/patterns specified as command line arguments to COREDUMPS.
 | 
						|
for i in ${!ARGS_COREDUMPS[@]} ; do
 | 
						|
	COREDUMPS+=("${ARGS_COREDUMPS[$i]}")
 | 
						|
done
 | 
						|
 | 
						|
# At this point, all glob entries that match files should be expanded.
 | 
						|
# Any entries that don't exist are probably globs that didn't match anything
 | 
						|
# and need to be pruned.  Any non coredumps are also pruned.
 | 
						|
 | 
						|
for i in ${!COREDUMPS[@]} ; do
 | 
						|
	if [ ! -f "${COREDUMPS[$i]}" ] ; then
 | 
						|
		unset COREDUMPS[$i]
 | 
						|
		continue
 | 
						|
	fi
 | 
						|
	# Some versions of 'file' don't allow only the first n bytes of the
 | 
						|
	# file to be processed so we use dd to grab just the first 32 bytes.
 | 
						|
	mimetype=$(dd if="${COREDUMPS[$i]}" bs=32 count=1 2>/dev/null | file -bi -)
 | 
						|
	if [[ ! "$mimetype" =~ coredump ]] ; then
 | 
						|
		unset COREDUMPS[$i]
 | 
						|
		continue
 | 
						|
	fi
 | 
						|
done
 | 
						|
 | 
						|
# Sort and weed out any dups
 | 
						|
IFS=$'\x0a'
 | 
						|
readarray -t COREDUMPS < <(echo -n "${COREDUMPS[*]}" | sort -u )
 | 
						|
unset IFS
 | 
						|
 | 
						|
# If --latest, get the last modified timestamp of each file,
 | 
						|
# sort them, then return the latest.
 | 
						|
if [ ${#COREDUMPS[@]} -gt 0 ] && $latest ; then
 | 
						|
	lf=$(find "${COREDUMPS[@]}" -printf '%T@ %p\n' | sort -n | tail -1)
 | 
						|
	COREDUMPS=("${lf#* }")
 | 
						|
fi
 | 
						|
 | 
						|
# Timestamp to use for output files
 | 
						|
df=${tarball_uniqueid:-$(${DATEFORMAT})}
 | 
						|
 | 
						|
if [ x"$asterisk_bin" = x ]; then
 | 
						|
	asterisk_bin=$(which asterisk)
 | 
						|
fi
 | 
						|
 | 
						|
if $running || $RUNNING ; then
 | 
						|
	# We need to go through some gyrations to find the pid of the running
 | 
						|
	# MAIN asterisk process and not someone or something running asterisk -r.
 | 
						|
 | 
						|
	unset pid
 | 
						|
 | 
						|
	# Simplest case first...
 | 
						|
	pids=$(pgrep -f "$asterisk_bin")
 | 
						|
	pidcount=$(echo $pids | wc -w)
 | 
						|
 | 
						|
	if [ $pidcount -eq 0 ] ; then
 | 
						|
		>&2 echo "Asterisk is not running"
 | 
						|
		exit 1
 | 
						|
	fi
 | 
						|
 | 
						|
	# Single process, great.
 | 
						|
	if [ $pidcount -eq 1 ] ; then
 | 
						|
		pid=$pids
 | 
						|
		echo "Found a single asterisk instance running as process $pid"
 | 
						|
	fi
 | 
						|
 | 
						|
	# More than 1 asterisk process running
 | 
						|
	if [ x"$pid" = x ] ; then
 | 
						|
		# More than 1 process running, let's try asking asterisk for it's
 | 
						|
		# pidfile
 | 
						|
		pidfile=$("$asterisk_bin" -rx "core show settings" 2>/dev/null | sed -n -r -e "s/^\s*pid file:\s+(.*)/\1/gpi")
 | 
						|
		# We found it
 | 
						|
		if [ x"$pidfile" != x -a -f "$pidfile" ] ; then
 | 
						|
			pid=$(cat "$pidfile")
 | 
						|
			echo "Found pidfile $pidfile with process $pid"
 | 
						|
		fi
 | 
						|
	fi
 | 
						|
 | 
						|
	# It's possible that asterisk was started with the -C option which means the
 | 
						|
	# control socket and pidfile might not be where we expect.  We're going to
 | 
						|
	# have to parse the process arguments to see if -C was specified.
 | 
						|
	# The first process that has a -C argument determines which config
 | 
						|
	# file to use to find the pidfile of the main process.
 | 
						|
	# NOTE: The ps command doesn't quote command line arguments that it
 | 
						|
	# displays so we need to look in /proc/<pid>/cmdline.
 | 
						|
 | 
						|
	if [ x"$pid" = x ] ; then
 | 
						|
		# BSDs might not mount /proc by default :(
 | 
						|
		mounted_proc=0
 | 
						|
		if uname -o | grep -qi "bsd" ; then
 | 
						|
			if ! mount | grep -qi "/proc" ; then
 | 
						|
				echo "Temporarily mounting /proc"
 | 
						|
				mounted_proc=1
 | 
						|
				mount -t procfs proc /proc
 | 
						|
			fi
 | 
						|
		fi
 | 
						|
 | 
						|
		for p in $pids ; do
 | 
						|
			# Fields in cmdline are delimited by NULLs
 | 
						|
			astetcconf=$(sed -n -r -e "s/.*\x00-C\x00([^\x00]+).*/\1/gp" /proc/$p/cmdline)
 | 
						|
			if [ x"$astetcconf" != x ] ; then
 | 
						|
				pidfile=$("$asterisk_bin" -C "$astetcconf" -rx "core show settings" 2>/dev/null | sed -n -r -e "s/^\s*pid file:\s+(.*)/\1/gpi")
 | 
						|
				if [ x"$pidfile" != x -a -f "$pidfile" ] ; then
 | 
						|
					pid=$(cat "$pidfile")
 | 
						|
					echo "Found pidfile $pidfile the hard way with process $pid"
 | 
						|
					break
 | 
						|
				fi
 | 
						|
			fi
 | 
						|
		done
 | 
						|
		if [ $mounted_proc -eq 1 ] ; then
 | 
						|
			echo "Unmounting /proc"
 | 
						|
			umount /proc
 | 
						|
		fi
 | 
						|
	fi
 | 
						|
 | 
						|
	if [ x"$pid" = x ] ; then
 | 
						|
		>&2 echo "Can't determine pid of the running asterisk instance"
 | 
						|
		exit 1
 | 
						|
	fi
 | 
						|
 | 
						|
	if $RUNNING ; then
 | 
						|
		answer=Y
 | 
						|
	else
 | 
						|
		read -p "WARNING:  Taking a core dump of the running asterisk instance will suspend call processing while the dump is saved.  Do you wish to continue? (y/N) " answer
 | 
						|
	fi
 | 
						|
	if [[ "$answer" =~ ^[Yy] ]] ; then
 | 
						|
		cf="${OUTPUTDIR:-/tmp}/core-asterisk-running-$df"
 | 
						|
		echo "Dumping running asterisk process to $cf"
 | 
						|
		${GDB} ${asterisk_bin} -p $pid -q --batch --ex "gcore $cf" >/dev/null 2>&1
 | 
						|
		COREDUMPS+=("$cf")
 | 
						|
	else
 | 
						|
		echo "Skipping dump of running process"
 | 
						|
	fi
 | 
						|
fi
 | 
						|
 | 
						|
if [ "${#COREDUMPS[@]}" -eq 0 ] ; then
 | 
						|
	echo "No coredumps found"
 | 
						|
	print_help
 | 
						|
fi
 | 
						|
 | 
						|
# Extract the gdb scripts from the end of this script
 | 
						|
# and save them to /tmp/.gdbinit
 | 
						|
 | 
						|
gdbinit=${OUTPUTDIR:-/tmp}/.ast_coredumper.gdbinit
 | 
						|
 | 
						|
trap "rm $gdbinit" EXIT
 | 
						|
 | 
						|
ss=`egrep -n "^#@@@SCRIPTSTART@@@" $0 |cut -f1 -d:`
 | 
						|
tail -n +${ss} $0 >$gdbinit
 | 
						|
 | 
						|
# Now iterate over the coredumps and dump the debugging info
 | 
						|
for i in ${!COREDUMPS[@]} ; do
 | 
						|
	cf=${COREDUMPS[$i]}
 | 
						|
	echo "Processing $cf"
 | 
						|
 | 
						|
	cfdir=`dirname ${cf}`
 | 
						|
	cfname=`basename ${cf}`
 | 
						|
	outputdir=${OUTPUTDIR:-${cfdir}}
 | 
						|
 | 
						|
	${GDB} -n --batch -q --ex "source $gdbinit" "$asterisk_bin" "$cf" 2>/dev/null | (
 | 
						|
		of=/dev/null
 | 
						|
		while IFS= read line ; do
 | 
						|
			if [[ "$line" =~ !@!@!@!\ ([^\ ]+)\ !@!@!@! ]] ; then
 | 
						|
				of=${outputdir}/${cfname}-${BASH_REMATCH[1]}
 | 
						|
				of=${of//:/-}
 | 
						|
				rm -f "$of"
 | 
						|
				echo "Creating $of"
 | 
						|
			fi
 | 
						|
			echo -e $"$line" >> "$of"
 | 
						|
		done
 | 
						|
	)
 | 
						|
 | 
						|
	if $tarball_coredumps ; then
 | 
						|
		cfname=${cfname//:/-}
 | 
						|
		tf=${outputdir}/${cfname}.tar.gz
 | 
						|
		echo "Creating ${tf}"
 | 
						|
 | 
						|
		dest=${outputdir}/${cfname}.output
 | 
						|
		rm -rf ${dest} 2>/dev/null || :
 | 
						|
 | 
						|
		libdir=usr/lib
 | 
						|
		[ -d /usr/lib64 ] && libdir+=64
 | 
						|
		mkdir -p ${dest}/tmp ${dest}/${libdir}/asterisk ${dest}/etc ${dest}/usr/sbin
 | 
						|
 | 
						|
		ln -s ${cf} ${dest}/tmp/${cfname}
 | 
						|
		cp ${outputdir}/${cfname}*.txt ${dest}/tmp/
 | 
						|
		[ -f /etc/os-release ] && cp /etc/os-release ${dest}/etc/
 | 
						|
		if $tarball_config ; then
 | 
						|
			cp -a /etc/asterisk ${dest}/etc/
 | 
						|
		fi
 | 
						|
		cp -a /${libdir}/libasterisk* ${dest}/${libdir}/
 | 
						|
		cp -a /${libdir}/asterisk/* ${dest}/${libdir}/asterisk/
 | 
						|
		cp -a /usr/sbin/asterisk ${dest}/usr/sbin
 | 
						|
		rm -rf ${tf}
 | 
						|
		tar -chzf ${tf} --transform="s/^[.]/${cfname}/" -C ${dest} .
 | 
						|
		sleep 3
 | 
						|
		rm -rf ${dest}
 | 
						|
		echo "Created $tf"
 | 
						|
	elif $tarball_results ; then
 | 
						|
		cfname=${cfname//:/-}
 | 
						|
		tf=${outputdir}/${cfname}.tar.gz
 | 
						|
		echo "Creating ${tf}"
 | 
						|
 | 
						|
		dest=${outputdir}/${cfname}.output
 | 
						|
		rm -rf ${dest} 2>/dev/null || :
 | 
						|
		mkdir -p ${dest}
 | 
						|
		cp ${outputdir}/${cfname}*.txt ${dest}/
 | 
						|
		if $tarball_config ; then
 | 
						|
			mkdir -p ${dest}/etc
 | 
						|
			cp -a /etc/asterisk ${dest}/etc/
 | 
						|
		fi
 | 
						|
		tar -chzf ${tf} --transform="s/^[.]/${cfname}/" -C ${dest} .
 | 
						|
		rm -rf ${dest}
 | 
						|
		echo "Created $tf"
 | 
						|
	fi
 | 
						|
 | 
						|
if $delete_coredumps_after ; then
 | 
						|
		rm -rf "${cf}"
 | 
						|
	fi
 | 
						|
 | 
						|
	if $delete_results_after ; then
 | 
						|
		rm -rf "${cf//:/-}"-{brief,full,thread1,locks}.txt
 | 
						|
	fi
 | 
						|
done
 | 
						|
 | 
						|
exit
 | 
						|
 | 
						|
# Be careful editng the inline scripts.
 | 
						|
# They're space-indented.
 | 
						|
 | 
						|
# We need the python bit because lock_infos isn't
 | 
						|
# a valid symbol in asterisk unless DEBUG_THREADS was
 | 
						|
# used during the compile.  Also, interrupt and continue
 | 
						|
# are only valid for a running program.
 | 
						|
 | 
						|
#@@@SCRIPTSTART@@@
 | 
						|
python
 | 
						|
class DumpAsteriskCommand(gdb.Command):
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        super(DumpAsteriskCommand, self).__init__ ("dump-asterisk",
 | 
						|
            gdb.COMMAND_OBSCURE, gdb.COMPLETE_COMMAND)
 | 
						|
 | 
						|
    def invoke(self, arg, from_tty):
 | 
						|
        try:
 | 
						|
            gdb.execute("interrupt", from_tty)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        print("!@!@!@! thread1.txt !@!@!@!\n")
 | 
						|
        try:
 | 
						|
            gdb.execute("p $_siginfo", from_tty)
 | 
						|
            gdb.execute("info signal $_siginfo.si_signo")
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        try:
 | 
						|
            gdb.execute("thread apply 1 bt full", from_tty)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        print("!@!@!@! brief.txt !@!@!@!\n")
 | 
						|
        try:
 | 
						|
            gdb.execute("p $_siginfo", from_tty)
 | 
						|
            gdb.execute("info signal $_siginfo.si_signo")
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        try:
 | 
						|
            gdb.execute("thread apply all bt", from_tty)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        print("!@!@!@! full.txt !@!@!@!\n")
 | 
						|
        try:
 | 
						|
            gdb.execute("p $_siginfo", from_tty)
 | 
						|
            gdb.execute("info signal $_siginfo.si_signo")
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        try:
 | 
						|
            gdb.execute("thread apply all bt full", from_tty)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        print("!@!@!@! locks.txt !@!@!@!\n")
 | 
						|
        try:
 | 
						|
            gdb.execute("p $_siginfo", from_tty)
 | 
						|
            gdb.execute("info signal $_siginfo.si_signo")
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        try:
 | 
						|
            gdb.execute("show_locks", from_tty)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
        try:
 | 
						|
            gdb.execute("continue", from_tty)
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
 | 
						|
DumpAsteriskCommand ()
 | 
						|
end
 | 
						|
 | 
						|
define show_locks
 | 
						|
   set $n = lock_infos.first
 | 
						|
 | 
						|
   if $argc == 0
 | 
						|
      printf "                                                                                                                    where_held count-|\n"
 | 
						|
      printf "                                                                                                                         suspended-| |\n"
 | 
						|
      printf "                                                                                                        type- |     times locked-| | |\n"
 | 
						|
      printf "thread         status   file                   line function                             lock name            | lock addr        | | |\n"
 | 
						|
   else
 | 
						|
      printf "thread,status,file,line,function,lock_name,lock_type,lock_addr,times_locked,suspended,where_held_count,where_held_file,where_held_line,where_held_function,there_held_thread\n"
 | 
						|
   end
 | 
						|
 | 
						|
   while $n
 | 
						|
      if $n->num_locks > 0
 | 
						|
      set $i = 0
 | 
						|
      while $i < $n->num_locks
 | 
						|
         if $n->locks[$i]->suspended == 0
 | 
						|
            if ((ast_mutex_t *)$n->locks[$i]->lock_addr)->tracking
 | 
						|
               if $n->locks[$i]->type > 0
 | 
						|
                  set $track = ((ast_rwlock_t *)$n->locks[$i]->lock_addr)->track
 | 
						|
               else
 | 
						|
                  set $track = ((ast_mutex_t *)$n->locks[$i]->lock_addr)->track
 | 
						|
               end
 | 
						|
            end
 | 
						|
            set $reentrancy = $track->reentrancy
 | 
						|
            set $pending = $n->locks[$i]->pending
 | 
						|
            if $argc > 0
 | 
						|
               printf "%p,%d,%s,%d,%s,%s,%d,%p,%d,%d,%d",\
 | 
						|
                  $n->thread_id, $n->locks[$i]->pending, $n->locks[$i]->file, $n->locks[$i]->line_num, $n->locks[$i]->func,\
 | 
						|
                  $n->locks[$i]->lock_name, $n->locks[$i]->type, $n->locks[$i]->lock_addr, $n->locks[$i]->times_locked,\
 | 
						|
                  $n->locks[$i]->suspended, $track->reentrancy
 | 
						|
               if $reentrancy
 | 
						|
                  if $pending
 | 
						|
                     printf ",%s,%d,%s,%p", $track->file[0], $track->lineno[0], $track->func[0], $track->thread[0]
 | 
						|
                  end
 | 
						|
               end
 | 
						|
            else
 | 
						|
               if $n->locks[$i]->pending < 0
 | 
						|
                  printf "%p failed   %-20s %6d %-36s %-20s %d %14p %3d %d %d",\
 | 
						|
                     $n->thread_id,\
 | 
						|
                     $n->locks[$i]->file, $n->locks[$i]->line_num, $n->locks[$i]->func,\
 | 
						|
                     $n->locks[$i]->lock_name, $n->locks[$i]->type, $n->locks[$i]->lock_addr, $n->locks[$i]->times_locked,\
 | 
						|
                     $n->locks[$i]->suspended, $track->reentrancy
 | 
						|
               end
 | 
						|
               if $n->locks[$i]->pending == 0
 | 
						|
                  printf "%p holding  %-20s %6d %-36s %-20s %d %14p %3d %d %d",\
 | 
						|
                     $n->thread_id,\
 | 
						|
                     $n->locks[$i]->file, $n->locks[$i]->line_num, $n->locks[$i]->func,\
 | 
						|
                     $n->locks[$i]->lock_name, $n->locks[$i]->type, $n->locks[$i]->lock_addr, $n->locks[$i]->times_locked,\
 | 
						|
                     $n->locks[$i]->suspended, $track->reentrancy
 | 
						|
               end
 | 
						|
               if $n->locks[$i]->pending > 0
 | 
						|
                  printf "%p waiting  %-20s %6d %-36s %-20s %d %14p %3d %d %d",\
 | 
						|
                     $n->thread_id,\
 | 
						|
                     $n->locks[$i]->file, $n->locks[$i]->line_num, $n->locks[$i]->func,\
 | 
						|
                     $n->locks[$i]->lock_name, $n->locks[$i]->type, $n->locks[$i]->lock_addr, $n->locks[$i]->times_locked,\
 | 
						|
                     $n->locks[$i]->suspended, $track->reentrancy
 | 
						|
               end
 | 
						|
               if $reentrancy
 | 
						|
                  if $pending
 | 
						|
                     printf "\n               held at: %-20s %6d %-36s by 0x%08lx", $track->file[0], $track->lineno[0], $track->func[0], $track->thread_id[0]
 | 
						|
                  end
 | 
						|
               end
 | 
						|
            end
 | 
						|
            printf "\n"
 | 
						|
         end
 | 
						|
         set $i = $i + 1
 | 
						|
      end
 | 
						|
    end
 | 
						|
    set $n = $n->entry->next
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
dump-asterisk
 |