xref: /aosp_15_r20/external/pigweed/pw_thread_embos/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_EMBOS_CONFIG_LOG_LEVEL
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_embos/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 "RTOS.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_status/status.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_thread/snapshot.h"
27*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_embos/config.h"
28*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_embos/util.h"
29*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_protos/thread.pwpb.h"
30*61c4878aSAndroid Build Coastguard Worker 
31*61c4878aSAndroid Build Coastguard Worker namespace pw::thread::embos {
32*61c4878aSAndroid Build Coastguard Worker namespace {
33*61c4878aSAndroid Build Coastguard Worker 
34*61c4878aSAndroid Build Coastguard Worker // TODO(amontanez): This might make unit testing codepaths that use this more
35*61c4878aSAndroid Build Coastguard Worker // challenging.
ThreadIsRunning(const OS_TASK & thread)36*61c4878aSAndroid Build Coastguard Worker inline bool ThreadIsRunning(const OS_TASK& thread) {
37*61c4878aSAndroid Build Coastguard Worker   return OS_GetpCurrentTask() == &thread;
38*61c4878aSAndroid Build Coastguard Worker }
39*61c4878aSAndroid Build Coastguard Worker 
CaptureThreadState(const OS_TASK & thread,proto::Thread::StreamEncoder & encoder)40*61c4878aSAndroid Build Coastguard Worker void CaptureThreadState(const OS_TASK& thread,
41*61c4878aSAndroid Build Coastguard Worker                         proto::Thread::StreamEncoder& encoder) {
42*61c4878aSAndroid Build Coastguard Worker   if (ThreadIsRunning(thread)) {
43*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Thread state: RUNNING");
44*61c4878aSAndroid Build Coastguard Worker     encoder.WriteState(proto::ThreadState::Enum::RUNNING);
45*61c4878aSAndroid Build Coastguard Worker     return;
46*61c4878aSAndroid Build Coastguard Worker   }
47*61c4878aSAndroid Build Coastguard Worker 
48*61c4878aSAndroid Build Coastguard Worker   // One byte is reserved for task status.
49*61c4878aSAndroid Build Coastguard Worker   //   - The lowest two bits are for a suspend counter.
50*61c4878aSAndroid Build Coastguard Worker   //   - The third-lowest bit is reserved for a "timeout." (ignored here)
51*61c4878aSAndroid Build Coastguard Worker   //   - The highest five bits indicate what the task is blocked on if non-zero.
52*61c4878aSAndroid Build Coastguard Worker   //
53*61c4878aSAndroid Build Coastguard Worker   // Note: embOS thread state is not part of the public API. This may not be
54*61c4878aSAndroid Build Coastguard Worker   // correct for all versions. This has been tested on embOS 4.22, and was
55*61c4878aSAndroid Build Coastguard Worker   // initially reported for embOS 5.06.
56*61c4878aSAndroid Build Coastguard Worker   //
57*61c4878aSAndroid Build Coastguard Worker   // Description of how `OS_TASK::Stat` is used by embOS:
58*61c4878aSAndroid Build Coastguard Worker   //   https://forum.segger.com/index.php/Thread/6548-ABANDONED-Task-state-values/?postID=23963#post23963
59*61c4878aSAndroid Build Coastguard Worker #if OS_VERSION_GENERIC < 42200 || OS_VERSION_GENERIC > 50600
60*61c4878aSAndroid Build Coastguard Worker #warning embOS thread state interpretation logic is not verfied as working on this version of embOS
61*61c4878aSAndroid Build Coastguard Worker #endif  // OS_VERSION_GENERIC < 42200 || OS_VERSION_GENERIC > 50600
62*61c4878aSAndroid Build Coastguard Worker 
63*61c4878aSAndroid Build Coastguard Worker   if ((thread.Stat & 0x3) != 0) {
64*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Thread state: SUSPENDED");
65*61c4878aSAndroid Build Coastguard Worker     encoder.WriteState(proto::ThreadState::Enum::SUSPENDED);
66*61c4878aSAndroid Build Coastguard Worker   } else if ((thread.Stat & 0xf8) == 0) {
67*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Thread state: READY");
68*61c4878aSAndroid Build Coastguard Worker     encoder.WriteState(proto::ThreadState::Enum::READY);
69*61c4878aSAndroid Build Coastguard Worker   } else {
70*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Thread state: BLOCKED");
71*61c4878aSAndroid Build Coastguard Worker     encoder.WriteState(proto::ThreadState::Enum::BLOCKED);
72*61c4878aSAndroid Build Coastguard Worker   }
73*61c4878aSAndroid Build Coastguard Worker }
74*61c4878aSAndroid Build Coastguard Worker 
75*61c4878aSAndroid Build Coastguard Worker }  // namespace
76*61c4878aSAndroid Build Coastguard Worker 
SnapshotThreads(void * running_thread_stack_pointer,proto::SnapshotThreadInfo::StreamEncoder & encoder,ProcessThreadStackCallback & stack_dumper)77*61c4878aSAndroid Build Coastguard Worker Status SnapshotThreads(void* running_thread_stack_pointer,
78*61c4878aSAndroid Build Coastguard Worker                        proto::SnapshotThreadInfo::StreamEncoder& encoder,
79*61c4878aSAndroid Build Coastguard Worker                        ProcessThreadStackCallback& stack_dumper) {
80*61c4878aSAndroid Build Coastguard Worker   struct {
81*61c4878aSAndroid Build Coastguard Worker     void* running_thread_stack_pointer;
82*61c4878aSAndroid Build Coastguard Worker     proto::SnapshotThreadInfo::StreamEncoder* encoder;
83*61c4878aSAndroid Build Coastguard Worker     ProcessThreadStackCallback* stack_dumper;
84*61c4878aSAndroid Build Coastguard Worker     Status thread_capture_status;
85*61c4878aSAndroid Build Coastguard Worker   } ctx;
86*61c4878aSAndroid Build Coastguard Worker   ctx.running_thread_stack_pointer = running_thread_stack_pointer;
87*61c4878aSAndroid Build Coastguard Worker   ctx.encoder = &encoder;
88*61c4878aSAndroid Build Coastguard Worker   ctx.stack_dumper = &stack_dumper;
89*61c4878aSAndroid Build Coastguard Worker 
90*61c4878aSAndroid Build Coastguard Worker   ThreadCallback thread_capture_cb([&ctx](const OS_TASK& thread) -> bool {
91*61c4878aSAndroid Build Coastguard Worker     proto::Thread::StreamEncoder thread_encoder =
92*61c4878aSAndroid Build Coastguard Worker         ctx.encoder->GetThreadsEncoder();
93*61c4878aSAndroid Build Coastguard Worker     ctx.thread_capture_status.Update(
94*61c4878aSAndroid Build Coastguard Worker         SnapshotThread(thread,
95*61c4878aSAndroid Build Coastguard Worker                        ctx.running_thread_stack_pointer,
96*61c4878aSAndroid Build Coastguard Worker                        thread_encoder,
97*61c4878aSAndroid Build Coastguard Worker                        *ctx.stack_dumper));
98*61c4878aSAndroid Build Coastguard Worker     // Always iterate all threads.
99*61c4878aSAndroid Build Coastguard Worker     return true;
100*61c4878aSAndroid Build Coastguard Worker   });
101*61c4878aSAndroid Build Coastguard Worker 
102*61c4878aSAndroid Build Coastguard Worker   if (Status status = ForEachThread(thread_capture_cb);
103*61c4878aSAndroid Build Coastguard Worker       !status.ok() && !status.IsFailedPrecondition()) {
104*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Failed to iterate threads during snapshot capture: %d",
105*61c4878aSAndroid Build Coastguard Worker                  static_cast<int>(status.code()));
106*61c4878aSAndroid Build Coastguard Worker   }
107*61c4878aSAndroid Build Coastguard Worker 
108*61c4878aSAndroid Build Coastguard Worker   return ctx.thread_capture_status;
109*61c4878aSAndroid Build Coastguard Worker }
110*61c4878aSAndroid Build Coastguard Worker 
SnapshotThread(const OS_TASK & thread,void * running_thread_stack_pointer,proto::Thread::StreamEncoder & encoder,ProcessThreadStackCallback & thread_stack_callback)111*61c4878aSAndroid Build Coastguard Worker Status SnapshotThread(const OS_TASK& thread,
112*61c4878aSAndroid Build Coastguard Worker                       void* running_thread_stack_pointer,
113*61c4878aSAndroid Build Coastguard Worker                       proto::Thread::StreamEncoder& encoder,
114*61c4878aSAndroid Build Coastguard Worker                       ProcessThreadStackCallback& thread_stack_callback) {
115*61c4878aSAndroid Build Coastguard Worker #if OS_TRACKNAME
116*61c4878aSAndroid Build Coastguard Worker   PW_LOG_DEBUG("Capturing thread info for %s", thread.Name);
117*61c4878aSAndroid Build Coastguard Worker   encoder.WriteName(as_bytes(span(std::string_view(thread.Name))));
118*61c4878aSAndroid Build Coastguard Worker #else
119*61c4878aSAndroid Build Coastguard Worker   PW_LOG_DEBUG("Capturing thread info for thread at 0x%08x", &thread);
120*61c4878aSAndroid Build Coastguard Worker #endif  // OS_TRACKNAME
121*61c4878aSAndroid Build Coastguard Worker 
122*61c4878aSAndroid Build Coastguard Worker   CaptureThreadState(thread, encoder);
123*61c4878aSAndroid Build Coastguard Worker 
124*61c4878aSAndroid Build Coastguard Worker #if OS_CHECKSTACK || OS_SUPPORT_MPU
125*61c4878aSAndroid Build Coastguard Worker   const StackContext thread_ctx = {
126*61c4878aSAndroid Build Coastguard Worker       .thread_name = thread.Name,
127*61c4878aSAndroid Build Coastguard Worker 
128*61c4878aSAndroid Build Coastguard Worker       .stack_low_addr = reinterpret_cast<uintptr_t>(thread.pStackBot),
129*61c4878aSAndroid Build Coastguard Worker 
130*61c4878aSAndroid Build Coastguard Worker       .stack_high_addr =
131*61c4878aSAndroid Build Coastguard Worker           reinterpret_cast<uintptr_t>(thread.pStackBot) + thread.StackSize,
132*61c4878aSAndroid Build Coastguard Worker 
133*61c4878aSAndroid Build Coastguard Worker       // If the thread is active, the stack pointer in the TCB is stale.
134*61c4878aSAndroid Build Coastguard Worker       .stack_pointer = reinterpret_cast<uintptr_t>(
135*61c4878aSAndroid Build Coastguard Worker           ThreadIsRunning(thread) ? running_thread_stack_pointer
136*61c4878aSAndroid Build Coastguard Worker                                   : thread.pStack),
137*61c4878aSAndroid Build Coastguard Worker       .stack_pointer_est_peak = reinterpret_cast<uintptr_t>(thread.pStackBot) +
138*61c4878aSAndroid Build Coastguard Worker                                 thread.StackSize - OS_GetStackUsed(&thread),
139*61c4878aSAndroid Build Coastguard Worker   };
140*61c4878aSAndroid Build Coastguard Worker 
141*61c4878aSAndroid Build Coastguard Worker   return SnapshotStack(thread_ctx, encoder, thread_stack_callback);
142*61c4878aSAndroid Build Coastguard Worker #else
143*61c4878aSAndroid Build Coastguard Worker   PW_LOG_DEBUG("Stack pointer: 0x%08x", running_thread_stack_pointer);
144*61c4878aSAndroid Build Coastguard Worker   encoder.WriteStackPointer(reinterpret_cast<uintptr_t>(
145*61c4878aSAndroid Build Coastguard Worker       ThreadIsRunning(thread) ? running_thread_stack_pointer : thread.pStack));
146*61c4878aSAndroid Build Coastguard Worker   return encoder.status();
147*61c4878aSAndroid Build Coastguard Worker #endif  // OS_CHECKSTACK || OS_SUPPORT_MPU
148*61c4878aSAndroid Build Coastguard Worker }
149*61c4878aSAndroid Build Coastguard Worker 
150*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::thread::embos
151