Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

esp32_exception_decoder: ESP32-C3 support; possibility for external configuration #612

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 71 additions & 46 deletions monitor/filter_exception_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,53 +31,74 @@ class Esp32ExceptionDecoder(DeviceMonitorFilter):

def __call__(self):
self.buffer = ""
self.backtrace_re = re.compile(
r"^Backtrace: ?((0x[0-9a-fA-F]+:0x[0-9a-fA-F]+ ?)+)\s*"
)
# regex matches potential PC value (0x4xxxxxxx)
# Logic identical to https://github.com/espressif/esp-idf/blob/master/tools/idf_monitor_base/constants.py#L56
self.pcaddr_re = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE)

self.firmware_path = None
self.addr2line_path = None
self.enabled = self.setup_paths()

if self.config.get("env:" + self.environment, "build_type") != "debug":
print(
return self

def setup_paths(self):
self.project_dir = path_to_unicode(os.path.abspath(self.project_dir))

self.project_strip_dir = os.environ.get("esp32_exception_decoder_project_strip_dir")
self.firmware_path = os.environ.get("esp32_exception_decoder_firmware_path")
self.addr2line_path = os.environ.get("esp32_exception_decoder_addr2line_path")
Comment on lines +47 to +49
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do mean these hacks?


if self.project_strip_dir is None:
self.project_strip_dir = self.project_dir

try:
if self.firmware_path is None or self.addr2line_path is None:
# Only load if necessary, as the call is expensive
data = load_project_ide_data(self.project_dir, self.environment)

if self.firmware_path is None:
# Only do this check when the firmware path is not externally provided
if self.config.get("env:" + self.environment, "build_type") != "debug":
print(
"""
Please build project in debug configuration to get more details about an exception.
See https://docs.platformio.org/page/projectconf/build_configurations.html

"""
)

return self
)
self.firmware_path = data["prog_path"]

def setup_paths(self):
self.project_dir = path_to_unicode(os.path.abspath(self.project_dir))
try:
data = load_project_ide_data(self.project_dir, self.environment)
self.firmware_path = data["prog_path"]
if not os.path.isfile(self.firmware_path):
sys.stderr.write(
"%s: firmware at %s does not exist, rebuild the project?\n"
"%s: disabling, firmware at %s does not exist, rebuild the project?\n"
% (self.__class__.__name__, self.firmware_path)
)
return False

cc_path = data.get("cc_path", "")
if "-gcc" in cc_path:
path = cc_path.replace("-gcc", "-addr2line")
if os.path.isfile(path):
self.addr2line_path = path
return True
if self.addr2line_path is None:
cc_path = data.get("cc_path", "")
if "-gcc" in cc_path:
self.addr2line_path = cc_path.replace("-gcc", "-addr2line")
else:
sys.stderr.write(
"%s: disabling, failed to find addr2line.\n" % self.__class__.__name__
)
return False

if not os.path.isfile(self.addr2line_path):
sys.stderr.write(
"%s: disabling, addr2line at %s does not exist\n"
% (self.__class__.__name__, self.addr2line_path)
)
return False

return True
except PlatformioException as e:
sys.stderr.write(
"%s: disabling, exception while looking for addr2line: %s\n"
% (self.__class__.__name__, e)
)
return False
sys.stderr.write(
"%s: disabling, failed to find addr2line.\n" % self.__class__.__name__
)
return False

def rx(self, text):
if not self.enabled:
Expand All @@ -97,14 +118,17 @@ def rx(self, text):
self.buffer = ""
last = idx + 1

m = self.backtrace_re.match(line)
if m is None:
continue
# Output each trace on a separate line below ours
# Logic identical to https://github.com/espressif/esp-idf/blob/master/tools/idf_monitor_base/logger.py#L131
for m in re.finditer(self.pcaddr_re, line):
if m is None:
continue

trace = self.get_backtrace(m)
if len(trace) != "":
text = text[: last] + trace + text[last :]
last += len(trace)

trace = self.get_backtrace(m)
if len(trace) != "":
text = text[: idx + 1] + trace + text[idx + 1 :]
last += len(trace)
return text

def get_backtrace(self, match):
Expand All @@ -114,19 +138,20 @@ def get_backtrace(self, match):
if PY2:
args = [a.encode(enc) for a in args]
try:
for i, addr in enumerate(match.group(1).split()):
if PY2:
addr = addr.encode(enc)
output = (
subprocess.check_output(args + [addr])
.decode(enc)
.strip()
)
output = output.replace(
"\n", "\n "
) # newlines happen with inlined methods
output = self.strip_project_dir(output)
trace += " #%-2d %s in %s\n" % (i, addr, output)
addr = match.group()
if PY2:
addr = addr.encode(enc)
output = (
subprocess.check_output(args + [addr])
.decode(enc)
.strip()
)
output = output.replace(
"\n", "\n "
) # newlines happen with inlined methods
output = self.strip_project_dir(output)
# Output the trace in yellow color so that it is easier to spot
trace += "\033[33m=> %s: %s\033[0m\n" % (addr, output)
except subprocess.CalledProcessError as e:
sys.stderr.write(
"%s: failed to call %s: %s\n"
Expand All @@ -136,8 +161,8 @@ def get_backtrace(self, match):

def strip_project_dir(self, trace):
while True:
idx = trace.find(self.project_dir)
idx = trace.find(self.project_strip_dir)
if idx == -1:
break
trace = trace[:idx] + trace[idx + len(self.project_dir) + 1 :]
trace = trace[:idx] + trace[idx + len(self.project_strip_dir) + 1 :]
return trace