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.process import BaseProcess
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.telemetry import MeasurementProtocol
@ -223,10 +223,9 @@ class GDBClient(BaseProcess): # pylint: disable=too-many-instance-attributes
self._handle_error(data)
def console_log(self, msg):
if helpers.is_mi_mode(self.args):
self.outReceived(('~"%s\\n"\n' % msg).encode())
else:
self.outReceived(("%s\n" % msg).encode())
if helpers.is_gdbmi_mode():
msg = helpers.escape_gdbmi_stream("~", msg)
self.outReceived(msg if is_bytes(msg) else msg.encode())
def _auto_exec_continue(self):
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:
return
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.console_log(
"PlatformIO: More configuration options -> http://bit.ly/pio-debug"
"PlatformIO: More configuration options -> http://bit.ly/pio-debug\n"
)
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

View File

@ -90,10 +90,12 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
try:
fs.ensure_udev_rules()
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
)
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
@ -118,12 +120,17 @@ def cli(ctx, project_dir, project_conf, environment, verbose, interface, __unpro
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):
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)
output.close()
stream.close()
else:
click.echo("Preparing firmware for debugging...")
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 platformio import exception, fs, util
from platformio.commands import PlatformioCLI
from platformio.commands.platform import platform_install as cmd_platform_install
from platformio.commands.run.command import cli as cmd_run
from platformio.compat import is_bytes
from platformio.managers.platform import PlatformFactory
from platformio.project.config import ProjectConfig
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
@staticmethod
def escape(text):
return re.sub(r"\\+", "\\\\\\\\", text)
def write(self, text):
if "\n" in 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.write(escape_gdbmi_stream("~", text))
self.STDOUT.flush()
def is_mi_mode(args):
return "--interpreter" in " ".join(args)
def is_gdbmi_mode():
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):

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 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.proc import where_is_program
@ -119,6 +120,11 @@ class DebugServer(BaseProcess):
def get_debug_port(self):
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):
self._process_ended = True
super(DebugServer, self).processEnded(reason)