include/ui/console.h: Delete is_surface_bgr()
[qemu/ar7.git] / scripts / oss-fuzz / output_reproducer.py
blob3608b0600e00d737c1eea025e71a2b8fde5a5762
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 """
5 Convert plain qtest traces to C or Bash reproducers
7 Use this to help build bug-reports or create in-tree reproducers for bugs.
8 Note: This will not format C code for you. Pipe the output through
9 clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, ColumnLimit: 90}"
10 or similar
11 """
13 import sys
14 import os
15 import argparse
16 import textwrap
17 from datetime import date
19 __author__ = "Alexander Bulekov <alxndr@bu.edu>"
20 __copyright__ = "Copyright (C) 2021, Red Hat, Inc."
21 __license__ = "GPL version 2 or (at your option) any later version"
23 __maintainer__ = "Alexander Bulekov"
24 __email__ = "alxndr@bu.edu"
27 def c_header(owner):
28 return """/*
29 * Autogenerated Fuzzer Test Case
31 * Copyright (c) {date} {owner}
33 * This work is licensed under the terms of the GNU GPL, version 2 or later.
34 * See the COPYING file in the top-level directory.
37 #include "qemu/osdep.h"
39 #include "libqos/libqtest.h"
41 """.format(date=date.today().year, owner=owner)
43 def c_comment(s):
44 """ Return a multi-line C comment. Assume the text is already wrapped """
45 return "/*\n * " + "\n * ".join(s.splitlines()) + "\n*/"
47 def print_c_function(s):
48 print("/* ")
49 for l in s.splitlines():
50 print(" * {}".format(l))
52 def bash_reproducer(path, args, trace):
53 result = '\\\n'.join(textwrap.wrap("cat << EOF | {} {}".format(path, args),
54 72, break_on_hyphens=False,
55 drop_whitespace=False))
56 for l in trace.splitlines():
57 result += "\n" + '\\\n'.join(textwrap.wrap(l,72,drop_whitespace=False))
58 result += "\nEOF"
59 return result
61 def c_reproducer(name, args, trace):
62 result = []
63 result.append("""static void {}(void)\n{{""".format(name))
65 # libqtest will add its own qtest args, so get rid of them
66 args = args.replace("-accel qtest","")
67 args = args.replace(",accel=qtest","")
68 args = args.replace("-machine accel=qtest","")
69 args = args.replace("-qtest stdio","")
70 result.append("""QTestState *s = qtest_init("{}");""".format(args))
71 for l in trace.splitlines():
72 param = l.split()
73 cmd = param[0]
74 if cmd == "write":
75 buf = param[3][2:] #Get the 0x... buffer and trim the "0x"
76 assert len(buf)%2 == 0
77 bufbytes = [buf[i:i+2] for i in range(0, len(buf), 2)]
78 bufstring = '\\x'+'\\x'.join(bufbytes)
79 addr = param[1]
80 size = param[2]
81 result.append("""qtest_bufwrite(s, {}, "{}", {});""".format(
82 addr, bufstring, size))
83 elif cmd.startswith("in") or cmd.startswith("read"):
84 result.append("qtest_{}(s, {});".format(
85 cmd, param[1]))
86 elif cmd.startswith("out") or cmd.startswith("write"):
87 result.append("qtest_{}(s, {}, {});".format(
88 cmd, param[1], param[2]))
89 elif cmd == "clock_step":
90 if len(param) ==1:
91 result.append("qtest_clock_step_next(s);")
92 else:
93 result.append("qtest_clock_step(s, {});".format(param[1]))
94 result.append("qtest_quit(s);\n}")
95 return "\n".join(result)
97 def c_main(name, arch):
98 return """int main(int argc, char **argv)
100 const char *arch = qtest_get_arch();
102 g_test_init(&argc, &argv, NULL);
104 if (strcmp(arch, "{arch}") == 0) {{
105 qtest_add_func("fuzz/{name}",{name});
108 return g_test_run();
109 }}""".format(name=name, arch=arch)
111 def main():
112 parser = argparse.ArgumentParser()
113 group = parser.add_mutually_exclusive_group()
114 group.add_argument("-bash", help="Only output a copy-pastable bash command",
115 action="store_true")
116 group.add_argument("-c", help="Only output a c function",
117 action="store_true")
118 parser.add_argument('-owner', help="If generating complete C source code, \
119 this specifies the Copyright owner",
120 nargs='?', default="<name of author>")
121 parser.add_argument("-no_comment", help="Don't include a bash reproducer \
122 as a comment in the C reproducers",
123 action="store_true")
124 parser.add_argument('-name', help="The name of the c function",
125 nargs='?', default="test_fuzz")
126 parser.add_argument('input_trace', help="input QTest command sequence \
127 (stdin by default)",
128 nargs='?', type=argparse.FileType('r'),
129 default=sys.stdin)
130 args = parser.parse_args()
132 qemu_path = os.getenv("QEMU_PATH")
133 qemu_args = os.getenv("QEMU_ARGS")
134 if not qemu_args or not qemu_path:
135 print("Please set QEMU_PATH and QEMU_ARGS environment variables")
136 sys.exit(1)
138 bash_args = qemu_args
139 if " -qtest stdio" not in qemu_args:
140 bash_args += " -qtest stdio"
142 arch = qemu_path.split("-")[-1]
143 trace = args.input_trace.read().strip()
145 if args.bash :
146 print(bash_reproducer(qemu_path, bash_args, trace))
147 else:
148 output = ""
149 if not args.c:
150 output += c_header(args.owner) + "\n"
151 if not args.no_comment:
152 output += c_comment(bash_reproducer(qemu_path, bash_args, trace))
153 output += c_reproducer(args.name, qemu_args, trace)
154 if not args.c:
155 output += c_main(args.name, arch)
156 print(output)
159 if __name__ == '__main__':
160 main()