1 ##########################################################################
3 # Copyright 2010 VMware, Inc.
6 # Permission is hereby granted, free of charge, to any person obtaining a copy
7 # of this software and associated documentation files (the "Software"), to deal
8 # in the Software without restriction, including without limitation the rights
9 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 # copies of the Software, and to permit persons to whom the Software is
11 # furnished to do so, subject to the following conditions:
13 # The above copyright notice and this permission notice shall be included in
14 # all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 ##########################################################################/
27 """Generic retracing code generator."""
33 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
36 import specs.stdapi as stdapi
39 class UnsupportedType(Exception):
43 def lookupHandle(handle, value):
44 if handle.key is None:
45 return "_%s_map[%s]" % (handle.name, value)
47 key_name, key_type = handle.key
48 return "_%s_map[%s][%s]" % (handle.name, key_name, value)
51 class ValueAllocator(stdapi.Visitor):
53 def visitLiteral(self, literal, lvalue, rvalue):
56 def visitConst(self, const, lvalue, rvalue):
57 self.visit(const.type, lvalue, rvalue)
59 def visitAlias(self, alias, lvalue, rvalue):
60 self.visit(alias.type, lvalue, rvalue)
62 def visitEnum(self, enum, lvalue, rvalue):
65 def visitBitmask(self, bitmask, lvalue, rvalue):
68 def visitArray(self, array, lvalue, rvalue):
69 print ' %s = static_cast<%s *>(_allocator.alloc(&%s, sizeof *%s));' % (lvalue, array.type, rvalue, lvalue)
71 def visitPointer(self, pointer, lvalue, rvalue):
72 print ' %s = static_cast<%s *>(_allocator.alloc(&%s, sizeof *%s));' % (lvalue, pointer.type, rvalue, lvalue)
74 def visitIntPointer(self, pointer, lvalue, rvalue):
77 def visitObjPointer(self, pointer, lvalue, rvalue):
80 def visitLinearPointer(self, pointer, lvalue, rvalue):
83 def visitReference(self, reference, lvalue, rvalue):
84 self.visit(reference.type, lvalue, rvalue);
86 def visitHandle(self, handle, lvalue, rvalue):
89 def visitBlob(self, blob, lvalue, rvalue):
92 def visitString(self, string, lvalue, rvalue):
95 def visitStruct(self, struct, lvalue, rvalue):
98 def visitPolymorphic(self, polymorphic, lvalue, rvalue):
99 assert polymorphic.defaultType is not None
100 self.visit(polymorphic.defaultType, lvalue, rvalue)
102 def visitOpaque(self, opaque, lvalue, rvalue):
106 class ValueDeserializer(stdapi.Visitor, stdapi.ExpanderMixin):
108 def visitLiteral(self, literal, lvalue, rvalue):
109 print ' %s = (%s).to%s();' % (lvalue, rvalue, literal.kind)
111 def visitConst(self, const, lvalue, rvalue):
112 self.visit(const.type, lvalue, rvalue)
114 def visitAlias(self, alias, lvalue, rvalue):
115 self.visit(alias.type, lvalue, rvalue)
117 def visitEnum(self, enum, lvalue, rvalue):
118 print ' %s = static_cast<%s>((%s).toSInt());' % (lvalue, enum, rvalue)
120 def visitBitmask(self, bitmask, lvalue, rvalue):
121 self.visit(bitmask.type, lvalue, rvalue)
123 def visitArray(self, array, lvalue, rvalue):
125 tmp = '_a_' + array.tag + '_' + str(self.seq)
128 print ' if (%s) {' % (lvalue,)
129 print ' const trace::Array *%s = dynamic_cast<const trace::Array *>(&%s);' % (tmp, rvalue)
130 length = '%s->values.size()' % (tmp,)
131 index = '_j' + array.tag
132 print ' for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length)
134 self.visit(array.type, '%s[%s]' % (lvalue, index), '*%s->values[%s]' % (tmp, index))
139 def visitPointer(self, pointer, lvalue, rvalue):
140 tmp = '_a_' + pointer.tag + '_' + str(self.seq)
143 print ' if (%s) {' % (lvalue,)
144 print ' const trace::Array *%s = dynamic_cast<const trace::Array *>(&%s);' % (tmp, rvalue)
146 self.visit(pointer.type, '%s[0]' % (lvalue,), '*%s->values[0]' % (tmp,))
150 def visitIntPointer(self, pointer, lvalue, rvalue):
151 print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, pointer, rvalue)
153 def visitObjPointer(self, pointer, lvalue, rvalue):
154 print ' %s = static_cast<%s>(retrace::toObjPointer(call, %s));' % (lvalue, pointer, rvalue)
156 def visitLinearPointer(self, pointer, lvalue, rvalue):
157 print ' %s = static_cast<%s>(retrace::toPointer(%s));' % (lvalue, pointer, rvalue)
159 def visitReference(self, reference, lvalue, rvalue):
160 self.visit(reference.type, lvalue, rvalue);
162 def visitHandle(self, handle, lvalue, rvalue):
163 #OpaqueValueDeserializer().visit(handle.type, lvalue, rvalue);
164 self.visit(handle.type, lvalue, rvalue);
165 new_lvalue = lookupHandle(handle, lvalue)
166 print ' if (retrace::verbosity >= 2) {'
167 print ' std::cout << "%s " << size_t(%s) << " <- " << size_t(%s) << "\\n";' % (handle.name, lvalue, new_lvalue)
169 if (new_lvalue.startswith('_program_map') or new_lvalue.startswith('_shader_map')):
170 print 'if (glretrace::supportsARBShaderObjects) {'
171 print ' %s = _handleARB_map[%s];' % (lvalue, lvalue)
173 print ' %s = %s;' % (lvalue, new_lvalue)
176 print ' %s = %s;' % (lvalue, new_lvalue)
178 def visitBlob(self, blob, lvalue, rvalue):
179 print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, blob, rvalue)
181 def visitString(self, string, lvalue, rvalue):
182 print ' %s = (%s)((%s).toString());' % (lvalue, string.expr, rvalue)
186 def visitStruct(self, struct, lvalue, rvalue):
187 tmp = '_s_' + struct.tag + '_' + str(self.seq)
190 print ' const trace::Struct *%s = dynamic_cast<const trace::Struct *>(&%s);' % (tmp, rvalue)
191 print ' assert(%s);' % (tmp)
192 for i in range(len(struct.members)):
193 member = struct.members[i]
194 self.visitMember(member, lvalue, '*%s->members[%s]' % (tmp, i))
196 def visitPolymorphic(self, polymorphic, lvalue, rvalue):
197 if polymorphic.defaultType is None:
198 switchExpr = self.expand(polymorphic.switchExpr)
199 print r' switch (%s) {' % switchExpr
200 for cases, type in polymorphic.iterSwitch():
204 if type.expr is not None:
205 caseLvalue = 'static_cast<%s>(%s)' % (type, caseLvalue)
208 self.visit(type, caseLvalue, rvalue)
212 if polymorphic.defaultType is None:
214 print r' retrace::warning(call) << "unexpected polymorphic case" << %s << "\n";' % (switchExpr,)
218 self.visit(polymorphic.defaultType, lvalue, rvalue)
220 def visitOpaque(self, opaque, lvalue, rvalue):
221 raise UnsupportedType
224 class OpaqueValueDeserializer(ValueDeserializer):
225 '''Value extractor that also understands opaque values.
227 Normally opaque values can't be retraced, unless they are being extracted
228 in the context of handles.'''
230 def visitOpaque(self, opaque, lvalue, rvalue):
231 print ' %s = static_cast<%s>(retrace::toPointer(%s));' % (lvalue, opaque, rvalue)
234 class SwizzledValueRegistrator(stdapi.Visitor, stdapi.ExpanderMixin):
235 '''Type visitor which will register (un)swizzled value pairs, to later be
238 def visitLiteral(self, literal, lvalue, rvalue):
241 def visitAlias(self, alias, lvalue, rvalue):
242 self.visit(alias.type, lvalue, rvalue)
244 def visitEnum(self, enum, lvalue, rvalue):
247 def visitBitmask(self, bitmask, lvalue, rvalue):
250 def visitArray(self, array, lvalue, rvalue):
251 print ' const trace::Array *_a%s = dynamic_cast<const trace::Array *>(&%s);' % (array.tag, rvalue)
252 print ' if (_a%s) {' % (array.tag)
253 length = '_a%s->values.size()' % array.tag
254 index = '_j' + array.tag
255 print ' for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length)
257 self.visit(array.type, '%s[%s]' % (lvalue, index), '*_a%s->values[%s]' % (array.tag, index))
262 def visitPointer(self, pointer, lvalue, rvalue):
263 print ' const trace::Array *_a%s = dynamic_cast<const trace::Array *>(&%s);' % (pointer.tag, rvalue)
264 print ' if (_a%s) {' % (pointer.tag)
266 self.visit(pointer.type, '%s[0]' % (lvalue,), '*_a%s->values[0]' % (pointer.tag,))
270 def visitIntPointer(self, pointer, lvalue, rvalue):
273 def visitObjPointer(self, pointer, lvalue, rvalue):
274 print r' retrace::addObj(call, %s, %s);' % (rvalue, lvalue)
276 def visitLinearPointer(self, pointer, lvalue, rvalue):
277 assert pointer.size is not None
278 if pointer.size is not None:
279 print r' retrace::addRegion((%s).toUIntPtr(), %s, %s);' % (rvalue, lvalue, pointer.size)
281 def visitReference(self, reference, lvalue, rvalue):
284 def visitHandle(self, handle, lvalue, rvalue):
285 print ' %s _origResult;' % handle.type
286 OpaqueValueDeserializer().visit(handle.type, '_origResult', rvalue);
287 if handle.range is None:
288 rvalue = "_origResult"
289 entry = lookupHandle(handle, rvalue)
290 if (entry.startswith('_program_map') or entry.startswith('_shader_map')):
291 print 'if (glretrace::supportsARBShaderObjects) {'
292 print ' _handleARB_map[%s] = %s;' % (rvalue, lvalue)
294 print ' %s = %s;' % (entry, lvalue)
297 print " %s = %s;" % (entry, lvalue)
298 print ' if (retrace::verbosity >= 2) {'
299 print ' std::cout << "{handle.name} " << {rvalue} << " -> " << {lvalue} << "\\n";'.format(**locals())
302 i = '_h' + handle.tag
303 lvalue = "%s + %s" % (lvalue, i)
304 rvalue = "_origResult + %s" % (i,)
305 entry = lookupHandle(handle, rvalue)
306 print ' for ({handle.type} {i} = 0; {i} < {handle.range}; ++{i}) {{'.format(**locals())
307 print ' {entry} = {lvalue};'.format(**locals())
308 print ' if (retrace::verbosity >= 2) {'
309 print ' std::cout << "{handle.name} " << ({rvalue}) << " -> " << ({lvalue}) << "\\n";'.format(**locals())
313 def visitBlob(self, blob, lvalue, rvalue):
316 def visitString(self, string, lvalue, rvalue):
321 def visitStruct(self, struct, lvalue, rvalue):
322 tmp = '_s_' + struct.tag + '_' + str(self.seq)
325 print ' const trace::Struct *%s = dynamic_cast<const trace::Struct *>(&%s);' % (tmp, rvalue)
326 print ' assert(%s);' % (tmp,)
327 print ' (void)%s;' % (tmp,)
328 for i in range(len(struct.members)):
329 member = struct.members[i]
330 self.visitMember(member, lvalue, '*%s->members[%s]' % (tmp, i))
332 def visitPolymorphic(self, polymorphic, lvalue, rvalue):
333 assert polymorphic.defaultType is not None
334 self.visit(polymorphic.defaultType, lvalue, rvalue)
336 def visitOpaque(self, opaque, lvalue, rvalue):
342 def retraceFunction(self, function):
343 print 'static void retrace_%s(trace::Call &call) {' % function.name
344 self.retraceFunctionBody(function)
348 def retraceInterfaceMethod(self, interface, method):
349 print 'static void retrace_%s__%s(trace::Call &call) {' % (interface.name, method.name)
350 self.retraceInterfaceMethodBody(interface, method)
354 def retraceFunctionBody(self, function):
355 assert function.sideeffects
357 if function.type is not stdapi.Void:
358 self.checkOrigResult(function)
360 self.deserializeArgs(function)
362 self.declareRet(function)
363 self.invokeFunction(function)
365 self.swizzleValues(function)
367 def retraceInterfaceMethodBody(self, interface, method):
368 assert method.sideeffects
370 if method.type is not stdapi.Void:
371 self.checkOrigResult(method)
373 self.deserializeThisPointer(interface)
375 self.deserializeArgs(method)
377 self.declareRet(method)
378 self.invokeInterfaceMethod(interface, method)
380 self.swizzleValues(method)
382 def checkOrigResult(self, function):
383 '''Hook for checking the original result, to prevent succeeding now
384 where the original did not, which would cause diversion and potentially
385 unpredictable results.'''
387 assert function.type is not stdapi.Void
389 if str(function.type) == 'HRESULT':
390 print r' if (call.ret && FAILED(call.ret->toSInt())) {'
394 def deserializeThisPointer(self, interface):
395 print r' %s *_this;' % (interface.name,)
396 print r' _this = static_cast<%s *>(retrace::toObjPointer(call, call.arg(0)));' % (interface.name,)
397 print r' if (!_this) {'
401 def deserializeArgs(self, function):
402 print ' retrace::ScopedAllocator _allocator;'
403 print ' (void)_allocator;'
405 for arg in function.args:
406 arg_type = arg.type.mutable()
407 print ' %s %s;' % (arg_type, arg.name)
408 rvalue = 'call.arg(%u)' % (arg.index,)
411 self.extractArg(function, arg, arg_type, lvalue, rvalue)
412 except UnsupportedType:
414 print ' memset(&%s, 0, sizeof %s); // FIXME' % (arg.name, arg.name)
419 self.failFunction(function)
420 sys.stderr.write('warning: unsupported %s call\n' % function.name)
423 def swizzleValues(self, function):
424 for arg in function.args:
426 arg_type = arg.type.mutable()
427 rvalue = 'call.arg(%u)' % (arg.index,)
430 self.regiterSwizzledValue(arg_type, lvalue, rvalue)
431 except UnsupportedType:
432 print ' // XXX: %s' % arg.name
433 if function.type is not stdapi.Void:
437 self.regiterSwizzledValue(function.type, lvalue, rvalue)
438 except UnsupportedType:
440 print ' // XXX: result'
442 def failFunction(self, function):
443 print ' if (retrace::verbosity >= 0) {'
444 print ' retrace::unsupported(call);'
448 def extractArg(self, function, arg, arg_type, lvalue, rvalue):
449 ValueAllocator().visit(arg_type, lvalue, rvalue)
451 ValueDeserializer().visit(arg_type, lvalue, rvalue)
453 def extractOpaqueArg(self, function, arg, arg_type, lvalue, rvalue):
455 ValueAllocator().visit(arg_type, lvalue, rvalue)
456 except UnsupportedType:
458 OpaqueValueDeserializer().visit(arg_type, lvalue, rvalue)
460 def regiterSwizzledValue(self, type, lvalue, rvalue):
461 visitor = SwizzledValueRegistrator()
462 visitor.visit(type, lvalue, rvalue)
464 def declareRet(self, function):
465 if function.type is not stdapi.Void:
466 print ' %s _result;' % (function.type)
468 def invokeFunction(self, function):
469 arg_names = ", ".join(function.argNames())
470 if function.type is not stdapi.Void:
471 print ' _result = %s(%s);' % (function.name, arg_names)
472 print ' (void)_result;'
473 self.checkResult(function.type)
475 print ' %s(%s);' % (function.name, arg_names)
477 def invokeInterfaceMethod(self, interface, method):
478 # On release our reference when we reach Release() == 0 call in the
480 if method.name == 'Release':
481 print ' if (call.ret->toUInt() == 0) {'
482 print ' retrace::delObj(call.arg(0));'
485 arg_names = ", ".join(method.argNames())
486 if method.type is not stdapi.Void:
487 print ' _result = _this->%s(%s);' % (method.name, arg_names)
488 print ' (void)_result;'
489 self.checkResult(method.type)
491 print ' _this->%s(%s);' % (method.name, arg_names)
493 def checkResult(self, resultType):
494 if str(resultType) == 'HRESULT':
495 print r' if (FAILED(_result)) {'
496 print r' retrace::warning(call) << "failed (0x" << std::hex << _result << std::dec << ")\n";'
499 def filterFunction(self, function):
502 table_name = 'retrace::callbacks'
504 def retraceApi(self, api):
506 print '#include "os_time.hpp"'
507 print '#include "trace_parser.hpp"'
508 print '#include "retrace.hpp"'
509 print '#include "retrace_swizzle.hpp"'
512 types = api.getAllTypes()
513 handles = [type for type in types if isinstance(type, stdapi.Handle)]
515 for handle in handles:
516 if handle.name not in handle_names:
517 if handle.key is None:
518 print 'static retrace::map<%s> _%s_map;' % (handle.type, handle.name)
520 key_name, key_type = handle.key
521 print 'static std::map<%s, retrace::map<%s> > _%s_map;' % (key_type, handle.type, handle.name)
522 handle_names.add(handle.name)
525 functions = filter(self.filterFunction, api.getAllFunctions())
526 for function in functions:
527 if function.sideeffects and not function.internal:
528 self.retraceFunction(function)
529 interfaces = api.getAllInterfaces()
530 for interface in interfaces:
531 for method in interface.iterMethods():
532 if method.sideeffects and not method.internal:
533 self.retraceInterfaceMethod(interface, method)
535 print 'const retrace::Entry %s[] = {' % self.table_name
536 for function in functions:
537 if not function.internal:
538 if function.sideeffects:
539 print ' {"%s", &retrace_%s},' % (function.name, function.name)
541 print ' {"%s", &retrace::ignore},' % (function.name,)
542 for interface in interfaces:
543 for method in interface.iterMethods():
544 if method.sideeffects:
545 print ' {"%s::%s", &retrace_%s__%s},' % (interface.name, method.name, interface.name, method.name)
547 print ' {"%s::%s", &retrace::ignore},' % (interface.name, method.name)
548 print ' {NULL, NULL}'