Пример #1
0
 def stop_on_entry(self):
     main_thread = pydevd_utils.get_main_thread()
     if main_thread is None:
         pydev_log.critical('Could not find main thread while setting Stop on Entry.')
     else:
         info = set_additional_thread_info(main_thread)
         info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE
         info.pydev_stop_on_entry = True
    def on_configurationdone_request(self, py_db, request):
        '''
        :param ConfigurationDoneRequest request:
        '''
        if not self._launch_or_attach_request_done:
            pydev_log.critical('Missing launch request or attach request before configuration done request.')

        self.api.run(py_db)
        self.api.notify_configuration_done(py_db)

        configuration_done_response = pydevd_base_schema.build_response(request)
        return NetCommand(CMD_RETURN, 0, configuration_done_response, is_json=True)
 def _load_modules(self):
     self.loaded_extensions = []
     if extensions:
         for module_loader, name, ispkg in pkgutil.walk_packages(extensions.__path__,
                                                                 extensions.__name__ + '.'):
             mod_name = name.split('.')[-1]
             if not ispkg and mod_name.startswith('pydevd_plugin'):
                 try:
                     __import__(name)
                     module = sys.modules[name]
                     self.loaded_extensions.append(module)
                 except ImportError:
                     pydev_log.critical('Unable to load extension: %s', name)
    def cmd_remove_break(self, py_db, cmd_id, seq, text):
        # command to remove some breakpoint
        # text is type\file\tid. Remove from breakpoints dictionary
        breakpoint_type, filename, breakpoint_id = text.split('\t', 2)

        filename = self.api.filename_to_server(filename)

        try:
            breakpoint_id = int(breakpoint_id)
        except ValueError:
            pydev_log.critical('Error removing breakpoint. Expected breakpoint_id to be an int. Found: %s', breakpoint_id)

        else:
            self.api.remove_breakpoint(py_db, filename, breakpoint_type, breakpoint_id)
Пример #5
0
def test_pydevd_log():
    from _pydev_bundle import pydev_log
    try:
        import StringIO as io
    except:
        import io
    from _pydev_bundle.pydev_log import log_context

    stream = io.StringIO()
    with log_context(0, stream=stream):
        pydev_log.critical('always')
        pydev_log.info('never')

    assert stream.getvalue() == 'always\n'

    stream = io.StringIO()
    with log_context(1, stream=stream):
        pydev_log.critical('always')
        pydev_log.info('this too')

    assert stream.getvalue() == 'always\nthis too\n'

    stream = io.StringIO()
    with log_context(0, stream=stream):
        pydev_log.critical('always %s', 1)

    assert stream.getvalue() == 'always 1\n'

    stream = io.StringIO()
    with log_context(0, stream=stream):
        pydev_log.critical('always %s %s', 1, 2)

    assert stream.getvalue() == 'always 1 2\n'

    stream = io.StringIO()
    with log_context(0, stream=stream):
        pydev_log.critical('always %s %s', 1)

    # Even if there's an error in the formatting, don't fail, just print the message and args.
    assert stream.getvalue() == 'always %s %s - (1,)\n'

    stream = io.StringIO()
    with log_context(0, stream=stream):
        try:
            raise RuntimeError()
        except:
            pydev_log.exception('foo')

        assert 'foo\n' in stream.getvalue()
        assert 'raise RuntimeError()' in stream.getvalue()
    def on_configurationdone_request(self, py_db, request):
        '''
        :param ConfigurationDoneRequest request:
        '''
        if not self._launch_or_attach_request_done:
            pydev_log.critical(
                'Missing launch request or attach request before configuration done request.'
            )

        self.api.run(py_db)
        self.api.notify_configuration_done(py_db)

        configuration_done_response = pydevd_base_schema.build_response(
            request)
        return NetCommand(CMD_RETURN,
                          0,
                          configuration_done_response,
                          is_json=True)
Пример #7
0
    def remove_breakpoint(self, py_db, filename, breakpoint_type, breakpoint_id):
        '''
        :param str filename:
            Note: must be already translated for the server.

        :param str breakpoint_type:
            One of: 'python-line', 'django-line', 'jinja2-line'.

        :param int breakpoint_id:
        '''
        file_to_id_to_breakpoint = None

        if breakpoint_type == 'python-line':
            breakpoints = py_db.breakpoints
            file_to_id_to_breakpoint = py_db.file_to_id_to_line_breakpoint

        elif py_db.plugin is not None:
            result = py_db.plugin.get_breakpoints(py_db, breakpoint_type)
            if result is not None:
                file_to_id_to_breakpoint = py_db.file_to_id_to_plugin_breakpoint
                breakpoints = result

        if file_to_id_to_breakpoint is None:
            pydev_log.critical('Error removing breakpoint. Cannot handle breakpoint of type %s', breakpoint_type)

        else:
            try:
                id_to_pybreakpoint = file_to_id_to_breakpoint.get(filename, {})
                if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
                    existing = id_to_pybreakpoint[breakpoint_id]
                    pydev_log.info('Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n' % (
                        filename, existing.line, existing.func_name.encode('utf-8'), breakpoint_id))

                del id_to_pybreakpoint[breakpoint_id]
                py_db.consolidate_breakpoints(filename, id_to_pybreakpoint, breakpoints)
                if py_db.plugin is not None:
                    py_db.has_plugin_line_breaks = py_db.plugin.has_line_breaks()

            except KeyError:
                pydev_log.info("Error removing breakpoint: Breakpoint id not found: %s id: %s. Available ids: %s\n",
                    filename, breakpoint_id, dict_keys(id_to_pybreakpoint))

        py_db.on_breakpoints_changed(removed=True)
Пример #8
0
        def _get_path_with_real_case(filename):
            # Note: this previously made:
            # convert_to_long_pathname(convert_to_short_pathname(filename))
            # but this is no longer done because we can't rely on getting the shortname
            # consistently (there are settings to disable it on Windows).
            # So, using approach which resolves by listing the dir.

            if IS_PY2 and isinstance(filename, unicode):  # noqa
                filename = filename.encode(getfilesystemencoding())

            if '~' in filename:
                filename = convert_to_long_pathname(filename)

            if filename.startswith('<') or not os_path_exists(filename):
                return filename  # Not much we can do.

            drive, parts = os.path.splitdrive(os.path.normpath(filename))
            drive = drive.upper()
            while parts.startswith(os.path.sep):
                parts = parts[1:]
                drive += os.path.sep
            parts = parts.lower().split(os.path.sep)

            try:
                return _resolve_listing(drive, iter(parts))
            except FileNotFoundError:
                _listdir_cache.clear()
                # Retry once after clearing the cache we have.
                try:
                    return _resolve_listing(drive, iter(parts))
                except FileNotFoundError:
                    if os_path_exists(filename):
                        # This is really strange, ask the user to report as error.
                        pydev_log.critical(
                            'pydev debugger: critical: unable to get real case for file. Details:\n'
                            'filename: %s\ndrive: %s\nparts: %s\n'
                            '(please create a ticket in the tracker to address this).',
                            filename, drive, parts
                        )
                        pydev_log.exception()
                    # Don't fail, just return the original file passed.
                    return filename
