ast_coredumper: Fix multiple issues

* Fixed an issue with tarball-coredumps when asterisk was invoked without an
absolute path.

* Fixed an issue with gdb itself segfaulting when trying to get symbols from
separate debuginfo files.  The command line arguments needed to be altered
such that the gdbinit files is loaded before anything else but the
`dump-asterisk` command is run after full initialization.

In the embedded gdbinit script:

* The extract_string_symbol function needed a `char *` cast to work properly.

* The s_strip function needed to be updated to continue to work with the
cpp_map_name_id channel storage backend.

* A new function was added to dump the channels when cpp_map_name_id was
used.

* The Channel object was updated to account for the new channel storage
backends

* The show_locks function was refactored to work correctly.
This commit is contained in:
George Joseph
2025-11-07 14:39:12 -07:00
committed by github-actions[bot]
parent 7d104d69a9
commit bb506faaa7

View File

@@ -266,6 +266,8 @@ for i in "${!COREDUMPS[@]}" ; do
libdir=$(dirname "${libfile}") libdir=$(dirname "${libfile}")
} }
[ "$(dirname "${astbin}")" == "." ] && astbin="$(which "${astbin}")" || :
astbin=$(realpath -e "${astbin}")
msg " ASTBIN: $astbin" msg " ASTBIN: $astbin"
msg " MODDIR: $moddir" msg " MODDIR: $moddir"
msg " ETCDIR: $etcdir" msg " ETCDIR: $etcdir"
@@ -288,7 +290,7 @@ for i in "${!COREDUMPS[@]}" ; do
cfname=$(basename "${cf}") cfname=$(basename "${cf}")
# Produce all the output files # Produce all the output files
${GDB} -n --batch -q --ex "source $gdbinit" "${astbin}" "$cf" 2>/dev/null | ( ${GDB} -n --batch -q --iex "source $gdbinit" -ex "dump-asterisk" "${astbin}" "$cf" 2>/dev/null | (
of=/dev/null of=/dev/null
while IFS= read -r line ; do while IFS= read -r line ; do
if [[ "$line" =~ !@!@!@!\ ([^\ ]+)\ !@!@!@! ]] ; then if [[ "$line" =~ !@!@!@!\ ([^\ ]+)\ !@!@!@! ]] ; then
@@ -507,7 +509,7 @@ extract_binary_name() {
# shellcheck disable=SC2317 # shellcheck disable=SC2317
extract_string_symbol() { extract_string_symbol() {
${GDB} "$1" "$2" -q --batch \ ${GDB} "$1" "$2" -q --batch \
-ex "p $3" 2>/dev/null \ -ex "p (char *)$3" 2>/dev/null \
| sed -n -r -e 's/[$]1\s*=\s*[0-9a-fx]+\s+<[^>]+>\s+"([^"]+)"/\1/gp' | sed -n -r -e 's/[$]1\s*=\s*[0-9a-fx]+\s+<[^>]+>\s+"([^"]+)"/\1/gp'
return 0 return 0
} }
@@ -771,6 +773,12 @@ def s_strip(value):
except: except:
pass pass
try:
if value.type.code in [ gdb.TYPE_CODE_ARRAY, gdb.TYPE_CODE_PTR ]:
return value.string()
except:
pass
return str(value).strip('" ') or "<None>" return str(value).strip('" ') or "<None>"
@@ -898,6 +906,64 @@ def get_container_rbtree_objects(name, type, on_object=None):
return objs return objs
def get_container_count(name):
return int(gdb.parse_and_eval(name).dereference()['elements'])
def get_container_map_objects(name, type, on_object=None):
"""Retrieve a list of objects from a C++ map.
Expected on_object signature:
res, stop = on_object(GDB Value)
The given callback, on_object, is called for each object found in the
map. The callback is passed a dereferenced GDB Value object and
expects an object to be returned, which is then appended to a list of
objects to be returned by this function. Iteration can be stopped by
returning "True" for the second return value.
If on_object is not specified then the dereferenced GDB value is instead
added directly to the returned list.
Args:
name: The name of the map
type: The type of objects stored in the container
on_object: Optional function called on each object found
Return:
A list of map objects
"""
objs = []
map = gdb.parse_and_eval(name)
node = map['_M_t']['_M_impl']['_M_header']['_M_left']
tree_size = map['_M_t']['_M_impl']['_M_node_count']
for i in range(0, tree_size):
obj_node = (node + 2)
obj_p = obj_node.cast(gdb.lookup_type(type).pointer().pointer()).dereference()
obj = obj_p.dereference()
res, stop = on_object(obj) if on_object else (obj, False)
if res:
objs.append(res)
if stop:
return objs
if node['_M_right'] != 0:
node = node['_M_right']
while node['_M_left'] != 0:
node = node['_M_left']
else:
tmp_node = node['_M_parent']
while node == tmp_node['_M_right']:
node = tmp_node
tmp_node = tmp_node['_M_parent']
if node['_M_right'] != tmp_node:
node = tmp_node
return objs
def get_map_count(name):
map = gdb.parse_and_eval(name)
return map['_M_t']['_M_impl']['_M_node_count']
def build_info(): def build_info():
@@ -969,7 +1035,7 @@ class TaskProcessor(object):
def __init__(self, name, processed, in_queue, max_depth, def __init__(self, name, processed, in_queue, max_depth,
low_water, high_water): low_water, high_water):
self.name = s_strip(name) self.name = str(name).strip('"')
self.processed = int(processed) self.processed = int(processed)
self.in_queue = int(in_queue) self.in_queue = int(in_queue)
self.max_depth = int(max_depth) self.max_depth = int(max_depth)
@@ -979,7 +1045,7 @@ class TaskProcessor(object):
class Channel(object): class Channel(object):
template = ("{name:30} {context:>20} {exten:>20} {priority:>10} {state:>25} " template = ("{name:42} {context:>20} {exten:>20} {priority:>10} {state:>25} "
"{app:>20} {data:>30} {caller_id:>15} {created:>30} " "{app:>20} {data:>30} {caller_id:>15} {created:>30} "
"{account_code:>15} {peer_account:>15} {bridge_id:>38}") "{account_code:>15} {peer_account:>15} {bridge_id:>38}")
@@ -989,14 +1055,22 @@ class Channel(object):
'account_code': 'Accountcode', 'peer_account': 'PeerAccount', 'account_code': 'Accountcode', 'peer_account': 'PeerAccount',
'bridge_id': 'BridgeID'} 'bridge_id': 'BridgeID'}
container = 'current_channel_storage_instance->handle->handle'
map = '(((struct mni_channelstorage_driver_pvt *)current_channel_storage_instance->handle)->by_name)'
@staticmethod @staticmethod
def objects(): def objects():
try: try:
objs = get_container_hash_objects('channels', driver = gdb.parse_and_eval("current_channel_storage_driver")
'struct ast_channel', Channel.from_value) driver_name = driver['driver_name'].string()
if driver_name == "cpp_map_name_id":
objs.sort(key=lambda x: x.name.lower()) objs = get_container_map_objects(Channel.map,
'struct ast_channel', Channel.from_value)
else:
objs = get_container_hash_objects(Channel.container,
'struct ast_channel', Channel.from_value)
objs.sort(key=lambda x: x.name.lower())
except: except:
return [] return []
@@ -1025,13 +1099,19 @@ class Channel(object):
@staticmethod @staticmethod
def summary(): def summary():
driver = gdb.parse_and_eval("current_channel_storage_driver")
driver_name = driver['driver_name'].string()
if driver_name == "cpp_map_name_id":
count = get_map_count(Channel.map)
else:
count = get_container_count(Channel.container)
try: try:
return ("{0} active channels\n" return ("Channel Driver Name: {0}\n"
"{1} active calls\n" "{1} active channels\n"
"{2} calls processed".format( "{2} active calls\n"
int(gdb.parse_and_eval( "{3} calls processed".format(driver_name,
'channels').dereference()['elements']), count,
get("countcalls"), get("countcalls"),
get("totalcalls"))) get("totalcalls")))
except: except:
@@ -1197,76 +1277,81 @@ DumpAsteriskCommand ()
end end
define show_locks define show_locks
set $n = lock_infos.first set $n = lock_infos.first
if $argc == 0 if $argc == 0
printf " where_held count-|\n" printf "%s\n", " where_held count-|"
printf " suspended-| |\n" printf "%s\n", " suspended-| |"
printf " type- | times locked-| | |\n" printf "%s\n", " type-| times locked-| | |"
printf "thread status file line function lock name | lock addr | | |\n" printf "%-14s %-36s %6s %-42s %-8s %-36s %3s %-14s %3s %3s %3s\n",\
else "thread","file","line","function","status","lock name","|","lock addr","|","|","|"
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" printf "%s\n", "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
end
while $n else
if $n->num_locks > 0 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 set $i = 0
while $i < $n->num_locks while $i < $n->num_locks
if $n->locks[$i]->suspended == 0 if $n->locks[$i]->suspended != 47
if ((ast_mutex_t *)$n->locks[$i]->lock_addr)->tracking if $n->locks[$i]->pending > 0
if $n->locks[$i]->type > 0 set $status = "waiting"
set $track = ((ast_rwlock_t *)$n->locks[$i]->lock_addr)->track end
else if $n->locks[$i]->pending < 0
set $track = ((ast_mutex_t *)$n->locks[$i]->lock_addr)->track set $status = "failed"
end end
end if $n->locks[$i]->pending == 0
set $reentrancy = $track->reentrancy set $status = "holding"
set $pending = $n->locks[$i]->pending end
if $argc > 0
printf "%p,%d,%s,%d,%s,%s,%d,%p,%d,%d,%d",\ if $n->locks[$i]->type == 0
$n->thread_id, $n->locks[$i]->pending, $n->locks[$i]->file, $n->locks[$i]->line_num, $n->locks[$i]->func,\ set $ltype = "M"
$n->locks[$i]->lock_name, $n->locks[$i]->type, $n->locks[$i]->lock_addr, $n->locks[$i]->times_locked,\ end
$n->locks[$i]->suspended, $track->reentrancy if $n->locks[$i]->type == 1
if $reentrancy set $ltype = "RD"
if $pending end
printf ",%s,%d,%s,%p", $track->file[0], $track->lineno[0], $track->func[0], $track->thread[0] if $n->locks[$i]->type == 2
end set $ltype = "WR"
end end
if ((ast_mutex_t *)$n->locks[$i]->lock_addr)->track
if $n->locks[$i]->type > 0
set $track = ((ast_rwlock_t *)$n->locks[$i]->lock_addr)->track
else else
if $n->locks[$i]->pending < 0 set $track = ((ast_mutex_t *)$n->locks[$i]->lock_addr)->track
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 end
printf "\n" end
end if $track
set $i = $i + 1 set $reentrancy = $track->reentrancy
else
set $reentrancy = 0
end
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, $reentrancy
if $reentrancy && $pending
printf ",%s,%d,%s,%p", $track->file[0], $track->lineno[0], $track->func[0], $track->thread[0]
end
else
printf "%14p %-36s %6d %-42s %-8s %-36s %3s %-14p %3d %3d %3d", \
$n->thread_id, $n->locks[$i]->file, $n->locks[$i]->line_num, $n->locks[$i]->func, $status, \
$n->locks[$i]->lock_name, $ltype, $n->locks[$i]->lock_addr, $n->locks[$i]->times_locked,\
$n->locks[$i]->suspended, $reentrancy
if $reentrancy && $pending
printf "\n held at: %s:%d %s() by 0x%08lx", \
$track->file[0], $track->lineno[0], $track->func[0], $track->thread_id[0]
end
end
printf "\n"
end
set $i = $i + 1
end end
end end
set $n = $n->entry->next set $n = $n->entry->next
end end
end end
dump-asterisk