This is an automated email from the ASF dual-hosted git repository.
jamesge pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-brpc.git
The following commit(s) were added to refs/heads/master by this push:
new f3d46b7 tools: add gdb_bthread_stack.py
new 95339cb Merge pull request #903 from lorinlee/bthread-tool
f3d46b7 is described below
commit f3d46b7a3f2ec0823259c150c94602520e8d8008
Author: LorinLee <[email protected]>
AuthorDate: Sun Aug 25 18:44:44 2019 +0800
tools: add gdb_bthread_stack.py
---
tools/gdb_bthread_stack.py | 243 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 243 insertions(+)
diff --git a/tools/gdb_bthread_stack.py b/tools/gdb_bthread_stack.py
new file mode 100644
index 0000000..582ba9e
--- /dev/null
+++ b/tools/gdb_bthread_stack.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+Bthread Stack Print Tool
+
+this only for running process, core dump is not supported.
+
+Get Started:
+ 1. gdb attach <pid>
+ 2. source gdb_bthread_stack.py
+ 3. bthread_begin
+ 4. bthread_list
+ 5. bthread_frame 0
+ 6. bt / up / down
+ 7. bthread_end
+
+Commands:
+ 1. bthread_num: print all bthread nums
+ 2. bthread_begin <num>: enter bthread debug mode, `num` is max scanned
bthreads, default will scan all
+ 3. bthread_list: list all bthreads
+ 4. bthread_frame <id>: switch stack to bthread, id will displayed in
bthread_list
+ 5. bthread_meta <id>: print bthread meta
+ 6. bthread_reg_restore: bthread_frame will modify registers, reg_restore
will restore them
+ 7. bthread_end: exit bthread debug mode
+ 8. bthread_regs <id>: print bthread registers
+
+when call bthread_frame, registers will be modified,
+remember to call bthread_end after debug, or process will be corrupted
+
+after call bthread_frame, you can call `bt`/`up`/`down`, or other gdb command
+"""
+
+import gdb
+
+bthreads = []
+status = False
+
+def get_bthread_num():
+ root_agent =
gdb.parse_and_eval("&(((((*bthread::g_task_control)._nbthreads)._combiner)._agents).root_)")
+ global_res =
int(gdb.parse_and_eval("((*bthread::g_task_control)._nbthreads)._combiner._global_result"))
+ get_agent = "(*(('bvar::detail::AgentCombiner<long, long,
bvar::detail::AddTo<long> >::Agent' *){}))"
+ last_node = root_agent
+ while True:
+ agent = gdb.parse_and_eval(get_agent.format(last_node))
+ if last_node != root_agent:
+ val = int(agent["element"]["_value"]["_M_i"])
+ global_res += val
+ if agent["next_"] == root_agent:
+ return global_res
+ last_node = agent["next_"]
+
+def get_all_bthreads(total):
+ global bthreads
+ bthreads = []
+ count = 0
+ groups =
int(gdb.parse_and_eval("'butil::ResourcePool<bthread::TaskMeta>::_ngroup'")["val"])
+ for group in range(groups):
+ blocks =
int(gdb.parse_and_eval("(*((*((('butil::static_atomic<butil::ResourcePool<bthread::TaskMeta>::BlockGroup*>'
*)('butil::ResourcePool<bthread::TaskMeta>::_block_groups')) +
{})).val)).nblock._M_i".format(group)))
+ for block in range(blocks):
+ items =
int(gdb.parse_and_eval("(*(*(('butil::atomic<butil::ResourcePool<bthread::TaskMeta>::Block*>'
*)((*((*((('butil::static_atomic<butil::ResourcePool<bthread::TaskMeta>::BlockGroup*>'
*)('butil::ResourcePool<bthread::TaskMeta>::_block_groups')) +
{})).val)).blocks) + {}))._M_b._M_p).nitem".format(group, block)))
+ for item in range(items):
+ task_meta = gdb.parse_and_eval("*(('bthread::TaskMeta'
*)((*(*(('butil::atomic<butil::ResourcePool<bthread::TaskMeta>::Block*>'
*)((*((*((('butil::static_atomic<butil::ResourcePool<bthread::TaskMeta>::BlockGroup*>'
*)('butil::ResourcePool<bthread::TaskMeta>::_block_groups')) +
{})).val)).blocks) + {}))._M_b._M_p).items) + {})".format(group, block, item))
+ version_tid = (int(task_meta["tid"]) >> 32)
+ version_butex = gdb.parse_and_eval("*(uint32_t
*){}".format(task_meta["version_butex"]))
+ if version_tid == int(version_butex) and
int(task_meta["attr"]["stack_type"]) != 0:
+ bthreads.append(task_meta)
+ count += 1
+ if count >= total:
+ return
+
+class BthreadListCmd(gdb.Command):
+ """list all bthreads, print format is 'id\ttid\tfunction\thas stack'"""
+ def __init__(self):
+ gdb.Command.__init__(self, "bthread_list", gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, tty):
+ global status
+ global bthreads
+ if not status:
+ print("Not in bthread debug mode")
+ return
+ print("id\t\ttid\t\tfunction\t\thas
stack\t\t\ttotal:{}".format(len(bthreads)))
+ for i, t in enumerate(bthreads):
+ print("#{}\t\t{}\t\t{}\t\t{}".format(i, t["tid"], t["fn"], "no" if
str(t["stack"]) == "0x0" else "yes"))
+
+class BthreadNumCmd(gdb.Command):
+ """list active bthreads num"""
+ def __init__(self):
+ gdb.Command.__init__(self, "bthread_num", gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, tty):
+ res = get_bthread_num()
+ print(res)
+
+class BthreadFrameCmd(gdb.Command):
+ """bthread_frame <id>, select bthread frame by id"""
+ def __init__(self):
+ gdb.Command.__init__(self, "bthread_frame", gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, tty):
+ global status
+ global bthreads
+ if not status:
+ print("Not in bthread debug mode")
+ return
+ if not arg:
+ print("bthread_frame <id>, see 'bthread_list'")
+ return
+ bthread_id = int(arg)
+ if bthread_id >= len(bthreads):
+ print("id {} exceeds max bthread nums {}".format(bthread_id,
len(bthreads)))
+ return
+ stack = bthreads[bthread_id]["stack"]
+ if str(stack) == "0x0":
+ print("this bthread has no stack")
+ return
+ context = gdb.parse_and_eval("(*(('bthread::ContextualStack'
*){})).context".format(stack))
+ rip = gdb.parse_and_eval("*(uint64_t*)({}+7*8)".format(context))
+ rbp = gdb.parse_and_eval("*(uint64_t*)({}+6*8)".format(context))
+ rsp = gdb.parse_and_eval("{}+8*8".format(context))
+ gdb.parse_and_eval("$rip = {}".format(rip))
+ gdb.parse_and_eval("$rsp = {}".format(rsp))
+ gdb.parse_and_eval("$rbp = {}".format(rbp))
+
+class BthreadRegsCmd(gdb.Command):
+ """bthread_regs <id>, print bthread registers"""
+ def __init__(self):
+ gdb.Command.__init__(self, "bthread_regs", gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, tty):
+ global status
+ global bthreads
+ if not status:
+ print("Not in bthread debug mode")
+ return
+ if not arg:
+ print("bthread_regs <id>, see 'bthread_list'")
+ return
+ bthread_id = int(arg)
+ if bthread_id >= len(bthreads):
+ print("id {} exceeds max bthread nums {}".format(bthread_id,
len(bthreads)))
+ return
+ stack = bthreads[bthread_id]["stack"]
+ if str(stack) == "0x0":
+ print("this bthread has no stack")
+ return
+ context = gdb.parse_and_eval("(*(('bthread::ContextualStack'
*){})).context".format(stack))
+ rip = int(gdb.parse_and_eval("*(uint64_t*)({}+7*8)".format(context)))
+ rbp = int(gdb.parse_and_eval("*(uint64_t*)({}+6*8)".format(context)))
+ rbx = int(gdb.parse_and_eval("*(uint64_t*)({}+5*8)".format(context)))
+ r15 = int(gdb.parse_and_eval("*(uint64_t*)({}+4*8)".format(context)))
+ r14 = int(gdb.parse_and_eval("*(uint64_t*)({}+3*8)".format(context)))
+ r13 = int(gdb.parse_and_eval("*(uint64_t*)({}+2*8)".format(context)))
+ r12 = int(gdb.parse_and_eval("*(uint64_t*)({}+1*8)".format(context)))
+ rsp = int(gdb.parse_and_eval("{}+8*8".format(context)))
+ print("rip: 0x{:x}\nrsp: 0x{:x}\nrbp: 0x{:x}\nrbx: 0x{:x}\nr15:
0x{:x}\nr14: 0x{:x}\nr13: 0x{:x}\nr12: 0x{:x}".format(rip, rsp, rbp, rbx, r15,
r14, r13, r12))
+
+class BthreadMetaCmd(gdb.Command):
+ """bthread_meta <id>, print task meta by id"""
+ def __init__(self):
+ gdb.Command.__init__(self, "bthread_meta", gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, tty):
+ global status
+ global bthreads
+ if not status:
+ print("Not in bthread debug mode")
+ return
+ if not arg:
+ print("bthread_meta <id>, see 'bthread_list'")
+ return
+ bthread_id = int(arg)
+ if bthread_id >= len(bthreads):
+ print("id {} exceeds max bthread nums {}".format(bthread_id,
len(bthreads)))
+ return
+ print(bthreads[bthread_id])
+
+class BthreadBeginCmd(gdb.Command):
+ """enter bthread debug mode"""
+ def __init__(self):
+ gdb.Command.__init__(self, "bthread_begin", gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, tty):
+ global status
+ if status:
+ print("Already in bthread debug mode, do not switch thread before
exec 'bthread_end' !!!")
+ return
+ active_bthreads = get_bthread_num()
+ scanned_bthreads = active_bthreads
+ if arg:
+ num_arg = int(arg)
+ if num_arg < active_bthreads:
+ scanned_bthreads = num_arg
+ else:
+ print("requested bthreads {} more than actived, will display
{} bthreads".format(num_arg, scanned_bthreads))
+ print("Active bthreads: {}, will display {}
bthreads".format(active_bthreads, scanned_bthreads))
+ get_all_bthreads(scanned_bthreads)
+ gdb.parse_and_eval("$saved_rip = $rip")
+ gdb.parse_and_eval("$saved_rsp = $rsp")
+ gdb.parse_and_eval("$saved_rbp = $rbp")
+ status = True
+ print("Enter bthread debug mode, do not switch thread before exec
'bthread_end' !!!")
+
+class BthreadRegRestoreCmd(gdb.Command):
+ """restore registers"""
+ def __init__(self):
+ gdb.Command.__init__(self, "bthread_reg_restore", gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, tty):
+ global status
+ if not status:
+ print("Not in bthread debug mode")
+ return
+ gdb.parse_and_eval("$rip = $saved_rip")
+ gdb.parse_and_eval("$rsp = $saved_rsp")
+ gdb.parse_and_eval("$rbp = $saved_rbp")
+ print("OK")
+
+class BthreadEndCmd(gdb.Command):
+ """exit bthread debug mode"""
+ def __init__(self):
+ gdb.Command.__init__(self, "bthread_end", gdb.COMMAND_STACK,
gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, tty):
+ global status
+ if not status:
+ print("Not in bthread debug mode")
+ return
+ gdb.parse_and_eval("$rip = $saved_rip")
+ gdb.parse_and_eval("$rsp = $saved_rsp")
+ gdb.parse_and_eval("$rbp = $saved_rbp")
+ status = False
+ print("Exit bthread debug mode")
+
+BthreadListCmd()
+BthreadNumCmd()
+BthreadBeginCmd()
+BthreadEndCmd()
+BthreadFrameCmd()
+BthreadMetaCmd()
+BthreadRegRestoreCmd()
+BthreadRegsCmd()
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]