diff options
Diffstat (limited to 'pw_thread/public')
-rw-r--r-- | pw_thread/public/pw_thread/config.h | 10 | ||||
-rw-r--r-- | pw_thread/public/pw_thread/snapshot.h | 5 | ||||
-rw-r--r-- | pw_thread/public/pw_thread/thread.h | 14 | ||||
-rw-r--r-- | pw_thread/public/pw_thread/thread_info.h | 117 | ||||
-rw-r--r-- | pw_thread/public/pw_thread/thread_iteration.h | 37 | ||||
-rw-r--r-- | pw_thread/public/pw_thread/thread_snapshot_service.h | 85 |
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 |