blob: 830f658035a83a2cf6a979761ab37194f6cf0c59 [file] [log] [blame]
// 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/debug_agent/debugged_job.h"
#include "src/developer/debug/debug_agent/object_provider.h"
#include "src/developer/debug/debug_agent/system_info.h"
#include "src/developer/debug/shared/component_utils.h"
#include "src/developer/debug/shared/logging/logging.h"
#include "src/developer/debug/shared/platform_message_loop.h"
#include "src/developer/debug/shared/regex.h"
#include "src/developer/debug/shared/zx_status.h"
#include "src/lib/fxl/logging.h"
namespace debug_agent {
DebuggedJob::DebuggedJob(std::shared_ptr<ObjectProvider> object_provider,
ProcessStartHandler* handler, zx_koid_t job_koid, zx::job job)
: object_provider_(std::move(object_provider)),
handler_(handler),
koid_(job_koid),
job_(std::move(job)) {}
DebuggedJob::~DebuggedJob() = default;
zx_status_t DebuggedJob::Init() {
debug_ipc::MessageLoopTarget* loop = debug_ipc::MessageLoopTarget::Current();
FXL_DCHECK(loop); // Loop must be created on this thread first.
// Register for debug exceptions.
debug_ipc::MessageLoopTarget::WatchJobConfig config;
config.job_name = object_provider_->NameForObject(job_);
config.job_handle = job_.get();
config.job_koid = koid_;
config.watcher = this;
return loop->WatchJobExceptions(std::move(config), &job_watch_handle_);
}
bool DebuggedJob::FilterInfo::Matches(const std::string& proc_name) {
if (regex.valid()) {
return regex.Match(proc_name);
}
// TODO(DX-953): Job filters should always be valid.
return proc_name.find(filter) != std::string::npos;
}
void DebuggedJob::OnProcessStarting(zx::exception exception_token,
zx_exception_info_t exception_info) {
zx::process process = object_provider_->GetProcessFromException(exception_token.get());
auto proc_name = object_provider_->NameForObject(process);
zx::thread initial_thread = object_provider_->GetThreadFromException(exception_token.get());
// Tools like fx serve will connect every second or so to the target, spamming
// logging for this with a lot of "/boot/bin/sh" starting.
// We filter this out as it makes debugging much harder.
if (proc_name != "/boot/bin/sh") {
DEBUG_LOG(Job) << "Debugged job " << koid_ << ": Process " << proc_name << " starting.";
}
// Search through the available filters. If the regex is not valid, fallback
// to checking if |proc_name| contains the filter.
FilterInfo* matching_filter = nullptr;
for (auto& filter : filters_) {
if (filter.Matches(proc_name)) {
matching_filter = &filter;
break;
}
}
if (matching_filter) {
DEBUG_LOG(Job) << "Filter " << matching_filter->filter << " matches process " << proc_name
<< ". Attaching.";
handler_->OnProcessStart(matching_filter->filter, std::move(process));
}
// Attached to the process. At that point it will get a new thread
// notification for the initial thread which it can stop or continue as it
// desires. Therefore, we can always resume the thread in the "new process"
// exception.
//
// Technically it's not necessary to reset the handle, but being explicit here
// helps readability.
exception_token.reset();
}
std::set<zx_koid_t> DebuggedJob::ApplyToJob(FilterInfo& filter, zx::job& job) {
std::set<zx_koid_t> matches;
for (auto& child : object_provider_->GetChildProcesses(job.get())) {
auto proc_name = object_provider_->NameForObject(child);
if (filter.Matches(proc_name)) {
DEBUG_LOG(Job) << "Filter " << filter.filter << " matches process " << proc_name;
matches.insert(object_provider_->KoidForObject(child));
}
}
for (auto& child : object_provider_->GetChildJobs(job.get())) {
auto m = ApplyToJob(filter, child);
matches.insert(m.begin(), m.end());
}
return matches;
}
std::set<zx_koid_t> DebuggedJob::SetFilters(std::vector<std::string> filters) {
filters_.clear();
filters_.reserve(filters.size());
std::set<zx_koid_t> matches;
for (auto& filter : filters) {
// We check if this is a package url. If that is the case, me only need
// the component as a filter, as the whole URL won't match.
debug_ipc::ComponentDescription desc;
if (debug_ipc::ExtractComponentFromPackageUrl(filter, &desc))
filter = desc.component_name;
debug_ipc::Regex regex;
if (!regex.Init(filter))
FXL_LOG(WARNING) << "Could not initialize regex for filter " << filter;
DEBUG_LOG(Job) << "Debug job " << koid_ << ": Adding filter " << filter;
FilterInfo filter_info = {};
filter_info.filter = std::move(filter);
filter_info.regex = std::move(regex);
auto m = ApplyToJob(filters_.emplace_back(std::move(filter_info)), job_);
matches.insert(m.begin(), m.end());
}
return matches;
}
void DebuggedJob::AppendFilter(std::string filter) {
// We check whether this filter already exists.
for (auto& existent_filter : filters_) {
if (existent_filter.filter == filter)
return;
}
debug_ipc::Regex regex;
if (!regex.Init(filter)) {
FXL_LOG(WARNING) << "Could not initialize regex for filter " << filter;
}
DEBUG_LOG(Job) << "Debugged job " << koid_ << ": Appending filter " << filter;
FilterInfo filter_info = {};
filter_info.filter = std::move(filter);
filter_info.regex = std::move(regex);
filters_.push_back(std::move(filter_info));
}
} // namespace debug_agent