summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/app_queue.c29
-rw-r--r--configs/samples/ast_debug_tools.conf.sample40
-rw-r--r--configs/samples/queues.conf.sample6
-rw-r--r--contrib/Makefile2
-rwxr-xr-xcontrib/scripts/ast_coredumper520
5 files changed, 591 insertions, 6 deletions
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 9176f9360..f33d22903 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -1668,6 +1668,7 @@ struct call_queue {
int talktime; /*!< Current avg talktime, based on the same exponential average */
int callscompleted; /*!< Number of queue calls completed */
int callsabandoned; /*!< Number of queue calls abandoned */
+ int callsabandonedinsl; /*!< Number of queue calls abandoned in servicelevel */
int servicelevel; /*!< seconds setting for servicelevel*/
int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
char monfmt[8]; /*!< Format to use when recording calls */
@@ -4621,6 +4622,9 @@ static int say_periodic_announcement(struct queue_ent *qe, int ringing)
/*! \brief Record that a caller gave up on waiting in queue */
static void record_abandoned(struct queue_ent *qe)
{
+ int callabandonedinsl = 0;
+ time_t now;
+
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
pbx_builtin_setvar_helper(qe->chan, "ABANDONED", "TRUE");
@@ -4633,6 +4637,13 @@ static void record_abandoned(struct queue_ent *qe)
"OriginalPosition", qe->opos,
"HoldTime", (int)(time(NULL) - qe->start));
+
+ time(&now);
+ callabandonedinsl = ((now - qe->start) <= qe->parent->servicelevel);
+ if (callabandonedinsl) {
+ qe->parent->callsabandonedinsl++;
+ }
+
qe->parent->callsabandoned++;
ao2_unlock(qe->parent);
@@ -9379,6 +9390,8 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char *
queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
float sl;
+ float sl2;
+
struct call_queue *realtime_queue = NULL;
ao2_lock(q);
@@ -9411,12 +9424,16 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char *
ast_str_append(&out, 0, "unlimited");
}
sl = 0;
+ sl2 = 0;
if (q->callscompleted > 0) {
sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
}
- ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
- int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
- q->callscompleted, q->callsabandoned,sl,q->servicelevel);
+ if (q->callscompleted + q->callsabandoned > 0) {
+ sl2 =100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted));
+ }
+
+ ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%%, SL2:%2.1f%% within %ds",
+ int2strat(q->strategy), q->holdtime, q->talktime, q->weight, q->callscompleted, q->callsabandoned, sl, sl2, q->servicelevel);
do_print(s, fd, ast_str_buffer(out));
if (!ao2_container_count(q->members)) {
do_print(s, fd, " No Members");
@@ -9773,6 +9790,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
struct call_queue *q;
struct queue_ent *qe;
float sl = 0;
+ float sl2 = 0;
struct member *mem;
struct ao2_iterator queue_iter;
struct ao2_iterator mem_iter;
@@ -9791,6 +9809,8 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
/* List queue properties */
if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
+ sl2 = (((q->callscompleted + q->callsabandoned) > 0) ? 100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted)) : 0);
+
astman_append(s, "Event: QueueParams\r\n"
"Queue: %s\r\n"
"Max: %d\r\n"
@@ -9802,11 +9822,12 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
"Abandoned: %d\r\n"
"ServiceLevel: %d\r\n"
"ServicelevelPerf: %2.1f\r\n"
+ "ServicelevelPerf2: %2.1f\r\n"
"Weight: %d\r\n"
"%s"
"\r\n",
q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
- q->callsabandoned, q->servicelevel, sl, q->weight, idText);
+ q->callsabandoned, q->servicelevel, sl, sl2, q->weight, idText);
++q_items;
/* List Queue Members */
diff --git a/configs/samples/ast_debug_tools.conf.sample b/configs/samples/ast_debug_tools.conf.sample
new file mode 100644
index 000000000..90e976f1b
--- /dev/null
+++ b/configs/samples/ast_debug_tools.conf.sample
@@ -0,0 +1,40 @@
+#
+# 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))
+
+# 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'
+#
+# Unix timestamp on *BSD/MacOS after installing 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'
diff --git a/configs/samples/queues.conf.sample b/configs/samples/queues.conf.sample
index 8a9c88402..ebb5da126 100644
--- a/configs/samples/queues.conf.sample
+++ b/configs/samples/queues.conf.sample
@@ -113,8 +113,10 @@ monitor-type = MixMonitor
;strategy = ringall
;
; Second settings for service level (default 0)
-; Used for service level statistics (calls answered within service level time
-; frame)
+; Used for service level statistics (calls answered within service level time frame)
+; There are 2 Service Level Performance Calculations SL and SL2
+; SL = (total calls answered within threshold / total calls answered) * 100
+; SL2 = ((calls answered within threshold + calls abandoned within threshold) / (answered calls + abandoned calls)) * 100
;servicelevel = 60
;
; A context may be specified, in which if the user types a SINGLE
diff --git a/contrib/Makefile b/contrib/Makefile
index 37f4df439..a5775cb82 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -22,6 +22,8 @@ include $(ASTTOPDIR)/Makefile.rules
install:
$(INSTALL) -d "$(DESTDIR)$(ASTDATADIR)/scripts"; \
$(INSTALL) -m 755 scripts/refcounter.py "$(DESTDIR)$(ASTDATADIR)/scripts/refcounter.py"; \
+ $(INSTALL) -m 755 scripts/ast_coredumper "$(DESTDIR)$(ASTDATADIR)/scripts/ast_coredumper"
uninstall:
rm -f "$(DESTDIR)$(ASTDATADIR)/scripts/refcounter.py"
+ rm -f "$(DESTDIR)$(ASTDATADIR)/scripts/ast_coredumper"
diff --git a/contrib/scripts/ast_coredumper b/contrib/scripts/ast_coredumper
new file mode 100755
index 000000000..c82732be9
--- /dev/null
+++ b/contrib/scripts/ast_coredumper
@@ -0,0 +1,520 @@
+#!/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 ]
+ [ --no-default-search ] [ --append-coredumps ]
+ [ <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.
+
+ --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 all coredumps processed.
+ The tarball name will be:
+ /tmp/asterisk.<timestamp>.coredumps.tar.gz
+
+ --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:
+ /tmp/asterisk.<timestamp>.results.tar.gz
+
+ --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.
+
+ --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.
+
+ <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
+ 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))
+
+ # 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
+}
+
+running=false
+RUNNING=false
+latest=false
+tarball_coredumps=false
+delete_coredumps_after=false
+tarball_results=false
+delete_results_after=false
+append_coredumps=false
+
+declare -a COREDUMPS
+declare -a ARGS_COREDUMPS
+
+# Read config files from least important to most important
+[ -f /etc/asterisk/ast_debug_tools.conf ] && source /etc/asterisk/ast_debug_tools.conf
+[ -f ~/ast_debug_tools.conf ] && source ~/ast_debug_tools.conf
+[ -f ./ast_debug_tools.conf ] && source ./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 [ ${#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
+ ;;
+ --delete-coredumps-after)
+ delete_coredumps_after=true
+ ;;
+ --tarball-results)
+ tarball_results=true
+ ;;
+ --delete-results-after)
+ delete_results_after=true
+ ;;
+ --append-coredumps)
+ append_coredumps=true
+ ;;
+ --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=$(${DATEFORMAT})
+
+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.
+ # The pid file may NOT be in /var/run/asterisk so we need to find any
+ # running asterisk process and see if -C was specified on the command
+ # line. The chances of more than 1 asterisk instance running with
+ # different -C options is so unlikely that we're going to ignore it.
+ #
+ # 'ps axo command' should work on Linux (back to CentOS6) and FreeBSD.
+ # If asterisk was started with -C, get the asterisk.conf file.
+ # If it wasn't, assume /etc/asterisk/asterisk.conf
+ astetcconf=`ps axo command | sed -n -r -e "s/.*asterisk\s+.*-C\s+([^ ]+).*/\1/gp" | tail -1`
+ [ x$astetcconf = x ] && astetcconf=/etc/asterisk/asterisk.conf
+ # Now parse out astrundir and cat asterisk.pid
+ astrundir=$(sed -n -r -e "s/astrundir\s+[=>]+\s+(.*)/\1/gp" $astetcconf)
+ pid=$(cat $astrundir/asterisk.pid 2>/dev/null || : )
+ if [ x$pid = x ] ; then
+ echo "Asterisk is not running"
+ else
+ 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="/tmp/core.asterisk.running.$df"
+ # We want a consistent coredump so stop the process
+ # and continue it after the dump is complete.
+ # kill -STOP $pid
+ ${GDB} -p $pid -q --batch --ex "gcore $cf" >/dev/null 2>&1
+ # kill -CONT $pid
+ COREDUMPS+=("$cf")
+ else
+ echo "Skipping dump of running process"
+ fi
+ 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
+
+ss=`egrep -n "^#@@@SCRIPTSTART@@@" $0 |cut -f1 -d:`
+tail -n +${ss} $0 >/tmp/.gdbinit
+
+# Now iterate over the coredumps and dump the debugging info
+for i in ${!COREDUMPS[@]} ; do
+ cf=${COREDUMPS[$i]}
+ echo "Processing $cf"
+ ${GDB} -n --batch -q --ex "source /tmp/.gdbinit" $(which asterisk) "$cf" 2>/dev/null | (
+ of=/dev/null
+ while IFS= read line ; do
+ if [[ "$line" =~ !@!@!@!\ ([^\ ]+)\ !@!@!@! ]] ; then
+ of=$cf.${BASH_REMATCH[1]}
+ of=${of//:/-}
+ rm -f "$of"
+ echo "Creating $of"
+ fi
+ echo -e $"$line" >> "$of"
+ done
+ )
+done
+
+if $tarball_coredumps ; then
+ tf=/tmp/asterisk.$df.coredumps.tar
+ echo "Creating $tf.gz"
+ for i in ${!COREDUMPS[@]} ; do
+ tar -uvf $tf "${COREDUMPS[@]}" 2>/dev/null
+ done
+ gzip $tf
+fi
+
+if $delete_coredumps_after ; then
+ for i in ${!COREDUMPS[@]} ; do
+ rm -rf "${COREDUMPS[$i]}"
+ done
+fi
+
+if $tarball_results ; then
+ tf=/tmp/asterisk.$df.results.tar
+ echo "Creating $tf.gz"
+ for i in ${!COREDUMPS[@]} ; do
+ tar -uvf $tf "${COREDUMPS[$i]//:/-}".{brief,full,thread1,locks}.txt 2>/dev/null
+ done
+ gzip $tf
+fi
+
+if $delete_results_after ; then
+ for i in ${!COREDUMPS[@]} ; do
+ rm -rf "${COREDUMPS[$i]//:/-}".{brief,full,thread1,locks}.txt
+ done
+fi
+
+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("thread apply 1 bt full", from_tty)
+ except:
+ pass
+ print("!@!@!@! brief.txt !@!@!@!\n")
+ try:
+ gdb.execute("thread apply all bt", from_tty)
+ except:
+ pass
+ print("!@!@!@! full.txt !@!@!@!\n")
+ try:
+ gdb.execute("thread apply all bt full", from_tty)
+ except:
+ pass
+ print("!@!@!@! locks.txt !@!@!@!\n")
+ 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