File: gencsupp.py

package info (click to toggle)
pypy 7.0.0%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 107,216 kB
  • sloc: python: 1,201,787; ansic: 62,419; asm: 5,169; cpp: 3,017; sh: 2,534; makefile: 545; xml: 243; lisp: 45; awk: 4
file content (210 lines) | stat: -rw-r--r-- 8,474 bytes parent folder | download | duplicates (5)
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import py, random, sys
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
from rpython.rtyper.lltypesystem.lloperation import LL_OPERATIONS
from rpython.translator.c.support import cdecl
from rpython.rlib import exports, revdb

#
# How It Works: we arrange things so that when replaying, all variables
# have the "same" content as they had during recording.  More precisely,
# we divide all variables according to their type in two categories:
#
#  * non-GC pointers, whose values are bitwise identical.
#
#  * GC pointers: these things are "moving", in the sense that the
#    bitwise value of a GC pointer during recording is different from
#    its bitwise value during replaying.  The object at the new
#    address contains the "same" content as the original one, using
#    this definition of "same".
#
# Note that most non-GC pointers are not followed during replaying: we
# never call external libraries nor call malloc to get pieces of raw
# memory.  Writes to raw memory are ignored, and reads return a value
# recorded in the log file.  However, the following are still needed:
#
#  * function pointers pointing to RPython functions;
#
#  * "static-immutable" structs like vtables.
#
# For now. we must make sure that these are at the same address as
# they were.  This is very roughly checked.  On Linux, it means you
# must run with Address Space Layout Randomization disabled.  This
# might be fixed in the future.
#


def extra_files():
    srcdir = py.path.local(__file__).join('..', 'src-revdb')
    return [
        srcdir / 'revdb.c',
    ]

## def mangle_name_prebuilt_raw(database, node, S):
##     if (S._gckind != 'gc' and not S._hints.get('is_excdata')
##                           and not S._hints.get('static_immutable')
##                           and not S._hints.get('ignore_revdb')
##                           and not S._hints.get('gcheader')):
##         database.all_raw_structures.append(node)
##         node.name = 'RPY_RDB_A(%s)' % (node.name,)
##         return True
##     else:
##         return False

def prepare_function(funcgen):
    if getattr(getattr(funcgen.graph, 'func', None), '_revdb_c_only_', False):
        extra_enter_text = 'RPY_REVDB_C_ONLY_ENTER'
        extra_return_text = 'RPY_REVDB_C_ONLY_LEAVE'
        return extra_enter_text, extra_return_text
    stack_bottom = False
    for block in funcgen.graph.iterblocks():
        for op in block.operations:
            if op.opname == 'gc_stack_bottom':
                stack_bottom = True
    if stack_bottom:
        name = funcgen.functionname
        funcgen.db.stack_bottom_funcnames.append(name)
        extra_enter_text = '\n'.join(
            ['/* this function is a callback */',
             'RPY_REVDB_CALLBACKLOC(RPY_CALLBACKLOC_%s);' % name] +
            ['\t' + emit('/*arg*/', funcgen.lltypename(v), funcgen.expr(v))
                for v in funcgen.graph.getargs()])
        extra_return_text = '/* RPY_CALLBACK_LEAVE(); */'
        return extra_enter_text, extra_return_text
    else:
        return None, None

def emit_void(normal_code):
    return 'RPY_REVDB_EMIT_VOID(%s);' % (normal_code,)

def emit(normal_code, tp, value):
    if tp == 'void @':
        return emit_void(normal_code)
    return 'RPY_REVDB_EMIT(%s, %s, %s);' % (normal_code, cdecl(tp, '_e'), value)

def emit_residual_call(funcgen, call_code, v_result, expr_result):
    if hasattr(funcgen, 'revdb_do_next_call'):
        del funcgen.revdb_do_next_call
        return call_code   # a hack for ll_call_destructor() to mean
                           # that the calls should really be done.
                           # Also used in rpython.rlib.clibffi.
    #
    if call_code in ('RPyGilAcquire();', 'RPyGilRelease();'):
        # Could also work with a regular RPY_REVDB_CALL_VOID, but we
        # use a different byte (0xFD, 0xFE instead of 0xFC) to detect more
        # sync misses.  In a single-threaded environment this 0xFD or 0xFE
        # byte is not needed at all, but in a multi-threaded
        # environment it ensures that during replaying, just after
        # reading the 0xFD or 0xFE, we switch to a different thread if needed
        # (actually implemented with stacklets).
        if call_code == 'RPyGilAcquire();':
            return 'RPY_REVDB_CALL_GIL_ACQUIRE();'
        else:
            return 'RPY_REVDB_CALL_GIL_RELEASE();'
    #
    tp = funcgen.lltypename(v_result)
    if tp == 'void @':
        return 'RPY_REVDB_CALL_VOID(%s);' % (call_code,)
    return 'RPY_REVDB_CALL(%s, %s, %s);' % (call_code, cdecl(tp, '_e'),
                                            expr_result)

