File: test_trackgcroot.py

package info (click to toggle)
pypy 5.6.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 97,040 kB
  • ctags: 185,069
  • sloc: python: 1,147,862; ansic: 49,642; cpp: 5,245; asm: 5,169; makefile: 529; sh: 481; xml: 232; lisp: 45
file content (180 lines) | stat: -rw-r--r-- 6,782 bytes parent folder | download | duplicates (3)
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
import py
import sys, re
from rpython.translator.c.gcc.trackgcroot import LOC_NOWHERE, LOC_REG
from rpython.translator.c.gcc.trackgcroot import LOC_EBP_PLUS, LOC_EBP_MINUS
from rpython.translator.c.gcc.trackgcroot import LOC_ESP_PLUS
from rpython.translator.c.gcc.trackgcroot import ElfAssemblerParser
from rpython.translator.c.gcc.trackgcroot import DarwinAssemblerParser
from rpython.translator.c.gcc.trackgcroot import PARSERS
from rpython.translator.c.gcc.trackgcroot import ElfFunctionGcRootTracker32
from StringIO import StringIO
import py.test

this_dir = py.path.local(__file__).dirpath()


def test_format_location():
    cls = ElfFunctionGcRootTracker32
    assert cls.format_location(LOC_NOWHERE) == '?'
    assert cls.format_location(LOC_REG | (1<<2)) == '%ebx'
    assert cls.format_location(LOC_REG | (2<<2)) == '%esi'
    assert cls.format_location(LOC_REG | (3<<2)) == '%edi'
    assert cls.format_location(LOC_REG | (4<<2)) == '%ebp'
    assert cls.format_location(LOC_EBP_PLUS + 0) == '(%ebp)'
    assert cls.format_location(LOC_EBP_PLUS + 4) == '4(%ebp)'
    assert cls.format_location(LOC_EBP_MINUS + 4) == '-4(%ebp)'
    assert cls.format_location(LOC_ESP_PLUS + 0) == '(%esp)'
    assert cls.format_location(LOC_ESP_PLUS + 4) == '4(%esp)'

def test_format_callshape():
    cls = ElfFunctionGcRootTracker32
    expected = ('{4(%ebp) '               # position of the return address
                '| 8(%ebp), 12(%ebp), 16(%ebp), 20(%ebp) '  # 4 saved regs
                '| 24(%ebp), 28(%ebp)}')                    # GC roots
    assert cls.format_callshape((LOC_EBP_PLUS+4,
                                 LOC_EBP_PLUS+8,
                                 LOC_EBP_PLUS+12,
                                 LOC_EBP_PLUS+16,
                                 LOC_EBP_PLUS+20,
                                 LOC_EBP_PLUS+24,
                                 LOC_EBP_PLUS+28)) == expected

def test_compress_callshape():
    cls = ElfFunctionGcRootTracker32
    shape = (1, 127, 0x1234, 0x5678, 0x234567,
             0x765432, 0x61626364, 0x41424344)
    bytes = list(cls.compress_callshape(shape))
    print bytes
    assert len(bytes) == 1+1+2+3+4+4+5+5+1
    assert cls.decompress_callshape(bytes) == list(shape)

def test_find_functions_elf():
    source = """\
\t.p2align 4,,15
.globl pypy_g_make_tree
\t.type\tpypy_g_make_tree, @function
\tFOO
\t.size\tpypy_g_make_tree, .-pypy_g_make_tree

\t.p2align 4,,15
.globl pypy_fn2
\t.type\tpypy_fn2, @function
\tBAR
\t.size\tpypy_fn2, .-pypy_fn2
\tMORE STUFF
"""
    lines = source.splitlines(True)
    parts = list(ElfAssemblerParser().find_functions(iter(lines)))
    assert len(parts) == 5
    assert parts[0] == (False, lines[:2])
    assert parts[1] == (True,  lines[2:5])
    assert parts[2] == (False, lines[5:8])
    assert parts[3] == (True,  lines[8:11])
    assert parts[4] == (False, lines[11:])

