Fix issue with GDB/MI Stream Records for PIO Debugger

This commit is contained in:
Ivan Kravets
2019-11-06 22:30:58 +02:00
parent 6b44a8ae75
commit 67aea4db3f
4 changed files with 55 additions and 29 deletions

View File

@ -30,7 +30,7 @@ from platformio import app, exception, fs, proc, util
from platformio.commands.debug import helpers, initcfgs from platformio.commands.debug import helpers, initcfgs
from platformio.commands.debug.process import BaseProcess from platformio.commands.debug.process import BaseProcess
from platformio.commands.debug.server import DebugServer from platformio.commands.debug.server import DebugServer
from platformio.compat import hashlib_encode_data from platformio.compat import hashlib_encode_data, is_bytes
from platformio.project.helpers import get_project_cache_dir from platformio.project.helpers import get_project_cache_dir
from platformio.telemetry import MeasurementProtocol from platformio.telemetry import MeasurementProtocol
@ -223,10 +223,9 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes
self._handle_error(data) self._handle_error(data)
def console_log(self, msg): def console_log(self, msg):
if helpers.is_mi_mode(self.args): if helpers.is_gdbmi_mode():
self.outReceived(('~"%s\\n"\n' % msg).encode()) msg = helpers.escape_gdbmi_stream("~", msg)
else: self.outReceived(msg if is_bytes(msg) else msg.encode())
self.outReceived(("%s\n" % msg).encode())
def _auto_exec_continue(self): def _auto_exec_continue(self):
auto_exec_delay = 0.5 # in seconds auto_exec_delay = 0.5 # in seconds
@ -239,14 +238,14 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes
if not self.debug_options["init_break"] or self._target_is_run: if not self.debug_options["init_break"] or self._target_is_run:
return return
self.console_log( self.console_log(
"PlatformIO: Resume the execution to `debug_init_break = %s`" "PlatformIO: Resume the execution to `debug_init_break = %s`\n"
% self.debug_options["init_break"] % self.debug_options["init_break"]
) )
self.console_log( self.console_log(
"PlatformIO: More configuration options -> http://bit.ly/pio-debug" "PlatformIO: More configuration options -> http://bit.ly/pio-debug\n"
) )
self.transport.write( self.transport.write(
b"0-exec-continue\n" if helpers.is_mi_mode(self.args) else b"continue\n" b"0-exec-continue\n" if helpers.is_gdbmi_mode() else b"continue\n"
) )
self._target_is_run = True self._target_is_run = True

View File

@ -90,10 +90,12 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
try: try:
fs.ensure_udev_rules() fs.ensure_udev_rules()
except exception.InvalidUdevRules as e: except exception.InvalidUdevRules as e:
for line in str(e).split("\n") + [""]: click.echo(
click.echo( helpers.escape_gdbmi_stream("~", str(e) + "\n")
('~"%s\\n"' if helpers.is_mi_mode(__unprocessed) else "%s") % line if helpers.is_gdbmi_mode()
) else str(e) + "\n",
nl=False,
)
debug_options["load_cmds"] = helpers.configure_esp32_load_cmds( debug_options["load_cmds"] = helpers.configure_esp32_load_cmds(
debug_options, configuration debug_options, configuration
@ -118,12 +120,17 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
debug_options["load_cmds"] = [] debug_options["load_cmds"] = []
if rebuild_prog: if rebuild_prog:
if helpers.is_mi_mode(__unprocessed): if helpers.is_gdbmi_mode():
click.echo('~"Preparing firmware for debugging...\\n"') click.echo(
output = helpers.GDBBytesIO() helpers.escape_gdbmi_stream(
with util.capture_std_streams(output): "~", "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) helpers.predebug_project(ctx, project_dir, env_name, preload, verbose)
output.close() stream.close()
else: else:
click.echo("Preparing firmware for debugging...") click.echo("Preparing firmware for debugging...")
helpers.predebug_project(ctx, project_dir, env_name, preload, verbose) helpers.predebug_project(ctx, project_dir, env_name, preload, verbose)

View File

@ -21,32 +21,46 @@ from io import BytesIO
from os.path import isfile from os.path import isfile
from platformio import exception, fs, util from platformio import exception, fs, util
from platformio.commands import PlatformioCLI
from platformio.commands.platform import platform_install as cmd_platform_install from platformio.commands.platform import platform_install as cmd_platform_install
from platformio.commands.run.command import cli as cmd_run from platformio.commands.run.command import cli as cmd_run
from platformio.compat import is_bytes
from platformio.managers.platform import PlatformFactory from platformio.managers.platform import PlatformFactory
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
from platformio.project.options import ProjectOptions from platformio.project.options import ProjectOptions
class GDBBytesIO(BytesIO): # pylint: disable=too-few-public-methods class GDBMIConsoleStream(BytesIO): # pylint: disable=too-few-public-methods
STDOUT = sys.stdout STDOUT = sys.stdout
@staticmethod
def escape(text):
return re.sub(r"\\+", "\\\\\\\\", text)
def write(self, text): def write(self, text):
if "\n" in text: self.STDOUT.write(escape_gdbmi_stream("~", text))
for line in text.strip().split("\n"):
self.STDOUT.write('~"%s\\n"\n' % self.escape(line))
else:
self.STDOUT.write('~"%s"' % self.escape(text))
self.STDOUT.flush() self.STDOUT.flush()
def is_mi_mode(args): def is_gdbmi_mode():
return "--interpreter" in " ".join(args) return "--interpreter" in " ".join(PlatformioCLI.leftover_args)
def escape_gdbmi_stream(prefix, stream):
bytes_stream = False
if is_bytes(stream):
bytes_stream = True
stream = stream.decode()
if not stream:
return b"" if bytes_stream else ""
ends_nl = stream.endswith("\n")
stream = re.sub(r"\\+", "\\\\\\\\", stream)
stream = stream.replace('"', '\\"')
stream = stream.replace("\n", "\\n")
stream = '%s"%s"' % (prefix, stream)
if ends_nl:
stream += "\n"
return stream.encode() if bytes_stream else stream
def get_default_debug_env(config): def get_default_debug_env(config):

View File

@ -19,6 +19,7 @@ from twisted.internet import error # pylint: disable=import-error
from twisted.internet import reactor # pylint: disable=import-error from twisted.internet import reactor # pylint: disable=import-error
from platformio import exception, fs, util from platformio import exception, fs, util
from platformio.commands.debug.helpers import escape_gdbmi_stream, is_gdbmi_mode
from platformio.commands.debug.process import BaseProcess from platformio.commands.debug.process import BaseProcess
from platformio.proc import where_is_program from platformio.proc import where_is_program
@ -119,6 +120,11 @@ class DebugServer(BaseProcess):
def get_debug_port(self): def get_debug_port(self):
return self._debug_port return self._debug_port
def outReceived(self, data):
super(DebugServer, self).outReceived(
escape_gdbmi_stream("@", data) if is_gdbmi_mode() else data
)
def processEnded(self, reason): def processEnded(self, reason):
self._process_ended = True self._process_ended = True
super(DebugServer, self).processEnded(reason) super(DebugServer, self).processEnded(reason)