| // 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. |
| |
| #define PW_LOG_LEVEL PW_CPU_EXCEPTION_CORTEX_M_LOG_LEVEL |
| |
| #include "pw_cpu_exception_cortex_m/snapshot.h" |
| |
| #include "pw_cpu_exception_cortex_m/proto_dump.h" |
| #include "pw_cpu_exception_cortex_m/util.h" |
| #include "pw_cpu_exception_cortex_m_private/config.h" |
| #include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h" |
| #include "pw_cpu_exception_cortex_m_protos/cpu_state.pwpb.h" |
| #include "pw_log/log.h" |
| #include "pw_protobuf/encoder.h" |
| #include "pw_status/status.h" |
| #include "pw_thread/snapshot.h" |
| #include "pw_thread_protos/thread.pwpb.h" |
| |
| namespace pw::cpu_exception::cortex_m { |
| namespace { |
| |
| constexpr char kMainStackHandlerModeName[] = "Main Stack (Handler Mode)"; |
| constexpr char kMainStackThreadModeName[] = "Main Stack (Thread Mode)"; |
| |
| Status CaptureMainStack( |
| ProcessorMode mode, |
| uintptr_t stack_low_addr, |
| uintptr_t stack_high_addr, |
| uintptr_t stack_pointer, |
| thread::proto::SnapshotThreadInfo::StreamEncoder& snapshot_encoder, |
| thread::ProcessThreadStackCallback& thread_stack_callback) { |
| thread::proto::Thread::StreamEncoder encoder = |
| snapshot_encoder.GetThreadsEncoder(); |
| |
| const char* thread_name; |
| thread::proto::ThreadState::Enum thread_state; |
| if (mode == ProcessorMode::kHandlerMode) { |
| thread_name = kMainStackHandlerModeName; |
| PW_LOG_DEBUG("Capturing thread info for Main Stack (Handler Mode)"); |
| thread_state = thread::proto::ThreadState::Enum::INTERRUPT_HANDLER; |
| PW_LOG_DEBUG("Thread state: INTERRUPT_HANDLER"); |
| } else { // mode == ProcessorMode::kThreadMode |
| thread_name = kMainStackThreadModeName; |
| PW_LOG_DEBUG("Capturing thread info for Main Stack (Thread Mode)"); |
| thread_state = thread::proto::ThreadState::Enum::RUNNING; |
| PW_LOG_DEBUG("Thread state: RUNNING"); |
| } |
| encoder.WriteState(thread_state); |
| encoder.WriteName(as_bytes(span(std::string_view(thread_name)))); |
| |
| const thread::StackContext thread_ctx = { |
| .thread_name = thread_name, |
| .stack_low_addr = stack_low_addr, |
| .stack_high_addr = stack_high_addr, |
| .stack_pointer = stack_pointer, |
| .stack_pointer_est_peak = std::nullopt, |
| }; |
| return thread::SnapshotStack(thread_ctx, encoder, thread_stack_callback); |
| } |
| |
| } // namespace |
| |
| Status SnapshotCpuState( |
| const pw_cpu_exception_State& cpu_state, |
| SnapshotCpuStateOverlay::StreamEncoder& snapshot_encoder) { |
| { |
| ArmV7mCpuState::StreamEncoder cpu_state_encoder = |
| snapshot_encoder.GetArmv7mCpuStateEncoder(); |
| DumpCpuStateProto(cpu_state_encoder, cpu_state); |
| } |
| return snapshot_encoder.status(); |
| } |
| |
| Status SnapshotMainStackThread( |
| uintptr_t stack_low_addr, |
| uintptr_t stack_high_addr, |
| thread::proto::SnapshotThreadInfo::StreamEncoder& encoder, |
| thread::ProcessThreadStackCallback& thread_stack_callback) { |
| uintptr_t stack_pointer; |
| asm volatile("mrs %0, msp\n" : "=r"(stack_pointer)); |
| |
| // First check if we're in Handler mode, AKA handling exceptions/interrupts. |
| // |
| // Handler mode vs thread mode can be determined via IPSR, bits 8:0 of xPSR. |
| // In thread mode the value is 0, in handler mode the value is non-zero. |
| uint32_t xpsr; |
| asm volatile("mrs %0, xpsr\n" : "=r"(xpsr)); |
| if ((xpsr & kXpsrIpsrMask) != 0) { |
| return CaptureMainStack(ProcessorMode::kHandlerMode, |
| stack_low_addr, |
| stack_high_addr, |
| stack_pointer, |
| encoder, |
| thread_stack_callback); |
| } |
| |
| // It looks like we're in Thread mode which means we need to check whether |
| // or not we are executing off the main stack currently. |
| // |
| // See ARMv7-M Architecture Reference Manual Section B1.4.4 for the control |
| // register values, in particular the SPSEL bit while in Thread mode which |
| // is 0 while running off the main stack and 1 while running off the proces |
| // stack. |
| uint32_t control; |
| asm volatile("mrs %0, control\n" : "=r"(control)); |
| if ((control & kControlThreadModeStackMask) != 0) { |
| return OkStatus(); // Main stack is not currently active. |
| } |
| |
| // We're running off the main stack in Thread mode. |
| return CaptureMainStack(ProcessorMode::kThreadMode, |
| stack_low_addr, |
| stack_high_addr, |
| stack_pointer, |
| encoder, |
| thread_stack_callback); |
| } |
| |
| Status SnapshotMainStackThread( |
| const pw_cpu_exception_State& cpu_state, |
| uintptr_t stack_low_addr, |
| uintptr_t stack_high_addr, |
| thread::proto::SnapshotThreadInfo::StreamEncoder& encoder, |
| thread::ProcessThreadStackCallback& thread_stack_callback) { |
| if (!MainStackActive(cpu_state)) { |
| return OkStatus(); // Main stack wasn't active, nothing to capture. |
| } |
| |
| return CaptureMainStack(ActiveProcessorMode(cpu_state), |
| stack_low_addr, |
| stack_high_addr, |
| cpu_state.extended.msp, |
| encoder, |
| thread_stack_callback); |
| } |
| |
| } // namespace pw::cpu_exception::cortex_m |