def get_debug_tool_name(self, custom=None): debug_tools = self._manifest.get("debug", {}).get("tools") tool_name = custom if tool_name == "custom": return tool_name if not debug_tools: raise exception.DebugSupportError(self._manifest['name']) if tool_name: if tool_name in debug_tools: return tool_name raise exception.DebugInvalidOptions( "Unknown debug tool `%s`. Please use one of `%s` or `custom`" % (tool_name, ", ".join(sorted(list(debug_tools))))) # automatically select best tool data = {"default": [], "onboard": [], "external": []} for key, value in debug_tools.items(): if value.get("default"): data['default'].append(key) elif value.get("onboard"): data['onboard'].append(key) data['external'].append(key) for key, value in data.items(): if not value: continue return sorted(value)[0] assert any(item for item in data)
def spawn(self, gdb_path, prog_path): session_hash = gdb_path + prog_path self._session_id = sha1(hashlib_encode_data(session_hash)).hexdigest() self._kill_previous_session() patterns = { "PROJECT_DIR": self.project_dir, "PROG_PATH": prog_path, "PROG_DIR": dirname(prog_path), "PROG_NAME": basename(splitext(prog_path)[0]), "DEBUG_PORT": self.debug_options["port"], "UPLOAD_PROTOCOL": self.debug_options["upload_protocol"], "INIT_BREAK": self.debug_options["init_break"] or "", "LOAD_CMDS": "\n".join(self.debug_options["load_cmds"] or []), } self._debug_server.spawn(patterns) if not patterns["DEBUG_PORT"]: patterns["DEBUG_PORT"] = self._debug_server.get_debug_port() self.generate_pioinit(self._gdbsrc_dir, patterns) # start GDB client args = [ "piogdb", "-q", "--directory", self._gdbsrc_dir, "--directory", self.project_dir, "-l", "10", ] args.extend(self.args) if not gdb_path: raise exception.DebugInvalidOptions("GDB client is not configured") gdb_data_dir = self._get_data_dir(gdb_path) if gdb_data_dir: args.extend(["--data-directory", gdb_data_dir]) args.append(patterns["PROG_PATH"]) return reactor.spawnProcess(self, gdb_path, args, path=self.project_dir, env=os.environ)
def reveal_debug_port(env_debug_port, tool_name, tool_settings): def _get_pattern(): if not env_debug_port: return None if set(["*", "?", "[", "]"]) & set(env_debug_port): return env_debug_port return None def _is_match_pattern(port): pattern = _get_pattern() if not pattern: return True return fnmatch(port, pattern) def _look_for_serial_port(hwids): for item in util.get_serialports(filter_hwid=True): if not _is_match_pattern(item['port']): continue port = item['port'] if tool_name.startswith("blackmagic"): if "windows" in util.get_systype() and \ port.startswith("COM") and len(port) > 4: port = "\\\\.\\%s" % port if "GDB" in item['description']: return port for hwid in hwids: hwid_str = ("%s:%s" % (hwid[0], hwid[1])).replace("0x", "") if hwid_str in item['hwid']: return port return None if env_debug_port and not _get_pattern(): return env_debug_port if not tool_settings.get("require_debug_port"): return None debug_port = _look_for_serial_port(tool_settings.get("hwids", [])) if not debug_port: raise exception.DebugInvalidOptions( "Please specify `debug_port` for environment") return debug_port
def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unprocessed): # use env variables from Eclipse or CLion for sysenv in ("CWD", "PWD", "PLATFORMIO_PROJECT_DIR"): if is_platformio_project(project_dir): break if os.getenv(sysenv): project_dir = os.getenv(sysenv) with util.cd(project_dir): config = ProjectConfig.get_instance( project_conf or join(project_dir, "platformio.ini")) config.validate(envs=[environment] if environment else None) env_name = environment or helpers.get_default_debug_env(config) env_options = config.items(env=env_name, as_dict=True) if not set(env_options.keys()) >= set(["platform", "board"]): raise exception.ProjectEnvsNotAvailable() debug_options = helpers.validate_debug_options(ctx, env_options) assert debug_options if not interface: return helpers.predebug_project(ctx, project_dir, env_name, False, verbose) configuration = load_project_ide_data(project_dir, env_name) if not configuration: raise exception.DebugInvalidOptions( "Could not load debug configuration") if "--version" in __unprocessed: result = util.exec_command([configuration['gdb_path'], "--version"]) if result['returncode'] == 0: return click.echo(result['out']) raise exception.PlatformioException("\n".join( [result['out'], result['err']])) try: util.ensure_udev_rules() except NameError: pass except exception.InvalidUdevRules as e: for line in str(e).split("\n") + [""]: click.echo( ('~"%s\\n"' if helpers.is_mi_mode(__unprocessed) else "%s") % line) debug_options['load_cmds'] = helpers.configure_esp32_load_cmds( debug_options, configuration) rebuild_prog = False preload = debug_options['load_cmds'] == ["preload"] load_mode = debug_options['load_mode'] if load_mode == "always": rebuild_prog = ( preload or not helpers.has_debug_symbols(configuration['prog_path'])) elif load_mode == "modified": rebuild_prog = ( helpers.is_prog_obsolete(configuration['prog_path']) or not helpers.has_debug_symbols(configuration['prog_path'])) else: rebuild_prog = not isfile(configuration['prog_path']) if preload or (not rebuild_prog and load_mode != "always"): # don't load firmware through debug server debug_options['load_cmds'] = [] if rebuild_prog: if helpers.is_mi_mode(__unprocessed): click.echo('~"Preparing firmware for debugging...\\n"') output = helpers.GDBBytesIO() with util.capture_std_streams(output): helpers.predebug_project(ctx, project_dir, env_name, preload, verbose) output.close() else: click.echo("Preparing firmware for debugging...") helpers.predebug_project(ctx, project_dir, env_name, preload, verbose) # save SHA sum of newly created prog if load_mode == "modified": helpers.is_prog_obsolete(configuration['prog_path']) if not isfile(configuration['prog_path']): raise exception.DebugInvalidOptions("Program/firmware is missed") # run debugging client inject_contrib_pysite() from platformio.commands.debug.client import GDBClient, reactor client = GDBClient(project_dir, __unprocessed, debug_options, env_options) client.spawn(configuration['gdb_path'], configuration['prog_path']) signal.signal(signal.SIGINT, lambda *args, **kwargs: None) reactor.run() return True
def spawn(self, patterns): # pylint: disable=too-many-branches systype = util.get_systype() server = self.debug_options.get("server") if not server: return None server = self.apply_patterns(server, patterns) server_executable = server['executable'] if not server_executable: return None if server['cwd']: server_executable = join(server['cwd'], server_executable) if ("windows" in systype and not server_executable.endswith(".exe") and isfile(server_executable + ".exe")): server_executable = server_executable + ".exe" if not isfile(server_executable): server_executable = where_is_program(server_executable) if not isfile(server_executable): raise exception.DebugInvalidOptions( "\nCould not launch Debug Server '%s'. Please check that it " "is installed and is included in a system PATH\n\n" "See documentation or contact [email protected]:\n" "http://docs.platformio.org/page/plus/debugging.html\n" % server_executable) self._debug_port = ":3333" openocd_pipe_allowed = all([ not self.debug_options['port'], "openocd" in server_executable ]) # yapf: disable if openocd_pipe_allowed: args = [] if server['cwd']: args.extend(["-s", server['cwd']]) args.extend([ "-c", "gdb_port pipe; tcl_port disabled; telnet_port disabled" ]) args.extend(server['arguments']) str_args = " ".join( [arg if arg.startswith("-") else '"%s"' % arg for arg in args]) self._debug_port = '| "%s" %s' % (server_executable, str_args) self._debug_port = self._debug_port.replace("\\", "\\\\") else: env = os.environ.copy() # prepend server "lib" folder to LD path if ("windows" not in systype and server['cwd'] and isdir(join(server['cwd'], "lib"))): ld_key = ("DYLD_LIBRARY_PATH" if "darwin" in systype else "LD_LIBRARY_PATH") env[ld_key] = join(server['cwd'], "lib") if os.environ.get(ld_key): env[ld_key] = "%s:%s" % (env[ld_key], os.environ.get(ld_key)) # prepend BIN to PATH if server['cwd'] and isdir(join(server['cwd'], "bin")): env['PATH'] = "%s%s%s" % ( join(server['cwd'], "bin"), os.pathsep, os.environ.get("PATH", os.environ.get("Path", ""))) self._transport = reactor.spawnProcess( self, server_executable, [server_executable] + server['arguments'], path=server['cwd'], env=env) if "mspdebug" in server_executable.lower(): self._debug_port = ":2000" elif "jlink" in server_executable.lower(): self._debug_port = ":2331" elif "qemu" in server_executable.lower(): self._debug_port = ":1234" return self._transport
def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unprocessed): app.set_session_var("custom_project_conf", project_conf) # use env variables from Eclipse or CLion for sysenv in ("CWD", "PWD", "PLATFORMIO_PROJECT_DIR"): if is_platformio_project(project_dir): break if os.getenv(sysenv): project_dir = os.getenv(sysenv) with fs.cd(project_dir): config = ProjectConfig.get_instance(project_conf) config.validate(envs=[environment] if environment else None) env_name = environment or helpers.get_default_debug_env(config) env_options = config.items(env=env_name, as_dict=True) if not set(env_options.keys()) >= set(["platform", "board"]): raise exception.ProjectEnvsNotAvailable() debug_options = helpers.validate_debug_options(ctx, env_options) assert debug_options if not interface: return helpers.predebug_project(ctx, project_dir, env_name, False, verbose) configuration = load_project_ide_data(project_dir, env_name) if not configuration: raise exception.DebugInvalidOptions( "Could not load debug configuration") if "--version" in __unprocessed: result = proc.exec_command([configuration["gdb_path"], "--version"]) if result["returncode"] == 0: return click.echo(result["out"]) raise exception.PlatformioException("\n".join( [result["out"], result["err"]])) try: fs.ensure_udev_rules() except exception.InvalidUdevRules as e: click.echo( helpers.escape_gdbmi_stream("~", str(e) + "\n") if helpers.is_gdbmi_mode() else str(e) + "\n", nl=False, ) debug_options["load_cmds"] = helpers.configure_esp32_load_cmds( debug_options, configuration) rebuild_prog = False preload = debug_options["load_cmds"] == ["preload"] load_mode = debug_options["load_mode"] if load_mode == "always": rebuild_prog = preload or not helpers.has_debug_symbols( configuration["prog_path"]) elif load_mode == "modified": rebuild_prog = helpers.is_prog_obsolete( configuration["prog_path"]) or not helpers.has_debug_symbols( configuration["prog_path"]) else: rebuild_prog = not isfile(configuration["prog_path"]) if preload or (not rebuild_prog and load_mode != "always"): # don't load firmware through debug server debug_options["load_cmds"] = [] if rebuild_prog: if helpers.is_gdbmi_mode(): click.echo( helpers.escape_gdbmi_stream( "~", "Preparing firmware for debugging...\n"), nl=False, ) stream = helpers.GDBMIConsoleStream() with util.capture_std_streams(stream): helpers.predebug_project(ctx, project_dir, env_name, preload, verbose) stream.close() else: click.echo("Preparing firmware for debugging...") helpers.predebug_project(ctx, project_dir, env_name, preload, verbose) # save SHA sum of newly created prog if load_mode == "modified": helpers.is_prog_obsolete(configuration["prog_path"]) if not isfile(configuration["prog_path"]): raise exception.DebugInvalidOptions("Program/firmware is missed") # run debugging client inject_contrib_pysite() # pylint: disable=import-outside-toplevel from platformio.commands.debug.client import GDBClient, reactor client = GDBClient(project_dir, __unprocessed, debug_options, env_options) client.spawn(configuration["gdb_path"], configuration["prog_path"]) signal.signal(signal.SIGINT, lambda *args, **kwargs: None) reactor.run() return True