pw_cpu_exception_cortex_m: Add CFSR decode tool
Adds a very simple tool to break down a CFSR value into the various
fault flags.
Change-Id: I3df3e938c6b62b3d57ba28ce3b7713bdf5eae6bd
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/40667
Reviewed-by: David Rogers <davidrogers@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
diff --git a/pw_cpu_exception_cortex_m/docs.rst b/pw_cpu_exception_cortex_m/docs.rst
index fd23ec6..3b7c9be 100644
--- a/pw_cpu_exception_cortex_m/docs.rst
+++ b/pw_cpu_exception_cortex_m/docs.rst
@@ -113,3 +113,33 @@
by >1.5KB when using plain-text logs, or ~460 Bytes when using tokenized
logging. It's useful to enable this for device bringup until your application
has an end-to-end crash reporting solution.
+
+Exception Analysis
+==================
+This module provides Python tooling to analyze CPU state captured by a Cortex-M
+core during an exception. This can be particularly useful as part of a larger
+crash report analyzer.
+
+CFSR decoder
+------------
+The ARMv7-M and ARMv8-M architectures have a Configurable Fault Status Register
+(CFSR) that explains what illegal behavior caused a fault. Even with no
+additional context, it can provide quite a bit of insight into what caused the
+CPU to fault. This module provides a simple command-line tool to decode raw CFSR
+contents (e.g. 0x00010000) as human-readable information (e.g. "Encountered
+invalid instruction"). An example of this tool in use is provided below:
+
+ .. code-block::
+
+ $ python -m pw_cpu_exception_cortex_m.cfsr_decoder 0x00010100
+ 20210412 15:11:14 INF Exception caused by a usage fault, bus fault.
+
+ Active Crash Fault Status Register (CFSR) fields:
+ IBUSERR Bus fault on instruction fetch.
+ UNDEFINSTR Encountered invalid instruction.
+
+ All registers:
+ cfsr 0x00010100
+
+.. note::
+ The CFSR is not supported on ARMv6-M CPUs (Cortex M0, M0+, M1).
diff --git a/pw_cpu_exception_cortex_m/py/BUILD.gn b/pw_cpu_exception_cortex_m/py/BUILD.gn
index df5dc96..efa0b85 100644
--- a/pw_cpu_exception_cortex_m/py/BUILD.gn
+++ b/pw_cpu_exception_cortex_m/py/BUILD.gn
@@ -20,6 +20,7 @@
setup = [ "setup.py" ]
sources = [
"pw_cpu_exception_cortex_m/__init__.py",
+ "pw_cpu_exception_cortex_m/cfsr_decoder.py",
"pw_cpu_exception_cortex_m/cortex_m_constants.py",
"pw_cpu_exception_cortex_m/exception_analyzer.py",
]
@@ -27,6 +28,7 @@
python_deps = [
"$dir_pw_cli/py",
"$dir_pw_protobuf_compiler/py",
+ "..:cpu_state_protos.python",
]
pylintrc = "$dir_pigweed/.pylintrc"
}
diff --git a/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/cfsr_decoder.py b/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/cfsr_decoder.py
new file mode 100644
index 0000000..380fb1f
--- /dev/null
+++ b/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/cfsr_decoder.py
@@ -0,0 +1,61 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""A simple tool to decode a CFSR register from the command line
+
+Example usage:
+
+ $ python -m pw_cpu_exception_cortex_m.cfsr_decoder 0x00010100
+
+ 20210412 15:09:01 INF Exception caused by a usage fault, bus fault.
+
+ Active Crash Fault Status Register (CFSR) fields:
+ IBUSERR Bus fault on instruction fetch.
+ UNDEFINSTR Encountered invalid instruction.
+
+ All registers:
+ cfsr 0x00010100
+"""
+
+import argparse
+import logging
+import sys
+import pw_cli.log
+
+from pw_cpu_exception_cortex_m_protos import cpu_state_pb2
+from pw_cpu_exception_cortex_m import exception_analyzer
+
+_LOG = logging.getLogger('decode_cfsr')
+
+
+def _parse_args() -> argparse.Namespace:
+ """Parses arguments for this script, splitting out the command to run."""
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('cfsr',
+ type=lambda val: int(val, 0),
+ help='The Cortex-M CFSR to decode')
+ return parser.parse_args()
+
+
+def dump_cfsr(cfsr: int) -> int:
+ cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
+ cpu_state_proto.cfsr = cfsr
+ cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
+ cpu_state_proto)
+ _LOG.info(cpu_state_info)
+ return 0
+
+
+if __name__ == '__main__':
+ pw_cli.log.install(level=logging.INFO)
+ sys.exit(dump_cfsr(**vars(_parse_args())))