blob: f38e16d47c7f9ecaa7cc3c25b9d1dfae99a3555e [file] [log] [blame]
Armando Montanezfec572b2021-06-28 12:13:57 -07001// 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.
Armando Montaneze5a5f382021-08-09 15:50:26 -070014
Armando Montanez5f10ff82021-08-13 13:47:39 -070015#define PW_LOG_LEVEL PW_THREAD_THREADX_CONFIG_LOG_LEVEL
16
Armando Montanezfec572b2021-06-28 12:13:57 -070017#include "pw_thread_threadx/snapshot.h"
18
19#include <string_view>
20
21#include "pw_function/function.h"
Armando Montanez5d08f062021-07-19 10:17:03 -070022#include "pw_log/log.h"
Armando Montanezfec572b2021-06-28 12:13:57 -070023#include "pw_protobuf/encoder.h"
24#include "pw_status/status.h"
25#include "pw_thread/snapshot.h"
26#include "pw_thread_protos/thread.pwpb.h"
Armando Montanez5f10ff82021-08-13 13:47:39 -070027#include "pw_thread_threadx/config.h"
Armando Montanezfec572b2021-06-28 12:13:57 -070028#include "pw_thread_threadx/util.h"
29#include "tx_api.h"
30#include "tx_thread.h"
31
32namespace pw::thread::threadx {
33namespace {
34
35// TODO(amontanez): This might make unit testing codepaths that use this more
36// challenging.
37inline bool ThreadIsRunning(const TX_THREAD& thread) {
38 const TX_THREAD* running_thread;
39 TX_THREAD_GET_CURRENT(running_thread);
40 return running_thread == &thread;
41}
42
43void CaptureThreadState(const TX_THREAD& thread,
44 Thread::StreamEncoder& encoder) {
45 if (ThreadIsRunning(thread)) {
Armando Montaneze5a5f382021-08-09 15:50:26 -070046 PW_LOG_DEBUG("Thread state: RUNNING");
Armando Montanezfec572b2021-06-28 12:13:57 -070047 encoder.WriteState(ThreadState::Enum::RUNNING);
48 return;
49 }
50
51 switch (thread.tx_thread_state) {
52 case TX_READY:
Armando Montaneze5a5f382021-08-09 15:50:26 -070053 PW_LOG_DEBUG("Thread state: READY");
Armando Montanezfec572b2021-06-28 12:13:57 -070054 encoder.WriteState(ThreadState::Enum::READY);
55 break;
56 case TX_COMPLETED:
57 case TX_TERMINATED:
Armando Montaneze5a5f382021-08-09 15:50:26 -070058 PW_LOG_DEBUG("Thread state: INACTIVE");
Armando Montanezfec572b2021-06-28 12:13:57 -070059 encoder.WriteState(ThreadState::Enum::INACTIVE);
60 break;
61 case TX_SUSPENDED:
62 case TX_SLEEP:
Armando Montaneze5a5f382021-08-09 15:50:26 -070063 PW_LOG_DEBUG("Thread state: SUSPENDED");
Armando Montanezfec572b2021-06-28 12:13:57 -070064 encoder.WriteState(ThreadState::Enum::SUSPENDED);
65 break;
66 case TX_QUEUE_SUSP:
67 case TX_SEMAPHORE_SUSP:
68 case TX_EVENT_FLAG:
69 case TX_BLOCK_MEMORY:
70 case TX_BYTE_MEMORY:
71 case TX_IO_DRIVER:
72 case TX_FILE:
73 case TX_TCP_IP:
74 case TX_MUTEX_SUSP:
Armando Montaneze5a5f382021-08-09 15:50:26 -070075 PW_LOG_DEBUG("Thread state: BLOCKED");
Armando Montanezfec572b2021-06-28 12:13:57 -070076 encoder.WriteState(ThreadState::Enum::BLOCKED);
77 break;
78 default:
Armando Montaneze5a5f382021-08-09 15:50:26 -070079 PW_LOG_DEBUG("Thread state: UNKNOWN");
Armando Montanezfec572b2021-06-28 12:13:57 -070080 encoder.WriteState(ThreadState::Enum::UNKNOWN);
81 }
82}
83
84} // namespace
85
86Status SnapshotThreads(void* running_thread_stack_pointer,
87 SnapshotThreadInfo::StreamEncoder& encoder,
88 ProcessThreadStackCallback& stack_dumper) {
89 struct {
90 void* running_thread_stack_pointer;
91 SnapshotThreadInfo::StreamEncoder* encoder;
92 ProcessThreadStackCallback* stack_dumper;
Armando Montanez5d08f062021-07-19 10:17:03 -070093 Status thread_capture_status;
Armando Montanezfec572b2021-06-28 12:13:57 -070094 } ctx;
95 ctx.running_thread_stack_pointer = running_thread_stack_pointer;
96 ctx.encoder = &encoder;
97 ctx.stack_dumper = &stack_dumper;
98
Armando Montanez5d08f062021-07-19 10:17:03 -070099 ThreadCallback thread_capture_cb([&ctx](const TX_THREAD& thread) -> bool {
Armando Montanezfec572b2021-06-28 12:13:57 -0700100 Thread::StreamEncoder thread_encoder = ctx.encoder->GetThreadsEncoder();
Armando Montanez5d08f062021-07-19 10:17:03 -0700101 ctx.thread_capture_status.Update(
102 SnapshotThread(thread,
103 ctx.running_thread_stack_pointer,
104 thread_encoder,
105 *ctx.stack_dumper));
106 // Always iterate all threads.
107 return true;
Armando Montanezfec572b2021-06-28 12:13:57 -0700108 });
109
Armando Montanez5d08f062021-07-19 10:17:03 -0700110 if (Status status = ForEachThread(thread_capture_cb); !status.ok()) {
111 PW_LOG_ERROR("Failed to iterate threads during snapshot capture: %d",
112 static_cast<int>(status.code()));
113 }
114
115 return ctx.thread_capture_status;
Armando Montanezfec572b2021-06-28 12:13:57 -0700116}
117
118Status SnapshotThread(const TX_THREAD& thread,
119 void* running_thread_stack_pointer,
120 Thread::StreamEncoder& encoder,
121 ProcessThreadStackCallback& thread_stack_callback) {
Armando Montaneze5a5f382021-08-09 15:50:26 -0700122 PW_LOG_DEBUG("Capturing thread info for %s", thread.tx_thread_name);
Armando Montanezfec572b2021-06-28 12:13:57 -0700123 encoder.WriteName(
124 std::as_bytes(std::span(std::string_view(thread.tx_thread_name))));
125
126 CaptureThreadState(thread, encoder);
127
128 const StackContext thread_ctx = {
129 .thread_name = thread.tx_thread_name,
130
131 // TODO(amontanez): When ThreadX is built with stack checking enabled, the
132 // lowest-addressed `unsigned long` is reserved for a watermark. This
133 // means in practice the stack pointer should never end up there. To be
134 // conservative, behave as though TX_THREAD_STACK_CHECK is always fully
135 // enabled.
136 .stack_low_addr =
137 reinterpret_cast<uintptr_t>(thread.tx_thread_stack_start) +
138 sizeof(ULONG),
139
140 .stack_high_addr =
141 reinterpret_cast<uintptr_t>(thread.tx_thread_stack_end),
142
143 // If the thread is active, the stack pointer in the TCB is stale.
144 .stack_pointer = reinterpret_cast<uintptr_t>(
145 ThreadIsRunning(thread) ? running_thread_stack_pointer
146 : thread.tx_thread_stack_ptr),
J. Silva06f85692021-09-08 15:44:18 -0700147 .stack_pointer_est_peak = std::nullopt,
Armando Montanezfec572b2021-06-28 12:13:57 -0700148 };
149
150 return SnapshotStack(thread_ctx, encoder, thread_stack_callback);
151}
152
153} // namespace pw::thread::threadx