coredump.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import os
  2. import queue
  3. import subprocess
  4. import sys
  5. import tempfile
  6. from contextlib import contextmanager
  7. from typing import Generator
  8. from .constants import COREDUMP_SCRIPT, TAG_KEY
  9. from .logger import Logger
  10. from .output_helpers import yellow_print
  11. from .web_socket_client import WebSocketClient
  12. # coredump related messages
  13. COREDUMP_UART_START = b'================= CORE DUMP START ================='
  14. COREDUMP_UART_END = b'================= CORE DUMP END ================='
  15. COREDUMP_UART_PROMPT = b'Press Enter to print core dump to UART...'
  16. # coredump states
  17. COREDUMP_IDLE = 0
  18. COREDUMP_READING = 1
  19. COREDUMP_DONE = 2
  20. # coredump decoding options
  21. COREDUMP_DECODE_DISABLE = 'disable'
  22. COREDUMP_DECODE_INFO = 'info'
  23. class CoreDump:
  24. def __init__(self, decode_coredumps, event_queue, logger, websocket_client, elf_file):
  25. # type: (str, queue.Queue, Logger, WebSocketClient, str) -> None
  26. self._coredump_buffer = b''
  27. self._decode_coredumps = decode_coredumps
  28. self.event_queue = event_queue
  29. self._reading_coredump = COREDUMP_IDLE
  30. self.logger = logger
  31. self.websocket_client = websocket_client
  32. self.elf_file = elf_file
  33. def _process_coredump(self): # type: () -> None
  34. if self._decode_coredumps != COREDUMP_DECODE_INFO:
  35. raise NotImplementedError('process_coredump: %s not implemented' % self._decode_coredumps)
  36. coredump_file = None
  37. try:
  38. # On Windows, the temporary file can't be read unless it is closed.
  39. # Set delete=False and delete the file manually later.
  40. with tempfile.NamedTemporaryFile(mode='wb', delete=False) as coredump_file:
  41. coredump_file.write(self._coredump_buffer)
  42. coredump_file.flush()
  43. if self.websocket_client:
  44. self.logger.output_enabled = True
  45. yellow_print('Communicating through WebSocket')
  46. self.websocket_client.send({'event': 'coredump',
  47. 'file': coredump_file.name,
  48. 'prog': self.elf_file})
  49. yellow_print('Waiting for debug finished event')
  50. self.websocket_client.wait([('event', 'debug_finished')])
  51. yellow_print('Communications through WebSocket is finished')
  52. else:
  53. cmd = [sys.executable,
  54. COREDUMP_SCRIPT,
  55. 'info_corefile',
  56. '--core', coredump_file.name,
  57. '--core-format', 'b64',
  58. self.elf_file
  59. ]
  60. output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
  61. self.logger.output_enabled = True
  62. self.logger.print(output) # noqa: E999
  63. self.logger.output_enabled = False # Will be reenabled in check_coredump_trigger_after_print
  64. except subprocess.CalledProcessError as e:
  65. yellow_print('Failed to run espcoredump script: {}\n{}\n\n'.format(e, e.output))
  66. self.logger.output_enabled = True
  67. self.logger.print(COREDUMP_UART_START + b'\n')
  68. self.logger.print(self._coredump_buffer)
  69. # end line will be printed in handle_serial_input
  70. finally:
  71. if coredump_file is not None:
  72. try:
  73. os.unlink(coredump_file.name)
  74. except OSError as e:
  75. yellow_print('Couldn\'t remote temporary core dump file ({})'.format(e))
  76. def _check_coredump_trigger_before_print(self, line): # type: (bytes) -> None
  77. if self._decode_coredumps == COREDUMP_DECODE_DISABLE:
  78. return
  79. if COREDUMP_UART_PROMPT in line:
  80. yellow_print('Initiating core dump!')
  81. self.event_queue.put((TAG_KEY, '\n'))
  82. return
  83. if COREDUMP_UART_START in line:
  84. yellow_print('Core dump started (further output muted)')
  85. self._reading_coredump = COREDUMP_READING
  86. self._coredump_buffer = b''
  87. self.logger.output_enabled = False
  88. return
  89. if COREDUMP_UART_END in line:
  90. self._reading_coredump = COREDUMP_DONE
  91. yellow_print('\nCore dump finished!')
  92. self._process_coredump()
  93. return
  94. if self._reading_coredump == COREDUMP_READING:
  95. kb = 1024
  96. buffer_len_kb = len(self._coredump_buffer) // kb
  97. self._coredump_buffer += line.replace(b'\r', b'') + b'\n'
  98. new_buffer_len_kb = len(self._coredump_buffer) // kb
  99. if new_buffer_len_kb > buffer_len_kb:
  100. yellow_print('Received %3d kB...' % new_buffer_len_kb, newline='\r')
  101. def _check_coredump_trigger_after_print(self): # type: () -> None
  102. if self._decode_coredumps == COREDUMP_DECODE_DISABLE:
  103. return
  104. # Re-enable output after the last line of core dump has been consumed
  105. if not self.logger.output_enabled and self._reading_coredump == COREDUMP_DONE:
  106. self._reading_coredump = COREDUMP_IDLE
  107. self.logger.output_enabled = True
  108. self._coredump_buffer = b''
  109. @contextmanager
  110. def check(self, line): # type: (bytes) -> Generator
  111. self._check_coredump_trigger_before_print(line)
  112. try:
  113. yield
  114. finally:
  115. self._check_coredump_trigger_after_print()