Example #1
0
def tool_main(context, tool, args):
    with Timer(tool.name):
        options, defaults = tool.parse_command_line(args)
        Timer.display = options.time_report
        Timer.indent = options.indent_timer_level
        Timer.fn = context.o.blue
        context.options = options
        context.version = version(tool.name)

        if options.version:
            context.o.green('{}\n'.format(context.version))
            return ReturnCode.OK

        if (options.unittest != 'off' and not unit_tests_ok(context)):
            raise Error('<d>unit test failures</>')

        if options.colortest:
            context.o.colortest()
            return ReturnCode.OK

        try:
            tool.handle_base_options(defaults)
        except ToolArgumentError as e:
            raise Error(e)

        dir_ = context.options.working_directory
        with WorkingDirectory(context, dir=dir_) as context.working_directory:
            return_code = tool.go()

        return return_code
Example #2
0
    def go(self) -> ReturnCode:
        options = self.context.options

        with Timer('loading debugger'):
            debugger = Debuggers(self.context).load(options.debugger,
                                                    self.dextIR)
            self.dextIR.debugger = debugger.debugger_info

        with Timer('running debugger'):
            if not debugger.is_available:
                msg = '<d>could not load {}</> ({})\n'.format(
                    debugger.name, debugger.loading_error)
                if options.verbose:
                    msg = '{}\n    {}'.format(
                        msg, '    '.join(debugger.loading_error_trace))
                raise Error(msg)

            with debugger:
                try:
                    debugger.start()
                except DebuggerException as e:
                    raise Error(e)

        with open(self.context.options.dextIR_path, 'wb') as fp:
            pickle.dump(self.dextIR, fp)
        return ReturnCode.OK
Example #3
0
 def raise_debugger_error(self, action, debugger):
     msg = '<d>could not {} {}</> ({})\n'.format(action, debugger.name,
                                                 debugger.loading_error)
     if self.options.verbose:
         msg = '{}\n    {}'.format(
             msg, '    '.join(debugger.loading_error_trace))
     raise Error(msg)
Example #4
0
    def handle_options(self, defaults):
        options = self.context.options

        options.input_path = os.path.abspath(options.input_path)
        if not os.path.isfile(options.input_path):
            raise Error('<d>could not find dextIR file</> <r>"{}"</>'.format(
                options.input_path))
Example #5
0
 def handle_options(self, defaults):
     options = self.context.options
     if "clang" not in options.builder.lower():
         raise Error(
             "--builder %s is not supported by the clang-opt-bisect tool - only 'clang' is "
             "supported " % options.builder)
     super(Tool, self).handle_options(defaults)
Example #6
0
 def go(self) -> ReturnCode:
     with Timer('list debuggers'):
         try:
             Debuggers(self.context).list()
         except DebuggerException as e:
             raise Error(e)
     return ReturnCode.OK
Example #7
0
def _is_valid_tool_name(tool_name):
    """ check tool name matches a tool directory within the dexter tools
        directory.
    """
    valid_tools = get_tool_names()
    if tool_name not in valid_tools:
        raise Error('invalid tool "{}" (choose from {})'.format(
            tool_name,
            ', '.join([t for t in valid_tools if not t.endswith('-')])))
Example #8
0
 def set_current_stack_frame(self, idx: int = 0):
     thread = self._debugger.CurrentThread
     stack_frames = thread.StackFrames
     try:
         stack_frame = stack_frames[idx]
         self._debugger.CurrentStackFrame = stack_frame.raw
     except IndexError:
         raise Error('attempted to access stack frame {} out of {}'.format(
             idx, len(stack_frames)))
