Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_cpu_exception_cortex_m / py / pw_cpu_exception_cortex_m / exception_analyzer.py
1 # Copyright 2020 The Pigweed Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 # use this file except in compliance with the License. You may obtain a copy of
5 # the License at
6 #
7 #     https://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations under
13 # the License.
14 """Tools to analyze Cortex-M CPU state context captured during an exception."""
15
16 from typing import Tuple
17
18 from pw_cpu_exception_cortex_m import cortex_m_constants
19
20
21 class CortexMExceptionAnalyzer:
22     """This class provides helper functions to dump a ArmV7mCpuState proto."""
23     def __init__(self, cpu_state):
24         self._cpu_state = cpu_state
25         self._active_cfsr_fields = None
26
27     def active_cfsr_fields(self) -> Tuple[cortex_m_constants.BitField, ...]:
28         """Returns a list of BitFields for each active CFSR flag."""
29
30         if self._active_cfsr_fields is not None:
31             return self._active_cfsr_fields
32
33         temp_field_list = []
34         if self._cpu_state.HasField('cfsr'):
35             for bit_field in cortex_m_constants.PW_CORTEX_M_CFSR_BIT_FIELDS:
36                 if self._cpu_state.cfsr & bit_field.bit_mask:
37                     temp_field_list.append(bit_field)
38         self._active_cfsr_fields = tuple(temp_field_list)
39         return self._active_cfsr_fields
40
41     def is_fault_active(self) -> bool:
42         """Returns true if the current CPU state indicates a fault is active."""
43         if self._cpu_state.HasField('cfsr') and self._cpu_state.cfsr != 0:
44             return True
45         if self._cpu_state.HasField('icsr'):
46             exception_number = (
47                 self._cpu_state.icsr
48                 & cortex_m_constants.PW_CORTEX_M_ICSR_VECTACTIVE_MASK)
49             if (cortex_m_constants.PW_CORTEX_M_HARD_FAULT_ISR_NUM <=
50                     exception_number <=
51                     cortex_m_constants.PW_CORTEX_M_USAGE_FAULT_ISR_NUM):
52                 return True
53         return False
54
55     def is_nested_fault(self) -> bool:
56         """Returns true if the current CPU state indicates a nested fault."""
57         if not self.is_fault_active():
58             return False
59         if (self._cpu_state.HasField('hfsr') and self._cpu_state.hfsr
60                 & cortex_m_constants.PW_CORTEX_M_HFSR_FORCED_MASK):
61             return True
62         return False
63
64     def exception_cause(self, show_active_cfsr_fields=True) -> str:
65         """Analyzes CPU state to tries and classify the exception.
66
67         Examples:
68             show_active_cfsr_fields=False
69               unknown exception
70               memory management fault at 0x00000000
71               usage fault, imprecise bus fault
72
73             show_active_cfsr_fields=True
74               usage fault [DIVBYZERO]
75               memory management fault at 0x00000000 [DACCVIOL] [MMARVALID]
76         """
77         cause = ''
78         # The CFSR can accumulate multiple exceptions.
79         split_major_cause = lambda cause: cause if not cause else cause + ', '
80
81         if self._cpu_state.HasField('cfsr') and self.is_fault_active():
82             if (self._cpu_state.cfsr
83                     & cortex_m_constants.PW_CORTEX_M_CFSR_USAGE_FAULT_MASK):
84                 cause += 'usage fault'
85
86             if (self._cpu_state.cfsr
87                     & cortex_m_constants.PW_CORTEX_M_CFSR_MEM_FAULT_MASK):
88                 cause = split_major_cause(cause)
89                 cause += 'memory management fault'
90                 if (self._cpu_state.cfsr
91                         & cortex_m_constants.PW_CORTEX_M_CFSR_MMARVALID_MASK):
92                     addr = '???' if not self._cpu_state.HasField(
93                         'mmfar') else f'0x{self._cpu_state.mmfar:08x}'
94                     cause += f' at {addr}'
95
96             if (self._cpu_state.cfsr
97                     & cortex_m_constants.PW_CORTEX_M_CFSR_BUS_FAULT_MASK):
98                 cause = split_major_cause(cause)
99                 if (self._cpu_state.cfsr &
100                         cortex_m_constants.PW_CORTEX_M_CFSR_IMPRECISERR_MASK):
101                     cause += 'imprecise '
102                 cause += 'bus fault'
103                 if (self._cpu_state.cfsr
104                         & cortex_m_constants.PW_CORTEX_M_CFSR_BFARVALID_MASK):
105                     addr = '???' if not self._cpu_state.HasField(
106                         'bfar') else f'0x{self._cpu_state.bfar:08x}'
107                     cause += f' at {addr}'
108             if show_active_cfsr_fields:
109                 for field in self.active_cfsr_fields():
110                     cause += f' [{field.name}]'
111
112         return cause if cause else 'unknown exception'
113
114     def dump_registers(self) -> str:
115         """Dumps all captured CPU registers as a multi-line string."""
116         registers = []
117         # TODO(amontanez): Do fancier decode of some registers like PC and LR.
118         for field in self._cpu_state.DESCRIPTOR.fields:
119             if self._cpu_state.HasField(field.name):
120                 register_value = getattr(self._cpu_state, field.name)
121                 registers.append(f'{field.name:<10} 0x{register_value:08x}')
122         return '\n'.join(registers)
123
124     def dump_active_active_cfsr_fields(self) -> str:
125         """Dumps CFSR flags with their descriptions as a multi-line string."""
126         fields = []
127         for field in self.active_cfsr_fields():
128             fields.append(f'{field.name:<11} {field.description}')
129         return '\n'.join(fields)
130
131     def __str__(self):
132         dump = [f'Exception caused by a {self.exception_cause(False)}.', '']
133         if self.active_cfsr_fields():
134             dump.extend((
135                 'Active Crash Fault Status Register (CFSR) fields:',
136                 self.dump_active_active_cfsr_fields(),
137                 '',
138             ))
139         else:
140             dump.extend((
141                 'No active Crash Fault Status Register (CFSR) fields.',
142                 '',
143             ))
144         dump.extend((
145             'All registers:',
146             self.dump_registers(),
147         ))
148         return '\n'.join(dump)