mirror of
https://github.com/scylladb/scylladb.git
synced 2026-05-31 12:06:44 +00:00
Merge "Add more tools to the GDB script" from Tomasz
This commit is contained in:
167
scylla-gdb.py
167
scylla-gdb.py
@@ -211,19 +211,26 @@ class scylla_memory(gdb.Command):
|
||||
front = int(span['link']['_next'])
|
||||
gdb.write('{index:5} {size:13} {total}\n'.format(index=index, size=(1<<index)*page_size, total=total*page_size))
|
||||
|
||||
def get_seastar_memory_start_and_size():
|
||||
cpu_mem = gdb.parse_and_eval('memory::cpu_mem')
|
||||
page_size = int(gdb.parse_and_eval('memory::page_size'))
|
||||
total_mem = int(cpu_mem['nr_pages']) * page_size
|
||||
start = int(cpu_mem['memory'])
|
||||
return start, total_mem
|
||||
|
||||
def seastar_memory_layout():
|
||||
orig = gdb.selected_thread()
|
||||
results = []
|
||||
for t in gdb.selected_inferior().threads():
|
||||
t.switch()
|
||||
cpu_mem = gdb.parse_and_eval('memory::cpu_mem')
|
||||
page_size = int(gdb.parse_and_eval('memory::page_size'))
|
||||
total_mem = int(cpu_mem['nr_pages']) * page_size
|
||||
start = int(cpu_mem['memory'])
|
||||
for t in reactor_threads():
|
||||
start, total_mem = get_seastar_memory_start_and_size()
|
||||
results.append((t, start, total_mem))
|
||||
orig.switch()
|
||||
return results
|
||||
|
||||
def get_thread_owning_memory(ptr):
|
||||
for t in reactor_threads():
|
||||
start, size = get_seastar_memory_start_and_size()
|
||||
if start <= ptr < start + size:
|
||||
return t
|
||||
|
||||
class scylla_ptr(gdb.Command):
|
||||
def __init__(self):
|
||||
gdb.Command.__init__(self, 'scylla ptr', gdb.COMMAND_USER, gdb.COMPLETE_COMMAND)
|
||||
@@ -362,6 +369,19 @@ class scylla_timers(gdb.Command):
|
||||
for t in intrusive_list(timer_list):
|
||||
gdb.write('(%s*) %s = %s\n' % (t.type, t.address, t))
|
||||
|
||||
def has_reactor():
|
||||
if gdb.parse_and_eval('local_engine'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def reactor_threads():
|
||||
orig = gdb.selected_thread()
|
||||
for t in gdb.selected_inferior().threads():
|
||||
t.switch()
|
||||
if has_reactor():
|
||||
yield t
|
||||
orig.switch()
|
||||
|
||||
def reactors():
|
||||
orig = gdb.selected_thread()
|
||||
for t in gdb.selected_inferior().threads():
|
||||
@@ -401,6 +421,134 @@ class scylla_mem_ranges(gdb.Command):
|
||||
for t, start, total_mem in seastar_memory_layout():
|
||||
gdb.write('0x%x +%d\n' % (start, total_mem))
|
||||
|
||||
class scylla_mem_range(gdb.Command):
|
||||
def __init__(self):
|
||||
gdb.Command.__init__(self, 'scylla mem-range', gdb.COMMAND_USER, gdb.COMPLETE_NONE)
|
||||
def invoke(self, arg, from_tty):
|
||||
if not has_reactor():
|
||||
gdb.write('Not a reactor thread')
|
||||
return
|
||||
gdb.write('0x%x +%d\n' % get_seastar_memory_start_and_size())
|
||||
|
||||
class thread_switched_in(object):
|
||||
def __init__(self, gdb_thread):
|
||||
self.new = gdb_thread
|
||||
def __enter__(self):
|
||||
self.old = gdb.selected_thread()
|
||||
self.new.switch()
|
||||
def __exit__(self, *_):
|
||||
self.old.switch()
|
||||
|
||||
class seastar_thread_context(object):
|
||||
ulong_type = gdb.lookup_type('unsigned long')
|
||||
|
||||
# FIXME: The jmpbuf interpreting code targets x86_64 and glibc 2.19
|
||||
# Offsets taken from sysdeps/x86_64/jmpbuf-offsets.h.
|
||||
jmpbuf_offsets = {
|
||||
'rbx': 0,
|
||||
'rbp': 1,
|
||||
'r12': 2,
|
||||
'r13': 3,
|
||||
'r14': 4,
|
||||
'r15': 5,
|
||||
'rsp': 6,
|
||||
'rip': 7,
|
||||
}
|
||||
mangled_registers = ['rip', 'rsp', 'rbp']
|
||||
|
||||
def save_regs(self):
|
||||
result = {}
|
||||
for reg in self.jmpbuf_offsets.keys():
|
||||
result[reg] = gdb.parse_and_eval('$%s' % reg).cast(self.ulong_type)
|
||||
return result
|
||||
|
||||
def restore_regs(self, values):
|
||||
gdb.newest_frame().select()
|
||||
for reg, value in values.items():
|
||||
gdb.execute('set $%s = %s' % (reg, value))
|
||||
|
||||
def get_fs_base(self):
|
||||
holder_addr = get_seastar_memory_start_and_size()[0]
|
||||
holder = gdb.Value(holder_addr).reinterpret_cast(self.ulong_type.pointer())
|
||||
saved = holder.dereference()
|
||||
gdb.execute('set *(void**)%s = 0' % holder_addr)
|
||||
if gdb.parse_and_eval('arch_prctl(0x1003, %d)' % holder_addr) != 0:
|
||||
raise Exception('arch_prctl() failed')
|
||||
fs_base = holder.dereference()
|
||||
gdb.execute('set *(void**)%s = %s' % (holder_addr, saved))
|
||||
return fs_base
|
||||
|
||||
def regs_from_jmpbuf(self, jmpbuf):
|
||||
canary = gdb.Value(self.get_fs_base()).reinterpret_cast(self.ulong_type.pointer())[6]
|
||||
result = {}
|
||||
for reg, offset in self.jmpbuf_offsets.items():
|
||||
value = jmpbuf['__jmpbuf'][offset].cast(self.ulong_type)
|
||||
if reg in self.mangled_registers:
|
||||
# glibc mangles by doing:
|
||||
# xor %reg, %fs:0x30
|
||||
# rol %reg, $0x11
|
||||
bits = 64
|
||||
shift = 0x11
|
||||
value = (value << (bits-shift)) & (2**bits-1) | (value >> shift)
|
||||
value = value ^ canary
|
||||
result[reg] = value
|
||||
return result
|
||||
|
||||
def is_switched_in(self):
|
||||
jmpbuf_link_ptr = gdb.parse_and_eval('seastar::g_current_context')
|
||||
if jmpbuf_link_ptr['thread'] == self.thread_ctx.address:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __init__(self, thread_ctx):
|
||||
self.thread_ctx = thread_ctx
|
||||
self.old_frame = gdb.selected_frame()
|
||||
self.old_regs = self.save_regs()
|
||||
self.old_gdb_thread = gdb.selected_thread()
|
||||
self.gdb_thread = get_thread_owning_memory(thread_ctx.address)
|
||||
self.new_regs = None
|
||||
|
||||
def __enter__(self):
|
||||
gdb.write('Switched to thread %d, (seastar::thread_context*) 0x%x\n' % (self.gdb_thread.num, self.thread_ctx.address))
|
||||
self.gdb_thread.switch()
|
||||
if not self.is_switched_in():
|
||||
self.new_regs = self.regs_from_jmpbuf(self.thread_ctx['_context']['jmpbuf'])
|
||||
self.restore_regs(self.new_regs)
|
||||
|
||||
def __exit__(self, *_):
|
||||
if self.new_regs:
|
||||
self.gdb_thread.switch()
|
||||
self.restore_regs(self.old_regs)
|
||||
self.old_gdb_thread.switch()
|
||||
self.old_frame.select()
|
||||
gdb.write('Switched to thread %d\n' % self.old_gdb_thread.num)
|
||||
|
||||
active_thread_context = None
|
||||
|
||||
def exit_thread_context():
|
||||
global active_thread_context
|
||||
if active_thread_context:
|
||||
active_thread_context.__exit__()
|
||||
active_thread_context = None
|
||||
|
||||
class scylla_thread(gdb.Command):
|
||||
def __init__(self):
|
||||
gdb.Command.__init__(self, 'scylla thread', gdb.COMMAND_USER,
|
||||
gdb.COMPLETE_COMMAND, True)
|
||||
def invoke(self, arg, for_tty):
|
||||
addr = gdb.parse_and_eval(arg)
|
||||
ctx = addr.reinterpret_cast(gdb.lookup_type('seastar::thread_context').pointer()).dereference()
|
||||
exit_thread_context()
|
||||
global active_thread_context
|
||||
active_thread_context = seastar_thread_context(ctx)
|
||||
active_thread_context.__enter__()
|
||||
|
||||
class scylla_unthread(gdb.Command):
|
||||
def __init__(self):
|
||||
gdb.Command.__init__(self, 'scylla unthread', gdb.COMMAND_USER, gdb.COMPLETE_NONE, True)
|
||||
def invoke(self, arg, for_tty):
|
||||
exit_thread_context()
|
||||
|
||||
scylla()
|
||||
scylla_databases()
|
||||
scylla_keyspaces()
|
||||
@@ -408,8 +556,11 @@ scylla_column_families()
|
||||
scylla_memory()
|
||||
scylla_ptr()
|
||||
scylla_mem_ranges()
|
||||
scylla_mem_range()
|
||||
scylla_lsa()
|
||||
scylla_lsa_zones()
|
||||
scylla_timers()
|
||||
scylla_apply()
|
||||
scylla_shard()
|
||||
scylla_thread()
|
||||
scylla_unthread()
|
||||
|
||||
Reference in New Issue
Block a user