summaryrefslogtreecommitdiff
path: root/misc/gdb.py
blob: 85507fb54098b3fb7233e8444b2149465f8ab105 (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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import textwrap

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

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

            print(f'Locals (size={local_size}):')
            for i in range(-3 - local_size, -3):
                self.print_stack(cfp, i, self.rp(cfp, i))
            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))
        print(self.regs(cfp, stack_size))

    def print_stack(self, cfp, bp_index, content):
        address = self.get_int(f'{cfp}->__bp__ + {bp_index}')
        value = self.get_value(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))

    def regs(self, cfp, bp_index):
        address = self.get_int(f'{cfp}->__bp__ + {bp_index}')
        regs = []
        for reg, field in { 'EP': 'ep', 'BP': '__bp__', 'SP': 'sp' }.items():
            if address == self.get_int(f'{cfp}->{field}'):
                regs.append(reg)
        return ' '.join(regs)

    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)
        if value == 0:
            return 'VM_BLOCK_HANDLER_NONE'
        if value == self.get_int('rb_block_param_proxy'):
            return 'rb_block_param_proxy'
        return ''

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