blob: e981317f87534e12e4d89add2bb0ead3c39214db [file] [log] [blame] [edit]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/client/job.h"
#include <lib/syslog/cpp/macros.h>
#include "src/developer/debug/shared/message_loop.h"
#include "src/developer/debug/zxdb/client/remote_api.h"
#include "src/developer/debug/zxdb/client/session.h"
namespace zxdb {
Job::Job(Session* session, bool is_implicit_root)
: ClientObject(session), is_implicit_root_(is_implicit_root), weak_factory_(this) {}
Job::~Job() {
// If the job is still running, make sure we broadcast terminated notifications before deleting
// everything.
ImplicitlyDetach();
}
fxl::WeakPtr<Job> Job::GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
void Job::ImplicitlyDetach() {
if (state_ == State::kAttached)
OnDetachReply(Err(), debug::Status(), [](fxl::WeakPtr<Job>, const Err&) {});
}
// static
void Job::OnAttachReplyThunk(fxl::WeakPtr<Job> job, Callback callback, const Err& err,
uint64_t koid, const debug::Status& status,
const std::string& job_name) {
if (job) {
job->OnAttachReply(std::move(callback), err, koid, status, job_name);
} else {
// The reply that the job was launched came after the local objects were destroyed.
if (err.has_error()) {
// Process not launched, forward the error.
callback(job, err);
} else {
callback(job, Err("Warning: job attach race, extra job is likely attached."));
}
}
}
void Job::OnAttachReply(Callback callback, const Err& err, uint64_t koid,
const debug::Status& status, const std::string& job_name) {
FX_DCHECK(state_ == State::kAttaching);
Err issue_err; // Error to send in callback.
if (err.has_error()) {
// Error from transport.
state_ = State::kNone;
issue_err = err;
} else if (status.has_error()) {
// Error from launching.
state_ = State::kNone;
issue_err = Err("Error attaching: " + status.message());
} else {
state_ = State::kAttached;
koid_ = koid;
name_ = job_name;
}
callback(GetWeakPtr(), issue_err);
}
void Job::AttachInternal(debug_ipc::TaskType type, uint64_t koid, Callback callback) {
if (state_ != State::kNone) {
// Avoid reentering caller to dispatch the error.
debug::MessageLoop::Current()->PostTask(
FROM_HERE, [callback = std::move(callback), weak_ptr = GetWeakPtr()]() mutable {
callback(std::move(weak_ptr), Err("Can't attach, job is already running or starting."));
});
return;
}
state_ = State::kAttaching;
debug_ipc::AttachRequest request;
request.koid = koid;
request.type = type;
session()->remote_api()->Attach(
request, [callback = std::move(callback), weak_job = weak_factory_.GetWeakPtr()](
const Err& err, debug_ipc::AttachReply reply) mutable {
OnAttachReplyThunk(std::move(weak_job), std::move(callback), err, reply.koid, reply.status,
reply.name);
});
}
void Job::Attach(uint64_t koid, Callback callback) {
AttachInternal(debug_ipc::TaskType::kJob, koid, std::move(callback));
}
void Job::AttachToSystemRoot(Callback callback) {
AttachInternal(debug_ipc::TaskType::kSystemRoot, 0, std::move(callback));
}
void Job::AttachForTesting(uint64_t koid, const std::string& name) {
state_ = State::kAttached;
koid_ = koid;
name_ = name;
}
void Job::Detach(Callback callback) {
if (state_ != State::kAttached) {
debug::MessageLoop::Current()->PostTask(
FROM_HERE, [callback = std::move(callback), weak_ptr = GetWeakPtr()]() mutable {
callback(std::move(weak_ptr), Err("Error detaching: No job."));
});
return;
}
// This job could have been the one automatically created. If the user explicitly detaches it, the
// user is taking control over what job it's attached to so we don't want to track it implicitly
// any more.
is_implicit_root_ = false;
debug_ipc::DetachRequest request;
request.koid = koid_;
request.type = debug_ipc::TaskType::kJob;
session()->remote_api()->Detach(
request, [callback = std::move(callback), weak_job = weak_factory_.GetWeakPtr()](
const Err& err, debug_ipc::DetachReply reply) mutable {
if (weak_job) {
weak_job->OnDetachReply(err, reply.status, std::move(callback));
} else {
// The reply that the process was launched came after the local objects were destroyed.
// We're still OK to dispatch either way.
callback(weak_job, err);
}
});
}
void Job::OnDetachReply(const Err& err, const debug::Status& status, Callback callback) {
FX_DCHECK(state_ == State::kAttached); // Should have a job.
Err issue_err; // Error to send in callback.
if (err.has_error()) {
// Error from transport.
state_ = State::kNone;
issue_err = err;
} else if (status.has_error()) {
// Error from detaching.
issue_err = Err("Error detaching: " + status.message());
} else {
// Successfully detached.
state_ = State::kNone;
}
if (state_ == State::kNone) {
koid_ = 0;
name_.clear();
}
callback(GetWeakPtr(), issue_err);
}
} // namespace zxdb