xref: /openbsd-src/gnu/llvm/lldb/utils/lldb-repro/lldb-repro.py (revision be691f3bb6417f04a68938fadbcaee2d5795e764)
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