Vulkan/Debug: Expose Frames from Thread

Previously Frame was an Thread-internally managed structure, with special accessors for the variable containers of the top most frame.

With this change, the Frame can be modified by calling `Thread::update()`. This allows for more powerful edits of the frame, while still preserving a mutex lock over the structure.

Bug: b/145351270
Change-Id: I81e87884e26bdd6f22b47e8f71b37bbfc91c7830
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/39882
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Vulkan/Debug/Context.cpp b/src/Vulkan/Debug/Context.cpp
index d11c130..1f51617 100644
--- a/src/Vulkan/Debug/Context.cpp
+++ b/src/Vulkan/Debug/Context.cpp
@@ -293,9 +293,9 @@
 }
 
 std::shared_ptr<Frame> Context::Lock::createFrame(
-    const std::shared_ptr<File> &file)
+    const std::shared_ptr<File> &file, std::string function)
 {
-	auto frame = std::make_shared<Frame>(ctx->nextFrameID++);
+	auto frame = std::make_shared<Frame>(ctx->nextFrameID++, std::move(function));
 	ctx->frames.add(frame->id, frame);
 	frame->arguments = createScope(file);
 	frame->locals = createScope(file);
diff --git a/src/Vulkan/Debug/Context.hpp b/src/Vulkan/Debug/Context.hpp
index 790587d..4c4d8af 100644
--- a/src/Vulkan/Debug/Context.hpp
+++ b/src/Vulkan/Debug/Context.hpp
@@ -89,9 +89,10 @@
 		// files() returns the full list of files.
 		std::vector<std::shared_ptr<File>> files();
 
-		// createFrame() returns a new frame for the given file.
+		// createFrame() returns a new frame for the given file and function
+		// name.
 		std::shared_ptr<Frame> createFrame(
-		    const std::shared_ptr<File> &file);
+		    const std::shared_ptr<File> &file, std::string function);
 
 		// get() returns the frame with the given ID, or null if the frame
 		// does not exist or no longer has any external shared_ptr references.
diff --git a/src/Vulkan/Debug/Location.hpp b/src/Vulkan/Debug/Location.hpp
index 3990472..c924a5a 100644
--- a/src/Vulkan/Debug/Location.hpp
+++ b/src/Vulkan/Debug/Location.hpp
@@ -26,17 +26,32 @@
 struct Location
 {
 	Location() = default;
-	inline Location(int line, const std::shared_ptr<File> &file);
+	inline Location(const std::shared_ptr<File> &file, int line, int column = 0);
 
-	int line = 0;  // 1 based. 0 represents no line.
+	inline bool operator==(const Location &o) const;
+	inline bool operator!=(const Location &o) const;
+
 	std::shared_ptr<File> file;
+	int line = 0;    // 1 based. 0 represents no line.
+	int column = 0;  // 1 based. 0 represents no particular column.
 };
 
-Location::Location(int line, const std::shared_ptr<File> &file)
-    : line(line)
-    , file(file)
+Location::Location(const std::shared_ptr<File> &file, int line, int column /* = 0 */)
+    : file(file)
+    , line(line)
+    , column(column)
 {}
 
+bool Location::operator==(const Location &o) const
+{
+	return file == o.file && line == o.line && column == o.column;
+}
+
+bool Location::operator!=(const Location &o) const
+{
+	return !(*this == o);
+}
+
 }  // namespace dbg
 }  // namespace vk
 
diff --git a/src/Vulkan/Debug/Server.cpp b/src/Vulkan/Debug/Server.cpp
index 244f5fc..f004fa8 100644
--- a/src/Vulkan/Debug/Server.cpp
+++ b/src/Vulkan/Debug/Server.cpp
@@ -198,8 +198,9 @@
 		    dap::StackTraceResponse response;
 		    response.totalFrames = stack.size();
 		    response.stackFrames.reserve(stack.size());
-		    for(auto const &frame : stack)
+		    for(int i = static_cast<int>(stack.size()) - 1; i >= 0; i--)
 		    {
+			    auto const &frame = stack[i];
 			    auto const &loc = frame.location;
 			    dap::StackFrame sf;
 			    sf.column = 0;
diff --git a/src/Vulkan/Debug/Thread.cpp b/src/Vulkan/Debug/Thread.cpp
index a542e37..753b7db 100644
--- a/src/Vulkan/Debug/Thread.cpp
+++ b/src/Vulkan/Debug/Thread.cpp
@@ -38,10 +38,9 @@
 	return name_;
 }
 