Пример #9
0
    def _show_debug_info(cls, cmd_id, seq, text):
        with cls._show_debug_info_lock:
            # Only one thread each time (rlock).
            if cls._showing_debug_info:
                # avoid recursing in the same thread (just printing could create
                # a new command when redirecting output).
                return

            cls._showing_debug_info += 1
            try:
                out_message = 'sending cmd --> '
                out_message += "%20s" % ID_TO_MEANING.get(str(cmd_id), 'UNKNOWN')
                out_message += ' '
                out_message += text.replace('\n', ' ')
                try:
                    pydev_log.critical('%s\n', out_message)
                except:
                    pass
            finally:
                cls._showing_debug_info -= 1
Пример #10
0
def resume_threads(thread_id, except_thread=None):
    pydev_log.info('Resuming threads: %s (except thread: %s)', thread_id,
                   except_thread)
    threads = []
    if thread_id == '*':
        threads = pydevd_utils.get_non_pydevd_threads()

    elif thread_id.startswith('__frame__:'):
        pydev_log.critical("Can't make tasklet run: %s", thread_id)

    else:
        threads = [pydevd_find_thread_by_id(thread_id)]

    for t in threads:
        if t is None or t is except_thread:
            pydev_log.info('Skipped resuming thread: %s', t)
            continue

        internal_run_thread(
            t, set_additional_thread_info=set_additional_thread_info)
Пример #11
0
    def exec_on_timeout(self):
        # Note: lock should already be obtained when executing this function.
        kwargs = self.kwargs
        on_timeout = self.on_timeout

        if not self.disposed:
            self.disposed = True
            self.kwargs = None
            self.on_timeout = None

            try:
                if _DEBUG:
                    pydev_log.critical(
                        'pydevd_timeout: Calling on timeout: %s with kwargs: %s',
                        on_timeout, kwargs)

                on_timeout(**kwargs)
            except Exception:
                pydev_log.exception(
                    'pydevd_timeout: Exception on callback timeout.')
Пример #12
0
    def _show_debug_info(cls, cmd_id, seq, text):
        with cls._show_debug_info_lock:
            # Only one thread each time (rlock).
            if cls._showing_debug_info:
                # avoid recursing in the same thread (just printing could create
                # a new command when redirecting output).
                return

            cls._showing_debug_info += 1
            try:
                out_message = 'sending cmd (%s) --> ' % (get_protocol(),)
                out_message += "%20s" % ID_TO_MEANING.get(str(cmd_id), 'UNKNOWN')
                out_message += ' '
                out_message += text.replace('\n', ' ')
                try:
                    pydev_log.critical('%s\n', out_message)
                except:
                    pass
            finally:
                cls._showing_debug_info -= 1
Пример #13
0
    def process_handles(self):
        '''
        :return int:
            Returns the time we should be waiting for to process the next event properly.
        '''
        with self._lock:
            if _DEBUG:
                pydev_log.critical('pydevd_timeout: Processing handles')
            self._event.clear()
            handles = self._handles
            new_handles = self._handles = []

            # Do all the processing based on this time (we want to consider snapshots
            # of processing time -- anything not processed now may be processed at the
            # next snapshot).
            curtime = time.time()

            min_handle_timeout = None

            for handle in handles:
                if curtime < handle.abs_timeout and not handle.disposed:
                    # It still didn't time out.
                    if _DEBUG:
                        pydev_log.critical(
                            'pydevd_timeout: Handle NOT processed: %s', handle)
                    new_handles.append(handle)
                    if min_handle_timeout is None:
                        min_handle_timeout = handle.abs_timeout

                    elif handle.abs_timeout < min_handle_timeout:
                        min_handle_timeout = handle.abs_timeout

                else:
                    if _DEBUG:
                        pydev_log.critical(
                            'pydevd_timeout: Handle processed: %s', handle)
                    # Timed out (or disposed), so, let's execute it (should be no-op if disposed).
                    handle.exec_on_timeout()

            if min_handle_timeout is None:
                return None
            else:
                timeout = min_handle_timeout - curtime
                if timeout <= 0:
                    pydev_log.critical(
                        'pydevd_timeout: Expected timeout to be > 0. Found: %s',
                        timeout)

                return timeout
    def on_setdebuggerproperty_request(self, py_db, request):
        args = request.arguments.kwargs
        if 'dontTraceStartPatterns' in args and 'dontTraceEndPatterns' in args:
            start_patterns = tuple(args['dontTraceStartPatterns'])
            end_patterns = tuple(args['dontTraceEndPatterns'])
            if self._can_set_dont_trace_pattern(py_db, start_patterns, end_patterns):

                def dont_trace_files_property_request(abs_path):
                    result = abs_path.startswith(start_patterns) or \
                            abs_path.endswith(end_patterns)
                    return result

                dont_trace_files_property_request.start_patterns = start_patterns
                dont_trace_files_property_request.end_patterns = end_patterns
                py_db.dont_trace_external_files = dont_trace_files_property_request
            else:
                # Don't trace pattern cannot be changed after it is set once. There are caches
                # throughout the debugger which rely on always having the same file type.
                message = ("Calls to set or change don't trace patterns (via setDebuggerProperty) are not "
                           "allowed since debugging has already started or don't trace patterns are already set.")
                pydev_log.critical(message)
                response_args = {'success':False, 'body': {}, 'message': message}
                response = pydevd_base_schema.build_response(request, kwargs=response_args)
                return NetCommand(CMD_RETURN, 0, response, is_json=True)

        # TODO: Support other common settings. Note that not all of these might be relevant to python.
        # JustMyCodeStepping: 0 or 1
        # AllowOutOfProcessSymbols: 0 or 1
        # DisableJITOptimization: 0 or 1
        # InterpreterOptions: 0 or 1
        # StopOnExceptionCrossingManagedBoundary: 0 or 1
        # WarnIfNoUserCodeOnLaunch: 0 or 1
        # EnableStepFiltering: true of false

        response = pydevd_base_schema.build_response(request, kwargs={'body': {}})
        return NetCommand(CMD_RETURN, 0, response, is_json=True)
