11364750dSJames Henderson# DExTer : Debugging Experience Tester 21364750dSJames Henderson# ~~~~~~ ~ ~~ ~ ~~ 31364750dSJames Henderson# 41364750dSJames Henderson# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 51364750dSJames Henderson# See https://llvm.org/LICENSE.txt for license information. 61364750dSJames Henderson# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 71364750dSJames Henderson"""This is the main entry point. 81364750dSJames HendersonIt implements some functionality common to all subtools such as command line 91364750dSJames Hendersonparsing and running the unit-testing harnesses, before calling the reequested 101364750dSJames Hendersonsubtool. 111364750dSJames Henderson""" 121364750dSJames Henderson 131364750dSJames Hendersonimport os 141364750dSJames Hendersonimport sys 151364750dSJames Henderson 161364750dSJames Hendersonfrom dex.utils import PrettyOutput, Timer 171364750dSJames Hendersonfrom dex.utils import ExtArgParse as argparse 181364750dSJames Hendersonfrom dex.utils import get_root_directory 191364750dSJames Hendersonfrom dex.utils.Exceptions import Error, ToolArgumentError 20*6779376eSStephen Tozerfrom dex.utils.Imports import load_module 2173a01952SStephen Tozerfrom dex.utils.Logging import Logger 221364750dSJames Hendersonfrom dex.utils.UnitTests import unit_tests_ok 231364750dSJames Hendersonfrom dex.utils.Version import version 241364750dSJames Hendersonfrom dex.utils import WorkingDirectory 251364750dSJames Hendersonfrom dex.utils.ReturnCode import ReturnCode 261364750dSJames Henderson 271364750dSJames Henderson 281364750dSJames Hendersondef _output_bug_report_message(context): 291364750dSJames Henderson """In the event of a catastrophic failure, print bug report request to the 301364750dSJames Henderson user. 311364750dSJames Henderson """ 321364750dSJames Henderson context.o.red( 33f98ee40fSTobias Hieta "\n\n" 34f98ee40fSTobias Hieta "<g>****************************************</>\n" 35f98ee40fSTobias Hieta "<b>****************************************</>\n" 36f98ee40fSTobias Hieta "****************************************\n" 37f98ee40fSTobias Hieta "** **\n" 38f98ee40fSTobias Hieta "** <y>This is a bug in <a>DExTer</>.</> **\n" 39f98ee40fSTobias Hieta "** **\n" 40f98ee40fSTobias Hieta "** <y>Please report it.</> **\n" 41f98ee40fSTobias Hieta "** **\n" 42f98ee40fSTobias Hieta "****************************************\n" 43f98ee40fSTobias Hieta "<b>****************************************</>\n" 44f98ee40fSTobias Hieta "<g>****************************************</>\n" 45f98ee40fSTobias Hieta "\n" 46f98ee40fSTobias Hieta "<b>system:</>\n" 47f98ee40fSTobias Hieta "<d>{}</>\n\n" 48f98ee40fSTobias Hieta "<b>version:</>\n" 49f98ee40fSTobias Hieta "<d>{}</>\n\n" 50f98ee40fSTobias Hieta "<b>args:</>\n" 51f98ee40fSTobias Hieta "<d>{}</>\n" 52f98ee40fSTobias Hieta "\n".format(sys.platform, version("DExTer"), [sys.executable] + sys.argv), 53f98ee40fSTobias Hieta stream=PrettyOutput.stderr, 54f98ee40fSTobias Hieta ) 551364750dSJames Henderson 561364750dSJames Henderson 571364750dSJames Hendersondef get_tools_directory(): 581364750dSJames Henderson """Returns directory path where DExTer tool imports can be 591364750dSJames Henderson found. 601364750dSJames Henderson """ 61f98ee40fSTobias Hieta tools_directory = os.path.join(get_root_directory(), "tools") 621364750dSJames Henderson assert os.path.isdir(tools_directory), tools_directory 631364750dSJames Henderson return tools_directory 641364750dSJames Henderson 651364750dSJames Henderson 661364750dSJames Hendersondef get_tool_names(): 67f98ee40fSTobias Hieta """Returns a list of expected DExTer Tools""" 681364750dSJames Henderson return [ 69f98ee40fSTobias Hieta "help", 70f98ee40fSTobias Hieta "list-debuggers", 71f98ee40fSTobias Hieta "no-tool-", 72f98ee40fSTobias Hieta "run-debugger-internal-", 73f98ee40fSTobias Hieta "test", 74f98ee40fSTobias Hieta "view", 751364750dSJames Henderson ] 761364750dSJames Henderson 771364750dSJames Henderson 781364750dSJames Hendersondef _set_auto_highlights(context): 79f98ee40fSTobias Hieta """Flag some strings for auto-highlighting.""" 80f98ee40fSTobias Hieta context.o.auto_reds.extend( 81f98ee40fSTobias Hieta [ 82f98ee40fSTobias Hieta r"[Ee]rror\:", 83f98ee40fSTobias Hieta r"[Ee]xception\:", 84f98ee40fSTobias Hieta r"un(expected|recognized) argument", 85f98ee40fSTobias Hieta ] 86f98ee40fSTobias Hieta ) 87f98ee40fSTobias Hieta context.o.auto_yellows.extend( 88f98ee40fSTobias Hieta [ 89f98ee40fSTobias Hieta r"[Ww]arning\:", 90f98ee40fSTobias Hieta r"\(did you mean ", 91f98ee40fSTobias Hieta r"During handling of the above exception, another exception", 92f98ee40fSTobias Hieta ] 93f98ee40fSTobias Hieta ) 941364750dSJames Henderson 951364750dSJames Henderson 961364750dSJames Hendersondef _get_options_and_args(context): 97f98ee40fSTobias Hieta """get the options and arguments from the commandline""" 981364750dSJames Henderson parser = argparse.ExtArgumentParser(context, add_help=False) 99f98ee40fSTobias Hieta parser.add_argument("tool", default=None, nargs="?") 1001364750dSJames Henderson options, args = parser.parse_known_args(sys.argv[1:]) 1011364750dSJames Henderson 1021364750dSJames Henderson return options, args 1031364750dSJames Henderson 1041364750dSJames Henderson 1051364750dSJames Hendersondef _get_tool_name(options): 1061364750dSJames Henderson """get the name of the dexter tool (if passed) specified on the command 1071364750dSJames Henderson line, otherwise return 'no_tool_'. 1081364750dSJames Henderson """ 1091364750dSJames Henderson tool_name = options.tool 1101364750dSJames Henderson if tool_name is None: 111f98ee40fSTobias Hieta tool_name = "no_tool_" 1121364750dSJames Henderson else: 1131364750dSJames Henderson _is_valid_tool_name(tool_name) 1141364750dSJames Henderson return tool_name 1151364750dSJames Henderson 1161364750dSJames Henderson 1171364750dSJames Hendersondef _is_valid_tool_name(tool_name): 1181364750dSJames Henderson """check tool name matches a tool directory within the dexter tools 1191364750dSJames Henderson directory. 1201364750dSJames Henderson """ 1211364750dSJames Henderson valid_tools = get_tool_names() 1221364750dSJames Henderson if tool_name not in valid_tools: 123f98ee40fSTobias Hieta raise Error( 124f98ee40fSTobias Hieta 'invalid tool "{}" (choose from {})'.format( 125f98ee40fSTobias Hieta tool_name, ", ".join([t for t in valid_tools if not t.endswith("-")]) 126f98ee40fSTobias Hieta ) 127f98ee40fSTobias Hieta ) 1281364750dSJames Henderson 1291364750dSJames Henderson 1301364750dSJames Hendersondef _import_tool_module(tool_name): 1311364750dSJames Henderson """Imports the python module at the tool directory specificed by 1321364750dSJames Henderson tool_name. 1331364750dSJames Henderson """ 1341364750dSJames Henderson # format tool argument to reflect tool directory form. 135f98ee40fSTobias Hieta tool_name = tool_name.replace("-", "_") 1361364750dSJames Henderson 1371364750dSJames Henderson tools_directory = get_tools_directory() 138*6779376eSStephen Tozer return load_module(tool_name, tools_directory) 1391364750dSJames Henderson 1401364750dSJames Henderson 1411364750dSJames Hendersondef tool_main(context, tool, args): 1421364750dSJames Henderson with Timer(tool.name): 1431364750dSJames Henderson options, defaults = tool.parse_command_line(args) 1441364750dSJames Henderson Timer.display = options.time_report 1451364750dSJames Henderson Timer.indent = options.indent_timer_level 1461364750dSJames Henderson Timer.fn = context.o.blue 1471364750dSJames Henderson context.options = options 1481364750dSJames Henderson context.version = version(tool.name) 1491364750dSJames Henderson 1501364750dSJames Henderson if options.version: 151f98ee40fSTobias Hieta context.o.green("{}\n".format(context.version)) 1521364750dSJames Henderson return ReturnCode.OK 1531364750dSJames Henderson 15473a01952SStephen Tozer if options.verbose: 15573a01952SStephen Tozer context.logger.verbosity = 2 15673a01952SStephen Tozer elif options.no_warnings: 15773a01952SStephen Tozer context.logger.verbosity = 0 15873a01952SStephen Tozer 159f98ee40fSTobias Hieta if options.unittest != "off" and not unit_tests_ok(context): 160f98ee40fSTobias Hieta raise Error("<d>unit test failures</>") 1611364750dSJames Henderson 1621364750dSJames Henderson if options.colortest: 1631364750dSJames Henderson context.o.colortest() 1641364750dSJames Henderson return ReturnCode.OK 1651364750dSJames Henderson 1661364750dSJames Henderson try: 1671364750dSJames Henderson tool.handle_base_options(defaults) 1681364750dSJames Henderson except ToolArgumentError as e: 1691364750dSJames Henderson raise Error(e) 1701364750dSJames Henderson 1711364750dSJames Henderson dir_ = context.options.working_directory 1721364750dSJames Henderson with WorkingDirectory(context, dir=dir_) as context.working_directory: 1731364750dSJames Henderson return_code = tool.go() 1741364750dSJames Henderson 1751364750dSJames Henderson return return_code 1761364750dSJames Henderson 1771364750dSJames Henderson 1781364750dSJames Hendersonclass Context(object): 1791364750dSJames Henderson """Context encapsulates globally useful objects and data; passed to many 1801364750dSJames Henderson Dexter functions. 1811364750dSJames Henderson """ 1821364750dSJames Henderson 1831364750dSJames Henderson def __init__(self): 1841364750dSJames Henderson self.o: PrettyOutput = None 18573a01952SStephen Tozer self.logger: Logger = None 1861364750dSJames Henderson self.working_directory: str = None 1871364750dSJames Henderson self.options: dict = None 1881364750dSJames Henderson self.version: str = None 1891364750dSJames Henderson self.root_directory: str = None 1901364750dSJames Henderson 1911364750dSJames Henderson 1921364750dSJames Hendersondef main() -> ReturnCode: 1931364750dSJames Henderson context = Context() 1941364750dSJames Henderson with PrettyOutput() as context.o: 19573a01952SStephen Tozer context.logger = Logger(context.o) 1961364750dSJames Henderson try: 1971364750dSJames Henderson context.root_directory = get_root_directory() 1981364750dSJames Henderson # Flag some strings for auto-highlighting. 1991364750dSJames Henderson _set_auto_highlights(context) 2001364750dSJames Henderson options, args = _get_options_and_args(context) 2011364750dSJames Henderson # raises 'Error' if command line tool is invalid. 2021364750dSJames Henderson tool_name = _get_tool_name(options) 2031364750dSJames Henderson module = _import_tool_module(tool_name) 2041364750dSJames Henderson return tool_main(context, module.Tool(context), args) 2051364750dSJames Henderson except Error as e: 20673a01952SStephen Tozer context.logger.error(str(e)) 2071364750dSJames Henderson try: 2081364750dSJames Henderson if context.options.error_debug: 2091364750dSJames Henderson raise 2101364750dSJames Henderson except AttributeError: 2111364750dSJames Henderson pass 2121364750dSJames Henderson return ReturnCode._ERROR 2131364750dSJames Henderson except (KeyboardInterrupt, SystemExit): 2141364750dSJames Henderson raise 2151364750dSJames Henderson except: # noqa 2161364750dSJames Henderson _output_bug_report_message(context) 2171364750dSJames Henderson raise 218