Example #9
0
    def handle_options(self, defaults):
        options = self.context.options

        if not options.builder and (options.cflags or options.ldflags):
            warn(self.context, '--cflags and --ldflags will be ignored when not'
                               ' using --builder')

        if options.binary:
            options.binary = os.path.abspath(options.binary)
            if not os.path.isfile(options.binary):
                raise Error('<d>could not find binary file</> <r>"{}"</>'
                            .format(options.binary))
        else:
            try:
                self.build_script = handle_builder_tool_options(self.context)
            except ToolArgumentError as e:
                raise Error(e)

        try:
            handle_debugger_tool_options(self.context, defaults)
        except ToolArgumentError as e:
            raise Error(e)

        options.test_path = os.path.abspath(options.test_path)
        options.test_path = os.path.normcase(options.test_path)
        if not os.path.isfile(options.test_path) and not os.path.isdir(options.test_path):
            raise Error(
                '<d>could not find test path</> <r>"{}"</>'.format(
                    options.test_path))

        options.results_directory = os.path.abspath(options.results_directory)
        if not os.path.isdir(options.results_directory):
            try:
                os.makedirs(options.results_directory, exist_ok=True)
            except OSError as e:
                raise Error(
                    '<d>could not create directory</> <r>"{}"</> <y>({})</>'.
                    format(options.results_directory, e.strerror))
Example #10
0
    def go(self) -> ReturnCode:
        options = self.context.options

        with open(options.input_path, 'rb') as fp:
            steps = pickle.load(fp)

        try:
            heuristic = Heuristic(self.context, steps)
        except HeuristicException as e:
            raise Error('could not apply heuristic: {}'.format(e))

        self.context.o.auto('{}\n\n{}\n\n{}\n\n'.format(
            heuristic.summary_string, steps, heuristic.verbose_output))

        return ReturnCode.OK
Example #11
0
    def __exit__(self, *args):
        os.chdir(self.orig_cwd)
        if self.context.options.save_temps:
            self.context.o.blue('"{}" left in place [--save-temps]\n'.format(
                self.path))
            return

        exception = AssertionError('should never be raised')
        for _ in range(100):
            try:
                shutil.rmtree(self.path)
                return
            except OSError as e:
                exception = e
                time.sleep(0.1)
        raise Error(exception)
Example #12
0
    def go(self) -> ReturnCode:
        with Timer('loading debugger'):
            debugger = Debuggers(self.context).load(self.options.debugger)

        with Timer('running debugger'):
            if not debugger.is_available:
                msg = '<d>could not load {}</> ({})\n'.format(
                    debugger.name, debugger.loading_error)
                if self.options.verbose:
                    msg = '{}\n    {}'.format(
                        msg, '    '.join(debugger.loading_error_trace))
                raise Error(msg)

        self.debugger_controller.run_debugger(debugger)

        with open(self.controller_path, 'wb') as fp:
            pickle.dump(self.debugger_controller, fp)
        return ReturnCode.OK
Example #13
0
    def _clang_opt_bisect_build(self, opt_bisect_limits):
        options = self.context.options
        compiler_options = [
            '{} -mllvm -opt-bisect-limit={}'.format(options.cflags,
                                                    opt_bisect_limit)
            for opt_bisect_limit in opt_bisect_limits
        ]
        linker_options = options.ldflags

        try:
            return run_external_build_script(
                self.context,
                source_files=options.source_files,
                compiler_options=compiler_options,
                linker_options=linker_options,
                script_path=self.build_script,
                executable_file=options.executable)
        except BuildScriptException as e:
            raise Error(e)
Example #14
0
    def handle_options(self, defaults):
        options = self.context.options

        # We accept either or both of --binary and --builder.
        if not options.binary and not options.builder:
            raise Error('expected --builder or --binary')

        # --binary overrides --builder
        if options.binary:
            if options.builder:
                warn(self.context, "overriding --builder with --binary\n")

            options.binary = os.path.abspath(options.binary)
            if not os.path.isfile(options.binary):
                raise Error(
                    '<d>could not find binary file</> <r>"{}"</>'.format(
                        options.binary))
        else:
            try:
                self.build_script = handle_builder_tool_options(self.context)
            except ToolArgumentError as e:
                raise Error(e)

        try:
            handle_debugger_tool_options(self.context, defaults)
        except ToolArgumentError as e:
            raise Error(e)

        options.test_path = os.path.abspath(options.test_path)
        options.test_path = os.path.normcase(options.test_path)
        if not os.path.isfile(options.test_path) and not os.path.isdir(
                options.test_path):
            raise Error('<d>could not find test path</> <r>"{}"</>'.format(
                options.test_path))

        options.results_directory = os.path.abspath(options.results_directory)
        if not os.path.isdir(options.results_directory):
            try:
                os.makedirs(options.results_directory, exist_ok=True)
            except OSError as e:
                raise Error(
                    '<d>could not create directory</> <r>"{}"</> <y>({})</>'.
                    format(options.results_directory, e.strerror))