-void Thread::update(const Location &location)
+void Thread::onLocationUpdate(std::unique_lock<std::mutex> &lock)
 {
-	std::unique_lock<std::mutex> lock(mutex);
-	frames.back()->location = location;
+	auto location = frames.back()->location;
 
 	if(state_ == State::Running)
 	{
@@ -79,11 +78,10 @@
 
 void Thread::enter(Context::Lock &ctxlck, const std::shared_ptr<File> &file, const std::string &function)
 {
-	auto frame = ctxlck.createFrame(file);
+	auto frame = ctxlck.createFrame(file, function);
 	auto isFunctionBreakpoint = ctxlck.isFunctionBreakpoint(function);
 
 	std::unique_lock<std::mutex> lock(mutex);
-	frame->function = function;
 	frames.push_back(frame);
 	if(isFunctionBreakpoint)
 	{
@@ -98,28 +96,22 @@
 	frames.pop_back();
 }
 
-std::shared_ptr<VariableContainer> Thread::registers() const
+void Thread::update(std::function<void(Frame &)> f)
 {
 	std::unique_lock<std::mutex> lock(mutex);
-	return frames.back()->registers->variables;
+	auto &frame = *frames.back();
+	auto oldLocation = frame.location;
+	f(frame);
+	if(frame.location != oldLocation)
+	{
+		onLocationUpdate(lock);
+	}
 }
 
-std::shared_ptr<VariableContainer> Thread::locals() const
+Frame Thread::frame() 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;
+	return *frames.back();
 }
 
 std::vector<Frame> Thread::stack() const
diff --git a/src/Vulkan/Debug/Thread.hpp b/src/Vulkan/Debug/Thread.hpp
index 77d7425..a59f47f 100644
--- a/src/Vulkan/Debug/Thread.hpp
+++ b/src/Vulkan/Debug/Thread.hpp
@@ -70,13 +70,13 @@
 public:
 	using ID = dbg::ID<Frame>;
 
-	inline Frame(ID id);
+	inline Frame(ID id, std::string function);
 
 	// The unique identifier of the stack frame.
 	const ID id;
 
 	// The name of function for this stack frame.
-	std::string function;
+	const std::string function;
 
 	// The current execution location within the stack frame.
 	Location location;
@@ -94,8 +94,9 @@
 	std::shared_ptr<Scope> hovers;
 };
 
-Frame::Frame(ID id)
+Frame::Frame(ID id, std::string function)
     : id(id)
+    , function(std::move(function))
 {}
 
 // Thread holds the state for a single thread of execution.
@@ -127,17 +128,8 @@
 	// 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;
+	// frame() returns a copy of the thread's top most stack frame.
+	Frame frame() const;
 
 	// stack() returns a copy of the thread's current stack frames.
 	std::vector<Frame> stack() const;
@@ -145,6 +137,11 @@
 	// state() returns the current thread's state.
 	State state() const;
 
+	// update() calls f to modify the top most frame of the stack.
+	// If the frame's location is changed, update() potentially blocks until the
+	// thread is resumed with one of the methods below.
+	void update(std::function<void(Frame &)> f);
+
 	// resume() resumes execution of the thread by unblocking a call to
 	// update() and setting the thread's state to State::Running.
 	void resume();
@@ -170,16 +167,14 @@
 	// 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;
 
+	void onLocationUpdate(std::unique_lock<std::mutex> &lock);
+
 	mutable std::mutex mutex;
 	std::string name_;
 	std::vector<std::shared_ptr<Frame>> frames;
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index 186b1ec..b40886a 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -1785,7 +1785,9 @@
 #ifdef ENABLE_VK_DEBUGGER
 		if(debuggerThread)
 		{
-			debuggerThread->update({ line++, debuggerFile });
+			debuggerThread->update([&](vk::dbg::Frame &frame) {
+				frame.location = { debuggerFile, line++, 0 };
+			});
 		}
 #endif  // ENABLE_VK_DEBUGGER