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,273 @@ +#!/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.write(f"#define _OBJ{index}_H\n") + f.write(f"extern int obj{index};\n") + f.write(f"void call_obj{index}(void);\n") + f.write(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.write(f"#include \"obj{index}.h\"\n\n") + f.write(f"int obj{index} = {index};\n") + f.write(f"void call_obj{index}(void)") + f.write(" {\n") + f.write(f" printf(\"%d\\n\", obj{index});\n") + f.write("}\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.write(f"#define _OBJ{index}_H\n") + f.write("namespace obj {\n") + f.write(f"class Obj{index}") + f.write(" {\n") + f.write("public:\n") + f.write(" static void Call(void);\n") + f.write("}; // class\n") + f.write("} // namespace\n") + f.write(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.write(f"#include \"obj{index}.h\"\n\n") + f.write("namespace obj {\n") + f.write(f"void Obj{index}::Call(void)") + f.write(" {\n") + f.write(f" std::cout << {index} << std::endl;;\n") + f.write("}\n") + f.write("} // 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}()") + f.write(" {\n") + f.write(f" print(\"{index}\")\n") + f.write("}\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(0, number_of_objects): + f.write(f"#include \"obj{i}.h\"\n") + + f.write("int main() {\n") + for i in range(0, 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(0, 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(0, 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(0, number_of_objects): + f.write(f"import obj{i}\n") + + f.write("public func main() {\n") + for i in range(0, 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(0, number_of_objects): + f.write(f" $(OBJDIR)/obj{i}.o \\\n") + f.write(f" $(OBJDIR)/main.o\n") + f.write("\n") + f.write("all: many-objects\n") + f.write("objdir:\n") + f.write("\tmkdir -p $(OBJDIR)\n") + f.write(f"$(OBJDIR)/%.o: %.c objdir\n") + f.write(f"\t$(CC) -g -c -o $@ $<\n") + f.write("many-objects: $(objects)\n") + f.write(f"\t$(CC) -g -o $@ $^\n") + f.write("clean:\n") + f.write("\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(0, number_of_objects): + f.write(f" $(OBJDIR)/obj{i}.o \\\n") + f.write(f" $(OBJDIR)/main.o\n") + f.write("\n") + f.write("all: many-objects\n") + f.write("objdir:\n") + f.write("\tmkdir -p $(OBJDIR)\n") + f.write(f"$(OBJDIR)/%.o: %.cpp objdir\n") + f.write(f"\t$(CXX) -g -c -o $@ $<\n") + f.write("many-objects: $(objects)\n") + f.write(f"\t$(CXX) -g -o $@ $^\n") + f.write("clean:\n") + f.write("\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\n") + f.write("SDK=$(shell xcrun --show-sdk-path)\n") + f.write("SWIFT_FE=swift -frontend\n") + f.write("SWIFT_FEFLAGS=-g -Onone -serialize-debugging-options \\\n") + f.write(" -sdk $(SDK) -enable-anonymous-context-mangled-names\n") + f.write("SWIFTC=swiftc\n") + f.write("\n") + f.write("objects = \\\n") + for i in range(0, 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") + + # Utility + f.write("all: many-objects\n") + f.write("objdir:\n") + f.write("\tmkdir -p $(OBJDIR)\n") + f.write("clean:\n") + f.write("\trm -rf objs/ many-objects\n") + + + # Building Objects and their respective swiftmodule + f.write(f"$(OBJDIR)/%.o: %.swift objdir\n") + f.write("\t$(SWIFT_FE) -c -primary-file $< -I objs/ \\\n") + f.write("\t $(SWIFT_FEFLAGS) -module-name $(patsubst %.swift,%,$<) \\\n") + f.write("\t $(if $(filter-out main.swift,$<),-parse-as-library) \\\n") + f.write("\t -emit-module-path $(patsubst %.o,%.partial.swiftmodule,$@) \\\n") + f.write("\t -o $@\n") + f.write("\t$(SWIFT_FE) $(SWIFT_FEFLAGS) -merge-modules \\\n") + f.write("\t -emit-module -emit-module-interface-path \\\n") + f.write("\t $(patsubst %.o,%.swiftinterface,$@) \\\n") + f.write("\t $(patsubst %.o,%.partial.swiftmodule,$@) \\\n") + f.write("\t -disable-diagnostic-passes -disable-sil-perf-optzns\\\n") + f.write("\t -module-name $(patsubst %.swift,%,$<) \\\n") + f.write("\t -o $(patsubst %.o,%.swiftmodule,$@)\n") + + # Main must be built last + f.write("$(OBJDIR)/main.o: main.swift $(objects)\n") + f.write("\t$(SWIFT_FE) -c -primary-file $< -I objs/ \\\n") + f.write("\t $(SWIFT_FEFLAGS) -module-name main \\\n") + f.write("\t -emit-module-path $(patsubst %.o,%.partial.swiftmodule,$@) \\\n") + f.write("\t -o $@\n") + f.write("\t$(SWIFT_FE) $(SWIFT_FEFLAGS) -merge-modules \\\n") + f.write("\t -emit-module -emit-module-interface-path \\\n") + f.write("\t $(patsubst %.o,%.swiftinterface,$@) \\\n") + f.write("\t $(patsubst %.o,%.partial.swiftmodule,$@) \\\n") + f.write("\t -disable-diagnostic-passes -disable-sil-perf-optzns\\\n") + f.write("\t -module-name $(patsubst %.swift,%,$<) \\\n") + f.write("\t -o $(patsubst %.o,%.swiftmodule,$@)\n") + + + # Linking the final binary + f.write("many-objects: $(OBJDIR)/main.o $(objects)\n") + f.write("\t$(SWIFTC) -Onone -Xfrontend -serialize-debugging-options \\\n") + f.write("\t -sdk $(SDK) -o $@ $^\n") + +def generate_sources_c(number_of_objects: int, generated_dir: str) -> None: + for i in range(0, 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(0, 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(0, 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)