• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 
15 #define PW_LOG_LEVEL PW_CPU_EXCEPTION_CORTEX_M_LOG_LEVEL
16 
17 #include "pw_cpu_exception_cortex_m/snapshot.h"
18 
19 #include "pw_cpu_exception_cortex_m/proto_dump.h"
20 #include "pw_cpu_exception_cortex_m/util.h"
21 #include "pw_cpu_exception_cortex_m_private/config.h"
22 #include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h"
23 #include "pw_cpu_exception_cortex_m_protos/cpu_state.pwpb.h"
24 #include "pw_log/log.h"
25 #include "pw_protobuf/encoder.h"
26 #include "pw_status/status.h"
27 #include "pw_thread/snapshot.h"
28 #include "pw_thread_protos/thread.pwpb.h"
29 
30 namespace pw::cpu_exception::cortex_m {
31 namespace {
32 
33 constexpr char kMainStackHandlerModeName[] = "Main Stack (Handler Mode)";
34 constexpr char kMainStackThreadModeName[] = "Main Stack (Thread Mode)";
35 
CaptureMainStack(ProcessorMode mode,uintptr_t stack_low_addr,uintptr_t stack_high_addr,uintptr_t stack_pointer,thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder & snapshot_encoder,thread::ProcessThreadStackCallback & thread_stack_callback)36 Status CaptureMainStack(
37     ProcessorMode mode,
38     uintptr_t stack_low_addr,
39     uintptr_t stack_high_addr,
40     uintptr_t stack_pointer,
41     thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder& snapshot_encoder,
42     thread::ProcessThreadStackCallback& thread_stack_callback) {
43   thread::proto::pwpb::Thread::StreamEncoder encoder =
44       snapshot_encoder.GetThreadsEncoder();
45 
46   const char* thread_name;
47   thread::proto::pwpb::ThreadState::Enum thread_state;
48   if (mode == ProcessorMode::kHandlerMode) {
49     thread_name = kMainStackHandlerModeName;
50     PW_LOG_DEBUG("Capturing thread info for Main Stack (Handler Mode)");
51     thread_state = thread::proto::pwpb::ThreadState::Enum::INTERRUPT_HANDLER;
52     PW_LOG_DEBUG("Thread state: INTERRUPT_HANDLER");
53   } else {  // mode == ProcessorMode::kThreadMode
54     thread_name = kMainStackThreadModeName;
55     PW_LOG_DEBUG("Capturing thread info for Main Stack (Thread Mode)");
56     thread_state = thread::proto::pwpb::ThreadState::Enum::RUNNING;
57     PW_LOG_DEBUG("Thread state: RUNNING");
58   }
59   // TODO: https://pwbug.dev/357138093 - Review IgnoreError calls in this file.
60   encoder.WriteState(thread_state).IgnoreError();
61   encoder.WriteName(as_bytes(span(std::string_view(thread_name))))
62       .IgnoreError();
63 
64   const thread::StackContext thread_ctx = {
65       .thread_name = thread_name,
66       .stack_low_addr = stack_low_addr,
67       .stack_high_addr = stack_high_addr,
68       .stack_pointer = stack_pointer,
69       .stack_pointer_est_peak = std::nullopt,
70   };
71   return thread::SnapshotStack(thread_ctx, encoder, thread_stack_callback);
72 }
73 
74 }  // namespace
75 
SnapshotCpuState(const pw_cpu_exception_State & cpu_state,pwpb::SnapshotCpuStateOverlay::StreamEncoder & snapshot_encoder)76 Status SnapshotCpuState(
77     const pw_cpu_exception_State& cpu_state,
78     pwpb::SnapshotCpuStateOverlay::StreamEncoder& snapshot_encoder) {
79   {
80     pwpb::ArmV7mCpuState::StreamEncoder cpu_state_encoder =
81         snapshot_encoder.GetArmv7mCpuStateEncoder();
82     DumpCpuStateProto(cpu_state_encoder, cpu_state).IgnoreError();
83   }
84   return snapshot_encoder.status();
85 }
86 
SnapshotMainStackThread(uintptr_t stack_low_addr,uintptr_t stack_high_addr,thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder & encoder,thread::ProcessThreadStackCallback & thread_stack_callback)87 Status SnapshotMainStackThread(
88     uintptr_t stack_low_addr,
89     uintptr_t stack_high_addr,
90     thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder& encoder,
91     thread::ProcessThreadStackCallback& thread_stack_callback) {
92   uintptr_t stack_pointer;
93   asm volatile("mrs %0, msp\n" : "=r"(stack_pointer));
94 
95   // First check if we're in Handler mode, AKA handling exceptions/interrupts.
96   //
97   // Handler mode vs thread mode can be determined via IPSR, bits 8:0 of xPSR.
98   // In thread mode the value is 0, in handler mode the value is non-zero.
99   uint32_t xpsr;
100   asm volatile("mrs %0, xpsr\n" : "=r"(xpsr));
101   if ((xpsr & kXpsrIpsrMask) != 0) {
102     return CaptureMainStack(ProcessorMode::kHandlerMode,
103                             stack_low_addr,
104                             stack_high_addr,
105                             stack_pointer,
106                             encoder,
107                             thread_stack_callback);
108   }
109 
110   // It looks like we're in Thread mode which means we need to check whether
111   // or not we are executing off the main stack currently.
112   //
113   // See ARMv7-M Architecture Reference Manual Section B1.4.4 for the control
114   // register values, in particular the SPSEL bit while in Thread mode which
115   // is 0 while running off the main stack and 1 while running off the proces
116   // stack.
117   uint32_t control;
118   asm volatile("mrs %0, control\n" : "=r"(control));
119   if ((control & kControlThreadModeStackMask) != 0) {
120     return OkStatus();  // Main stack is not currently active.
121   }
122 
123   // We're running off the main stack in Thread mode.
124   return CaptureMainStack(ProcessorMode::kThreadMode,
125                           stack_low_addr,
126                           stack_high_addr,
127                           stack_pointer,
128                           encoder,
129                           thread_stack_callback);
130 }
131 
SnapshotMainStackThread(const pw_cpu_exception_State & cpu_state,uintptr_t stack_low_addr,uintptr_t stack_high_addr,thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder & encoder,thread::ProcessThreadStackCallback & thread_stack_callback)132 Status SnapshotMainStackThread(
133     const pw_cpu_exception_State& cpu_state,
134     uintptr_t stack_low_addr,
135     uintptr_t stack_high_addr,
136     thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder& encoder,
137     thread::ProcessThreadStackCallback& thread_stack_callback) {
138   if (!MainStackActive(cpu_state)) {
139     return OkStatus();  // Main stack wasn't active, nothing to capture.
140   }
141 
142   return CaptureMainStack(ActiveProcessorMode(cpu_state),
143                           stack_low_addr,
144                           stack_high_addr,
145                           cpu_state.extended.msp,
146                           encoder,
147                           thread_stack_callback);
148 }
149 
150 }  // namespace pw::cpu_exception::cortex_m
151