Example #15
0
    def _write(self, text, stream):
        text = str(text)

        # Users can embed color control tags in their output
        # (e.g. <r>hello</> <y>world</> would write the word 'hello' in red and
        # 'world' in yellow).
        # This function parses these tags using a very simple recursive
        # descent.
        colors = {
            'r': self.red,
            'y': self.yellow,
            'g': self.green,
            'b': self.blue,
            'd': self.default,
            'a': self.auto,
        }

        # Find all tags (whether open or close)
        tags = [
            t for t in re.finditer('<([{}/])>'.format(''.join(colors)), text)
        ]

        if not tags:
            # No tags.  Just write the text to the current stream and return.
            # 'unmangling' any tags that have been mangled so that they won't
            # render as colors (for example in error output from this
            # function).
            stream = self._set_valid_stream(stream)
            stream.py.write(text.replace(r'\>', '>'))
            return

        open_tags = [i for i in tags if i.group(1) != '/']
        close_tags = [i for i in tags if i.group(1) == '/']

        if (len(open_tags) != len(close_tags)
                or any(o.start() >= c.start()
                       for (o, c) in zip(open_tags, close_tags))):
            raise Error('open/close tag mismatch in "{}"'.format(
                text.rstrip()).replace('>', r'\>'))

        open_tag = open_tags.pop(0)

        # We know that the tags balance correctly, so figure out where the
        # corresponding close tag is to the current open tag.
        tag_nesting = 1
        close_tag = None
        for tag in tags[1:]:
            if tag.group(1) == '/':
                tag_nesting -= 1
            else:
                tag_nesting += 1
            if tag_nesting == 0:
                close_tag = tag
                break
        else:
            assert False, text

        # Use the method on the top of the stack for text prior to the open
        # tag.
        before = text[:open_tag.start()]
        if before:
            self._stack[-1](before, lock=_null_lock, stream=stream)

        # Use the specified color for the tag itself.
        color = open_tag.group(1)
        within = text[open_tag.end():close_tag.start()]
        if within:
            colors[color](within, lock=_null_lock, stream=stream)

        # Use the method on the top of the stack for text after the close tag.
        after = text[close_tag.end():]
        if after:
            self._stack[-1](after, lock=_null_lock, stream=stream)
Example #16
0
 def handle_options(self, defaults):
     if not self.context.options.subtool:
         raise Error('<d>no subtool specified</>\n\n{}\n'.format(
             self.parser.format_help()))
