mirror of
https://fuchsia.googlesource.com/third_party/pigweed.googlesource.com/pigweed/pigweed
synced 2024-09-20 13:51:05 +00:00
fec572b92d
Adds an optional library for capturing ThreadX thread state in proto snapshots. Change-Id: I47dbc5a57faf3d4411255ea83f04f17bf0a4dc46 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/51405 Commit-Queue: Rob Mohr <mohrr@google.com> Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com> Reviewed-by: Ewout van Bekkum <ewout@google.com>
132 lines
4.3 KiB
C++
132 lines
4.3 KiB
C++
// Copyright 2021 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.
|
|
#include "pw_thread_threadx/snapshot.h"
|
|
|
|
#include <string_view>
|
|
|
|
#include "pw_function/function.h"
|
|
#include "pw_protobuf/encoder.h"
|
|
#include "pw_status/status.h"
|
|
#include "pw_thread/snapshot.h"
|
|
#include "pw_thread_protos/thread.pwpb.h"
|
|
#include "pw_thread_threadx/util.h"
|
|
#include "tx_api.h"
|
|
#include "tx_thread.h"
|
|
|
|
namespace pw::thread::threadx {
|
|
namespace {
|
|
|
|
// TODO(amontanez): This might make unit testing codepaths that use this more
|
|
// challenging.
|
|
inline bool ThreadIsRunning(const TX_THREAD& thread) {
|
|
const TX_THREAD* running_thread;
|
|
TX_THREAD_GET_CURRENT(running_thread);
|
|
return running_thread == &thread;
|
|
}
|
|
|
|
void CaptureThreadState(const TX_THREAD& thread,
|
|
Thread::StreamEncoder& encoder) {
|
|
if (ThreadIsRunning(thread)) {
|
|
encoder.WriteState(ThreadState::Enum::RUNNING);
|
|
return;
|
|
}
|
|
|
|
switch (thread.tx_thread_state) {
|
|
case TX_READY:
|
|
encoder.WriteState(ThreadState::Enum::READY);
|
|
break;
|
|
case TX_COMPLETED:
|
|
case TX_TERMINATED:
|
|
encoder.WriteState(ThreadState::Enum::INACTIVE);
|
|
break;
|
|
case TX_SUSPENDED:
|
|
case TX_SLEEP:
|
|
encoder.WriteState(ThreadState::Enum::SUSPENDED);
|
|
break;
|
|
case TX_QUEUE_SUSP:
|
|
case TX_SEMAPHORE_SUSP:
|
|
case TX_EVENT_FLAG:
|
|
case TX_BLOCK_MEMORY:
|
|
case TX_BYTE_MEMORY:
|
|
case TX_IO_DRIVER:
|
|
case TX_FILE:
|
|
case TX_TCP_IP:
|
|
case TX_MUTEX_SUSP:
|
|
encoder.WriteState(ThreadState::Enum::BLOCKED);
|
|
break;
|
|
default:
|
|
encoder.WriteState(ThreadState::Enum::UNKNOWN);
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Status SnapshotThreads(void* running_thread_stack_pointer,
|
|
SnapshotThreadInfo::StreamEncoder& encoder,
|
|
ProcessThreadStackCallback& stack_dumper) {
|
|
struct {
|
|
void* running_thread_stack_pointer;
|
|
SnapshotThreadInfo::StreamEncoder* encoder;
|
|
ProcessThreadStackCallback* stack_dumper;
|
|
} ctx;
|
|
ctx.running_thread_stack_pointer = running_thread_stack_pointer;
|
|
ctx.encoder = &encoder;
|
|
ctx.stack_dumper = &stack_dumper;
|
|
|
|
ThreadCallback thread_capture_cb([&ctx](const TX_THREAD& thread) -> Status {
|
|
Thread::StreamEncoder thread_encoder = ctx.encoder->GetThreadsEncoder();
|
|
return SnapshotThread(thread,
|
|
ctx.running_thread_stack_pointer,
|
|
thread_encoder,
|
|
*ctx.stack_dumper);
|
|
});
|
|
|
|
return ForEachThread(thread_capture_cb);
|
|
}
|
|
|
|
Status SnapshotThread(const TX_THREAD& thread,
|
|
void* running_thread_stack_pointer,
|
|
Thread::StreamEncoder& encoder,
|
|
ProcessThreadStackCallback& thread_stack_callback) {
|
|
encoder.WriteName(
|
|
std::as_bytes(std::span(std::string_view(thread.tx_thread_name))));
|
|
|
|
CaptureThreadState(thread, encoder);
|
|
|
|
const StackContext thread_ctx = {
|
|
.thread_name = thread.tx_thread_name,
|
|
|
|
// TODO(amontanez): When ThreadX is built with stack checking enabled, the
|
|
// lowest-addressed `unsigned long` is reserved for a watermark. This
|
|
// means in practice the stack pointer should never end up there. To be
|
|
// conservative, behave as though TX_THREAD_STACK_CHECK is always fully
|
|
// enabled.
|
|
.stack_low_addr =
|
|
reinterpret_cast<uintptr_t>(thread.tx_thread_stack_start) +
|
|
sizeof(ULONG),
|
|
|
|
.stack_high_addr =
|
|
reinterpret_cast<uintptr_t>(thread.tx_thread_stack_end),
|
|
|
|
// If the thread is active, the stack pointer in the TCB is stale.
|
|
.stack_pointer = reinterpret_cast<uintptr_t>(
|
|
ThreadIsRunning(thread) ? running_thread_stack_pointer
|
|
: thread.tx_thread_stack_ptr),
|
|
};
|
|
|
|
return SnapshotStack(thread_ctx, encoder, thread_stack_callback);
|
|
}
|
|
|
|
} // namespace pw::thread::threadx
|