diff --git a/lldb/scripts/generate-project.py b/lldb/scripts/generate-project.py new file mode 100755 --- /dev/null +++ b/lldb/scripts/generate-project.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 + +# Project generation script +# ========================= +# The purpose of this script is to generate a project with an arbitrary amount +# of source files. This is useful for testing the performance impact of a given +# LLDB change. For example, you can use this tool to generate a project with +# 10,000 C++ source files and see how quickly LLDB processes the resulting +# binary and related metadata (e.g. debug info). + +import os +import sys +import typing + + +def print_usage() -> None: + print("Usage: generate-project.py ") + + +def generate_c_header(directory: str, index: int) -> None: + header_path = f"{directory}/obj{index}.h" + with open(header_path, "w") as f: + f.write( + f"#ifndef _OBJ{index}_H\n" + f"#define _OBJ{index}_H\n" + f"extern int obj{index};\n" + f"void call_obj{index}(void);\n" + f"#endif // _OBJ{index}_H\n" + ) + + +def generate_c_impl(directory: str, index: int) -> None: + impl_path = f"{directory}/obj{index}.c" + with open(impl_path, "w") as f: + f.write( + "#include \n" + f'#include "obj{index}.h"\n\n' + f"int obj{index} = {index};\n" + f"void call_obj{index}(void)" + " {\n" + f' printf("%d\\n", obj{index});\n' + "}\n" + ) + + +def generate_cpp_header(directory: str, index: int) -> None: + header_path = f"{directory}/obj{index}.h" + with open(header_path, "w") as f: + f.write( + f"#ifndef _OBJ{index}_H\n" + f"#define _OBJ{index}_H\n" + "namespace obj {\n" + f"class Obj{index}" + " {\n" + "public:\n" + " static void Call(void);\n" + "}; // class\n" + "} // namespace\n" + f"#endif // _OBJ{index}_H\n" + ) + + +def generate_cpp_impl(directory: str, index: int) -> None: + impl_path = f"{directory}/obj{index}.cpp" + with open(impl_path, "w") as f: + f.write( + "#include \n" + f'#include "obj{index}.h"\n\n' + "namespace obj {\n" + f"void Obj{index}::Call(void)" + " {\n" + f" std::cout << {index} << std::endl;;\n" + "}\n" + "} // namespace\n" + ) + + +def generate_swift_impl(directory: str, index: int) -> None: + impl_path = f"{directory}/obj{index}.swift" + with open(impl_path, "w") as f: + f.write(f"public func call_obj{index}()" " {\n" f' print("{index}")\n' "}\n") + + +def generate_c_driver(directory: str, number_of_objects: int) -> None: + main_path = f"{directory}/main.c" + with open(main_path, "w") as f: + for i in range(number_of_objects): + f.write(f'#include "obj{i}.h"\n') + + f.write("int main() {\n") + for i in range(number_of_objects): + f.write(f" call_obj{i}();\n") + + f.write(" return 0;\n") + f.write("}\n") + + +def generate_cpp_driver(directory: str, number_of_objects: int) -> None: + main_path = f"{directory}/main.cpp" + with open(main_path, "w") as f: + for i in range(number_of_objects): + f.write(f'#include "obj{i}.h"\n') + + f.write("using namespace obj;\n") + f.write("int main() {\n") + for i in range(number_of_objects): + f.write(f" Obj{i}::Call();\n") + + f.write(" return 0;\n") + f.write("}\n") + + +def generate_swift_driver(directory: str, number_of_objects: int) -> None: + main_path = f"{directory}/main.swift" + with open(main_path, "w") as f: + for i in range(number_of_objects): + f.write(f"import obj{i}\n") + + f.write("public func main() {\n") + for i in range(number_of_objects): + f.write(f" call_obj{i}()\n") + f.write("}\n") + f.write("main()\n") + + +def generate_c_makefile(directory: str, number_of_objects: int) -> None: + makefile_path = f"{directory}/Makefile" + with open(makefile_path, "w") as f: + f.write("OBJDIR=objs\n\n") + f.write("objects = \\\n") + for i in range(number_of_objects): + f.write(f" $(OBJDIR)/obj{i}.o \\\n") + f.write(" $(OBJDIR)/main.o\n") + f.write("\n") + f.write( + """ +all: many-objects +objdir: +\tmkdir -p $(OBJDIR) +$(OBJDIR)/%.o: %.c objdir +\t$(CC) -g -c -o $@ $< +many-objects: $(objects) +\t$(CC) -g -o $@ $^ +clean: +\trm -rf objs/ many-objects +""" + ) + + +def generate_cpp_makefile(directory: str, number_of_objects: int) -> None: + makefile_path = f"{directory}/Makefile" + with open(makefile_path, "w") as f: + f.write("OBJDIR=objs\n\n") + f.write("objects = \\\n") + for i in range(number_of_objects): + f.write(f" $(OBJDIR)/obj{i}.o \\\n") + f.write(" $(OBJDIR)/main.o\n") + f.write("\n") + f.write( + """ +all: many-objects +objdir: +\tmkdir -p $(OBJDIR) +$(OBJDIR)/%.o: %.cpp objdir +\t$(CXX) -g -c -o $@ $< +many-objects: $(objects) +\t$(CXX) -g -o $@ $^ +clean: +\trm -rf objs/ many-objects +""" + ) + + +def generate_swift_makefile(directory: str, number_of_objects: int) -> None: + makefile_path = f"{directory}/Makefile" + with open(makefile_path, "w") as f: + # Definitions + f.write( + """ +OBJDIR=objs +SDK=$(shell xcrun --show-sdk-path) +SWIFT_FE=swift -frontend +SWIFT_FEFLAGS=-g -Onone -serialize-debugging-options \\ + -sdk $(SDK) -enable-anonymous-context-mangled-names +SWIFTC=swiftc +objects = \\ +""" + ) + for i in range(number_of_objects - 1): + f.write(f" $(OBJDIR)/obj{i}.o \\\n") + f.write(f" $(OBJDIR)/obj{number_of_objects - 1}.o\n") + f.write("\n") + + f.write( + """ +all: many-objects +objdir: +\tmkdir -p $(OBJDIR) +clean: +\trm -rf objs/ many-objects + +$(OBJDIR)/%.o: %.swift objdir +\t$(SWIFT_FE) -c -primary-file $< -I objs/ \\ +\t $(SWIFT_FEFLAGS) -module-name $(patsubst %.swift,%,$<) \\ +\t $(if $(filter-out main.swift,$<),-parse-as-library) \\ +\t -emit-module-path $(patsubst %.o,%.partial.swiftmodule,$@) \\ +\t -o $@ +\t$(SWIFT_FE) $(SWIFT_FEFLAGS) -merge-modules \\ +\t -emit-module -emit-module-interface-path \\ +\t $(patsubst %.o,%.swiftinterface,$@) \\ +\t $(patsubst %.o,%.partial.swiftmodule,$@) \\ +\t -disable-diagnostic-passes -disable-sil-perf-optzns\\ +\t -module-name $(patsubst %.swift,%,$<) \\ +\t -o $(patsubst %.o,%.swiftmodule,$@) + +$(OBJDIR)/main.o: main.swift $(objects) +\t$(SWIFT_FE) -c -primary-file $< -I objs/ \\ +\t $(SWIFT_FEFLAGS) -module-name main \\ +\t -emit-module-path $(patsubst %.o,%.partial.swiftmodule,$@) \\ +\t -o $@ +\t$(SWIFT_FE) $(SWIFT_FEFLAGS) -merge-modules \\ +\t -emit-module -emit-module-interface-path \\ +\t $(patsubst %.o,%.swiftinterface,$@) \\ +\t $(patsubst %.o,%.partial.swiftmodule,$@) \\ +\t -disable-diagnostic-passes -disable-sil-perf-optzns\\ +\t -module-name $(patsubst %.swift,%,$<) \\ +\t -o $(patsubst %.o,%.swiftmodule,$@) + +many-objects: $(OBJDIR)/main.o $(objects) +\t$(SWIFTC) -Onone -Xfrontend -serialize-debugging-options \\ +\t -sdk $(SDK) -o $@ $^ +""" + ) + + +def generate_sources_c(number_of_objects: int, generated_dir: str) -> None: + for i in range(number_of_objects): + generate_c_header(generated_dir, i) + generate_c_impl(generated_dir, i) + + generate_c_driver(generated_dir, number_of_objects) + generate_c_makefile(generated_dir, number_of_objects) + + +def generate_sources_cpp(number_of_objects: int, generated_dir: str) -> None: + for i in range(number_of_objects): + generate_cpp_header(generated_dir, i) + generate_cpp_impl(generated_dir, i) + + generate_cpp_driver(generated_dir, number_of_objects) + generate_cpp_makefile(generated_dir, number_of_objects) + + +def generate_sources_swift(number_of_objects: int, generated_dir: str) -> None: + for i in range(number_of_objects): + generate_swift_impl(generated_dir, i) + + generate_swift_driver(generated_dir, number_of_objects) + generate_swift_makefile(generated_dir, number_of_objects) + + +def generate_sources(number_of_objects: int, language: str, destination: str) -> None: + if os.path.exists(destination): + print("Directory already exists, please delete or rename it") + sys.exit(1) + + os.mkdir(destination) + generation_funcs = { + "c": generate_sources_c, + "cpp": generate_sources_cpp, + "swift": generate_sources_swift, + } + generation_funcs[language](number_of_objects, destination) + + +if __name__ == "__main__": + # TODO: If we want to add more arguments, it might be better to add a proper + # argparser to this tool. + if len(sys.argv) != 4: + print_usage() + sys.exit(1) + + try: + number_of_objects = int(sys.argv[1]) + except Exception: + print("Error: Unable to convert argv[1] to number") + print_usage() + sys.exit(1) + + language = sys.argv[2] + destination = sys.argv[3] + if number_of_objects < 1: + print("Can't generate less than 1 object(s)") + print_usage() + sys.exit(1) + + supported_languages = ["c", "cpp", "swift"] + if language not in supported_languages: + print(f"Unrecognized language: {language}") + print(f"Supported languages: {supported_languages}") + sys.exit(1) + + generate_sources(number_of_objects, language, destination)