mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
Use new Fuchsia kernel API to suspend threads.
We are transitioning to a token-based API and will be removing the old one. Changes to use a thread state wait rather than reading the registers in a loop to determine when the thread is actually suspended. Change-Id: I4b015bb0fc74b15177304a62be6c1d9a59b45c80 Reviewed-on: https://chromium-review.googlesource.com/1100170 Commit-Queue: Brett Wilson <brettw@chromium.org> Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
parent
2771ebf805
commit
639cba075c
@ -40,80 +40,42 @@ zx_obj_type_t GetHandleType(zx_handle_t handle) {
|
|||||||
return basic.type;
|
return basic.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SuspensionResult {
|
// Returns the suspend token of the suspended thread. This function attempts
|
||||||
FailedSuspendCall,
|
// to wait a short time for the thread to actually suspend before returning
|
||||||
FailedToSuspendInTimelyFashion,
|
// but this is not guaranteed.
|
||||||
Succeeded,
|
base::ScopedZxHandle SuspendThread(zx_handle_t thread) {
|
||||||
};
|
zx_handle_t token = ZX_HANDLE_INVALID;
|
||||||
|
zx_status_t status = zx_task_suspend_token(thread, &token);
|
||||||
SuspensionResult SuspendThread(zx_handle_t thread) {
|
if (status != ZX_OK) {
|
||||||
zx_status_t status = zx_task_suspend(thread);
|
ZX_LOG(ERROR, status) << "zx_task_suspend";
|
||||||
ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_task_suspend";
|
base::ScopedZxHandle();
|
||||||
if (status != ZX_OK)
|
|
||||||
return SuspensionResult::FailedSuspendCall;
|
|
||||||
// zx_task_suspend() suspends the thread "sometime soon", but it's hard to
|
|
||||||
// use when it's not guaranteed to be suspended after return. Try reading the
|
|
||||||
// thread state until the registers are retrievable, which means that the
|
|
||||||
// thread is actually suspended. Don't wait forever in case the suspend
|
|
||||||
// failed for whatever reason, but try a few times.
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
|
||||||
zx_thread_state_general_regs_t regs;
|
|
||||||
status = zx_thread_read_state(
|
|
||||||
thread, ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs));
|
|
||||||
if (status == ZX_OK) {
|
|
||||||
return SuspensionResult::Succeeded;
|
|
||||||
}
|
|
||||||
zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
|
|
||||||
}
|
}
|
||||||
LOG(ERROR) << "thread failed to suspend";
|
|
||||||
return SuspensionResult::FailedToSuspendInTimelyFashion;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ResumeThread(zx_handle_t thread) {
|
zx_signals_t observed = 0u;
|
||||||
zx_status_t status = zx_task_resume(thread, 0);
|
if (zx_object_wait_one(thread, ZX_THREAD_SUSPENDED,
|
||||||
ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_task_resume";
|
zx_deadline_after(ZX_MSEC(50)), &observed) != ZX_OK) {
|
||||||
return status == ZX_OK;
|
LOG(ERROR) << "thread failed to suspend";
|
||||||
|
}
|
||||||
|
return base::ScopedZxHandle(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ScopedTaskSuspend::ScopedTaskSuspend(zx_handle_t task) : task_(task) {
|
ScopedTaskSuspend::ScopedTaskSuspend(zx_handle_t task) {
|
||||||
DCHECK_NE(task_, zx_process_self());
|
DCHECK_NE(task, zx_process_self());
|
||||||
DCHECK_NE(task_, zx_thread_self());
|
DCHECK_NE(task, zx_thread_self());
|
||||||
|
|
||||||
zx_obj_type_t type = GetHandleType(task_);
|
zx_obj_type_t type = GetHandleType(task);
|
||||||
if (type == ZX_OBJ_TYPE_THREAD) {
|
if (type == ZX_OBJ_TYPE_THREAD) {
|
||||||
// Note that task_ is only marked invalid if the zx_task_suspend() call
|
suspend_tokens_.push_back(SuspendThread(task));
|
||||||
// completely fails, otherwise the suspension might just not have taken
|
|
||||||
// effect yet, so avoid leaving it suspended forever by still resuming on
|
|
||||||
// destruction.
|
|
||||||
if (SuspendThread(task_) == SuspensionResult::FailedSuspendCall) {
|
|
||||||
task_ = ZX_HANDLE_INVALID;
|
|
||||||
}
|
|
||||||
} else if (type == ZX_OBJ_TYPE_PROCESS) {
|
} else if (type == ZX_OBJ_TYPE_PROCESS) {
|
||||||
for (const auto& thread : GetChildHandles(task_, ZX_INFO_PROCESS_THREADS)) {
|
for (const auto& thread : GetChildHandles(task, ZX_INFO_PROCESS_THREADS))
|
||||||
SuspendThread(thread.get());
|
suspend_tokens_.push_back(SuspendThread(thread.get()));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "unexpected handle type";
|
LOG(ERROR) << "unexpected handle type";
|
||||||
task_ = ZX_HANDLE_INVALID;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedTaskSuspend::~ScopedTaskSuspend() {
|
ScopedTaskSuspend::~ScopedTaskSuspend() = default;
|
||||||
if (task_ != ZX_HANDLE_INVALID) {
|
|
||||||
zx_obj_type_t type = GetHandleType(task_);
|
|
||||||
if (type == ZX_OBJ_TYPE_THREAD) {
|
|
||||||
ResumeThread(task_);
|
|
||||||
} else if (type == ZX_OBJ_TYPE_PROCESS) {
|
|
||||||
for (const auto& thread :
|
|
||||||
GetChildHandles(task_, ZX_INFO_PROCESS_THREADS)) {
|
|
||||||
ResumeThread(thread.get());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG(ERROR) << "unexpected handle type";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -17,20 +17,21 @@
|
|||||||
|
|
||||||
#include <zircon/types.h>
|
#include <zircon/types.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/fuchsia/scoped_zx_handle.h"
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
//! \brief Manages the suspension of another task.
|
//! \brief Manages the suspension of another task.
|
||||||
//!
|
//!
|
||||||
//! Currently, suspends and resumes are not counted on Fuchsia, so while this
|
//! The underlying API only supports suspending threads (despite its name) not
|
||||||
//! class attempts to manage suspension of a task, if another caller or process
|
//! entire tasks. As a result, it's possible some threads may not be correctly
|
||||||
//! is simultaneously suspending or resuming this task, the results may not be
|
//! suspended/resumed as their creation might race enumeration.
|
||||||
//! as expected.
|
|
||||||
//!
|
//!
|
||||||
//! Additionally, the underlying API only supports suspending threads (despite
|
//! Additionally, suspending a thread is asynchronous and may take an
|
||||||
//! its name) not entire tasks. As a result, it's possible some threads may not
|
//! arbitrary amount of time.
|
||||||
//! be correctly suspended/resumed as their creation might race enumeration.
|
|
||||||
//!
|
//!
|
||||||
//! Because of these limitations, this class is limited to being a best-effort,
|
//! Because of these limitations, this class is limited to being a best-effort,
|
||||||
//! and correct suspension/resumption cannot be relied upon.
|
//! and correct suspension/resumption cannot be relied upon.
|
||||||
@ -43,7 +44,8 @@ class ScopedTaskSuspend {
|
|||||||
~ScopedTaskSuspend();
|
~ScopedTaskSuspend();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
zx_handle_t task_; // weak
|
// Could be one (for a thread) or many (for every process in a thread).
|
||||||
|
std::vector<base::ScopedZxHandle> suspend_tokens_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspend);
|
DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspend);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user