Vulkan/Debug: Split EventListener
Into `ServerEventListener` and `ClientEventListener`.
The new debugger implementation wants to listen to breakpoints being set so that traps can be dynamically enabled.
Bug: b/145351270
Change-Id: Iafe64426874da3aa109d4b59b34cb1e36bd06828
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/48690
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Vulkan/CMakeLists.txt b/src/Vulkan/CMakeLists.txt
index 58ca289..5128fe9 100644
--- a/src/Vulkan/CMakeLists.txt
+++ b/src/Vulkan/CMakeLists.txt
@@ -106,6 +106,7 @@
Debug/Context.cpp
Debug/Context.hpp
Debug/Debug.cpp
+ Debug/EventListener.cpp
Debug/EventListener.hpp
Debug/File.cpp
Debug/File.hpp
diff --git a/src/Vulkan/Debug/Context.cpp b/src/Vulkan/Debug/Context.cpp
index eb43b4a..da3d962 100644
--- a/src/Vulkan/Debug/Context.cpp
+++ b/src/Vulkan/Debug/Context.cpp
@@ -28,65 +28,45 @@
namespace {
-class Broadcaster : public vk::dbg::EventListener
+////////////////////////////////////////////////////////////////////////////////
+// Broadcaster - template base class for ServerEventBroadcaster and
+// ClientEventBroadcaster
+////////////////////////////////////////////////////////////////////////////////
+template<typename Listener>
+class Broadcaster : public Listener
{
public:
- using Thread = vk::dbg::Thread;
+ void add(Listener *);
+ void remove(Listener *);
- // EventListener
- void onThreadStarted(Thread::ID) override;
- void onThreadStepped(Thread::ID) override;
- void onLineBreakpointHit(Thread::ID) override;
- void onFunctionBreakpointHit(Thread::ID) override;
-
- void add(EventListener *);
- void remove(EventListener *);
-
-private:
+protected:
template<typename F>
inline void foreach(F &&);
template<typename F>
inline void modify(F &&);
- using ListenerSet = std::unordered_set<EventListener *>;
+ using ListenerSet = std::unordered_set<Listener *>;
std::recursive_mutex mutex;
std::shared_ptr<ListenerSet> listeners = std::make_shared<ListenerSet>();
int listenersInUse = 0;
};
-void Broadcaster::onThreadStarted(Thread::ID id)
-{
- foreach([&](EventListener *l) { l->onThreadStarted(id); });
-}
-
-void Broadcaster::onThreadStepped(Thread::ID id)
-{
- foreach([&](EventListener *l) { l->onThreadStepped(id); });
-}
-
-void Broadcaster::onLineBreakpointHit(Thread::ID id)
-{
- foreach([&](EventListener *l) { l->onLineBreakpointHit(id); });
-}
-
-void Broadcaster::onFunctionBreakpointHit(Thread::ID id)
-{
- foreach([&](EventListener *l) { l->onFunctionBreakpointHit(id); });
-}
-
-void Broadcaster::add(EventListener *l)
+template<typename Listener>
+void Broadcaster<Listener>::add(Listener *l)
{
modify([&]() { listeners->emplace(l); });
}
-void Broadcaster::remove(EventListener *l)
+template<typename Listener>
+void Broadcaster<Listener>::remove(Listener *l)
{
modify([&]() { listeners->erase(l); });
}
+template<typename Listener>
template<typename F>
-void Broadcaster::foreach(F &&f)
+void Broadcaster<Listener>::foreach(F &&f)
{
std::unique_lock<std::recursive_mutex> lock(mutex);
++listenersInUse;
@@ -95,8 +75,9 @@
--listenersInUse;
}
+template<typename Listener>
template<typename F>
-void Broadcaster::modify(F &&f)
+void Broadcaster<Listener>::modify(F &&f)
{
std::unique_lock<std::recursive_mutex> lock(mutex);
if(listenersInUse > 0)
@@ -108,6 +89,57 @@
f();
}
+////////////////////////////////////////////////////////////////////////////////
+// ServerEventBroadcaster
+////////////////////////////////////////////////////////////////////////////////
+class ServerEventBroadcaster : public Broadcaster<vk::dbg::ServerEventListener>
+{
+public:
+ using Thread = vk::dbg::Thread;
+
+ void onThreadStarted(Thread::ID id) override
+ {
+ foreach([&](auto *l) { l->onThreadStarted(id); });
+ }
+
+ void onThreadStepped(Thread::ID id) override
+ {
+ foreach([&](auto *l) { l->onThreadStepped(id); });
+ }
+
+ void onLineBreakpointHit(Thread::ID id) override
+ {
+ foreach([&](auto *l) { l->onLineBreakpointHit(id); });
+ }
+
+ void onFunctionBreakpointHit(Thread::ID id) override
+ {
+ foreach([&](auto *l) { l->onFunctionBreakpointHit(id); });
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// ClientEventBroadcaster
+////////////////////////////////////////////////////////////////////////////////
+class ClientEventBroadcaster : public Broadcaster<vk::dbg::ClientEventListener>
+{
+public:
+ void onSetBreakpoint(const vk::dbg::Location &location, bool &handled) override
+ {
+ foreach([&](auto *l) { l->onSetBreakpoint(location, handled); });
+ }
+
+ void onSetBreakpoint(const std::string &func, bool &handled) override
+ {
+ foreach([&](auto *l) { l->onSetBreakpoint(func, handled); });
+ }
+
+ void onBreakpointsChanged() override
+ {
+ foreach([&](auto *l) { l->onBreakpointsChanged(); });
+ }
+};
+
} // namespace
namespace vk {
@@ -121,13 +153,17 @@
public:
// Context compliance
Lock lock() override;
- void addListener(EventListener *) override;
- void removeListener(EventListener *) override;
- EventListener *broadcast() override;
+ void addListener(ClientEventListener *) override;
+ void removeListener(ClientEventListener *) override;
+ ClientEventListener *clientEventBroadcast() override;
+ void addListener(ServerEventListener *) override;
+ void removeListener(ServerEventListener *) override;
+ ServerEventListener *serverEventBroadcast() override;
void addFile(const std::shared_ptr<File> &file);
- Broadcaster broadcaster;
+ ServerEventBroadcaster serverEventBroadcaster;
+ ClientEventBroadcaster clientEventBroadcaster;
std::mutex mutex;
std::unordered_map<std::thread::id, std::shared_ptr<Thread>> threadsByStdId;
@@ -150,19 +186,34 @@
return Lock(this);
}
-void Context::Impl::addListener(EventListener *l)
+void Context::Impl::addListener(ClientEventListener *l)
{
- broadcaster.add(l);
+ clientEventBroadcaster.add(l);
}
-void Context::Impl::removeListener(EventListener *l)
+void Context::Impl::removeListener(ClientEventListener *l)
{
- broadcaster.remove(l);
+ clientEventBroadcaster.remove(l);
}
-EventListener *Context::Impl::broadcast()
+ClientEventListener *Context::Impl::clientEventBroadcast()
{
- return &broadcaster;
+ return &clientEventBroadcaster;
+}
+
+void Context::Impl::addListener(ServerEventListener *l)
+{
+ serverEventBroadcaster.add(l);
+}
+
+void Context::Impl::removeListener(ServerEventListener *l)
+{
+ serverEventBroadcaster.remove(l);
+}
+
+ServerEventListener *Context::Impl::serverEventBroadcast()
+{
+ return &serverEventBroadcaster;
}
void Context::Impl::addFile(const std::shared_ptr<File> &file)
@@ -239,7 +290,7 @@
thread->setName(name);
ctx->threadsByStdId.emplace(std::this_thread::get_id(), thread);
- ctx->broadcast()->onThreadStarted(id);
+ ctx->serverEventBroadcast()->onThreadStarted(id);
return thread;
}
diff --git a/src/Vulkan/Debug/Context.hpp b/src/Vulkan/Debug/Context.hpp
index 92f3086..832c9c4 100644
--- a/src/Vulkan/Debug/Context.hpp
+++ b/src/Vulkan/Debug/Context.hpp
@@ -30,7 +30,8 @@
class Frame;
class Scope;
class VariableContainer;
-class EventListener;
+class ClientEventListener;
+class ServerEventListener;
// Context holds the full state of the debugger, including all current files,
// threads, frames and variables. It also holds a list of EventListeners that
@@ -145,16 +146,27 @@
// access.
virtual Lock lock() = 0;
- // addListener() registers an EventListener for event notifications.
- virtual void addListener(EventListener *) = 0;
+ // addListener() registers an ClientEventListener for event notifications.
+ virtual void addListener(ClientEventListener *) = 0;
- // removeListener() unregisters an EventListener that was previously
+ // removeListener() unregisters an ClientEventListener that was previously
// registered by a call to addListener().
- virtual void removeListener(EventListener *) = 0;
+ virtual void removeListener(ClientEventListener *) = 0;
- // broadcast() returns an EventListener that will broadcast all methods on
- // to all registered EventListeners.
- virtual EventListener *broadcast() = 0;
+ // clientEventBroadcast() returns an ClientEventListener that will broadcast
+ // all method calls on to all registered ServerEventListeners.
+ virtual ClientEventListener *clientEventBroadcast() = 0;
+
+ // addListener() registers an ServerEventListener for event notifications.
+ virtual void addListener(ServerEventListener *) = 0;
+
+ // removeListener() unregisters an ServerEventListener that was previously
+ // registered by a call to addListener().
+ virtual void removeListener(ServerEventListener *) = 0;
+
+ // serverEventBroadcast() returns an ServerEventListener that will broadcast
+ // all method calls on to all registered ServerEventListeners.
+ virtual ServerEventListener *serverEventBroadcast() = 0;
};
} // namespace dbg
diff --git a/src/Vulkan/Debug/EventListener.cpp b/src/Vulkan/Debug/EventListener.cpp
new file mode 100644
index 0000000..19e3ad8
--- /dev/null
+++ b/src/Vulkan/Debug/EventListener.cpp
@@ -0,0 +1,24 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "EventListener.hpp"
+
+namespace vk {
+namespace dbg {
+
+ServerEventListener::~ServerEventListener() = default;
+ClientEventListener::~ClientEventListener() = default;
+
+} // namespace dbg
+} // namespace vk
diff --git a/src/Vulkan/Debug/EventListener.hpp b/src/Vulkan/Debug/EventListener.hpp
index 8de0dfd..3ab287c 100644
--- a/src/Vulkan/Debug/EventListener.hpp
+++ b/src/Vulkan/Debug/EventListener.hpp
@@ -15,16 +15,20 @@
#ifndef VK_DEBUG_EVENT_LISTENER_HPP_
#define VK_DEBUG_EVENT_LISTENER_HPP_
+#include "ID.hpp"
+
namespace vk {
namespace dbg {
+struct Location;
class Thread;
-// EventListener is an interface that is used to listen for thread events.
-class EventListener
+// ServerEventListener is an interface that is used to listen for events raised
+// by the server (the debugger).
+class ServerEventListener
{
public:
- virtual ~EventListener() = default;
+ virtual ~ServerEventListener();
// onThreadStarted() is called when a new thread begins execution.
virtual void onThreadStarted(ID<Thread>) {}
@@ -42,6 +46,23 @@
virtual void onFunctionBreakpointHit(ID<Thread>) {}
};
+// ClientEventListener is an interface that is used to listen for events raised
+// by the client (the IDE).
+class ClientEventListener
+{
+public:
+ virtual ~ClientEventListener();
+
+ // onSetBreakpoint() is called when a breakpoint location is set.
+ virtual void onSetBreakpoint(const Location &, bool &handled) {}
+
+ // onSetBreakpoint() is called when a function breakpoint is set.
+ virtual void onSetBreakpoint(const std::string &func, bool &handled) {}
+
+ // onBreakpointsChange() is called after breakpoints have been changed.
+ virtual void onBreakpointsChanged() {}
+};
+
} // namespace dbg
} // namespace vk
diff --git a/src/Vulkan/Debug/Server.cpp b/src/Vulkan/Debug/Server.cpp
index 6b3b692..b6d4443 100644
--- a/src/Vulkan/Debug/Server.cpp
+++ b/src/Vulkan/Debug/Server.cpp
@@ -43,7 +43,7 @@
namespace vk {
namespace dbg {
-class Server::Impl : public Server, public EventListener
+class Server::Impl : public Server, public ServerEventListener
{
public:
Impl(const std::shared_ptr<Context> &ctx, int port);
@@ -100,14 +100,27 @@
session->registerHandler(
[this](const dap::SetFunctionBreakpointsRequest &req) {
DAP_LOG("SetFunctionBreakpointsRequest receieved");
- auto lock = ctx->lock();
+
dap::SetFunctionBreakpointsResponse response;
- for(auto const &bp : req.breakpoints)
+ for(auto const &reqBP : req.breakpoints)
{
- DAP_LOG("Setting breakpoint for function '%s'", bp.name.c_str());
- lock.addFunctionBreakpoint(bp.name.c_str());
- response.breakpoints.push_back({});
+ DAP_LOG("Setting breakpoint for function '%s'", reqBP.name.c_str());
+
+ bool verified = false;
+ ctx->clientEventBroadcast()->onSetBreakpoint(reqBP.name, verified);
+
+ dap::Breakpoint resBP{};
+ resBP.verified = verified;
+ response.breakpoints.emplace_back(std::move(resBP));
}
+ {
+ auto lock = ctx->lock();
+ for(auto const &reqBP : req.breakpoints)
+ {
+ lock.addFunctionBreakpoint(reqBP.name.c_str());
+ }
+ }
+ ctx->clientEventBroadcast()->onBreakpointsChanged();
return response;
});
@@ -115,7 +128,6 @@
[this](const dap::SetBreakpointsRequest &req)
-> dap::ResponseOrError<dap::SetBreakpointsResponse> {
DAP_LOG("SetBreakpointsRequest receieved");
- bool verified = false;
size_t numBreakpoints = 0;
if(req.breakpoints.has_value())
@@ -124,14 +136,27 @@
numBreakpoints = breakpoints.size();
if(auto file = this->file(req.source))
{
+ dap::SetBreakpointsResponse response;
file->clearBreakpoints();
- for(auto const &bp : breakpoints)
+ for(size_t i = 0; i < numBreakpoints; i++)
{
- file->addBreakpoint(bp.line);
+ auto &reqBP = breakpoints[i];
+ Location location{ file, reqBP.line };
+ file->addBreakpoint(reqBP.line);
+
+ bool verified = false;
+ ctx->clientEventBroadcast()->onSetBreakpoint(location, verified);
+
+ dap::Breakpoint respBP;
+ respBP.verified = verified;
+ respBP.source = req.source;
+ response.breakpoints.push_back(respBP);
}
- verified = true;
+ ctx->clientEventBroadcast()->onBreakpointsChanged();
+ return response;
}
- else if(req.source.name.has_value())
+
+ if(req.source.name.has_value())
{
std::vector<int> lines;
lines.reserve(breakpoints.size());
@@ -144,14 +169,16 @@
}
}
+ // Generic response.
dap::SetBreakpointsResponse response;
for(size_t i = 0; i < numBreakpoints; i++)
{
dap::Breakpoint bp;
- bp.verified = verified;
+ bp.verified = false;
bp.source = req.source;
response.breakpoints.push_back(bp);
}
+ ctx->clientEventBroadcast()->onBreakpointsChanged();
return response;
});
diff --git a/src/Vulkan/Debug/Thread.cpp b/src/Vulkan/Debug/Thread.cpp
index 8228465..9e57bc7 100644
--- a/src/Vulkan/Debug/Thread.cpp
+++ b/src/Vulkan/Debug/Thread.cpp
@@ -23,7 +23,7 @@
Thread::Thread(ID id, Context *ctx)
: id(id)
- , broadcast(ctx->broadcast())
+ , broadcast(ctx->serverEventBroadcast())
{}
void Thread::setName(const std::string &name)
@@ -185,4 +185,4 @@
}
} // namespace dbg
-} // namespace vk
\ No newline at end of file
+} // namespace vk
diff --git a/src/Vulkan/Debug/Thread.hpp b/src/Vulkan/Debug/Thread.hpp
index 690fb37..184d396 100644
--- a/src/Vulkan/Debug/Thread.hpp
+++ b/src/Vulkan/Debug/Thread.hpp
@@ -32,7 +32,7 @@
class File;
class VariableContainer;
-class EventListener;
+class ServerEventListener;
// Scope is a container for variables and is used to provide source data for the
// DAP 'Scope' type:
@@ -181,7 +181,7 @@
const ID id;
private:
- EventListener *const broadcast;
+ ServerEventListener *const broadcast;
void onLocationUpdate(marl::lock &lock) REQUIRES(mutex);