blob: 1a9e17d6790c5e6a7725d099aa2258932f60a05d [file] [log] [blame]
Armando Montanez952f2d52021-06-30 16:27:47 -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_EMBOS_CONFIG_LOG_LEVEL
16
Armando Montanez952f2d52021-06-30 16:27:47 -070017#include "pw_thread_embos/snapshot.h"
18
19#include <string_view>
20
21#include "RTOS.h"
22#include "pw_function/function.h"
23#include "pw_log/log.h"
24#include "pw_protobuf/encoder.h"
25#include "pw_status/status.h"
26#include "pw_thread/snapshot.h"
Armando Montanez5f10ff82021-08-13 13:47:39 -070027#include "pw_thread_embos/config.h"
Armando Montanez952f2d52021-06-30 16:27:47 -070028#include "pw_thread_embos/util.h"
29#include "pw_thread_protos/thread.pwpb.h"
30
31namespace pw::thread::embos {
32namespace {
33
34// TODO(amontanez): This might make unit testing codepaths that use this more
35// challenging.
36inline bool ThreadIsRunning(const OS_TASK& thread) {
37 return OS_GetpCurrentTask() == &thread;
38}
39
40void CaptureThreadState(const OS_TASK& thread, Thread::StreamEncoder& encoder) {
41 if (ThreadIsRunning(thread)) {
Armando Montaneze5a5f382021-08-09 15:50:26 -070042 PW_LOG_DEBUG("Thread state: RUNNING");
Armando Montanez952f2d52021-06-30 16:27:47 -070043 encoder.WriteState(ThreadState::Enum::RUNNING);
44 return;
45 }
46
47 // One byte is reserved for task status.
48 // - The lowest two bits are for a suspend counter.
49 // - The third-lowest bit is reserved for a "timeout." (ignored here)
50 // - The highest five bits indicate what the task is blocked on if non-zero.
51 //
52 // Note: embOS thread state is not part of the public API. This may not be
53 // correct for all versions. This has been tested on embOS 4.22, and was
54 // initially reported for embOS 5.06.
55 //
56 // Description of how `OS_TASK::Stat` is used by embOS:
57 // https://forum.segger.com/index.php/Thread/6548-ABANDONED-Task-state-values/?postID=23963#post23963
58#if OS_VERSION_GENERIC < 42200 || OS_VERSION_GENERIC > 50600
59#warning embOS thread state interpretation logic is not verfied as working on this version of embOS
60#endif // OS_VERSION_GENERIC < 42200 || OS_VERSION_GENERIC > 50600
61
62 if ((thread.Stat & 0x3) != 0) {
Armando Montaneze5a5f382021-08-09 15:50:26 -070063 PW_LOG_DEBUG("Thread state: SUSPENDED");
Armando Montanez952f2d52021-06-30 16:27:47 -070064 encoder.WriteState(ThreadState::Enum::SUSPENDED);
65 } else if ((thread.Stat & 0xf8) == 0) {
Armando Montaneze5a5f382021-08-09 15:50:26 -070066 PW_LOG_DEBUG("Thread state: READY");
Armando Montanez952f2d52021-06-30 16:27:47 -070067 encoder.WriteState(ThreadState::Enum::READY);
68 } else {
Armando Montaneze5a5f382021-08-09 15:50:26 -070069 PW_LOG_DEBUG("Thread state: BLOCKED");
Armando Montanez952f2d52021-06-30 16:27:47 -070070 encoder.WriteState(ThreadState::Enum::BLOCKED);
71 }
72}
73
74} // namespace
75
76Status SnapshotThreads(void* running_thread_stack_pointer,
77 SnapshotThreadInfo::StreamEncoder& encoder,
78 ProcessThreadStackCallback& stack_dumper) {
79 struct {
80 void* running_thread_stack_pointer;
81 SnapshotThreadInfo::StreamEncoder* encoder;
82 ProcessThreadStackCallback* stack_dumper;
Armando Montanez5d4eed62021-07-19 10:15:09 -070083 Status thread_capture_status;
Armando Montanez952f2d52021-06-30 16:27:47 -070084 } ctx;
85 ctx.running_thread_stack_pointer = running_thread_stack_pointer;
86 ctx.encoder = &encoder;
87 ctx.stack_dumper = &stack_dumper;
88
Armando Montanez5d4eed62021-07-19 10:15:09 -070089 ThreadCallback thread_capture_cb([&ctx](const OS_TASK& thread) -> bool {
Armando Montanez952f2d52021-06-30 16:27:47 -070090 Thread::StreamEncoder thread_encoder = ctx.encoder->GetThreadsEncoder();
Armando Montanez5d4eed62021-07-19 10:15:09 -070091 ctx.thread_capture_status.Update(
92 SnapshotThread(thread,
93 ctx.running_thread_stack_pointer,
94 thread_encoder,
95 *ctx.stack_dumper));
96 // Always iterate all threads.
97 return true;
Armando Montanez952f2d52021-06-30 16:27:47 -070098 });
99
Ewout van Bekkumb40a6b62021-07-23 21:32:49 -0700100 if (Status status = ForEachThread(thread_capture_cb);
101 !status.ok() && !status.IsFailedPrecondition()) {
Armando Montanez5d4eed62021-07-19 10:15:09 -0700102 PW_LOG_ERROR("Failed to iterate threads during snapshot capture: %d",
103 static_cast<int>(status.code()));
104 }
105
106 return ctx.thread_capture_status;
Armando Montanez952f2d52021-06-30 16:27:47 -0700107}
108
109Status SnapshotThread(const OS_TASK& thread,
110 void* running_thread_stack_pointer,
111 Thread::StreamEncoder& encoder,
112 ProcessThreadStackCallback& thread_stack_callback) {
113#if OS_TRACKNAME
Armando Montaneze5a5f382021-08-09 15:50:26 -0700114 PW_LOG_DEBUG("Capturing thread info for %s", thread.Name);
Armando Montanez952f2d52021-06-30 16:27:47 -0700115 encoder.WriteName(std::as_bytes(std::span(std::string_view(thread.Name))));
Ewout van Bekkum4bcb07b2021-07-26 12:21:29 -0700116#else
Armando Montaneze5a5f382021-08-09 15:50:26 -0700117 PW_LOG_DEBUG("Capturing thread info for thread at 0x%08x", &thread);
Armando Montanez952f2d52021-06-30 16:27:47 -0700118#endif // OS_TRACKNAME
119
120 CaptureThreadState(thread, encoder);
121
122#if OS_CHECKSTACK || OS_SUPPORT_MPU
123 const StackContext thread_ctx = {
124 .thread_name = thread.Name,
125
126 .stack_low_addr = reinterpret_cast<uintptr_t>(thread.pStackBot),
127
128 .stack_high_addr =
129 reinterpret_cast<uintptr_t>(thread.pStackBot) + thread.StackSize,
130
131 // If the thread is active, the stack pointer in the TCB is stale.
132 .stack_pointer = reinterpret_cast<uintptr_t>(
133 ThreadIsRunning(thread) ? running_thread_stack_pointer
134 : thread.pStack),
J. Silva06f85692021-09-08 15:44:18 -0700135 .stack_pointer_est_peak = reinterpret_cast<uintptr_t>(thread.pStackBot) +
136 thread.StackSize - OS_GetStackUsed(&thread),
Armando Montanez952f2d52021-06-30 16:27:47 -0700137 };
138
139 return SnapshotStack(thread_ctx, encoder, thread_stack_callback);
140#else
Armando Montaneze5a5f382021-08-09 15:50:26 -0700141 PW_LOG_DEBUG("Stack pointer: 0x%08x", running_thread_stack_pointer);
Armando Montanez952f2d52021-06-30 16:27:47 -0700142 encoder.WriteStackPointer(reinterpret_cast<uintptr_t>(
143 ThreadIsRunning(thread) ? running_thread_stack_pointer : thread.pStack));
144 return encoder.status();
145#endif // OS_CHECKSTACK || OS_SUPPORT_MPU
146}
147
148} // namespace pw::thread::embos