def main_loop(self) -> None: self._pre_start() try: while self.console_reader.alive and self.serial_reader.alive: try: self._main_loop() except KeyboardInterrupt: yellow_print( 'To exit from IDF monitor please use \"Ctrl+]\"') self.serial_write(codecs.encode(CTRL_C)) except SerialStopException: normal_print('Stopping condition has been received\n') except KeyboardInterrupt: pass finally: try: self.console_reader.stop() self.serial_reader.stop() self.logger.stop_logging() # Cancelling _invoke_processing_last_line_timer is not # important here because receiving empty data doesn't matter. self._invoke_processing_last_line_timer = None except Exception: # noqa pass normal_print('\n')
def start_logging(self): # type: () -> None if not self._log_file: name = 'log.{}.{}.txt'.format( os.path.splitext(os.path.basename(self.elf_file))[0], datetime.datetime.now().strftime('%Y%m%d%H%M%S')) try: self._log_file = open(name, 'wb+') yellow_print('\nLogging is enabled into file {}'.format(name)) except Exception as e: red_print('\nLog file {} cannot be created: {}'.format( name, e))
def stop_logging(self): # type: () -> None if self._log_file: try: name = self._log_file.name self._log_file.close() yellow_print( '\nLogging is disabled and file {} has been closed'.format( name)) except Exception as e: red_print('\nLog file cannot be closed: {}'.format(e)) finally: self._log_file = None
def flash(action, ctx, args): """ Run esptool to flash the entire project, from an argfile generated by the build system """ ensure_build_directory(args, ctx.info_name) project_desc = _get_project_desc(ctx, args) if project_desc['target'] == 'linux': yellow_print('skipping flash since running on linux...') return esp_port = args.port or _get_default_serial_port(args) run_target(action, args, {'ESPBAUD': str(args.baud), 'ESPPORT': esp_port})
def process_coredump(self): # type: () -> None if self._decode_coredumps != COREDUMP_DECODE_INFO: raise NotImplementedError('process_coredump: %s not implemented' % self._decode_coredumps) coredump_script = os.path.join(os.path.dirname(__file__), '..', 'components', 'espcoredump', 'espcoredump.py') coredump_file = None try: # On Windows, the temporary file can't be read unless it is closed. # Set delete=False and delete the file manually later. with tempfile.NamedTemporaryFile(mode='wb', delete=False) as coredump_file: coredump_file.write(self._coredump_buffer) coredump_file.flush() if self.websocket_client: self._output_enabled = True yellow_print('Communicating through WebSocket') self.websocket_client.send({ 'event': 'coredump', 'file': coredump_file.name, 'prog': self.elf_file }) yellow_print('Waiting for debug finished event') self.websocket_client.wait([('event', 'debug_finished')]) yellow_print('Communications through WebSocket is finished') else: cmd = [ sys.executable, coredump_script, 'info_corefile', '--core', coredump_file.name, '--core-format', 'b64', self.elf_file ] output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) self._output_enabled = True self._print(output) self._output_enabled = False # Will be reenabled in check_coredump_trigger_after_print except subprocess.CalledProcessError as e: yellow_print('Failed to run espcoredump script: {}\n{}\n\n'.format( e, e.output)) self._output_enabled = True self._print(COREDUMP_UART_START + b'\n') self._print(self._coredump_buffer) # end line will be printed in handle_serial_input finally: if coredump_file is not None: try: os.unlink(coredump_file.name) except OSError as e: yellow_print( 'Couldn\'t remote temporary core dump file ({})'. format(e))
def check_coredump_trigger_before_print(self, line): # type: (bytes) -> None if self._decode_coredumps == COREDUMP_DECODE_DISABLE: return if COREDUMP_UART_PROMPT in line: yellow_print('Initiating core dump!') self.event_queue.put((TAG_KEY, '\n')) return if COREDUMP_UART_START in line: yellow_print('Core dump started (further output muted)') self._reading_coredump = COREDUMP_READING self._coredump_buffer = b'' self._output_enabled = False return if COREDUMP_UART_END in line: self._reading_coredump = COREDUMP_DONE yellow_print('\nCore dump finished!') self.process_coredump() return if self._reading_coredump == COREDUMP_READING: kb = 1024 buffer_len_kb = len(self._coredump_buffer) // kb self._coredump_buffer += line.replace(b'\r', b'') + b'\n' new_buffer_len_kb = len(self._coredump_buffer) // kb if new_buffer_len_kb > buffer_len_kb: yellow_print('Received %3d kB...' % (new_buffer_len_kb), newline='\r')
def run_make(self, target): # type: (str) -> None with self: if isinstance(self.make, list): popen_args = self.make + [target] else: popen_args = [self.make, target] yellow_print('Running %s...' % ' '.join(popen_args)) p = subprocess.Popen(popen_args, env=os.environ) try: p.wait() except KeyboardInterrupt: p.wait() if p.returncode != 0: self.prompt_next_action('Build failed') else: self.output_enable(True)
def print_hints(*filenames: str) -> None: """Getting output files and printing hints on how to resolve errors based on the output.""" with open(os.path.join(os.path.dirname(__file__), 'hints.yml'), 'r') as file: hints = yaml.safe_load(file) for file_name in filenames: with open(file_name, 'r') as file: output = ' '.join(line.strip() for line in file if line.strip()) for hint in hints: try: match = re.compile(hint['re']).findall(output) except KeyError: raise KeyError("Argument 're' missing in {}. Check hints.yml file.".format(hint)) except re.error as e: raise re.error('{} from hints.yml have {} problem. Check hints.yml file.'.format(hint['re'], e)) if match: extra_info = ', '.join(match) if hint.get('match_to_output', '') else '' try: yellow_print(' '.join(['HINT:', hint['hint'].format(extra_info)])) except KeyError: raise KeyError("Argument 'hint' missing in {}. Check hints.yml file.".format(hint))
def check_panic_decode_trigger(self, line): # type: (bytes) -> None if self._decode_panic == PANIC_DECODE_DISABLE: return if self._reading_panic == PANIC_IDLE and re.search( PANIC_START, line.decode('ascii', errors='ignore')): self._reading_panic = PANIC_READING yellow_print('Stack dump detected') if self._reading_panic == PANIC_READING and PANIC_STACK_DUMP in line: self._output_enabled = False if self._reading_panic == PANIC_READING: self._panic_buffer += line.replace(b'\r', b'') + b'\n' if self._reading_panic == PANIC_READING and PANIC_END in line: self._reading_panic = PANIC_IDLE self._output_enabled = True self.process_panic_output(self._panic_buffer) self._panic_buffer = b''
def check_gdbstub_trigger(self, line): # type: (bytes) -> None line = self._gdb_buffer + line self._gdb_buffer = b'' m = re.search(b'\\$(T..)#(..)', line) # look for a gdb "reason" for a break if m is not None: try: chsum = sum(ord(bytes([p])) for p in m.group(1)) & 0xFF calc_chsum = int(m.group(2), 16) except ValueError: return # payload wasn't valid hex digits if chsum == calc_chsum: if self.websocket_client: yellow_print('Communicating through WebSocket') self.websocket_client.send({ 'event': 'gdb_stub', 'port': self.serial.port, 'prog': self.elf_file }) yellow_print('Waiting for debug finished event') self.websocket_client.wait([('event', 'debug_finished')]) yellow_print( 'Communications through WebSocket is finished') else: self.run_gdb() else: red_print( 'Malformed gdb message... calculated checksum %02x received %02x' % (chsum, calc_chsum))
async def read_and_write_stream(self, input_stream: asyncio.StreamReader, output_filename: str, output_stream: TextIO=sys.stdout) -> None: """read the output of the `input_stream` and then write it into `output_filename` and `output_stream`""" def delete_ansi_escape(text: str) -> str: ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') return ansi_escape.sub('', text) def prepare_for_print(out: bytes) -> str: # errors='ignore' is here because some chips produce some garbage bytes result = out.decode(errors='ignore') if not output_stream.isatty(): # delete escape sequence if we printing in environments where ANSI coloring is disabled return delete_ansi_escape(result) return result def print_progression(output: str) -> None: # Print a new line on top of the previous line sys.stdout.write('\x1b[K') print('\r', end='') print(fit_text_in_terminal(output.strip('\n\r')), end='', file=output_stream) try: with open(output_filename, 'w') as output_file: while True: out = await input_stream.readline() if not out: break output = prepare_for_print(out) output_file.write(output) # print output in progression way but only the progression related (that started with '[') and if verbose flag is not set if self.force_progression and output[0] == '[' and '-v' not in self.args and output_stream.isatty(): print_progression(output) else: print(output, end='', file=output_stream) except (RuntimeError, EnvironmentError) as e: yellow_print('WARNING: The exception {} was raised and we can\'t capture all your {} and ' 'hints on how to resolve errors can be not accurate.'.format(e, output_stream.name.strip('<>')))
def process_panic_output(self, panic_output): # type: (bytes) -> None panic_output_decode_script = os.path.join(os.path.dirname(__file__), '..', 'tools', 'gdb_panic_server.py') panic_output_file = None try: # On Windows, the temporary file can't be read unless it is closed. # Set delete=False and delete the file manually later. with tempfile.NamedTemporaryFile( mode='wb', delete=False) as panic_output_file: panic_output_file.write(panic_output) panic_output_file.flush() cmd = [ self.toolchain_prefix + 'gdb', '--batch', '-n', self.elf_file, '-ex', "target remote | \"{python}\" \"{script}\" --target {target} \"{output_file}\"" .format(python=sys.executable, script=panic_output_decode_script, target=self.target, output_file=panic_output_file.name), '-ex', 'bt' ] output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) yellow_print('\nBacktrace:\n\n') self._print(output) except subprocess.CalledProcessError as e: yellow_print( 'Failed to run gdb_panic_server.py script: {}\n{}\n\n'.format( e, e.output)) self._print(panic_output) finally: if panic_output_file is not None: try: os.unlink(panic_output_file.name) except OSError as e: yellow_print( 'Couldn\'t remove temporary panic output file ({})'. format(e))
def main(): # type: () -> None parser = argparse.ArgumentParser( 'idf_monitor - a serial output monitor for esp-idf') parser.add_argument('--port', '-p', help='Serial port device', default=os.environ.get('ESPTOOL_PORT', '/dev/ttyUSB0')) parser.add_argument( '--disable-address-decoding', '-d', help= "Don't print lines about decoded addresses from the application ELF file", action='store_true', default=True if os.environ.get('ESP_MONITOR_DECODE') == 0 else False) parser.add_argument('--baud', '-b', help='Serial port baud rate', type=int, default=os.getenv('IDF_MONITOR_BAUD', os.getenv('MONITORBAUD', 115200))) parser.add_argument('--make', '-m', help='Command to run make', type=str, default='make') parser.add_argument('--encrypted', help='Use encrypted targets while running make', action='store_true') parser.add_argument( '--toolchain-prefix', help='Triplet prefix to add before cross-toolchain names', default=DEFAULT_TOOLCHAIN_PREFIX) parser.add_argument( '--eol', choices=['CR', 'LF', 'CRLF'], type=lambda c: c.upper(), help='End of line to use when sending to the serial port', default='CR') parser.add_argument('elf_file', help='ELF file of application', type=argparse.FileType('rb')) parser.add_argument('--print_filter', help='Filtering string', default=DEFAULT_PRINT_FILTER) parser.add_argument( '--decode-coredumps', choices=[COREDUMP_DECODE_INFO, COREDUMP_DECODE_DISABLE], default=COREDUMP_DECODE_INFO, help='Handling of core dumps found in serial output') parser.add_argument( '--decode-panic', choices=[PANIC_DECODE_BACKTRACE, PANIC_DECODE_DISABLE], default=PANIC_DECODE_DISABLE, help='Handling of panic handler info found in serial output') parser.add_argument( '--target', help='Target name (used when stack dump decoding is enabled)', default=os.environ.get('IDF_TARGET', 'esp32')) parser.add_argument('--revision', help='Revision of the target', type=int, default=0) parser.add_argument( '--ws', default=os.environ.get('ESP_IDF_MONITOR_WS', None), help= 'WebSocket URL for communicating with IDE tools for debugging purposes' ) args = parser.parse_args() # GDB uses CreateFile to open COM port, which requires the COM name to be r'\\.\COMx' if the COM # number is larger than 10 if os.name == 'nt' and args.port.startswith('COM'): args.port = args.port.replace('COM', r'\\.\COM') yellow_print( '--- WARNING: GDB cannot open serial ports accessed as COMx') yellow_print('--- Using %s instead...' % args.port) elif args.port.startswith('/dev/tty.') and sys.platform == 'darwin': args.port = args.port.replace('/dev/tty.', '/dev/cu.') yellow_print( '--- WARNING: Serial ports accessed as /dev/tty.* will hang gdb if launched.' ) yellow_print('--- Using %s instead...' % args.port) serial_instance = serial.serial_for_url(args.port, args.baud, do_not_open=True) serial_instance.dtr = False serial_instance.rts = False args.elf_file.close() # don't need this as a file # remove the parallel jobserver arguments from MAKEFLAGS, as any # parent make is only running 1 job (monitor), so we can re-spawn # all of the child makes we need (the -j argument remains part of # MAKEFLAGS) try: makeflags = os.environ['MAKEFLAGS'] makeflags = re.sub(r'--jobserver[^ =]*=[0-9,]+ ?', '', makeflags) os.environ['MAKEFLAGS'] = makeflags except KeyError: pass # not running a make jobserver # Pass the actual used port to callee of idf_monitor (e.g. make) through `ESPPORT` environment # variable # To make sure the key as well as the value are str type, by the requirements of subprocess espport_key = str('ESPPORT') espport_val = str(args.port) os.environ.update({espport_key: espport_val}) ws = WebSocketClient(args.ws) if args.ws else None try: monitor = Monitor( serial_instance, args.elf_file.name, args.print_filter, args.make, args.encrypted, args.toolchain_prefix, args.eol, args.decode_coredumps, args.decode_panic, args.target, ws, enable_address_decoding=not args.disable_address_decoding) yellow_print('--- idf_monitor on {p.name} {p.baudrate} ---'.format( p=serial_instance)) yellow_print( '--- Quit: {} | Menu: {} | Help: {} followed by {} ---'.format( key_description(monitor.console_parser.exit_key), key_description(monitor.console_parser.menu_key), key_description(monitor.console_parser.menu_key), key_description(CTRL_H))) if args.print_filter != DEFAULT_PRINT_FILTER: yellow_print('--- Print filter: {} ---'.format(args.print_filter)) monitor.main_loop() finally: if ws: ws.close()
def monitor(action: str, ctx: click.core.Context, args: PropertyDict, print_filter: str, monitor_baud: str, encrypted: bool, no_reset: bool, timestamps: bool, timestamp_format: str, force_color: bool) -> None: """ Run idf_monitor.py to watch build output """ project_desc = _get_project_desc(ctx, args) elf_file = os.path.join(args.build_dir, project_desc['app_elf']) idf_monitor = os.path.join(os.environ['IDF_PATH'], 'tools/idf_monitor.py') monitor_args = [PYTHON, idf_monitor] if project_desc['target'] != 'linux': if no_reset and args.port is None: msg = ( 'WARNING: --no-reset is ignored. ' 'Please specify the port with the --port argument in order to use this option.' ) yellow_print(msg) no_reset = False esp_port = args.port or _get_default_serial_port(args) monitor_args += ['-p', esp_port] baud = monitor_baud or os.getenv('IDF_MONITOR_BAUD') or os.getenv( 'MONITORBAUD') if baud is None: # Baud hasn't been changed locally (by local baud argument nor by environment variables) # # Use the global baud rate if it has been changed by the command line. # Use project_desc['monitor_baud'] as the last option. global_baud_defined = ctx._parameter_source[ 'baud'] == click.core.ParameterSource.COMMANDLINE baud = args.baud if global_baud_defined else project_desc[ 'monitor_baud'] monitor_args += ['-b', baud] monitor_args += [ '--toolchain-prefix', project_desc['monitor_toolprefix'] ] coredump_decode = get_sdkconfig_value(project_desc['config_file'], 'CONFIG_ESP_COREDUMP_DECODE') if coredump_decode is not None: monitor_args += ['--decode-coredumps', coredump_decode] target_arch_riscv = get_sdkconfig_value( project_desc['config_file'], 'CONFIG_IDF_TARGET_ARCH_RISCV') monitor_args += ['--target', project_desc['target']] revision = project_desc.get('rev') if revision: monitor_args += ['--revision', revision] if target_arch_riscv: monitor_args += ['--decode-panic', 'backtrace'] if print_filter is not None: monitor_args += ['--print_filter', print_filter] if elf_file: monitor_args += [elf_file] if encrypted: monitor_args += ['--encrypted'] if no_reset: monitor_args += ['--no-reset'] if timestamps: monitor_args += ['--timestamps'] if timestamp_format: monitor_args += ['--timestamp-format', timestamp_format] if force_color or os.name == 'nt': monitor_args += ['--force-color'] idf_py = [PYTHON] + _get_commandline_options( ctx) # commands to re-run idf.py monitor_args += ['-m', ' '.join("'%s'" % a for a in idf_py)] hints = not args.no_hints RunTool('idf_monitor', monitor_args, args.project_dir, build_dir=args.build_dir, hints=hints)()
def main_loop(self): # 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: 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 try: 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 except SerialStopException: normal_print('Stopping condition has been received\n') except KeyboardInterrupt: pass finally: try: self.console_reader.stop() self.serial_reader.stop() self.stop_logging() # Cancelling _invoke_processing_last_line_timer is not # important here because receiving empty data doesn't matter. self._invoke_processing_last_line_timer = None except Exception: pass normal_print('\n')
def output_toggle(self): # type: () -> None self._output_enabled = not self._output_enabled yellow_print( '\nToggle output display: {}, Type Ctrl-T Ctrl-Y to show/disable output again.' .format(self._output_enabled))
def main() -> None: parser = get_parser() args = parser.parse_args() # The port name is changed in cases described in the following lines. Use a local argument and # avoid the modification of args.port. port = args.port # GDB uses CreateFile to open COM port, which requires the COM name to be r'\\.\COMx' if the COM # number is larger than 10 if os.name == 'nt' and port.startswith('COM'): port = port.replace('COM', r'\\.\COM') yellow_print( '--- WARNING: GDB cannot open serial ports accessed as COMx') yellow_print('--- Using %s instead...' % port) elif port.startswith('/dev/tty.') and sys.platform == 'darwin': port = port.replace('/dev/tty.', '/dev/cu.') yellow_print( '--- WARNING: Serial ports accessed as /dev/tty.* will hang gdb if launched.' ) yellow_print('--- Using %s instead...' % port) if isinstance(args.elf_file, io.BufferedReader): elf_file = args.elf_file.name args.elf_file.close() # don't need this as a file else: elf_file = args.elf_file # remove the parallel jobserver arguments from MAKEFLAGS, as any # parent make is only running 1 job (monitor), so we can re-spawn # all of the child makes we need (the -j argument remains part of # MAKEFLAGS) try: makeflags = os.environ[MAKEFLAGS_ENVIRON] makeflags = re.sub(r'--jobserver[^ =]*=[0-9,]+ ?', '', makeflags) os.environ[MAKEFLAGS_ENVIRON] = makeflags except KeyError: pass # not running a make jobserver ws = WebSocketClient(args.ws) if args.ws else None try: cls: Type[Monitor] if args.target == 'linux': serial_instance = None cls = LinuxMonitor yellow_print('--- idf_monitor on linux ---') else: serial_instance = serial.serial_for_url(port, args.baud, do_not_open=True) serial_instance.dtr = False serial_instance.rts = False # Pass the actual used port to callee of idf_monitor (e.g. idf.py/cmake) through `ESPPORT` environment # variable. # Note that the port must be original port argument without any replacement done in IDF Monitor (idf.py # has a check for this). # To make sure the key as well as the value are str type, by the requirements of subprocess espport_val = str(args.port) os.environ.update({ESPPORT_ENVIRON: espport_val}) cls = SerialMonitor yellow_print('--- idf_monitor on {p.name} {p.baudrate} ---'.format( p=serial_instance)) monitor = cls(serial_instance, elf_file, args.print_filter, args.make, args.encrypted, not args.no_reset, args.toolchain_prefix, args.eol, args.decode_coredumps, args.decode_panic, args.target, ws, not args.disable_address_decoding, args.timestamps, args.timestamp_format, args.force_color) yellow_print( '--- Quit: {} | Menu: {} | Help: {} followed by {} ---'.format( key_description(monitor.console_parser.exit_key), key_description(monitor.console_parser.menu_key), key_description(monitor.console_parser.menu_key), key_description(CTRL_H))) if args.print_filter != DEFAULT_PRINT_FILTER: yellow_print('--- Print filter: {} ---'.format(args.print_filter)) monitor.main_loop() except KeyboardInterrupt: pass finally: if ws: ws.close()
def main(): # type: () -> None parser = get_parser() args = parser.parse_args() # GDB uses CreateFile to open COM port, which requires the COM name to be r'\\.\COMx' if the COM # number is larger than 10 if os.name == 'nt' and args.port.startswith('COM'): args.port = args.port.replace('COM', r'\\.\COM') yellow_print( '--- WARNING: GDB cannot open serial ports accessed as COMx') yellow_print('--- Using %s instead...' % args.port) elif args.port.startswith('/dev/tty.') and sys.platform == 'darwin': args.port = args.port.replace('/dev/tty.', '/dev/cu.') yellow_print( '--- WARNING: Serial ports accessed as /dev/tty.* will hang gdb if launched.' ) yellow_print('--- Using %s instead...' % args.port) serial_instance = serial.serial_for_url(args.port, args.baud, do_not_open=True) serial_instance.dtr = False serial_instance.rts = False args.elf_file.close() # don't need this as a file # remove the parallel jobserver arguments from MAKEFLAGS, as any # parent make is only running 1 job (monitor), so we can re-spawn # all of the child makes we need (the -j argument remains part of # MAKEFLAGS) try: makeflags = os.environ[MAKEFLAGS_ENVIRON] makeflags = re.sub(r'--jobserver[^ =]*=[0-9,]+ ?', '', makeflags) os.environ[MAKEFLAGS_ENVIRON] = makeflags except KeyError: pass # not running a make jobserver # Pass the actual used port to callee of idf_monitor (e.g. make) through `ESPPORT` environment # variable # To make sure the key as well as the value are str type, by the requirements of subprocess espport_val = str(args.port) os.environ.update({ESPPORT_ENVIRON: espport_val}) ws = WebSocketClient(args.ws) if args.ws else None try: monitor = Monitor( serial_instance, args.elf_file.name, args.print_filter, args.make, args.encrypted, args.toolchain_prefix, args.eol, args.decode_coredumps, args.decode_panic, args.target, ws, enable_address_decoding=not args.disable_address_decoding, timestamps=args.timestamps, timestamp_format=args.timestamp_format) yellow_print('--- idf_monitor on {p.name} {p.baudrate} ---'.format( p=serial_instance)) yellow_print( '--- Quit: {} | Menu: {} | Help: {} followed by {} ---'.format( key_description(monitor.console_parser.exit_key), key_description(monitor.console_parser.menu_key), key_description(monitor.console_parser.menu_key), key_description(CTRL_H))) if args.print_filter != DEFAULT_PRINT_FILTER: yellow_print('--- Print filter: {} ---'.format(args.print_filter)) monitor.main_loop() except KeyboardInterrupt: pass finally: if ws: ws.close()
def main_loop(self): # type: () -> None self.console_reader.start() self.serial_reader.start() self.gdb_helper.gdb_exit = False self.serial_handler.start_cmd_sent = False try: while self.console_reader.alive and self.serial_reader.alive: try: if self.gdb_helper.gdb_exit: self.gdb_helper.gdb_exit = False time.sleep(GDB_EXIT_TIMEOUT) try: # Continue the program after exit from the GDB self.serial.write( codecs.encode(GDB_UART_CONTINUE_COMMAND)) self.serial_handler.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 try: item = self.cmd_queue.get_nowait() except queue.Empty: try: item = self.event_queue.get( timeout=EVENT_QUEUE_TIMEOUT) except queue.Empty: continue event_tag, data = item if event_tag == TAG_CMD: self.serial_handler.handle_commands( data, self.target, self.run_make, self.console_reader, self.serial_reader) 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.serial_handler.handle_serial_input( data, self.console_parser, self.coredump, self.gdb_helper, self._line_matcher, self.check_gdb_stub_and_run) 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( LAST_LINE_THREAD_INTERVAL, 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.serial_handler.handle_serial_input( data, self.console_parser, self.coredump, self.gdb_helper, self._line_matcher, self.check_gdb_stub_and_run, 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(CTRL_C)) 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 except SerialStopException: normal_print('Stopping condition has been received\n') except KeyboardInterrupt: pass finally: try: self.console_reader.stop() self.serial_reader.stop() self.logger.stop_logging() # Cancelling _invoke_processing_last_line_timer is not # important here because receiving empty data doesn't matter. self._invoke_processing_last_line_timer = None except Exception: # noqa pass normal_print('\n')