def trace_dispatch(self, frame, event, arg): # ENDIF # DEBUG = 'code_to_debug' in frame.f_code.co_filename main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args # if DEBUG: print('frame trace_dispatch %s %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, info.pydev_step_cmd)) try: info.is_tracing = True line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger._finish_debugging_session: return None if event == 'call' else NO_FTRACE plugin_manager = main_debugger.plugin is_exception_event = event == 'exception' has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks if is_exception_event: if has_exception_breakpoints: should_stop, frame = self.should_stop_on_exception(frame, event, arg) if should_stop: self.handle_exception(frame, event, arg) return self.trace_dispatch is_line = False is_return = False is_call = False else: is_line = event == 'line' is_return = event == 'return' is_call = event == 'call' if not is_line and not is_return and not is_call: # Unexpected: just keep the same trace func. return self.trace_dispatch need_signature_trace_return = False if main_debugger.signature_factory is not None: if is_call: need_signature_trace_return = send_signature_call_trace(main_debugger, frame, filename) elif is_return: send_signature_return_trace(main_debugger, frame, filename, arg) stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd if is_exception_event: breakpoints_for_file = None # CMD_STEP_OVER = 108 if stop_frame and stop_frame is not frame and step_cmd == 108 and \ arg[0] in (StopIteration, GeneratorExit) and arg[2] is None: info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107 info.pydev_step_stop = None else: # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break # eventually. Force the step mode to step into and the step stop frame to None. # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user # to make a step in or step over at that location). # Note: this is especially troublesome when we're skipping code with the # @DontTrace comment. if stop_frame is frame and is_return and step_cmd in (109, 108): # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 if not frame.f_code.co_flags & 0x20: # CO_GENERATOR = 0x20 (inspect.CO_GENERATOR) info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107 info.pydev_step_stop = None breakpoints_for_file = main_debugger.breakpoints.get(filename) can_skip = False if info.pydev_state == 1: # STATE_RUN = 1 # we can skip if: # - we have no stop marked # - we should make a step return/step over and we're not in the current frame # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 can_skip = (step_cmd == -1 and stop_frame is None) \ or (step_cmd in (109, 108) and stop_frame is not frame) if can_skip: if plugin_manager is not None and main_debugger.has_plugin_line_breaks: can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame) # CMD_STEP_OVER = 108 if can_skip and main_debugger.show_return_values and info.pydev_step_cmd == 108 and frame.f_back is info.pydev_step_stop: # trace function for showing return values after step over can_skip = False # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace # also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, # so, that's why the additional checks are there. if not breakpoints_for_file: if can_skip: if has_exception_breakpoints: return self.trace_exception else: if need_signature_trace_return: return self.trace_return else: return None if is_call else NO_FTRACE else: # When cached, 0 means we don't have a breakpoint and 1 means we have. if can_skip: breakpoints_in_line_cache = frame_skips_cache.get(line_cache_key, -1) if breakpoints_in_line_cache == 0: return self.trace_dispatch breakpoints_in_frame_cache = frame_skips_cache.get(frame_cache_key, -1) if breakpoints_in_frame_cache != -1: # Gotten from cache. has_breakpoint_in_frame = breakpoints_in_frame_cache == 1 else: has_breakpoint_in_frame = False # Checks the breakpoint to see if there is a context match in some function curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>', '<lambda>'): curr_func_name = '' for breakpoint in dict_iter_values(breakpoints_for_file): # jython does not support itervalues() # will match either global or some function if breakpoint.func_name in ('None', curr_func_name): has_breakpoint_in_frame = True break # Cache the value (1 or 0 or -1 for default because of cython). if has_breakpoint_in_frame: frame_skips_cache[frame_cache_key] = 1 else: frame_skips_cache[frame_cache_key] = 0 if can_skip and not has_breakpoint_in_frame: if has_exception_breakpoints: return self.trace_exception else: if need_signature_trace_return: return self.trace_return else: return None if is_call else NO_FTRACE # We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame # if DEBUG: print('NOT skipped: %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, event, frame.__class__.__name__)) try: flag = False # return is not taken into account for breakpoint hit because we'd have a double-hit in this case # (one for the line and the other for the return). stop_info = {} breakpoint = None exist_result = False stop = False bp_type = None if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file: breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd == CMD_STEP_OVER and stop_frame is frame and (is_line or is_return): stop = False # we don't stop on breakpoint if we have to stop by step-over (it will be processed later) elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args) if result: exist_result = True flag, breakpoint, new_frame, bp_type = result if breakpoint: # ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint # lets do the conditional stuff here if stop or exist_result: eval_result = False if breakpoint.has_condition: eval_result = main_debugger.handle_breakpoint_condition(info, breakpoint, new_frame) if breakpoint.expression is not None: main_debugger.handle_breakpoint_expression(breakpoint, info, new_frame) if breakpoint.is_logpoint and info.pydev_message is not None and len(info.pydev_message) > 0: cmd = main_debugger.cmd_factory.make_io_message(info.pydev_message + os.linesep, '1') main_debugger.writer.add_command(cmd) if breakpoint.has_condition and not eval_result: return self.trace_dispatch if is_call and frame.f_code.co_name in ('<module>', '<lambda>'): # If we find a call for a module, it means that the module is being imported/executed for the # first time. In this case we have to ignore this hit as it may later duplicated by a # line event at the same place (so, if there's a module with a print() in the first line # the user will hit that line twice, which is not what we want). # # As for lambda, as it only has a single statement, it's not interesting to trace # its call and later its line event as they're usually in the same line. return self.trace_dispatch else: # if the frame is traced after breakpoint stop, # but the file should be ignored while stepping because of filters if step_cmd != -1: if main_debugger.is_filter_enabled and main_debugger.is_ignored_by_filters(filename): # ignore files matching stepping filters return self.trace_dispatch if main_debugger.is_filter_libraries and not main_debugger.in_project_scope(filename): # ignore library files while stepping return self.trace_dispatch if main_debugger.show_return_values: if is_return and info.pydev_step_cmd == CMD_STEP_OVER and frame.f_back == info.pydev_step_stop: self.show_return_values(frame, arg) elif main_debugger.remove_return_values_flag: try: self.remove_return_values(main_debugger, frame) finally: main_debugger.remove_return_values_flag = False if stop: self.set_suspend( thread, CMD_SET_BREAK, suspend_other_threads=breakpoint and breakpoint.suspend_policy == "ALL", ) elif flag and plugin_manager is not None: result = plugin_manager.suspend(main_debugger, thread, frame, bp_type) if result: frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.do_wait_suspend(thread, frame, event, arg) return self.trace_dispatch else: if not breakpoint and is_line: # No stop from anyone and no breakpoint found in line (cache that). frame_skips_cache[line_cache_key] = 0 except: traceback.print_exc() raise # step handling. We stop when we hit the right frame try: should_skip = 0 if pydevd_dont_trace.should_trace_hook is not None: if self.should_skip == -1: # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code # Which will be handled by this frame is read-only, so, we can cache it safely. if not pydevd_dont_trace.should_trace_hook(frame, filename): # -1, 0, 1 to be Cython-friendly should_skip = self.should_skip = 1 else: should_skip = self.should_skip = 0 else: should_skip = self.should_skip plugin_stop = False if should_skip: stop = False elif step_cmd == CMD_STEP_INTO: stop = is_line or is_return if plugin_manager is not None: result = plugin_manager.cmd_step_into(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_STEP_INTO_MY_CODE: if main_debugger.in_project_scope(frame.f_code.co_filename): stop = is_line elif step_cmd == CMD_STEP_OVER: stop = stop_frame is frame and (is_line or is_return) if frame.f_code.co_flags & CO_GENERATOR: if is_return: stop = False if plugin_manager is not None: result = plugin_manager.cmd_step_over(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_SMART_STEP_INTO: stop = False if info.pydev_smart_step_stop is frame: info.pydev_func_name = '.invalid.' # Must match the type in cython info.pydev_smart_step_stop = None if is_line or is_exception_event: curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>') or curr_func_name is None: curr_func_name = '' if curr_func_name == info.pydev_func_name: stop = True elif step_cmd == CMD_STEP_RETURN: stop = is_return and stop_frame is frame else: stop = False if stop and step_cmd != -1 and is_return and IS_PY3K and hasattr(frame, "f_back"): f_code = getattr(frame.f_back, 'f_code', None) if f_code is not None: if main_debugger.get_file_type( get_abs_path_real_path_and_base_from_file(f_code.co_filename)) == main_debugger.PYDEV_FILE: stop = False if plugin_stop: stopped_on_plugin = plugin_manager.stop(main_debugger, frame, event, self._args, stop_info, arg, step_cmd) elif stop: if is_line: self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, frame, event, arg) else: # return event back = frame.f_back if back is not None: # When we get to the pydevd run function, the debugging has actually finished for the main thread # (note that it can still go on for other threads, but for this one, we just make it finish) # So, just setting it to None should be OK _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back) if (base, back.f_code.co_name) in (DEBUG_START, DEBUG_START_PY3K): back = None elif base == TRACE_PROPERTY: # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) # if we're in a return, we want it to appear to the user in the previous frame! return None if is_call else NO_FTRACE elif pydevd_dont_trace.should_trace_hook is not None: if not pydevd_dont_trace.should_trace_hook(back, back_filename): # In this case, we'll have to skip the previous one because it shouldn't be traced. # Also, we have to reset the tracing, because if the parent's parent (or some # other parent) has to be traced and it's not currently, we wouldn't stop where # we should anymore (so, a step in/over/return may not stop anywhere if no parent is traced). # Related test: _debugger_case17a.py main_debugger.set_trace_for_frame_and_parents(back) return None if is_call else NO_FTRACE if back is not None: # if we're in a return, we want it to appear to the user in the previous frame! self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, back, event, arg) else: # in jython we may not have a back frame info.pydev_step_stop = None info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN except KeyboardInterrupt: raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: return None if is_call else NO_FTRACE # if we are quitting, let's stop the tracing if not main_debugger.quitting: return self.trace_dispatch else: return None if is_call else NO_FTRACE finally: info.is_tracing = False
def trace_dispatch(self, frame, event, arg): # ENDIF main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args # print('frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event, info.pydev_step_cmd) try: info.is_tracing = True line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger._finish_debugging_session: return None plugin_manager = main_debugger.plugin is_exception_event = event == 'exception' has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks if is_exception_event: if has_exception_breakpoints: flag, frame = self.should_stop_on_exception( frame, event, arg) if flag: self.handle_exception(frame, event, arg) return self.trace_dispatch is_line = False is_return = False is_call = False else: is_line = event == 'line' is_return = event == 'return' is_call = event == 'call' if not is_line and not is_return and not is_call: # I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace. return None need_trace_return = False if is_call and main_debugger.signature_factory: need_trace_return = send_signature_call_trace( main_debugger, frame, filename) if is_return and main_debugger.signature_factory: send_signature_return_trace(main_debugger, frame, filename, arg) stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd if is_exception_event: breakpoints_for_file = None # CMD_STEP_OVER = 108 if stop_frame and stop_frame is not frame and step_cmd == 108 and \ arg[0] in (StopIteration, GeneratorExit) and arg[2] is None: info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107 info.pydev_step_stop = None else: # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break # eventually. Force the step mode to step into and the step stop frame to None. # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user # to make a step in or step over at that location). # Note: this is especially troublesome when we're skipping code with the # @DontTrace comment. if stop_frame is frame and is_return and step_cmd in ( 109, 108): # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 if not frame.f_code.co_flags & 0x20: # CO_GENERATOR = 0x20 (inspect.CO_GENERATOR) info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107 info.pydev_step_stop = None breakpoints_for_file = main_debugger.breakpoints.get(filename) can_skip = False if info.pydev_state == 1: # STATE_RUN = 1 #we can skip if: #- we have no stop marked #- we should make a step return/step over and we're not in the current frame # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 can_skip = (step_cmd == -1 and stop_frame is None) \ or (step_cmd in (109, 108) and stop_frame is not frame) if can_skip: if plugin_manager is not None and main_debugger.has_plugin_line_breaks: can_skip = not plugin_manager.can_not_skip( main_debugger, self, frame) # CMD_STEP_OVER = 108 if can_skip and is_return and main_debugger.show_return_values and info.pydev_step_cmd == 108 and frame.f_back is info.pydev_step_stop: # trace function for showing return values after step over can_skip = False if main_debugger.frame_eval_func and event == 'return' and info.pydev_step_cmd == -1: frames_set = main_debugger.disable_tracing_after_exit_frames.get( get_thread_id(thread), None) if frames_set is not None: if frame in frames_set: frames_set.remove(frame) if len(frames_set) == 0: # there were some frames, but we exited all of them, stop tracing main_debugger.disable_tracing_after_exit_frames.pop( get_thread_id(thread)) main_debugger.SetTrace(None) # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace # also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, # so, that's why the additional checks are there. if not breakpoints_for_file: if can_skip: if has_exception_breakpoints: return self.trace_exception else: if need_trace_return: return self.trace_return else: return None else: # When cached, 0 means we don't have a breakpoint and 1 means we have. if can_skip: breakpoints_in_line_cache = frame_skips_cache.get( line_cache_key, -1) if breakpoints_in_line_cache == 0: return self.trace_dispatch breakpoints_in_frame_cache = frame_skips_cache.get( frame_cache_key, -1) if breakpoints_in_frame_cache != -1: # Gotten from cache. has_breakpoint_in_frame = breakpoints_in_frame_cache == 1 else: has_breakpoint_in_frame = False # Checks the breakpoint to see if there is a context match in some function curr_func_name = frame.f_code.co_name #global context is set with an empty name if curr_func_name in ('?', '<module>'): curr_func_name = '' for breakpoint in dict_iter_values( breakpoints_for_file ): #jython does not support itervalues() #will match either global or some function if breakpoint.func_name in ('None', curr_func_name): has_breakpoint_in_frame = True break # Cache the value (1 or 0 or -1 for default because of cython). if has_breakpoint_in_frame: frame_skips_cache[frame_cache_key] = 1 else: frame_skips_cache[frame_cache_key] = 0 if can_skip and not has_breakpoint_in_frame: if has_exception_breakpoints: return self.trace_exception else: if need_trace_return: return self.trace_return else: return None #We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame #print('NOT skipped', frame.f_lineno, frame.f_code.co_name, event) try: flag = False #return is not taken into account for breakpoint hit because we'd have a double-hit in this case #(one for the line and the other for the return). stop_info = {} breakpoint = None exist_result = False stop = False bp_type = None if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and dict_contains( breakpoints_for_file, line): breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd == CMD_STEP_OVER and stop_frame is frame and ( is_line or is_return): stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later) elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint( main_debugger, self, frame, event, self._args) if result: exist_result = True flag, breakpoint, new_frame, bp_type = result if breakpoint: #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint # lets do the conditional stuff here if stop or exist_result: condition = breakpoint.condition if condition is not None: try: val = eval(condition, new_frame.f_globals, new_frame.f_locals) if not val: return self.trace_dispatch except: if type(condition) != type(''): if hasattr(condition, 'encode'): condition = condition.encode('utf-8') msg = 'Error while evaluating expression: %s\n' % ( condition, ) sys.stderr.write(msg) traceback.print_exc() if not main_debugger.suspend_on_breakpoint_exception: return self.trace_dispatch else: stop = True try: # add exception_type and stacktrace into thread additional info etype, value, tb = sys.exc_info() try: error = ''.join( traceback. format_exception_only( etype, value)) stack = traceback.extract_stack( f=tb.tb_frame.f_back) # On self.set_suspend(thread, CMD_SET_BREAK) this info will be # sent to the client. info.conditional_breakpoint_exception = \ ('Condition:\n' + condition + '\n\nError:\n' + error, stack) finally: etype, value, tb = None, None, None except: traceback.print_exc() if breakpoint.expression is not None: try: try: val = eval(breakpoint.expression, new_frame.f_globals, new_frame.f_locals) except: val = sys.exc_info()[1] finally: if val is not None: info.pydev_message = str(val) if not main_debugger.first_breakpoint_reached: if is_call: back = frame.f_back if back is not None: # When we start debug session, we call execfile in pydevd run function. It produces an additional # 'call' event for tracing and we stop on the first line of code twice. _, back_filename, base = get_abs_path_real_path_and_base_from_frame( back) if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \ (base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]): stop = False main_debugger.first_breakpoint_reached = True else: # if the frame is traced after breakpoint stop, # but the file should be ignored while stepping because of filters if step_cmd != -1: if main_debugger.is_filter_enabled and main_debugger.is_ignored_by_filters( filename): # ignore files matching stepping filters return self.trace_dispatch if main_debugger.is_filter_libraries and main_debugger.not_in_scope( filename): # ignore library files while stepping return self.trace_dispatch if main_debugger.show_return_values or main_debugger.remove_return_values_flag: self.manage_return_values(main_debugger, frame, event, arg) if stop: self.set_suspend(thread, CMD_SET_BREAK) if breakpoint and breakpoint.suspend_policy == "ALL": main_debugger.suspend_all_other_threads(thread) elif flag and plugin_manager is not None: result = plugin_manager.suspend(main_debugger, thread, frame, bp_type) if result: frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.do_wait_suspend(thread, frame, event, arg) return self.trace_dispatch else: if not breakpoint and not is_return: # No stop from anyone and no breakpoint found in line (cache that). frame_skips_cache[line_cache_key] = 0 except: traceback.print_exc() raise #step handling. We stop when we hit the right frame try: should_skip = 0 if pydevd_dont_trace.should_trace_hook is not None: if self.should_skip == -1: # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code # Which will be handled by this frame is read-only, so, we can cache it safely. if not pydevd_dont_trace.should_trace_hook( frame, filename): # -1, 0, 1 to be Cython-friendly should_skip = self.should_skip = 1 else: should_skip = self.should_skip = 0 else: should_skip = self.should_skip plugin_stop = False if should_skip: stop = False elif step_cmd == CMD_STEP_INTO: stop = is_line or is_return if plugin_manager is not None: result = plugin_manager.cmd_step_into( main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_STEP_INTO_MY_CODE: if not main_debugger.not_in_scope( frame.f_code.co_filename): stop = is_line elif step_cmd == CMD_STEP_OVER: stop = stop_frame is frame and (is_line or is_return) if frame.f_code.co_flags & CO_GENERATOR: if is_return: stop = False if plugin_manager is not None: result = plugin_manager.cmd_step_over( main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_SMART_STEP_INTO: stop = False if info.pydev_smart_step_stop is frame: info.pydev_func_name = '.invalid.' # Must match the type in cython info.pydev_smart_step_stop = None if is_line or is_exception_event: curr_func_name = frame.f_code.co_name #global context is set with an empty name if curr_func_name in ( '?', '<module>') or curr_func_name is None: curr_func_name = '' if curr_func_name == info.pydev_func_name: stop = True elif step_cmd == CMD_STEP_RETURN: stop = is_return and stop_frame is frame elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT: stop = False if is_line or is_exception_event: #Yes, we can only act on line events (weird hum?) #Note: This code is duplicated at pydevd.py #Acting on exception events after debugger breaks with exception curr_func_name = frame.f_code.co_name #global context is set with an empty name if curr_func_name in ('?', '<module>'): curr_func_name = '' if curr_func_name == info.pydev_func_name: line = info.pydev_next_line if frame.f_lineno == line: stop = True else: if frame.f_trace is None: frame.f_trace = self.trace_dispatch frame.f_lineno = line frame.f_trace = None stop = True else: stop = False if step_cmd != -1 and is_return and IS_PY3K and hasattr( frame, "f_back"): f_code = getattr(frame.f_back, 'f_code', None) if f_code is not None: back_filename = os.path.basename(f_code.co_filename) file_type = get_file_type(back_filename) if file_type == PYDEV_FILE: stop = False if plugin_stop: stopped_on_plugin = plugin_manager.stop( main_debugger, frame, event, self._args, stop_info, arg, step_cmd) elif stop: if is_line: self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, frame, event, arg) else: #return event back = frame.f_back if back is not None: #When we get to the pydevd run function, the debugging has actually finished for the main thread #(note that it can still go on for other threads, but for this one, we just make it finish) #So, just setting it to None should be OK _, back_filename, base = get_abs_path_real_path_and_base_from_frame( back) if base == DEBUG_START[ 0] and back.f_code.co_name == DEBUG_START[ 1]: back = None elif base == TRACE_PROPERTY: # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) #if we're in a return, we want it to appear to the user in the previous frame! return None elif pydevd_dont_trace.should_trace_hook is not None: if not pydevd_dont_trace.should_trace_hook( back, back_filename): # In this case, we'll have to skip the previous one because it shouldn't be traced. # Also, we have to reset the tracing, because if the parent's parent (or some # other parent) has to be traced and it's not currently, we wouldn't stop where # we should anymore (so, a step in/over/return may not stop anywhere if no parent is traced). # Related test: _debugger_case17a.py main_debugger.set_trace_for_frame_and_parents( back, overwrite_prev_trace=True) return None if back is not None: #if we're in a return, we want it to appear to the user in the previous frame! self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, back, event, arg) else: #in jython we may not have a back frame info.pydev_step_stop = None info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN except KeyboardInterrupt: raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: return None #if we are quitting, let's stop the tracing retVal = None if not main_debugger.quitting: retVal = self.trace_dispatch return retVal finally: info.is_tracing = False
def trace_dispatch(self, frame, event, arg): # ENDIF main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args # print('frame trace_dispatch %s %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, info.pydev_step_cmd)) # The thread can be already suspended by another function, e.g. built-in breakpoint hook. if info.is_tracing: return None try: info.is_tracing = True line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger._finish_debugging_session: if event != 'call': frame.f_trace = NO_FTRACE return None # IFDEF CYTHON # if event == 'opcode': # instructions = self._get_instructions(frame) # for i, inst in enumerate(instructions): # if inst.offset == frame.f_lasti: # opname, arg, argval = inst.opname, inst.arg, str(inst.argval) # print('frame trace_dispatch %s %s %s %s %s %s %s %s' % (frame.f_lineno, frame.f_lasti, frame.f_code.co_name, # frame.f_code.co_filename, event, opname, arg, argval)) # try: # self._bytecode_offset = instructions[i + 1].offset # except IndexError: # break # return self.trace_dispatch # ENDIF plugin_manager = main_debugger.plugin is_exception_event = event == 'exception' has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks if is_exception_event: if has_exception_breakpoints: should_stop, frame = self.should_stop_on_exception( frame, event, arg) if should_stop: self.handle_exception(frame, event, arg) # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch is_line = False is_return = False is_call = False else: is_line = event == 'line' is_return = event == 'return' is_call = event == 'call' if not is_line and not is_return and not is_call: # Unexpected: just keep the same trace func. # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch need_signature_trace_return = False if main_debugger.signature_factory is not None: if is_call: need_signature_trace_return = send_signature_call_trace( main_debugger, frame, filename) elif is_return: send_signature_return_trace(main_debugger, frame, filename, arg) stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd is_generator_or_coroutime = frame.f_code.co_flags & 0xa0 # 0xa0 == CO_GENERATOR = 0x20 | CO_COROUTINE = 0x80 breakpoints_for_file = main_debugger.breakpoints.get(filename) if not is_exception_event: if is_generator_or_coroutime: if is_return: # Dealing with coroutines and generators: # When in a coroutine we change the perceived event to the debugger because # a call, StopIteration exception and return are usually just pausing/unpausing it. returns_cache_key = (frame_cache_key, 'returns') return_lines = frame_skips_cache.get(returns_cache_key) if return_lines is None: # Note: we're collecting the return lines by inspecting the bytecode as # there are multiple returns and multiple stop iterations when awaiting and # it doesn't give any clear indication when a coroutine or generator is # finishing or just pausing. return_lines = set() for x in main_debugger.collect_return_info( frame.f_code): # Note: cython does not support closures in cpdefs (so we can't use # a list comprehension). return_lines.add(x.return_line) frame_skips_cache[returns_cache_key] = return_lines if line not in return_lines: # Not really a return (coroutine/generator paused). return self.trace_dispatch elif is_call: # Don't stop when calling coroutines, we will on other event anyway if necessary. return self.trace_dispatch can_skip = False if info.pydev_state == 1: # STATE_RUN = 1 # we can skip if: # - we have no stop marked # - we should make a step return/step over and we're not in the current frame # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 can_skip = (step_cmd == -1 and stop_frame is None) \ or (step_cmd in (109, 108) and stop_frame is not frame) if can_skip: if plugin_manager is not None and main_debugger.has_plugin_line_breaks: can_skip = not plugin_manager.can_not_skip( main_debugger, self, frame, info) # CMD_STEP_OVER = 108 if can_skip and main_debugger.show_return_values and info.pydev_step_cmd == 108 and frame.f_back is info.pydev_step_stop: # trace function for showing return values after step over can_skip = False # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace # also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, # so, that's why the additional checks are there. if not breakpoints_for_file: if can_skip: if has_exception_breakpoints: frame.f_trace = self.trace_exception return self.trace_exception else: if need_signature_trace_return: frame.f_trace = self.trace_return return self.trace_return else: if not is_call: frame.f_trace = NO_FTRACE return None else: # When cached, 0 means we don't have a breakpoint and 1 means we have. if can_skip: breakpoints_in_line_cache = frame_skips_cache.get( line_cache_key, -1) if breakpoints_in_line_cache == 0: # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch breakpoints_in_frame_cache = frame_skips_cache.get( frame_cache_key, -1) if breakpoints_in_frame_cache != -1: # Gotten from cache. has_breakpoint_in_frame = breakpoints_in_frame_cache == 1 else: has_breakpoint_in_frame = False # Checks the breakpoint to see if there is a context match in some function curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>', '<lambda>'): curr_func_name = '' for breakpoint in dict_iter_values( breakpoints_for_file ): # jython does not support itervalues() # will match either global or some function if breakpoint.func_name in ('None', curr_func_name): has_breakpoint_in_frame = True break # Cache the value (1 or 0 or -1 for default because of cython). if has_breakpoint_in_frame: frame_skips_cache[frame_cache_key] = 1 else: frame_skips_cache[frame_cache_key] = 0 if can_skip and not has_breakpoint_in_frame: if has_exception_breakpoints: frame.f_trace = self.trace_exception return self.trace_exception else: if need_signature_trace_return: frame.f_trace = self.trace_return return self.trace_return else: if not is_call: frame.f_trace = NO_FTRACE return None # We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame # print('NOT skipped: %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, event, frame.__class__.__name__)) try: flag = False # return is not taken into account for breakpoint hit because we'd have a double-hit in this case # (one for the line and the other for the return). stop_info = {} breakpoint = None exist_result = False stop = False bp_type = None smart_stop_frame = info.pydev_smart_step_context.smart_step_stop context_start_line = info.pydev_smart_step_context.start_line context_end_line = info.pydev_smart_step_context.end_line is_within_context = context_start_line <= line <= context_end_line if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file: breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd == CMD_STEP_OVER: if stop_frame is frame and (is_line or is_return): stop = False # we don't stop on breakpoint if we have to stop by step-over (it will be processed later) elif is_generator_or_coroutime and frame.f_back and frame.f_back is stop_frame: stop = False # we don't stop on breakpoint if stepping is active and we enter a `genexpr` or coroutine context elif step_cmd == CMD_SMART_STEP_INTO and ( frame.f_back is smart_stop_frame and is_within_context): stop = False elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint( main_debugger, self, frame, event, self._args) if result: exist_result = True flag, breakpoint, new_frame, bp_type = result if breakpoint: # ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint # lets do the conditional stuff here if stop or exist_result: eval_result = False if breakpoint.has_condition: eval_result = handle_breakpoint_condition( main_debugger, info, breakpoint, new_frame) if breakpoint.expression is not None: handle_breakpoint_expression( breakpoint, info, new_frame) if breakpoint.is_logpoint and info.pydev_message is not None and len( info.pydev_message) > 0: cmd = main_debugger.cmd_factory.make_io_message( info.pydev_message + os.linesep, '1') main_debugger.writer.add_command(cmd) if breakpoint.has_condition and not eval_result: # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch if is_call and frame.f_code.co_name in ('<module>', '<lambda>'): # If we find a call for a module, it means that the module is being imported/executed for the # first time. In this case we have to ignore this hit as it may later duplicated by a # line event at the same place (so, if there's a module with a print() in the first line # the user will hit that line twice, which is not what we want). # # As for lambda, as it only has a single statement, it's not interesting to trace # its call and later its line event as they're usually in the same line. # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch else: # if the frame is traced after breakpoint stop, # but the file should be ignored while stepping because of filters if step_cmd != -1: if main_debugger.is_filter_enabled and main_debugger.is_ignored_by_filters( filename): # ignore files matching stepping filters # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch if main_debugger.is_filter_libraries and not main_debugger.in_project_scope( filename): # ignore library files while stepping # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch if main_debugger.show_return_values or main_debugger.remove_return_values_flag: self.manage_return_values(main_debugger, frame, event, arg) if stop: self.set_suspend( thread, CMD_SET_BREAK, suspend_other_threads=breakpoint and breakpoint.suspend_policy == "ALL", ) elif flag and plugin_manager is not None: result = plugin_manager.suspend(main_debugger, thread, frame, bp_type) if result: frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.do_wait_suspend(thread, frame, event, arg) # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch else: if not breakpoint and is_line: # No stop from anyone and no breakpoint found in line (cache that). frame_skips_cache[line_cache_key] = 0 except KeyboardInterrupt: self.clear_run_state(info) raise except: traceback.print_exc() raise # step handling. We stop when we hit the right frame try: should_skip = 0 if pydevd_dont_trace.should_trace_hook is not None: if self.should_skip == -1: # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code # Which will be handled by this frame is read-only, so, we can cache it safely. if not pydevd_dont_trace.should_trace_hook( frame, filename): # -1, 0, 1 to be Cython-friendly should_skip = self.should_skip = 1 else: should_skip = self.should_skip = 0 else: should_skip = self.should_skip plugin_stop = False if should_skip: stop = False elif step_cmd == CMD_SMART_STEP_INTO: stop = False if smart_stop_frame is frame: if not is_within_context or not IS_CPYTHON: # We don't stop on jumps in multiline statements, which the Python interpreter does in some cases, # if we they happen in smart step into context. info.pydev_func_name = '.invalid.' # Must match the type in cython stop = True # act as if we did a step into if is_line or is_exception_event: curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ( '?', '<module>') or curr_func_name is None: curr_func_name = '' if smart_stop_frame and smart_stop_frame is frame.f_back: if curr_func_name == info.pydev_func_name and not IS_CPYTHON: # for implementations other than CPython we don't perform any additional checks stop = True else: try: if curr_func_name != info.pydev_func_name and frame.f_back: # try to find function call name using bytecode analysis curr_func_name = find_last_call_name( frame.f_back) if curr_func_name == info.pydev_func_name: stop = find_last_func_call_order(frame.f_back, context_start_line) \ == info.pydev_smart_step_context.call_order except: pydev_log.debug( "Exception while handling smart step into in frame tracer, step into will be performed instead." ) info.pydev_smart_step_context.reset() stop = True # act as if we did a step into # we have to check this case for situations when a user has tried to step into a native function or method, # e.g. `len()`, `list.append()`, etc and this was the only call in a return statement if smart_stop_frame is frame and is_return: stop = True elif step_cmd == CMD_STEP_INTO: stop = is_line or is_return if plugin_manager is not None: result = plugin_manager.cmd_step_into( main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_STEP_INTO_MY_CODE: if main_debugger.in_project_scope( frame.f_code.co_filename): stop = is_line elif step_cmd == CMD_STEP_OVER: stop = stop_frame is frame if stop: # checking the we're going to stop in the project scope if is_line: stop = main_debugger.in_project_scope( frame.f_code.co_filename) elif is_return: stop = frame.f_back and main_debugger.in_project_scope( frame.f_back.f_code.co_filename) if not stop: # we have to promote the back frame (if any) to the step stop frame because we want to continue tracing # in the hope of one of the parent frames will be in the project scope back = frame.f_back if back: info.pydev_step_stop = back else: # if there's no back frame, we just stop as soon as possible info.pydev_step_cmd = CMD_STEP_INTO info.pydev_step_stop = None else: stop = False if plugin_manager is not None: result = plugin_manager.cmd_step_over( main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_STEP_RETURN: stop = is_return and stop_frame is frame else: stop = False if stop and step_cmd != -1 and is_return and IS_PY3K and hasattr( frame, "f_back"): f_code = getattr(frame.f_back, 'f_code', None) if f_code is not None: back_filename = os.path.basename(f_code.co_filename) file_type = get_file_type(back_filename) if file_type == PYDEV_FILE: stop = False if plugin_stop: stopped_on_plugin = plugin_manager.stop( main_debugger, frame, event, self._args, stop_info, arg, step_cmd) elif stop: if is_line: self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, frame, event, arg) else: # return event back = frame.f_back if back is not None: # When we get to the pydevd run function, the debugging has actually finished for the main thread # (note that it can still go on for other threads, but for this one, we just make it finish) # So, just setting it to None should be OK _, back_filename, base = get_abs_path_real_path_and_base_from_frame( back) if (base, back.f_code.co_name) in (DEBUG_START, DEBUG_START_PY3K): back = None elif base == TRACE_PROPERTY: # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) # if we're in a return, we want it to appear to the user in the previous frame! if not is_call: frame.f_trace = NO_FTRACE return None elif pydevd_dont_trace.should_trace_hook is not None: if not pydevd_dont_trace.should_trace_hook( back, back_filename): # In this case, we'll have to skip the previous one because it shouldn't be traced. # Also, we have to reset the tracing, because if the parent's parent (or some # other parent) has to be traced and it's not currently, we wouldn't stop where # we should anymore (so, a step in/over/return may not stop anywhere if no parent is traced). # Related test: _debugger_case17a.py main_debugger.set_trace_for_frame_and_parents( back) if not is_call: frame.f_trace = NO_FTRACE return None if back is not None: # if we're in a return, we want it to appear to the user in the previous frame! self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, back, event, arg) else: # in jython we may not have a back frame self.clear_run_state(info) except KeyboardInterrupt: self.clear_run_state(info) raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: if not is_call: frame.f_trace = NO_FTRACE return None # if we are quitting, let's stop the tracing if not main_debugger.quitting: # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch else: if not is_call: frame.f_trace = NO_FTRACE return None finally: info.is_tracing = False
def trace_dispatch(self, frame, event, arg): # ENDIF main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args # print('frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event, info.pydev_step_cmd) try: info.is_tracing = True line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger._finish_debugging_session: return None plugin_manager = main_debugger.plugin is_exception_event = event == 'exception' has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks if is_exception_event: if has_exception_breakpoints: flag, frame = self.should_stop_on_exception(frame, event, arg) if flag: self.handle_exception(frame, event, arg) return self.trace_dispatch is_line = False is_return = False is_call = False else: is_line = event == 'line' is_return = event == 'return' is_call = event == 'call' if not is_line and not is_return and not is_call: # I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace. return None need_trace_return = False if is_call and main_debugger.signature_factory: need_trace_return = send_signature_call_trace(main_debugger, frame, filename) if is_return and main_debugger.signature_factory: send_signature_return_trace(main_debugger, frame, filename, arg) stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd if is_exception_event: breakpoints_for_file = None # CMD_STEP_OVER = 108 if stop_frame and stop_frame is not frame and step_cmd == 108 and \ arg[0] in (StopIteration, GeneratorExit) and arg[2] is None: info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107 info.pydev_step_stop = None else: # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break # eventually. Force the step mode to step into and the step stop frame to None. # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user # to make a step in or step over at that location). # Note: this is especially troublesome when we're skipping code with the # @DontTrace comment. if stop_frame is frame and is_return and step_cmd in (109, 108): # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 if not frame.f_code.co_flags & 0x20: # CO_GENERATOR = 0x20 (inspect.CO_GENERATOR) info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107 info.pydev_step_stop = None breakpoints_for_file = main_debugger.breakpoints.get(filename) can_skip = False if info.pydev_state == 1: # STATE_RUN = 1 #we can skip if: #- we have no stop marked #- we should make a step return/step over and we're not in the current frame # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 can_skip = (step_cmd == -1 and stop_frame is None) \ or (step_cmd in (109, 108) and stop_frame is not frame) if can_skip: if plugin_manager is not None and main_debugger.has_plugin_line_breaks: can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame) # CMD_STEP_OVER = 108 if can_skip and is_return and main_debugger.show_return_values and info.pydev_step_cmd == 108 and frame.f_back is info.pydev_step_stop: # trace function for showing return values after step over can_skip = False # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace # also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, # so, that's why the additional checks are there. if not breakpoints_for_file: if can_skip: if has_exception_breakpoints: return self.trace_exception else: if need_trace_return: return self.trace_return else: return None else: # When cached, 0 means we don't have a breakpoint and 1 means we have. if can_skip: breakpoints_in_line_cache = frame_skips_cache.get(line_cache_key, -1) if breakpoints_in_line_cache == 0: return self.trace_dispatch breakpoints_in_frame_cache = frame_skips_cache.get(frame_cache_key, -1) if breakpoints_in_frame_cache != -1: # Gotten from cache. has_breakpoint_in_frame = breakpoints_in_frame_cache == 1 else: has_breakpoint_in_frame = False # Checks the breakpoint to see if there is a context match in some function curr_func_name = frame.f_code.co_name #global context is set with an empty name if curr_func_name in ('?', '<module>'): curr_func_name = '' for breakpoint in dict_iter_values(breakpoints_for_file): #jython does not support itervalues() #will match either global or some function if breakpoint.func_name in ('None', curr_func_name): has_breakpoint_in_frame = True break # Cache the value (1 or 0 or -1 for default because of cython). if has_breakpoint_in_frame: frame_skips_cache[frame_cache_key] = 1 else: frame_skips_cache[frame_cache_key] = 0 if can_skip and not has_breakpoint_in_frame: if has_exception_breakpoints: return self.trace_exception else: if need_trace_return: return self.trace_return else: return None #We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame # print('NOT skipped', frame.f_lineno, frame.f_code.co_name, event) try: flag = False #return is not taken into account for breakpoint hit because we'd have a double-hit in this case #(one for the line and the other for the return). stop_info = {} breakpoint = None exist_result = False stop = False bp_type = None if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file: breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd == CMD_STEP_OVER and stop_frame is frame and (is_line or is_return): stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later) elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args) if result: exist_result = True flag, breakpoint, new_frame, bp_type = result if breakpoint: #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint # lets do the conditional stuff here if stop or exist_result: condition = breakpoint.condition if condition is not None: eval_result = handle_breakpoint_condition(main_debugger, info, breakpoint, new_frame) if not eval_result: return self.trace_dispatch if breakpoint.expression is not None: handle_breakpoint_expression(breakpoint, info, new_frame) if not main_debugger.first_breakpoint_reached: if is_call: back = frame.f_back if back is not None: # When we start debug session, we call execfile in pydevd run function. It produces an additional # 'call' event for tracing and we stop on the first line of code twice. _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back) if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \ (base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]): stop = False main_debugger.first_breakpoint_reached = True else: # if the frame is traced after breakpoint stop, # but the file should be ignored while stepping because of filters if step_cmd != -1: if main_debugger.is_filter_enabled and main_debugger.is_ignored_by_filters(filename): # ignore files matching stepping filters return self.trace_dispatch if main_debugger.is_filter_libraries and main_debugger.not_in_scope(filename): # ignore library files while stepping return self.trace_dispatch if main_debugger.show_return_values: if is_return and info.pydev_step_cmd == CMD_STEP_OVER and frame.f_back == info.pydev_step_stop: self.show_return_values(frame, arg) elif main_debugger.remove_return_values_flag: try: self.remove_return_values(main_debugger, frame) finally: main_debugger.remove_return_values_flag = False if stop: self.set_suspend(thread, CMD_SET_BREAK) if breakpoint and breakpoint.suspend_policy == "ALL": main_debugger.suspend_all_other_threads(thread) elif flag and plugin_manager is not None: result = plugin_manager.suspend(main_debugger, thread, frame, bp_type) if result: frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.do_wait_suspend(thread, frame, event, arg) return self.trace_dispatch else: if not breakpoint and not is_return: # No stop from anyone and no breakpoint found in line (cache that). frame_skips_cache[line_cache_key] = 0 except: traceback.print_exc() raise #step handling. We stop when we hit the right frame try: should_skip = 0 if pydevd_dont_trace.should_trace_hook is not None: if self.should_skip == -1: # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code # Which will be handled by this frame is read-only, so, we can cache it safely. if not pydevd_dont_trace.should_trace_hook(frame, filename): # -1, 0, 1 to be Cython-friendly should_skip = self.should_skip = 1 else: should_skip = self.should_skip = 0 else: should_skip = self.should_skip plugin_stop = False if should_skip: stop = False elif step_cmd == CMD_STEP_INTO: stop = is_line or is_return if plugin_manager is not None: result = plugin_manager.cmd_step_into(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_STEP_INTO_MY_CODE: if not main_debugger.not_in_scope(frame.f_code.co_filename): stop = is_line elif step_cmd == CMD_STEP_OVER: stop = stop_frame is frame and (is_line or is_return) if frame.f_code.co_flags & CO_GENERATOR: if is_return: stop = False if plugin_manager is not None: result = plugin_manager.cmd_step_over(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_SMART_STEP_INTO: stop = False if info.pydev_smart_step_stop is frame: info.pydev_func_name = '.invalid.' # Must match the type in cython info.pydev_smart_step_stop = None if is_line or is_exception_event: curr_func_name = frame.f_code.co_name #global context is set with an empty name if curr_func_name in ('?', '<module>') or curr_func_name is None: curr_func_name = '' if curr_func_name == info.pydev_func_name: stop = True elif step_cmd == CMD_STEP_RETURN: stop = is_return and stop_frame is frame elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT: try: stop, _, response_msg = main_debugger.set_next_statement(frame, event, info.pydev_func_name, info.pydev_next_line) except ValueError: pass else: stop = False if stop and step_cmd != -1 and is_return and IS_PY3K and hasattr(frame, "f_back"): f_code = getattr(frame.f_back, 'f_code', None) if f_code is not None: back_filename = os.path.basename(f_code.co_filename) file_type = get_file_type(back_filename) if file_type == PYDEV_FILE: stop = False if plugin_stop: stopped_on_plugin = plugin_manager.stop(main_debugger, frame, event, self._args, stop_info, arg, step_cmd) elif stop: if is_line: self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, frame, event, arg) else: #return event back = frame.f_back if back is not None: #When we get to the pydevd run function, the debugging has actually finished for the main thread #(note that it can still go on for other threads, but for this one, we just make it finish) #So, just setting it to None should be OK _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back) if base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]: back = None elif base == TRACE_PROPERTY: # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) #if we're in a return, we want it to appear to the user in the previous frame! return None elif pydevd_dont_trace.should_trace_hook is not None: if not pydevd_dont_trace.should_trace_hook(back, back_filename): # In this case, we'll have to skip the previous one because it shouldn't be traced. # Also, we have to reset the tracing, because if the parent's parent (or some # other parent) has to be traced and it's not currently, we wouldn't stop where # we should anymore (so, a step in/over/return may not stop anywhere if no parent is traced). # Related test: _debugger_case17a.py main_debugger.set_trace_for_frame_and_parents(back, overwrite_prev_trace=True) return None if back is not None: #if we're in a return, we want it to appear to the user in the previous frame! self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, back, event, arg) else: #in jython we may not have a back frame info.pydev_step_stop = None info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN except KeyboardInterrupt: raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: return None #if we are quitting, let's stop the tracing retVal = None if not main_debugger.quitting: retVal = self.trace_dispatch return retVal finally: info.is_tracing = False
def trace_dispatch(self, frame, event, arg): # ENDIF # DEBUG = 'code_to_debug' in frame.f_code.co_filename main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args # if DEBUG: print('frame trace_dispatch %s %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, info.pydev_step_cmd)) try: info.is_tracing = True line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger._finish_debugging_session: return None if event == 'call' else NO_FTRACE plugin_manager = main_debugger.plugin is_exception_event = event == 'exception' has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks if is_exception_event: if has_exception_breakpoints: should_stop, frame = self.should_stop_on_exception(frame, event, arg) if should_stop: self.handle_exception(frame, event, arg) return self.trace_dispatch is_line = False is_return = False is_call = False else: is_line = event == 'line' is_return = event == 'return' is_call = event == 'call' if not is_line and not is_return and not is_call: # Unexpected: just keep the same trace func. return self.trace_dispatch need_signature_trace_return = False if main_debugger.signature_factory is not None: if is_call: need_signature_trace_return = send_signature_call_trace(main_debugger, frame, filename) elif is_return: send_signature_return_trace(main_debugger, frame, filename, arg) stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd if is_exception_event: breakpoints_for_file = None # CMD_STEP_OVER = 108, CMD_STEP_OVER_MY_CODE = 159 if stop_frame and stop_frame is not frame and step_cmd in (108, 159) and \ arg[0] in (StopIteration, GeneratorExit) and arg[2] is None: if step_cmd == CMD_STEP_OVER: info.pydev_step_cmd = CMD_STEP_INTO else: info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE info.pydev_step_stop = None else: # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break # eventually. Force the step mode to step into and the step stop frame to None. # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user # to make a step in or step over at that location). # Note: this is especially troublesome when we're skipping code with the # @DontTrace comment. if stop_frame is frame and is_return and step_cmd in (108, 109, 159, 160): # CMD_STEP_OVER = 108, CMD_STEP_RETURN = 109, CMD_STEP_OVER_MY_CODE = 159, CMD_STEP_RETURN_MY_CODE = 160 if not frame.f_code.co_flags & 0x20: # CO_GENERATOR = 0x20 (inspect.CO_GENERATOR) if step_cmd in (108, 109): info.pydev_step_cmd = CMD_STEP_INTO else: info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE info.pydev_step_stop = None breakpoints_for_file = main_debugger.breakpoints.get(filename) can_skip = False if info.pydev_state == 1: # STATE_RUN = 1 # we can skip if: # - we have no stop marked # - we should make a step return/step over and we're not in the current frame # CMD_STEP_OVER = 108, CMD_STEP_RETURN = 109, CMD_STEP_OVER_MY_CODE = 159, CMD_STEP_RETURN_MY_CODE = 160 can_skip = (step_cmd == -1 and stop_frame is None) \ or (step_cmd in (108, 109, 159, 160) and stop_frame is not frame) if can_skip: if plugin_manager is not None and ( main_debugger.has_plugin_line_breaks or main_debugger.has_plugin_exception_breaks): can_skip = plugin_manager.can_skip(main_debugger, frame) # CMD_STEP_OVER = 108, CMD_STEP_OVER_MY_CODE = 159 if can_skip and main_debugger.show_return_values and info.pydev_step_cmd in (108, 159) and frame.f_back is info.pydev_step_stop: # trace function for showing return values after step over can_skip = False # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace # also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, # so, that's why the additional checks are there. if not breakpoints_for_file: if can_skip: if has_exception_breakpoints: return self.trace_exception else: if need_signature_trace_return: return self.trace_return else: return None if is_call else NO_FTRACE else: # When cached, 0 means we don't have a breakpoint and 1 means we have. if can_skip: breakpoints_in_line_cache = frame_skips_cache.get(line_cache_key, -1) if breakpoints_in_line_cache == 0: return self.trace_dispatch breakpoints_in_frame_cache = frame_skips_cache.get(frame_cache_key, -1) if breakpoints_in_frame_cache != -1: # Gotten from cache. has_breakpoint_in_frame = breakpoints_in_frame_cache == 1 else: has_breakpoint_in_frame = False # Checks the breakpoint to see if there is a context match in some function curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>', '<lambda>'): curr_func_name = '' for breakpoint in dict_iter_values(breakpoints_for_file): # jython does not support itervalues() # will match either global or some function if breakpoint.func_name in ('None', curr_func_name): has_breakpoint_in_frame = True break # Cache the value (1 or 0 or -1 for default because of cython). if has_breakpoint_in_frame: frame_skips_cache[frame_cache_key] = 1 else: frame_skips_cache[frame_cache_key] = 0 if can_skip and not has_breakpoint_in_frame: if has_exception_breakpoints: return self.trace_exception else: if need_signature_trace_return: return self.trace_return else: return None if is_call else NO_FTRACE # We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame # if DEBUG: print('NOT skipped: %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, event, frame.__class__.__name__)) try: flag = False # return is not taken into account for breakpoint hit because we'd have a double-hit in this case # (one for the line and the other for the return). stop_info = {} breakpoint = None exist_result = False stop = False bp_type = None if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file: breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd in (CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE) and (stop_frame is frame and is_line): stop = False # we don't stop on breakpoint if we have to stop by step-over (it will be processed later) elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args) if result: exist_result = True flag, breakpoint, new_frame, bp_type = result if breakpoint: # ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint # lets do the conditional stuff here if stop or exist_result: eval_result = False if breakpoint.has_condition: eval_result = main_debugger.handle_breakpoint_condition(info, breakpoint, new_frame) if breakpoint.expression is not None: main_debugger.handle_breakpoint_expression(breakpoint, info, new_frame) if breakpoint.is_logpoint and info.pydev_message is not None and len(info.pydev_message) > 0: cmd = main_debugger.cmd_factory.make_io_message(info.pydev_message + os.linesep, '1') main_debugger.writer.add_command(cmd) if breakpoint.has_condition: if not eval_result: return self.trace_dispatch elif breakpoint.is_logpoint: return self.trace_dispatch if is_call and frame.f_code.co_name in ('<module>', '<lambda>'): # If we find a call for a module, it means that the module is being imported/executed for the # first time. In this case we have to ignore this hit as it may later duplicated by a # line event at the same place (so, if there's a module with a print() in the first line # the user will hit that line twice, which is not what we want). # # As for lambda, as it only has a single statement, it's not interesting to trace # its call and later its line event as they're usually in the same line. return self.trace_dispatch if main_debugger.show_return_values: if is_return and info.pydev_step_cmd in (CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE) and frame.f_back == info.pydev_step_stop: self.show_return_values(frame, arg) elif main_debugger.remove_return_values_flag: try: self.remove_return_values(main_debugger, frame) finally: main_debugger.remove_return_values_flag = False if stop: self.set_suspend( thread, CMD_SET_BREAK, suspend_other_threads=breakpoint and breakpoint.suspend_policy == "ALL", ) elif flag and plugin_manager is not None: result = plugin_manager.suspend(main_debugger, thread, frame, bp_type) if result: frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.do_wait_suspend(thread, frame, event, arg) return self.trace_dispatch else: if not breakpoint and is_line: # No stop from anyone and no breakpoint found in line (cache that). frame_skips_cache[line_cache_key] = 0 except: traceback.print_exc() raise # step handling. We stop when we hit the right frame try: should_skip = 0 if pydevd_dont_trace.should_trace_hook is not None: if self.should_skip == -1: # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code # Which will be handled by this frame is read-only, so, we can cache it safely. if not pydevd_dont_trace.should_trace_hook(frame, filename): # -1, 0, 1 to be Cython-friendly should_skip = self.should_skip = 1 else: should_skip = self.should_skip = 0 else: should_skip = self.should_skip plugin_stop = False if should_skip: stop = False elif step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE): force_check_project_scope = step_cmd == CMD_STEP_INTO_MY_CODE if is_line: if force_check_project_scope or main_debugger.is_files_filter_enabled: stop = not main_debugger.apply_files_filter(frame, frame.f_code.co_filename, force_check_project_scope) else: stop = True elif is_return and frame.f_back is not None: if main_debugger.get_file_type( get_abs_path_real_path_and_base_from_frame(frame.f_back)) == main_debugger.PYDEV_FILE: stop = False else: if force_check_project_scope or main_debugger.is_files_filter_enabled: stop = not main_debugger.apply_files_filter(frame.f_back, frame.f_back.f_code.co_filename, force_check_project_scope) else: stop = True if plugin_manager is not None: result = plugin_manager.cmd_step_into(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd in (CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE): # Note: when dealing with a step over my code it's the same as a step over (the # difference is that when we return from a frame in one we go to regular step # into and in the other we go to a step into my code). stop = stop_frame is frame and is_line # Note: don't stop on a return for step over, only for line events # i.e.: don't stop in: (stop_frame is frame.f_back and is_return) as we'd stop twice in that line. if frame.f_code.co_flags & CO_GENERATOR: if is_return: stop = False if plugin_manager is not None: result = plugin_manager.cmd_step_over(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_SMART_STEP_INTO: stop = False if info.pydev_smart_step_stop is frame: info.pydev_func_name = '.invalid.' # Must match the type in cython info.pydev_smart_step_stop = None if is_line or is_exception_event: curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>') or curr_func_name is None: curr_func_name = '' if curr_func_name == info.pydev_func_name: stop = True elif step_cmd in (CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE): stop = is_return and stop_frame is frame else: stop = False if stop and step_cmd != -1 and is_return and IS_PY3K and hasattr(frame, "f_back"): f_code = getattr(frame.f_back, 'f_code', None) if f_code is not None: if main_debugger.get_file_type( get_abs_path_real_path_and_base_from_file(f_code.co_filename)) == main_debugger.PYDEV_FILE: stop = False if plugin_stop: stopped_on_plugin = plugin_manager.stop(main_debugger, frame, event, self._args, stop_info, arg, step_cmd) elif stop: if is_line: self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, frame, event, arg) else: # return event back = frame.f_back if back is not None: # When we get to the pydevd run function, the debugging has actually finished for the main thread # (note that it can still go on for other threads, but for this one, we just make it finish) # So, just setting it to None should be OK _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back) if (base, back.f_code.co_name) in (DEBUG_START, DEBUG_START_PY3K): back = None elif base == TRACE_PROPERTY: # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) # if we're in a return, we want it to appear to the user in the previous frame! return None if is_call else NO_FTRACE elif pydevd_dont_trace.should_trace_hook is not None: if not pydevd_dont_trace.should_trace_hook(back, back_filename): # In this case, we'll have to skip the previous one because it shouldn't be traced. # Also, we have to reset the tracing, because if the parent's parent (or some # other parent) has to be traced and it's not currently, we wouldn't stop where # we should anymore (so, a step in/over/return may not stop anywhere if no parent is traced). # Related test: _debugger_case17a.py main_debugger.set_trace_for_frame_and_parents(back) return None if is_call else NO_FTRACE if back is not None: # if we're in a return, we want it to appear to the user in the previous frame! self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, back, event, arg) else: # in jython we may not have a back frame info.pydev_step_stop = None info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN except KeyboardInterrupt: raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: return None if is_call else NO_FTRACE # if we are quitting, let's stop the tracing if not main_debugger.quitting: return self.trace_dispatch else: return None if is_call else NO_FTRACE finally: info.is_tracing = False
def trace_dispatch(self, frame, event, arg): # ENDIF main_debugger, filename, info, thread = self._args try: # print 'frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event info.is_tracing = True if main_debugger._finish_debugging_session: return None if event == "call" and main_debugger.signature_factory: send_signature_call_trace(main_debugger, frame, filename) plugin_manager = main_debugger.plugin is_exception_event = event == "exception" has_exception_breakpoints = ( main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks ) if is_exception_event: if has_exception_breakpoints: flag, frame = self.should_stop_on_exception(frame, event, arg) if flag: self.handle_exception(frame, event, arg) return self.trace_dispatch elif event not in ("line", "call", "return"): # I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace. return None stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd if is_exception_event: breakpoints_for_file = None if ( stop_frame and stop_frame is not frame and step_cmd == CMD_STEP_OVER and arg[0] in (StopIteration, GeneratorExit) and arg[2] is None ): info.pydev_step_cmd = CMD_STEP_INTO info.pydev_step_stop = None else: # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break # eventually. Force the step mode to step into and the step stop frame to None. # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user # to make a step in or step over at that location). # Note: this is especially troublesome when we're skipping code with the # @DontTrace comment. if stop_frame is frame and event == "return" and step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER): if not frame.f_code.co_flags & CO_GENERATOR: info.pydev_step_cmd = CMD_STEP_INTO info.pydev_step_stop = None breakpoints_for_file = main_debugger.breakpoints.get(filename) can_skip = False if info.pydev_state == STATE_RUN: # we can skip if: # - we have no stop marked # - we should make a step return/step over and we're not in the current frame can_skip = (step_cmd == -1 and stop_frame is None) or ( step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame ) if can_skip and plugin_manager is not None and main_debugger.has_plugin_line_breaks: can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame) if can_skip and main_debugger.show_return_values: # trace function for showing return values after step over if ( info.pydev_step_cmd == CMD_STEP_OVER and hasattr(frame, "f_back") and frame.f_back == info.pydev_step_stop ): can_skip = False # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace # also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, # so, that's why the additional checks are there. if not breakpoints_for_file: if can_skip: if has_exception_breakpoints: return self.trace_exception else: return None else: # checks the breakpoint to see if there is a context match in some function curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ("?", "<module>"): curr_func_name = "" for breakpoint in dict_iter_values(breakpoints_for_file): # jython does not support itervalues() # will match either global or some function if breakpoint.func_name in ("None", curr_func_name): break else: # if we had some break, it won't get here (so, that's a context that we want to skip) if can_skip: if has_exception_breakpoints: return self.trace_exception else: return None # We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame # print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event try: line = frame.f_lineno flag = False # return is not taken into account for breakpoint hit because we'd have a double-hit in this case # (one for the line and the other for the return). stop_info = {} breakpoint = None exist_result = False stop = False bp_type = None if ( not flag and event != "return" and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and dict_contains(breakpoints_for_file, line) ): breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ("line", "return"): stop = ( False ) # we don't stop on breakpoint if we have to stop by step-over (it will be processed later) elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args) if result: exist_result = True (flag, breakpoint, new_frame, bp_type) = result if breakpoint: # ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint # lets do the conditional stuff here if stop or exist_result: condition = breakpoint.condition if condition is not None: try: val = eval(condition, new_frame.f_globals, new_frame.f_locals) if not val: return self.trace_dispatch except: if type(condition) != type(""): if hasattr(condition, "encode"): condition = condition.encode("utf-8") msg = "Error while evaluating expression: %s\n" % (condition,) sys.stderr.write(msg) traceback.print_exc() if not main_debugger.suspend_on_breakpoint_exception: return self.trace_dispatch else: stop = True try: # add exception_type and stacktrace into thread additional info etype, value, tb = sys.exc_info() try: error = "".join(traceback.format_exception_only(etype, value)) stack = traceback.extract_stack(f=tb.tb_frame.f_back) # On self.set_suspend(thread, CMD_SET_BREAK) this info will be # sent to the client. info.conditional_breakpoint_exception = ( "Condition:\n" + condition + "\n\nError:\n" + error, stack, ) finally: etype, value, tb = None, None, None except: traceback.print_exc() if breakpoint.expression is not None: try: try: val = eval(breakpoint.expression, new_frame.f_globals, new_frame.f_locals) except: val = sys.exc_info()[1] finally: if val is not None: info.pydev_message = str(val) if not main_debugger.first_breakpoint_reached: if event == "call": if hasattr(frame, "f_back"): back = frame.f_back if back is not None: # When we start debug session, we call execfile in pydevd run function. It produces an additional # 'call' event for tracing and we stop on the first line of code twice. _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back) if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or ( base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1] ): stop = False main_debugger.first_breakpoint_reached = True else: # if the frame is traced after breakpoint stop, # but the file should be ignored while stepping because of filters if step_cmd != -1: if main_debugger.is_filter_enabled and main_debugger.is_ignored_by_filters(filename): # ignore files matching stepping filters return self.trace_dispatch if main_debugger.is_filter_libraries and main_debugger.not_in_scope(filename): # ignore library files while stepping return self.trace_dispatch if main_debugger.show_return_values or main_debugger.remove_return_values_flag: self.manage_return_values(main_debugger, frame, event, arg) if stop: self.set_suspend(thread, CMD_SET_BREAK) if breakpoint and breakpoint.suspend_policy == "ALL": main_debugger.suspend_all_other_threads(thread) elif flag and plugin_manager is not None: result = plugin_manager.suspend(main_debugger, thread, frame, bp_type) if result: frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.do_wait_suspend(thread, frame, event, arg) return self.trace_dispatch except: traceback.print_exc() raise # step handling. We stop when we hit the right frame try: should_skip = 0 if pydevd_dont_trace.should_trace_hook is not None: if self.should_skip == -1: # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code # Which will be handled by this frame is read-only, so, we can cache it safely. if not pydevd_dont_trace.should_trace_hook(frame, filename): # -1, 0, 1 to be Cython-friendly should_skip = self.should_skip = 1 else: should_skip = self.should_skip = 0 else: should_skip = self.should_skip plugin_stop = False if should_skip: stop = False elif step_cmd == CMD_STEP_INTO: stop = event in ("line", "return") if plugin_manager is not None: result = plugin_manager.cmd_step_into(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_STEP_INTO_MY_CODE: if not main_debugger.not_in_scope(frame.f_code.co_filename): stop = event == "line" elif step_cmd == CMD_STEP_OVER: stop = stop_frame is frame and event in ("line", "return") if frame.f_code.co_flags & CO_GENERATOR: if event == "return": stop = False if plugin_manager is not None: result = plugin_manager.cmd_step_over(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_SMART_STEP_INTO: stop = False if info.pydev_smart_step_stop is frame: info.pydev_func_name = ".invalid." # Must match the type in cython info.pydev_smart_step_stop = None if event == "line" or event == "exception": curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ("?", "<module>") or curr_func_name is None: curr_func_name = "" if curr_func_name == info.pydev_func_name: stop = True elif step_cmd == CMD_STEP_RETURN: stop = event == "return" and stop_frame is frame elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT: stop = False if event == "line" or event == "exception": # Yes, we can only act on line events (weird hum?) # Note: This code is duplicated at pydevd.py # Acting on exception events after debugger breaks with exception curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ("?", "<module>"): curr_func_name = "" if curr_func_name == info.pydev_func_name: line = info.pydev_next_line if frame.f_lineno == line: stop = True else: if frame.f_trace is None: frame.f_trace = self.trace_dispatch frame.f_lineno = line frame.f_trace = None stop = True else: stop = False if stop and step_cmd != -1 and IS_PY3K: # in Py3k we start script via our custom `execfile` function, and we shouldn't stop there # while stepping when execution is finished if event == "return" and hasattr(frame, "f_back") and hasattr(frame.f_back, "f_code"): back_filename = os.path.basename(frame.f_back.f_code.co_filename) file_type = get_file_type(back_filename) if file_type == PYDEV_FILE: stop = False if plugin_stop: stopped_on_plugin = plugin_manager.stop( main_debugger, frame, event, self._args, stop_info, arg, step_cmd ) elif stop: if event == "line": self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, frame, event, arg) else: # return event back = frame.f_back if back is not None: # When we get to the pydevd run function, the debugging has actually finished for the main thread # (note that it can still go on for other threads, but for this one, we just make it finish) # So, just setting it to None should be OK _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back) if base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]: back = None elif base == TRACE_PROPERTY: # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) # if we're in a return, we want it to appear to the user in the previous frame! return None elif pydevd_dont_trace.should_trace_hook is not None: if not pydevd_dont_trace.should_trace_hook(back, back_filename): # In this case, we'll have to skip the previous one because it shouldn't be traced. # Also, we have to reset the tracing, because if the parent's parent (or some # other parent) has to be traced and it's not currently, we wouldn't stop where # we should anymore (so, a step in/over/return may not stop anywhere if no parent is traced). # Related test: _debugger_case17a.py main_debugger.set_trace_for_frame_and_parents(back, overwrite_prev_trace=True) return None if back is not None: # if we're in a return, we want it to appear to the user in the previous frame! self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, back, event, arg) else: # in jython we may not have a back frame info.pydev_step_stop = None info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN except KeyboardInterrupt: raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: return None # if we are quitting, let's stop the tracing retVal = None if not main_debugger.quitting: retVal = self.trace_dispatch return retVal finally: info.is_tracing = False
def __call__(self, frame, event, arg): ''' This is the callback used when we enter some context in the debugger. We also decorate the thread we are in with info about the debugging. The attributes added are: pydev_state pydev_step_stop pydev_step_cmd pydev_notify_kill :param PyDB py_db: This is the global debugger (this method should actually be added as a method to it). ''' # IFDEF CYTHON # cdef str filename; # cdef str base; # cdef int pydev_step_cmd; # cdef tuple cache_key; # cdef dict cache_skips; # cdef bint is_stepping; # cdef tuple abs_path_real_path_and_base; # cdef PyDBAdditionalThreadInfo additional_info; # ENDIF # print('ENTER: trace_dispatch', frame.f_code.co_filename, frame.f_lineno, event, frame.f_code.co_name) py_db, t, additional_info, cache_skips, frame_skips_cache = self._args pydev_step_cmd = additional_info.pydev_step_cmd is_stepping = pydev_step_cmd != -1 try: if py_db._finish_debugging_session: if not py_db._termination_event_set: #that was not working very well because jython gave some socket errors try: if py_db.output_checker is None: kill_all_pydev_threads() except: traceback.print_exc() py_db._termination_event_set = True return None # if thread is not alive, cancel trace_dispatch processing if not is_thread_alive(t): py_db._process_thread_not_alive(get_thread_id(t)) return None # suspend tracing try: # Make fast path faster! abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename] except: abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame) if py_db.thread_analyser is not None: py_db.thread_analyser.log_event(frame) if py_db.asyncio_analyser is not None: py_db.asyncio_analyser.log_event(frame) filename = abs_path_real_path_and_base[1] # Note: it's important that the context name is also given because we may hit something once # in the global context and another in the local context. cache_key = (frame.f_lineno, frame.f_code.co_name, filename) if not is_stepping and cache_key in cache_skips: # print('skipped: trace_dispatch (cache hit)', cache_key, frame.f_lineno, event, frame.f_code.co_name) return None file_type = get_file_type(abs_path_real_path_and_base[-1]) #we don't want to debug threading or anything related to pydevd if file_type is not None: if file_type == 1: # inlining LIB_FILE = 1 if py_db.not_in_scope(filename): # print('skipped: trace_dispatch (not in scope)', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type) cache_skips[cache_key] = 1 return None else: # print('skipped: trace_dispatch', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type) cache_skips[cache_key] = 1 return None if is_stepping: if py_db.is_filter_enabled and py_db.is_ignored_by_filters(filename): # ignore files matching stepping filters return None if py_db.is_filter_libraries and py_db.not_in_scope(filename): # ignore library files while stepping return None # print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type) if additional_info.is_tracing: return None #we don't wan't to trace code invoked from pydevd_frame.trace_dispatch if event == 'call' and py_db.signature_factory: # We can only have a call when entering a context, so, check at this level, not at the PyDBFrame. send_signature_call_trace(py_db, frame, filename) # Just create PyDBFrame directly (removed support for Python versions < 2.5, which required keeping a weak # reference to the frame). ret = PyDBFrame((py_db, filename, additional_info, t, frame_skips_cache, (frame.f_code.co_name, frame.f_code.co_firstlineno, filename))).trace_dispatch(frame, event, arg) if ret is None: cache_skips[cache_key] = 1 return None # IFDEF CYTHON # return SafeCallWrapper(ret) # ELSE return ret # ENDIF except SystemExit: return None except Exception: if py_db._finish_debugging_session: return None # Don't log errors when we're shutting down. # Log it try: if traceback is not None: # This can actually happen during the interpreter shutdown in Python 2.7 traceback.print_exc() except: # Error logging? We're really in the interpreter shutdown... # (https://github.com/fabioz/PyDev.Debugger/issues/8) pass return None