diff --git a/tools/idf_monitor.py b/tools/idf_monitor.py index e1a9164b5c..465db7ad32 100755 --- a/tools/idf_monitor.py +++ b/tools/idf_monitor.py @@ -140,6 +140,8 @@ class Monitor(object): self._decode_panic = decode_panic self._reading_panic = PANIC_IDLE self._panic_buffer = b'' + self.gdb_exit = False + self.start_cmd_sent = False def invoke_processing_last_line(self): # type: () -> None @@ -149,43 +151,70 @@ class Monitor(object): # type: () -> None self.console_reader.start() self.serial_reader.start() + self.gdb_exit = False + self.start_cmd_sent = False try: while self.console_reader.alive and self.serial_reader.alive: try: - item = self.cmd_queue.get_nowait() - except queue.Empty: - try: - item = self.event_queue.get(True, 0.03) - except queue.Empty: - continue + if self.gdb_exit is True: + self.gdb_exit = False + + time.sleep(0.3) + try: + # Continue the program after exit from the GDB + self.serial.write(codecs.encode('+$c#63')) + self.start_cmd_sent = True + except serial.SerialException: + pass # this shouldn't happen, but sometimes port has closed in serial thread + except UnicodeEncodeError: + pass # this can happen if a non-ascii character was passed, ignoring - event_tag, data = item - if event_tag == TAG_CMD: - self.handle_commands(data, self.target) - elif event_tag == TAG_KEY: try: - self.serial.write(codecs.encode(data)) + item = self.cmd_queue.get_nowait() + except queue.Empty: + try: + item = self.event_queue.get(True, 0.03) + except queue.Empty: + continue + + (event_tag, data) = item + + if event_tag == TAG_CMD: + self.handle_commands(data, self.target) + elif event_tag == TAG_KEY: + try: + self.serial.write(codecs.encode(data)) + except serial.SerialException: + pass # this shouldn't happen, but sometimes port has closed in serial thread + except UnicodeEncodeError: + pass # this can happen if a non-ascii character was passed, ignoring + elif event_tag == TAG_SERIAL: + self.handle_serial_input(data) + if self._invoke_processing_last_line_timer is not None: + self._invoke_processing_last_line_timer.cancel() + self._invoke_processing_last_line_timer = threading.Timer(0.1, self.invoke_processing_last_line) + self._invoke_processing_last_line_timer.start() + # If no futher data is received in the next short period + # of time then the _invoke_processing_last_line_timer + # generates an event which will result in the finishing of + # the last line. This is fix for handling lines sent + # without EOL. + elif event_tag == TAG_SERIAL_FLUSH: + self.handle_serial_input(data, finalize_line=True) + else: + raise RuntimeError("Bad event data %r" % ((event_tag,data),)) + except KeyboardInterrupt: + try: + yellow_print("To exit from IDF monitor please use \"Ctrl+]\"") + self.serial.write(codecs.encode('\x03')) except serial.SerialException: pass # this shouldn't happen, but sometimes port has closed in serial thread except UnicodeEncodeError: pass # this can happen if a non-ascii character was passed, ignoring - elif event_tag == TAG_SERIAL: - self.handle_serial_input(data) - if self._invoke_processing_last_line_timer is not None: - self._invoke_processing_last_line_timer.cancel() - self._invoke_processing_last_line_timer = threading.Timer(0.1, self.invoke_processing_last_line) - self._invoke_processing_last_line_timer.start() - # If no further data is received in the next short period - # of time then the _invoke_processing_last_line_timer - # generates an event which will result in the finishing of - # the last line. This is fix for handling lines sent - # without EOL. - elif event_tag == TAG_SERIAL_FLUSH: - self.handle_serial_input(data, finalize_line=True) - else: - raise RuntimeError('Bad event data %r' % ((event_tag, data),)) except SerialStopException: normal_print('Stopping condition has been received\n') + except KeyboardInterrupt: + pass finally: try: self.console_reader.stop() @@ -200,6 +229,13 @@ class Monitor(object): def handle_serial_input(self, data, finalize_line=False): # type: (bytes, bool) -> None + # Remove "+" after Continue command + if self.start_cmd_sent is True: + self.start_cmd_sent = False + pos = data.find(b"+") + if pos != -1: + data = data[1:] + sp = data.split(b'\n') if self._last_line_part != b'': # add unprocessed part from previous "data" to the first line @@ -260,6 +296,7 @@ class Monitor(object): def __exit__(self, *args, **kwargs): # type: ignore """ Use 'with self' to temporarily disable monitoring behaviour """ self.console_reader.start() + self.serial_reader.gdb_exit = self.gdb_exit # write gdb_exit flag self.serial_reader.start() def prompt_next_action(self, reason): # type: (str) -> None @@ -480,10 +517,25 @@ class Monitor(object): cmd = ['%sgdb' % self.toolchain_prefix, '-ex', 'set serial baud %d' % self.serial.baudrate, '-ex', 'target remote %s' % self.serial.port, - '-ex', 'interrupt', # monitor has already parsed the first 'reason' command, need a second self.elf_file] - process = subprocess.Popen(cmd, cwd='.') - process.wait() + + # Here we handling GDB as a process + if True: + # Open GDB process + try: + process = subprocess.Popen(cmd, cwd=".") + except KeyboardInterrupt: + pass + + # We ignore Ctrl+C interrupt form external process abd wait responce util GDB will be finished. + while True: + try: + process.wait() + break + except KeyboardInterrupt: + pass # We ignore the Ctrl+C + self.gdb_exit = True + except OSError as e: red_print('%s: %s' % (' '.join(cmd), e)) except KeyboardInterrupt: @@ -499,7 +551,6 @@ class Monitor(object): subprocess.call(['stty', 'sane']) except Exception: pass # don't care if there's no stty, we tried... - self.prompt_next_action('gdb exited') def output_enable(self, enable): # type: (bool) -> None self._output_enabled = enable @@ -733,6 +784,8 @@ def main(): # type: () -> None yellow_print('--- Print filter: {} ---'.format(args.print_filter)) monitor.main_loop() + except KeyboardInterrupt: + pass finally: if ws: ws.close() diff --git a/tools/idf_monitor_base/serial_reader.py b/tools/idf_monitor_base/serial_reader.py index 29e26860b0..c3b7a60bf0 100644 --- a/tools/idf_monitor_base/serial_reader.py +++ b/tools/idf_monitor_base/serial_reader.py @@ -38,6 +38,7 @@ class SerialReader(StoppableThread): self.baud = serial_instance.baudrate self.serial = serial_instance self.event_queue = event_queue + self.gdb_exit = False if not hasattr(self.serial, 'cancel_read'): # enable timeout for checking alive flag, # if cancel_read not available @@ -47,10 +48,27 @@ class SerialReader(StoppableThread): # type: () -> None if not self.serial.is_open: self.serial.baudrate = self.baud - self.serial.rts = True # Force an RTS reset on open + # We can come to this thread at startup or from external application line GDB. + # If we come from GDB we would like to continue to run without reset. + if self.gdb_exit is False: + # This sequence of DTR/RTS and open/close set the serial port to + # condition when GDB not make reset of the target by switching DTR/RTS. + self.serial.rts = True # IO0=LOW + self.serial.dtr = self.serial.dtr # usbser.sys workaround + self.serial.open() + self.serial.close() + self.serial.rts = False # IO0=HIGH + self.serial.dtr = False + else: # if we exit from GDB, we don't need to reset the target + self.serial.rts = False + self.serial.dtr = True + + # Current state not reset the target! + self.gdb_exit = False self.serial.open() - self.serial.rts = False - self.serial.dtr = self.serial.dtr # usbser.sys workaround + time.sleep(0.005) # Add a delay to meet the requirements of minimal EN low time (2ms for ESP32-C3) + self.serial.rts = False # Set rts/dtr to the working state + self.serial.dtr = self.serial.dtr # usbser.sys workaround try: while self.alive: try: