chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Bthread Stack Print Tool
|
||||
|
||||
this only for running process, core dump is not supported.
|
||||
|
||||
Get Started:
|
||||
1. lldb attach -p <pid>
|
||||
2. command script import lldb_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
|
||||
9. bthread_all: print all bthread frames
|
||||
|
||||
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 lldb
|
||||
|
||||
|
||||
class GlobalState():
|
||||
def __init__(self):
|
||||
self.started: bool = False
|
||||
self.bthreads: list = []
|
||||
self.saved_regs: dict = {}
|
||||
|
||||
def reset(self) -> None:
|
||||
self.started = False
|
||||
self.bthreads.clear()
|
||||
|
||||
def get_bthread(self, idx_str: str) -> lldb.SBValue:
|
||||
if not self.started:
|
||||
print("Not in bthread debug mode")
|
||||
return None
|
||||
if len(idx_str) == 0:
|
||||
print("bthread_frame <id>, see 'bthread_list'")
|
||||
try:
|
||||
bthread_idx = int(idx_str)
|
||||
except ValueError:
|
||||
print("please input a valid interger.")
|
||||
return None
|
||||
|
||||
if bthread_idx >= len(self.bthreads):
|
||||
print("id {} exceeds max bthread nums {}".format(
|
||||
bthread_idx, len(self.bthreads)))
|
||||
return None
|
||||
return self.bthreads[bthread_idx]
|
||||
|
||||
|
||||
global_state = GlobalState()
|
||||
|
||||
|
||||
def get_child(value: lldb.SBValue, childs_value_name: str) -> lldb.SBValue:
|
||||
r"""get child value by value name str split by '.'"""
|
||||
result = value
|
||||
childs_value_list = childs_value_name.split('.')
|
||||
for child_value_name in childs_value_list:
|
||||
result = result.GetChildMemberWithName(child_value_name)
|
||||
return result
|
||||
|
||||
|
||||
def find_global_value(target: lldb.SBTarget, value_name: str) -> lldb.SBValue:
|
||||
r""" find global value by value name"""
|
||||
name_list = value_name.split('.')
|
||||
root_name = name_list[0]
|
||||
root_value = target.FindGlobalVariables(
|
||||
root_name, 1, lldb.eMatchTypeNormal)[0]
|
||||
if (len(name_list) == 1):
|
||||
return root_value
|
||||
return get_child(root_value, '.'.join(name_list[1:]))
|
||||
|
||||
|
||||
def get_bthreads_num(target: lldb.SBTarget):
|
||||
root_agent = find_global_value(
|
||||
target, "bthread::g_task_control._nbthreads._combiner._agents.root_")
|
||||
global_res = find_global_value(
|
||||
target, "bthread::g_task_control._nbthreads._combiner._global_result").GetValueAsSigned()
|
||||
long_type = target.GetBasicType(lldb.eBasicTypeLong)
|
||||
|
||||
last_node = root_agent
|
||||
# agent_type: bvar::detail::AgentCombiner<long, long, bvar::detail::AddTo<long> >::Agent>
|
||||
agent_type: lldb.SBType = last_node.GetType().GetTemplateArgumentType(0)
|
||||
while True:
|
||||
agent = last_node.Cast(agent_type)
|
||||
if (last_node.GetLocation() != root_agent.GetLocation()):
|
||||
val = get_child(agent, "element._value").Cast(
|
||||
long_type).GetValueAsSigned()
|
||||
global_res += val
|
||||
if (get_child(agent, "next_").Dereference().GetLocation() == root_agent.GetLocation()):
|
||||
return global_res
|
||||
last_node = get_child(agent, "next_").Dereference()
|
||||
|
||||
|
||||
def get_all_bthreads(target: lldb.SBTarget, total: int):
|
||||
bthreads = []
|
||||
groups = find_global_value(
|
||||
target, "butil::ResourcePool<bthread::TaskMeta>::_ngroup.val").GetValueAsUnsigned()
|
||||
long_type = target.GetBasicType(lldb.eBasicTypeLong)
|
||||
uint32_t_type = target.FindFirstType("uint32_t")
|
||||
block_groups = find_global_value(
|
||||
target, "butil::ResourcePool<bthread::TaskMeta>::_block_groups")
|
||||
for group in range(groups):
|
||||
block_group = get_child(
|
||||
block_groups.GetChildAtIndex(group), "val").Dereference()
|
||||
nblock = get_child(block_group, "nblock").Cast(
|
||||
long_type).GetValueAsUnsigned()
|
||||
blocks = get_child(block_group, "blocks")
|
||||
for block in range(nblock):
|
||||
# block_type: butil::ResourcePool<bthread::TaskMeta>::Block
|
||||
block_type = blocks.GetChildAtIndex(
|
||||
block).GetType().GetTemplateArgumentType(0)
|
||||
block = blocks.GetChildAtIndex(
|
||||
block).Cast(block_type).Dereference()
|
||||
nitem = get_child(block, "nitem").GetValueAsUnsigned()
|
||||
task_meta_array_type = target.FindFirstType(
|
||||
"bthread::TaskMeta").GetArrayType(nitem)
|
||||
tasks = get_child(block, "items").Cast(task_meta_array_type)
|
||||
for i in range(nitem):
|
||||
task_meta = tasks.GetChildAtIndex(i)
|
||||
version_tid = get_child(
|
||||
task_meta, "tid").GetValueAsUnsigned() >> 32
|
||||
version_butex = get_child(task_meta, "version_butex").Cast(
|
||||
uint32_t_type.GetPointerType()).Dereference().GetValueAsUnsigned()
|
||||
# stack_type: bthread::ContextualStack
|
||||
stack_type = get_child(
|
||||
task_meta, "attr.stack_type").GetValueAsUnsigned()
|
||||
if version_tid == version_butex and stack_type != 0:
|
||||
if len(bthreads) >= total:
|
||||
return bthreads
|
||||
bthreads.append(task_meta)
|
||||
return bthreads
|
||||
|
||||
# lldb bthread commands
|
||||
def bthread_begin(debugger, command, result, internal_dict):
|
||||
if global_state.started:
|
||||
print("Already in bthread debug mode, do not switch thread before exec 'bthread_end' !!!")
|
||||
return
|
||||
target = debugger.GetSelectedTarget()
|
||||
active_bthreads = get_bthreads_num(target)
|
||||
|
||||
if len(command) == 0:
|
||||
request_bthreds = active_bthreads
|
||||
else:
|
||||
try:
|
||||
request_bthreds = int(command)
|
||||
except ValueError:
|
||||
print("please input a valid interger.")
|
||||
return
|
||||
|
||||
scanned_bthreds = active_bthreads
|
||||
if request_bthreds > active_bthreads:
|
||||
print("requested bthreads {} more than actived, will display {} bthreads".format(
|
||||
request_bthreds, active_bthreads))
|
||||
else:
|
||||
scanned_bthreds = request_bthreds
|
||||
print("Active bthreads: {}, will display {} bthreads".format(
|
||||
active_bthreads, scanned_bthreds))
|
||||
global_state.bthreads = get_all_bthreads(target, scanned_bthreds)
|
||||
|
||||
# backup registers
|
||||
current_frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
|
||||
saved_regs = dict()
|
||||
saved_regs["rip"] = current_frame.FindRegister("rip").GetValueAsUnsigned()
|
||||
saved_regs["rsp"] = current_frame.FindRegister("rsp").GetValueAsUnsigned()
|
||||
saved_regs["rbp"] = current_frame.FindRegister("rbp").GetValueAsUnsigned()
|
||||
global_state.saved_regs = saved_regs
|
||||
|
||||
global_state.started = True
|
||||
print("Enter bthread debug mode, do not switch thread before exec 'bthread_end' !!!")
|
||||
|
||||
|
||||
def bthread_list(debugger, command, result, internal_dict):
|
||||
r"""list all bthreads, print format is 'id\ttid\tfunction\thas stack'"""
|
||||
if not global_state.started:
|
||||
print("Not in bthread debug mode")
|
||||
return
|
||||
|
||||
print("id\t\ttid\t\tfunction\t\t\t\thas stack\t\t\ttotal:{}".format(
|
||||
len(global_state.bthreads)))
|
||||
for i, t in enumerate(global_state.bthreads):
|
||||
tid = get_child(t, "tid").GetValueAsUnsigned()
|
||||
fn = get_child(t, "fn")
|
||||
has_stack = get_child(t, "stack").GetLocation() == "0x0"
|
||||
print("#{}\t\t{}\t\t{}\t\t{}".format(
|
||||
i, tid, fn, "no" if has_stack else "yes"))
|
||||
|
||||
|
||||
def bthread_num(debugger, command, result, internal_dict):
|
||||
r"""list active bthreads num"""
|
||||
if not global_state.started:
|
||||
print("Not in bthread debug mode")
|
||||
return
|
||||
|
||||
target = debugger.GetSelectedTarget()
|
||||
active_bthreads = get_bthreads_num(target)
|
||||
print(active_bthreads)
|
||||
|
||||
|
||||
def bthread_frame(debugger, command, result, internal_dict):
|
||||
r"""bthread_frame <id>, select bthread frame by id"""
|
||||
bthread = global_state.get_bthread(command)
|
||||
if bthread is None:
|
||||
return
|
||||
|
||||
stack = bthread.GetChildMemberWithName("stack")
|
||||
context = stack.Dereference().GetChildMemberWithName("context")
|
||||
|
||||
target = debugger.GetSelectedTarget()
|
||||
uint64_t_type = target.FindFirstType("uint64_t")
|
||||
target = debugger.GetSelectedTarget()
|
||||
|
||||
rip = target.CreateValueFromAddress("rip", lldb.SBAddress(
|
||||
context.GetValueAsUnsigned() + 7*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
rbp = target.CreateValueFromAddress("rbp", lldb.SBAddress(
|
||||
context.GetValueAsUnsigned() + 6*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
rsp = context.GetValueAsUnsigned() + 8*8
|
||||
|
||||
debugger.HandleCommand(f"register write rip {rip}")
|
||||
debugger.HandleCommand(f"register write rbp {rbp}")
|
||||
debugger.HandleCommand(f"register write rsp {rsp}")
|
||||
|
||||
|
||||
def bthread_all(debugger, command, result, internal_dict):
|
||||
r"""print all bthread frames"""
|
||||
if not global_state.started:
|
||||
print("Not in bthread debug mode")
|
||||
return
|
||||
|
||||
bthreads = global_state.bthreads
|
||||
bthread_num = len(bthreads)
|
||||
for i in range(bthread_num):
|
||||
bthread_frame(debugger, str(i), result, internal_dict)
|
||||
debugger.HandleCommand("bt")
|
||||
|
||||
|
||||
def bthread_meta(debugger, command, result, internal_dict):
|
||||
r"""bthread_meta <id>, print task meta by id"""
|
||||
bthread = global_state.get_bthread(command)
|
||||
if bthread is None:
|
||||
return
|
||||
print(bthread)
|
||||
|
||||
|
||||
def bthread_regs(debugger, command, result, internal_dict):
|
||||
r"""bthread_regs <id>, print bthread registers"""
|
||||
bthread = global_state.get_bthread(command)
|
||||
if bthread is None:
|
||||
return
|
||||
target = debugger.GetSelectedTarget()
|
||||
stack = get_child(bthread, "stack").Dereference()
|
||||
context = get_child(stack, "context")
|
||||
ctx_addr = context.GetValueAsUnsigned()
|
||||
uint64_t_type = target.FindFirstType("uint64_t")
|
||||
|
||||
rip = target.CreateValueFromAddress("rip", lldb.SBAddress(
|
||||
ctx_addr + 7*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
rbp = target.CreateValueFromAddress("rbp", lldb.SBAddress(
|
||||
ctx_addr + 6*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
rbx = target.CreateValueFromAddress("rbx", lldb.SBAddress(
|
||||
ctx_addr + 5*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
r15 = target.CreateValueFromAddress("r15", lldb.SBAddress(
|
||||
ctx_addr + 4*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
r14 = target.CreateValueFromAddress("r14", lldb.SBAddress(
|
||||
ctx_addr + 3*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
r13 = target.CreateValueFromAddress("r13", lldb.SBAddress(
|
||||
ctx_addr + 2*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
r12 = target.CreateValueFromAddress("r12", lldb.SBAddress(
|
||||
ctx_addr + 1*8, target), uint64_t_type).GetValueAsUnsigned()
|
||||
rsp = ctx_addr + 8*8
|
||||
|
||||
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))
|
||||
|
||||
|
||||
def bthread_reg_restore(debugger, command, result, internal_dict):
|
||||
r"""restore registers"""
|
||||
if not global_state.started:
|
||||
print("Not in bthread debug mode")
|
||||
return
|
||||
for reg_name, reg_value in global_state.saved_regs.items():
|
||||
debugger.HandleCommand(f"register write {reg_name} {reg_value}")
|
||||
|
||||
|
||||
def bthread_end(debugger, command, result, internal_dict):
|
||||
r"""exit bthread debug mode"""
|
||||
if not global_state.started:
|
||||
print("Not in bthread debug mode")
|
||||
return
|
||||
bthread_reg_restore(debugger, command, result, internal_dict)
|
||||
global_state.reset()
|
||||
print("Exit bthread debug mode")
|
||||
|
||||
|
||||
# And the initialization code to add commands.
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_begin bthread_begin')
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_list bthread_list')
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_frame bthread_frame')
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_num bthread_num')
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_all bthread_all')
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_meta bthread_meta')
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_regs bthread_regs')
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_reg_restore bthread_reg_restore')
|
||||
debugger.HandleCommand(
|
||||
'command script add -f lldb_bthread_stack.bthread_end bthread_end')
|
||||
Reference in New Issue
Block a user