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
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
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)
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))
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)
def go(self) -> ReturnCode: with Timer('list debuggers'): try: Debuggers(self.context).list() except DebuggerException as e: raise Error(e) return ReturnCode.OK
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('-')])))
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)))
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))
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
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)
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
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)
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))
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)
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()))
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)
def error(self, message): """Use the Dexception Error mechanism (including auto-colored output). """ raise Error('{}\n\n{}'.format(message, self.format_usage()))