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_THREADX_CONFIG_LOG_LEVEL
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_threadx/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 "pw_function/function.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_protobuf/encoder.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_thread/snapshot.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_protos/thread.pwpb.h"
27*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_threadx/config.h"
28*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_threadx/util.h"
29*61c4878aSAndroid Build Coastguard Worker #include "tx_api.h"
30*61c4878aSAndroid Build Coastguard Worker #include "tx_thread.h"
31*61c4878aSAndroid Build Coastguard Worker
32*61c4878aSAndroid Build Coastguard Worker namespace pw::thread::threadx {
33*61c4878aSAndroid Build Coastguard Worker namespace {
34*61c4878aSAndroid Build Coastguard Worker
35*61c4878aSAndroid Build Coastguard Worker // TODO(amontanez): This might make unit testing codepaths that use this more
36*61c4878aSAndroid Build Coastguard Worker // challenging.
ThreadIsRunning(const TX_THREAD & thread)37*61c4878aSAndroid Build Coastguard Worker inline bool ThreadIsRunning(const TX_THREAD& thread) {
38*61c4878aSAndroid Build Coastguard Worker const TX_THREAD* running_thread;
39*61c4878aSAndroid Build Coastguard Worker TX_THREAD_GET_CURRENT(running_thread);
40*61c4878aSAndroid Build Coastguard Worker return running_thread == &thread;
41*61c4878aSAndroid Build Coastguard Worker }
42*61c4878aSAndroid Build Coastguard Worker
CaptureThreadState(const TX_THREAD & thread,proto::Thread::StreamEncoder & encoder)43*61c4878aSAndroid Build Coastguard Worker void CaptureThreadState(const TX_THREAD& thread,
44*61c4878aSAndroid Build Coastguard Worker proto::Thread::StreamEncoder& encoder) {
45*61c4878aSAndroid Build Coastguard Worker if (ThreadIsRunning(thread)) {
46*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Thread state: RUNNING");
47*61c4878aSAndroid Build Coastguard Worker encoder.WriteState(proto::ThreadState::Enum::RUNNING);
48*61c4878aSAndroid Build Coastguard Worker return;
49*61c4878aSAndroid Build Coastguard Worker }
50*61c4878aSAndroid Build Coastguard Worker
51*61c4878aSAndroid Build Coastguard Worker switch (thread.tx_thread_state) {
52*61c4878aSAndroid Build Coastguard Worker case TX_READY:
53*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Thread state: READY");
54*61c4878aSAndroid Build Coastguard Worker encoder.WriteState(proto::ThreadState::Enum::READY);
55*61c4878aSAndroid Build Coastguard Worker break;
56*61c4878aSAndroid Build Coastguard Worker case TX_COMPLETED:
57*61c4878aSAndroid Build Coastguard Worker case TX_TERMINATED:
58*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Thread state: INACTIVE");
59*61c4878aSAndroid Build Coastguard Worker encoder.WriteState(proto::ThreadState::Enum::INACTIVE);
60*61c4878aSAndroid Build Coastguard Worker break;
61*61c4878aSAndroid Build Coastguard Worker case TX_SUSPENDED:
62*61c4878aSAndroid Build Coastguard Worker case TX_SLEEP:
63*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Thread state: SUSPENDED");
64*61c4878aSAndroid Build Coastguard Worker encoder.WriteState(proto::ThreadState::Enum::SUSPENDED);
65*61c4878aSAndroid Build Coastguard Worker break;
66*61c4878aSAndroid Build Coastguard Worker case TX_QUEUE_SUSP:
67*61c4878aSAndroid Build Coastguard Worker case TX_SEMAPHORE_SUSP:
68*61c4878aSAndroid Build Coastguard Worker case TX_EVENT_FLAG:
69*61c4878aSAndroid Build Coastguard Worker case TX_BLOCK_MEMORY:
70*61c4878aSAndroid Build Coastguard Worker case TX_BYTE_MEMORY:
71*61c4878aSAndroid Build Coastguard Worker case TX_IO_DRIVER:
72*61c4878aSAndroid Build Coastguard Worker case TX_FILE:
73*61c4878aSAndroid Build Coastguard Worker case TX_TCP_IP:
74*61c4878aSAndroid Build Coastguard Worker case TX_MUTEX_SUSP:
75*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Thread state: BLOCKED");
76*61c4878aSAndroid Build Coastguard Worker encoder.WriteState(proto::ThreadState::Enum::BLOCKED);
77*61c4878aSAndroid Build Coastguard Worker break;
78*61c4878aSAndroid Build Coastguard Worker default:
79*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Thread state: UNKNOWN");
80*61c4878aSAndroid Build Coastguard Worker encoder.WriteState(proto::ThreadState::Enum::UNKNOWN);
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::SnapshotThreadInfo::StreamEncoder & encoder,ProcessThreadStackCallback & stack_dumper)86*61c4878aSAndroid Build Coastguard Worker Status SnapshotThreads(void* running_thread_stack_pointer,
87*61c4878aSAndroid Build Coastguard Worker proto::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::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
99*61c4878aSAndroid Build Coastguard Worker ThreadCallback thread_capture_cb([&ctx](const TX_THREAD& thread) -> bool {
100*61c4878aSAndroid Build Coastguard Worker proto::Thread::StreamEncoder thread_encoder =
101*61c4878aSAndroid Build Coastguard Worker ctx.encoder->GetThreadsEncoder();
102*61c4878aSAndroid Build Coastguard Worker ctx.thread_capture_status.Update(
103*61c4878aSAndroid Build Coastguard Worker SnapshotThread(thread,
104*61c4878aSAndroid Build Coastguard Worker ctx.running_thread_stack_pointer,
105*61c4878aSAndroid Build Coastguard Worker thread_encoder,
106*61c4878aSAndroid Build Coastguard Worker *ctx.stack_dumper));
107*61c4878aSAndroid Build Coastguard Worker // Always iterate all threads.
108*61c4878aSAndroid Build Coastguard Worker return true;
109*61c4878aSAndroid Build Coastguard Worker });
110*61c4878aSAndroid Build Coastguard Worker
111*61c4878aSAndroid Build Coastguard Worker if (Status status = ForEachThread(thread_capture_cb); !status.ok()) {
112*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to iterate threads during snapshot capture: %d",
113*61c4878aSAndroid Build Coastguard Worker static_cast<int>(status.code()));
114*61c4878aSAndroid Build Coastguard Worker }
115*61c4878aSAndroid Build Coastguard Worker
116*61c4878aSAndroid Build Coastguard Worker return ctx.thread_capture_status;
117*61c4878aSAndroid Build Coastguard Worker }
118*61c4878aSAndroid Build Coastguard Worker
SnapshotThread(const TX_THREAD & thread,void * running_thread_stack_pointer,proto::Thread::StreamEncoder & encoder,ProcessThreadStackCallback & thread_stack_callback)119*61c4878aSAndroid Build Coastguard Worker Status SnapshotThread(const TX_THREAD& thread,
120*61c4878aSAndroid Build Coastguard Worker void* running_thread_stack_pointer,
121*61c4878aSAndroid Build Coastguard Worker proto::Thread::StreamEncoder& encoder,
122*61c4878aSAndroid Build Coastguard Worker ProcessThreadStackCallback& thread_stack_callback) {
123*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Capturing thread info for %s", thread.tx_thread_name);
124*61c4878aSAndroid Build Coastguard Worker encoder.WriteName(as_bytes(span(std::string_view(thread.tx_thread_name))));
125*61c4878aSAndroid Build Coastguard Worker
126*61c4878aSAndroid Build Coastguard Worker CaptureThreadState(thread, encoder);
127*61c4878aSAndroid Build Coastguard Worker
128*61c4878aSAndroid Build Coastguard Worker const StackContext thread_ctx = {
129*61c4878aSAndroid Build Coastguard Worker .thread_name = thread.tx_thread_name,
130*61c4878aSAndroid Build Coastguard Worker
131*61c4878aSAndroid Build Coastguard Worker // TODO(amontanez): When ThreadX is built with stack checking enabled, the
132*61c4878aSAndroid Build Coastguard Worker // lowest-addressed `unsigned long` is reserved for a watermark. This
133*61c4878aSAndroid Build Coastguard Worker // means in practice the stack pointer should never end up there. To be
134*61c4878aSAndroid Build Coastguard Worker // conservative, behave as though TX_THREAD_STACK_CHECK is always fully
135*61c4878aSAndroid Build Coastguard Worker // enabled.
136*61c4878aSAndroid Build Coastguard Worker .stack_low_addr =
137*61c4878aSAndroid Build Coastguard Worker reinterpret_cast<uintptr_t>(thread.tx_thread_stack_start) +
138*61c4878aSAndroid Build Coastguard Worker sizeof(ULONG),
139*61c4878aSAndroid Build Coastguard Worker
140*61c4878aSAndroid Build Coastguard Worker .stack_high_addr =
141*61c4878aSAndroid Build Coastguard Worker reinterpret_cast<uintptr_t>(thread.tx_thread_stack_end),
142*61c4878aSAndroid Build Coastguard Worker
143*61c4878aSAndroid Build Coastguard Worker // If the thread is active, the stack pointer in the TCB is stale.
144*61c4878aSAndroid Build Coastguard Worker .stack_pointer = reinterpret_cast<uintptr_t>(
145*61c4878aSAndroid Build Coastguard Worker ThreadIsRunning(thread) ? running_thread_stack_pointer
146*61c4878aSAndroid Build Coastguard Worker : thread.tx_thread_stack_ptr),
147*61c4878aSAndroid Build Coastguard Worker .stack_pointer_est_peak = std::nullopt,
148*61c4878aSAndroid Build Coastguard Worker };
149*61c4878aSAndroid Build Coastguard Worker
150*61c4878aSAndroid Build Coastguard Worker return SnapshotStack(thread_ctx, encoder, thread_stack_callback);
151*61c4878aSAndroid Build Coastguard Worker }
152*61c4878aSAndroid Build Coastguard Worker
153*61c4878aSAndroid Build Coastguard Worker } // namespace pw::thread::threadx
154