Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(393)

Side by Side Diff: src/trusted/validator/x86/testing/tf/tf.py

Issue 10908137: (abandoned) Validator tests: convert hexes to TFs and run on bots (for prod. validator only) (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: restore 'read overflow' and 'SegmentationError' Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import copy
6 import re
7
8 import asm
9 import utils
10 import val_runner
11
12
13 class Instruction(object):
14 __slots__ = [
15 'offset',
16 'asm',
17 'hex',
18 'data',
19 'outs', # {validator: [message, ...], ...}
20 ]
21
22 def __init__(self):
23 self.offset = None
24 self.hex = None
25 self.asm = None
26 self.outs = dict((v, []) for v in val_runner.VALIDATORS)
27
28 @property
29 def size(self):
30 return len(self.data)
31
32 @property
33 def end_offset(self):
34 return self.offset + self.size
35
36 def CheckAsm(self, bits):
Mark Seaborn 2012/09/27 02:25:43 This method doesn't seem to be called.
37 if self.asm is None:
38 return
39 asm_data = asm.Assemble(bits, self.asm)
40 assert self.data == asm_data, (utils.DataToReadableHex(asm_data),
41 utils.DataToReadableHex(self.data))
42
43 def __repr__(self):
44 return 'Instr(%s)@0x%x' % (self.asm or self.hex, self.offset)
45
46 _out_suffix = '_out'
47
48 @staticmethod
49 def Parse(bits, lines):
50 instr = Instruction()
51 for line in lines:
52 assert ':' in line, "can't parse line '%s'" % line
53 field, value = line.split(':', 1)
54 field = field.strip()
55
56 if field.endswith(Instruction._out_suffix):
57 validator = field[:-len(Instruction._out_suffix)]
58 instr.outs[validator].append(value.strip())
59 else:
60 assert getattr(instr, field) is None, 'field %s is already set' % field
61 setattr(instr, field, value.strip())
62
63 if instr.hex is not None:
64 instr.data = utils.ReadableHexToData(instr.hex)
65 else:
Mark Seaborn 2012/09/27 02:25:43 Hmm, having a fallback here means that in normal t
66 instr.data = asm.Assemble(bits, instr.asm)
67 instr.hex = utils.DataToReadableHex(instr.data)
68 return instr
69
70 def ToLines(self, offset=None):
71 lines = []
72 if self.offset != offset:
73 lines.append('offset: %s' % self.offset)
74 if self.asm is not None:
75 lines.append('asm: %s' % self.asm)
76 if self.hex is not None:
77 lines.append('hex: %s' % self.hex)
78
79 for validator in val_runner.VALIDATORS:
80 for msg in self.outs[validator]:
81 lines.append('%s%s: %s' % (validator, Instruction._out_suffix, msg))
82
83 return lines
84
85
86 class Test(object):
87 __slots__ = [
88 'bits',
Mark Seaborn 2012/09/27 02:25:43 Some comments on what these fields mean would be h
89 'separators',
90 'sections',
91 'instructions',
92 'safe',
93 ]
94
95 # Each separator/section is a list of strings.
96 # Number of separators always exceeds number of sections by one, because
97 # they interleave, starting and finishing with (possibly empty) separator.
98
99 @staticmethod
100 def Parse(lines):
101 test = Test()
102 test.safe = None
103 test.separators = [[]]
104 test.sections = []
105
106 # Parser state: whether we are currently parsing
107 # separator (comments/whitelines) or section.
108 in_section = False
109
110 for line in lines:
111 line = line.strip()
112 is_sep = line == '' or line.startswith('#')
113
114 if is_sep:
115 if in_section:
116 test.separators.append([line])
117 else:
118 test.separators[-1].append(line)
119 else:
120 if in_section:
121 test.sections[-1].append(line)
122 else:
123 test.sections.append([line])
124
125 in_section = not is_sep
126
127 if in_section:
128 test.separators.append([])
129
130 assert len(test.separators) == len(test.sections) + 1
131
132 # header section is required; it specifies BITS and OUTCOME
133 assert len(test.sections) >= 1
134
135 for line in test.sections[0]:
136 m = re.match(r'(.*):\s*(.*)$', line)
137 field, value = m.groups()
138 if field == 'BITS':
139 test.bits = int(value)
140 elif field == 'OUTCOME':
141 assert value in ['valid', 'invalid']
142 test.safe = value == 'valid'
143 else:
144 raise AssertionError('Unrecognized field %s in special section' %
145 field)
146
147 test.instructions = []
148 offset = 0
149 for section in test.sections[1:]:
150 instr = Instruction.Parse(test.bits, section)
151
152 if instr.hex is None:
153 code = asm.Assemble(test.bits, instr.asm)
154 instr.hex = utils.DataToReadableHex(code)
155
156 if instr.offset is not None:
157 instr.offset = int(instr.offset)
158 else:
159 instr.offset = offset
160 test.instructions.append(instr)
161 offset = instr.end_offset
162
163 return test
164
165 def Print(self, fout):
166 self.sections[0] = ['BITS: %s' % self.bits]
167 if self.safe is not None:
168 self.sections[0].append(
169 'OUTCOME: valid' if self.safe else 'OUTCOME: invalid')
170
171 offset = 0
172 for i, instr in enumerate(self.instructions):
173 self.sections[i+1] = instr.ToLines(offset)
174 offset = instr.end_offset
175
176 assert len(self.separators) == len(self.sections) + 1
177 groups = []
178 for sep, sec in zip(self.separators, self.sections):
179 groups.append(sep)
180 groups.append(sec)
181 groups.append(self.separators[-1])
182
183 for group in groups:
184 for line in group:
185 fout.write('%s\n' % line)
186
187 def PrepareCode(self):
188 code_size = max(i.end_offset for i in self.instructions)
189 code_size = ((code_size - 1) // 32 + 1) * 32
190 code = ['\x90'] * code_size
191
192 for i in self.instructions:
193 code[i.offset : i.end_offset] = list(i.data)
194
195 return ''.join(code)
196
197 def RunValidator(self, validator):
198 assert validator in val_runner.VALIDATORS
199 if validator == 'nc':
200 return val_runner.RunValidator(validator, self.bits, self.PrepareCode())
201
202 test = copy.deepcopy(self)
203
204 # When RDFA validator encounters invalid instruction, it stops processing
205 # current 32-byte bundle and moves on straight to the next one, while
206 # prod. validator recovers from error and continues decoding.
207 # To avoid many spurious errors, some kind of error recovery for RDFA
208 # is emulated in the following process:
209 #
210 # Whenever instuction can't be decoded, the whole section containing it
211 # is replaced with nops, and validation is repeated from start. This
212 # patching can happen several times. As a result of validation we take
213 # union of all errors discovered on all passes.
214 # This approach is expected to work well only when partition to sections
215 # matches closely partition to instructions, which is reasonable assumption
216 # (typical section is a single instruction or few instructions forming
217 # pseudoinstruction).
218 #
219 # This procedure is more or less safe (in a sense that it's unlikely
220 # to mask any bugs in validator), because first it is guaranteed to
221 # preserve all errors found in the first pass (on unmodified code),
222 # and second all spurious errors incorrectly introduced by patching
223 # will likely be spotted as discrepancies compared to prod. validator.
Mark Seaborn 2012/09/27 02:25:43 This means you're relying on having the prod valid
224
225 errors = set()
226 safe = True
227
228 while True:
229 res = val_runner.RunValidator(validator, test.bits, test.PrepareCode())
230 safe = safe and res.safe
231 errors.update(res.errors)
232
233 patched = False
234 for loc, msg in res.errors:
235 if msg == 'DFA error in validator':
236 for i in test.instructions:
237 if i.offset == loc:
238 nops = '\x90' * i.size
239 if i.data != nops:
240 i.data = nops
241 patched = True
242 if not patched:
243 break
244
245 return val_runner.ValidationResults(safe=safe, errors=sorted(errors))
246
247 def ExpectedErrors(self, validator):
248 errors = []
249 for i in self.instructions:
250 for msg in i.outs[validator]:
251 # Check if message has the form
252 # [at +<delta>] <error message>
253 m = re.match(r'\[at \+(\d+)\]\s(.*)$', msg)
254 if m is not None:
255 delta = int(m.group(1))
256 msg = m.group(2)
257 else:
258 delta = 0
259 errors.append((i.offset + delta, msg))
260 return errors
OLDNEW
« no previous file with comments | « src/trusted/validator/x86/testing/tf/hex2tf.py ('k') | src/trusted/validator/x86/testing/tf/utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698