Пример #15
0
    def _get_except_target_info(instructions, exception_end_instruction_index,
                                offset_to_instruction_idx):
        next_3 = [
            j_instruction.opname
            for j_instruction in instructions[exception_end_instruction_index:
                                              exception_end_instruction_index +
                                              3]
        ]
        # print('next_3:', [(j_instruction.opname, j_instruction.argval) for j_instruction in instructions[exception_end_instruction_index:exception_end_instruction_index + 3]])
        if next_3 == ['POP_TOP', 'POP_TOP',
                      'POP_TOP']:  # try..except without checking exception.
            try:
                jump_instruction = instructions[exception_end_instruction_index
                                                - 1]
                if jump_instruction.opname not in ('JUMP_FORWARD',
                                                   'JUMP_ABSOLUTE'):
                    return None
            except IndexError:
                pass

            if jump_instruction.opname == 'JUMP_ABSOLUTE':
                # On latest versions of Python 3 the interpreter has a go-backwards step,
                # used to show the initial line of a for/while, etc (which is this
                # JUMP_ABSOLUTE)... we're not really interested in it, but rather on where
                # it points to.
                except_end_instruction = instructions[
                    offset_to_instruction_idx[jump_instruction.argval]]
                idx = offset_to_instruction_idx[except_end_instruction.argval]
                # Search for the POP_EXCEPT which should be at the end of the block.
                for pop_except_instruction in reversed(instructions[:idx]):
                    if pop_except_instruction.opname == 'POP_EXCEPT':
                        except_end_instruction = pop_except_instruction
                        return _TargetInfo(except_end_instruction)
                else:
                    return None  # i.e.: Continue outer loop

            else:
                # JUMP_FORWARD
                i = offset_to_instruction_idx[jump_instruction.argval]
                try:
                    # i.e.: the jump is to the instruction after the block finishes (so, we need to
                    # get the previous instruction as that should be the place where the exception
                    # block finishes).
                    except_end_instruction = instructions[i - 1]
                except:
                    pydev_log.critical(
                        'Error when computing try..except block end.')
                    return None
                return _TargetInfo(except_end_instruction)

        elif next_3 and next_3[0] == 'DUP_TOP':  # try..except AssertionError.
            iter_in = instructions[exception_end_instruction_index + 1:]
            for j, jump_if_not_exc_instruction in enumerate(iter_in):
                if jump_if_not_exc_instruction.opname == 'JUMP_IF_NOT_EXC_MATCH':
                    # Python 3.9
                    except_end_instruction = instructions[
                        offset_to_instruction_idx[
                            jump_if_not_exc_instruction.argval]]
                    return _TargetInfo(except_end_instruction,
                                       jump_if_not_exc_instruction)

                elif jump_if_not_exc_instruction.opname == 'COMPARE_OP' and jump_if_not_exc_instruction.argval == 'exception match':
                    # Python 3.8 and before
                    try:
                        next_instruction = iter_in[j + 1]
                    except:
                        continue
                    if next_instruction.opname == 'POP_JUMP_IF_FALSE':
                        except_end_instruction = instructions[
                            offset_to_instruction_idx[next_instruction.argval]]
                        return _TargetInfo(except_end_instruction,
                                           next_instruction)
            else:
                return None  # i.e.: Continue outer loop

        else:
            # i.e.: we're not interested in try..finally statements, only try..except.
            return None
Пример #16
0
    def cmd_set_break(self, py_db, cmd_id, seq, text):
        # func name: 'None': match anything. Empty: match global, specified: only method context.
        # command to add some breakpoint.
        # text is filename\tline. Add to breakpoints dictionary
        suspend_policy = u"NONE"  # Can be 'NONE' or 'ALL'
        is_logpoint = False
        hit_condition = None
        if py_db._set_breakpoints_with_id:
            try:
                try:
                    breakpoint_id, btype, filename, line, func_name, condition, expression, hit_condition, is_logpoint, suspend_policy = text.split(
                        u'\t', 9)
                except ValueError:  # not enough values to unpack
                    # No suspend_policy passed (use default).
                    breakpoint_id, btype, filename, line, func_name, condition, expression, hit_condition, is_logpoint = text.split(
                        u'\t', 8)
                is_logpoint = is_logpoint == u'True'
            except ValueError:  # not enough values to unpack
                breakpoint_id, btype, filename, line, func_name, condition, expression = text.split(
                    u'\t', 6)

            breakpoint_id = int(breakpoint_id)
            line = int(line)

            # We must restore new lines and tabs as done in
            # AbstractDebugTarget.breakpointAdded
            condition = condition.replace(u"@_@NEW_LINE_CHAR@_@", u'\n').\
                replace(u"@_@TAB_CHAR@_@", u'\t').strip()

            expression = expression.replace(u"@_@NEW_LINE_CHAR@_@", u'\n').\
                replace(u"@_@TAB_CHAR@_@", u'\t').strip()
        else:
            # Note: this else should be removed after PyCharm migrates to setting
            # breakpoints by id (and ideally also provides func_name).
            btype, filename, line, func_name, suspend_policy, condition, expression = text.split(
                u'\t', 6)
            # If we don't have an id given for each breakpoint, consider
            # the id to be the line.
            breakpoint_id = line = int(line)

            condition = condition.replace(u"@_@NEW_LINE_CHAR@_@", u'\n'). \
                replace(u"@_@TAB_CHAR@_@", u'\t').strip()

            expression = expression.replace(u"@_@NEW_LINE_CHAR@_@", u'\n'). \
                replace(u"@_@TAB_CHAR@_@", u'\t').strip()

        if condition is not None and (len(condition) <= 0
                                      or condition == u"None"):
            condition = None

        if expression is not None and (len(expression) <= 0
                                       or expression == u"None"):
            expression = None

        if hit_condition is not None and (len(hit_condition) <= 0
                                          or hit_condition == u"None"):
            hit_condition = None

        result = self.api.add_breakpoint(py_db,
                                         self.api.filename_to_str(filename),
                                         btype, breakpoint_id, line, condition,
                                         func_name, expression, suspend_policy,
                                         hit_condition, is_logpoint)
        error_code = result.error_code

        if error_code:
            translated_filename = result.translated_filename
            if error_code == self.api.ADD_BREAKPOINT_FILE_NOT_FOUND:
                pydev_log.critical(
                    'pydev debugger: warning: Trying to add breakpoint to file that does not exist: %s (will have no effect).'
                    % (translated_filename, ))

            elif error_code == self.api.ADD_BREAKPOINT_FILE_EXCLUDED_BY_FILTERS:
                pydev_log.critical(
                    'pydev debugger: warning: Trying to add breakpoint to file that is excluded by filters: %s (will have no effect).'
                    % (translated_filename, ))

            else:
                # Shouldn't get here.
                pydev_log.critical(
                    'pydev debugger: warning: Breakpoint not validated (reason unknown -- please report as error): %s.'
                    % (translated_filename, ))
