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)
Debug/Context.cpp
Debug/Context.hpp
Debug/Debug.cpp
Debug/EventListener.cpp
Debug/EventListener.hpp
Debug/File.cpp
Debug/File.hpp
......
......@@ -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;
// EventListener
void onThreadStarted(Thread::ID) override;
void onThreadStepped(Thread::ID) override;
void onLineBreakpointHit(Thread::ID) override;
void onFunctionBreakpointHit(Thread::ID) override;
void add(Listener *);
void remove(Listener *);
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 @@ void Broadcaster::foreach(F &&f)
--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 @@ void Broadcaster::modify(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 vk {
......@@ -121,13 +153,17 @@ class Context::Impl : public Context
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 @@ Context::Lock Context::Impl::lock()
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)
......@@ -239,7 +290,7 @@ std::shared_ptr<Thread> Context::Lock::currentThread()
thread->setName(name);
ctx->threadsByStdId.emplace(std::this_thread::get_id(), thread);
ctx->broadcast()->onThreadStarted(id);
ctx->serverEventBroadcast()->onThreadStarted(id);
return thread;
}
......
......@@ -30,7 +30,8 @@ class File;
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 @@ public:
// 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
......
// 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 @@
#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 @@ public:
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
......
......@@ -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 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
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'", 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());
lock.addFunctionBreakpoint(bp.name.c_str());
response.breakpoints.push_back({});
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 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
[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 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
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 @@ Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
}
}
// 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;
});
......
......@@ -23,7 +23,7 @@ namespace dbg {
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 @@ void Thread::stepOut()
}
} // namespace dbg
} // namespace vk
\ No newline at end of file
} // namespace vk
......@@ -32,7 +32,7 @@ namespace dbg {
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 @@ public:
const ID id;
private:
EventListener *const broadcast;
ServerEventListener *const broadcast;
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