mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-27 13:30:41 +00:00 
			
		
		
		
	 907b5105f1
			
		
	
	
		907b5105f1
		
	
	
	
	
		
			
			Since commit a2ce7dbd91 ("meson: convert tests/qtest to meson"),
libqtest.h is under libqos/ directory, while libqtest.c is still in
qtest/. Move back to its original location to avoid mixing with libqos/.
Suggested-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
		
	
			
		
			
				
	
	
		
			161 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| """
 | |
| Convert plain qtest traces to C or Bash reproducers
 | |
| 
 | |
| Use this to help build bug-reports or create in-tree reproducers for bugs.
 | |
| Note: This will not format C code for you. Pipe the output through
 | |
| clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, ColumnLimit: 90}"
 | |
| or similar
 | |
| """
 | |
| 
 | |
| import sys
 | |
| import os
 | |
| import argparse
 | |
| import textwrap
 | |
| from datetime import date
 | |
| 
 | |
| __author__     = "Alexander Bulekov <alxndr@bu.edu>"
 | |
| __copyright__  = "Copyright (C) 2021, Red Hat, Inc."
 | |
| __license__    = "GPL version 2 or (at your option) any later version"
 | |
| 
 | |
| __maintainer__ = "Alexander Bulekov"
 | |
| __email__      = "alxndr@bu.edu"
 | |
| 
 | |
| 
 | |
| def c_header(owner):
 | |
|     return """/*
 | |
|  * Autogenerated Fuzzer Test Case
 | |
|  *
 | |
|  * Copyright (c) {date} {owner}
 | |
|  *
 | |
|  * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | |
|  * See the COPYING file in the top-level directory.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| 
 | |
| #include "libqtest.h"
 | |
| 
 | |
|     """.format(date=date.today().year, owner=owner)
 | |
| 
 | |
| def c_comment(s):
 | |
|     """ Return a multi-line C comment. Assume the text is already wrapped """
 | |
|     return "/*\n * " + "\n * ".join(s.splitlines()) + "\n*/"
 | |
| 
 | |
| def print_c_function(s):
 | |
|     print("/* ")
 | |
|     for l in s.splitlines():
 | |
|         print(" * {}".format(l))
 | |
| 
 | |
| def bash_reproducer(path, args, trace):
 | |
|     result = '\\\n'.join(textwrap.wrap("cat << EOF | {} {}".format(path, args),
 | |
|                                        72, break_on_hyphens=False,
 | |
|                                        drop_whitespace=False))
 | |
|     for l in trace.splitlines():
 | |
|         result += "\n" + '\\\n'.join(textwrap.wrap(l,72,drop_whitespace=False))
 | |
|     result += "\nEOF"
 | |
|     return result
 | |
| 
 | |
| def c_reproducer(name, args, trace):
 | |
|     result = []
 | |
|     result.append("""static void {}(void)\n{{""".format(name))
 | |
| 
 | |
|     # libqtest will add its own qtest args, so get rid of them
 | |
|     args = args.replace("-accel qtest","")
 | |
|     args = args.replace(",accel=qtest","")
 | |
|     args = args.replace("-machine accel=qtest","")
 | |
|     args = args.replace("-qtest stdio","")
 | |
|     result.append("""QTestState *s = qtest_init("{}");""".format(args))
 | |
|     for l in trace.splitlines():
 | |
|         param = l.split()
 | |
|         cmd = param[0]
 | |
|         if cmd == "write":
 | |
|             buf = param[3][2:] #Get the 0x... buffer and trim the "0x"
 | |
|             assert len(buf)%2 == 0
 | |
|             bufbytes = [buf[i:i+2] for i in range(0, len(buf), 2)]
 | |
|             bufstring = '\\x'+'\\x'.join(bufbytes)
 | |
|             addr = param[1]
 | |
|             size = param[2]
 | |
|             result.append("""qtest_bufwrite(s, {}, "{}", {});""".format(
 | |
|                           addr, bufstring, size))
 | |
|         elif cmd.startswith("in") or cmd.startswith("read"):
 | |
|             result.append("qtest_{}(s, {});".format(
 | |
|                           cmd, param[1]))
 | |
|         elif cmd.startswith("out") or cmd.startswith("write"):
 | |
|             result.append("qtest_{}(s, {}, {});".format(
 | |
|                           cmd, param[1], param[2]))
 | |
|         elif cmd == "clock_step":
 | |
|             if len(param) ==1:
 | |
|                 result.append("qtest_clock_step_next(s);")
 | |
|             else:
 | |
|                 result.append("qtest_clock_step(s, {});".format(param[1]))
 | |
|     result.append("qtest_quit(s);\n}")
 | |
|     return "\n".join(result)
 | |
| 
 | |
| def c_main(name, arch):
 | |
|     return """int main(int argc, char **argv)
 | |
| {{
 | |
|     const char *arch = qtest_get_arch();
 | |
| 
 | |
|     g_test_init(&argc, &argv, NULL);
 | |
| 
 | |
|    if (strcmp(arch, "{arch}") == 0) {{
 | |
|         qtest_add_func("fuzz/{name}",{name});
 | |
|    }}
 | |
| 
 | |
|    return g_test_run();
 | |
| }}""".format(name=name, arch=arch)
 | |
| 
 | |
| def main():
 | |
|     parser = argparse.ArgumentParser()
 | |
|     group = parser.add_mutually_exclusive_group()
 | |
|     group.add_argument("-bash", help="Only output a copy-pastable bash command",
 | |
|                         action="store_true")
 | |
|     group.add_argument("-c", help="Only output a c function",
 | |
|                         action="store_true")
 | |
|     parser.add_argument('-owner', help="If generating complete C source code, \
 | |
|                         this specifies the Copyright owner",
 | |
|                         nargs='?', default="<name of author>")
 | |
|     parser.add_argument("-no_comment", help="Don't include a bash reproducer \
 | |
|                         as a comment in the C reproducers",
 | |
|                         action="store_true")
 | |
|     parser.add_argument('-name', help="The name of the c function",
 | |
|                         nargs='?', default="test_fuzz")
 | |
|     parser.add_argument('input_trace', help="input QTest command sequence \
 | |
|                         (stdin by default)",
 | |
|                         nargs='?', type=argparse.FileType('r'),
 | |
|                         default=sys.stdin)
 | |
|     args = parser.parse_args()
 | |
| 
 | |
|     qemu_path = os.getenv("QEMU_PATH")
 | |
|     qemu_args = os.getenv("QEMU_ARGS")
 | |
|     if not qemu_args or not qemu_path:
 | |
|         print("Please set QEMU_PATH and QEMU_ARGS environment variables")
 | |
|         sys.exit(1)
 | |
| 
 | |
|     bash_args = qemu_args
 | |
|     if " -qtest stdio" not in  qemu_args:
 | |
|         bash_args += " -qtest stdio"
 | |
| 
 | |
|     arch = qemu_path.split("-")[-1]
 | |
|     trace = args.input_trace.read().strip()
 | |
| 
 | |
|     if args.bash :
 | |
|         print(bash_reproducer(qemu_path, bash_args, trace))
 | |
|     else:
 | |
|         output = ""
 | |
|         if not args.c:
 | |
|             output += c_header(args.owner) + "\n"
 | |
|         if not args.no_comment:
 | |
|             output += c_comment(bash_reproducer(qemu_path, bash_args, trace))
 | |
|         output += c_reproducer(args.name, qemu_args, trace)
 | |
|         if not args.c:
 | |
|             output += c_main(args.name, arch)
 | |
|         print(output)
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 |