diff --git a/llvm_emulator/emulator.py b/llvm_emulator/emulator.py index 84ef0d0..41cf336 100644 --- a/llvm_emulator/emulator.py +++ b/llvm_emulator/emulator.py @@ -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(): diff --git a/llvm_emulator/stepper.py b/llvm_emulator/stepper.py index ac3aa0e..15b8d3d 100644 --- a/llvm_emulator/stepper.py +++ b/llvm_emulator/stepper.py @@ -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 = '<>' @@ -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()