Commit aa58f0cf by Ben Clayton

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: 's avatarAntonio Maiorano <amaiorano@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com>
parent 44be0942
...@@ -106,6 +106,7 @@ if(SWIFTSHADER_ENABLE_VULKAN_DEBUGGER) ...@@ -106,6 +106,7 @@ if(SWIFTSHADER_ENABLE_VULKAN_DEBUGGER)
Debug/Context.cpp Debug/Context.cpp
Debug/Context.hpp Debug/Context.hpp
Debug/Debug.cpp Debug/Debug.cpp
Debug/EventListener.cpp
Debug/EventListener.hpp Debug/EventListener.hpp
Debug/File.cpp Debug/File.cpp
Debug/File.hpp Debug/File.hpp
......
...@@ -28,65 +28,45 @@ ...@@ -28,65 +28,45 @@
namespace { namespace {
class Broadcaster : public vk::dbg::EventListener ////////////////////////////////////////////////////////////////////////////////
// Broadcaster - template base class for ServerEventBroadcaster and
// ClientEventBroadcaster
////////////////////////////////////////////////////////////////////////////////
template<typename Listener>
class Broadcaster : public Listener
{ {
public: 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 *); protected:
void remove(EventListener *);
private:
template<typename F> template<typename F>
inline void foreach(F &&); inline void foreach(F &&);
template<typename F> template<typename F>
inline void modify(F &&); inline void modify(F &&);
using ListenerSet = std::unordered_set<EventListener *>; using ListenerSet = std::unordered_set<Listener *>;
std::recursive_mutex mutex; std::recursive_mutex mutex;
std::shared_ptr<ListenerSet> listeners = std::make_shared<ListenerSet>(); std::shared_ptr<ListenerSet> listeners = std::make_shared<ListenerSet>();
int listenersInUse = 0; int listenersInUse = 0;
}; };
void Broadcaster::onThreadStarted(Thread::ID id) template<typename Listener>
{ void Broadcaster<Listener>::add(Listener *l)
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)
{ {
modify([&]() { listeners->emplace(l); }); modify([&]() { listeners->emplace(l); });
} }
void Broadcaster::remove(EventListener *l) template<typename Listener>
void Broadcaster<Listener>::remove(Listener *l)
{ {
modify([&]() { listeners->erase(l); }); modify([&]() { listeners->erase(l); });
} }
template<typename Listener>
template<typename F> template<typename F>
void Broadcaster::foreach(F &&f) void Broadcaster<Listener>::foreach(F &&f)
{ {
std::unique_lock<std::recursive_mutex> lock(mutex); std::unique_lock<std::recursive_mutex> lock(mutex);
++listenersInUse; ++listenersInUse;
...@@ -95,8 +75,9 @@ void Broadcaster::foreach(F &&f) ...@@ -95,8 +75,9 @@ void Broadcaster::foreach(F &&f)
--listenersInUse; --listenersInUse;
} }
template<typename Listener>
template<typename F> template<typename F>
void Broadcaster::modify(F &&f) void Broadcaster<Listener>::modify(F &&f)
{ {
std::unique_lock<std::recursive_mutex> lock(mutex); std::unique_lock<std::recursive_mutex> lock(mutex);
if(listenersInUse > 0) if(listenersInUse > 0)
...@@ -108,6 +89,57 @@ void Broadcaster::modify(F &&f) ...@@ -108,6 +89,57 @@ void Broadcaster::modify(F &&f)
f(); 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
namespace vk { namespace vk {
...@@ -121,13 +153,17 @@ class Context::Impl : public Context ...@@ -121,13 +153,17 @@ class Context::Impl : public Context
public: public:
// Context compliance // Context compliance
Lock lock() override; Lock lock() override;
void addListener(EventListener *) override; void addListener(ClientEventListener *) override;
void removeListener(EventListener *) override; void removeListener(ClientEventListener *) override;
EventListener *broadcast() override; ClientEventListener *clientEventBroadcast() override;
void addListener(ServerEventListener *) override;
void removeListener(ServerEventListener *) override;
ServerEventListener *serverEventBroadcast() override;
void addFile(const std::shared_ptr<File> &file); void addFile(const std::shared_ptr<File> &file);
Broadcaster broadcaster; ServerEventBroadcaster serverEventBroadcaster;
ClientEventBroadcaster clientEventBroadcaster;
std::mutex mutex; std::mutex mutex;
std::unordered_map<std::thread::id, std::shared_ptr<Thread>> threadsByStdId; std::unordered_map<std::thread::id, std::shared_ptr<Thread>> threadsByStdId;
...@@ -150,19 +186,34 @@ Context::Lock Context::Impl::lock() ...@@ -150,19 +186,34 @@ Context::Lock Context::Impl::lock()
return Lock(this); return Lock(this);
} }
void Context::Impl::addListener(EventListener *l) void Context::Impl::addListener(ClientEventListener *l)
{
clientEventBroadcaster.add(l);
}
void Context::Impl::removeListener(ClientEventListener *l)
{
clientEventBroadcaster.remove(l);
}
ClientEventListener *Context::Impl::clientEventBroadcast()
{
return &clientEventBroadcaster;
}
void Context::Impl::addListener(ServerEventListener *l)
{ {
broadcaster.add(l); serverEventBroadcaster.add(l);
} }
void Context::Impl::removeListener(EventListener *l) void Context::Impl::removeListener(ServerEventListener *l)
{ {
broadcaster.remove(l); serverEventBroadcaster.remove(l);
} }
EventListener *Context::Impl::broadcast() ServerEventListener *Context::Impl::serverEventBroadcast()
{ {
return &broadcaster; return &serverEventBroadcaster;
} }
void Context::Impl::addFile(const std::shared_ptr<File> &file) void Context::Impl::addFile(const std::shared_ptr<File> &file)
...@@ -239,7 +290,7 @@ std::shared_ptr<Thread> Context::Lock::currentThread() ...@@ -239,7 +290,7 @@ std::shared_ptr<Thread> Context::Lock::currentThread()
thread->setName(name); thread->setName(name);
ctx->threadsByStdId.emplace(std::this_thread::get_id(), thread); ctx->threadsByStdId.emplace(std::this_thread::get_id(), thread);
ctx->broadcast()->onThreadStarted(id); ctx->serverEventBroadcast()->onThreadStarted(id);
return thread; return thread;
} }
......
...@@ -30,7 +30,8 @@ class File; ...@@ -30,7 +30,8 @@ class File;
class Frame; class Frame;
class Scope; class Scope;
class VariableContainer; class VariableContainer;
class EventListener; class ClientEventListener;
class ServerEventListener;
// Context holds the full state of the debugger, including all current files, // Context holds the full state of the debugger, including all current files,
// threads, frames and variables. It also holds a list of EventListeners that // threads, frames and variables. It also holds a list of EventListeners that
...@@ -145,16 +146,27 @@ public: ...@@ -145,16 +146,27 @@ public:
// access. // access.
virtual Lock lock() = 0; virtual Lock lock() = 0;
// addListener() registers an EventListener for event notifications. // addListener() registers an ClientEventListener for event notifications.
virtual void addListener(EventListener *) = 0; 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(). // 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 // clientEventBroadcast() returns an ClientEventListener that will broadcast
// to all registered EventListeners. // all method calls on to all registered ServerEventListeners.
virtual EventListener *broadcast() = 0; 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 } // namespace dbg
......
// 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
...@@ -15,16 +15,20 @@ ...@@ -15,16 +15,20 @@
#ifndef VK_DEBUG_EVENT_LISTENER_HPP_ #ifndef VK_DEBUG_EVENT_LISTENER_HPP_
#define VK_DEBUG_EVENT_LISTENER_HPP_ #define VK_DEBUG_EVENT_LISTENER_HPP_
#include "ID.hpp"
namespace vk { namespace vk {
namespace dbg { namespace dbg {
struct Location;
class Thread; class Thread;
// EventListener is an interface that is used to listen for thread events. // ServerEventListener is an interface that is used to listen for events raised
class EventListener // by the server (the debugger).
class ServerEventListener
{ {
public: public:
virtual ~EventListener() = default; virtual ~ServerEventListener();
// onThreadStarted() is called when a new thread begins execution. // onThreadStarted() is called when a new thread begins execution.
virtual void onThreadStarted(ID<Thread>) {} virtual void onThreadStarted(ID<Thread>) {}
...@@ -42,6 +46,23 @@ public: ...@@ -42,6 +46,23 @@ public:
virtual void onFunctionBreakpointHit(ID<Thread>) {} 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 dbg
} // namespace vk } // namespace vk
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
namespace vk { namespace vk {
namespace dbg { namespace dbg {
class Server::Impl : public Server, public EventListener class Server::Impl : public Server, public ServerEventListener
{ {
public: public:
Impl(const std::shared_ptr<Context> &ctx, int port); Impl(const std::shared_ptr<Context> &ctx, int port);
...@@ -100,14 +100,27 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port) ...@@ -100,14 +100,27 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
session->registerHandler( session->registerHandler(
[this](const dap::SetFunctionBreakpointsRequest &req) { [this](const dap::SetFunctionBreakpointsRequest &req) {
DAP_LOG("SetFunctionBreakpointsRequest receieved"); DAP_LOG("SetFunctionBreakpointsRequest receieved");
auto lock = ctx->lock();
dap::SetFunctionBreakpointsResponse response; dap::SetFunctionBreakpointsResponse response;
for(auto const &bp : req.breakpoints) for(auto const &reqBP : req.breakpoints)
{
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));
}
{ {
DAP_LOG("Setting breakpoint for function '%s'", bp.name.c_str()); auto lock = ctx->lock();
lock.addFunctionBreakpoint(bp.name.c_str()); for(auto const &reqBP : req.breakpoints)
response.breakpoints.push_back({}); {
lock.addFunctionBreakpoint(reqBP.name.c_str());
}
} }
ctx->clientEventBroadcast()->onBreakpointsChanged();
return response; return response;
}); });
...@@ -115,7 +128,6 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port) ...@@ -115,7 +128,6 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
[this](const dap::SetBreakpointsRequest &req) [this](const dap::SetBreakpointsRequest &req)
-> dap::ResponseOrError<dap::SetBreakpointsResponse> { -> dap::ResponseOrError<dap::SetBreakpointsResponse> {
DAP_LOG("SetBreakpointsRequest receieved"); DAP_LOG("SetBreakpointsRequest receieved");
bool verified = false;
size_t numBreakpoints = 0; size_t numBreakpoints = 0;
if(req.breakpoints.has_value()) if(req.breakpoints.has_value())
...@@ -124,14 +136,27 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port) ...@@ -124,14 +136,27 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
numBreakpoints = breakpoints.size(); numBreakpoints = breakpoints.size();
if(auto file = this->file(req.source)) if(auto file = this->file(req.source))
{ {
dap::SetBreakpointsResponse response;
file->clearBreakpoints(); 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; std::vector<int> lines;
lines.reserve(breakpoints.size()); lines.reserve(breakpoints.size());
...@@ -144,14 +169,16 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port) ...@@ -144,14 +169,16 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
} }
} }
// Generic response.
dap::SetBreakpointsResponse response; dap::SetBreakpointsResponse response;
for(size_t i = 0; i < numBreakpoints; i++) for(size_t i = 0; i < numBreakpoints; i++)
{ {
dap::Breakpoint bp; dap::Breakpoint bp;
bp.verified = verified; bp.verified = false;
bp.source = req.source; bp.source = req.source;
response.breakpoints.push_back(bp); response.breakpoints.push_back(bp);
} }
ctx->clientEventBroadcast()->onBreakpointsChanged();
return response; return response;
}); });
......
...@@ -23,7 +23,7 @@ namespace dbg { ...@@ -23,7 +23,7 @@ namespace dbg {
Thread::Thread(ID id, Context *ctx) Thread::Thread(ID id, Context *ctx)
: id(id) : id(id)
, broadcast(ctx->broadcast()) , broadcast(ctx->serverEventBroadcast())
{} {}
void Thread::setName(const std::string &name) void Thread::setName(const std::string &name)
...@@ -185,4 +185,4 @@ void Thread::stepOut() ...@@ -185,4 +185,4 @@ void Thread::stepOut()
} }
} // namespace dbg } // namespace dbg
} // namespace vk } // namespace vk
\ No newline at end of file
...@@ -32,7 +32,7 @@ namespace dbg { ...@@ -32,7 +32,7 @@ namespace dbg {
class File; class File;
class VariableContainer; class VariableContainer;
class EventListener; class ServerEventListener;
// Scope is a container for variables and is used to provide source data for the // Scope is a container for variables and is used to provide source data for the
// DAP 'Scope' type: // DAP 'Scope' type:
...@@ -181,7 +181,7 @@ public: ...@@ -181,7 +181,7 @@ public:
const ID id; const ID id;
private: private:
EventListener *const broadcast; ServerEventListener *const broadcast;
void onLocationUpdate(marl::lock &lock) REQUIRES(mutex); void onLocationUpdate(marl::lock &lock) REQUIRES(mutex);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment