[debugger] Hook up filter noun to debug agent
Change-Id: Icb5a369e94f71c74c965cabe49c57b1971a406f6
diff --git a/src/developer/debug/zxdb/client/BUILD.gn b/src/developer/debug/zxdb/client/BUILD.gn
index bad78d7..3cb45f7 100644
--- a/src/developer/debug/zxdb/client/BUILD.gn
+++ b/src/developer/debug/zxdb/client/BUILD.gn
@@ -65,6 +65,7 @@
"cloud_storage_symbol_server.cc",
"curl.cc",
"disassembler.cc",
+ "filter.cc",
"finish_physical_frame_thread_controller.cc",
"finish_thread_controller.cc",
"frame.cc",
@@ -207,6 +208,7 @@
"backtrace_cache_unittest.cc",
"breakpoint_impl_unittest.cc",
"disassembler_unittest.cc",
+ "filter_unittest.cc",
"finish_physical_frame_thread_controller_unittest.cc",
"finish_thread_controller_unittest.cc",
"frame_fingerprint_unittest.cc",
diff --git a/src/developer/debug/zxdb/client/filter.cc b/src/developer/debug/zxdb/client/filter.cc
new file mode 100644
index 0000000..45c0af7
--- /dev/null
+++ b/src/developer/debug/zxdb/client/filter.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 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/filter.h"
+
+#include "src/developer/debug/zxdb/client/session.h"
+
+namespace zxdb {
+
+Filter::Filter(Session* session) : ClientObject(session) {}
+
+void Filter::SetPattern(const std::string& pattern) {
+ pattern_ = pattern;
+
+ session()->system_impl().MarkFiltersDirty();
+}
+
+void Filter::SetJob(JobContext* job) {
+ job_ = job ? std::optional(job->GetWeakPtr()) : std::nullopt;
+
+ session()->system_impl().MarkFiltersDirty();
+}
+
+} // namespace zxdb
diff --git a/src/developer/debug/zxdb/client/filter.h b/src/developer/debug/zxdb/client/filter.h
index 0214ca3..5f3248c9 100644
--- a/src/developer/debug/zxdb/client/filter.h
+++ b/src/developer/debug/zxdb/client/filter.h
@@ -7,22 +7,32 @@
#include <optional>
+#include "src/developer/debug/zxdb/client/client_object.h"
#include "src/developer/debug/zxdb/client/job_context.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace zxdb {
-class Filter {
+class Filter : public ClientObject {
public:
- void set_pattern(const std::string& pattern) { pattern_ = pattern; }
- void set_job(JobContext* job) {
- job_ = job ? std::optional(job->GetWeakPtr()) : std::nullopt;
- }
+ explicit Filter(Session* session);
+
+ void SetPattern(const std::string& pattern);
+ void SetJob(JobContext* job);
+
const std::string& pattern() { return pattern_; }
JobContext* job() { return job_ ? job_->get() : nullptr; }
+ bool valid() { return !pattern_.empty() && (!job_ || job_->get()); }
private:
std::string pattern_;
+
+ // This exists in one of 3 states:
+ // 1) Optional contains non-null pointer. That points to the job this
+ // applies to.
+ // 2) Optional is a nullopt. This filter applies to all jobs.
+ // 3) Optional contains a null pointer. This filter was meant to apply to a
+ // job that disappeared.
std::optional<fxl::WeakPtr<JobContext>> job_;
};
diff --git a/src/developer/debug/zxdb/client/filter_unittest.cc b/src/developer/debug/zxdb/client/filter_unittest.cc
new file mode 100644
index 0000000..068d395
--- /dev/null
+++ b/src/developer/debug/zxdb/client/filter_unittest.cc
@@ -0,0 +1,199 @@
+// Copyright 2019 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/filter.h"
+
+#include "gtest/gtest.h"
+#include "src/developer/debug/shared/platform_message_loop.h"
+#include "src/developer/debug/zxdb/client/remote_api_test.h"
+#include "src/developer/debug/zxdb/client/session.h"
+
+namespace zxdb {
+
+namespace {
+
+using debug_ipc::MessageLoop;
+
+class FilterSink : public RemoteAPI {
+ public:
+ void JobFilter(
+ const debug_ipc::JobFilterRequest& request,
+ std::function<void(const Err&, debug_ipc::JobFilterReply)> cb) override {
+ filters[request.job_koid] = request.filters;
+
+ MessageLoop::Current()->PostTask(FROM_HERE, [cb]() {
+ cb(Err(), debug_ipc::JobFilterReply());
+ MessageLoop::Current()->QuitNow();
+ });
+ }
+
+ void Attach(
+ const debug_ipc::AttachRequest& request,
+ std::function<void(const Err&, debug_ipc::AttachReply)> cb) override {
+ debug_ipc::AttachReply reply;
+ reply.koid = request.koid;
+ reply.name = "test";
+ MessageLoop::Current()->PostTask(FROM_HERE,
+ [cb, reply]() { cb(Err(), reply); });
+ }
+
+ std::map<uint64_t, std::vector<std::string>> filters;
+};
+
+class FilterTest : public RemoteAPITest {
+ public:
+ FilterTest() = default;
+ ~FilterTest() override = default;
+
+ FilterSink& sink() { return *sink_; }
+
+ protected:
+ std::unique_ptr<RemoteAPI> GetRemoteAPIImpl() override {
+ auto sink = std::make_unique<FilterSink>();
+ sink_ = sink.get();
+ return std::move(sink);
+ }
+
+ private:
+ FilterSink* sink_; // Owned by the session.
+};
+
+} // namespace
+
+TEST_F(FilterTest, SetFilters) {
+ Filter* filter = session().system().CreateNewFilter();
+
+ auto contexts = session().system().GetJobContexts();
+ ASSERT_EQ(1u, contexts.size());
+ auto context = contexts[0];
+ bool job_context_alive;
+ Err ctx_err;
+
+ context->Attach(1234, [&job_context_alive, &ctx_err](
+ fxl::WeakPtr<JobContext> ctx, const Err& err) {
+ ctx_err = err;
+ job_context_alive = !!ctx;
+ MessageLoop::Current()->QuitNow();
+ });
+ MessageLoop::Current()->Run();
+
+ EXPECT_FALSE(ctx_err.has_error());
+ EXPECT_TRUE(job_context_alive);
+
+ filter->SetPattern("foo");
+ MessageLoop::Current()->Run();
+
+ ASSERT_EQ(1u, sink().filters[1234u].size());
+ EXPECT_EQ("foo", sink().filters[1234u][0]);
+}
+
+TEST_F(FilterTest, SetSpecificFilters) {
+ Filter* filter = session().system().CreateNewFilter();
+
+ auto contexts = session().system().GetJobContexts();
+ ASSERT_EQ(1u, contexts.size());
+ auto context_a = contexts[0];
+ auto context_b = session().system().CreateNewJobContext(context_a);
+ bool job_context_alive;
+ Err ctx_err;
+
+ context_a->Attach(1234, [&job_context_alive, &ctx_err](
+ fxl::WeakPtr<JobContext> ctx, const Err& err) {
+ ctx_err = err;
+ job_context_alive = !!ctx;
+ MessageLoop::Current()->QuitNow();
+ });
+ MessageLoop::Current()->Run();
+
+ EXPECT_FALSE(ctx_err.has_error());
+ EXPECT_TRUE(job_context_alive);
+
+ context_b->Attach(5678, [&job_context_alive, &ctx_err](
+ fxl::WeakPtr<JobContext> ctx, const Err& err) {
+ ctx_err = err;
+ job_context_alive = !!ctx;
+ MessageLoop::Current()->QuitNow();
+ });
+ MessageLoop::Current()->Run();
+
+ EXPECT_FALSE(ctx_err.has_error());
+ EXPECT_TRUE(job_context_alive);
+
+ filter->SetPattern("foo");
+ filter->SetJob(context_a);
+ MessageLoop::Current()->Run();
+
+ ASSERT_EQ(1u, sink().filters[1234u].size());
+ EXPECT_EQ("foo", sink().filters[1234u][0]);
+ ASSERT_EQ(0u, sink().filters[5678u].size());
+}
+
+TEST_F(FilterTest, SetBroadFilters) {
+ Filter* filter = session().system().CreateNewFilter();
+
+ auto contexts = session().system().GetJobContexts();
+ ASSERT_EQ(1u, contexts.size());
+ auto context_a = contexts[0];
+ auto context_b = session().system().CreateNewJobContext(context_a);
+ bool job_context_alive;
+ Err ctx_err;
+
+ context_a->Attach(1234, [&job_context_alive, &ctx_err](
+ fxl::WeakPtr<JobContext> ctx, const Err& err) {
+ ctx_err = err;
+ job_context_alive = !!ctx;
+ MessageLoop::Current()->QuitNow();
+ });
+ MessageLoop::Current()->Run();
+
+ EXPECT_FALSE(ctx_err.has_error());
+ EXPECT_TRUE(job_context_alive);
+
+ context_b->Attach(5678, [&job_context_alive, &ctx_err](
+ fxl::WeakPtr<JobContext> ctx, const Err& err) {
+ ctx_err = err;
+ job_context_alive = !!ctx;
+ MessageLoop::Current()->QuitNow();
+ });
+ MessageLoop::Current()->Run();
+
+ EXPECT_FALSE(ctx_err.has_error());
+ EXPECT_TRUE(job_context_alive);
+
+ filter->SetPattern("foo");
+ MessageLoop::Current()->Run();
+
+ ASSERT_EQ(1u, sink().filters[1234u].size());
+ EXPECT_EQ("foo", sink().filters[1234u][0]);
+ ASSERT_EQ(1u, sink().filters[5678u].size());
+ EXPECT_EQ("foo", sink().filters[5678u][0]);
+}
+
+TEST_F(FilterTest, SetExAnteFilters) {
+ Filter* filter = session().system().CreateNewFilter();
+
+ filter->SetPattern("foo");
+
+ auto contexts = session().system().GetJobContexts();
+ ASSERT_EQ(1u, contexts.size());
+ auto context = contexts[0];
+ bool job_context_alive;
+ Err ctx_err;
+
+ context->Attach(1234, [&job_context_alive, &ctx_err](
+ fxl::WeakPtr<JobContext> ctx, const Err& err) {
+ ctx_err = err;
+ job_context_alive = !!ctx;
+ MessageLoop::Current()->QuitNow();
+ });
+ MessageLoop::Current()->Run();
+
+ EXPECT_FALSE(ctx_err.has_error());
+ EXPECT_TRUE(job_context_alive);
+
+ ASSERT_EQ(1u, sink().filters[1234u].size());
+ EXPECT_EQ("foo", sink().filters[1234u][0]);
+}
+
+} // namespace zxdb
diff --git a/src/developer/debug/zxdb/client/job_context.cc b/src/developer/debug/zxdb/client/job_context.cc
index ae2456d..5c2361f 100644
--- a/src/developer/debug/zxdb/client/job_context.cc
+++ b/src/developer/debug/zxdb/client/job_context.cc
@@ -10,17 +10,10 @@
// Schema Definition -----------------------------------------------------------
-const char* ClientSettings::Job::kFilters = "filters";
-static const char* kFiltersDescription =
- R"( List of filters to be applied to a job. Filters are used against each new
- process being spawned under the attached job. If there is match, zxdb will
- automatically attach to the process.)";
-
namespace {
fxl::RefPtr<SettingSchema> CreateSchema() {
auto schema = fxl::MakeRefCounted<SettingSchema>();
- schema->AddList(ClientSettings::Job::kFilters, kFiltersDescription, {});
return schema;
}
diff --git a/src/developer/debug/zxdb/client/job_context.h b/src/developer/debug/zxdb/client/job_context.h
index 44764ba..e62d749 100644
--- a/src/developer/debug/zxdb/client/job_context.h
+++ b/src/developer/debug/zxdb/client/job_context.h
@@ -72,6 +72,10 @@
// executed when the detach is complete (or fails).
virtual void Detach(Callback callback) = 0;
+ // Set filters for this job. TODO: Remove this as soon as fidlcat can use the
+ // object model.
+ virtual void SendAndUpdateFilters(std::vector<std::string> filters) = 0;
+
// Provides the setting schema for this object.
static fxl::RefPtr<SettingSchema> GetSchema();
diff --git a/src/developer/debug/zxdb/client/job_context_impl.cc b/src/developer/debug/zxdb/client/job_context_impl.cc
index 4f1a49e..66083f1 100644
--- a/src/developer/debug/zxdb/client/job_context_impl.cc
+++ b/src/developer/debug/zxdb/client/job_context_impl.cc
@@ -24,7 +24,6 @@
system_(system),
is_implicit_root_(is_implicit_root),
impl_weak_factory_(this) {
- settings_.AddObserver(ClientSettings::Job::kFilters, this);
settings_.set_name("job");
}
@@ -161,8 +160,14 @@
});
}
+void JobContextImpl::SendAndUpdateFilters(std::vector<std::string> filters) {
+ SendAndUpdateFilters(filters, last_filter_set_failed_);
+}
+
void JobContextImpl::SendAndUpdateFilters(std::vector<std::string> filters,
bool force_send) {
+ last_filter_set_failed_ = false;
+
if (!job_.get()) {
filters_ = std::move(filters);
return;
@@ -183,11 +188,9 @@
FXL_LOG(ERROR) << "Error adding filter: "
<< debug_ipc::ZxStatusToString(reply.status);
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_);
+ // Agent failed, mark that we had trouble setting filters and
+ // return.
+ weak_job_context->last_filter_set_failed_ = true;
}
return;
}
@@ -199,8 +202,7 @@
void JobContextImpl::OnSettingChanged(const SettingStore&,
const std::string& setting_name) {
- FXL_CHECK(setting_name == ClientSettings::Job::kFilters);
- SendAndUpdateFilters(settings_.GetList(setting_name));
+ FXL_NOTREACHED() << "No settings supported for jobs.";
}
void JobContextImpl::OnDetachReply(const Err& err, uint32_t status,
diff --git a/src/developer/debug/zxdb/client/job_context_impl.h b/src/developer/debug/zxdb/client/job_context_impl.h
index 09c3018..9a6e19b2 100644
--- a/src/developer/debug/zxdb/client/job_context_impl.h
+++ b/src/developer/debug/zxdb/client/job_context_impl.h
@@ -52,6 +52,7 @@
void AttachToSystemRoot(Callback callback) override;
void AttachToComponentRoot(Callback callback) override;
void Detach(Callback callback) override;
+ void SendAndUpdateFilters(std::vector<std::string> filters) override;
// SettingStoreObserver implementation
void OnSettingChanged(const SettingStore&,
@@ -66,6 +67,7 @@
std::unique_ptr<JobImpl> job_;
std::vector<std::string> filters_;
bool is_implicit_root_;
+ bool last_filter_set_failed_ = false;
fxl::WeakPtrFactory<JobContextImpl> impl_weak_factory_;
@@ -82,8 +84,7 @@
// If job is running this will update |filters_| only after getting OK from
// agent else it will set |filters_| and return.
- void SendAndUpdateFilters(std::vector<std::string> filters,
- bool force_send = false);
+ void SendAndUpdateFilters(std::vector<std::string> filters, bool force_send);
FXL_DISALLOW_COPY_AND_ASSIGN(JobContextImpl);
};
diff --git a/src/developer/debug/zxdb/client/setting_schema_definition.h b/src/developer/debug/zxdb/client/setting_schema_definition.h
index 1adbd73..af594b5 100644
--- a/src/developer/debug/zxdb/client/setting_schema_definition.h
+++ b/src/developer/debug/zxdb/client/setting_schema_definition.h
@@ -30,9 +30,7 @@
static const char* kSymbolCache;
};
- struct Job {
- static const char* kFilters;
- };
+ struct Job {};
struct Target {
static const char* kStoreBacktraces;
diff --git a/src/developer/debug/zxdb/client/system_impl.cc b/src/developer/debug/zxdb/client/system_impl.cc
index 9fd0866..7bfa7e5 100644
--- a/src/developer/debug/zxdb/client/system_impl.cc
+++ b/src/developer/debug/zxdb/client/system_impl.cc
@@ -213,6 +213,50 @@
return nullptr;
}
+void SystemImpl::MarkFiltersDirty() {
+ if (!debug_ipc::MessageLoop::Current()) {
+ filters_dirty_ = false;
+ return;
+ }
+
+ if (filters_dirty_) {
+ return;
+ }
+
+ filters_dirty_ = true;
+
+ debug_ipc::MessageLoop::Current()->PostTask(
+ FROM_HERE, [weak_this = weak_factory_.GetWeakPtr()]() {
+ if (!weak_this) {
+ return;
+ }
+
+ std::map<JobContext*, std::vector<std::string>> filters_by_job;
+ std::vector<std::string> filters;
+
+ for (const auto& filter : weak_this->filters_) {
+ if (!filter->valid()) {
+ continue;
+ }
+
+ if (!filter->job()) {
+ filters.push_back(filter->pattern());
+ } else {
+ filters_by_job[filter->job()].push_back(filter->pattern());
+ }
+ }
+
+ for (auto& ctx : weak_this->job_contexts_) {
+ auto& items = filters_by_job[ctx.get()];
+ std::copy(filters.begin(), filters.end(), std::back_inserter(items));
+
+ ctx->SendAndUpdateFilters(items);
+ }
+
+ weak_this->filters_dirty_ = false;
+ });
+}
+
void SystemImpl::NotifyDidCreateProcess(Process* process) {
for (auto& observer : observers())
observer.GlobalDidCreateProcess(process);
@@ -434,7 +478,8 @@
}
Filter* SystemImpl::CreateNewFilter() {
- Filter* to_return = filters_.emplace_back(std::make_unique<Filter>()).get();
+ Filter* to_return =
+ filters_.emplace_back(std::make_unique<Filter>(session())).get();
// Notify observers (may mutate filter list).
for (auto& observer : observers())
@@ -566,6 +611,8 @@
job_contexts_.push_back(std::move(job_context));
for (auto& observer : observers())
observer.DidCreateJobContext(for_observers);
+
+ MarkFiltersDirty();
}
void SystemImpl::OnSettingChanged(const SettingStore& store,
diff --git a/src/developer/debug/zxdb/client/system_impl.h b/src/developer/debug/zxdb/client/system_impl.h
index ca9663f..325e7d0 100644
--- a/src/developer/debug/zxdb/client/system_impl.h
+++ b/src/developer/debug/zxdb/client/system_impl.h
@@ -32,6 +32,9 @@
ProcessImpl* ProcessImplFromKoid(uint64_t koid) const;
+ // Lets us know that we should sync the filters with the debug agent.
+ void MarkFiltersDirty();
+
// Broadcasts the global process notifications.
void NotifyDidCreateProcess(Process* process);
void NotifyWillDestroyProcess(Process* process);
@@ -124,6 +127,7 @@
std::map<uint32_t, std::unique_ptr<BreakpointImpl>> breakpoints_;
std::vector<std::unique_ptr<Filter>> filters_;
+ bool filters_dirty_ = false;
SystemSymbols symbols_;
diff --git a/src/developer/debug/zxdb/console/console_main.cc b/src/developer/debug/zxdb/console/console_main.cc
index 21554d3..e153ece 100644
--- a/src/developer/debug/zxdb/console/console_main.cc
+++ b/src/developer/debug/zxdb/console/console_main.cc
@@ -65,6 +65,14 @@
return err;
}
+ for (const auto& filter : options.filter) {
+ std::string cmd = "filter attach " + filter;
+ actions->push_back(Action("Filter", [cmd](const Action&, const Session&,
+ Console* console) {
+ console->ProcessInputLine(cmd.c_str(), ActionFlow::PostActionCallback);
+ }));
+ }
+
return Err();
}
@@ -130,17 +138,6 @@
// Redundant adds are ignored.
session->system().settings().SetList(ClientSettings::System::kSymbolPaths,
std::move(paths));
-
- // Filters, there should already be a default job context.
- if (!options.filter.empty()) {
- JobContext* default_job = session->system().GetJobContexts()[0];
- Err err = default_job->settings().SetList(ClientSettings::Job::kFilters,
- options.filter);
-
- // The filters aren't currently validated when setting the list so an error
- // here means we used the API wrong.
- FXL_DCHECK(!err.has_error()) << err.msg();
- }
}
} // namespace
diff --git a/src/developer/debug/zxdb/console/verbs_process.cc b/src/developer/debug/zxdb/console/verbs_process.cc
index 9d916d3..cadf5153 100644
--- a/src/developer/debug/zxdb/console/verbs_process.cc
+++ b/src/developer/debug/zxdb/console/verbs_process.cc
@@ -416,10 +416,10 @@
} else {
JobContext* job = cmd.HasNoun(Noun::kJob) ? cmd.job_context() : nullptr;
filter = context->session()->system().CreateNewFilter();
- filter->set_job(job);
+ filter->SetJob(job);
}
- filter->set_pattern(cmd.args()[0]);
+ filter->SetPattern(cmd.args()[0]);
Console::get()->Output("Waiting for process matching /" + cmd.args()[0] +
"/");
diff --git a/tools/fidlcat/lib/interception_workflow.cc b/tools/fidlcat/lib/interception_workflow.cc
index d83c2ab..eb8cafb 100644
--- a/tools/fidlcat/lib/interception_workflow.cc
+++ b/tools/fidlcat/lib/interception_workflow.cc
@@ -10,6 +10,7 @@
#include "src/developer/debug/shared/platform_message_loop.h"
#include "src/developer/debug/shared/zx_status.h"
#include "src/developer/debug/zxdb/client/breakpoint.h"
+#include "src/developer/debug/zxdb/client/filter.h"
#include "src/developer/debug/zxdb/client/remote_api.h"
#include "src/developer/debug/zxdb/client/setting_schema_definition.h"
#include "src/developer/debug/zxdb/client/thread.h"
@@ -258,12 +259,7 @@
void InterceptionWorkflow::Filter(const std::vector<std::string>& filter,
SimpleErrorFunction and_then) {
zxdb::JobContext* default_job = session_->system().GetJobContexts()[0];
- zxdb::Err err = default_job->settings().SetList(
- zxdb::ClientSettings::Job::kFilters, filter);
-
- // The filters aren't currently validated when setting the list so an error
- // here means we used the API wrong.
- FXL_DCHECK(err.ok()) << err.msg();
+ default_job->SendAndUpdateFilters(filter);
GetTarget()->AddObserver(new SetupTargetObserver(std::move(and_then)));
AddObserver(GetTarget());
}