bulbazord created this revision. bulbazord added reviewers: JDevlieghere, aprantl, jingham, fdeazeve, mib, kastiglione, clayborg. Herald added a project: All. bulbazord requested review of this revision. Herald added a project: LLDB. Herald added a subscriber: lldb-commits.
The purpose of this tool is to be able to generate a project with an arbitrary number of source files in a given programming language. I believe this can be useful as it lets you benchmark a given LLDB change for performance locally. For example, if you are changing the way LLDB processes a specific debug info section, you may want to make sure that you're not regressing performance (or if you're making the change for performance, you want to ensure that it actually improves things). You could use this tool to generate 10,000 (or more) source files and see how quickly lldb processes the debug info of the resulting binary. I understand that many folks will measure performance changes against large projects, usually proprietary in nature. I believe this is quite a valid way to measure performance changes and this tool isn't meant to change that. I wanted to offer this tool primarily as an alternative that gives the developer more control over the size and contents of the project (which is not something you always have when working with proprietary projects). Currently this tool only supports C, C++, and Swift. The Swift portions will only work on macOS right now and will not work with upstream lldb. There are also multiple ways to build Swift projects, this just chooses one specific approach. Future work could include adding an option to choose the structure of the generated Swift project. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D152569 Files: lldb/scripts/generate-project.py
Index: lldb/scripts/generate-project.py =================================================================== --- /dev/null +++ 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 <number of objects> <language> <destination>") + +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 <stdio.h>\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 <iostream>\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)
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits