aboutsummaryrefslogtreecommitdiff
path: root/pw_thread/public
diff options
context:
space:
mode:
Diffstat (limited to 'pw_thread/public')
-rw-r--r--pw_thread/public/pw_thread/config.h10
-rw-r--r--pw_thread/public/pw_thread/snapshot.h5
-rw-r--r--pw_thread/public/pw_thread/thread.h14
-rw-r--r--pw_thread/public/pw_thread/thread_info.h117
-rw-r--r--pw_thread/public/pw_thread/thread_iteration.h37
-rw-r--r--pw_thread/public/pw_thread/thread_snapshot_service.h85
6 files changed, 263 insertions, 5 deletions
diff --git a/pw_thread/public/pw_thread/config.h b/pw_thread/public/pw_thread/config.h
index c2c0adce6..7b83a7eca 100644
--- a/pw_thread/public/pw_thread/config.h
+++ b/pw_thread/public/pw_thread/config.h
@@ -17,3 +17,13 @@
#ifndef PW_THREAD_CONFIG_LOG_LEVEL
#define PW_THREAD_CONFIG_LOG_LEVEL PW_LOG_LEVEL_DEBUG
#endif // PW_THREAD_CONFIG_LOG_LEVEL
+
+// The max number of threads to use by default for thread snapshot service.
+#ifndef PW_THREAD_MAXIMUM_THREADS
+#define PW_THREAD_MAXIMUM_THREADS 10
+#endif // PW_THREAD_MAXIMUM_THREADS
+
+// The max number of threads to bundle by default for thread snapshot service.
+#ifndef PW_THREAD_NUM_BUNDLED_THREADS
+#define PW_THREAD_NUM_BUNDLED_THREADS 3
+#endif // PW_THREAD_MAXIMUM_THREADS \ No newline at end of file
diff --git a/pw_thread/public/pw_thread/snapshot.h b/pw_thread/public/pw_thread/snapshot.h
index dfa96deda..c2502ae2b 100644
--- a/pw_thread/public/pw_thread/snapshot.h
+++ b/pw_thread/public/pw_thread/snapshot.h
@@ -13,6 +13,7 @@
// the License.
#pragma once
+#include <optional>
#include <string_view>
#include "pw_bytes/span.h"
@@ -27,7 +28,7 @@ namespace pw::thread {
// field. This should encode either raw_backtrace or raw_stack to the provided
// Thread stream encoder.
using ProcessThreadStackCallback =
- Function<Status(Thread::StreamEncoder&, ConstByteSpan)>;
+ Function<Status(proto::Thread::StreamEncoder&, ConstByteSpan)>;
struct StackContext {
std::string_view thread_name;
@@ -47,7 +48,7 @@ struct StackContext {
// stack_end_pointer
// stack_pointer
Status SnapshotStack(const StackContext& stack,
- Thread::StreamEncoder& encoder,
+ proto::Thread::StreamEncoder& encoder,
const ProcessThreadStackCallback& thread_stack_callback);
} // namespace pw::thread
diff --git a/pw_thread/public/pw_thread/thread.h b/pw_thread/public/pw_thread/thread.h
index aa9271cf1..792afaa1d 100644
--- a/pw_thread/public/pw_thread/thread.h
+++ b/pw_thread/public/pw_thread/thread.h
@@ -33,15 +33,23 @@ namespace pw::thread {
// core/processor affinity, and/or an optional reference to a pre-allocated
// Context (the collection of memory allocations needed for a thread to run).
//
-// Options shall NOT permit starting as detached, this must be done explicitly
-// through the Thread API.
+// Options shall NOT have an attribute to start threads as detached vs joinable.
+// All `pw::thread::Thread` instances must be explicitly `join()`'d or
+// `detach()`'d through the run-time Thread API.
+//
+// Note that if backends set `PW_THREAD_JOINING_ENABLED` to false, backends may
+// use native OS specific APIs to create native detached threads because the
+// `join()` API would be compiled out. However, users must still explicitly
+// invoke `detach()`.
//
// Options must not contain any memory needed for a thread to run (TCB,
// stack, etc.). The Options may be deleted or re-used immediately after
// starting a thread.
class Options {
protected:
- constexpr Options() = default;
+ // We can't use `= default` here, because it allows to create an Options
+ // instance in C++17 with `pw::thread::Options{}` syntax.
+ constexpr Options() {}
};
// The class Thread can represent a single thread of execution. Threads allow
diff --git a/pw_thread/public/pw_thread/thread_info.h b/pw_thread/public/pw_thread/thread_info.h
new file mode 100644
index 000000000..e18ebe524
--- /dev/null
+++ b/pw_thread/public/pw_thread/thread_info.h
@@ -0,0 +1,117 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <bitset>
+#include <optional>
+
+#include "pw_span/span.h"
+
+namespace pw::thread {
+
+// The class ThreadInfo provides a summary of specific thread information and is
+// used by thread iteration to dump thread info generically.
+//
+// Captures the following fields:
+// stack_start_pointer
+// stack_end_pointer
+// stack_est_peak_pointer
+// thread_name
+class ThreadInfo {
+ public:
+ ThreadInfo() = default;
+
+ constexpr std::optional<uintptr_t> stack_low_addr() const {
+ return get_stack_info_ptr(kStackLowAddress);
+ }
+
+ void set_stack_low_addr(uintptr_t val) {
+ set_stack_info_ptr(kStackLowAddress, val);
+ }
+
+ void clear_stack_low_addr() { clear_stack_info_ptr(kStackLowAddress); }
+
+ constexpr std::optional<uintptr_t> stack_high_addr() const {
+ return get_stack_info_ptr(kStackHighAddress);
+ }
+
+ void set_stack_high_addr(uintptr_t val) {
+ set_stack_info_ptr(kStackHighAddress, val);
+ }
+
+ void clear_stack_high_addr() { clear_stack_info_ptr(kStackHighAddress); }
+
+ constexpr std::optional<uintptr_t> stack_pointer() const {
+ return get_stack_info_ptr(kStackPointer);
+ }
+
+ void set_stack_pointer(uintptr_t val) {
+ set_stack_info_ptr(kStackPointer, val);
+ }
+
+ void clear_stack_pointer() { clear_stack_info_ptr(kStackPointer); }
+
+ constexpr std::optional<uintptr_t> stack_peak_addr() const {
+ return get_stack_info_ptr(kStackPeakAddress);
+ }
+
+ void set_stack_peak_addr(uintptr_t val) {
+ set_stack_info_ptr(kStackPeakAddress, val);
+ }
+
+ void clear_stack_peak_addr() { clear_stack_info_ptr(kStackPeakAddress); }
+
+ constexpr std::optional<span<const std::byte>> thread_name() const {
+ return has_value_[kThreadName] ? std::make_optional(thread_name_)
+ : std::nullopt;
+ }
+
+ void set_thread_name(span<const std::byte> val) {
+ thread_name_ = val;
+ has_value_.set(kThreadName, true);
+ }
+
+ void clear_thread_name() { clear_stack_info_ptr(kThreadName); }
+
+ private:
+ enum ThreadInfoIndex {
+ kStackLowAddress,
+ kStackHighAddress,
+ kStackPointer,
+ kStackPeakAddress,
+ kThreadName,
+ kMaxNumMembersDoNotUse,
+ };
+
+ constexpr std::optional<uintptr_t> get_stack_info_ptr(
+ ThreadInfoIndex index) const {
+ return has_value_[index] ? std::make_optional(stack_info_ptrs_[index])
+ : std::nullopt;
+ }
+
+ void set_stack_info_ptr(ThreadInfoIndex index, uintptr_t val) {
+ stack_info_ptrs_[index] = val;
+ has_value_.set(index, true);
+ }
+
+ void clear_stack_info_ptr(ThreadInfoIndex index) {
+ has_value_.set(index, false);
+ }
+
+ std::bitset<ThreadInfoIndex::kMaxNumMembersDoNotUse> has_value_;
+ uintptr_t stack_info_ptrs_[ThreadInfoIndex::kMaxNumMembersDoNotUse];
+ span<const std::byte> thread_name_;
+};
+
+} // namespace pw::thread
diff --git a/pw_thread/public/pw_thread/thread_iteration.h b/pw_thread/public/pw_thread/thread_iteration.h
new file mode 100644
index 000000000..d1babbae5
--- /dev/null
+++ b/pw_thread/public/pw_thread/thread_iteration.h
@@ -0,0 +1,37 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_function/function.h"
+#include "pw_status/status.h"
+#include "pw_thread/thread_info.h"
+
+namespace pw::thread {
+
+// A callback that is executed for each thread when using ForEachThread(). The
+// callback should return a struct reference for each thread iteration.
+//
+// This ThreadCallback should be treated as if the scheduler may be disabled,
+// meaning:
+// - Processing inside of the callback should be kept to a minimum.
+// - Callback should never attempt to block.
+using ThreadCallback = pw::Function<bool(const ThreadInfo&)>;
+
+// Iterates through all threads that haven't been deleted, calling the provided
+// callback on each thread.
+//
+// Warning: This may disable the scheduler.
+Status ForEachThread(const ThreadCallback& cb);
+
+} // namespace pw::thread
diff --git a/pw_thread/public/pw_thread/thread_snapshot_service.h b/pw_thread/public/pw_thread/thread_snapshot_service.h
new file mode 100644
index 000000000..06445eb0d
--- /dev/null
+++ b/pw_thread/public/pw_thread/thread_snapshot_service.h
@@ -0,0 +1,85 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_rpc/raw/server_reader_writer.h"
+#include "pw_span/span.h"
+#include "pw_status/status.h"
+#include "pw_thread/config.h"
+#include "pw_thread/thread_info.h"
+#include "pw_thread_protos/thread.pwpb.h"
+#include "pw_thread_protos/thread_snapshot_service.pwpb.h"
+#include "pw_thread_protos/thread_snapshot_service.raw_rpc.pb.h"
+
+namespace pw::thread::proto {
+
+Status ProtoEncodeThreadInfo(pwpb::SnapshotThreadInfo::StreamEncoder& encoder,
+ const ThreadInfo& thread_info);
+
+// Calculates encoded buffer size based on code gen constants.
+constexpr size_t RequiredServiceBufferSize(
+ size_t num_threads = PW_THREAD_MAXIMUM_THREADS) {
+ constexpr size_t kSizeOfResponse =
+ pwpb::SnapshotThreadInfo::kMaxEncodedSizeBytes +
+ pwpb::Thread::kMaxEncodedSizeBytes;
+ return kSizeOfResponse * num_threads;
+}
+
+// The ThreadSnapshotService will return peak stack usage across running
+// threads when requested by GetPeak().
+//
+// Parameter encode_buffer: buffer where thread information is encoded. Size
+// depends on RequiredBufferSize().
+//
+// Parameter thread_proto_indices: array keeping track of thread boundaries in
+// the encode buffer. The service uses these indices to send response data out
+// in bundles.
+//
+// Parameter num_bundled_threads: constant describing number of threads per
+// bundle in response.
+class ThreadSnapshotService
+ : public pw_rpc::raw::ThreadSnapshotService::Service<
+ ThreadSnapshotService> {
+ public:
+ constexpr ThreadSnapshotService(
+ span<std::byte> encode_buffer,
+ Vector<size_t>& thread_proto_indices,
+ size_t num_bundled_threads = PW_THREAD_NUM_BUNDLED_THREADS)
+ : encode_buffer_(encode_buffer),
+ thread_proto_indices_(thread_proto_indices),
+ num_bundled_threads_(num_bundled_threads) {}
+ void GetPeakStackUsage(ConstByteSpan request, rpc::RawServerWriter& response);
+
+ private:
+ span<std::byte> encode_buffer_;
+ Vector<size_t>& thread_proto_indices_;
+ size_t num_bundled_threads_;
+};
+
+// A ThreadSnapshotService that allocates required buffers based on the
+// number of running threads on a device.
+template <size_t kNumThreads = PW_THREAD_MAXIMUM_THREADS>
+class ThreadSnapshotServiceBuffer : public ThreadSnapshotService {
+ public:
+ ThreadSnapshotServiceBuffer()
+ : ThreadSnapshotService(encode_buffer_, thread_proto_indices_) {}
+
+ private:
+ std::array<std::byte, RequiredServiceBufferSize(kNumThreads)> encode_buffer_;
+ // + 1 is needed to account for extra index that comes with the first
+ // submessage start or the last submessage end.
+ Vector<size_t, kNumThreads + 1> thread_proto_indices_;
+};
+
+} // namespace pw::thread::proto