added options for making the emulator less attention-demanding.

This commit is contained in:
Jon Michael Aanes 2017-11-13 18:05:44 +01:00
parent dda7b25c15
commit 6c38906a51
2 changed files with 85 additions and 43 deletions

View File

@ -10,7 +10,6 @@ import argparse
from llvm_emulator import stepper, parser from llvm_emulator import stepper, parser
def main(): def main():
arg_parser = argparse.ArgumentParser(description='A hacky LLVM-- emulator/debugger') arg_parser = argparse.ArgumentParser(description='A hacky LLVM-- emulator/debugger')
@ -18,6 +17,16 @@ def main():
action='store', type=str, dest='auto_path', action='store', type=str, dest='auto_path',
help='Automatically step through llvm in the given file') help='Automatically step through llvm in the given file')
arg_parser.add_argument('-s', '--step',
action='store', type=int, dest='auto_step',
default=100,
help='How often to prompt for continuation in auto-mode, 0 is never')
arg_parser.add_argument('-p', '--print',
action='store', type=int, dest='print_level',
default=3,
help='Verboseness of the emulator')
parse_res_raw = arg_parser.parse_args() parse_res_raw = arg_parser.parse_args()
parse_res = vars(parse_res_raw) parse_res = vars(parse_res_raw)
@ -26,12 +35,16 @@ def main():
llvm_parser.build() llvm_parser.build()
if parse_res['auto_path'] is not None: if parse_res['auto_path'] is not None:
go_auto(parse_res['auto_path']) step_behavior = {
'step_ask': parse_res['auto_step'],
'step_print_level': parse_res['print_level']
}
go_auto(parse_res['auto_path'], step_behavior)
else: else:
enter_interactive_mode() enter_interactive_mode()
def go_auto(path_to_file): def go_auto(path_to_file, step_behavior):
with open(path_to_file, 'r') as f: with open(path_to_file, 'r') as f:
file_contents = f.read() file_contents = f.read()
print('Parsing {}' print('Parsing {}'
@ -39,7 +52,7 @@ def go_auto(path_to_file):
ast = llvm_parser.parse(file_contents) ast = llvm_parser.parse(file_contents)
print('Beginning execution of {}' print('Beginning execution of {}'
.format(path_to_file)) .format(path_to_file))
stepper.auto_step(ast) stepper.auto_step(ast, step_behavior = step_behavior)
def enter_interactive_mode(): def enter_interactive_mode():

View File

@ -2,21 +2,43 @@ from enum import Enum
from llvm_emulator import ll from llvm_emulator import ll
import sys
def TODO(msg): ############
print('TODO: not implemented yet at {}' # Messages #
.format(msg))
PRINT_LEVEL_NONE = 0
PRINT_LEVEL_ERR = 1
PRINT_LEVEL_WARN = 2
PRINT_LEVEL_INFO = 3
PRINT_LEVEL = PRINT_LEVEL_NONE
###
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def err(msg): def err(msg):
print('ERROR: {}' if PRINT_LEVEL >= PRINT_LEVEL_ERR:
.format(msg)) eprint('ERROR: {}'
.format(msg))
def TODO(msg):
err('TODO: not implemented yet at {}'
.format(msg))
def warn(msg): def warn(msg):
print('WARNING: {}' if PRINT_LEVEL >= PRINT_LEVEL_WARN:
.format(msg)) eprint('WARNING: {}'
.format(msg))
def info(*args, **kwargs):
if PRINT_LEVEL >= PRINT_LEVEL_INFO:
print(*args, **kwargs)
##########
# Stuff? #
class Garbage(Enum): class Garbage(Enum):
GARBAGE = '<<Unitialized memory>>' GARBAGE = '<<Unitialized memory>>'
@ -50,14 +72,14 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
.format(ssa_target)) .format(ssa_target))
else: else:
# TODO # TODO
print('%{} <- {}' info('%{} <- {}'
.format(ssa_target, res)) .format(ssa_target, res))
ssa_env[ssa_target] = res ssa_env[ssa_target] = res
return insns_rest, terminator, blocks, stack_frames, ssa_env, heap, call_res return insns_rest, terminator, blocks, stack_frames, ssa_env, heap, call_res
# TODO # TODO
print('Evaluating {}' info('Evaluating {}'
.format(ll.insn2s(next_insn))) .format(ll.insn2s(next_insn)))
res = None res = None
@ -70,7 +92,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
res = eval_binop(bop, left_v, right_v) res = eval_binop(bop, left_v, right_v)
# TODO # TODO
print('{} {}, {}' info('{} {}, {}'
.format(bop, left_v, right_v)) .format(bop, left_v, right_v))
elif isinstance(next_insn, ll.Alloca): elif isinstance(next_insn, ll.Alloca):
ty = next_insn.ty ty = next_insn.ty
@ -78,7 +100,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
size = base_ty2size(base_ty) size = base_ty2size(base_ty)
# TODO # TODO
print('alloca {} --> allocating {} cells' info('alloca {} --> allocating {} cells'
.format(ll.ty2s(base_ty), size)) .format(ll.ty2s(base_ty), size))
ptr = len(heap) ptr = len(heap)
@ -94,7 +116,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
location_v = eval_oper(location, ssa_env, global_env) location_v = eval_oper(location, ssa_env, global_env)
# TODO # TODO
print('load heap[{}]' info('load heap[{}]'
.format(location_v)) .format(location_v))
if size != 1: if size != 1:
@ -118,7 +140,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
location_v = eval_oper(location, ssa_env, global_env) location_v = eval_oper(location, ssa_env, global_env)
# TODO # TODO
print('heap[{}] <- {}' info('heap[{}] <- {}'
.format(location_v, value_v)) .format(location_v, value_v))
if location_v == 0: if location_v == 0:
err('You are not allowed to store at location 0 (Null)') err('You are not allowed to store at location 0 (Null)')
@ -137,7 +159,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
res = eval_icmp(cnd, left_v, right_v) res = eval_icmp(cnd, left_v, right_v)
# TODO # TODO
print('icmp {} {}, {}' info('icmp {} {}, {}'
.format(cnd, left_v, right_v)) .format(cnd, left_v, right_v))
elif isinstance(next_insn, ll.Call): elif isinstance(next_insn, ll.Call):
callee = next_insn.callee callee = next_insn.callee
@ -174,7 +196,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
return insns_rest, terminator, blocks, stack_frames, ssa_env, heap, call_res return insns_rest, terminator, blocks, stack_frames, ssa_env, heap, call_res
parameters = function.parameters parameters = function.parameters
print('call @{} ({})' info('call @{} ({})'
.format(callee.val, .format(callee.val,
', '.join('%{} <- {}'.format(par[1], arg) ', '.join('%{} <- {}'.format(par[1], arg)
for par, arg in zip(parameters, arguments_v)))) for par, arg in zip(parameters, arguments_v))))
@ -196,7 +218,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
res = oper_v res = oper_v
# TODO # TODO
print('bitcast {} {} to {}' info('bitcast {} {} to {}'
.format(ll.ty2s(from_ty), oper_v, ll.ty2s(to_ty))) .format(ll.ty2s(from_ty), oper_v, ll.ty2s(to_ty)))
elif isinstance(next_insn, ll.Gep): elif isinstance(next_insn, ll.Gep):
res = 0 res = 0
@ -226,12 +248,12 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
res = gep_res res = gep_res
# TODO # TODO
print('Getting adress of {}{}'. info('Getting adress of {}{}'.
format(ll.ty2s(actual_base_ty), format(ll.ty2s(actual_base_ty),
''.join('[{}]' ''.join('[{}]'
.format(eval_oper(step_oper, ssa_env, global_env)) .format(eval_oper(step_oper, ssa_env, global_env))
for _, step_oper in steps))) for _, step_oper in steps)))
print('Gep formula: {}' info('Gep formula: {}'
.format(formula)) .format(formula))
elif isinstance(next_insn, ll.Zext): elif isinstance(next_insn, ll.Zext):
oper = next_insn.oper oper = next_insn.oper
@ -241,7 +263,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
res = oper_v res = oper_v
# TODO # TODO
print('zext {} {} to {}' info('zext {} {} to {}'
.format(ll.ty2s(from_ty), oper_v, ll.ty2s(to_ty))) .format(ll.ty2s(from_ty), oper_v, ll.ty2s(to_ty)))
elif isinstance(next_insn, ll.Ptrtoint): elif isinstance(next_insn, ll.Ptrtoint):
oper = next_insn.oper oper = next_insn.oper
@ -251,7 +273,7 @@ def step(insns, terminator, blocks, stack_frames, ssa_env, global_env, heap,
res = oper_v res = oper_v
# TODO # TODO
print('ptrtoint {}* {} to {}' info('ptrtoint {}* {} to {}'
.format(ll.ty2s(pointer_ty), oper_v, ll.ty2s(to_ty))) .format(ll.ty2s(pointer_ty), oper_v, ll.ty2s(to_ty)))
elif isinstance(next_insn, ll.CallResult): elif isinstance(next_insn, ll.CallResult):
res = next_insn.val res = next_insn.val
@ -269,7 +291,7 @@ def terminate(terminator, blocks, stack_frames, ssa_env, global_env, heap, call_
if id is not None and id in ssa_env: if id is not None and id in ssa_env:
del ssa_env[id] del ssa_env[id]
print('Evaluating {}' info('Evaluating {}'
.format(ll.terminator2s(terminator))) .format(ll.terminator2s(terminator)))
if isinstance(terminator, ll.Ret): if isinstance(terminator, ll.Ret):
@ -280,7 +302,7 @@ def terminate(terminator, blocks, stack_frames, ssa_env, global_env, heap, call_
oper_v = eval_oper(oper, ssa_env, global_env) oper_v = eval_oper(oper, ssa_env, global_env)
# TODO # TODO
print('Returning {}' info('Returning {}'
.format(oper_v)) .format(oper_v))
if len(stack_frames) == 0: if len(stack_frames) == 0:
@ -309,7 +331,7 @@ def terminate(terminator, blocks, stack_frames, ssa_env, global_env, heap, call_
clear_block_from_ssa_env(new_insns, ssa_env) clear_block_from_ssa_env(new_insns, ssa_env)
# TODO # TODO
print('Jumping unconditionally to {}' info('Jumping unconditionally to {}'
.format(label)) .format(label))
return (new_insns, new_terminator, blocks, stack_frames, return (new_insns, new_terminator, blocks, stack_frames,
@ -333,7 +355,7 @@ def terminate(terminator, blocks, stack_frames, ssa_env, global_env, heap, call_
clear_block_from_ssa_env(new_insns, ssa_env) clear_block_from_ssa_env(new_insns, ssa_env)
# TODO # TODO
print('Operand was {}. Branching to {}' info('Operand was {}. Branching to {}'
.format(operand_v, label)) .format(operand_v, label))
return (new_insns, new_terminator, blocks, stack_frames, return (new_insns, new_terminator, blocks, stack_frames,
@ -532,7 +554,7 @@ def alloc_globals(gdecls, heap):
for gdecl in gdecls.values(): for gdecl in gdecls.values():
location = alloc_global(gdecl.body) location = alloc_global(gdecl.body)
print('heap[{}] <- {}' info('heap[{}] <- {}'
.format(location, ll.gdecl2s(gdecl))) .format(location, ll.gdecl2s(gdecl)))
global_env[gdecl.name] = location global_env[gdecl.name] = location
@ -549,7 +571,7 @@ def emulate_builtin(name, arguments_v, ssa_env, heap):
.format(name)) .format(name))
else: else:
size = max(arguments_v[0], 1) size = max(arguments_v[0], 1)
print('Allocating {} cells' info('Allocating {} cells'
.format(size)) .format(size))
res = len(heap) res = len(heap)
for i in range(size): for i in range(size):
@ -562,7 +584,7 @@ def emulate_builtin(name, arguments_v, ssa_env, heap):
num_elements = arguments_v[0] num_elements = arguments_v[0]
cells_per_element = arguments_v[1] cells_per_element = arguments_v[1]
init_ptr = arguments_v[2] init_ptr = arguments_v[2]
print('Allocating {} + {} + {} (size + pointer + content) cells' info('Allocating {} + {} + {} (size + pointer + content) cells'
.format(1, 1, num_elements * cells_per_element)) .format(1, 1, num_elements * cells_per_element))
struct_begin = len(heap) struct_begin = len(heap)
heap.append(num_elements) heap.append(num_elements)
@ -588,7 +610,7 @@ def emulate_builtin(name, arguments_v, ssa_env, heap):
.format(array_begin, array_last, .format(array_begin, array_last,
init_ptr, init_val_s)) init_ptr, init_val_s))
# TODO # TODO
print('heap[{}] <- {}, heap[{}] <- {}, --- {}' info('heap[{}] <- {}, heap[{}] <- {}, --- {}'
.format(struct_begin, num_elements, .format(struct_begin, num_elements,
struct_begin + 1, array_begin, struct_begin + 1, array_begin,
array_init_s)) array_init_s))
@ -599,10 +621,10 @@ def emulate_builtin(name, arguments_v, ssa_env, heap):
.format(name)) .format(name))
else: else:
struct_begin = arguments_v[1] struct_begin = arguments_v[1]
print('Printing string, heap[{}]:' info('Printing string, heap[{}]:'
.format(struct_begin)) .format(struct_begin))
printee = heap[struct_begin + 1] printee = heap[struct_begin + 1]
print(printee) info(printee)
if not isinstance(printee, str): if not isinstance(printee, str):
warn('What was printed, was not stored as a string in the heap') warn('What was printed, was not stored as a string in the heap')
elif name in builtins: elif name in builtins:
@ -616,7 +638,14 @@ def emulate_builtin(name, arguments_v, ssa_env, heap):
return res, new_heap, should_exit return res, new_heap, should_exit
def auto_step(ast, function_name='tigermain', function_args=[1234, 5678]): def auto_step(ast, function_name='tigermain', function_args=[1234, 5678], step_behavior={}):
# Set info-level
if True:
global PRINT_LEVEL
PRINT_LEVEL = step_behavior['step_print_level']
print(PRINT_LEVEL)
#
tdecls = ast.tdecls tdecls = ast.tdecls
fdecls = ast.fdecls fdecls = ast.fdecls
gdecls = ast.gdecls gdecls = ast.gdecls
@ -633,9 +662,9 @@ def auto_step(ast, function_name='tigermain', function_args=[1234, 5678]):
call_res = [] call_res = []
global_env = alloc_globals(gdecls, heap) global_env = alloc_globals(gdecls, heap)
print('Heap after globals are allocated:') info('Heap after globals are allocated:')
print(heap) info(heap)
print() info()
step_cnt = 0 step_cnt = 0
while True: while True:
@ -644,18 +673,18 @@ def auto_step(ast, function_name='tigermain', function_args=[1234, 5678]):
stack_frames, ssa_env, stack_frames, ssa_env,
global_env, heap, tdecls, global_env, heap, tdecls,
fdecls, call_res) fdecls, call_res)
print() info()
step_cnt += 1 step_cnt += 1
if terminator is None: if terminator is None:
print('Stepping done!\nFinal ssa_env: {}' info('Stepping done!\nFinal ssa_env: {}'
.format(ssa_env)) .format(ssa_env))
print('Final heap: {}' info('Final heap: {}'
.format(heap)) .format(heap))
print('Program resulted in {} after {} steps'. info('Program resulted in {} after {} steps'.
format(insns[0][1].val, step_cnt)) format(insns[0][1].val, step_cnt))
break break
if step_cnt % 100 == 0: if step_behavior['step_ask'] != 0 and step_cnt % step_behavior['step_ask'] == 0:
while True: while True:
stop_q = input('We have now done {} steps. Continue? [Y/n]: ' stop_q = input('We have now done {} steps. Continue? [Y/n]: '
.format(step_cnt)).lower() .format(step_cnt)).lower()