Example #17
0
    def _run_test(self, test_name):  # noqa
        options = self.context.options

        per_pass_score = []
        current_bisect_pass_summary = defaultdict(list)

        max_limits = self._get_bisect_limits()
        overall_limit = sum(max_limits)
        prev_score = 1.0
        prev_steps_str = None

        for current_limit in range(overall_limit + 1):
            # Take the overall limit number and split it across buckets for
            # each source file.
            limit_remaining = current_limit
            file_limits = [0] * len(max_limits)
            for i, max_limit in enumerate(max_limits):
                if limit_remaining < max_limit:
                    file_limits[i] += limit_remaining
                    break
                else:
                    file_limits[i] = max_limit
                    limit_remaining -= file_limits[i]

            f = [l for l in file_limits if l]
            current_file_index = len(f) - 1 if f else 0

            _, err, builderIR = self._clang_opt_bisect_build(file_limits)
            err_lines = err.splitlines()
            # Find the last line that specified a running pass.
            for l in err_lines[::-1]:
                match = Tool._re_running_pass.match(l)
                if match:
                    pass_info = match.groups()
                    break
            else:
                pass_info = (0, None, None)

            try:
                debugger_controller =self._init_debugger_controller()
                debugger_controller = run_debugger_subprocess(
                    debugger_controller, self.context.working_directory.path)
                steps = debugger_controller.step_collection
            except DebuggerException:
                steps =  DextIR(
                    executable_path=self.context.options.executable,
                    source_paths=self.context.options.source_files,
                    dexter_version=self.context.version)

            steps.builder = builderIR

            try:
                heuristic = Heuristic(self.context, steps)
            except HeuristicException as e:
                raise Error(e)

            score_difference = heuristic.score - prev_score
            prev_score = heuristic.score

            isnan = heuristic.score != heuristic.score
            if isnan or score_difference < 0:
                color1 = 'r'
                color2 = 'r'
            elif score_difference > 0:
                color1 = 'g'
                color2 = 'g'
            else:
                color1 = 'y'
                color2 = 'd'

            summary = '<{}>running pass {}/{} on "{}"'.format(
                color2, pass_info[0], max_limits[current_file_index],
                test_name)
            if len(options.source_files) > 1:
                summary += ' [{}/{}]'.format(current_limit, overall_limit)

            pass_text = ''.join(p for p in pass_info[1:] if p)
            summary += ': {} <{}>{:+.4f}</> <{}>{}</></>\n'.format(
                heuristic.summary_string, color1, score_difference, color2,
                pass_text)

            self.context.o.auto(summary)

            heuristic_verbose_output = heuristic.verbose_output

            if options.verbose:
                self.context.o.auto(heuristic_verbose_output)

            steps_str = str(steps)
            steps_changed = steps_str != prev_steps_str
            prev_steps_str = steps_str

            # If a results directory has been specified and this is the first
            # pass or something has changed, write a text file containing
            # verbose information on the current status.
            if options.results_directory and (current_limit == 0 or
                                              score_difference or
                                              steps_changed):
                file_name = '-'.join(
                    str(s) for s in [
                        'status', test_name, '{{:0>{}}}'.format(
                            len(str(overall_limit))).format(current_limit),
                        '{:.4f}'.format(heuristic.score).replace(
                            '.', '_'), pass_info[1]
                    ] if s is not None)

                file_name = ''.join(
                    c for c in file_name
                    if c.isalnum() or c in '()-_./ ').strip().replace(
                    ' ', '_').replace('/', '_')

                output_text_path = os.path.join(options.results_directory,
                                                '{}.txt'.format(file_name))
                with open(output_text_path, 'w') as fp:
                    self.context.o.auto(summary + '\n', stream=Stream(fp))
                    self.context.o.auto(str(steps) + '\n', stream=Stream(fp))
                    self.context.o.auto(
                        heuristic_verbose_output + '\n', stream=Stream(fp))

                output_dextIR_path = os.path.join(options.results_directory,
                                                  '{}.dextIR'.format(file_name))
                with open(output_dextIR_path, 'wb') as fp:
                    pickle.dump(steps, fp, protocol=pickle.HIGHEST_PROTOCOL)

            per_pass_score.append((test_name, pass_text,
                                   heuristic.score))

            if pass_info[1]:
                self._all_bisect_pass_summary[pass_info[1]].append(
                    score_difference)

                current_bisect_pass_summary[pass_info[1]].append(
                    score_difference)

        if options.results_directory:
            per_pass_score_path = os.path.join(
                options.results_directory,
                '{}-per_pass_score.csv'.format(test_name))

            with open(per_pass_score_path, mode='w', newline='') as fp:
                writer = csv.writer(fp, delimiter=',')
                writer.writerow(['Source File', 'Pass', 'Score'])

                for path, pass_, score in per_pass_score:
                    writer.writerow([path, pass_, score])
            self.context.o.blue('wrote "{}"\n'.format(per_pass_score_path))

            pass_summary_path = os.path.join(
                options.results_directory, '{}-pass-summary.csv'.format(test_name))

            self._write_pass_summary(pass_summary_path,
                                     current_bisect_pass_summary)
Example #18
0
 def error(self, message):
     """Use the Dexception Error mechanism (including auto-colored output).
     """
     raise Error('{}\n\n{}'.format(message, self.format_usage()))