blob: ca3db2cfdff2a3107eac52a62151b9a6e9b50f38 [file] [log] [blame]
// 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::SnapshotThreadInfo::StreamEncoder& snapshot_encoder,
thread::ProcessThreadStackCallback& thread_stack_callback) {
thread::Thread::StreamEncoder encoder = snapshot_encoder.GetThreadsEncoder();
const char* thread_name;
thread::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::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::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::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::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