Пример #17
0
    def _norm_file_to_client(filename,
                             cache=norm_filename_to_client_container):
        # The result of this method will be passed to eclipse
        # So, this would be 'NormFileFromPythonToEclipse'
        try:
            return cache[filename]
        except KeyError:
            # used to translate a path from the debug server to the client
            translated = _NormFile(filename)

            # After getting the real path, let's get it with the path with
            # the real case and then obtain a new normalized copy, just in case
            # the path is different now.
            translated_proper_case = get_path_with_real_case(translated)
            translated = _NormFile(translated_proper_case)

            path_mapping_applied = False

            if IS_WINDOWS:
                if translated.lower() != translated_proper_case.lower():
                    translated_proper_case = translated
                    if DEBUG_CLIENT_SERVER_TRANSLATION:
                        pydev_log.critical(
                            'pydev debugger: _NormFile changed path (from: %s to %s)',
                            translated_proper_case, translated)

            for i, (eclipse_prefix,
                    python_prefix) in enumerate(paths_from_eclipse_to_python):
                if translated.startswith(python_prefix):
                    if DEBUG_CLIENT_SERVER_TRANSLATION:
                        pydev_log.critical(
                            'pydev debugger: replacing to client: %s',
                            translated)

                    # Note: use the non-normalized version.
                    eclipse_prefix = initial_paths[i][0]
                    translated = eclipse_prefix + translated_proper_case[
                        len(python_prefix):]
                    if DEBUG_CLIENT_SERVER_TRANSLATION:
                        pydev_log.critical(
                            'pydev debugger: sent to client: %s', translated)
                    path_mapping_applied = True
                    break
            else:
                if DEBUG_CLIENT_SERVER_TRANSLATION:
                    pydev_log.critical(
                        'pydev debugger: to client: unable to find matching prefix for: %s in %s',
                        translated,
                        [x[1] for x in paths_from_eclipse_to_python])
                    translated = translated_proper_case

            if eclipse_sep != python_sep:
                translated = translated.replace(python_sep, eclipse_sep)

            translated = _path_to_expected_str(translated)

            # The resulting path is not in the python process, so, we cannot do a _NormFile here,
            # only at the beginning of this method.
            cache[filename] = (translated, path_mapping_applied)

            if translated not in _client_filename_in_utf8_to_source_reference:
                if path_mapping_applied:
                    source_reference = 0
                else:
                    source_reference = _next_source_reference()
                    pydev_log.debug(
                        'Created source reference: %s for untranslated path: %s',
                        source_reference, filename)

                _client_filename_in_utf8_to_source_reference[
                    translated] = source_reference
                _source_reference_to_server_filename[
                    source_reference] = filename

            return (translated, path_mapping_applied)
Пример #18
0
    return library_dir


# Note: we can't call sysconfig.get_path from _NormPath (it deadlocks on Python 2.7) so, we
# need to get the library dir during module loading.
_library_dir = _get_library_dir()

# defined as a list of tuples where the 1st element of the tuple is the path in the client machine
# and the 2nd element is the path in the server machine.
# see module docstring for more details.
try:
    PATHS_FROM_ECLIPSE_TO_PYTHON = json.loads(
        os.environ.get('PATHS_FROM_ECLIPSE_TO_PYTHON', '[]'))
except Exception:
    pydev_log.critical(
        'Error loading PATHS_FROM_ECLIPSE_TO_PYTHON from environment variable.'
    )
    pydev_log.exception()
    PATHS_FROM_ECLIPSE_TO_PYTHON = []
else:
    if not isinstance(PATHS_FROM_ECLIPSE_TO_PYTHON, list):
        pydev_log.critical(
            'Expected PATHS_FROM_ECLIPSE_TO_PYTHON loaded from environment variable to be a list.'
        )
        PATHS_FROM_ECLIPSE_TO_PYTHON = []
    else:
        # Converting json lists to tuple
        PATHS_FROM_ECLIPSE_TO_PYTHON = [
            tuple(x) for x in PATHS_FROM_ECLIPSE_TO_PYTHON
        ]
Пример #19
0
def set_trace_to_threads(tracing_func):
    lib = load_python_helper_lib()
    if lib is None:  # This is the case if it's not CPython.
        pydev_log.info(
            'Unable to load helper lib to set tracing to all threads (unsupported python vm).'
        )
        return -1

    pydev_log.info(
        'Successfully Loaded helper lib to set tracing to all threads.')

    ret = 0
    set_trace_func = TracingFunctionHolder._original_tracing or sys.settrace

    # Note: use sys._current_frames() keys to get the thread ids because it'll return
    # thread ids created in C/C++ where there's user code running, unlike the APIs
    # from the threading module which see only threads created through it (unless
    # a call for threading.current_thread() was previously done in that thread,
    # in which case a dummy thread would've been created for it).
    thread_idents = set(sys._current_frames().keys())
    thread_idents = thread_idents.difference(
        # Ignore pydevd threads.
        set(t.ident for t in threading.enumerate()
            if getattr(t, 'pydev_do_not_trace', False)))

    curr_ident = thread.get_ident()
    curr_thread = threading._active.get(curr_ident)

    for thread_ident in thread_idents:
        # If that thread is not available in the threading module we also need to create a
        # dummy thread for it (otherwise it'll be invisible to the debugger).
        if thread_ident not in threading._active:

            class _DummyThread(threading._DummyThread):
                def _set_ident(self):
                    # Note: Hack to set the thread ident that we want.
                    if IS_PY2:
                        self._Thread__ident = thread_ident
                    else:
                        self._ident = thread_ident

            t = _DummyThread()
            # Reset to the base class (don't expose our own version of the class).
            t.__class__ = threading._DummyThread

            with threading._active_limbo_lock:
                # On Py2 it'll put in active getting the current indent, not using the
                # ident that was set, so, we have to update it (should be harmless on Py3
                # so, do it always).
                threading._active[thread_ident] = t
                threading._active[curr_ident] = curr_thread

                if t.ident != thread_ident:
                    # Check if it actually worked.
                    pydev_log.critical(
                        'pydevd: creation of _DummyThread with fixed thread ident did not succeed.'
                    )

        # Some (ptvsd) tests failed because of this, so, leave it always disabled for now.
        # show_debug_info = 1 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1 else 0
        show_debug_info = 0

        # Hack to increase _Py_TracingPossible.
        # See comments on py_custom_pyeval_settrace.hpp
        proceed = thread.allocate_lock()
        proceed.acquire()

        def dummy_trace(frame, event, arg):
            return dummy_trace

        def increase_tracing_count():
            SetTrace(dummy_trace)
            proceed.release()

        start_new_thread = pydev_monkey.get_original_start_new_thread(thread)
        start_new_thread(increase_tracing_count, ())
        proceed.acquire()  # Only proceed after the release() is done.
        proceed = None

        result = lib.AttachDebuggerTracing(
            ctypes.c_int(show_debug_info),
            ctypes.py_object(set_trace_func),
            ctypes.py_object(tracing_func),
            ctypes.c_uint(thread_ident),
            ctypes.py_object(None),
        )
        if result != 0:
            pydev_log.info(
                'Unable to set tracing for existing thread. Result: %s',
                result)
            ret = result

    return ret
