diff --git a/klee_stm_gdb.py b/klee_stm_gdb.py new file mode 100644 index 0000000000000000000000000000000000000000..03c5318350ae4de87ef2810c75c7b9f963d3413a --- /dev/null +++ b/klee_stm_gdb.py @@ -0,0 +1,424 @@ +import gdb +import os +import sys +import struct +import sqlite3 +from subprocess import call +import subprocess + +# Should ideally properly just import from ktest-tool +# from .ktesttool import KTest + +version_no = 3 + +file_list = [] +file_index_current = 0 +object_index_current = 0 + +tasks = [] +task_to_test = -1 + +""" Max number of events guard """ +object_index_max = 100 + +database_name = "klee_profiling" +path = "output" + +""" Create an folder named output if it doesn't exist """ +os.makedirs(path, exist_ok=True) +""" Enter the output folder """ +os.chdir(path) + +""" Check if a database exists, otherwise create one """ + +if os.path.isfile("%s%s" % (database_name, ".db")): + os.rename("%s%s" % (database_name, ".db"), + "%s%s" % (database_name, "_old.db")) + # conn = sqlite3.connect(database_name) + # cur = conn.cursor() + # print("Opened already created database") +conn = sqlite3.connect("%s%s" % (database_name, ".db")) +cur = conn.cursor() +cur.execute('''CREATE TABLE IF NOT EXISTS events + (ID INTEGER PRIMARY KEY AUTOINCREMENT, + FILE TEXT NOT NULL, + TIME INT NOT NULL, + RESOURCE TEXT NOT NULL, + ACTION TEXT, + JOB TEXT);''') + + +class KTestError(Exception): + pass + + +class KTest: + + @staticmethod + def fromfile(path): + if not os.path.exists(path): + print("ERROR: file %s not found" % (path)) + sys.exit(1) + + f = open(path, 'rb') + hdr = f.read(5) + if len(hdr) != 5 or (hdr != b'KTEST' and hdr != b"BOUT\n"): + raise KTestError('unrecognized file') + version, = struct.unpack('>i', f.read(4)) + if version > version_no: + raise KTestError('unrecognized version') + numArgs, = struct.unpack('>i', f.read(4)) + args = [] + for i in range(numArgs): + size, = struct.unpack('>i', f.read(4)) + args.append(str(f.read(size).decode(encoding='ascii'))) + + if version >= 2: + symArgvs, = struct.unpack('>i', f.read(4)) + symArgvLen, = struct.unpack('>i', f.read(4)) + else: + symArgvs = 0 + symArgvLen = 0 + + numObjects, = struct.unpack('>i', f.read(4)) + objects = [] + for i in range(numObjects): + size, = struct.unpack('>i', f.read(4)) + name = f.read(size) + size, = struct.unpack('>i', f.read(4)) + bytes = f.read(size) + objects.append((name, bytes)) + + # Create an instance + b = KTest(version, args, symArgvs, symArgvLen, objects) + # Augment with extra filename field + b.filename = path + return b + + def __init__(self, version, args, symArgvs, symArgvLen, objects): + self.version = version + self.symArgvs = symArgvs + self.symArgvLen = symArgvLen + self.args = args + self.objects = objects + + # add a field that represents the name of the program used to + # generate this .ktest file: + program_full_path = self.args[0] + program_name = os.path.basename(program_full_path) + # sometimes program names end in .bc, so strip them + if program_name.endswith('.bc'): + program_name = program_name[:-3] + self.programName = program_name + + +class MainBP(gdb.Breakpoint): + + global tasks + + def stop(self): + # print("##### breakpoint") + global file_index_current + gdb.events.stop.connect(stop_event) + if self.location == "init": + gdb.write("Breakpoint in init()\n") + gdb.prompt_hook = prompt + gdb.post_event(posted_event_main) + # gdb.execute('stop') + # gdb.execute('jump %r' % tasks[0]) + + elif self.location == "idle": + """ Idle loop is reached, now jump to the task specified + in the "task" value in the ktest files. + + If the task value is greater than the number of tasks, + then run all of the tasks. + + """ + gdb.write("Breakpoint in loop()\n") + gdb.prompt_hook = prompt + + print("Tasks: ", tasks) + print("Name of task to test:", tasks[task_to_test]) + if not task_to_test == -1: + gdb.write('Task to call: %s \n' % tasks[task_to_test]) + gdb.execute('call %s' % tasks[task_to_test]) + else: + print("No task defined") + """ + for task in tasks: + gdb.write('Task: %r \n' % task) + gdb.execute('jump %r' % task) + """ + + # gdb.write("Breakpoint in finish_execution\n") + # gdb.write("Stopped before the main loop\n") + """ Save the execution time and + reload back to the main function. + """ + gdb.prompt_hook = prompt + gdb.post_event(posted_event_finish_execution) + + """ Needed to actually stop after the breakpoint """ + return True + + +# Subscribing to the stop events +def stop_event(evt): + # print("#### stop event") + # print("evt %r" % evt) + gdb.events.stop.disconnect(stop_event) + + +# Hooking the prompt: +def prompt(current): + # print("current %r" % current) + # print("#### prompt") + gdb.prompt_hook = current + + +# Posting events (which seem to work well when height=0) +# def posted_event(): + # print("#### posted event") + # gdb.execute(" + + +""" +Called when the breakpoint at main() is hit +""" + + +def posted_event_main(): + # print("# main BP") + global file_index_current + ktest_setdata(file_index_current) + gdb.execute("continue") + + +def posted_event_finish_execution(): + """ Called when the breakpoint at finish_execution() is hit """ + global file_list + global file_index_current + global object_index_current + global object_index_max + + # gdb.execute("print eventlist") + + # print("object_current: %r " % object_index_current) + print("object_max: %r " % object_index_max) + + while object_index_current < object_index_max: + """ Collect all data for the database """ + event_time = gdb.parse_and_eval("eventlist[" + + str(object_index_current) + + "].time") + + event_resource = gdb.parse_and_eval("eventlist[" + + str(object_index_current) + + "].elem") + + event_action = gdb.parse_and_eval("eventlist[" + + str(object_index_current) + + "].action") + + """ Parse which running job is active """ + + event_job = gdb.parse_and_eval("job") + + """ + print("file: %r " % str(file_list[file_index_current])) + print("time: %r " % int(event_time)) + print("resource: %r" % str(event_resource)) + print("action: %r" % str(event_action)) + """ + + event = [] + + event.append(str(file_list[file_index_current])) + event.append(int(event_time)) + event.append(str(event_resource)) + event.append(str(event_action)) + event.append("j" + str(event_job)) + + print("Event: %r " % event) + + try: + cur = conn.cursor() + + cur.execute('INSERT INTO events(FILE, TIME, RESOURCE, ACTION, JOB)\ + VALUES (?,?,?,?,?)', event) + + except sqlite3.Error as e: + print("An error occurred:", e.args[0]) + + object_index_current += 1 + """ If this was the END of execution go for next file """ + if str(event_action) == 'E': + """ All events covered, break out from loop """ + break + + """ Reset object counter for next file """ + file_index_current += 1 + object_index_current = 0 + + """ All done, commit to database and tidy after ourselves """ + if len(file_list) == file_index_current: + print("Committing to database") + conn.commit() + + conn.close() + gdb.execute("quit") + else: + gdb.execute("run") + + +def trimZeros(str): + for i in range(len(str))[::-1]: + if str[i] != '\x00': + return str[:i + 1] + + return '' + + +def ktest_setdata(file_index): + """ + Substitute every variable found in ktest-file + """ + global file_list + global task_to_test + b = KTest.fromfile(file_list[file_index]) + # print('ktest filename : %r' % filename) + gdb.write('ktest file: %r \n' % file_list[file_index]) + # print('args : %r' % b.args) + # print('num objects: %r' % len(b.objects)) + for i, (name, data) in enumerate(b.objects): + str = trimZeros(data) + + """ If Name is "task", skip it """ + if name.decode('UTF-8') == "task": + print('object %4d: name: %r' % (i, name)) + print('object %4d: size: %r' % (i, len(data))) + # print(struct.unpack('i', str).repr()) + # task_to_test = struct.unpack('i', str)[0] + # print("str: ", str) + # print("str: ", str[0]) + # task_to_test = struct.unpack('i', str)[0] + task_to_test = int(str[0]) + print("Task to test:", task_to_test) + else: + print('object %4d: name: %r' % (i, name)) + print('object %4d: size: %r' % (i, len(data))) + # if opts.writeInts and len(data) == 4: + obj_data = struct.unpack('i', str)[0] + print('object %4d: data: %r' % + (i, obj_data)) + # gdb.execute('whatis %r' % name.decode('UTF-8')) + gdb.execute('whatis %r' % obj_data) + gdb.execute('set variable %s = %r' % + (name.decode('UTF-8'), obj_data)) + # gdb.write('Variable %s is:' % name.decode('UTF-8')) + # gdb.execute('print %s' % name.decode('UTF-8')) + # else: + # print('object %4d: data: %r' % (i, str)) + print("Done with setdata") + + +def ktest_iterate(): + """ Get the list of folders in current directory, sort and then grab the + last one. + """ + curdir = os.getcwd() + print(curdir) + """ We have already entered the output folder """ + rustoutputfolder = "../target/x86_64-unknown-linux-gnu/debug/examples/" + try: + os.chdir(rustoutputfolder) + except IOError: + print(rustoutputfolder + "not found. Need to run\n") + print("xargo build --example resource --features\ + klee_mode --target x86_64-unknown-linux-gnu ") + + dirlist = next(os.walk("."))[1] + dirlist.sort() + print(dirlist) + try: + directory = dirlist[-1] + except IOError: + print("No KLEE output, need to run KLEE") + + print("Using ktest-files from directory:\n" + rustoutputfolder + directory) + + """ Iterate over all files ending with ktest in the "klee-last" folder """ + for filename in os.listdir(directory): + if filename.endswith(".ktest"): + file_list.append(os.path.join(rustoutputfolder + directory, + filename)) + else: + continue + + file_list.sort() + """ Return to the old path """ + os.chdir(curdir) + return file_list + + +def tasklist_get(): + """ Parse the automatically generated tasklist + """ + + print(os.getcwd()) + with open('../klee/tasks.txt') as fin: + for line in fin: + print(line) + if not line == "// autogenerated file\n": + return [x.strip().strip("[]\"") for x in line.split(',')] + + +def klee_run(): + + PWD = os.getcwd() + user_id = subprocess.check_output(['id', '-u']).decode() + group_id = subprocess.check_output(['id', '-g']).decode() + + klee_cmd = ("docker run --rm --user " + + user_id[:-1] + ":" + group_id[:-1] + + " -v " + + PWD + + "/target/x86_64-unknown-linux-gnu/debug/examples:/mnt\ + -w /mnt -it afoht/llvm-klee-4 /bin/bash -c 'klee '*'.bc'") + print(klee_cmd) + call(klee_cmd, shell=True) + + +""" Run KLEE on the generated files """ +# print(klee_run()) + + +""" Break at main to set variable values """ +# AddBreakpoint("main") +MainBP("init") + + +""" Tell gdb-dashboard to hide """ +gdb.execute("dashboard -enabled off") +gdb.execute("dashboard -output /dev/null") + +""" Also break at the main-loop """ +MainBP("idle") +# MainBP("terminate_execution") + +"""Used for making it scriptable""" +gdb.execute("set confirm off") +gdb.execute("set pagination off") +gdb.execute("set verbose off") + +""" Save all ktest files into an array """ +file_list = ktest_iterate() +print(file_list) + +""" Get all the tasks to jump to """ +tasks = tasklist_get() +print(tasks) + +""" Run until the main() breakpoint """ +gdb.execute("c")