Vulkan/Debug: Add Thread and EventListener

EventListener is an interface that is used to listen for thread events.
Thread holds the state for a single thread of execution.
Frame holds a number of variable scopes for one of a thread's stack frame.
Scope is a container for frame variables.

Bug: b/145351270
Change-Id: Ic61e17f32cfd6929dbd7b0fce1ffb716301fc73e
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38897
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Vulkan/Debug/EventListener.hpp b/src/Vulkan/Debug/EventListener.hpp
new file mode 100644
index 0000000..6d96066
--- /dev/null
+++ b/src/Vulkan/Debug/EventListener.hpp
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef VK_DEBUG_EVENT_LISTENER_HPP_
+#define VK_DEBUG_EVENT_LISTENER_HPP_
+
+namespace vk
+{
+namespace dbg
+{
+
+class Thread;
+
+// EventListener is an interface that is used to listen for thread events.
+class EventListener
+{
+public:
+	virtual ~EventListener() = default;
+
+	// onThreadStarted() is called when a new thread begins execution.
+	virtual void onThreadStarted(ID<Thread>) {}
+
+	// onThreadStepped() is called when a thread performs a single line /
+	// instruction step.
+	virtual void onThreadStepped(ID<Thread>) {}
+
+	// onLineBreakpointHit() is called when a thread hits a line breakpoint and
+	// pauses execution.
+	virtual void onLineBreakpointHit(ID<Thread>) {}
+
+	// onFunctionBreakpointHit() is called when a thread hits a function
+	// breakpoint and pauses execution.
+	virtual void onFunctionBreakpointHit(ID<Thread>) {}
+};
+
+}  // namespace dbg
+}  // namespace vk
+
+#endif  // VK_DEBUG_EVENT_LISTENER_HPP_
diff --git a/src/Vulkan/Debug/Thread.cpp b/src/Vulkan/Debug/Thread.cpp
new file mode 100644
index 0000000..b61c5d9
--- /dev/null
+++ b/src/Vulkan/Debug/Thread.cpp
@@ -0,0 +1,183 @@
+// 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 "Thread.hpp"
+
+#include "Context.hpp"
+#include "EventListener.hpp"
+#include "File.hpp"
+
+namespace vk
+{
+namespace dbg
+{
+
+Thread::Thread(ID id, Context* ctx) :
+    id(id),
+    broadcast(ctx->broadcast()) {}
+
+void Thread::setName(const std::string& name)
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	name_ = name;
+}
+
+std::string Thread::name() const
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	return name_;
+}
+
+void Thread::update(const Location& location)
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	frames.back()->location = location;
+
+	if(state_ == State::Running)
+	{
+		if(location.file->hasBreakpoint(location.line))
+		{
+			broadcast->onLineBreakpointHit(id);
+			state_ = State::Paused;
+		}
+	}
+
+	switch(state_)
+	{
+	case State::Paused:
+	{
+		stateCV.wait(lock, [this] { return state_ != State::Paused; });
+		break;
+	}
+
+	case State::Stepping:
+	{
+		if(!pauseAtFrame || pauseAtFrame == frames.back())
+		{
+			broadcast->onThreadStepped(id);
+			state_ = State::Paused;
+			stateCV.wait(lock, [this] { return state_ != State::Paused; });
+			pauseAtFrame = 0;
+		}
+		break;
+	}
+
+	case State::Running:
+		break;
+	}
+}
+
+void Thread::enter(Context::Lock& ctxlck, const std::shared_ptr<File>& file, const std::string& function)
+{
+	auto frame = ctxlck.createFrame(file);
+	auto isFunctionBreakpoint = ctxlck.isFunctionBreakpoint(function);
+
+	std::unique_lock<std::mutex> lock(mutex);
+	frame->function = function;
+	frames.push_back(frame);
+	if(isFunctionBreakpoint)
+	{
+		broadcast->onFunctionBreakpointHit(id);
+		state_ = State::Paused;
+	}
+}
+
+void Thread::exit()
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	frames.pop_back();
+}
+
+std::shared_ptr<VariableContainer> Thread::registers() const
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	return frames.back()->registers->variables;
+}
+
+std::shared_ptr<VariableContainer> Thread::locals() const
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	return frames.back()->locals->variables;
+}
+
+std::shared_ptr<VariableContainer> Thread::arguments() const
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	return frames.back()->arguments->variables;
+}
+
+std::shared_ptr<VariableContainer> Thread::hovers() const
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	return frames.back()->hovers->variables;
+}
+
+std::vector<Frame> Thread::stack() const
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	std::vector<Frame> out;
+	out.reserve(frames.size());
+	for(auto frame : frames)
+	{
+		out.push_back(*frame);
+	}
+	return out;
+}
+
+Thread::State Thread::state() const
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	return state_;
+}
+
+void Thread::resume()
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	state_ = State::Running;
+	lock.unlock();
+	stateCV.notify_all();
+}
+
+void Thread::pause()
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	state_ = State::Paused;
+}
+
+void Thread::stepIn()
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	state_ = State::Stepping;
+	pauseAtFrame.reset();
+	stateCV.notify_all();
+}
+
+void Thread::stepOver()
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	state_ = State::Stepping;
+	pauseAtFrame = frames.back();
+	stateCV.notify_all();
+}
+
+void Thread::stepOut()
+{
+	std::unique_lock<std::mutex> lock(mutex);
+	state_ = State::Stepping;
+	pauseAtFrame = (frames.size() > 1) ? frames[frames.size() - 1] : nullptr;
+	stateCV.notify_all();
+}
+
+}  // namespace dbg
+}  // namespace vk
\ No newline at end of file
diff --git a/src/Vulkan/Debug/Thread.hpp b/src/Vulkan/Debug/Thread.hpp
new file mode 100644
index 0000000..8199d67
--- /dev/null
+++ b/src/Vulkan/Debug/Thread.hpp
@@ -0,0 +1,194 @@
+// 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.
+
+#ifndef VK_DEBUG_THREAD_HPP_
+#define VK_DEBUG_THREAD_HPP_
+
+#include "Context.hpp"
+#include "ID.hpp"
+#include "Location.hpp"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace vk
+{
+namespace dbg
+{
+
+class File;
+class VariableContainer;
+class EventListener;
+
+// Scope is a container for variables and is used to provide source data for the
+// DAP 'Scope' type:
+// https://microsoft.github.io/debug-adapter-protocol/specification#Types_Scope
+class Scope
+{
+public:
+	using ID = dbg::ID<Scope>;
+
+	inline Scope(ID id,
+	             const std::shared_ptr<File>& file,
+	             const std::shared_ptr<VariableContainer>& variables);
+
+	// The unique identifier of the scope.
+	const ID id;
+
+	// The file this scope is associated with.
+	const std::shared_ptr<File> file;
+
+	// The scope's variables.
+	const std::shared_ptr<VariableContainer> variables;
+};
+
+Scope::Scope(ID id,
+             const std::shared_ptr<File>& file,
+             const std::shared_ptr<VariableContainer>& variables) :
+    id(id),
+    file(file),
+    variables(variables) {}
+
+// Frame holds a number of variable scopes for one of a thread's stack frame,
+// and is used to provide source data for the DAP 'StackFrame' type:
+// https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame
+class Frame
+{
+public:
+	using ID = dbg::ID<Frame>;
+
+	inline Frame(ID id);
+
+	// The unique identifier of the stack frame.
+	const ID id;
+
+	// The name of function for this stack frame.
+	std::string function;
+
+	// The current execution location within the stack frame.
+	Location location;
+
+	// The scope for the frame's arguments.
+	std::shared_ptr<Scope> arguments;
+
+	// The scope for the frame's locals.
+	std::shared_ptr<Scope> locals;
+
+	// The scope for the frame's registers.
+	std::shared_ptr<Scope> registers;
+
+	// The scope for variables that should only appear in hover tooltips.
+	std::shared_ptr<Scope> hovers;
+};
+
+Frame::Frame(ID id) :
+    id(id) {}
+
+// Thread holds the state for a single thread of execution.
+class Thread
+{
+public:
+	using ID = dbg::ID<Thread>;
+
+	// The current execution state.
+	enum class State
+	{
+		Running,   // Thread is running.
+		Stepping,  // Thread is currently single line stepping.
+		Paused     // Thread is currently paused.
+	};
+
+	Thread(ID id, Context* ctx);
+
+	// setName() sets the name of the thread.
+	void setName(const std::string&);
+
+	// name() returns the name of the thread.
+	std::string name() const;
+
+	// enter() pushes the thread's stack with a new frame created with the given
+	// file and function.
+	void enter(Context::Lock& lock, const std::shared_ptr<File>& file, const std::string& function);
+
+	// exit() pops the thread's stack frame.
+	void exit();
+
+	// registers() returns the thread's current stack frame's register variables.
+	std::shared_ptr<VariableContainer> registers() const;
+
+	// locals() returns the thread's current stack frame's local variables.
+	std::shared_ptr<VariableContainer> locals() const;
+
+	// arguments() returns the thread's current stack frame's argument variables.
+	std::shared_ptr<VariableContainer> arguments() const;
+
+	// hovers() returns the thread's current stack frame's hover variables.
+	std::shared_ptr<VariableContainer> hovers() const;
+
+	// stack() returns a copy of the thread's current stack frames.
+	std::vector<Frame> stack() const;
+
+	// state() returns the current thread's state.
+	State state() const;
+
+	// resume() resumes execution of the thread by unblocking a call to
+	// update() and setting the thread's state to State::Running.
+	void resume();
+
+	// pause() suspends execution of the thread by blocking the next call to
+	// update() and setting the thread's state to State::Paused.
+	void pause();
+
+	// stepIn() temporarily resumes execution of the thread by unblocking a
+	// call to update(), and setting the thread's state to State::Stepping.
+	// The next call to update() will suspend execution again.
+	void stepIn();
+
+	// stepOver() temporarily resumes execution of the thread by unblocking a
+	// call to update(), and setting the thread's state to State::Stepping.
+	// The next call to update() within the same stack frame will suspend
+	// execution again.
+	void stepOver();
+
+	// stepOut() temporarily resumes execution of the thread by unblocking a
+	// call to update(), and setting the thread's state to State::Stepping.
+	// The next call to update() at the stack frame above the current frame will
+	// suspend execution again.
+	void stepOut();
+
+	// update() updates the current stack frame's location, and potentially
+	// blocks until the thread is resumed with one of the methods above.
+	void update(const Location& location);
+
+	// The unique identifier of the thread.
+	const ID id;
+
+private:
+	EventListener* const broadcast;
+
+	mutable std::mutex mutex;
+	std::string name_;
+	std::vector<std::shared_ptr<Frame>> frames;
+	std::condition_variable stateCV;
+	State state_ = State::Running;
+	std::shared_ptr<Frame> pauseAtFrame;
+};
+
+}  // namespace dbg
+}  // namespace vk
+
+#endif  // VK_DEBUG_THREAD_HPP_