1dda28197Spatrick#!/usr/bin/env python 2dda28197Spatrick"""lldb-repro 3dda28197Spatrick 4dda28197Spatricklldb-repro is a utility to transparently capture and replay debugger sessions 5dda28197Spatrickthrough the command line driver. Its used to test the reproducers by running 6dda28197Spatrickthe test suite twice. 7dda28197Spatrick 8dda28197SpatrickDuring the first run, with 'capture' as its first argument, it captures a 9dda28197Spatrickreproducer for every lldb invocation and saves it to a well-know location 10dda28197Spatrickderived from the arguments and current working directory. 11dda28197Spatrick 12dda28197SpatrickDuring the second run, with 'replay' as its first argument, the test suite is 13dda28197Spatrickrun again but this time every invocation of lldb replays the previously 14dda28197Spatrickrecorded session. 15dda28197Spatrick""" 16dda28197Spatrick 17dda28197Spatrickimport hashlib 18dda28197Spatrickimport os 19dda28197Spatrickimport shutil 20dda28197Spatrickimport subprocess 21dda28197Spatrickimport sys 22dda28197Spatrickimport tempfile 23dda28197Spatrick 24dda28197Spatrick 25dda28197Spatrickdef help(): 26dda28197Spatrick print("usage: {} capture|replay [args]".format(sys.argv[0])) 27dda28197Spatrick 28dda28197Spatrick 29dda28197Spatrickdef main(): 30dda28197Spatrick if len(sys.argv) < 2: 31dda28197Spatrick help() 32dda28197Spatrick return 1 33dda28197Spatrick 34dda28197Spatrick # Compute an MD5 hash based on the input arguments and the current working 35dda28197Spatrick # directory. 36dda28197Spatrick h = hashlib.md5() 37dda28197Spatrick h.update(' '.join(sys.argv[2:]).encode('utf-8')) 38dda28197Spatrick h.update(os.getcwd().encode('utf-8')) 39dda28197Spatrick input_hash = h.hexdigest() 40dda28197Spatrick 41dda28197Spatrick # Use the hash to "uniquely" identify a reproducer path. 42dda28197Spatrick reproducer_path = os.path.join(tempfile.gettempdir(), input_hash) 43dda28197Spatrick 44dda28197Spatrick # Create a new lldb invocation with capture or replay enabled. 45dda28197Spatrick lldb = os.path.join(os.path.dirname(sys.argv[0]), 'lldb') 46dda28197Spatrick new_args = [lldb] 47dda28197Spatrick if sys.argv[1] == "replay": 48dda28197Spatrick new_args.extend(['--replay', reproducer_path]) 49dda28197Spatrick elif sys.argv[1] == "capture": 50dda28197Spatrick new_args.extend([ 51dda28197Spatrick '--capture', '--capture-path', reproducer_path, 52dda28197Spatrick '--reproducer-generate-on-exit' 53dda28197Spatrick ]) 54dda28197Spatrick new_args.extend(sys.argv[2:]) 55dda28197Spatrick else: 56dda28197Spatrick help() 57dda28197Spatrick return 1 58dda28197Spatrick 59dda28197Spatrick exit_code = subprocess.call(new_args) 60dda28197Spatrick 61dda28197Spatrick # The driver always exists with a zero exit code during replay. Store the 62dda28197Spatrick # exit code and return that for tests that expect a non-zero exit code. 63dda28197Spatrick exit_code_path = os.path.join(reproducer_path, 'exit_code.txt') 64dda28197Spatrick if sys.argv[1] == "replay": 65*be691f3bSpatrick replay_exit_code = exit_code 66dda28197Spatrick with open(exit_code_path, 'r') as f: 67dda28197Spatrick exit_code = int(f.read()) 68*be691f3bSpatrick if replay_exit_code != 0: 69*be691f3bSpatrick print("error: replay failed with exit code {}".format(replay_exit_code)) 70*be691f3bSpatrick print("invocation: " + " ".join(new_args)) 71*be691f3bSpatrick # Return 1 if the expected exit code is 0 or vice versa. 72*be691f3bSpatrick return 1 if (exit_code == 0) else 0 73dda28197Spatrick shutil.rmtree(reproducer_path, True) 74dda28197Spatrick elif sys.argv[1] == "capture": 75dda28197Spatrick with open(exit_code_path, 'w') as f: 76dda28197Spatrick f.write('%d' % exit_code) 77dda28197Spatrick 78dda28197Spatrick return exit_code 79dda28197Spatrick 80dda28197Spatrick 81dda28197Spatrickif __name__ == '__main__': 82dda28197Spatrick exit(main()) 83