blob: d59771577f1423532517da608a88f5c11125ae67 [file] [log] [blame]
Ewout van Bekkumdb698f32021-07-14 20:58:39 -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 Montanez5f10ff82021-08-13 13:47:39 -070014
15#define PW_LOG_LEVEL PW_THREAD_FREERTOS_CONFIG_LOG_LEVEL
16
Ewout van Bekkumdb698f32021-07-14 20:58:39 -070017#include "pw_thread_freertos/snapshot.h"
18
19#include <span>
20#include <string_view>
21
22#include "FreeRTOS.h"
23#include "pw_function/function.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"
Armando Montanez5f10ff82021-08-13 13:47:39 -070028#include "pw_thread_freertos/config.h"
Ewout van Bekkumdb698f32021-07-14 20:58:39 -070029#include "pw_thread_freertos/freertos_tsktcb.h"
30#include "pw_thread_freertos/util.h"
31#include "pw_thread_protos/thread.pwpb.h"
32#include "task.h"
33
34namespace pw::thread::freertos {
35namespace {
36
37void CaptureThreadState(eTaskState thread_state,
38 Thread::StreamEncoder& encoder) {
39 switch (thread_state) {
40 case eRunning:
Armando Montaneze5a5f382021-08-09 15:50:26 -070041 PW_LOG_DEBUG("Thread state: RUNNING");
Ewout van Bekkumdb698f32021-07-14 20:58:39 -070042 encoder.WriteState(ThreadState::Enum::RUNNING);
43 return;
44
45 case eReady:
Armando Montaneze5a5f382021-08-09 15:50:26 -070046 PW_LOG_DEBUG("Thread state: READY");
Ewout van Bekkumdb698f32021-07-14 20:58:39 -070047 encoder.WriteState(ThreadState::Enum::READY);
48 return;
49
50 case eBlocked:
Armando Montaneze5a5f382021-08-09 15:50:26 -070051 PW_LOG_DEBUG("Thread state: BLOCKED");
Ewout van Bekkumdb698f32021-07-14 20:58:39 -070052 encoder.WriteState(ThreadState::Enum::BLOCKED);
53 return;
54
55 case eSuspended:
Armando Montaneze5a5f382021-08-09 15:50:26 -070056 PW_LOG_DEBUG("Thread state: SUSPENDED");
Ewout van Bekkumdb698f32021-07-14 20:58:39 -070057 encoder.WriteState(ThreadState::Enum::SUSPENDED);
58 return;
59
60 case eDeleted:
Armando Montaneze5a5f382021-08-09 15:50:26 -070061 PW_LOG_DEBUG("Thread state: INACTIVE");
Ewout van Bekkumdb698f32021-07-14 20:58:39 -070062 encoder.WriteState(ThreadState::Enum::INACTIVE);
63 return;
64
65 case eInvalid:
66 default:
Armando Montaneze5a5f382021-08-09 15:50:26 -070067 PW_LOG_DEBUG("Thread state: UNKNOWN");
Ewout van Bekkumdb698f32021-07-14 20:58:39 -070068 encoder.WriteState(ThreadState::Enum::UNKNOWN);
69 return;
70 }
71}
72
73} // namespace
74
75Status SnapshotThreads(void* running_thread_stack_pointer,
76 SnapshotThreadInfo::StreamEncoder& encoder,
77 ProcessThreadStackCallback& stack_dumper) {
78 struct {
79 void* running_thread_stack_pointer;
80 SnapshotThreadInfo::StreamEncoder* encoder;
81 ProcessThreadStackCallback* stack_dumper;
82 Status thread_capture_status;
83 } ctx;
84 ctx.running_thread_stack_pointer = running_thread_stack_pointer;
85 ctx.encoder = &encoder;
86 ctx.stack_dumper = &stack_dumper;
87 ctx.thread_capture_status = OkStatus();
88
89 ThreadCallback thread_capture_cb(
90 [&ctx](TaskHandle_t thread, eTaskState thread_state) -> bool {
91 Thread::StreamEncoder thread_encoder = ctx.encoder->GetThreadsEncoder();
92 ctx.thread_capture_status.Update(
93 SnapshotThread(thread,
94 thread_state,
95 ctx.running_thread_stack_pointer,
96 thread_encoder,
97 *ctx.stack_dumper));
98 return true; // Iterate through all threads.
99 });
Ewout van Bekkumb40a6b62021-07-23 21:32:49 -0700100 if (const Status status = ForEachThread(thread_capture_cb);
101 !status.ok() && !status.IsFailedPrecondition()) {
Ewout van Bekkumdb698f32021-07-14 20:58:39 -0700102 PW_LOG_ERROR("Failed to iterate threads during snapshot capture: %d",
103 status.code());
104 }
105 return ctx.thread_capture_status;
106}
107
108Status SnapshotThread(TaskHandle_t thread,
109 eTaskState thread_state,
110 void* running_thread_stack_pointer,
111 Thread::StreamEncoder& encoder,
112 ProcessThreadStackCallback& thread_stack_callback) {
113 const tskTCB& tcb = *reinterpret_cast<tskTCB*>(thread);
114
Armando Montaneze5a5f382021-08-09 15:50:26 -0700115 PW_LOG_DEBUG("Capturing thread info for %s", tcb.pcTaskName);
Ewout van Bekkumdb698f32021-07-14 20:58:39 -0700116 encoder.WriteName(std::as_bytes(std::span(std::string_view(tcb.pcTaskName))));
117
118 CaptureThreadState(thread_state, encoder);
119
120 // TODO(pwbug/422): Update this once we add support for ascending stacks.
121 static_assert(portSTACK_GROWTH < 0, "Ascending stacks are not yet supported");
122
123 // If the thread is active, the stack pointer in the TCB is stale.
124 const uintptr_t stack_pointer = reinterpret_cast<uintptr_t>(
125 thread_state == eRunning ? running_thread_stack_pointer
126 : tcb.pxTopOfStack);
127 const uintptr_t stack_low_addr = reinterpret_cast<uintptr_t>(tcb.pxStack);
128
129#if ((portSTACK_GROWTH > 0) || (configRECORD_STACK_HIGH_ADDRESS == 1))
130 const StackContext thread_ctx = {
131 .thread_name = tcb.pcTaskName,
132 .stack_low_addr = stack_low_addr,
133 .stack_high_addr = reinterpret_cast<uintptr_t>(tcb.pxEndOfStack),
134 .stack_pointer = stack_pointer,
J. Silva06f85692021-09-08 15:44:18 -0700135 .stack_pointer_est_peak = std::nullopt,
Ewout van Bekkumdb698f32021-07-14 20:58:39 -0700136 };
137 return SnapshotStack(thread_ctx, encoder, thread_stack_callback);
138#else
139 encoder.WriteStackEndPointer(stack_low_addr);
140 encoder.WriteStackPointer(stack_pointer);
141 return encoder.status();
142#endif // ((portSTACK_GROWTH > 0) || (configRECORD_STACK_HIGH_ADDRESS == 1))
143}
144
145} // namespace pw::thread::freertos