File: storage.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 (153 lines) | stat: -rw-r--r-- 5,779 bytes parent folder | download | duplicates (7)
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

""" This file represents a storage mechanism that let us invent unique names
for all loops and bridges, so http requests can refer to them by name
"""

import py
import os
import linecache
from rpython.tool.disassembler import dis
from rpython.tool.jitlogparser.module_finder import gather_all_code_objs

class LoopStorage(object):
    def __init__(self, extrapath=None):
        self.loops = None
        self.functions = {}
        self.codes = {}
        self.disassembled_codes = {}
        self.extrapath = extrapath

    def load_code(self, fname):
        try:
            return self.codes[fname]
        except KeyError:
            if os.path.isabs(fname):
                res = gather_all_code_objs(fname)
            else:
                if self.extrapath is None:
                    raise IOError("Cannot find %s" % fname)
                res = gather_all_code_objs(os.path.join(self.extrapath, fname))
            self.codes[fname] = res
            return res

    def disassemble_code(self, fname, startlineno, name, generic_format=False):
        # 'generic_format' is False for PyPy2 (returns a
        # disassembler.CodeRepresentation) or True otherwise (returns a
        # GenericCode, without attempting any disassembly)
        try:
            if py.path.local(fname).check(file=False):
                return None # cannot find source file
        except py.error.EACCES:
            return None # cannot open the file
        key = (fname, startlineno, name)
        try:
            return self.disassembled_codes[key]
        except KeyError:
            pass
        if generic_format:
            res = GenericCode(fname, startlineno, name)
        else:
            codeobjs = self.load_code(fname)
            if (startlineno, name) not in codeobjs:
                # cannot find the code obj at this line: this can happen for
                # various reasons, e.g. because the .py files changed since
                # the log was produced, or because the co_firstlineno
                # attribute of the code object is wrong (e.g., code objects
                # produced by gateway.applevel(), such as the ones found in
                # nanos.py)
                return None
            code = codeobjs[(startlineno, name)]
            res = dis(code)
        self.disassembled_codes[key] = res
        return res

    def reconnect_loops(self, loops):
        """ Re-connect loops in a way that entry bridges are filtered out
        and normal bridges are associated with guards. Returning list of
        normal loops.
        """
        res = []
        guard_dict = {}
        for loop_no, loop in enumerate(loops):
            for op in loop.operations:
                if op.name.startswith('guard_') or op.name.startswith('vec_guard_'):
                    guard_dict[int(op.descr[len('<Guard0x'):-1], 16)] = (op, loop)
        for loop in loops:
            if loop.comment:
                comment = loop.comment.strip()
                if 'entry bridge' in comment:
                    pass
                elif comment.startswith('# bridge out of'):
                    no = int(comment[len('# bridge out of Guard 0x'):].split(' ', 1)[0], 16)
                    op, parent = guard_dict[no]
                    op.bridge = loop
                    op.percentage = ((getattr(loop, 'count', 1) * 100) /
                                     max(getattr(parent, 'count', 1), 1))
                    loop.no = no
                    continue
            res.append(loop)
        self.loops = res
        return res


class GenericCode(object):
    def __init__(self, fname, startlineno, name):
        self.filename = fname
        self.startlineno = startlineno
        self.name = name
        self._first_bytecodes = {}     # {lineno: bytecode_name}
        self._source = None

    def __repr__(self):
        return 'GenericCode(%r, %r, %r)' % (
            self.filename, self.startlineno, self.name)

    def get_opcode_from_info(self, info):
        lineno = ~info.bytecode_no
        bname = info.bytecode_name
        if self._first_bytecodes.setdefault(lineno, bname) == bname:
            # this is the first opcode of the line---or, at least,
            # the first time we ask for an Opcode on that line.
            line_starts_here = True
        else:
            line_starts_here = False
        return GenericOpcode(lineno, line_starts_here, bname)

    @property
    def source(self):
        if self._source is None:
            src = linecache.getlines(self.filename)
            if self.startlineno > 0:
                src = src[self.startlineno - 1:]
            self._source = [s.rstrip('\n\r') for s in src]
        return self._source


class GenericOpcode(object):
    def __init__(self, lineno, line_starts_here, bytecode_extra=''):
        self.lineno = lineno
        self.line_starts_here = line_starts_here
        self.bytecode_extra = bytecode_extra

    def __repr__(self):
        return 'GenericOpcode(%r, %r, %r)' % (
            self.lineno, self.line_starts_here, self.bytecode_extra)

    def __eq__(self, other):
        if not isinstance(other, GenericOpcode):
            return NotImplemented
        return (self.lineno == other.lineno and
                self.line_starts_here == other.line_starts_here and
                self.bytecode_extra == other.bytecode_extra)

    def __ne__(self, other):
        if not isinstance(other, GenericOpcode):
            return NotImplemented
        return not (self == other)

    def __hash__(self):
        return hash((self.lineno, self.line_starts_here, self.bytecode_extra))

    def match_name(self, opcode_name):
        return (self.bytecode_extra == opcode_name or
                self.bytecode_extra.endswith(' ' + opcode_name))