xref: /openbsd-src/gnu/llvm/lldb/utils/lldb-repro/lldb-repro.py (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
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        with open(exit_code_path, 'r') as f:
66            assert exit_code == 0
67            exit_code = int(f.read())
68        shutil.rmtree(reproducer_path, True)
69    elif sys.argv[1] == "capture":
70        with open(exit_code_path, 'w') as f:
71            f.write('%d' % exit_code)
72
73    return exit_code
74
75
76if __name__ == '__main__':
77    exit(main())
78