def record_malloc_uid(expr):
    return ' RPY_REVDB_REC_UID(%s);' % (expr,)

def boehm_register_finalizer(funcgen, op):
    return 'rpy_reverse_db_register_destructor(%s, %s);' % (
        funcgen.expr(op.args[0]), funcgen.expr(op.args[1]))

def cast_gcptr_to_int(funcgen, op):
    return '%s = RPY_REVDB_CAST_PTR_TO_INT(%s);' % (
        funcgen.expr(op.result), funcgen.expr(op.args[0]))

set_revdb_protected = set(opname for opname, opdesc in LL_OPERATIONS.items()
                                 if opdesc.revdb_protect)


def prepare_database(db):
    FUNCPTR = lltype.Ptr(lltype.FuncType([revdb._CMDPTR, lltype.Ptr(rstr.STR)],
                                         lltype.Void))
    ALLOCFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.LONGLONG, llmemory.GCREF],
                                              lltype.Void))

    bk = db.translator.annotator.bookkeeper
    cmds = getattr(db.translator, 'revdb_commands', {})
    numcmds = [(num, func) for (num, func) in cmds.items()
                           if isinstance(num, int)]

    S = lltype.Struct('RPY_REVDB_COMMANDS',
                  ('names', lltype.FixedSizeArray(rffi.INT, len(numcmds) + 1)),
                  ('funcs', lltype.FixedSizeArray(FUNCPTR, len(numcmds))),
                  ('alloc', ALLOCFUNCPTR))
    s = lltype.malloc(S, flavor='raw', immortal=True, zero=True)

    i = 0
    for name, func in cmds.items():
        fnptr = lltype.getfunctionptr(bk.getdesc(func).getuniquegraph())
        if isinstance(name, int):
            assert name != 0
            s.names[i] = rffi.cast(rffi.INT, name)
            s.funcs[i] = fnptr
            i += 1
        elif name == "ALLOCATING":
            s.alloc = fnptr
        else:
            raise AssertionError("bad tag in register_debug_command(): %r"
                                 % (name,))

    exports.EXPORTS_obj2name[s._as_obj()] = 'rpy_revdb_commands'
    db.get(s)

    db.stack_bottom_funcnames = []
    ## db.all_raw_structures = []

def write_revdb_def_file(db, target_path):
    f = target_path.open('w')
    funcnames = sorted(db.stack_bottom_funcnames)
    ## print >> f, "#define RDB_VERSION  0x%x" % random.randrange(0, sys.maxint)
    ## print >> f
    for i, fn in enumerate(funcnames):
        print >> f, '#define RPY_CALLBACKLOC_%s %d' % (fn, i)
    print >> f
    print >> f, '#define RPY_CALLBACKLOCS \\'
    funcnames = funcnames or ['NULL']
    for i, fn in enumerate(funcnames):
        if i == len(funcnames) - 1:
            tail = ''
        else:
            tail = ', \\'
        print >> f, '\t(void *)%s%s' % (fn, tail)
    ## print >> f

    ## def _base(name):
    ##     assert name.startswith('RPY_RDB_A(')
    ##     if name.endswith('.b'):
    ##         name = name[:-2]
    ##     name = name[len('RPY_RDB_A('):-1]
    ##     return name

    ## rawstructs = sorted(db.all_raw_structures, key=lambda node: node.name)
    ## print >> f, '#define RPY_RDB_A(name)  (*rpy_rdb_struct.name)'
    ## print >> f, 'struct rpy_rdb_a_s {'
    ## for i, node in enumerate(rawstructs):
    ##     print >> f, '\t%s;' % (cdecl(node.typename, '*'+_base(node.name)),)
    ## if not rawstructs:
    ##     print >> f, '\tchar dummy;'
    ## print >> f, '};'
    ## print >> f, 'RPY_EXTERN struct rpy_rdb_a_s rpy_rdb_struct;'
    ## print >> f
    ## print >> f, '#define RPY_RDB_STRUCT_CONTENT \\'
    ## if not rawstructs:
    ##     print >> f, '\t0'
    ## else:
    ##     for i, node in enumerate(rawstructs):
    ##         if i == len(rawstructs) - 1:
    ##             tail = ''
    ##         else:
    ##             tail = ', \\'
    ##         name = '&' + _base(node.name)
    ##         if node.typename != node.implementationtypename:
    ##             name = '(%s)%s' % (cdecl(node.typename, '*'), name)
    ##         print >> f, '\t%s%s' % (name, tail)
    f.close()