def test_find_functions_darwin():
    source = """\
\t.text
\t.align 4,0x90
.globl _pypy_g_ll_str__StringR_Ptr_GcStruct_rpy_strin_rpy_strin
_pypy_g_ll_str__StringR_Ptr_GcStruct_rpy_strin_rpy_strin:
L0:
\tFOO
\t.align 4,0x90
_static:
\tSTATIC
\t.align 4,0x90
.globl _pypy_g_ll_issubclass__object_vtablePtr_object_vtablePtr
_pypy_g_ll_issubclass__object_vtablePtr_object_vtablePtr:
\tBAR
\t.cstring
\t.ascii "foo"
\t.text
\t.align 4,0x90
.globl _pypy_g_RPyRaiseException
_pypy_g_RPyRaiseException:
\tBAZ
\t.section stuff
"""
    lines = source.splitlines(True)
    parts = list(DarwinAssemblerParser().find_functions(iter(lines)))
    assert len(parts) == 6
    assert parts[0] == (False, lines[:3])
    assert parts[1] == (True,  lines[3:7])
    assert parts[2] == (True,  lines[7:11])
    assert parts[3] == (True,  lines[11:18])
    assert parts[4] == (True,  lines[18:20])
    assert parts[5] == (False, lines[20:])
 
def test_computegcmaptable():
    tests = []
    for format in ('elf', 'elf64', 'darwin', 'darwin64', 'msvc'):
        for path in this_dir.join(format).listdir("track*.s"):
            n = path.purebasename[5:]
            try:
                n = int(n)
            except ValueError:
                pass
            tests.append((format, n, path))
    tests.sort()
    for format, _, path in tests:
        yield check_computegcmaptable, format, path


r_expected        = re.compile(r"\s*;;\s*expected\s+([{].+[}])")
r_gcroot_constant = re.compile(r";\tmov\t.+, .+_constant_always_one_")

def check_computegcmaptable(format, path):
    if format == 'msvc':
        r_globallabel = re.compile(r"([\w]+)::")
    elif format == 'darwin' or format == 'darwin64':
        py.test.skip("disabled on OS/X's terribly old gcc")
    else:
        r_globallabel = re.compile(r"([\w.]+)=[.]+")
    print
    print path.dirpath().basename + '/' + path.basename
    lines = path.readlines()
    expectedlines = lines[:]
    tracker = PARSERS[format].FunctionGcRootTracker(lines)
    table = tracker.computegcmaptable(verbose=sys.maxint)
    tabledict = {}
    seen = {}
    for entry in table:
        print '%s: %s' % (entry[0], tracker.format_callshape(entry[1]))
        tabledict[entry[0]] = entry[1]
    # find the ";; expected" lines
    prevline = ""
    for i, line in enumerate(lines):
        match = r_expected.match(line)
        if match:
            expected = match.group(1)
            prevmatch = r_globallabel.match(prevline)
            assert prevmatch, "the computed table is not complete"
            label = prevmatch.group(1)
            assert label in tabledict
            got = tabledict[label]
            assert tracker.format_callshape(got) == expected
            seen[label] = True
            if format == 'msvc':
                expectedlines.insert(i-2, 'PUBLIC\t%s\n' % (label,))
                expectedlines.insert(i-1, '%s::\n' % (label,))
            else:
                expectedlines.insert(i-3, '\t.globl\t%s\n' % (label,))
                expectedlines.insert(i-2, '\t.hidden\t%s\n' % (label,))
                expectedlines.insert(i-1, '%s=.+%d\n' % (label,
                                                         tracker.OFFSET_LABELS))
        if format == 'msvc' and r_gcroot_constant.match(line):
            expectedlines[i] = ';' + expectedlines[i]
            expectedlines[i+1] = (expectedlines[i+1]
                                  .replace('\timul\t', '\tmov\t')
                                  + '\t; GCROOT\n')
        prevline = line
    assert len(seen) == len(tabledict), (
        "computed table contains unexpected entries:\n%r" %
        [key for key in tabledict if key not in seen])
    print '--------------- got ---------------'
    print ''.join(lines)
    print '------------- expected ------------'
    print ''.join(expectedlines)
    print '-----------------------------------'
    assert lines == expectedlines