Skip to content

Commit

Permalink
added fake stub generation
Browse files Browse the repository at this point in the history
  • Loading branch information
cnvogelg committed Mar 16, 2018
1 parent 3289a83 commit fc5f0e8
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 31 deletions.
78 changes: 52 additions & 26 deletions amitools/vamos/libcore/stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -129,17 +149,20 @@ 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

# wrap profiling?
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)
Expand Down Expand Up @@ -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:
Expand All @@ -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)
Expand Down
105 changes: 100 additions & 5 deletions test/unit/libcore_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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()
Expand All @@ -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()
Expand All @@ -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'
Expand All @@ -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'
Expand All @@ -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()
Expand All @@ -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)
Expand All @@ -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'
Expand All @@ -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)

0 comments on commit fc5f0e8

Please sign in to comment.