2023-07-21 01:02:48 +03:00
|
|
|
import argparse
|
2023-03-31 19:13:33 +03:00
|
|
|
import textwrap
|
|
|
|
|
2023-08-03 02:43:11 +03:00
|
|
|
# usage: [-h] [-a | --all | --no-all] [-s STACK_SIZE] [uplevel]
|
2023-08-03 02:43:29 +03:00
|
|
|
#
|
2023-07-21 01:02:48 +03:00
|
|
|
# Dump a control frame
|
2023-08-03 02:43:29 +03:00
|
|
|
#
|
2023-07-21 01:02:48 +03:00
|
|
|
# positional arguments:
|
|
|
|
# uplevel CFP offset from the stack top
|
2023-08-03 02:43:29 +03:00
|
|
|
#
|
2023-07-21 01:02:48 +03:00
|
|
|
# options:
|
|
|
|
# -h, --help show this help message and exit
|
2023-08-03 02:43:11 +03:00
|
|
|
# -a, --all, --no-all dump all frames
|
2023-07-21 01:02:48 +03:00
|
|
|
# -s STACK_SIZE, --stack-size STACK_SIZE
|
|
|
|
# override stack_size (useful for JIT frames)
|
Put misc/gdb.py [experimental]
This works like:
```
(gdb) cfp
CFP (count=3, addr=0x7ffff73fef50):
$1 = {pc = 0x555556bf7818, sp = 0x7ffff72ff078, iseq = 0x7ffff2603270, self = 140737344619296, ep = 0x7ffff72ff058, block_code = 0x0,
__bp__ = 0x7ffff72ff060, jit_return = 0x555558c2b000}
Stack (size=3):
[0] FIXNUM: 1
[1] T_STRING: "" bytesize:0 (embed) encoding:1 coderange:7bit $2 = (struct RString *) 0x7ffff249ea80
[2] [PROMOTED] T_OBJECT: $3 = {flags = 21474844769, klass = 140737344040416}
$4 = {0x24, 0x24, 0x24}
(gdb) cfp + 1
CFP (count=3, addr=0x7ffff73fef90):
$5 = {pc = 0x5555567a78f8, sp = 0x7ffff72ff040, iseq = 0x7ffff26032d0, self = 140737344619296, ep = 0x7ffff72ff038, block_code = 0x0,
__bp__ = 0x7ffff72ff040, jit_return = 0x555558c2b000}
Stack (size=0):
```
2023-03-31 09:57:13 +03:00
|
|
|
class CFP(gdb.Command):
|
2023-03-31 12:54:24 +03:00
|
|
|
FRAME_MAGICS = [
|
|
|
|
# frame types
|
|
|
|
'VM_FRAME_MAGIC_METHOD',
|
|
|
|
'VM_FRAME_MAGIC_BLOCK',
|
|
|
|
'VM_FRAME_MAGIC_CLASS',
|
|
|
|
'VM_FRAME_MAGIC_TOP',
|
|
|
|
'VM_FRAME_MAGIC_CFUNC',
|
|
|
|
'VM_FRAME_MAGIC_IFUNC',
|
|
|
|
'VM_FRAME_MAGIC_EVAL',
|
|
|
|
'VM_FRAME_MAGIC_RESCUE',
|
|
|
|
'VM_FRAME_MAGIC_DUMMY',
|
|
|
|
]
|
|
|
|
FRAME_FLAGS = [
|
|
|
|
# frame flag
|
|
|
|
'VM_FRAME_FLAG_FINISH',
|
|
|
|
'VM_FRAME_FLAG_BMETHOD',
|
|
|
|
'VM_FRAME_FLAG_CFRAME',
|
|
|
|
'VM_FRAME_FLAG_LAMBDA',
|
|
|
|
'VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM',
|
|
|
|
'VM_FRAME_FLAG_CFRAME_KW',
|
|
|
|
'VM_FRAME_FLAG_PASSED',
|
|
|
|
# env flag
|
|
|
|
'VM_ENV_FLAG_LOCAL',
|
|
|
|
'VM_ENV_FLAG_ESCAPED',
|
|
|
|
'VM_ENV_FLAG_WB_REQUIRED',
|
|
|
|
'VM_ENV_FLAG_ISOLATED',
|
|
|
|
]
|
|
|
|
|
Put misc/gdb.py [experimental]
This works like:
```
(gdb) cfp
CFP (count=3, addr=0x7ffff73fef50):
$1 = {pc = 0x555556bf7818, sp = 0x7ffff72ff078, iseq = 0x7ffff2603270, self = 140737344619296, ep = 0x7ffff72ff058, block_code = 0x0,
__bp__ = 0x7ffff72ff060, jit_return = 0x555558c2b000}
Stack (size=3):
[0] FIXNUM: 1
[1] T_STRING: "" bytesize:0 (embed) encoding:1 coderange:7bit $2 = (struct RString *) 0x7ffff249ea80
[2] [PROMOTED] T_OBJECT: $3 = {flags = 21474844769, klass = 140737344040416}
$4 = {0x24, 0x24, 0x24}
(gdb) cfp + 1
CFP (count=3, addr=0x7ffff73fef90):
$5 = {pc = 0x5555567a78f8, sp = 0x7ffff72ff040, iseq = 0x7ffff26032d0, self = 140737344619296, ep = 0x7ffff72ff038, block_code = 0x0,
__bp__ = 0x7ffff72ff040, jit_return = 0x555558c2b000}
Stack (size=0):
```
2023-03-31 09:57:13 +03:00
|
|
|
def __init__(self):
|
|
|
|
super(CFP, self).__init__('cfp', gdb.COMMAND_USER)
|
|
|
|
|
2023-07-21 01:02:48 +03:00
|
|
|
self.parser = argparse.ArgumentParser(description='Dump a control frame')
|
|
|
|
self.parser.add_argument('uplevel', type=int, nargs='?', default=0, help='CFP offset from the stack top')
|
2023-08-03 02:43:11 +03:00
|
|
|
self.parser.add_argument('-a', '--all', action=argparse.BooleanOptionalAction, help='dump all frames')
|
2023-07-21 01:02:48 +03:00
|
|
|
self.parser.add_argument('-s', '--stack-size', type=int, help='override stack_size (useful for JIT frames)')
|
|
|
|
|
|
|
|
def invoke(self, args, from_tty):
|
|
|
|
try:
|
|
|
|
args = self.parser.parse_args(args.split())
|
|
|
|
except SystemExit:
|
|
|
|
return
|
|
|
|
cfp = f'(ruby_current_ec->cfp + ({args.uplevel}))'
|
2023-08-03 19:55:36 +03:00
|
|
|
end_cfp = self.get_int('ruby_current_ec->vm_stack + ruby_current_ec->vm_stack_size')
|
|
|
|
cfp_index = int((end_cfp - self.get_int(cfp) - 1) / self.get_int('sizeof(rb_control_frame_t)'))
|
Put misc/gdb.py [experimental]
This works like:
```
(gdb) cfp
CFP (count=3, addr=0x7ffff73fef50):
$1 = {pc = 0x555556bf7818, sp = 0x7ffff72ff078, iseq = 0x7ffff2603270, self = 140737344619296, ep = 0x7ffff72ff058, block_code = 0x0,
__bp__ = 0x7ffff72ff060, jit_return = 0x555558c2b000}
Stack (size=3):
[0] FIXNUM: 1
[1] T_STRING: "" bytesize:0 (embed) encoding:1 coderange:7bit $2 = (struct RString *) 0x7ffff249ea80
[2] [PROMOTED] T_OBJECT: $3 = {flags = 21474844769, klass = 140737344040416}
$4 = {0x24, 0x24, 0x24}
(gdb) cfp + 1
CFP (count=3, addr=0x7ffff73fef90):
$5 = {pc = 0x5555567a78f8, sp = 0x7ffff72ff040, iseq = 0x7ffff26032d0, self = 140737344619296, ep = 0x7ffff72ff038, block_code = 0x0,
__bp__ = 0x7ffff72ff040, jit_return = 0x555558c2b000}
Stack (size=0):
```
2023-03-31 09:57:13 +03:00
|
|
|
|
2023-08-03 02:43:11 +03:00
|
|
|
if args.all:
|
|
|
|
cfp_count = int((end_cfp - self.get_int('ruby_current_ec->cfp')) / self.get_int('sizeof(rb_control_frame_t)')) - 1 # exclude dummy CFP
|
|
|
|
for i in range(cfp_count):
|
|
|
|
print('-' * 80)
|
|
|
|
self.invoke(str(cfp_count - i - 1), from_tty)
|
|
|
|
return
|
|
|
|
|
2023-08-03 19:55:36 +03:00
|
|
|
print('CFP (addr=0x{:x}, index={}):'.format(self.get_int(cfp), cfp_index))
|
Put misc/gdb.py [experimental]
This works like:
```
(gdb) cfp
CFP (count=3, addr=0x7ffff73fef50):
$1 = {pc = 0x555556bf7818, sp = 0x7ffff72ff078, iseq = 0x7ffff2603270, self = 140737344619296, ep = 0x7ffff72ff058, block_code = 0x0,
__bp__ = 0x7ffff72ff060, jit_return = 0x555558c2b000}
Stack (size=3):
[0] FIXNUM: 1
[1] T_STRING: "" bytesize:0 (embed) encoding:1 coderange:7bit $2 = (struct RString *) 0x7ffff249ea80
[2] [PROMOTED] T_OBJECT: $3 = {flags = 21474844769, klass = 140737344040416}
$4 = {0x24, 0x24, 0x24}
(gdb) cfp + 1
CFP (count=3, addr=0x7ffff73fef90):
$5 = {pc = 0x5555567a78f8, sp = 0x7ffff72ff040, iseq = 0x7ffff26032d0, self = 140737344619296, ep = 0x7ffff72ff038, block_code = 0x0,
__bp__ = 0x7ffff72ff040, jit_return = 0x555558c2b000}
Stack (size=0):
```
2023-03-31 09:57:13 +03:00
|
|
|
gdb.execute(f'p *({cfp})')
|
|
|
|
print()
|
|
|
|
|
2023-04-01 10:19:27 +03:00
|
|
|
if self.get_int(f'{cfp}->iseq'):
|
|
|
|
local_size = self.get_int(f'{cfp}->iseq->body->local_table_size - {cfp}->iseq->body->param.size')
|
|
|
|
param_size = self.get_int(f'{cfp}->iseq->body->param.size')
|
2023-03-31 13:05:29 +03:00
|
|
|
|
2023-08-03 02:43:11 +03:00
|
|
|
if local_size:
|
|
|
|
print(f'Params (size={param_size}):')
|
|
|
|
for i in range(-3 - local_size - param_size, -3 - local_size):
|
|
|
|
self.print_stack(cfp, i, self.rp(cfp, i))
|
|
|
|
print()
|
|
|
|
|
|
|
|
if param_size:
|
|
|
|
print(f'Locals (size={local_size}):')
|
|
|
|
for i in range(-3 - local_size, -3):
|
|
|
|
self.print_stack(cfp, i, self.rp(cfp, i))
|
|
|
|
print()
|
2023-03-31 13:05:29 +03:00
|
|
|
|
2023-03-31 12:54:24 +03:00
|
|
|
print('Env:')
|
2023-08-03 02:23:17 +03:00
|
|
|
self.print_env(cfp, -3, self.rp_env(cfp, -3))
|
|
|
|
self.print_env(cfp, -2, self.specval(cfp, -2))
|
|
|
|
self.print_env(cfp, -1, self.frame_types(cfp, -1))
|
2023-03-31 12:54:24 +03:00
|
|
|
print()
|
|
|
|
|
2023-08-22 21:49:36 +03:00
|
|
|
# We can't calculate BP for the first frame.
|
|
|
|
# vm_base_ptr doesn't work for C frames either.
|
|
|
|
if cfp_index > 0 and self.get_int(f'{cfp}->iseq'):
|
2023-08-03 19:55:36 +03:00
|
|
|
if args.stack_size is not None:
|
|
|
|
stack_size = args.stack_size
|
|
|
|
else:
|
|
|
|
stack_size = int((self.get_int(f'{cfp}->sp') - self.get_int(f'vm_base_ptr({cfp})')) / 8)
|
|
|
|
print(f'Stack (size={stack_size}):')
|
|
|
|
for i in range(0, stack_size):
|
|
|
|
self.print_stack(cfp, i, self.rp(cfp, i))
|
|
|
|
print(self.regs(cfp, stack_size))
|
2023-03-31 12:54:24 +03:00
|
|
|
|
2023-08-03 02:23:17 +03:00
|
|
|
def print_env(self, cfp, bp_index, content):
|
|
|
|
ep_index = bp_index + 1
|
|
|
|
address = self.get_int(f'((rb_control_frame_t *){cfp})->ep + {ep_index}')
|
|
|
|
value = self.get_env(cfp, bp_index)
|
|
|
|
regs = self.regs(cfp, bp_index)
|
|
|
|
if content:
|
|
|
|
content = textwrap.indent(content, ' ' * 3).lstrip() # Leave the regs column empty
|
|
|
|
content = f'{content} '
|
|
|
|
print('{:2} 0x{:x} [{}] {}(0x{:x})'.format(regs, address, bp_index, content, value))
|
|
|
|
|
2023-03-31 12:54:24 +03:00
|
|
|
def print_stack(self, cfp, bp_index, content):
|
2023-07-20 21:54:47 +03:00
|
|
|
address = self.get_int(f'vm_base_ptr({cfp}) + {bp_index}')
|
2023-03-31 13:20:40 +03:00
|
|
|
value = self.get_value(cfp, bp_index)
|
2023-03-31 19:13:33 +03:00
|
|
|
regs = self.regs(cfp, bp_index)
|
2023-03-31 13:20:40 +03:00
|
|
|
if content:
|
2023-03-31 19:13:33 +03:00
|
|
|
content = textwrap.indent(content, ' ' * 3).lstrip() # Leave the regs column empty
|
2023-03-31 13:20:40 +03:00
|
|
|
content = f'{content} '
|
2023-03-31 19:13:33 +03:00
|
|
|
print('{:2} 0x{:x} [{}] {}(0x{:x})'.format(regs, address, bp_index, content, value))
|
|
|
|
|
|
|
|
def regs(self, cfp, bp_index):
|
2023-07-20 21:54:47 +03:00
|
|
|
address = self.get_int(f'vm_base_ptr({cfp}) + {bp_index}')
|
2023-03-31 19:13:33 +03:00
|
|
|
regs = []
|
2023-07-20 21:54:47 +03:00
|
|
|
for reg, field in { 'EP': 'ep', 'SP': 'sp' }.items():
|
2023-03-31 19:13:33 +03:00
|
|
|
if address == self.get_int(f'{cfp}->{field}'):
|
|
|
|
regs.append(reg)
|
|
|
|
return ' '.join(regs)
|
2023-03-31 12:54:24 +03:00
|
|
|
|
|
|
|
def rp(self, cfp, bp_index):
|
|
|
|
value = self.get_value(cfp, bp_index)
|
|
|
|
return self.get_string(f'rp {value}').rstrip()
|
|
|
|
|
2023-08-03 02:23:17 +03:00
|
|
|
def rp_env(self, cfp, bp_index):
|
|
|
|
value = self.get_env(cfp, bp_index)
|
|
|
|
return self.get_string(f'rp {value}').rstrip()
|
|
|
|
|
2023-03-31 12:54:24 +03:00
|
|
|
# specval: block_handler or previous EP
|
|
|
|
def specval(self, cfp, bp_index):
|
2023-08-03 02:23:17 +03:00
|
|
|
value = self.get_env(cfp, bp_index)
|
2023-04-01 10:19:43 +03:00
|
|
|
if value == 0:
|
|
|
|
return 'VM_BLOCK_HANDLER_NONE'
|
|
|
|
if value == self.get_int('rb_block_param_proxy'):
|
|
|
|
return 'rb_block_param_proxy'
|
2023-03-31 13:20:40 +03:00
|
|
|
return ''
|
2023-03-31 12:54:24 +03:00
|
|
|
|
|
|
|
def frame_types(self, cfp, bp_index):
|
|
|
|
types = []
|
2023-08-03 02:23:17 +03:00
|
|
|
value = self.get_env(cfp, bp_index)
|
2023-03-31 12:54:24 +03:00
|
|
|
|
|
|
|
magic_mask = self.get_int('VM_FRAME_MAGIC_MASK')
|
|
|
|
for magic in self.FRAME_MAGICS:
|
|
|
|
magic_value = self.get_int(magic)
|
|
|
|
if value & magic_mask == magic_value:
|
|
|
|
types.append(magic)
|
|
|
|
|
|
|
|
for flag in self.FRAME_FLAGS:
|
|
|
|
flag_value = self.get_int(flag)
|
|
|
|
if value & flag_value:
|
|
|
|
types.append(flag)
|
|
|
|
|
2023-03-31 13:20:40 +03:00
|
|
|
return ' | '.join(types)
|
2023-03-31 12:54:24 +03:00
|
|
|
|
2023-08-03 02:23:17 +03:00
|
|
|
def get_env(self, cfp, bp_index):
|
|
|
|
ep_index = bp_index + 1
|
|
|
|
return self.get_int(f'((rb_control_frame_t *){cfp})->ep[{ep_index}]')
|
|
|
|
|
2023-03-31 12:54:24 +03:00
|
|
|
def get_value(self, cfp, bp_index):
|
2023-07-20 21:54:47 +03:00
|
|
|
return self.get_int(f'vm_base_ptr({cfp})[{bp_index}]')
|
Put misc/gdb.py [experimental]
This works like:
```
(gdb) cfp
CFP (count=3, addr=0x7ffff73fef50):
$1 = {pc = 0x555556bf7818, sp = 0x7ffff72ff078, iseq = 0x7ffff2603270, self = 140737344619296, ep = 0x7ffff72ff058, block_code = 0x0,
__bp__ = 0x7ffff72ff060, jit_return = 0x555558c2b000}
Stack (size=3):
[0] FIXNUM: 1
[1] T_STRING: "" bytesize:0 (embed) encoding:1 coderange:7bit $2 = (struct RString *) 0x7ffff249ea80
[2] [PROMOTED] T_OBJECT: $3 = {flags = 21474844769, klass = 140737344040416}
$4 = {0x24, 0x24, 0x24}
(gdb) cfp + 1
CFP (count=3, addr=0x7ffff73fef90):
$5 = {pc = 0x5555567a78f8, sp = 0x7ffff72ff040, iseq = 0x7ffff26032d0, self = 140737344619296, ep = 0x7ffff72ff038, block_code = 0x0,
__bp__ = 0x7ffff72ff040, jit_return = 0x555558c2b000}
Stack (size=0):
```
2023-03-31 09:57:13 +03:00
|
|
|
|
|
|
|
def get_int(self, expr):
|
|
|
|
return int(self.get_string(f'printf "%ld", ({expr})'))
|
|
|
|
|
|
|
|
def get_string(self, expr):
|
|
|
|
return gdb.execute(expr, to_string=True)
|
|
|
|
|
|
|
|
CFP()
|