Пример #20
0
 def on_critical(msg):
     from _pydev_bundle import pydev_log
     pydev_log.critical(msg)
Пример #21
0
def set_trace_to_threads(tracing_func,
                         thread_idents=None,
                         create_dummy_thread=True):
    assert tracing_func is not None

    ret = 0

    # Note: use sys._current_frames() keys to get the thread ids because it'll return
    # thread ids created in C/C++ where there's user code running, unlike the APIs
    # from the threading module which see only threads created through it (unless
    # a call for threading.current_thread() was previously done in that thread,
    # in which case a dummy thread would've been created for it).
    if thread_idents is None:
        thread_idents = set(sys._current_frames().keys())

        for t in threading.enumerate():
            # PY-44778: ignore pydevd threads and also add any thread that wasn't found on
            # sys._current_frames() as some existing threads may not appear in
            # sys._current_frames() but may be available through the `threading` module.
            if getattr(t, 'pydev_do_not_trace', False):
                thread_idents.discard(t.ident)
            else:
                thread_idents.add(t.ident)

    curr_ident = thread.get_ident()
    curr_thread = threading._active.get(curr_ident)

    if curr_ident in thread_idents and len(thread_idents) != 1:
        # The current thread must be updated first (because we need to set
        # the reference to `curr_thread`).
        thread_idents = list(thread_idents)
        thread_idents.remove(curr_ident)
        thread_idents.insert(0, curr_ident)

    for thread_ident in thread_idents:
        # If that thread is not available in the threading module we also need to create a
        # dummy thread for it (otherwise it'll be invisible to the debugger).
        if create_dummy_thread:
            if thread_ident not in threading._active:

                class _DummyThread(threading._DummyThread):
                    def _set_ident(self):
                        # Note: Hack to set the thread ident that we want.
                        if IS_PY2:
                            self._Thread__ident = thread_ident
                        else:
                            self._ident = thread_ident

                t = _DummyThread()
                # Reset to the base class (don't expose our own version of the class).
                t.__class__ = threading._DummyThread

                if thread_ident == curr_ident:
                    curr_thread = t

                with threading._active_limbo_lock:
                    # On Py2 it'll put in active getting the current indent, not using the
                    # ident that was set, so, we have to update it (should be harmless on Py3
                    # so, do it always).
                    threading._active[thread_ident] = t
                    threading._active[curr_ident] = curr_thread

                    if t.ident != thread_ident:
                        # Check if it actually worked.
                        pydev_log.critical(
                            'pydevd: creation of _DummyThread with fixed thread ident did not succeed.'
                        )

        # Some (ptvsd) tests failed because of this, so, leave it always disabled for now.
        # show_debug_info = 1 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1 else 0
        show_debug_info = 0

        # Hack to increase _Py_TracingPossible.
        # See comments on py_custom_pyeval_settrace.hpp
        proceed = thread.allocate_lock()
        proceed.acquire()

        def dummy_trace(frame, event, arg):
            return dummy_trace

        def increase_tracing_count():
            set_trace = TracingFunctionHolder._original_tracing or sys.settrace
            set_trace(dummy_trace)
            proceed.release()

        start_new_thread = pydev_monkey.get_original_start_new_thread(thread)
        start_new_thread(increase_tracing_count, ())
        proceed.acquire()  # Only proceed after the release() is done.
        proceed = None

        # Note: The set_trace_func is not really used anymore in the C side.
        set_trace_func = TracingFunctionHolder._original_tracing or sys.settrace

        lib = _load_python_helper_lib()
        if lib is None:  # This is the case if it's not CPython.
            pydev_log.info(
                'Unable to load helper lib to set tracing to all threads (unsupported python vm).'
            )
            ret = -1
        else:
            try:
                result = lib.AttachDebuggerTracing(
                    ctypes.c_int(show_debug_info),
                    ctypes.py_object(set_trace_func),
                    ctypes.py_object(tracing_func),
                    ctypes.c_uint(thread_ident),
                    ctypes.py_object(None),
                )
            except:
                if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1:
                    # There is no need to show this unless debug tracing is enabled.
                    pydev_log.exception('Error attaching debugger tracing')
                ret = -1
            else:
                if result != 0:
                    pydev_log.info(
                        'Unable to set tracing for existing thread. Result: %s',
                        result)
                    ret = result

    return ret
