DEV: gdb: add a number of gdb scripts to navigate in core dumps
These is a collection of functions I'm occasionally using to navigate in core dumps. Only working ones were extracted. Those requiring knowledge of global variables (e.g. pools, proxy list) use the one extracted from the post_mortem struct. That one is defined in post-mortem.gdb and needs to be initialized using "pm_init post_mortem" or "pm_init <pointer>". From this point a number of global variables are accessible even if symbols are missing; those ones are then used by other functions to dump streams, threads, pools, proxies etc. The files can be sourced or copy-pasted into a gdb session. It's worth trying to keep them up-to-date, as the old ones used to navigate through tasks are no longer usable due to massive changes.
This commit is contained in:
parent
52240680f1
commit
e240be5495
|
@ -0,0 +1,118 @@
|
|||
# sets $tag and $node from $arg0, for internal use only
|
||||
define _ebtree_set_tag_node
|
||||
set $tag = (unsigned long)$arg0 & 0x1
|
||||
set $node = (unsigned long)$arg0 & 0xfffffffffffffffe
|
||||
set $node = (struct eb_node *)$node
|
||||
end
|
||||
|
||||
# get root from any node (leaf of node), returns in $node
|
||||
define ebtree_root
|
||||
set $node = (struct eb_root *)$arg0->node_p
|
||||
if $node == 0
|
||||
# sole node
|
||||
set $node = (struct eb_root *)$arg0->leaf_p
|
||||
end
|
||||
# walk up
|
||||
while 1
|
||||
_ebtree_set_tag_node $node
|
||||
if $node->branches.b[1] == 0
|
||||
break
|
||||
end
|
||||
set $node = $node->node_p
|
||||
end
|
||||
# root returned in $node
|
||||
end
|
||||
|
||||
# returns $node filled with the first node of ebroot $arg0
|
||||
define ebtree_first
|
||||
# browse ebtree left until encoutering leaf
|
||||
set $node = (struct eb_node *)$arg0->b[0]
|
||||
while 1
|
||||
_ebtree_set_tag_node $node
|
||||
if $tag == 0
|
||||
loop_break
|
||||
end
|
||||
set $node = (struct eb_root *)$node->branches.b[0]
|
||||
end
|
||||
# extract last node
|
||||
_ebtree_set_tag_node $node
|
||||
end
|
||||
|
||||
# finds next ebtree node after $arg0, and returns it in $node
|
||||
define ebtree_next
|
||||
# get parent
|
||||
set $node = (struct eb_root *)$arg0->leaf_p
|
||||
# Walking up from right branch, so we cannot be below root
|
||||
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
|
||||
while 1
|
||||
_ebtree_set_tag_node $node
|
||||
if $tag == 0
|
||||
loop_break
|
||||
end
|
||||
set $node = (struct eb_root *)$node->node_p
|
||||
end
|
||||
set $node = (struct eb_root *)$node->branches.b[1]
|
||||
# walk down (left side => 0)
|
||||
# while (eb_gettag(start) == EB_NODE) // #define EB_NODE 1
|
||||
while 1
|
||||
_ebtree_set_tag_node $node
|
||||
if $node == 0
|
||||
loop_break
|
||||
end
|
||||
if $tag != 1
|
||||
loop_break
|
||||
end
|
||||
set $node = (struct eb_root *)$node->branches.b[0]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# sets $tag and $node from $arg0, for internal use only
|
||||
define _ebsctree_set_tag_node
|
||||
set $tag = (unsigned long)$arg0 & 0x1
|
||||
set $node = (unsigned long)$arg0 & 0xfffffffffffffffe
|
||||
set $node = (struct eb32sc_node *)$node
|
||||
end
|
||||
|
||||
# returns $node filled with the first node of ebroot $arg0
|
||||
define ebsctree_first
|
||||
# browse ebsctree left until encoutering leaf
|
||||
set $node = (struct eb32sc_node *)$arg0->b[0]
|
||||
while 1
|
||||
_ebsctree_set_tag_node $node
|
||||
if $tag == 0
|
||||
loop_break
|
||||
end
|
||||
set $node = (struct eb_root *)$node->branches.b[0]
|
||||
end
|
||||
# extract last node
|
||||
_ebsctree_set_tag_node $node
|
||||
end
|
||||
|
||||
# finds next ebtree node after $arg0, and returns it in $node
|
||||
define ebsctree_next
|
||||
# get parent
|
||||
set $node = (struct eb_root *)$arg0->node.leaf_p
|
||||
# Walking up from right branch, so we cannot be below root
|
||||
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
|
||||
while 1
|
||||
_ebsctree_set_tag_node $node
|
||||
if $tag == 0
|
||||
loop_break
|
||||
end
|
||||
set $node = (struct eb_root *)$node->node.node_p
|
||||
end
|
||||
set $node = (struct eb_root *)$node->node.branches.b[1]
|
||||
# walk down (left side => 0)
|
||||
# while (eb_gettag(start) == EB_NODE) // #define EB_NODE 1
|
||||
while 1
|
||||
_ebsctree_set_tag_node $node
|
||||
if $node == 0
|
||||
loop_break
|
||||
end
|
||||
if $tag != 1
|
||||
loop_break
|
||||
end
|
||||
set $node = (struct eb_root *)$node->node.branches.b[0]
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# lists entries starting at list head $arg0
|
||||
define list_dump
|
||||
set $h = $arg0
|
||||
set $p = *(void **)$h
|
||||
while ($p != $h)
|
||||
printf "%#lx\n", $p
|
||||
if ($p == 0)
|
||||
loop_break
|
||||
end
|
||||
set $p = *(void **)$p
|
||||
end
|
||||
end
|
||||
|
||||
# list all entries starting at list head $arg0 until meeting $arg1
|
||||
define list_find
|
||||
set $h = $arg0
|
||||
set $k = $arg1
|
||||
set $p = *(void **)$h
|
||||
while ($p != $h)
|
||||
printf "%#lx\n", $p
|
||||
if ($p == 0 || $p == $k)
|
||||
loop_break
|
||||
end
|
||||
set $p = *(void **)$p
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# dump pool contents (2.9 and above, with buckets)
|
||||
define pools_dump
|
||||
set $h = $po
|
||||
set $p = *(void **)$h
|
||||
while ($p != $h)
|
||||
set $e = (struct pool_head *)(((char *)$p) - (unsigned long)&((struct pool_head *)0)->list)
|
||||
|
||||
set $total = 0
|
||||
set $used = 0
|
||||
set $idx = 0
|
||||
while $idx < sizeof($e->buckets) / sizeof($e->buckets[0])
|
||||
set $total=$total + $e->buckets[$idx].allocated
|
||||
set $used=$used + $e->buckets[$idx].used
|
||||
set $idx=$idx + 1
|
||||
end
|
||||
|
||||
set $mem = $total * $e->size
|
||||
printf "list=%#lx pool_head=%p name=%s size=%u alloc=%u used=%u mem=%u\n", $p, $e, $e->name, $e->size, $total, $used, $mem
|
||||
set $p = *(void **)$p
|
||||
end
|
||||
end
|
|
@ -0,0 +1,47 @@
|
|||
# This script will set the post_mortem struct pointer ($pm) from the one found
|
||||
# in the "post_mortem" symbol. If not found or if not correct, it's the same
|
||||
# address as the "_post_mortem" section, which can be found using "info files"
|
||||
# or "objdump -h" on the executable. The guessed value is the by a first call
|
||||
# to pm_init, but if not correct, you just need to call pm_init again with the
|
||||
# correct pointer, e.g:
|
||||
# pm_init 0xcfd400
|
||||
|
||||
define pm_init
|
||||
set $pm = (struct post_mortem*)$arg0
|
||||
set $g = $pm.global
|
||||
set $ti = $pm.thread_info
|
||||
set $tc = $pm.thread_ctx
|
||||
set $tgi = $pm.tgroup_info
|
||||
set $tgc = $pm.tgroup_ctx
|
||||
set $fd = $pm.fdtab
|
||||
set $pxh = *$pm.proxies
|
||||
set $po = $pm.pools
|
||||
set $ac = $pm.activity
|
||||
end
|
||||
|
||||
# show basic info on the running process (OS, uid, etc)
|
||||
define pm_show_info
|
||||
print $pm->platform
|
||||
print $pm->process
|
||||
end
|
||||
|
||||
# show thread IDs to easily map between gdb threads and tid
|
||||
define pm_show_threads
|
||||
set $t = 0
|
||||
while $t < $g.nbthread
|
||||
printf "Tid %4d: pthread_id=%#lx stack_top=%#lx\n", $t, $ti[$t].pth_id, $ti[$t].stack_top
|
||||
set $t = $t + 1
|
||||
end
|
||||
end
|
||||
|
||||
# dump all threads' dump buffers
|
||||
define pm_show_thread_dump
|
||||
set $t = 0
|
||||
while $t < $g.nbthread
|
||||
printf "%s\n", $tc[$t].thread_dump_buffer->area
|
||||
set $t = $t + 1
|
||||
end
|
||||
end
|
||||
|
||||
# initialize the various pointers
|
||||
pm_init &post_mortem
|
|
@ -0,0 +1,25 @@
|
|||
# list proxies starting with the one in argument (typically $pxh)
|
||||
define px_list
|
||||
set $p = (struct proxy *)$arg0
|
||||
while ($p != 0)
|
||||
printf "%p (", $p
|
||||
if $p->cap & 0x10
|
||||
printf "LB,"
|
||||
end
|
||||
if $p->cap & 0x1
|
||||
printf "FE,"
|
||||
end
|
||||
if $p->cap & 0x2
|
||||
printf "BE,"
|
||||
end
|
||||
printf "%s)", $p->id
|
||||
if $p->cap & 0x1
|
||||
printf " feconn=%u cmax=%u cum_conn=%llu cpsmax=%u", $p->feconn, $p->fe_counters.conn_max, $p->fe_counters.cum_conn, $p->fe_counters.cps_max
|
||||
end
|
||||
if $p->cap & 0x2
|
||||
printf " beconn=%u served=%u queued=%u qmax=%u cum_sess=%llu wact=%u", $p->beconn, $p->served, $p->queue.length, $p->be_counters.nbpend_max, $p->be_counters.cum_sess, $p->lbprm.tot_wact
|
||||
end
|
||||
printf "\n"
|
||||
set $p = ($p)->next
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
# list servers in a proxy whose pointer is passed in argument
|
||||
define px_list_srv
|
||||
set $h = (struct proxy *)$arg0
|
||||
set $p = ($h)->srv
|
||||
while ($p != 0)
|
||||
printf "%#lx %s maxconn=%u cur_sess=%u max_sess=%u served=%u queued=%u st=%u->%u ew=%u sps_max=%u\n", $p, $p->id, $p->maxconn, $p->cur_sess, $p->counters.cur_sess_max, $p->served, $p->queue.length, $p->cur_state, $p->next_state, $p->cur_eweight, $p->counters.sps_max
|
||||
set $p = ($p)->next
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
# list all streams for all threads
|
||||
define stream_dump
|
||||
set $t = 0
|
||||
while $t < $g.nbthread
|
||||
set $h = &$tc[$t].streams
|
||||
printf "Tid %4d: &streams=%p\n", $t, $h
|
||||
set $p = *(void **)$h
|
||||
while ($p != $h)
|
||||
set $s = (struct stream *)(((char *)$p) - (unsigned long)&((struct stream *)0)->list)
|
||||
printf " &list=%#lx strm=%p uid=%u strm.fe=%s strm.flg=%#x strm.list={n=%p,p=%p}\n", $p, $s, $s->uniq_id, $s->sess->fe->id, $s->flags, $s->list.n, $s->list.p
|
||||
if ($p == 0)
|
||||
loop_break
|
||||
end
|
||||
set $p = *(void **)$p
|
||||
end
|
||||
set $t = $t + 1
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue