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
def main():
arg_parser = argparse.ArgumentParser(description='A hacky LLVM-- emulator/debugger')
@ -18,6 +17,16 @@ def main():
action='store', type=str, dest='auto_path',
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 = vars(parse_res_raw)
@ -26,12 +35,16 @@ def main():
llvm_parser.build()
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:
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:
file_contents = f.read()
print('Parsing {}'
@ -39,7 +52,7 @@ def go_auto(path_to_file):
ast = llvm_parser.parse(file_contents)
print('Beginning execution of {}'
.format(path_to_file))
stepper.auto_step(ast)
stepper.auto_step(ast, step_behavior = step_behavior)
def enter_interactive_mode():

View File

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