| // 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 "garnet/bin/zxdb/client/job_context_impl.h" |
| |
| #include <sstream> |
| |
| #include "garnet/bin/zxdb/client/job_impl.h" |
| #include "garnet/bin/zxdb/client/remote_api.h" |
| #include "garnet/bin/zxdb/client/session.h" |
| #include "garnet/bin/zxdb/client/setting_schema_definition.h" |
| #include "garnet/bin/zxdb/client/system_impl.h" |
| #include "garnet/lib/debug_ipc/helper/message_loop.h" |
| #include "garnet/lib/debug_ipc/helper/zx_status.h" |
| #include "garnet/public/lib/fxl/logging.h" |
| #include "garnet/public/lib/fxl/strings/string_printf.h" |
| |
| namespace zxdb { |
| |
| JobContextImpl::JobContextImpl(SystemImpl* system, |
| bool is_implicit_component_root) |
| : JobContext(system->session()), |
| system_(system), |
| is_implicit_component_root_(is_implicit_component_root), |
| impl_weak_factory_(this) { |
| settings_.AddObserver(ClientSettings::Job::kFilters, this); |
| } |
| |
| JobContextImpl::~JobContextImpl() { |
| // If the job is still running, make sure we broadcast terminated |
| // notifications before deleting everything. |
| ImplicitlyDetach(); |
| } |
| |
| std::unique_ptr<JobContextImpl> JobContextImpl::Clone(SystemImpl* system) { |
| return std::make_unique<JobContextImpl>(system, false); |
| } |
| |
| void JobContextImpl::ImplicitlyDetach() { |
| if (GetJob()) |
| OnDetachReply(Err(), 0, [](fxl::WeakPtr<JobContext>, const Err&) {}); |
| } |
| |
| JobContext::State JobContextImpl::GetState() const { return state_; } |
| |
| Job* JobContextImpl::GetJob() const { return job_.get(); } |
| |
| // static |
| void JobContextImpl::OnAttachReplyThunk( |
| fxl::WeakPtr<JobContextImpl> job_context, Callback callback, const Err& err, |
| uint64_t koid, uint32_t status, const std::string& job_name) { |
| if (job_context) { |
| job_context->OnAttachReply(std::move(callback), err, koid, status, |
| job_name); |
| if (!job_context->filters_.empty()) { |
| job_context->SendAndUpdateFilters(job_context->filters_, true); |
| } |
| } 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_context, err); |
| } else { |
| callback(job_context, Err("Warning: job attach race, extra job is " |
| "likely attached.")); |
| } |
| } |
| } |
| |
| void JobContextImpl::OnAttachReply(Callback callback, const Err& err, |
| uint64_t koid, uint32_t status, |
| const std::string& job_name) { |
| FXL_DCHECK(state_ == State::kAttaching || state_ == State::kStarting); |
| FXL_DCHECK(!job_.get()); // Shouldn't 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 != 0) { |
| // Error from launching. |
| state_ = State::kNone; |
| issue_err = Err(fxl::StringPrintf("Error attaching, status = %d.", status)); |
| } else { |
| state_ = State::kRunning; |
| job_ = std::make_unique<JobImpl>(this, koid, job_name); |
| } |
| |
| callback(GetWeakPtr(), issue_err); |
| } |
| |
| void JobContextImpl::AttachInternal(debug_ipc::AttachRequest::Type type, |
| uint64_t koid, Callback callback) { |
| if (state_ != State::kNone) { |
| // Avoid reentering caller to dispatch the error. |
| debug_ipc::MessageLoop::Current()->PostTask( |
| FROM_HERE, [ callback, weak_ptr = GetWeakPtr() ]() { |
| 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, weak_job_context = impl_weak_factory_.GetWeakPtr() ]( |
| const Err& err, debug_ipc::AttachReply reply) { |
| OnAttachReplyThunk(std::move(weak_job_context), std::move(callback), |
| err, reply.koid, reply.status, reply.name); |
| }); |
| } |
| |
| void JobContextImpl::Attach(uint64_t koid, Callback callback) { |
| AttachInternal(debug_ipc::AttachRequest::Type::kJob, koid, callback); |
| } |
| |
| void JobContextImpl::AttachToComponentRoot(Callback callback) { |
| AttachInternal(debug_ipc::AttachRequest::Type::kComponentRoot, 0, callback); |
| } |
| |
| void JobContextImpl::Detach(Callback callback) { |
| if (!job_.get()) { |
| debug_ipc::MessageLoop::Current()->PostTask( |
| FROM_HERE, [ callback, weak_ptr = GetWeakPtr() ]() { |
| callback(std::move(weak_ptr), Err("Error detaching: No job.")); |
| }); |
| return; |
| } |
| |
| // This job could have been the one automatically created to watch the |
| // component root. 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_component_root_ = false; |
| |
| debug_ipc::DetachRequest request; |
| request.koid = job_->GetKoid(); |
| request.type = debug_ipc::DetachRequest::Type::kJob; |
| session()->remote_api()->Detach(request, [ |
| callback, weak_job_context = impl_weak_factory_.GetWeakPtr() |
| ](const Err& err, debug_ipc::DetachReply reply) { |
| if (weak_job_context) { |
| weak_job_context->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_context, err); |
| } |
| }); |
| } |
| |
| void JobContextImpl::SendAndUpdateFilters(std::vector<std::string> filters, |
| bool force_send) { |
| if (!job_.get()) { |
| filters_ = std::move(filters); |
| return; |
| } |
| if (!force_send && filters_ == filters) { |
| return; |
| } |
| debug_ipc::JobFilterRequest request; |
| request.job_koid = job_->GetKoid(); |
| request.filters = filters; |
| session()->remote_api()->JobFilter( |
| request, [ filters, weak_job_context = impl_weak_factory_.GetWeakPtr() ]( |
| const Err& err, debug_ipc::JobFilterReply reply) { |
| if (reply.status != 0) { |
| printf("Error adding filter, error: %s.", |
| debug_ipc::ZxStatusToString(reply.status).data()); |
| if (weak_job_context) { |
| // Agent failed, reset filters in settings. |
| // This will also trigger another callback but would be a no-op |
| // because |force_send| would be false. |
| weak_job_context->settings_.SetList(ClientSettings::Job::kFilters, |
| weak_job_context->filters_); |
| } |
| return; |
| } |
| if (weak_job_context) { |
| weak_job_context->filters_ = std::move(filters); |
| } |
| }); |
| } |
| |
| void JobContextImpl::OnSettingChanged(const SettingStore&, |
| const std::string& setting_name) { |
| FXL_CHECK(setting_name == ClientSettings::Job::kFilters); |
| SendAndUpdateFilters(settings_.GetList(setting_name)); |
| } |
| |
| void JobContextImpl::OnDetachReply(const Err& err, uint32_t status, |
| Callback callback) { |
| FXL_DCHECK(job_.get()); // 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 != 0) { |
| // Error from detaching. |
| // TODO(donosoc): Print error using ZxStatusToString |
| issue_err = Err(fxl::StringPrintf("Error detaching, status = %d.", status)); |
| } else { |
| // Successfully detached. |
| state_ = State::kNone; |
| job_.release(); |
| } |
| |
| callback(GetWeakPtr(), issue_err); |
| } |
| |
| } // namespace zxdb |