From fc5f0e88365b56a51c1458e15af99cb1df13a195 Mon Sep 17 00:00:00 2001 From: Christian Vogelgsang Date: Fri, 16 Mar 2018 13:06:26 +0100 Subject: [PATCH] added fake stub generation --- amitools/vamos/libcore/stub.py | 78 ++++++++++++++++-------- test/unit/libcore_stub.py | 105 +++++++++++++++++++++++++++++++-- 2 files changed, 152 insertions(+), 31 deletions(-) diff --git a/amitools/vamos/libcore/stub.py b/amitools/vamos/libcore/stub.py index 307d823b..02bdf323 100644 --- a/amitools/vamos/libcore/stub.py +++ b/amitools/vamos/libcore/stub.py @@ -6,6 +6,7 @@ from amitools.vamos.Exceptions import VamosInternalError from amitools.vamos.CPU import * + class LibStub(object): """a lib stub is a frontend object instance that wraps all calls into a library implementation. @@ -16,8 +17,10 @@ class LibStub(object): A wrapped call adds exception handler, return value processing or optional profiling features. """ - def __init__(self, name, impl, fd, profile=None): - """create a stub for a given implementation and associated fd description""" + + def __init__(self, name, fd, impl=None, profile=None): + """create a stub for a given implementation and + associated fd description""" self.name = name self.impl = impl self.fd = fd @@ -47,7 +50,7 @@ def _gen_arg_dump(self, args, ctx): if reg[0] == 'a': reg_num += 8 val = ctx.cpu.r_reg(reg_num) - result.append("%s[%s]=%08x" % (name,reg,val)) + result.append("%s[%s]=%08x" % (name, reg, val)) return ", ".join(result) def _handle_exc(self): @@ -59,6 +62,7 @@ def _handle_exc(self): class LibStubGen(object): """the lib stub generator scans a lib impl and creates stubs for all methods found there""" + def __init__(self, exc_handler=None, log_missing=None, log_valid=None, ignore_invalid=True): @@ -67,6 +71,21 @@ def __init__(self, exc_handler=None, self.log_valid = log_valid self.ignore_invalid = ignore_invalid + def gen_fake_stub(self, name, fd, ctx, profile=None): + """a fake stub exists without an implementation and only contains + "missing" functions + """ + # create stub object + stub = LibStub(name, fd, profile=profile) + + # iterate all functions in fd + for fd_func in fd.get_funcs(): + stub_func = self.wrap_missing_func(fd_func, ctx, profile) + self._set_method(fd_func, stub, stub_func) + + self._set_exc_handler(stub) + return stub + def gen_stub(self, name, impl, fd, ctx, profile=None): # first scan the impl scanner = LibImplScanner(inc_std_funcs=True) @@ -75,45 +94,46 @@ def gen_stub(self, name, impl, fd, ctx, profile=None): num_invalid = scanner.get_num_invalid_funcs() num_error = scanner.get_num_error_funcs() if num_invalid > 0 and not self.ignore_invalid: - raise VamosInternalError("'%s' impl has %d invalid funcs!" % (name, num_invalid)) + raise VamosInternalError( + "'%s' impl has %d invalid funcs!" % (name, num_invalid)) if num_error > 0: - raise VamosInternalError("'%s' impl has %d error funcs!" % (name, num_error)) + raise VamosInternalError( + "'%s' impl has %d error funcs!" % (name, num_error)) # create stub object - stub = LibStub(name, impl, fd, profile) + stub = LibStub(name, fd, impl, profile) # generate valid funcs valid_funcs = scanner.get_valid_funcs().values() for fd_func, impl_method in valid_funcs: stub_func = self.wrap_func(fd_func, impl_method, ctx, profile) - # bind method to stub instance - stub_method = MethodType(stub_func, stub) - # store in stub - setattr(stub, fd_func.get_name(), stub_method) - # add to func tab - index = fd_func.get_index() - stub.func_tab[index] = stub_method + self._set_method(fd_func, stub, stub_func) # generate missing funcs missing_funcs = scanner.get_missing_funcs().values() for fd_func in missing_funcs: stub_func = self.wrap_missing_func(fd_func, ctx, profile) - # bind method to stub instance - stub_method = MethodType(stub_func, stub) - # store in stub - setattr(stub, fd_func.get_name(), stub_method) - # add to func tab - index = fd_func.get_index() - stub.func_tab[index] = stub_method + self._set_method(fd_func, stub, stub_func) + self._set_exc_handler(stub) + # return final stub + return stub + + def _set_method(self, fd_func, stub, stub_func): + # bind method to stub instance + stub_method = MethodType(stub_func, stub) + # store in stub + setattr(stub, fd_func.get_name(), stub_method) + # add to func tab + index = fd_func.get_index() + stub.func_tab[index] = stub_method + + def _set_exc_handler(self, stub): # (optional) replace exception handler of stub if self.exc_handler is not None: method = MethodType(self.exc_handler, stub) setattr(stub, "_handle_exc", method) - # return final stub - return stub - def wrap_missing_func(self, fd_func, ctx, profile): """create a stub func for a missing function in impl returns an unbound method for the stub instance @@ -129,10 +149,12 @@ def stub_func(this, *args, **kwargs): func_args = fd_func.get_args() bias = fd_func.get_bias() # with tracing + def stub_func(this, *args, **kwargs): callee_pc = this._get_callee_pc(ctx) - call_info = "%4d %s( %s ) from PC=%06x" % (bias, name, this._gen_arg_dump(func_args, ctx), callee_pc) - log.warn("? CALL: %s -> d0=0 (default)",call_info) + call_info = "%4d %s( %s ) from PC=%06x" % ( + bias, name, this._gen_arg_dump(func_args, ctx), callee_pc) + log.warn("? CALL: %s -> d0=0 (default)", call_info) ctx.cpu.w_reg(REG_D0, 0) func = stub_func @@ -140,6 +162,7 @@ def stub_func(this, *args, **kwargs): if profile: index = fd_func.get_index() prof = profile.get_func_prof(index) + def profile_func(this, *args, **kwargs): stub_func(this, *args, **kwargs) prof.count(0.0) @@ -174,9 +197,11 @@ def base_func(this, *args, **kwargs): name = fd_func.get_name() func_args = fd_func.get_args() bias = fd_func.get_bias() + def log_func(this, *args, **kwargs): callee_pc = this._get_callee_pc(ctx) - call_info = "%4d %s( %s ) from PC=%06x" % (bias, name, this._gen_arg_dump(func_args, ctx), callee_pc) + call_info = "%4d %s( %s ) from PC=%06x" % ( + bias, name, this._gen_arg_dump(func_args, ctx), callee_pc) log.info("{ CALL: %s" % call_info) res = base_func(this, *args, **kwargs) if res is not None: @@ -197,6 +222,7 @@ def log_func(this, *args, **kwargs): call = log_func else: call = base_func + def profile_func(this, *args, **kwargs): start = time.clock() call(this, *args, **kwargs) diff --git a/test/unit/libcore_stub.py b/test/unit/libcore_stub.py index e367b547..369e5e8f 100644 --- a/test/unit/libcore_stub.py +++ b/test/unit/libcore_stub.py @@ -8,12 +8,14 @@ from amitools.vamos.libcore import LibProfile from amitools.fd import read_lib_fd + def _check_stub(stub): # check func tab func_tab = stub.get_func_tab() for f in func_tab: assert f is not None + def _check_profile(fd, profile): print_hello_func = fd.get_func_by_name('PrintHello') dummy_func = fd.get_func_by_name('Dummy') @@ -23,20 +25,38 @@ def _check_profile(fd, profile): assert dummy_func_prof.get_calls() == 1 profile.dump() + def _check_log(caplog): assert caplog.record_tuples == [ - ('valid', logging.INFO, '{ CALL: 30 PrintHello( ) from PC=000000'), - ('valid', logging.INFO, '} CALL: -> d0=00000000'), - ('missing', logging.WARN, '? CALL: 54 Dummy( a[d0]=00000000, b[d1]=00000000 ) from PC=000000 -> d0=0 (default)'), - ('valid', logging.INFO, '{ CALL: 48 Swap( a[d0]=00000000, b[d1]=00000000 ) from PC=000000'), - ('valid', logging.INFO, '} CALL: -> d0=00000000, d1=00000000') + ('valid', logging.INFO, + '{ CALL: 30 PrintHello( ) from PC=000000'), + ('valid', logging.INFO, + '} CALL: -> d0=00000000'), + ('missing', logging.WARN, + '? CALL: 54 Dummy( a[d0]=00000000, b[d1]=00000000 ) from PC=000000 -> d0=0 (default)'), + ('valid', logging.INFO, + '{ CALL: 48 Swap( a[d0]=00000000, b[d1]=00000000 ) from PC=000000'), + ('valid', logging.INFO, '} CALL: -> d0=00000000, d1=00000000') + ] + + +def _check_log_fake(caplog): + assert caplog.record_tuples == [ + ('missing', logging.WARN, + '? CALL: 30 PrintHello( ) from PC=000000 -> d0=0 (default)'), + ('missing', logging.WARN, + '? CALL: 54 Dummy( a[d0]=00000000, b[d1]=00000000 ) from PC=000000 -> d0=0 (default)'), + ('missing', logging.WARN, + '? CALL: 48 Swap( a[d0]=00000000, b[d1]=00000000 ) from PC=000000 -> d0=0 (default)') ] + def _create_ctx(): cpu = MockCPU() mem = MockMemory() return LibCtx(cpu, mem) + def libcore_stub_gen_base_test(): name = 'vamostest.library' impl = VamosTestLibrary() @@ -51,6 +71,7 @@ def libcore_stub_gen_base_test(): stub.Dummy() stub.Swap() + def libcore_stub_gen_profile_test(): name = 'vamostest.library' impl = VamosTestLibrary() @@ -67,6 +88,7 @@ def libcore_stub_gen_profile_test(): stub.Swap() _check_profile(fd, profile) + def libcore_stub_gen_log_test(caplog): caplog.set_level(logging.INFO) name = 'vamostest.library' @@ -85,6 +107,7 @@ def libcore_stub_gen_log_test(caplog): stub.Swap() _check_log(caplog) + def libcore_stub_gen_log_profile_test(caplog): caplog.set_level(logging.INFO) name = 'vamostest.library' @@ -105,6 +128,7 @@ def libcore_stub_gen_log_profile_test(caplog): _check_log(caplog) _check_profile(fd, profile) + def libcore_stub_gen_exc_default_test(capsys): name = 'vamostest.library' impl = VamosTestLibrary() @@ -124,12 +148,14 @@ def libcore_stub_gen_exc_default_test(capsys): assert lines[0] == 'Traceback (most recent call last):' assert lines[-1] == 'RuntimeError: VamosTest' + def libcore_stub_gen_exc_custom_test(capsys): name = 'vamostest.library' impl = VamosTestLibrary() fd = read_lib_fd(name) ctx = _create_ctx() # create stub + def custom_handler(self): print("hello, world!") gen = LibStubGen(exc_handler=custom_handler) @@ -143,6 +169,7 @@ def custom_handler(self): lines = captured.out.strip().split('\n') assert lines[-1] == "hello, world!" + def libcore_stub_gen_multi_arg(capsys): caplog.set_level(logging.INFO) name = 'vamostest.library' @@ -162,3 +189,71 @@ def libcore_stub_gen_multi_arg(capsys): stub.Swap('hugo', None, c=3) _check_log(caplog) _check_profile(fd, profile) + + +def libcore_stub_gen_fake_base_test(): + name = 'vamostest.library' + fd = read_lib_fd(name) + ctx = _create_ctx() + # create stub + gen = LibStubGen() + stub = gen.gen_fake_stub(name, fd, ctx) + _check_stub(stub) + # call func + stub.PrintHello() + stub.Dummy() + stub.Swap() + + +def libcore_stub_gen_fake_profile_test(): + name = 'vamostest.library' + fd = read_lib_fd(name) + ctx = _create_ctx() + profile = LibProfile(name, fd) + # create stub + gen = LibStubGen() + stub = gen.gen_fake_stub(name, fd, ctx, profile) + _check_stub(stub) + # call func + stub.PrintHello() + stub.Dummy() + stub.Swap() + _check_profile(fd, profile) + + +def libcore_stub_gen_fake_log_test(caplog): + caplog.set_level(logging.INFO) + name = 'vamostest.library' + fd = read_lib_fd(name) + ctx = _create_ctx() + log_missing = logging.getLogger('missing') + log_valid = logging.getLogger('valid') + # create stub + gen = LibStubGen(log_missing=log_missing, log_valid=log_valid) + stub = gen.gen_fake_stub(name, fd, ctx) + _check_stub(stub) + # call func + stub.PrintHello() + stub.Dummy() + stub.Swap() + _check_log_fake(caplog) + + +def libcore_stub_gen_fake_log_profile_test(caplog): + caplog.set_level(logging.INFO) + name = 'vamostest.library' + fd = read_lib_fd(name) + ctx = _create_ctx() + log_missing = logging.getLogger('missing') + log_valid = logging.getLogger('valid') + profile = LibProfile(name, fd) + # create stub + gen = LibStubGen(log_missing=log_missing, log_valid=log_valid) + stub = gen.gen_fake_stub(name, fd, ctx, profile) + _check_stub(stub) + # call func + stub.PrintHello() + stub.Dummy() + stub.Swap() + _check_log_fake(caplog) + _check_profile(fd, profile)