Пример #22
0
def get_python_helper_lib_filename():
    # Note: we have an independent (and similar -- but not equal) version of this method in
    # `add_code_to_python_process.py` which should be kept synchronized with this one (we do a copy
    # because the `pydevd_attach_to_process` is mostly independent and shouldn't be imported in the
    # debugger -- the only situation where it's imported is if the user actually does an attach to
    # process, through `attach_pydevd.py`, but this should usually be called from the IDE directly
    # and not from the debugger).
    libdir = os.path.join(os.path.dirname(__file__),
                          'pydevd_attach_to_process')

    arch = ''
    if IS_WINDOWS:
        # prefer not using platform.machine() when possible (it's a bit heavyweight as it may
        # spawn a subprocess).
        arch = os.environ.get("PROCESSOR_ARCHITEW6432",
                              os.environ.get('PROCESSOR_ARCHITECTURE', ''))

    if not arch:
        arch = platform.machine()
        if not arch:
            pydev_log.info('platform.machine() did not return valid value.'
                           )  # This shouldn't happen...
            return None

    if IS_WINDOWS:
        extension = '.dll'
        suffix_64 = 'amd64'
        suffix_32 = 'x86'

    elif IS_LINUX:
        extension = '.so'
        suffix_64 = 'amd64'
        suffix_32 = 'x86'

    elif IS_MAC:
        extension = '.dylib'
        suffix_64 = 'x86_64'
        suffix_32 = 'x86'

    else:
        pydev_log.info('Unable to set trace to all threads in platform: %s',
                       sys.platform)
        return None

    if arch.lower() not in ('amd64', 'x86', 'x86_64', 'i386', 'x86'):
        # We don't support this processor by default. Still, let's support the case where the
        # user manually compiled it himself with some heuristics.
        #
        # Ideally the user would provide a library in the format: "attach_<arch>.<extension>"
        # based on the way it's currently compiled -- see:
        # - windows/compile_windows.bat
        # - linux_and_mac/compile_linux.sh
        # - linux_and_mac/compile_mac.sh

        try:
            found = [
                name for name in os.listdir(libdir)
                if name.startswith('attach_') and name.endswith(extension)
            ]
        except:
            if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1:
                # There is no need to show this unless debug tracing is enabled.
                pydev_log.exception('Error listing dir: %s', libdir)
            return None

        expected_name = 'attach_' + arch + extension
        expected_name_linux = 'attach_linux_' + arch + extension

        filename = None
        if expected_name in found:  # Heuristic: user compiled with "attach_<arch>.<extension>"
            filename = os.path.join(libdir, expected_name)

        elif IS_LINUX and expected_name_linux in found:  # Heuristic: user compiled with "attach_linux_<arch>.<extension>"
            filename = os.path.join(libdir, expected_name_linux)

        elif len(
                found
        ) == 1:  # Heuristic: user removed all libraries and just left his own lib.
            filename = os.path.join(libdir, found[0])

        else:  # Heuristic: there's one additional library which doesn't seem to be our own. Find the odd one.
            filtered = [
                name for name in found
                if not name.endswith((suffix_64 + extension,
                                      suffix_32 + extension))
            ]
            if len(filtered
                   ) == 1:  # If more than one is available we can't be sure...
                filename = os.path.join(libdir, found[0])

        if filename is None:
            pydev_log.info(
                'Unable to set trace to all threads in arch: %s (did not find a %s lib in %s).',
                arch, expected_name, libdir)
            return None

        pydev_log.info('Using %s lib in arch: %s.', filename, arch)

    else:
        # Happy path for which we have pre-compiled binaries.
        if IS_64BIT_PROCESS:
            suffix = suffix_64
        else:
            suffix = suffix_32

        if IS_WINDOWS or IS_MAC:  # just the extension changes
            prefix = 'attach_'
        elif IS_LINUX:  #
            prefix = 'attach_linux_'  # historically it has a different name
        else:
            pydev_log.info(
                'Unable to set trace to all threads in platform: %s',
                sys.platform)
            return None

        filename = os.path.join(libdir, '%s%s%s' % (prefix, suffix, extension))

    if not os.path.exists(filename):
        pydev_log.critical('Expected: %s to exist.', filename)
        return None

    return filename
    def cmd_set_break(self, py_db, cmd_id, seq, text):
        # func name: 'None': match anything. Empty: match global, specified: only method context.
        # command to add some breakpoint.
        # text is filename\tline. Add to breakpoints dictionary
        suspend_policy = "NONE"  # Can be 'NONE' or 'ALL'
        is_logpoint = False
        hit_condition = None
        if py_db._set_breakpoints_with_id:
            try:
                try:
                    breakpoint_id, btype, filename, line, func_name, condition, expression, hit_condition, is_logpoint, suspend_policy = text.split('\t', 9)
                except ValueError:  # not enough values to unpack
                    # No suspend_policy passed (use default).
                    breakpoint_id, btype, filename, line, func_name, condition, expression, hit_condition, is_logpoint = text.split('\t', 8)
                is_logpoint = is_logpoint == 'True'
            except ValueError:  # not enough values to unpack
                breakpoint_id, btype, filename, line, func_name, condition, expression = text.split('\t', 6)

            breakpoint_id = int(breakpoint_id)
            line = int(line)

            # We must restore new lines and tabs as done in
            # AbstractDebugTarget.breakpointAdded
            condition = condition.replace("@_@NEW_LINE_CHAR@_@", '\n').\
                replace("@_@TAB_CHAR@_@", '\t').strip()

            expression = expression.replace("@_@NEW_LINE_CHAR@_@", '\n').\
                replace("@_@TAB_CHAR@_@", '\t').strip()
        else:
            # Note: this else should be removed after PyCharm migrates to setting
            # breakpoints by id (and ideally also provides func_name).
            btype, filename, line, func_name, suspend_policy, condition, expression = text.split('\t', 6)
            # If we don't have an id given for each breakpoint, consider
            # the id to be the line.
            breakpoint_id = line = int(line)

            condition = condition.replace("@_@NEW_LINE_CHAR@_@", '\n'). \
                replace("@_@TAB_CHAR@_@", '\t').strip()

            expression = expression.replace("@_@NEW_LINE_CHAR@_@", '\n'). \
                replace("@_@TAB_CHAR@_@", '\t').strip()

        if condition is not None and (len(condition) <= 0 or condition == "None"):
            condition = None

        if expression is not None and (len(expression) <= 0 or expression == "None"):
            expression = None

        if hit_condition is not None and (len(hit_condition) <= 0 or hit_condition == "None"):
            hit_condition = None

        filename = self.api.filename_to_server(filename)
        func_name = self.api.to_str(func_name)

        error_code = self.api.add_breakpoint(
            py_db, filename, btype, breakpoint_id, line, condition, func_name, expression, suspend_policy, hit_condition, is_logpoint)

        if error_code:
            if error_code == self.api.ADD_BREAKPOINT_FILE_NOT_FOUND:
                pydev_log.critical('pydev debugger: warning: Trying to add breakpoint to file that does not exist: %s (will have no effect).' % (filename,))

            elif error_code == self.api.ADD_BREAKPOINT_FILE_EXCLUDED_BY_FILTERS:
                pydev_log.critical('pydev debugger: warning: Trying to add breakpoint to file that is excluded by filters: %s (will have no effect).' % (filename,))

            else:
                # Shouldn't get here.
                pydev_log.critical('pydev debugger: warning: Breakpoint not validated (reason unknown -- please report as error): %s.' % (filename,))
