summaryrefslogtreecommitdiff
path: root/misc/gdb.py
blob: 6f26fc1111e54a2d3d87f1373615f72698533c95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# Usage:
#   cfp: Dump the current cfp
#   cfp + 1: Dump the caller cfp
class CFP(gdb.Command):
    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',
    ]

    def __init__(self):
        super(CFP, self).__init__('cfp', gdb.COMMAND_USER)

    def invoke(self, offset, from_tty):
        if not offset:
            offset = '0'
        cfp = f'(ruby_current_ec->cfp + ({offset}))'

        end_cfp = self.get_int('ruby_current_ec->vm_stack + ruby_current_ec->vm_stack_size')
        cfp_count = int((end_cfp - self.get_int('ruby_current_ec->cfp')) / self.get_int('sizeof(rb_control_frame_t)'))

        print('CFP (count={}, addr=0x{:x}):'.format(cfp_count, self.get_int(cfp)))
        gdb.execute(f'p *({cfp})')
        print()

        print('Env:')
        self.print_stack(cfp, -3, self.rp(cfp, -3))
        self.print_stack(cfp, -2, self.specval(cfp, -2))
        self.print_stack(cfp, -1, self.frame_types(cfp, -1))
        print()

        stack_size = int((self.get_int(f'{cfp}->sp') - self.get_int(f'{cfp}->__bp__')) / 8)
        print(f'Stack (size={stack_size}):')
        for i in range(0, stack_size):
            self.print_stack(cfp, i, self.rp(cfp, i))

    def print_stack(self, cfp, bp_index, content):
        address = self.get_int(f'{cfp}->__bp__ + {bp_index}')
        print('0x{:x} [{}] {}'.format(address, bp_index, content))

    def rp(self, cfp, bp_index):
        value = self.get_value(cfp, bp_index)
        return self.get_string(f'rp {value}').rstrip()

    # specval: block_handler or previous EP
    def specval(self, cfp, bp_index):
        value = self.get_value(cfp, bp_index)
        specval = '0x{:x}'.format(value)
        for block_handler in ['VM_BLOCK_HANDLER_NONE', 'rb_block_param_proxy']:
            if value == self.get_int(block_handler):
                return f'{specval} ({block_handler})'
        return specval

    def frame_types(self, cfp, bp_index):
        types = []
        value = self.get_value(cfp, bp_index)

        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)

        return '0x{:x} ({})'.format(self.get_value(cfp, bp_index), ' | '.join(types))

    def get_value(self, cfp, bp_index):
        return self.get_int(f'{cfp}->__bp__[{bp_index}]')

    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()