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