xref: /aosp_15_r20/external/pigweed/pw_thread_freertos/snapshot.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2021 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #define PW_LOG_LEVEL PW_THREAD_FREERTOS_CONFIG_LOG_LEVEL
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_freertos/snapshot.h"
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include <string_view>
20*61c4878aSAndroid Build Coastguard Worker 
21*61c4878aSAndroid Build Coastguard Worker #include "FreeRTOS.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_function/function.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_protobuf/encoder.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_span/span.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
27*61c4878aSAndroid Build Coastguard Worker #include "pw_thread/snapshot.h"
28*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_freertos/config.h"
29*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_freertos/freertos_tsktcb.h"
30*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_freertos/util.h"
31*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_protos/thread.pwpb.h"
32*61c4878aSAndroid Build Coastguard Worker #include "task.h"
33*61c4878aSAndroid Build Coastguard Worker 
34*61c4878aSAndroid Build Coastguard Worker namespace pw::thread::freertos {
35*61c4878aSAndroid Build Coastguard Worker namespace {
36*61c4878aSAndroid Build Coastguard Worker 
37*61c4878aSAndroid Build Coastguard Worker // The externed function is an internal FreeRTOS kernel function from
38*61c4878aSAndroid Build Coastguard Worker // FreeRTOS/Source/tasks.c needed in order to calculate a thread's stack usage
39*61c4878aSAndroid Build Coastguard Worker // from interrupts which the native APIs do not permit.
40*61c4878aSAndroid Build Coastguard Worker #if ((configUSE_TRACE_FACILITY == 1) || \
41*61c4878aSAndroid Build Coastguard Worker      (INCLUDE_uxTaskGetStackHighWaterMark == 1))
42*61c4878aSAndroid Build Coastguard Worker extern "C" uint16_t prvTaskCheckFreeStackSpace(const uint8_t* pucStackByte);
43*61c4878aSAndroid Build Coastguard Worker #endif  // ((configUSE_TRACE_FACILITY == 1) ||
44*61c4878aSAndroid Build Coastguard Worker         // (INCLUDE_uxTaskGetStackHighWaterMark == 1))
45*61c4878aSAndroid Build Coastguard Worker 
CaptureThreadState(eTaskState thread_state,proto::pwpb::Thread::StreamEncoder & encoder)46*61c4878aSAndroid Build Coastguard Worker void CaptureThreadState(eTaskState thread_state,
47*61c4878aSAndroid Build Coastguard Worker                         proto::pwpb::Thread::StreamEncoder& encoder) {
48*61c4878aSAndroid Build Coastguard Worker   switch (thread_state) {
49*61c4878aSAndroid Build Coastguard Worker     case eRunning:
50*61c4878aSAndroid Build Coastguard Worker       PW_LOG_DEBUG("Thread state: RUNNING");
51*61c4878aSAndroid Build Coastguard Worker       encoder.WriteState(proto::pwpb::ThreadState::Enum::RUNNING).IgnoreError();
52*61c4878aSAndroid Build Coastguard Worker       return;
53*61c4878aSAndroid Build Coastguard Worker 
54*61c4878aSAndroid Build Coastguard Worker     case eReady:
55*61c4878aSAndroid Build Coastguard Worker       PW_LOG_DEBUG("Thread state: READY");
56*61c4878aSAndroid Build Coastguard Worker       encoder.WriteState(proto::pwpb::ThreadState::Enum::READY).IgnoreError();
57*61c4878aSAndroid Build Coastguard Worker       return;
58*61c4878aSAndroid Build Coastguard Worker 
59*61c4878aSAndroid Build Coastguard Worker     case eBlocked:
60*61c4878aSAndroid Build Coastguard Worker       PW_LOG_DEBUG("Thread state: BLOCKED");
61*61c4878aSAndroid Build Coastguard Worker       encoder.WriteState(proto::pwpb::ThreadState::Enum::BLOCKED).IgnoreError();
62*61c4878aSAndroid Build Coastguard Worker       return;
63*61c4878aSAndroid Build Coastguard Worker 
64*61c4878aSAndroid Build Coastguard Worker     case eSuspended:
65*61c4878aSAndroid Build Coastguard Worker       PW_LOG_DEBUG("Thread state: SUSPENDED");
66*61c4878aSAndroid Build Coastguard Worker       encoder.WriteState(proto::pwpb::ThreadState::Enum::SUSPENDED)
67*61c4878aSAndroid Build Coastguard Worker           .IgnoreError();
68*61c4878aSAndroid Build Coastguard Worker       return;
69*61c4878aSAndroid Build Coastguard Worker 
70*61c4878aSAndroid Build Coastguard Worker     case eDeleted:
71*61c4878aSAndroid Build Coastguard Worker       PW_LOG_DEBUG("Thread state: INACTIVE");
72*61c4878aSAndroid Build Coastguard Worker       encoder.WriteState(proto::pwpb::ThreadState::Enum::INACTIVE)
73*61c4878aSAndroid Build Coastguard Worker           .IgnoreError();
74*61c4878aSAndroid Build Coastguard Worker       return;
75*61c4878aSAndroid Build Coastguard Worker 
76*61c4878aSAndroid Build Coastguard Worker     case eInvalid:
77*61c4878aSAndroid Build Coastguard Worker     default:
78*61c4878aSAndroid Build Coastguard Worker       PW_LOG_DEBUG("Thread state: UNKNOWN");
79*61c4878aSAndroid Build Coastguard Worker       encoder.WriteState(proto::pwpb::ThreadState::Enum::UNKNOWN).IgnoreError();
80*61c4878aSAndroid Build Coastguard Worker       return;
81*61c4878aSAndroid Build Coastguard Worker   }
82*61c4878aSAndroid Build Coastguard Worker }
83*61c4878aSAndroid Build Coastguard Worker 
84*61c4878aSAndroid Build Coastguard Worker }  // namespace
85*61c4878aSAndroid Build Coastguard Worker 
SnapshotThreads(void * running_thread_stack_pointer,proto::pwpb::SnapshotThreadInfo::StreamEncoder & encoder,ProcessThreadStackCallback & stack_dumper)86*61c4878aSAndroid Build Coastguard Worker Status SnapshotThreads(void* running_thread_stack_pointer,
87*61c4878aSAndroid Build Coastguard Worker                        proto::pwpb::SnapshotThreadInfo::StreamEncoder& encoder,
88*61c4878aSAndroid Build Coastguard Worker                        ProcessThreadStackCallback& stack_dumper) {
89*61c4878aSAndroid Build Coastguard Worker   struct {
90*61c4878aSAndroid Build Coastguard Worker     void* running_thread_stack_pointer;
91*61c4878aSAndroid Build Coastguard Worker     proto::pwpb::SnapshotThreadInfo::StreamEncoder* encoder;
92*61c4878aSAndroid Build Coastguard Worker     ProcessThreadStackCallback* stack_dumper;
93*61c4878aSAndroid Build Coastguard Worker     Status thread_capture_status;
94*61c4878aSAndroid Build Coastguard Worker   } ctx;
95*61c4878aSAndroid Build Coastguard Worker   ctx.running_thread_stack_pointer = running_thread_stack_pointer;
96*61c4878aSAndroid Build Coastguard Worker   ctx.encoder = &encoder;
97*61c4878aSAndroid Build Coastguard Worker   ctx.stack_dumper = &stack_dumper;
98*61c4878aSAndroid Build Coastguard Worker   ctx.thread_capture_status = OkStatus();
99*61c4878aSAndroid Build Coastguard Worker 
100*61c4878aSAndroid Build Coastguard Worker   ThreadCallback thread_capture_cb(
101*61c4878aSAndroid Build Coastguard Worker       [&ctx](TaskHandle_t thread, eTaskState thread_state) -> bool {
102*61c4878aSAndroid Build Coastguard Worker         proto::pwpb::Thread::StreamEncoder thread_encoder =
103*61c4878aSAndroid Build Coastguard Worker             ctx.encoder->GetThreadsEncoder();
104*61c4878aSAndroid Build Coastguard Worker         ctx.thread_capture_status.Update(
105*61c4878aSAndroid Build Coastguard Worker             SnapshotThread(thread,
106*61c4878aSAndroid Build Coastguard Worker                            thread_state,
107*61c4878aSAndroid Build Coastguard Worker                            ctx.running_thread_stack_pointer,
108*61c4878aSAndroid Build Coastguard Worker                            thread_encoder,
109*61c4878aSAndroid Build Coastguard Worker                            *ctx.stack_dumper));
110*61c4878aSAndroid Build Coastguard Worker         return true;  // Iterate through all threads.
111*61c4878aSAndroid Build Coastguard Worker       });
112*61c4878aSAndroid Build Coastguard Worker   if (const Status status = ForEachThread(thread_capture_cb);
113*61c4878aSAndroid Build Coastguard Worker       !status.ok() && !status.IsFailedPrecondition()) {
114*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Failed to iterate threads during snapshot capture: %d",
115*61c4878aSAndroid Build Coastguard Worker                  status.code());
116*61c4878aSAndroid Build Coastguard Worker   }
117*61c4878aSAndroid Build Coastguard Worker   return ctx.thread_capture_status;
118*61c4878aSAndroid Build Coastguard Worker }
119*61c4878aSAndroid Build Coastguard Worker 
SnapshotThread(TaskHandle_t thread,eTaskState thread_state,void * running_thread_stack_pointer,proto::pwpb::Thread::StreamEncoder & encoder,ProcessThreadStackCallback & thread_stack_callback)120*61c4878aSAndroid Build Coastguard Worker Status SnapshotThread(
121*61c4878aSAndroid Build Coastguard Worker     TaskHandle_t thread,
122*61c4878aSAndroid Build Coastguard Worker     eTaskState thread_state,
123*61c4878aSAndroid Build Coastguard Worker     void* running_thread_stack_pointer,
124*61c4878aSAndroid Build Coastguard Worker     proto::pwpb::Thread::StreamEncoder& encoder,
125*61c4878aSAndroid Build Coastguard Worker     [[maybe_unused]] ProcessThreadStackCallback& thread_stack_callback) {
126*61c4878aSAndroid Build Coastguard Worker   const tskTCB& tcb = *reinterpret_cast<tskTCB*>(thread);
127*61c4878aSAndroid Build Coastguard Worker 
128*61c4878aSAndroid Build Coastguard Worker   PW_LOG_DEBUG("Capturing thread info for %s", tcb.pcTaskName);
129*61c4878aSAndroid Build Coastguard Worker   PW_TRY(encoder.WriteName(as_bytes(span(std::string_view(tcb.pcTaskName)))));
130*61c4878aSAndroid Build Coastguard Worker 
131*61c4878aSAndroid Build Coastguard Worker   CaptureThreadState(thread_state, encoder);
132*61c4878aSAndroid Build Coastguard Worker 
133*61c4878aSAndroid Build Coastguard Worker   // TODO: b/234890430 - Update this once we add support for ascending stacks.
134*61c4878aSAndroid Build Coastguard Worker   static_assert(portSTACK_GROWTH < 0, "Ascending stacks are not yet supported");
135*61c4878aSAndroid Build Coastguard Worker 
136*61c4878aSAndroid Build Coastguard Worker   // If running_thread_stack_pointer is null, always use the stack pointer
137*61c4878aSAndroid Build Coastguard Worker   // stored to the TCB.
138*61c4878aSAndroid Build Coastguard Worker   bool use_running_thread_stack_pointer =
139*61c4878aSAndroid Build Coastguard Worker       (thread_state == eRunning && running_thread_stack_pointer != nullptr);
140*61c4878aSAndroid Build Coastguard Worker 
141*61c4878aSAndroid Build Coastguard Worker   const uintptr_t stack_pointer = reinterpret_cast<uintptr_t>(
142*61c4878aSAndroid Build Coastguard Worker       use_running_thread_stack_pointer ? running_thread_stack_pointer
143*61c4878aSAndroid Build Coastguard Worker                                        : tcb.pxTopOfStack);
144*61c4878aSAndroid Build Coastguard Worker   const uintptr_t stack_low_addr = reinterpret_cast<uintptr_t>(tcb.pxStack);
145*61c4878aSAndroid Build Coastguard Worker 
146*61c4878aSAndroid Build Coastguard Worker #if ((portSTACK_GROWTH > 0) || (configRECORD_STACK_HIGH_ADDRESS == 1))
147*61c4878aSAndroid Build Coastguard Worker   const uintptr_t stack_high_addr =
148*61c4878aSAndroid Build Coastguard Worker       reinterpret_cast<uintptr_t>(tcb.pxEndOfStack);
149*61c4878aSAndroid Build Coastguard Worker   const StackContext thread_ctx = {
150*61c4878aSAndroid Build Coastguard Worker       .thread_name = tcb.pcTaskName,
151*61c4878aSAndroid Build Coastguard Worker       .stack_low_addr = stack_low_addr,
152*61c4878aSAndroid Build Coastguard Worker       .stack_high_addr = stack_high_addr,
153*61c4878aSAndroid Build Coastguard Worker       .stack_pointer = stack_pointer,
154*61c4878aSAndroid Build Coastguard Worker #if ((configUSE_TRACE_FACILITY == 1) || \
155*61c4878aSAndroid Build Coastguard Worker      (INCLUDE_uxTaskGetStackHighWaterMark == 1))
156*61c4878aSAndroid Build Coastguard Worker #if (portSTACK_GROWTH > 0)
157*61c4878aSAndroid Build Coastguard Worker       .stack_pointer_est_peak =
158*61c4878aSAndroid Build Coastguard Worker           stack_high_addr -
159*61c4878aSAndroid Build Coastguard Worker           (sizeof(StackType_t) *
160*61c4878aSAndroid Build Coastguard Worker            prvTaskCheckFreeStackSpace(
161*61c4878aSAndroid Build Coastguard Worker                reinterpret_cast<const uint8_t*>(stack_high_addr))),
162*61c4878aSAndroid Build Coastguard Worker #else
163*61c4878aSAndroid Build Coastguard Worker       .stack_pointer_est_peak =
164*61c4878aSAndroid Build Coastguard Worker           stack_low_addr +
165*61c4878aSAndroid Build Coastguard Worker           (sizeof(StackType_t) *
166*61c4878aSAndroid Build Coastguard Worker            prvTaskCheckFreeStackSpace(
167*61c4878aSAndroid Build Coastguard Worker                reinterpret_cast<const uint8_t*>(stack_low_addr))),
168*61c4878aSAndroid Build Coastguard Worker #endif  // (portSTACK_GROWTH > 0)
169*61c4878aSAndroid Build Coastguard Worker #else
170*61c4878aSAndroid Build Coastguard Worker       .stack_pointer_est_peak = std::nullopt,
171*61c4878aSAndroid Build Coastguard Worker #endif  // ((configUSE_TRACE_FACILITY == 1) ||
172*61c4878aSAndroid Build Coastguard Worker         // (INCLUDE_uxTaskGetStackHighWaterMark == 1))
173*61c4878aSAndroid Build Coastguard Worker   };
174*61c4878aSAndroid Build Coastguard Worker   return SnapshotStack(thread_ctx, encoder, thread_stack_callback);
175*61c4878aSAndroid Build Coastguard Worker #else
176*61c4878aSAndroid Build Coastguard Worker   encoder.WriteStackEndPointer(stack_low_addr);
177*61c4878aSAndroid Build Coastguard Worker   encoder.WriteStackPointer(stack_pointer);
178*61c4878aSAndroid Build Coastguard Worker   return encoder.status();
179*61c4878aSAndroid Build Coastguard Worker #endif  // ((portSTACK_GROWTH > 0) || (configRECORD_STACK_HIGH_ADDRESS == 1))
180*61c4878aSAndroid Build Coastguard Worker }
181*61c4878aSAndroid Build Coastguard Worker 
182*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::thread::freertos
183