def _pydev_stop_at_break(): frame = sys._getframe(1) t = threading.currentThread() if t.additional_info.is_tracing: return False if t.additional_info.pydev_step_cmd == -1 and frame.f_trace in (None, dummy_tracing_holder.dummy_trace_func): # do not handle breakpoints while stepping, because they're handled by old tracing function t.additional_info.is_tracing = True debugger = get_global_debugger() try: 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) filename = abs_path_real_path_and_base[1] breakpoints_for_file = debugger.breakpoints.get(filename) line = _get_line_for_frame(frame) try: breakpoint = breakpoints_for_file[line] except KeyError: pydev_log.debug("Couldn't find breakpoint in the file {} on line {}".format(frame.f_code.co_filename, line)) t.additional_info.is_tracing = False return False if breakpoint and handle_breakpoint(frame, t, debugger, breakpoint): pydev_log.debug("Suspending at breakpoint in file: {} on line {}".format(frame.f_code.co_filename, line)) debugger.set_suspend(t, CMD_SET_BREAK) debugger.do_wait_suspend(t, frame, 'line', None, "frame_eval") t.additional_info.is_tracing = False return t.additional_info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT return False
def break_into_debugger(): """If a remote debugger is attached, pauses execution of all threads, and breaks into the debugger with current thread as active. """ ptvsd.log.info('break_into_debugger()') if not is_attached(): ptvsd.log.info('break_into_debugger() ignored - debugger not attached') return # Get the first frame in the stack that's not an internal frame. global_debugger = get_global_debugger() stop_at_frame = sys._getframe().f_back while stop_at_frame is not None and global_debugger.get_file_type( get_abs_path_real_path_and_base_from_frame(stop_at_frame)) == global_debugger.PYDEV_FILE: stop_at_frame = stop_at_frame.f_back _pydevd_settrace( suspend=True, trace_only_current_thread=True, patch_multiprocessing=False, stop_at_frame=stop_at_frame, ) stop_at_frame = None
def _schedule_callback(prev, next): ''' Called when a context is stopped or a new context is made runnable. ''' try: if not prev and not next: return if next: register_tasklet_info(next) # Ok, making next runnable: set the tracing facility in it. debugger = get_global_debugger() if debugger is not None and next.frame: if hasattr(next.frame, 'f_trace'): next.frame.f_trace = debugger.trace_dispatch debugger = None if prev: register_tasklet_info(prev) try: for tasklet_ref, tasklet_info in dict_items(_weak_tasklet_registered_to_info): # Make sure it's a copy! tasklet = tasklet_ref() if tasklet is None or not tasklet.alive: # Garbage-collected already! try: del _weak_tasklet_registered_to_info[tasklet_ref] except KeyError: pass if tasklet_info.frame_id is not None: remove_custom_frame(tasklet_info.frame_id) else: if tasklet.paused or tasklet.blocked or tasklet.scheduled: if tasklet.frame and tasklet.frame.f_back: f_back = tasklet.frame.f_back base = get_abs_path_real_path_and_base_from_frame(f_back)[-1] is_file_to_ignore = dict_contains(DONT_TRACE, base) if not is_file_to_ignore: if tasklet_info.frame_id is None: tasklet_info.frame_id = add_custom_frame(f_back, tasklet_info.tasklet_name, tasklet.thread_id) else: update_custom_frame(tasklet_info.frame_id, f_back, tasklet.thread_id) elif tasklet.is_current: if tasklet_info.frame_id is not None: # Remove info about stackless suspended when it starts to run. remove_custom_frame(tasklet_info.frame_id) tasklet_info.frame_id = None finally: tasklet = None tasklet_info = None f_back = None except: import traceback;traceback.print_exc() if _application_set_schedule_callback is not None: return _application_set_schedule_callback(prev, next)
def add_custom_frame(frame, name, thread_id): ''' It's possible to show paused frames by adding a custom frame through this API (it's intended to be used for coroutines, but could potentially be used for generators too). :param frame: The topmost frame to be shown paused when a thread with thread.ident == thread_id is paused. :param name: The name to be shown for the custom thread in the UI. :param thread_id: The thread id to which this frame is related (must match thread.ident). :return: str Returns the custom thread id which will be used to show the given frame paused. ''' with CustomFramesContainer.custom_frames_lock: curr_thread_id = get_current_thread_id(threading.currentThread()) next_id = CustomFramesContainer._next_frame_id = CustomFramesContainer._next_frame_id + 1 # Note: the frame id kept contains an id and thread information on the thread where the frame was added # so that later on we can check if the frame is from the current thread by doing frame_id.endswith('|'+thread_id). frame_custom_thread_id = '__frame__:%s|%s' % (next_id, curr_thread_id) if DEBUG: sys.stderr.write('add_custom_frame: %s (%s) %s %s\n' % ( frame_custom_thread_id, get_abs_path_real_path_and_base_from_frame(frame)[-1], frame.f_lineno, frame.f_code.co_name)) CustomFramesContainer.custom_frames[frame_custom_thread_id] = CustomFrame(name, frame, thread_id) CustomFramesContainer._py_db_command_thread_event.set() return frame_custom_thread_id
def make_thread_stack_str(self, frame, frame_id_to_lineno=None): ''' :param frame_id_to_lineno: If available, the line number for the frame will be gotten from this dict, otherwise frame.f_lineno will be used (needed for unhandled exceptions as the place where we report may be different from the place where it's raised). ''' if frame_id_to_lineno is None: frame_id_to_lineno = {} make_valid_xml_value = pydevd_xml.make_valid_xml_value cmd_text_list = [] append = cmd_text_list.append curr_frame = frame frame = None # Clear frame reference try: py_db = get_global_debugger() while curr_frame: frame_id = id(curr_frame) if curr_frame.f_code is None: break # Iron Python sometimes does not have it! method_name = curr_frame.f_code.co_name # method name (if in method) or ? if global if method_name is None: break # Iron Python sometimes does not have it! abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(curr_frame) if py_db.get_file_type(abs_path_real_path_and_base) == py_db.PYDEV_FILE: # Skip pydevd files. curr_frame = curr_frame.f_back continue filename_in_utf8 = pydevd_file_utils.norm_file_to_client(abs_path_real_path_and_base[0]) if not filesystem_encoding_is_utf8 and hasattr(filename_in_utf8, "decode"): # filename_in_utf8 is a byte string encoded using the file system encoding # convert it to utf8 filename_in_utf8 = filename_in_utf8.decode(file_system_encoding).encode("utf-8") # print("file is ", filename_in_utf8) lineno = frame_id_to_lineno.get(frame_id, curr_frame.f_lineno) # print("line is ", lineno) # Note: variables are all gotten 'on-demand'. append('<frame id="%s" name="%s" ' % (frame_id , make_valid_xml_value(method_name))) append('file="%s" line="%s">' % (quote(make_valid_xml_value(filename_in_utf8), '/>_= \t'), lineno)) append("</frame>") curr_frame = curr_frame.f_back except: traceback.print_exc() curr_frame = None # Clear frame reference return ''.join(cmd_text_list)
def get_text_list_for_frame(frame): # partial copy-paste from make_thread_suspend_str curFrame = frame cmdTextList = [] try: while curFrame: #print cmdText myId = str(id(curFrame)) #print "id is ", myId if curFrame.f_code is None: break #Iron Python sometimes does not have it! myName = curFrame.f_code.co_name #method name (if in method) or ? if global if myName is None: break #Iron Python sometimes does not have it! #print "name is ", myName filename = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(curFrame)[1] myFile = pydevd_file_utils.norm_file_to_client(filename) if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"): # myFile is a byte string encoded using the file system encoding # convert it to utf8 myFile = myFile.decode(file_system_encoding).encode("utf-8") #print "file is ", myFile #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame) myLine = str(curFrame.f_lineno) #print "line is ", myLine #the variables are all gotten 'on-demand' #variables = pydevd_xml.frame_vars_to_xml(curFrame.f_locals) variables = '' cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_xml.make_valid_xml_value(myName))) cmdTextList.append('file="%s" line="%s">' % (quote(myFile, '/>_= \t'), myLine)) cmdTextList.append(variables) cmdTextList.append("</frame>") curFrame = curFrame.f_back except : traceback.print_exc() return cmdTextList
def add_custom_frame(frame, name, thread_id): CustomFramesContainer.custom_frames_lock.acquire() try: curr_thread_id = get_current_thread_id(threading.currentThread()) next_id = CustomFramesContainer._next_frame_id = CustomFramesContainer._next_frame_id + 1 # Note: the frame id kept contains an id and thread information on the thread where the frame was added # so that later on we can check if the frame is from the current thread by doing frame_id.endswith('|'+thread_id). frame_id = '__frame__:%s|%s' % (next_id, curr_thread_id) if DEBUG: sys.stderr.write('add_custom_frame: %s (%s) %s %s\n' % ( frame_id, get_abs_path_real_path_and_base_from_frame(frame)[-1], frame.f_lineno, frame.f_code.co_name)) CustomFramesContainer.custom_frames[frame_id] = CustomFrame(name, frame, thread_id) CustomFramesContainer._py_db_command_thread_event.set() return frame_id finally: CustomFramesContainer.custom_frames_lock.release()
def get_text_list_for_frame(frame): # partial copy-paste from make_thread_suspend_str curFrame = frame cmdTextList = [] try: while curFrame: # print cmdText myId = str(id(curFrame)) # print "id is ", myId if curFrame.f_code is None: break # Iron Python sometimes does not have it! myName = curFrame.f_code.co_name # method name (if in method) or ? if global if myName is None: break # Iron Python sometimes does not have it! # print "name is ", myName filename = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(curFrame)[1] myFile = pydevd_file_utils.norm_file_to_client(filename) # print "file is ", myFile # myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame) myLine = str(curFrame.f_lineno) # print "line is ", myLine # the variables are all gotten 'on-demand' # variables = pydevd_xml.frame_vars_to_xml(curFrame.f_locals) variables = '' cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_xml.make_valid_xml_value(myName))) cmdTextList.append('file="%s" line="%s">' % (quote(myFile, '/>_= \t'), myLine)) cmdTextList.append(variables) cmdTextList.append("</frame>") curFrame = curFrame.f_back except : pydev_log.exception() return cmdTextList
def _pydev_stop_at_break(line): frame = sys._getframe(1) t = threading.currentThread() if t.additional_info.is_tracing: return False t.additional_info.is_tracing = True try: debugger = get_global_debugger() try: 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) filename = abs_path_real_path_and_base[1] try: python_breakpoint = debugger.breakpoints[filename][line] except: # print("Couldn't find breakpoint in the file %s on line %s" % (frame.f_code.co_filename, line)) # Could be KeyError if line is not there or TypeError if breakpoints_for_file is None. # Note: using catch-all exception for performance reasons (if the user adds a breakpoint # and then removes it after hitting it once, this method added for the programmatic # breakpoint will keep on being called and one of those exceptions will always be raised # here). return if python_breakpoint: pydev_log.debug("Suspending at breakpoint in file: {} on line {}".format(frame.f_code.co_filename, line)) t.additional_info.trace_suspend_type = 'frame_eval' pydevd_frame_eval_cython_wrapper = sys.modules['_pydevd_frame_eval.pydevd_frame_eval_cython_wrapper'] thread_info = pydevd_frame_eval_cython_wrapper.get_thread_info_py() if thread_info.thread_trace_func is not None: frame.f_trace = thread_info.thread_trace_func else: debugger = get_global_debugger() frame.f_trace = debugger.get_thread_local_trace_func() finally: t.additional_info.is_tracing = False
def _iter_visible_frames_info(self, py_db, frame, frame_id_to_lineno): while frame is not None: if frame.f_code is None: continue # IronPython sometimes does not have it! method_name = frame.f_code.co_name # method name (if in method) or ? if global if method_name is None: continue # IronPython sometimes does not have it! abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame) if py_db.get_file_type(abs_path_real_path_and_base) == py_db.PYDEV_FILE: # Skip pydevd files. frame = frame.f_back continue filename_in_utf8 = pydevd_file_utils.norm_file_to_client(abs_path_real_path_and_base[0]) frame_id = id(frame) lineno = frame_id_to_lineno.get(frame_id, frame.f_lineno) yield frame_id, frame, method_name, filename_in_utf8, lineno frame = frame.f_back
def log_event(self, frame): write_log = False self_obj = None if "self" in frame.f_locals: self_obj = frame.f_locals["self"] if isinstance( self_obj, threading.Thread) or self_obj.__class__ == ObjectWrapper: write_log = True if hasattr(frame, "f_back") and frame.f_back is not None: back = frame.f_back if hasattr(back, "f_back") and back.f_back is not None: back = back.f_back if "self" in back.f_locals: if isinstance(back.f_locals["self"], threading.Thread): write_log = True try: if write_log: t = threadingCurrentThread() back = frame.f_back if not back: return _, name, back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame( back) event_time = cur_time() - self.start_time method_name = frame.f_code.co_name if isinstance(self_obj, threading.Thread): if not hasattr(self_obj, "_pydev_run_patched"): wrap_attr(self_obj, "run") if (method_name in THREAD_METHODS) and (back_base not in DONT_TRACE_THREADING or \ (method_name in INNER_METHODS and back_base in INNER_FILES)): thread_id = get_thread_id(self_obj) name = self_obj.getName() real_method = frame.f_code.co_name parent = None if real_method == "_stop": if back_base in INNER_FILES and \ back.f_code.co_name == "_wait_for_tstate_lock": back = back.f_back.f_back real_method = "stop" if hasattr(self_obj, "_pydev_join_called"): parent = get_thread_id(t) elif real_method == "join": # join called in the current thread, not in self object if not self_obj.is_alive(): return thread_id = get_thread_id(t) name = t.getName() self_obj._pydev_join_called = True if real_method == "start": parent = get_thread_id(t) send_message("threading_event", event_time, name, thread_id, "thread", real_method, back.f_code.co_filename, back.f_lineno, back, parent=parent) # print(event_time, self_obj.getName(), thread_id, "thread", # real_method, back.f_code.co_filename, back.f_lineno) if method_name == "pydev_after_run_call": if hasattr(frame, "f_back") and frame.f_back is not None: back = frame.f_back if hasattr(back, "f_back") and back.f_back is not None: back = back.f_back if "self" in back.f_locals: if isinstance(back.f_locals["self"], threading.Thread): my_self_obj = frame.f_back.f_back.f_locals[ "self"] my_back = frame.f_back.f_back my_thread_id = get_thread_id(my_self_obj) send_massage = True if IS_PY3K and hasattr(my_self_obj, "_pydev_join_called"): send_massage = False # we can't detect stop after join in Python 2 yet if send_massage: send_message("threading_event", event_time, "Thread", my_thread_id, "thread", "stop", my_back.f_code.co_filename, my_back.f_lineno, my_back, parent=None) if self_obj.__class__ == ObjectWrapper: if back_base in DONT_TRACE_THREADING: # do not trace methods called from threading return back_back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame( back.f_back)[-1] back = back.f_back if back_back_base in DONT_TRACE_THREADING: # back_back_base is the file, where the method was called froms return if method_name == "__init__": send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", method_name, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(frame.f_locals["self"]))) if "attr" in frame.f_locals and \ (frame.f_locals["attr"] in LOCK_METHODS or frame.f_locals["attr"] in QUEUE_METHODS): real_method = frame.f_locals["attr"] if method_name == "call_begin": real_method += "_begin" elif method_name == "call_end": real_method += "_end" else: return if real_method == "release_end": # do not log release end. Maybe use it later return send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", real_method, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj))) if real_method in ("put_end", "get_end"): # fake release for queue, cause we don't call it directly send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", "release", back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj))) # print(event_time, t.getName(), get_thread_id(t), "lock", # real_method, back.f_code.co_filename, back.f_lineno) except Exception: traceback.print_exc()
def make_thread_stack_str(self, frame, frame_id_to_lineno=None): ''' :param frame_id_to_lineno: If available, the line number for the frame will be gotten from this dict, otherwise frame.f_lineno will be used (needed for unhandled exceptions as the place where we report may be different from the place where it's raised). ''' if frame_id_to_lineno is None: frame_id_to_lineno = {} make_valid_xml_value = pydevd_xml.make_valid_xml_value cmd_text_list = [] append = cmd_text_list.append curr_frame = frame frame = None # Clear frame reference try: py_db = get_global_debugger() while curr_frame: frame_id = id(curr_frame) if curr_frame.f_code is None: break # Iron Python sometimes does not have it! method_name = curr_frame.f_code.co_name # method name (if in method) or ? if global if method_name is None: break # Iron Python sometimes does not have it! abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame( curr_frame) if py_db.get_file_type( abs_path_real_path_and_base) == py_db.PYDEV_FILE: # Skip pydevd files. curr_frame = curr_frame.f_back continue filename_in_utf8 = pydevd_file_utils.norm_file_to_client( abs_path_real_path_and_base[0]) if not filesystem_encoding_is_utf8 and hasattr( filename_in_utf8, "decode"): # filename_in_utf8 is a byte string encoded using the file system encoding # convert it to utf8 filename_in_utf8 = filename_in_utf8.decode( file_system_encoding).encode("utf-8") # print("file is ", filename_in_utf8) lineno = frame_id_to_lineno.get(frame_id, curr_frame.f_lineno) # print("line is ", lineno) # Note: variables are all gotten 'on-demand'. append('<frame id="%s" name="%s" ' % (frame_id, make_valid_xml_value(method_name))) append('file="%s" line="%s">' % (quote(make_valid_xml_value(filename_in_utf8), '/>_= \t'), lineno)) append("</frame>") curr_frame = curr_frame.f_back except: traceback.print_exc() curr_frame = None # Clear frame reference return ''.join(cmd_text_list)
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 tuple abs_path_real_path_and_base; # cdef PyDBAdditionalThreadInfo additional_info; # ENDIF py_db, t, additional_info = self._args 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) 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(abs_path_real_path_and_base[1]): # print('skipped: trace_dispatch (not in scope)', base, frame.f_lineno, event, frame.f_code.co_name, file_type) return None else: # print('skipped: trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type) return None if additional_info.pydev_step_cmd != -1: if py_db.is_filter_enabled and py_db.is_ignored_by_filters(abs_path_real_path_and_base[1]): # ignore files matching stepping filters return None if py_db.is_filter_libraries and py_db.not_in_scope(abs_path_real_path_and_base[1]): # 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 # each new frame... # IFDEF CYTHON # # Note that on Cython we only support more modern idioms (no support for < Python 2.5) # return PyDBFrame((py_db, abs_path_real_path_and_base[1], additional_info, t)).trace_dispatch(frame, event, arg) # ELSE return additional_info.create_db_frame((py_db, abs_path_real_path_and_base[1], additional_info, t, frame)).trace_dispatch(frame, event, arg) # 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
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 frame_cache_key; # cdef dict cache_skips; # cdef bint is_stepping; # cdef tuple abs_path_real_path_and_base; # cdef PyDBAdditionalThreadInfo additional_info; # ENDIF # DEBUG = 'code_to_debug' in frame.f_code.co_filename # if DEBUG: print('ENTER: trace_dispatch: %s %s %s %s' % (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_thread is None: kill_all_pydev_threads() except: pydev_log_exception() py_db._termination_event_set = True return None if event == 'call' else NO_FTRACE # if thread is not alive, cancel trace_dispatch processing if not is_thread_alive(t): py_db.notify_thread_not_alive(get_current_thread_id(t)) return None if event == 'call' else NO_FTRACE 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) # 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. frame_cache_key = (frame.f_code.co_firstlineno, frame.f_code.co_name, frame.f_code.co_filename) if frame_cache_key in cache_skips: if not is_stepping: # if DEBUG: print('skipped: trace_dispatch (cache hit)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name) return None if event == 'call' else NO_FTRACE else: # When stepping we can't take into account caching based on the breakpoints (only global filtering). if cache_skips.get(frame_cache_key) == 1: if additional_info.pydev_original_step_cmd in ( CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE ) and not _global_notify_skipped_step_in: notify_skipped_step_in_because_of_filters( py_db, frame) back_frame = frame.f_back if back_frame is not None and pydev_step_cmd in ( CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE): back_frame_cache_key = ( back_frame.f_code.co_firstlineno, back_frame.f_code.co_name, back_frame.f_code.co_filename) if cache_skips.get(back_frame_cache_key) == 1: # if DEBUG: print('skipped: trace_dispatch (cache hit: 1)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name) return None if event == 'call' else NO_FTRACE else: # if DEBUG: print('skipped: trace_dispatch (cache hit: 2)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name) return None if event == 'call' else NO_FTRACE 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) filename = abs_path_real_path_and_base[1] file_type = py_db.get_file_type( abs_path_real_path_and_base ) # 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 not py_db.in_project_scope(filename): # if DEBUG: 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[frame_cache_key] = 1 return None if event == 'call' else NO_FTRACE else: # if DEBUG: print('skipped: trace_dispatch', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type) cache_skips[frame_cache_key] = 1 return None if event == 'call' else NO_FTRACE if py_db.is_files_filter_enabled: if py_db.apply_files_filter(frame, filename, False): cache_skips[frame_cache_key] = 1 if is_stepping and additional_info.pydev_original_step_cmd in ( CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE ) and not _global_notify_skipped_step_in: notify_skipped_step_in_because_of_filters(py_db, frame) # A little gotcha, sometimes when we're stepping in we have to stop in a # return event showing the back frame as the current frame, so, we need # to check not only the current frame but the back frame too. back_frame = frame.f_back if back_frame is not None and pydev_step_cmd in ( CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE): if py_db.apply_files_filter( back_frame, back_frame.f_code.co_filename, False): back_frame_cache_key = ( back_frame.f_code.co_firstlineno, back_frame.f_code.co_name, back_frame.f_code.co_filename) cache_skips[back_frame_cache_key] = 1 # if DEBUG: print('skipped: trace_dispatch (filtered out: 1)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name) return None if event == 'call' else NO_FTRACE else: # if DEBUG: print('skipped: trace_dispatch (filtered out: 2)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name) return None if event == 'call' else NO_FTRACE # if DEBUG: print('trace_dispatch', filename, frame.f_lineno, event, frame.f_code.co_name, file_type) if additional_info.is_tracing: return None if event == 'call' else NO_FTRACE # we don't wan't to trace code invoked from pydevd_frame.trace_dispatch # 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_cache_key, )).trace_dispatch(frame, event, arg) if ret is None: # 1 means skipped because of filters. # 2 means skipped because no breakpoints were hit. cache_skips[frame_cache_key] = 2 return None if event == 'call' else NO_FTRACE # IFDEF CYTHON # frame.f_trace = SafeCallWrapper(ret) # Make sure we keep the returned tracer. # ELSE frame.f_trace = ret # Make sure we keep the returned tracer. # ENDIF return ret except SystemExit: return None if event == 'call' else NO_FTRACE except Exception: if py_db._finish_debugging_session: return None if event == 'call' else NO_FTRACE # Don't log errors when we're shutting down. # Log it try: if pydev_log_exception is not None: # This can actually happen during the interpreter shutdown in Python 2.7 pydev_log_exception() except: # Error logging? We're really in the interpreter shutdown... # (https://github.com/fabioz/PyDev.Debugger/issues/8) pass return None if event == 'call' else NO_FTRACE
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 frame_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_thread is None: kill_all_pydev_threads() except: traceback.print_exc() py_db._termination_event_set = True if event != 'call': frame.f_trace = NO_FTRACE return None # if thread is not alive, cancel trace_dispatch processing if not is_thread_alive(t): py_db.notify_thread_not_alive(get_current_thread_id(t)) if event != 'call': frame.f_trace = NO_FTRACE return None # suspend tracing 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) # 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. frame_cache_key = (frame.f_code.co_firstlineno, frame.f_code.co_name, frame.f_code.co_filename) if not is_stepping and frame_cache_key in cache_skips: # print('skipped: trace_dispatch (cache hit)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name) if event != 'call': frame.f_trace = NO_FTRACE return None 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) filename = abs_path_real_path_and_base[1] 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 not py_db.in_project_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[frame_cache_key] = 1 if event != 'call': frame.f_trace = NO_FTRACE 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[frame_cache_key] = 1 if event != 'call': frame.f_trace = NO_FTRACE return None if is_stepping: if py_db.is_filter_enabled and py_db.is_ignored_by_filters(filename): # ignore files matching stepping filters if event != 'call': frame.f_trace = NO_FTRACE return None if py_db.is_filter_libraries and not py_db.in_project_scope(filename): # ignore library files while stepping if event != 'call': frame.f_trace = NO_FTRACE return None # print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type) if additional_info.is_tracing: if event != 'call': frame.f_trace = NO_FTRACE return None # we don't wan't to trace code invoked from pydevd_frame.trace_dispatch # 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_cache_key, ) ).trace_dispatch(frame, event, arg) if ret is None: cache_skips[frame_cache_key] = 1 if event != 'call': frame.f_trace = NO_FTRACE return None # IFDEF CYTHON # ret = SafeCallWrapper(ret) # ENDIF frame.f_trace = ret # Make sure we keep the returned tracer. return ret except SystemExit: if event != 'call': frame.f_trace = NO_FTRACE return None except Exception: if py_db._finish_debugging_session: if event != 'call': frame.f_trace = NO_FTRACE 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 if event != 'call': frame.f_trace = NO_FTRACE return None
def log_event(self, frame): write_log = False self_obj = None if dict_contains(frame.f_locals, "self"): self_obj = frame.f_locals["self"] if isinstance(self_obj, threading.Thread) or self_obj.__class__ == ObjectWrapper: write_log = True if hasattr(frame, "f_back") and frame.f_back is not None: back = frame.f_back if hasattr(back, "f_back") and back.f_back is not None: back = back.f_back if dict_contains(back.f_locals, "self"): if isinstance(back.f_locals["self"], threading.Thread): write_log = True try: if write_log: t = threadingCurrentThread() back = frame.f_back if not back: return _, name, back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(back) event_time = cur_time() - self.start_time method_name = frame.f_code.co_name if isinstance(self_obj, threading.Thread): if not hasattr(self_obj, "_pydev_run_patched"): wrap_attr(self_obj, "run") if (method_name in THREAD_METHODS) and (back_base not in DONT_TRACE_THREADING or \ (method_name in INNER_METHODS and back_base in INNER_FILES)): thread_id = get_thread_id(self_obj) name = self_obj.getName() real_method = frame.f_code.co_name parent = None if real_method == "_stop": if back_base in INNER_FILES and \ back.f_code.co_name == "_wait_for_tstate_lock": back = back.f_back.f_back real_method = "stop" if hasattr(self_obj, "_pydev_join_called"): parent = get_thread_id(t) elif real_method == "join": # join called in the current thread, not in self object if not self_obj.is_alive(): return thread_id = get_thread_id(t) name = t.getName() setattr(self_obj, "_pydev_join_called", True) if real_method == "start": parent = get_thread_id(t) send_message("threading_event", event_time, name, thread_id, "thread", real_method, back.f_code.co_filename, back.f_lineno, back, parent=parent) # print(event_time, self_obj.getName(), thread_id, "thread", # real_method, back.f_code.co_filename, back.f_lineno) if method_name == "pydev_after_run_call": if hasattr(frame, "f_back") and frame.f_back is not None: back = frame.f_back if hasattr(back, "f_back") and back.f_back is not None: back = back.f_back if dict_contains(back.f_locals, "self"): if isinstance(back.f_locals["self"], threading.Thread): my_self_obj = frame.f_back.f_back.f_locals["self"] my_back = frame.f_back.f_back my_thread_id = get_thread_id(my_self_obj) send_massage = True if IS_PY3K and hasattr(my_self_obj, "_pydev_join_called"): send_massage = False # we can't detect stop after join in Python 2 yet if send_massage: send_message("threading_event", event_time, "Thread", my_thread_id, "thread", "stop", my_back.f_code.co_filename, my_back.f_lineno, my_back, parent=None) if self_obj.__class__ == ObjectWrapper: if back_base in DONT_TRACE_THREADING: # do not trace methods called from threading return back_back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(back.f_back)[-1] back = back.f_back if back_back_base in DONT_TRACE_THREADING: # back_back_base is the file, where the method was called froms return if method_name == "__init__": send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", method_name, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(frame.f_locals["self"]))) if dict_contains(frame.f_locals, "attr") and \ (frame.f_locals["attr"] in LOCK_METHODS or frame.f_locals["attr"] in QUEUE_METHODS): real_method = frame.f_locals["attr"] if method_name == "call_begin": real_method += "_begin" elif method_name == "call_end": real_method += "_end" else: return if real_method == "release_end": # do not log release end. Maybe use it later return send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", real_method, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj))) if real_method in ("put_end", "get_end"): # fake release for queue, cause we don't call it directly send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock", "release", back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj))) # print(event_time, t.getName(), get_thread_id(t), "lock", # real_method, back.f_code.co_filename, back.f_lineno) except Exception: traceback.print_exc()
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 handle_exception(self, frame, event, arg): try: # print 'handle_exception', frame.f_lineno, frame.f_code.co_name # We have 3 things in arg: exception type, description, traceback object trace_obj = arg[2] main_debugger = self._args[0] if not hasattr(trace_obj, 'tb_next'): return #Not always there on Jython... initial_trace_obj = trace_obj if trace_obj.tb_next is None and trace_obj.tb_frame is frame: #I.e.: tb_next should be only None in the context it was thrown (trace_obj.tb_frame is frame is just a double check). if main_debugger.break_on_exceptions_thrown_in_same_context: #Option: Don't break if an exception is caught in the same function from which it is thrown return else: #Get the trace_obj from where the exception was raised... while trace_obj.tb_next is not None: trace_obj = trace_obj.tb_next if main_debugger.ignore_exceptions_thrown_in_lines_with_ignore_exception: for check_trace_obj in (initial_trace_obj, trace_obj): filename = get_abs_path_real_path_and_base_from_frame(check_trace_obj.tb_frame)[1] filename_to_lines_where_exceptions_are_ignored = self.filename_to_lines_where_exceptions_are_ignored lines_ignored = filename_to_lines_where_exceptions_are_ignored.get(filename) if lines_ignored is None: lines_ignored = filename_to_lines_where_exceptions_are_ignored[filename] = {} try: curr_stat = os.stat(filename) curr_stat = (curr_stat.st_size, curr_stat.st_mtime) except: curr_stat = None last_stat = self.filename_to_stat_info.get(filename) if last_stat != curr_stat: self.filename_to_stat_info[filename] = curr_stat lines_ignored.clear() try: linecache.checkcache(filename) except: #Jython 2.1 linecache.checkcache() from_user_input = main_debugger.filename_to_lines_where_exceptions_are_ignored.get(filename) if from_user_input: merged = {} merged.update(lines_ignored) #Override what we have with the related entries that the user entered merged.update(from_user_input) else: merged = lines_ignored exc_lineno = check_trace_obj.tb_lineno # print ('lines ignored', lines_ignored) # print ('user input', from_user_input) # print ('merged', merged, 'curr', exc_lineno) if exc_lineno not in merged: #Note: check on merged but update lines_ignored. try: line = linecache.getline(filename, exc_lineno, check_trace_obj.tb_frame.f_globals) except: #Jython 2.1 line = linecache.getline(filename, exc_lineno) if IGNORE_EXCEPTION_TAG.match(line) is not None: lines_ignored[exc_lineno] = 1 return else: #Put in the cache saying not to ignore lines_ignored[exc_lineno] = 0 else: #Ok, dict has it already cached, so, let's check it... if merged.get(exc_lineno, 0): return thread = self._args[3] try: frame_id_to_frame = {} frame_id_to_frame[id(frame)] = frame f = trace_obj.tb_frame while f is not None: frame_id_to_frame[id(f)] = f f = f.f_back f = None thread_id = get_thread_id(thread) pydevd_vars.add_additional_frame_by_id(thread_id, frame_id_to_frame) try: main_debugger.send_caught_exception_stack(thread, arg, id(frame)) self.set_suspend(thread, CMD_STEP_CAUGHT_EXCEPTION) self.do_wait_suspend(thread, frame, event, arg) main_debugger.send_caught_exception_stack_proceeded(thread) finally: pydevd_vars.remove_additional_frame_by_id(thread_id) except: traceback.print_exc() main_debugger.set_trace_for_frame_and_parents(frame) finally: #Clear some local variables... trace_obj = None initial_trace_obj = None check_trace_obj = None f = None frame_id_to_frame = None main_debugger = None thread = None
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, info) # 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 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 breakpoint is None and not (is_return or is_exception_event): # 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_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 self.clear_run_state(info) except KeyboardInterrupt: self.clear_run_state(info) 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 _schedule_callback(prev, next): ''' Called when a context is stopped or a new context is made runnable. ''' try: if not prev and not next: return if next: register_tasklet_info(next) # Ok, making next runnable: set the tracing facility in it. debugger = get_global_debugger() if debugger is not None and next.frame: if hasattr(next.frame, 'f_trace'): next.frame.f_trace = debugger.get_thread_local_trace_func( ) debugger = None if prev: register_tasklet_info(prev) try: for tasklet_ref, tasklet_info in dict_items( _weak_tasklet_registered_to_info ): # Make sure it's a copy! tasklet = tasklet_ref() if tasklet is None or not tasklet.alive: # Garbage-collected already! try: del _weak_tasklet_registered_to_info[tasklet_ref] except KeyError: pass if tasklet_info.frame_id is not None: remove_custom_frame(tasklet_info.frame_id) else: if tasklet.paused or tasklet.blocked or tasklet.scheduled: if tasklet.frame and tasklet.frame.f_back: f_back = tasklet.frame.f_back base = get_abs_path_real_path_and_base_from_frame( f_back)[-1] is_file_to_ignore = base in DONT_TRACE if not is_file_to_ignore: if tasklet_info.frame_id is None: tasklet_info.frame_id = add_custom_frame( f_back, tasklet_info.tasklet_name, tasklet.thread_id) else: update_custom_frame( tasklet_info.frame_id, f_back, tasklet.thread_id) elif tasklet.is_current: if tasklet_info.frame_id is not None: # Remove info about stackless suspended when it starts to run. remove_custom_frame(tasklet_info.frame_id) tasklet_info.frame_id = None finally: tasklet = None tasklet_info = None f_back = None except: import traceback traceback.print_exc() if _application_set_schedule_callback is not None: return _application_set_schedule_callback(prev, next)
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 trace_dispatch(self, frame, event, arg): # ENDIF # Note: this is a big function because most of the logic related to hitting a breakpoint and # stepping is contained in it. Ideally this could be split among multiple functions, but the # problem in this case is that in pure-python function calls are expensive and even more so # when tracing is on (because each function call will get an additional tracing call). We # try to address this by using the info.is_tracing for the fastest possible return, but the # cost is still high (maybe we could use code-generation in the future and make the code # generation be better split among what each part does). # DEBUG = '_debugger_case_generator.py' 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 %s, stop: %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, constant_to_str(info.pydev_step_cmd), arg, info.pydev_step_stop)) try: info.is_tracing += 1 line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger.pydb_disposed: return None if event == 'call' else NO_FTRACE plugin_manager = main_debugger.plugin has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd if frame.f_code.co_flags & 0xa0: # 0xa0 == CO_GENERATOR = 0x20 | CO_COROUTINE = 0x80 # 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. if event == 'line': is_line = True is_call = False is_return = False is_exception_event = False elif event == 'return': is_line = False is_call = False is_return = True is_exception_event = False 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 else: # Tricky handling: usually when we're on a frame which is about to exit # we set the step mode to step into, but in this case we'd end up in the # asyncio internal machinery, which is not what we want, so, we just # ask the stop frame to be a level up. # # Note that there's an issue here which we may want to fix in the future: if # the back frame is a frame which is filtered, we won't stop properly. # Solving this may not be trivial as we'd need to put a scope in the step # in, but we may have to do it anyways to have a step in which doesn't end # up in asyncio). if stop_frame is frame: if step_cmd in (CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE, CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE): f = self._get_unfiltered_back_frame( main_debugger, frame) if f is not None: info.pydev_step_cmd = CMD_STEP_INTO_COROUTINE info.pydev_step_stop = f else: if step_cmd == CMD_STEP_OVER: info.pydev_step_cmd = CMD_STEP_INTO info.pydev_step_stop = None elif step_cmd == CMD_STEP_OVER_MY_CODE: info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE info.pydev_step_stop = None elif step_cmd == CMD_STEP_INTO_COROUTINE: # We're exiting this one, so, mark the new coroutine context. f = self._get_unfiltered_back_frame( main_debugger, frame) if f is not None: info.pydev_step_stop = f else: info.pydev_step_cmd = CMD_STEP_INTO info.pydev_step_stop = None elif event == 'exception': breakpoints_for_file = None 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 return self.trace_dispatch else: # event == 'call' or event == 'c_XXX' return self.trace_dispatch else: if event == 'line': is_line = True is_call = False is_return = False is_exception_event = False elif event == 'return': is_line = False is_return = True is_call = False is_exception_event = False # 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 ( CMD_STEP_OVER, CMD_STEP_RETURN, CMD_STEP_OVER_MY_CODE, CMD_STEP_RETURN_MY_CODE): if step_cmd in (CMD_STEP_OVER, CMD_STEP_RETURN): info.pydev_step_cmd = CMD_STEP_INTO else: info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE info.pydev_step_stop = None elif event == 'call': is_line = False is_call = True is_return = False is_exception_event = False elif event == 'exception': is_exception_event = True breakpoints_for_file = None 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: # Unexpected: just keep the same trace func (i.e.: event == 'c_XXX'). return self.trace_dispatch if not is_exception_event: 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 # - we're stepping into a coroutine context and we're not in that context if step_cmd == -1: can_skip = True elif step_cmd in (CMD_STEP_OVER, CMD_STEP_RETURN, CMD_STEP_OVER_MY_CODE, CMD_STEP_RETURN_MY_CODE ) and stop_frame is not frame: can_skip = True elif step_cmd == CMD_STEP_INTO_COROUTINE: f = frame while f is not None: if f is stop_frame: break f = f.f_back else: can_skip = True 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) if can_skip and main_debugger.show_return_values and info.pydev_step_cmd in ( CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE ) and frame.f_back is stop_frame: # 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: 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 func_lines = _get_func_lines(frame.f_code) if func_lines is None: # This is a fallback for implementations where we can't get the function # lines -- i.e.: jython (in this case clients need to provide the function # name to decide on the skip or we won't be able to skip the function # completely). # 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 bp in dict_iter_values( breakpoints_for_file ): # jython does not support itervalues() # will match either global or some function if bp.func_name in ('None', curr_func_name): has_breakpoint_in_frame = True break else: for bp_line in breakpoints_for_file: # iterate on keys if bp_line in func_lines: 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: 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: stop = False elif breakpoint.is_logpoint: stop = False 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 is stop_frame)) or (info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE) and (frame is stop_frame)) or (info.pydev_step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_INTO_COROUTINE))): 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: pydev_log.exception() 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, CMD_STEP_INTO_COROUTINE): 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( 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 else: stop = False if stop: if step_cmd == CMD_STEP_INTO_COROUTINE: # i.e.: Check if we're stepping into the proper context. f = frame while f is not None: if f is stop_frame: break f = f.f_back else: stop = False 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 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( frame.f_back) == 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, original_step_cmd=info.pydev_original_step_cmd) self.do_wait_suspend(thread, frame, event, arg) elif is_return: # 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, original_step_cmd=info.pydev_original_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_original_step_cmd = -1 info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN except KeyboardInterrupt: raise except: try: pydev_log.exception() info.pydev_original_step_cmd = -1 info.pydev_step_cmd = -1 info.pydev_step_stop = None 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 -= 1
def _schedule_callback(prev, next): ''' Called when a context is stopped or a new context is made runnable. ''' try: if not prev and not next: return current_frame = sys._getframe() if next: register_tasklet_info(next) # Ok, making next runnable: set the tracing facility in it. debugger = get_global_debugger() if debugger is not None: next.trace_function = debugger.trace_dispatch frame = next.frame if frame is current_frame: frame = frame.f_back if hasattr(frame, 'f_trace'): # Note: can be None (but hasattr should cover for that too). frame.f_trace = debugger.trace_dispatch debugger = None if prev: register_tasklet_info(prev) try: for tasklet_ref, tasklet_info in dict_items(_weak_tasklet_registered_to_info): # Make sure it's a copy! tasklet = tasklet_ref() if tasklet is None or not tasklet.alive: # Garbage-collected already! try: del _weak_tasklet_registered_to_info[tasklet_ref] except KeyError: pass if tasklet_info.frame_id is not None: remove_custom_frame(tasklet_info.frame_id) else: is_running = stackless.get_thread_info(tasklet.thread_id)[1] is tasklet if tasklet is prev or (tasklet is not next and not is_running): # the tasklet won't run after this scheduler action: # - the tasklet is the previous tasklet # - it is not the next tasklet and it is not an already running tasklet frame = tasklet.frame if frame is current_frame: frame = frame.f_back if frame is not None: base = get_abs_path_real_path_and_base_from_frame(frame)[-1] # print >>sys.stderr, "SchedCB: %r, %d, '%s', '%s'" % (tasklet, frame.f_lineno, _filename, base) is_file_to_ignore = base in DONT_TRACE if not is_file_to_ignore: tasklet_info.update_name() if tasklet_info.frame_id is None: tasklet_info.frame_id = add_custom_frame(frame, tasklet_info.tasklet_name, tasklet.thread_id) else: update_custom_frame(tasklet_info.frame_id, frame, tasklet.thread_id, name=tasklet_info.tasklet_name) elif tasklet is next or is_running: if tasklet_info.frame_id is not None: # Remove info about stackless suspended when it starts to run. remove_custom_frame(tasklet_info.frame_id) tasklet_info.frame_id = None finally: tasklet = None tasklet_info = None frame = None except: import traceback;traceback.print_exc() if _application_set_schedule_callback is not None: return _application_set_schedule_callback(prev, next)
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 tuple abs_path_real_path_and_base; # cdef PyDBAdditionalThreadInfo additional_info; # ENDIF py_db, t, additional_info = self._args 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) 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(abs_path_real_path_and_base[1]): # print('skipped: trace_dispatch (not in scope)', base, frame.f_lineno, event, frame.f_code.co_name, file_type) return None else: # print('skipped: trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type) return None if additional_info.pydev_step_cmd != -1: if py_db.is_filter_enabled and py_db.is_ignored_by_filters(abs_path_real_path_and_base[1]): # ignore files matching stepping filters return None if py_db.is_filter_libraries and py_db.not_in_scope(abs_path_real_path_and_base[1]): # 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 # each new frame... # IFDEF CYTHON # # Note that on Cython we only support more modern idioms (no support for < Python 2.5) # return PyDBFrame((py_db, abs_path_real_path_and_base[1], additional_info, t)).trace_dispatch(frame, event, arg) # ELSE return additional_info.create_db_frame((py_db, abs_path_real_path_and_base[1], additional_info, t, frame)).trace_dispatch(frame, event, arg) # 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
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 need_trace_return = False if main_debugger._finish_debugging_session: return None if event == 'call' and main_debugger.signature_factory: need_trace_return = send_signature_call_trace( main_debugger, frame, filename) if event == 'return' and main_debugger.signature_factory: send_signature_return_trace(main_debugger, frame, filename, arg) 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 is 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 is '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: if need_trace_return: return self.trace_return 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: 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: 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 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 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 _is_next_stack_trace_in_project_roots(trace): if trace and trace.tb_next and trace.tb_next.tb_frame: frame = trace.tb_next.tb_frame return in_project_roots(pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(frame)[0]) return False
def handle_exception(self, frame, event, arg): try: # print('handle_exception', frame.f_lineno, frame.f_code.co_name) # We have 3 things in arg: exception type, description, traceback object trace_obj = arg[2] main_debugger = self._args[0] initial_trace_obj = trace_obj if trace_obj.tb_next is None and trace_obj.tb_frame is frame: # I.e.: tb_next should be only None in the context it was thrown (trace_obj.tb_frame is frame is just a double check). pass else: # Get the trace_obj from where the exception was raised... while trace_obj.tb_next is not None: trace_obj = trace_obj.tb_next if main_debugger.ignore_exceptions_thrown_in_lines_with_ignore_exception: for check_trace_obj in (initial_trace_obj, trace_obj): filename = get_abs_path_real_path_and_base_from_frame( check_trace_obj.tb_frame)[1] filename_to_lines_where_exceptions_are_ignored = self.filename_to_lines_where_exceptions_are_ignored lines_ignored = filename_to_lines_where_exceptions_are_ignored.get( filename) if lines_ignored is None: lines_ignored = filename_to_lines_where_exceptions_are_ignored[ filename] = {} try: curr_stat = os.stat(filename) curr_stat = (curr_stat.st_size, curr_stat.st_mtime) except: curr_stat = None last_stat = self.filename_to_stat_info.get(filename) if last_stat != curr_stat: self.filename_to_stat_info[filename] = curr_stat lines_ignored.clear() try: linecache.checkcache(filename) except: # Jython 2.1 linecache.checkcache() from_user_input = main_debugger.filename_to_lines_where_exceptions_are_ignored.get( filename) if from_user_input: merged = {} merged.update(lines_ignored) # Override what we have with the related entries that the user entered merged.update(from_user_input) else: merged = lines_ignored exc_lineno = check_trace_obj.tb_lineno # print ('lines ignored', lines_ignored) # print ('user input', from_user_input) # print ('merged', merged, 'curr', exc_lineno) if exc_lineno not in merged: # Note: check on merged but update lines_ignored. try: line = linecache.getline( filename, exc_lineno, check_trace_obj.tb_frame.f_globals) except: # Jython 2.1 line = linecache.getline(filename, exc_lineno) if IGNORE_EXCEPTION_TAG.match(line) is not None: lines_ignored[exc_lineno] = 1 return else: # Put in the cache saying not to ignore lines_ignored[exc_lineno] = 0 else: # Ok, dict has it already cached, so, let's check it... if merged.get(exc_lineno, 0): return thread = self._args[3] try: frame_id_to_frame = {} frame_id_to_frame[id(frame)] = frame f = trace_obj.tb_frame while f is not None: frame_id_to_frame[id(f)] = f f = f.f_back f = None main_debugger.send_caught_exception_stack( thread, arg, id(frame)) self.set_suspend(thread, CMD_STEP_CAUGHT_EXCEPTION) self.do_wait_suspend(thread, frame, event, arg) main_debugger.send_caught_exception_stack_proceeded(thread) except: traceback.print_exc() main_debugger.set_trace_for_frame_and_parents(frame) finally: # Make sure the user cannot see the '__exception__' we added after we leave the suspend state. remove_exception_from_frame(frame) # Clear some local variables... frame = None trace_obj = None initial_trace_obj = None check_trace_obj = None f = None frame_id_to_frame = None main_debugger = None thread = None
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 _schedule_callback(prev, next): ''' Called when a context is stopped or a new context is made runnable. ''' try: if not prev and not next: return current_frame = sys._getframe() if next: register_tasklet_info(next) # Ok, making next runnable: set the tracing facility in it. debugger = get_global_debugger() if debugger is not None: next.trace_function = debugger.get_thread_local_trace_func() frame = next.frame if frame is current_frame: frame = frame.f_back if hasattr(frame, 'f_trace'): # Note: can be None (but hasattr should cover for that too). frame.f_trace = debugger.get_thread_local_trace_func() debugger = None if prev: register_tasklet_info(prev) try: for tasklet_ref, tasklet_info in dict_items(_weak_tasklet_registered_to_info): # Make sure it's a copy! tasklet = tasklet_ref() if tasklet is None or not tasklet.alive: # Garbage-collected already! try: del _weak_tasklet_registered_to_info[tasklet_ref] except KeyError: pass if tasklet_info.frame_id is not None: remove_custom_frame(tasklet_info.frame_id) else: is_running = stackless.get_thread_info(tasklet.thread_id)[1] is tasklet if tasklet is prev or (tasklet is not next and not is_running): # the tasklet won't run after this scheduler action: # - the tasklet is the previous tasklet # - it is not the next tasklet and it is not an already running tasklet frame = tasklet.frame if frame is current_frame: frame = frame.f_back if frame is not None: abs_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame) # print >>sys.stderr, "SchedCB: %r, %d, '%s', '%s'" % (tasklet, frame.f_lineno, _filename, base) if debugger.get_file_type(abs_real_path_and_base) is None: tasklet_info.update_name() if tasklet_info.frame_id is None: tasklet_info.frame_id = add_custom_frame(frame, tasklet_info.tasklet_name, tasklet.thread_id) else: update_custom_frame(tasklet_info.frame_id, frame, tasklet.thread_id, name=tasklet_info.tasklet_name) elif tasklet is next or is_running: if tasklet_info.frame_id is not None: # Remove info about stackless suspended when it starts to run. remove_custom_frame(tasklet_info.frame_id) tasklet_info.frame_id = None finally: tasklet = None tasklet_info = None frame = None except: import traceback;traceback.print_exc() if _application_set_schedule_callback is not None: return _application_set_schedule_callback(prev, next)
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 # 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
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 \ or main_debugger.stop_on_failed_tests 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 in (CMD_STEP_OVER, CMD_STEP_INTO_COROUTINE): stop = stop_frame is frame if stop: if is_line: # the only case we shouldn't stop on a line, is when we traversing though asynchronous framework machinery if step_cmd == CMD_STEP_INTO_COROUTINE: 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: back = frame.f_back if back: info.pydev_step_stop = back if main_debugger.in_project_scope( frame.f_code.co_filename): # we are returning from the project scope, step over should always lead to the project scope if is_generator_or_coroutime and step_cmd == CMD_STEP_OVER: # setting ad hoc command to ensure we will skip line stops in an asynchronous framework info.pydev_step_cmd = CMD_STEP_INTO_COROUTINE else: # we were already outside the project scope because of step into or breakpoint, it's ok to stop # if we are not chopping a way through an asynchronous framework stop = not step_cmd == CMD_STEP_INTO_COROUTINE 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 CMD_STEP_OVER and 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 is_test_item_or_set_up_caller(trace): """Check if the frame is the test item or set up caller. A test function caller is a function that calls actual test code which can be, for example, `unittest.TestCase` test method or function `pytest` assumes to be a test. A caller function is the one we want to trace to catch failed test events. Tracing test functions themselves is not possible because some exceptions can be caught in the test code, and we are interested only in exceptions that are propagated to the test framework level. """ if not trace: return False frame = trace.tb_frame abs_path, _, _ = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame( frame) if in_project_roots(abs_path): # We are interested in exceptions made it to the test framework scope. return False if not trace.tb_next: # This can happen when the exception has been raised inside a test item or set up caller. return False if not _is_next_stack_trace_in_project_roots(trace): # The next stack frame must be the frame of a project scope function, otherwise we risk stopping # at a line a few times since multiple test framework functions we are looking for may appear in the stack. return False # Set up and tear down methods can be checked immediately, since they are shared by both `pytest` and `unittest`. unittest_set_up_and_tear_down_methods = ('_callSetUp', '_callTearDown') if frame.f_code.co_name in unittest_set_up_and_tear_down_methods: return True # It is important to check if the tests are run with `pytest` first because it can run `unittest` code # internally. This may lead to stopping on broken tests twice: one in the `pytest` test runner # and second in the `unittest` runner. is_pytest = False f = frame while f: # noinspection SpellCheckingInspection if f.f_code.co_name == 'pytest_cmdline_main': is_pytest = True f = f.f_back unittest_caller_names = ['_callTestMethod', 'runTest', 'run'] if IS_PY3K: unittest_caller_names.append('subTest') if is_pytest: # noinspection SpellCheckingInspection if frame.f_code.co_name in ('pytest_pyfunc_call', 'call_fixture_func', '_eval_scope_callable', '_teardown_yield_fixture'): return True else: return frame.f_code.co_name in unittest_caller_names else: import unittest test_case_obj = frame.f_locals.get('self') # Check for `_FailedTest` is important to detect cases when tests cannot be run on the first place, # e.g. there was an import error in the test module. Can happen both in Python 3.8 and earlier versions. if isinstance( test_case_obj, getattr(getattr(unittest, 'loader', None), '_FailedTest', None)): return False if frame.f_code.co_name in unittest_caller_names: # unittest and nose return True return False