Пример #24
0
def set_trace_to_threads(tracing_func, target_threads=None):
    if not IS_CPYTHON or ctypes is None or sys.version_info[:2] > (3, 7):
        return -1

    if IS_WINDOWS:
        if IS_64BIT_PROCESS:
            suffix = 'amd64'
        else:
            suffix = 'x86'

        filename = os.path.join(os.path.dirname(__file__),
                                'pydevd_attach_to_process',
                                'attach_%s.dll' % (suffix, ))

    elif IS_LINUX:
        if IS_64BIT_PROCESS:
            suffix = 'amd64'
        else:
            suffix = 'x86'

        filename = os.path.join(os.path.dirname(__file__),
                                'pydevd_attach_to_process',
                                'attach_linux_%s.so' % (suffix, ))

    elif IS_MAC:
        if IS_64BIT_PROCESS:
            suffix = 'x86_64.dylib'
        else:
            suffix = 'x86.dylib'

        filename = os.path.join(os.path.dirname(__file__),
                                'pydevd_attach_to_process',
                                'attach_%s' % (suffix, ))

    else:
        pydev_log.info('Unable to set trace to all threads in platform: %s',
                       sys.platform)
        return -1

    if not os.path.exists(filename):
        pydev_log.critical('Expected: %s to exist.', filename)
        return -1

    try:
        lib = ctypes.cdll.LoadLibrary(filename)
    except:
        pydev_log.exception('Error loading: %s', filename)
        return -1

    if hasattr(sys, 'getswitchinterval'):
        get_interval, set_interval = sys.getswitchinterval, sys.setswitchinterval
    else:
        get_interval, set_interval = sys.getcheckinterval, sys.setcheckinterval

    prev_value = get_interval()
    ret = 0
    try:
        # Prevent going to any other thread... if we switch the thread during this operation we
        # could potentially corrupt the interpreter.
        set_interval(2**15)

        set_trace_func = TracingFunctionHolder._original_tracing or sys.settrace

        if target_threads is None:
            target_threads = list(threading.enumerate())

        for t in target_threads:
            if t and not getattr(t, 'pydev_do_not_trace', None):
                show_debug_info = 0
                result = lib.AttachDebuggerTracing(
                    ctypes.c_int(show_debug_info),
                    ctypes.py_object(set_trace_func),
                    ctypes.py_object(tracing_func), ctypes.c_uint(t.ident))
                if result != 0:
                    pydev_log.info(
                        'Unable to set tracing for existing threads. Result: %s',
                        result)
                    ret = result
    finally:
        set_interval(prev_value)

    return ret
        library_dir = os.path.dirname(os.__file__)

    return library_dir


# Note: we can't call sysconfig.get_path from _apply_func_and_normalize_case (it deadlocks on Python 2.7) so, we
# need to get the library dir during module loading.
_library_dir = _get_library_dir()

# defined as a list of tuples where the 1st element of the tuple is the path in the client machine
# and the 2nd element is the path in the server machine.
# see module docstring for more details.
try:
    PATHS_FROM_ECLIPSE_TO_PYTHON = json.loads(os.environ.get('PATHS_FROM_ECLIPSE_TO_PYTHON', '[]'))
except Exception:
    pydev_log.critical('Error loading PATHS_FROM_ECLIPSE_TO_PYTHON from environment variable.')
    pydev_log.exception()
    PATHS_FROM_ECLIPSE_TO_PYTHON = []
else:
    if not isinstance(PATHS_FROM_ECLIPSE_TO_PYTHON, list):
        pydev_log.critical('Expected PATHS_FROM_ECLIPSE_TO_PYTHON loaded from environment variable to be a list.')
        PATHS_FROM_ECLIPSE_TO_PYTHON = []
    else:
        # Converting json lists to tuple
        PATHS_FROM_ECLIPSE_TO_PYTHON = [tuple(x) for x in PATHS_FROM_ECLIPSE_TO_PYTHON]

# example:
# PATHS_FROM_ECLIPSE_TO_PYTHON = [
#  (r'd:\temp\temp_workspace_2\test_python\src\yyy\yyy',
#   r'd:\temp\temp_workspace_2\test_python\src\hhh\xxx')
# ]
Пример #26
0
    def remove_breakpoint(self, py_db, received_filename, breakpoint_type,
                          breakpoint_id):
        '''
        :param str received_filename:
            Note: must be sent as it was received in the protocol. It may be translated in this
            function.

        :param str breakpoint_type:
            One of: 'python-line', 'django-line', 'jinja2-line'.

        :param int breakpoint_id:
        '''
        for key, val in dict_items(py_db.api_received_breakpoints):
            original_filename, existing_breakpoint_id = key
            _new_filename, _api_add_breakpoint_params = val
            if received_filename == original_filename and existing_breakpoint_id == breakpoint_id:
                del py_db.api_received_breakpoints[key]
                break
        else:
            pydev_log.info(
                'Did not find breakpoint to remove: %s (breakpoint id: %s)',
                received_filename, breakpoint_id)

        file_to_id_to_breakpoint = None
        received_filename = self.filename_to_server(received_filename)
        canonical_normalized_filename = pydevd_file_utils.canonical_normalized_path(
            received_filename)

        if breakpoint_type == 'python-line':
            breakpoints = py_db.breakpoints
            file_to_id_to_breakpoint = py_db.file_to_id_to_line_breakpoint

        elif py_db.plugin is not None:
            result = py_db.plugin.get_breakpoints(py_db, breakpoint_type)
            if result is not None:
                file_to_id_to_breakpoint = py_db.file_to_id_to_plugin_breakpoint
                breakpoints = result

        if file_to_id_to_breakpoint is None:
            pydev_log.critical(
                'Error removing breakpoint. Cannot handle breakpoint of type %s',
                breakpoint_type)

        else:
            try:
                id_to_pybreakpoint = file_to_id_to_breakpoint.get(
                    canonical_normalized_filename, {})
                if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
                    existing = id_to_pybreakpoint[breakpoint_id]
                    pydev_log.info(
                        'Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n'
                        % (canonical_normalized_filename, existing.line,
                           existing.func_name.encode('utf-8'), breakpoint_id))

                del id_to_pybreakpoint[breakpoint_id]
                py_db.consolidate_breakpoints(canonical_normalized_filename,
                                              id_to_pybreakpoint, breakpoints)
                if py_db.plugin is not None:
                    py_db.has_plugin_line_breaks = py_db.plugin.has_line_breaks(
                    )

            except KeyError:
                pydev_log.info(
                    "Error removing breakpoint: Breakpoint id not found: %s id: %s. Available ids: %s\n",
                    canonical_normalized_filename, breakpoint_id,
                    dict_keys(id_to_pybreakpoint))

        py_db.on_breakpoints_changed(removed=True)
