diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d44e75cf..ec3eb58be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,6 +138,7 @@ set(BUSTUB_THIRD_PARTY_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/third_party/libpg_query/include ${PROJECT_SOURCE_DIR}/third_party/argparse/include ${PROJECT_SOURCE_DIR}/third_party/cpp_random_distributions + ${PROJECT_SOURCE_DIR}/third_party/backward-cpp/include ) include_directories(${BUSTUB_SRC_INCLUDE_DIR} ${BUSTUB_TEST_INCLUDE_DIR} ${BUSTUB_THIRD_PARTY_INCLUDE_DIR}) @@ -151,9 +152,13 @@ endfunction() # Other CMake modules # MUST BE ADDED AFTER CONFIGURING COMPILER PARAMETERS # ##################################################################################################################### +set(CMAKE_MODULE_PATH "${BUSTUB_BUILD_SUPPORT_DIR}/cmake;${CMAKE_MODULE_PATH}") +find_package(LibElf) +find_package(LibDwarf) + +add_subdirectory(third_party) add_subdirectory(src) add_subdirectory(test) -add_subdirectory(third_party) add_subdirectory(tools) # ##################################################################################################################### diff --git a/build_support/cmake/FindLibDwarf.cmake b/build_support/cmake/FindLibDwarf.cmake new file mode 100644 index 000000000..8943e3f0b --- /dev/null +++ b/build_support/cmake/FindLibDwarf.cmake @@ -0,0 +1,131 @@ +# Copyright (c) 2004-present, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the hphp/hsl/ subdirectory of this source tree. + +# - Try to find libdwarf +# Once done this will define +# +# LIBDWARF_FOUND - system has libdwarf +# LIBDWARF_INCLUDE_DIRS - the libdwarf include directory +# LIBDWARF_LIBRARIES - Link these to use libdwarf +# LIBDWARF_DEFINITIONS - Compiler switches required for using libdwarf +# + +# Locate libelf library at first +if(NOT LIBELF_FOUND) + find_package(LibElf) +endif(NOT LIBELF_FOUND) + +if(LIBDWARF_LIBRARIES AND LIBDWARF_INCLUDE_DIRS) + set(LibDwarf_FIND_QUIETLY TRUE) +endif(LIBDWARF_LIBRARIES AND LIBDWARF_INCLUDE_DIRS) + +find_package(PkgConfig) +pkg_check_modules(PkgConfig_LibDwarf QUIET libdwarf) + +find_path(DWARF_INCLUDE_DIR + NAMES + libdwarf.h dwarf.h + PATHS + ${PkgConfig_LibDwarf_INCLUDE_DIRS} + /usr/include + /usr/include/libdwarf + /usr/local/include + /usr/local/include/libdwarf + /opt/local/include + /sw/include + ENV CPATH) # PATH and INCLUDE will also work + +if(DWARF_INCLUDE_DIR) + set(LIBDWARF_INCLUDE_DIRS ${DWARF_INCLUDE_DIR}) +endif() + +find_library(LIBDWARF_LIBRARIES + NAMES + dwarf libdwarf + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ${PkgConfig_LibDwarf_LIBRARY_DIRS} + ENV LIBRARY_PATH # PATH and LIB will also work + ENV LD_LIBRARY_PATH) +include(FindPackageHandleStandardArgs) + +# handle the QUIETLY and REQUIRED arguments and set LIBDWARF_FOUND to TRUE +# if all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibDwarf DEFAULT_MSG + LIBELF_FOUND + LIBDWARF_LIBRARIES + LIBDWARF_INCLUDE_DIRS) + +if(LIBDWARF_LIBRARIES AND LIBDWARF_INCLUDE_DIRS) + set(CMAKE_REQUIRED_INCLUDES ${LIBDWARF_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LIBRARIES ${LIBDWARF_LIBRARIES} ${LIBELF_LIBRARIES}) + + # libdwarf makes breaking changes occasionally and doesn't provide an easy + # way to test for them. The following checks should detect the changes and + # pass that information on accordingly. + INCLUDE(CheckCXXSourceCompiles) + INCLUDE(CheckFunctionExists) + + MACRO(CHECK_LIBDWARF_INIT init params var) + # Check for the existence of this particular init function. + unset(INIT_EXISTS CACHE) + CHECK_FUNCTION_EXISTS(${init} INIT_EXISTS) + + if(INIT_EXISTS) + set(LIBDWARF_USE_INIT_C ${var}) + + # Check to see if we can use a const name. + unset(DW_CONST CACHE) + + if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # -std=c++11 is already set in HPHPCompiler.cmake, don't + # add -std=c++0x on top of that or clang will give errors + set(CMAKE_REQUIRED_FLAGS "-std=c++0x") + endif() + + CHECK_CXX_SOURCE_COMPILES(" + #include + #include + int dwarfCallback(const char * a, int b, Dwarf_Unsigned c, + Dwarf_Unsigned d, Dwarf_Unsigned e, Dwarf_Unsigned f, + Dwarf_Unsigned * g, Dwarf_Ptr h, int * i) {} + int main() { ${init}(${params}); return 0; }" DW_CONST) + + if(DW_CONST) + set(LIBDWARF_CONST_NAME 1) + else() + set(LIBDWARF_CONST_NAME 0) + endif() + endif() + ENDMACRO(CHECK_LIBDWARF_INIT) + + # Order is important, last one is used. + CHECK_LIBDWARF_INIT("dwarf_producer_init" + "0, dwarfCallback, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr" 0) + CHECK_LIBDWARF_INIT("dwarf_producer_init_c" "0, dwarfCallback, nullptr, nullptr, nullptr, nullptr" 1) + + set(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_LIBRARIES) +endif() + +if(LIBDWARF_CONST_NAME) + message(STATUS "libdwarf uses const char* type") +else() + message(STATUS "libdwarf uses char* type") +endif() + +if(LIBDWARF_USE_INIT_C) + message(STATUS "libdwarf has dwarf_producer_init_c") +else() + message(STATUS "libdwarf does not have dwarf_producer_init_c, using dwarf_producer_init") +endif() + +mark_as_advanced(LIBDW_INCLUDE_DIR DWARF_INCLUDE_DIR) +mark_as_advanced(LIBDWARF_INCLUDE_DIRS LIBDWARF_LIBRARIES) +mark_as_advanced(LIBDWARF_CONST_NAME LIBDWARF_USE_INIT_C) diff --git a/build_support/cmake/FindLibElf.cmake b/build_support/cmake/FindLibElf.cmake new file mode 100644 index 000000000..732c646a2 --- /dev/null +++ b/build_support/cmake/FindLibElf.cmake @@ -0,0 +1,68 @@ +# - Try to find libelf +# Once done this will define +# +# LIBELF_FOUND - system has libelf +# LIBELF_INCLUDE_DIRS - the libelf include directory +# LIBELF_LIBRARIES - Link these to use libelf +# LIBELF_DEFINITIONS - Compiler switches required for using libelf +# +# Copyright (c) 2008 Bernhard Walle +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +if(LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS) + set(LibElf_FIND_QUIETLY TRUE) +endif(LIBELF_LIBRARIES AND LIBELF_INCLUDE_DIRS) + +find_package(PkgConfig) +pkg_check_modules(PkgConfig_LibElf QUIET libelf) + +find_path(LIBELF_INCLUDE_DIRS + NAMES + libelf.h + PATHS + ${PkgConfig_LibElf_INCLUDE_DIRS} + /usr/include + /usr/include/libelf + /usr/local/include + /usr/local/include/libelf + /opt/local/include + /opt/local/include/libelf + /sw/include + /sw/include/libelf + ENV CPATH) + +find_library(LIBELF_LIBRARIES + NAMES + elf + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ${PkgConfig_LibElf_LIBRARY_DIRS} + ENV LIBRARY_PATH + ENV LD_LIBRARY_PATH) + +include(FindPackageHandleStandardArgs) + +# handle the QUIETLY and REQUIRED arguments and set LIBELF_FOUND to TRUE if all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibElf DEFAULT_MSG + LIBELF_LIBRARIES + LIBELF_INCLUDE_DIRS) + +SET(CMAKE_REQUIRED_LIBRARIES elf) +INCLUDE(CheckCXXSourceCompiles) +CHECK_CXX_SOURCE_COMPILES("#include +int main() { + Elf *e = (Elf*)0; + size_t sz; + elf_getshdrstrndx(e, &sz); + return 0; +}" ELF_GETSHDRSTRNDX) +SET(CMAKE_REQUIRED_LIBRARIES) + +mark_as_advanced(LIBELF_INCLUDE_DIRS LIBELF_LIBRARIES ELF_GETSHDRSTRNDX) diff --git a/build_support/packages.sh b/build_support/packages.sh index 8bfd1b3cb..190828453 100755 --- a/build_support/packages.sh +++ b/build_support/packages.sh @@ -79,6 +79,7 @@ install_mac() { brew ls --versions doxygen || brew install doxygen brew ls --versions git || brew install git (brew ls --versions llvm | grep 14) || brew install llvm@14 + brew ls --versions libelf || brew install libelf } install_linux() { @@ -94,7 +95,9 @@ install_linux() { doxygen \ git \ pkg-config \ - zlib1g-dev + zlib1g-dev \ + libelf-dev \ + libdwarf-dev } main "$@" diff --git a/build_support/run_clang_tidy.py b/build_support/run_clang_tidy.py index f3c4f52be..913e87126 100755 --- a/build_support/run_clang_tidy.py +++ b/build_support/run_clang_tidy.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -#===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===# +# ===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===# # # The LLVM Compiler Infrastructure # @@ -10,7 +10,7 @@ # Modified from the LLVM project for the Terrier project. # # Added `only_diff` cli arg to run clang-tidy on git diff. -#===------------------------------------------------------------------------===# +# ===------------------------------------------------------------------------===# # FIXME: Integrate with clang-tidy-diff.py """ @@ -40,7 +40,7 @@ import json import multiprocessing import os -import pprint # TERRIER: we want to print out formatted lists of files +import pprint # TERRIER: we want to print out formatted lists of files import re import shutil import subprocess @@ -48,11 +48,12 @@ import tempfile import threading import traceback + # import yaml # TERRIER: not necessary if we don't want automatic fixes from run_clang_tidy_extra import CheckConfig -is_py2 = sys.version[0] == '2' +is_py2 = sys.version[0] == "2" if is_py2: import Queue as queue @@ -62,12 +63,12 @@ def find_compilation_database(path): """Adjusts the directory until a compilation database is found.""" - result = './' + result = "./" while not os.path.isfile(os.path.join(result, path)): - if os.path.realpath(result) == '/': - print('Error: could not find compilation database.') + if os.path.realpath(result) == "/": + print("Error: could not find compilation database.") sys.exit(1) - result += '../' + result += "../" return os.path.realpath(result) @@ -76,6 +77,7 @@ def make_absolute(f, directory): return f return os.path.normpath(os.path.join(directory, f)) + def supports_color(): """ Modified from https://github.com/django/django/blob/main/django/core/management/color.py @@ -83,7 +85,6 @@ def supports_color(): and False otherwise. """ - # isatty is not always implemented, #6223. is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty() @@ -98,40 +99,50 @@ def supports_color(): os.environ.get("TERM_PROGRAM") == "vscode" ) -def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path, - header_filter, extra_arg, extra_arg_before, quiet, - config): + +def get_tidy_invocation( + f, + clang_tidy_binary, + checks, + tmpdir, + build_path, + header_filter, + extra_arg, + extra_arg_before, + quiet, + config, +): """Gets a command line for clang-tidy.""" start = [clang_tidy_binary] if supports_color(): - start.append('--use-color') + start.append("--use-color") if header_filter is not None: - start.append('-header-filter=' + header_filter) + start.append("-header-filter=" + header_filter) else: # Show warnings in all in-project headers by default. # start.append('-header-filter=^' + build_path + '/.*') # TERRIER: we have our .clang-tidy file pass if checks: - start.append('-checks=' + checks) + start.append("-checks=" + checks) if tmpdir is not None: - start.append('-export-fixes') + start.append("-export-fixes") # Get a temporary file. We immediately close the handle so clang-tidy can # overwrite it. - (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir) + (handle, name) = tempfile.mkstemp(suffix=".yaml", dir=tmpdir) os.close(handle) start.append(name) for arg in extra_arg: - start.append('-extra-arg=%s' % arg) + start.append("-extra-arg=%s" % arg) for arg in extra_arg_before: - start.append('-extra-arg-before=%s' % arg) - start.append('-p=' + build_path) + start.append("-extra-arg-before=%s" % arg) + start.append("-p=" + build_path) if quiet: - start.append('-quiet') + start.append("-quiet") if config: - start.append('-config=' + config) + start.append("-config=" + config) start.append(f) return start @@ -142,8 +153,8 @@ def merge_replacement_files(tmpdir, mergefile): # the top level key 'Diagnostics' in the output yaml files mergekey = "Diagnostics" merged = [] - for replacefile in glob.iglob(os.path.join(tmpdir, '*.yaml')): - content = yaml.safe_load(open(replacefile, 'r')) + for replacefile in glob.iglob(os.path.join(tmpdir, "*.yaml")): + content = yaml.safe_load(open(replacefile, "r")) if not content: continue # Skip empty files. merged.extend(content.get(mergekey, [])) @@ -153,22 +164,24 @@ def merge_replacement_files(tmpdir, mergefile): # include/clang/Tooling/ReplacementsYaml.h, but the value # is actually never used inside clang-apply-replacements, # so we set it to '' here. - output = {'MainSourceFile': '', mergekey: merged} - with open(mergefile, 'w') as out: + output = {"MainSourceFile": "", mergekey: merged} + with open(mergefile, "w") as out: yaml.safe_dump(output, out) else: # Empty the file: - open(mergefile, 'w').close() + open(mergefile, "w").close() def check_clang_apply_replacements_binary(args): """Checks if invoking supplied clang-apply-replacements binary works.""" try: - subprocess.check_call( - [args.clang_apply_replacements_binary, '--version']) + subprocess.check_call([args.clang_apply_replacements_binary, "--version"]) except: - print('Unable to run clang-apply-replacements. Is clang-apply-replacements ' - 'binary correctly specified?', file=sys.stderr) + print( + "Unable to run clang-apply-replacements. Is clang-apply-replacements " + "binary correctly specified?", + file=sys.stderr, + ) traceback.print_exc() sys.exit(1) @@ -177,9 +190,9 @@ def apply_fixes(args, tmpdir): """Calls clang-apply-fixes on a given directory.""" invocation = [args.clang_apply_replacements_binary] if args.format: - invocation.append('-format') + invocation.append("-format") if args.style: - invocation.append('-style=' + args.style) + invocation.append("-style=" + args.style) invocation.append(tmpdir) subprocess.call(invocation) @@ -190,10 +203,18 @@ def run_tidy(args, tmpdir, build_path, queue, lock, failed_files): name = queue.get() print("Checking: {}".format(name)) sys.stdout.flush() - invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks, - tmpdir, build_path, args.header_filter, - args.extra_arg, args.extra_arg_before, - args.quiet, args.config) + invocation = get_tidy_invocation( + name, + args.clang_tidy_binary, + args.checks, + tmpdir, + build_path, + args.header_filter, + args.extra_arg, + args.extra_arg_before, + args.quiet, + args.config, + ) cc = CheckConfig() # name is the full path of the file for clang-tidy to check if cc.should_skip(name): @@ -201,7 +222,8 @@ def run_tidy(args, tmpdir, build_path, queue, lock, failed_files): continue proc = subprocess.Popen( - invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) output, err = proc.communicate() if proc.returncode != 0: failed_files.append(name) @@ -212,73 +234,113 @@ def run_tidy(args, tmpdir, build_path, queue, lock, failed_files): # sys.stderr.write(err + '\n') # In particular, we only want important lines: with lock: - output = output.decode('utf-8') if output is not None else None - err = err.decode('utf-8') if output is not None else None + output = output.decode("utf-8") if output is not None else None + err = err.decode("utf-8") if output is not None else None # unfortunately, our error messages are actually on STDOUT # STDERR tells how many warnings are generated, # but this includes non-user-code warnings, so it is useless... if output: - sys.stdout.write('\n') + sys.stdout.write("\n") sys.stdout.write(output) queue.task_done() def main(): - parser = argparse.ArgumentParser(description='Runs clang-tidy over all files ' - 'in a compilation database. Requires ' - 'clang-tidy and clang-apply-replacements in ' - '$PATH.') - parser.add_argument('-clang-tidy-binary', metavar='PATH', - default='clang-tidy', - help='path to clang-tidy binary') - parser.add_argument('-clang-apply-replacements-binary', metavar='PATH', - default='clang-apply-replacements', - help='path to clang-apply-replacements binary') - parser.add_argument('-checks', default=None, - help='checks filter, when not specified, use clang-tidy ' - 'default') - parser.add_argument('-config', default=None, - help='Specifies a configuration in YAML/JSON format: ' - ' -config="{Checks: \'*\', ' - ' CheckOptions: [{key: x, ' - ' value: y}]}" ' - 'When the value is empty, clang-tidy will ' - 'attempt to find a file named .clang-tidy for ' - 'each source file in its parent directories.') - parser.add_argument('-header-filter', default=None, - help='regular expression matching the names of the ' - 'headers to output diagnostics from. Diagnostics from ' - 'the main file of each translation unit are always ' - 'displayed.') - parser.add_argument('-export-fixes', metavar='filename', dest='export_fixes', - help='Create a yaml file to store suggested fixes in, ' - 'which can be applied with clang-apply-replacements.') - parser.add_argument('-j', type=int, default=0, - help='number of tidy instances to be run in parallel.') - parser.add_argument('files', nargs='*', default=['.*'], - help='files to be processed (regex on path)') - parser.add_argument('-fix', action='store_true', help='apply fix-its') - parser.add_argument('-format', action='store_true', help='Reformat code ' - 'after applying fixes') - parser.add_argument('-style', default='file', help='The style of reformat ' - 'code after applying fixes') - parser.add_argument('-p', dest='build_path', - help='Path used to read a compile command database.') - parser.add_argument('-extra-arg', dest='extra_arg', - action='append', default=[], - help='Additional argument to append to the compiler ' - 'command line.') - parser.add_argument('-extra-arg-before', dest='extra_arg_before', - action='append', default=[], - help='Additional argument to prepend to the compiler ' - 'command line.') - parser.add_argument('-quiet', action='store_true', - help='Run clang-tidy in quiet mode') - parser.add_argument('-only-diff', action='store_true', - help='Only run clang-tidy on diff file to master branch') + parser = argparse.ArgumentParser( + description="Runs clang-tidy over all files " + "in a compilation database. Requires " + "clang-tidy and clang-apply-replacements in " + "$PATH." + ) + parser.add_argument( + "-clang-tidy-binary", + metavar="PATH", + default="clang-tidy", + help="path to clang-tidy binary", + ) + parser.add_argument( + "-clang-apply-replacements-binary", + metavar="PATH", + default="clang-apply-replacements", + help="path to clang-apply-replacements binary", + ) + parser.add_argument( + "-checks", + default=None, + help="checks filter, when not specified, use clang-tidy " "default", + ) + parser.add_argument( + "-config", + default=None, + help="Specifies a configuration in YAML/JSON format: " + " -config=\"{Checks: '*', " + " CheckOptions: [{key: x, " + ' value: y}]}" ' + "When the value is empty, clang-tidy will " + "attempt to find a file named .clang-tidy for " + "each source file in its parent directories.", + ) + parser.add_argument( + "-header-filter", + default=None, + help="regular expression matching the names of the " + "headers to output diagnostics from. Diagnostics from " + "the main file of each translation unit are always " + "displayed.", + ) + parser.add_argument( + "-export-fixes", + metavar="filename", + dest="export_fixes", + help="Create a yaml file to store suggested fixes in, " + "which can be applied with clang-apply-replacements.", + ) + parser.add_argument( + "-j", + type=int, + default=0, + help="number of tidy instances to be run in parallel.", + ) + parser.add_argument( + "files", nargs="*", default=[".*"], help="files to be processed (regex on path)" + ) + parser.add_argument("-fix", action="store_true", help="apply fix-its") + parser.add_argument( + "-format", action="store_true", help="Reformat code " "after applying fixes" + ) + parser.add_argument( + "-style", + default="file", + help="The style of reformat " "code after applying fixes", + ) + parser.add_argument( + "-p", dest="build_path", help="Path used to read a compile command database." + ) + parser.add_argument( + "-extra-arg", + dest="extra_arg", + action="append", + default=[], + help="Additional argument to append to the compiler " "command line.", + ) + parser.add_argument( + "-extra-arg-before", + dest="extra_arg_before", + action="append", + default=[], + help="Additional argument to prepend to the compiler " "command line.", + ) + parser.add_argument( + "-quiet", action="store_true", help="Run clang-tidy in quiet mode" + ) + parser.add_argument( + "-only-diff", + action="store_true", + help="Only run clang-tidy on diff file to master branch", + ) args = parser.parse_args() - db_path = 'compile_commands.json' + db_path = "compile_commands.json" if args.build_path is not None: build_path = args.build_path @@ -287,11 +349,11 @@ def main(): build_path = find_compilation_database(db_path) try: - invocation = [args.clang_tidy_binary, '-list-checks'] - invocation.append('-p=' + build_path) + invocation = [args.clang_tidy_binary, "-list-checks"] + invocation.append("-p=" + build_path) if args.checks: - invocation.append('-checks=' + args.checks) - invocation.append('-') + invocation.append("-checks=" + args.checks) + invocation.append("-") subprocess.check_call(invocation) except: print("Unable to run clang-tidy.", file=sys.stderr) @@ -299,8 +361,7 @@ def main(): # Load the database and extract all files. database = json.load(open(os.path.join(build_path, db_path))) - files = [make_absolute(entry['file'], entry['directory']) - for entry in database] + files = [make_absolute(entry["file"], entry["directory"]) for entry in database] # Running clang-tidy in the whole project is slow. Therefore, we added # support for running clang-tidy on git diff. When `only_diff` is @@ -313,13 +374,20 @@ def main(): if args.only_diff: # Get the path of the repo, e.g. /Users/terrier/bustub git_repo = subprocess.run( - ["git", "rev-parse", "--show-toplevel"], capture_output=True) + ["git", "rev-parse", "--show-toplevel"], capture_output=True + ) git_repo_path = git_repo.stdout.decode("utf-8").strip() # Get all files changed compared with origin/master result = subprocess.run( - ["git", "--no-pager", "diff", "--name-only", "origin/master"], capture_output=True) - git_changed_file_list = list(map(lambda x: make_absolute( - x, git_repo_path), result.stdout.decode("utf-8").strip().split("\n"))) + ["git", "--no-pager", "diff", "--name-only", "origin/master"], + capture_output=True, + ) + git_changed_file_list = list( + map( + lambda x: make_absolute(x, git_repo_path), + result.stdout.decode("utf-8").strip().split("\n"), + ) + ) git_changed_file_set = set(git_changed_file_list) # Only retain files that exists in git diff files = list(filter(lambda x: x in git_changed_file_set, files)) @@ -334,7 +402,7 @@ def main(): tmpdir = tempfile.mkdtemp() # Build up a big regexy filter from all command line arguments. - file_name_re = re.compile('|'.join(args.files)) + file_name_re = re.compile("|".join(args.files)) return_code = 0 try: @@ -344,8 +412,10 @@ def main(): failed_files = [] lock = threading.Lock() for _ in range(max_task): - t = threading.Thread(target=run_tidy, - args=(args, tmpdir, build_path, task_queue, lock, failed_files)) + t = threading.Thread( + target=run_tidy, + args=(args, tmpdir, build_path, task_queue, lock, failed_files), + ) t.daemon = True t.start() @@ -354,13 +424,12 @@ def update_progress(current_file, num_files): if current_file == num_files or pct % max(2, num_files // 10) == 0: stars = pct // 10 spaces = 10 - pct // 10 - print('\rProgress: [{}{}] ({}% / File {} of {})'.format( - 'x' * stars, - ' ' * spaces, - pct, - current_file, - num_files - ), end='') + print( + "\rProgress: [{}{}] ({}% / File {} of {})".format( + "x" * stars, " " * spaces, pct, current_file, num_files + ), + end="", + ) sys.stdout.flush() if current_file == num_files: print() @@ -368,6 +437,8 @@ def update_progress(current_file, num_files): # Fill the queue with files. for i, name in enumerate(files): if file_name_re.search(name): + if name.endswith("tools/backtrace.cpp"): + continue put_file = False while not put_file: try: @@ -375,7 +446,7 @@ def update_progress(current_file, num_files): put_file = True # update_progress(i, len(files)) except queue.Full: - print('Still waiting to put files into clang-tidy queue.') + print("Still waiting to put files into clang-tidy queue.") sys.stdout.flush() # Wait for all threads to be done. @@ -384,34 +455,35 @@ def update_progress(current_file, num_files): if len(failed_files): return_code = 1 # TERRIER: We want to see the failed files - print('The files that failed were:') + print("The files that failed were:") print(pprint.pformat(failed_files)) print( - 'Note that a failing .h file will fail all the .cpp files that include it.\n') + "Note that a failing .h file will fail all the .cpp files that include it.\n" + ) except KeyboardInterrupt: # This is a sad hack. Unfortunately subprocess goes # bonkers with ctrl-c and we start forking merrily. - print('\nCtrl-C detected, goodbye.') + print("\nCtrl-C detected, goodbye.") if tmpdir: shutil.rmtree(tmpdir) os.kill(0, 9) if args.export_fixes: - print('Writing fixes to ' + args.export_fixes + ' ...') + print("Writing fixes to " + args.export_fixes + " ...") try: merge_replacement_files(tmpdir, args.export_fixes) except: - print('Error exporting fixes.\n', file=sys.stderr) + print("Error exporting fixes.\n", file=sys.stderr) traceback.print_exc() return_code = 1 if args.fix: - print('Applying fixes ...') + print("Applying fixes ...") try: apply_fixes(args, tmpdir) except: - print('Error applying fixes.\n', file=sys.stderr) + print("Error applying fixes.\n", file=sys.stderr) traceback.print_exc() return_code = 1 @@ -422,6 +494,5 @@ def update_progress(current_file, num_files): sys.exit(return_code) -if __name__ == '__main__': +if __name__ == "__main__": main() - \ No newline at end of file diff --git a/src/include/common/macros.h b/src/include/common/macros.h index 4e01a43ce..5f4741fab 100644 --- a/src/include/common/macros.h +++ b/src/include/common/macros.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include namespace bustub { @@ -23,7 +24,7 @@ namespace bustub { #define BUSTUB_ENSURE(expr, message) \ if (!(expr)) { \ - throw std::logic_error(message); \ + std::terminate(); \ } #define UNREACHABLE(message) throw std::logic_error(message) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 09a63d454..402c42d2f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,7 +24,8 @@ foreach (bustub_test_source ${BUSTUB_TEST_SOURCES}) string(REPLACE ".cpp" "" bustub_test_name ${bustub_test_filename}) # Add the test target separately and as part of "make check-tests". - add_executable(${bustub_test_name} EXCLUDE_FROM_ALL ${bustub_test_source}) + add_executable(${bustub_test_name} EXCLUDE_FROM_ALL ${bustub_test_source} "${PROJECT_SOURCE_DIR}/tools/backtrace.cpp") + add_backward(${bustub_test_name}) add_dependencies(build-tests ${bustub_test_name}) add_dependencies(check-tests ${bustub_test_name}) diff --git a/test/backtrace/crash_test.cpp b/test/backtrace/crash_test.cpp new file mode 100644 index 000000000..9afd8c631 --- /dev/null +++ b/test/backtrace/crash_test.cpp @@ -0,0 +1,34 @@ +#include "common/exception.h" +#include "common/macros.h" +#include "gtest/gtest.h" + +// NOLINTBEGIN + +namespace bustub { + +TEST(CrashTest, DISABLED_PtrAccess) { + // ASAN will show the full backtrace + int *p = nullptr; + *p = 2; +} + +TEST(CrashTest, DISABLED_GtestAssert) { + // Gtest will show the line that failed + ASSERT_TRUE(false); +} + +TEST(CrashTest, DISABLED_Assert) { + // Default assertion implementation, no backtrace, only lineno + BUSTUB_ASSERT(false, "assert failure"); +} + +TEST(CrashTest, DISABLED_Ensure) { + // Full stacktrace provided by backward-cpp + BUSTUB_ENSURE(false, "assert failure"); +} + +TEST(CrashTest, Throw) {} + +} // namespace bustub + +// NOLINTEND diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index a992ea0a1..146903d67 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -13,3 +13,5 @@ add_subdirectory(libfort) add_subdirectory(argparse) add_subdirectory(utf8proc) + +add_subdirectory(backward-cpp) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index e3e373f71..f091e0f2c 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -6,3 +6,8 @@ add_subdirectory(wasm-bpt-printer) add_subdirectory(terrier_bench) add_subdirectory(bpm_bench) add_subdirectory(btree_bench) + +add_backward(shell) +add_backward(sqllogictest) +add_backward(terrier-bench) +add_backward(b_plus_tree_printer) diff --git a/tools/backtrace.cpp b/tools/backtrace.cpp new file mode 100644 index 000000000..0dc51d81a --- /dev/null +++ b/tools/backtrace.cpp @@ -0,0 +1,22 @@ +#include +#include +#include "backward.hpp" + +class TerminateHandler { + public: + TerminateHandler() { + std::set_terminate([]() { + backward::StackTrace st; + st.load_here(32); + backward::Printer p; + p.object = true; + p.snippet = false; + p.color_mode = backward::ColorMode::automatic; + p.address = true; + p.print(st, stderr); + std::abort(); + }); + } +}; + +TerminateHandler th;