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, lval=False):
44 if handle.key is None:
45 return "_%s_map[%s]" % (handle.name, value)
47 key_name, key_type = handle.key
48 if handle.name == "location" and lval == False:
49 return "_location_map[%s].lookupUniformLocation(%s)" % (key_name, value)
51 return "_%s_map[%s][%s]" % (handle.name, key_name, value)
54 class ValueAllocator(stdapi.Visitor):
56 def visitLiteral(self, literal, lvalue, rvalue):
59 def visitConst(self, const, lvalue, rvalue):
60 self.visit(const.type, lvalue, rvalue)
62 def visitAlias(self, alias, lvalue, rvalue):
63 self.visit(alias.type, lvalue, rvalue)
65 def visitEnum(self, enum, lvalue, rvalue):
68 def visitBitmask(self, bitmask, lvalue, rvalue):
71 def visitArray(self, array, lvalue, rvalue):
72 print ' %s = static_cast<%s *>(_allocator.alloc(&%s, sizeof *%s));' % (lvalue, array.type, rvalue, lvalue)
74 def visitPointer(self, pointer, lvalue, rvalue):
75 print ' %s = static_cast<%s *>(_allocator.alloc(&%s, sizeof *%s));' % (lvalue, pointer.type, rvalue, lvalue)
77 def visitIntPointer(self, pointer, lvalue, rvalue):
80 def visitObjPointer(self, pointer, lvalue, rvalue):
83 def visitLinearPointer(self, pointer, lvalue, rvalue):
86 def visitReference(self, reference, lvalue, rvalue):
87 self.visit(reference.type, lvalue, rvalue);
89 def visitHandle(self, handle, lvalue, rvalue):
92 def visitBlob(self, blob, lvalue, rvalue):
95 def visitString(self, string, lvalue, rvalue):
98 def visitStruct(self, struct, lvalue, rvalue):
101 def visitPolymorphic(self, polymorphic, lvalue, rvalue):
102 assert polymorphic.defaultType is not None
103 self.visit(polymorphic.defaultType, lvalue, rvalue)
105 def visitOpaque(self, opaque, lvalue, rvalue):
109 class ValueDeserializer(stdapi.Visitor, stdapi.ExpanderMixin):
111 def visitLiteral(self, literal, lvalue, rvalue):
112 print ' %s = (%s).to%s();' % (lvalue, rvalue, literal.kind)
114 def visitConst(self, const, lvalue, rvalue):
115 self.visit(const.type, lvalue, rvalue)
117 def visitAlias(self, alias, lvalue, rvalue):
118 self.visit(alias.type, lvalue, rvalue)
120 def visitEnum(self, enum, lvalue, rvalue):
121 print ' %s = static_cast<%s>((%s).toSInt());' % (lvalue, enum, rvalue)
123 def visitBitmask(self, bitmask, lvalue, rvalue):
124 self.visit(bitmask.type, lvalue, rvalue)
126 def visitArray(self, array, lvalue, rvalue):
128 tmp = '_a_' + array.tag + '_' + str(self.seq)
131 print ' if (%s) {' % (lvalue,)
132 print ' const trace::Array *%s = (%s).toArray();' % (tmp, rvalue)
133 length = '%s->values.size()' % (tmp,)
134 index = '_j' + array.tag
135 print ' for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length)
137 self.visit(array.type, '%s[%s]' % (lvalue, index), '*%s->values[%s]' % (tmp, index))
142 def visitPointer(self, pointer, lvalue, rvalue):
143 tmp = '_a_' + pointer.tag + '_' + str(self.seq)
146 print ' if (%s) {' % (lvalue,)
147 print ' const trace::Array *%s = (%s).toArray();' % (tmp, rvalue)
149 self.visit(pointer.type, '%s[0]' % (lvalue,), '*%s->values[0]' % (tmp,))
153 def visitIntPointer(self, pointer, lvalue, rvalue):
154 print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, pointer, rvalue)
156 def visitObjPointer(self, pointer, lvalue, rvalue):
157 print ' %s = static_cast<%s>(retrace::toObjPointer(call, %s));' % (lvalue, pointer, rvalue)
159 def visitLinearPointer(self, pointer, lvalue, rvalue):
160 print ' %s = static_cast<%s>(retrace::toPointer(%s));' % (lvalue, pointer, rvalue)
162 def visitReference(self, reference, lvalue, rvalue):
163 self.visit(reference.type, lvalue, rvalue);
165 def visitHandle(self, handle, lvalue, rvalue):
166 #OpaqueValueDeserializer().visit(handle.type, lvalue, rvalue);
167 self.visit(handle.type, lvalue, rvalue);
168 new_lvalue = lookupHandle(handle, lvalue)
169 print ' if (retrace::verbosity >= 2) {'
170 print ' std::cout << "%s " << size_t(%s) << " <- " << size_t(%s) << "\\n";' % (handle.name, lvalue, new_lvalue)
172 if (new_lvalue.startswith('_program_map') or new_lvalue.startswith('_shader_map')):
173 print 'if (glretrace::supportsARBShaderObjects) {'
174 print ' %s = _handleARB_map[%s];' % (lvalue, lvalue)
176 print ' %s = %s;' % (lvalue, new_lvalue)
179 print ' %s = %s;' % (lvalue, new_lvalue)
181 def visitBlob(self, blob, lvalue, rvalue):
182 print ' %s = static_cast<%s>((%s).toPointer());' % (lvalue, blob, rvalue)
184 def visitString(self, string, lvalue, rvalue):
185 print ' %s = (%s)((%s).toString());' % (lvalue, string.expr, rvalue)
189 def visitStruct(self, struct, lvalue, rvalue):
190 tmp = '_s_' + struct.tag + '_' + str(self.seq)
193 print ' const trace::Struct *%s = (%s).toStruct();' % (tmp, rvalue)
194 print ' assert(%s);' % (tmp)
195 for i in range(len(struct.members)):
196 member = struct.members[i]
197 self.visitMember(member, lvalue, '*%s->members[%s]' % (tmp, i))
199 def visitPolymorphic(self, polymorphic, lvalue, rvalue):
200 if polymorphic.defaultType is None:
201 switchExpr = self.expand(polymorphic.switchExpr)
202 print r' switch (%s) {' % switchExpr
203 for cases, type in polymorphic.iterSwitch():
207 if type.expr is not None:
208 caseLvalue = 'static_cast<%s>(%s)' % (type, caseLvalue)
211 self.visit(type, caseLvalue, rvalue)
215 if polymorphic.defaultType is None:
217 print r' retrace::warning(call) << "unexpected polymorphic case" << %s << "\n";' % (switchExpr,)
221 self.visit(polymorphic.defaultType, lvalue, rvalue)
223 def visitOpaque(self, opaque, lvalue, rvalue):
224 raise UnsupportedType
227 class OpaqueValueDeserializer(ValueDeserializer):
228 '''Value extractor that also understands opaque values.
230 Normally opaque values can't be retraced, unless they are being extracted
231 in the context of handles.'''
233 def visitOpaque(self, opaque, lvalue, rvalue):
234 print ' %s = static_cast<%s>(retrace::toPointer(%s));' % (lvalue, opaque, rvalue)
237 class SwizzledValueRegistrator(stdapi.Visitor, stdapi.ExpanderMixin):
238 '''Type visitor which will register (un)swizzled value pairs, to later be
241 def visitLiteral(self, literal, lvalue, rvalue):
244 def visitAlias(self, alias, lvalue, rvalue):
245 self.visit(alias.type, lvalue, rvalue)
247 def visitEnum(self, enum, lvalue, rvalue):
250 def visitBitmask(self, bitmask, lvalue, rvalue):
253 def visitArray(self, array, lvalue, rvalue):
254 print ' const trace::Array *_a%s = (%s).toArray();' % (array.tag, rvalue)
255 print ' if (_a%s) {' % (array.tag)
256 length = '_a%s->values.size()' % array.tag
257 index = '_j' + array.tag
258 print ' for (size_t {i} = 0; {i} < {length}; ++{i}) {{'.format(i = index, length = length)
260 self.visit(array.type, '%s[%s]' % (lvalue, index), '*_a%s->values[%s]' % (array.tag, index))
265 def visitPointer(self, pointer, lvalue, rvalue):
266 print ' const trace::Array *_a%s = (%s).toArray();' % (pointer.tag, rvalue)
267 print ' if (_a%s) {' % (pointer.tag)
269 self.visit(pointer.type, '%s[0]' % (lvalue,), '*_a%s->values[0]' % (pointer.tag,))
273 def visitIntPointer(self, pointer, lvalue, rvalue):
276 def visitObjPointer(self, pointer, lvalue, rvalue):
277 print r' retrace::addObj(call, %s, %s);' % (rvalue, lvalue)
279 def visitLinearPointer(self, pointer, lvalue, rvalue):
280 assert pointer.size is not None
281 if pointer.size is not None:
282 print r' retrace::addRegion((%s).toUIntPtr(), %s, %s);' % (rvalue, lvalue, pointer.size)
284 def visitReference(self, reference, lvalue, rvalue):
287 def visitHandle(self, handle, lvalue, rvalue):
288 print ' %s _origResult;' % handle.type
289 OpaqueValueDeserializer().visit(handle.type, '_origResult', rvalue);
290 if handle.range is None:
291 rvalue = "_origResult"
292 entry = lookupHandle(handle, rvalue, True)
293 if (entry.startswith('_program_map') or entry.startswith('_shader_map')):
294 print 'if (glretrace::supportsARBShaderObjects) {'
295 print ' _handleARB_map[%s] = %s;' % (rvalue, lvalue)
297 print ' %s = %s;' % (entry, lvalue)
300 print " %s = %s;" % (entry, lvalue)
301 print ' if (retrace::verbosity >= 2) {'
302 print ' std::cout << "{handle.name} " << {rvalue} << " -> " << {lvalue} << "\\n";'.format(**locals())
305 i = '_h' + handle.tag
306 lvalue = "%s + %s" % (lvalue, i)
307 rvalue = "_origResult + %s" % (i,)
308 entry = lookupHandle(handle, rvalue)
309 print ' for ({handle.type} {i} = 0; {i} < {handle.range}; ++{i}) {{'.format(**locals())
310 print ' {entry} = {lvalue};'.format(**locals())
311 print ' if (retrace::verbosity >= 2) {'
312 print ' std::cout << "{handle.name} " << ({rvalue}) << " -> " << ({lvalue}) << "\\n";'.format(**locals())
316 def visitBlob(self, blob, lvalue, rvalue):
319 def visitString(self, string, lvalue, rvalue):
324 def visitStruct(self, struct, lvalue, rvalue):
325 tmp = '_s_' + struct.tag + '_' + str(self.seq)
328 print ' const trace::Struct *%s = (%s).toStruct();' % (tmp, rvalue)
329 print ' assert(%s);' % (tmp,)
330 print ' (void)%s;' % (tmp,)
331 for i in range(len(struct.members)):
332 member = struct.members[i]
333 self.visitMember(member, lvalue, '*%s->members[%s]' % (tmp, i))
335 def visitPolymorphic(self, polymorphic, lvalue, rvalue):
336 assert polymorphic.defaultType is not None
337 self.visit(polymorphic.defaultType, lvalue, rvalue)
339 def visitOpaque(self, opaque, lvalue, rvalue):
345 def retraceFunction(self, function):
346 print 'static void retrace_%s(trace::Call &call) {' % function.name
347 self.retraceFunctionBody(function)
351 def retraceInterfaceMethod(self, interface, method):
352 print 'static void retrace_%s__%s(trace::Call &call) {' % (interface.name, method.name)
353 self.retraceInterfaceMethodBody(interface, method)
357 def retraceFunctionBody(self, function):
358 assert function.sideeffects
360 if function.type is not stdapi.Void:
361 self.checkOrigResult(function)
363 self.deserializeArgs(function)
365 self.declareRet(function)
366 self.invokeFunction(function)
368 self.swizzleValues(function)
370 def retraceInterfaceMethodBody(self, interface, method):
371 assert method.sideeffects
373 if method.type is not stdapi.Void:
374 self.checkOrigResult(method)
376 self.deserializeThisPointer(interface)
378 self.deserializeArgs(method)
380 self.declareRet(method)
381 self.invokeInterfaceMethod(interface, method)
383 self.swizzleValues(method)
385 def checkOrigResult(self, function):
386 '''Hook for checking the original result, to prevent succeeding now
387 where the original did not, which would cause diversion and potentially
388 unpredictable results.'''
390 assert function.type is not stdapi.Void
392 if str(function.type) == 'HRESULT':
393 print r' if (call.ret && FAILED(call.ret->toSInt())) {'
397 def deserializeThisPointer(self, interface):
398 print r' %s *_this;' % (interface.name,)
399 print r' _this = static_cast<%s *>(retrace::toObjPointer(call, call.arg(0)));' % (interface.name,)
400 print r' if (!_this) {'
404 def deserializeArgs(self, function):
405 print ' retrace::ScopedAllocator _allocator;'
406 print ' (void)_allocator;'
408 for arg in function.args:
409 arg_type = arg.type.mutable()
410 print ' %s %s;' % (arg_type, arg.name)
411 rvalue = 'call.arg(%u)' % (arg.index,)
414 self.extractArg(function, arg, arg_type, lvalue, rvalue)
415 except UnsupportedType:
417 print ' memset(&%s, 0, sizeof %s); // FIXME' % (arg.name, arg.name)
422 self.failFunction(function)
423 sys.stderr.write('warning: unsupported %s call\n' % function.name)
426 def swizzleValues(self, function):
427 for arg in function.args:
429 arg_type = arg.type.mutable()
430 rvalue = 'call.arg(%u)' % (arg.index,)
433 self.regiterSwizzledValue(arg_type, lvalue, rvalue)
434 except UnsupportedType:
435 print ' // XXX: %s' % arg.name
436 if function.type is not stdapi.Void:
440 self.regiterSwizzledValue(function.type, lvalue, rvalue)
441 except UnsupportedType:
443 print ' // XXX: result'
445 def failFunction(self, function):
446 print ' if (retrace::verbosity >= 0) {'
447 print ' retrace::unsupported(call);'
451 def extractArg(self, function, arg, arg_type, lvalue, rvalue):
452 ValueAllocator().visit(arg_type, lvalue, rvalue)
454 ValueDeserializer().visit(arg_type, lvalue, rvalue)
456 def extractOpaqueArg(self, function, arg, arg_type, lvalue, rvalue):
458 ValueAllocator().visit(arg_type, lvalue, rvalue)
459 except UnsupportedType:
461 OpaqueValueDeserializer().visit(arg_type, lvalue, rvalue)
463 def regiterSwizzledValue(self, type, lvalue, rvalue):
464 visitor = SwizzledValueRegistrator()
465 visitor.visit(type, lvalue, rvalue)
467 def declareRet(self, function):
468 if function.type is not stdapi.Void:
469 print ' %s _result;' % (function.type)
471 def invokeFunction(self, function):
472 arg_names = ", ".join(function.argNames())
473 if function.type is not stdapi.Void:
474 print ' _result = %s(%s);' % (function.name, arg_names)
475 print ' (void)_result;'
476 self.checkResult(function.type)
478 print ' %s(%s);' % (function.name, arg_names)
480 def invokeInterfaceMethod(self, interface, method):
481 # On release our reference when we reach Release() == 0 call in the
483 if method.name == 'Release':
484 print ' if (call.ret->toUInt() == 0) {'
485 print ' retrace::delObj(call.arg(0));'
488 arg_names = ", ".join(method.argNames())
489 if method.type is not stdapi.Void:
490 print ' _result = _this->%s(%s);' % (method.name, arg_names)
491 print ' (void)_result;'
492 self.checkResult(method.type)
494 print ' _this->%s(%s);' % (method.name, arg_names)
496 def checkResult(self, resultType):
497 if str(resultType) == 'HRESULT':
498 print r' if (FAILED(_result)) {'
499 print ' static char szMessageBuffer[128];'
500 print r' retrace::warning(call) << "call returned 0x" << std::hex << _result << std::dec << ": " << (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, _result, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szMessageBuffer, sizeof szMessageBuffer, NULL) ? szMessageBuffer : "???") << "\n";'
503 def filterFunction(self, function):
506 table_name = 'retrace::callbacks'
508 def retraceApi(self, api):
510 print '#include "os_time.hpp"'
511 print '#include "trace_parser.hpp"'
512 print '#include "retrace.hpp"'
513 print '#include "retrace_swizzle.hpp"'
516 types = api.getAllTypes()
517 handles = [type for type in types if isinstance(type, stdapi.Handle)]
519 for handle in handles:
520 if handle.name not in handle_names:
521 if handle.key is None:
522 print 'static retrace::map<%s> _%s_map;' % (handle.type, handle.name)
524 key_name, key_type = handle.key
525 print 'static std::map<%s, retrace::map<%s> > _%s_map;' % (key_type, handle.type, handle.name)
526 handle_names.add(handle.name)
529 functions = filter(self.filterFunction, api.getAllFunctions())
530 for function in functions:
531 if function.sideeffects and not function.internal:
532 self.retraceFunction(function)
533 interfaces = api.getAllInterfaces()
534 for interface in interfaces:
535 for method in interface.iterMethods():
536 if method.sideeffects and not method.internal:
537 self.retraceInterfaceMethod(interface, method)
539 print 'const retrace::Entry %s[] = {' % self.table_name
540 for function in functions:
541 if not function.internal:
542 if function.sideeffects:
543 print ' {"%s", &retrace_%s},' % (function.name, function.name)
545 print ' {"%s", &retrace::ignore},' % (function.name,)
546 for interface in interfaces:
547 for method in interface.iterMethods():
548 if method.sideeffects:
549 print ' {"%s::%s", &retrace_%s__%s},' % (interface.name, method.name, interface.name, method.name)
551 print ' {"%s::%s", &retrace::ignore},' % (interface.name, method.name)
552 print ' {NULL, NULL}'