Пример #27
0
    def add_breakpoint(self, py_db, filename, breakpoint_type, breakpoint_id,
                       line, condition, func_name, expression, suspend_policy,
                       hit_condition, is_logpoint):
        '''
        :param str filename:
            Note: must be already translated for the server.

        :param str breakpoint_type:
            One of: 'python-line', 'django-line', 'jinja2-line'.

        :param int breakpoint_id:

        :param int line:

        :param condition:
            Either None or the condition to activate the breakpoint.

        :param str func_name:
            If "None" (str), may hit in any context.
            Empty string will hit only top level.
            Any other value must match the scope of the method to be matched.

        :param str expression:
            None or the expression to be evaluated.

        :param suspend_policy:
            Either "NONE" (to suspend only the current thread when the breakpoint is hit) or
            "ALL" (to suspend all threads when a breakpoint is hit).

        :param str hit_condition:
            An expression where `@HIT@` will be replaced by the number of hits.
            i.e.: `@HIT@ == x` or `@HIT@ >= x`

        :param bool is_logpoint:
            If True and an expression is passed, pydevd will create an io message command with the
            result of the evaluation.
        '''
        assert filename.__class__ == str  # i.e.: bytes on py2 and str on py3
        assert func_name.__class__ == str  # i.e.: bytes on py2 and str on py3

        if not pydevd_file_utils.exists(filename):
            pydev_log.critical('pydev debugger: warning: trying to add breakpoint'\
                ' to file that does not exist: %s (will have no effect)\n' % (filename,))
            return

        if breakpoint_type == 'python-line':
            added_breakpoint = LineBreakpoint(line,
                                              condition,
                                              func_name,
                                              expression,
                                              suspend_policy,
                                              hit_condition=hit_condition,
                                              is_logpoint=is_logpoint)
            breakpoints = py_db.breakpoints
            file_to_id_to_breakpoint = py_db.file_to_id_to_line_breakpoint
            supported_type = True

        else:
            result = None
            plugin = py_db.get_plugin_lazy_init()
            if plugin is not None:
                result = plugin.add_breakpoint('add_line_breakpoint',
                                               py_db,
                                               breakpoint_type,
                                               filename,
                                               line,
                                               condition,
                                               expression,
                                               func_name,
                                               hit_condition=hit_condition,
                                               is_logpoint=is_logpoint)
            if result is not None:
                supported_type = True
                added_breakpoint, breakpoints = result
                file_to_id_to_breakpoint = py_db.file_to_id_to_plugin_breakpoint
            else:
                supported_type = False

        if not supported_type:
            raise NameError(breakpoint_type)

        if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
            pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n',
                            filename, line, func_name)

        if filename in file_to_id_to_breakpoint:
            id_to_pybreakpoint = file_to_id_to_breakpoint[filename]
        else:
            id_to_pybreakpoint = file_to_id_to_breakpoint[filename] = {}

        id_to_pybreakpoint[breakpoint_id] = added_breakpoint
        py_db.consolidate_breakpoints(filename, id_to_pybreakpoint,
                                      breakpoints)
        if py_db.plugin is not None:
            py_db.has_plugin_line_breaks = py_db.plugin.has_line_breaks()

        py_db.on_breakpoints_changed()
Пример #28
0
    def add_breakpoint(
            self, py_db, filename, breakpoint_type, breakpoint_id, line, condition, func_name, expression, suspend_policy, hit_condition, is_logpoint):
        '''
        :param str filename:
            Note: must be already translated for the server.

        :param str breakpoint_type:
            One of: 'python-line', 'django-line', 'jinja2-line'.

        :param int breakpoint_id:

        :param int line:

        :param condition:
            Either None or the condition to activate the breakpoint.

        :param str func_name:
            If "None" (str), may hit in any context.
            Empty string will hit only top level.
            Any other value must match the scope of the method to be matched.

        :param str expression:
            None or the expression to be evaluated.

        :param suspend_policy:
            Either "NONE" (to suspend only the current thread when the breakpoint is hit) or
            "ALL" (to suspend all threads when a breakpoint is hit).

        :param str hit_condition:
            An expression where `@HIT@` will be replaced by the number of hits.
            i.e.: `@HIT@ == x` or `@HIT@ >= x`

        :param bool is_logpoint:
            If True and an expression is passed, pydevd will create an io message command with the
            result of the evaluation.
        '''
        assert filename.__class__ == str  # i.e.: bytes on py2 and str on py3
        assert func_name.__class__ == str  # i.e.: bytes on py2 and str on py3

        if not pydevd_file_utils.exists(filename):
            pydev_log.critical('pydev debugger: warning: trying to add breakpoint'\
                ' to file that does not exist: %s (will have no effect)\n' % (filename,))
            return

        if breakpoint_type == 'python-line':
            added_breakpoint = LineBreakpoint(line, condition, func_name, expression, suspend_policy, hit_condition=hit_condition, is_logpoint=is_logpoint)
            breakpoints = py_db.breakpoints
            file_to_id_to_breakpoint = py_db.file_to_id_to_line_breakpoint
            supported_type = True

        else:
            result = None
            plugin = py_db.get_plugin_lazy_init()
            if plugin is not None:
                result = plugin.add_breakpoint('add_line_breakpoint', py_db, breakpoint_type, filename, line, condition, expression, func_name, hit_condition=hit_condition, is_logpoint=is_logpoint)
            if result is not None:
                supported_type = True
                added_breakpoint, breakpoints = result
                file_to_id_to_breakpoint = py_db.file_to_id_to_plugin_breakpoint
            else:
                supported_type = False

        if not supported_type:
            raise NameError(breakpoint_type)

        if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
            pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n', filename, line, func_name)

        if filename in file_to_id_to_breakpoint:
            id_to_pybreakpoint = file_to_id_to_breakpoint[filename]
        else:
            id_to_pybreakpoint = file_to_id_to_breakpoint[filename] = {}

        id_to_pybreakpoint[breakpoint_id] = added_breakpoint
        py_db.consolidate_breakpoints(filename, id_to_pybreakpoint, breakpoints)
        if py_db.plugin is not None:
            py_db.has_plugin_line_breaks = py_db.plugin.has_line_breaks()

        py_db.on_breakpoints_changed()