| // 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) | 
 |     , ctx(ctx) | 
 | {} | 
 |  | 
 | void Thread::setName(const std::string &name) | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	name_ = name; | 
 | } | 
 |  | 
 | std::string Thread::name() const | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	return name_; | 
 | } | 
 |  | 
 | void Thread::onLocationUpdate(marl::lock &lock) | 
 | { | 
 | 	auto location = frames.back()->location; | 
 |  | 
 | 	if(state_ == State::Running) | 
 | 	{ | 
 | 		if(location.file->hasBreakpoint(location.line)) | 
 | 		{ | 
 | 			ctx->serverEventBroadcast()->onLineBreakpointHit(id); | 
 | 			state_ = State::Paused; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	switch(state_) | 
 | 	{ | 
 | 		case State::Paused: | 
 | 		{ | 
 | 			lock.wait(stateCV, [this]() REQUIRES(mutex) { return state_ != State::Paused; }); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		case State::Stepping: | 
 | 		{ | 
 | 			bool pause = false; | 
 |  | 
 | 			{ | 
 | 				auto frame = pauseAtFrame.lock(); | 
 | 				pause = !frame;             // Pause if there's no pause-at-frame... | 
 | 				if(frame == frames.back())  // ... or if we've reached the pause-at-frame | 
 | 				{ | 
 | 					pause = true; | 
 | 					pauseAtFrame.reset(); | 
 | 				} | 
 | 			} | 
 |  | 
 | 			if(pause) | 
 | 			{ | 
 | 				ctx->serverEventBroadcast()->onThreadStepped(id); | 
 | 				state_ = State::Paused; | 
 | 				lock.wait(stateCV, [this]() REQUIRES(mutex) { return state_ != State::Paused; }); | 
 | 			} | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		case State::Running: | 
 | 			break; | 
 | 	} | 
 | } | 
 |  | 
 | void Thread::enter(const std::shared_ptr<File> &file, const std::string &function, const UpdateFrame &f) | 
 | { | 
 | 	std::shared_ptr<Frame> frame; | 
 | 	bool isFunctionBreakpoint; | 
 | 	{ | 
 | 		auto lock = ctx->lock(); | 
 | 		frame = lock.createFrame(file, function); | 
 | 		isFunctionBreakpoint = lock.isFunctionBreakpoint(function); | 
 | 	} | 
 |  | 
 | 	{ | 
 | 		marl::lock lock(mutex); | 
 | 		frames.push_back(frame); | 
 |  | 
 | 		if(f) { f(*frame); } | 
 |  | 
 | 		if(isFunctionBreakpoint) | 
 | 		{ | 
 | 			ctx->serverEventBroadcast()->onFunctionBreakpointHit(id); | 
 | 			state_ = State::Paused; | 
 | 		} | 
 |  | 
 | 		onLocationUpdate(lock); | 
 | 	} | 
 | } | 
 |  | 
 | void Thread::exit(bool isStep /* = false */) | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	frames.pop_back(); | 
 | 	if(isStep) | 
 | 	{ | 
 | 		onLocationUpdate(lock); | 
 | 	} | 
 | } | 
 |  | 
 | void Thread::update(bool isStep, const UpdateFrame &f) | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	auto &frame = *frames.back(); | 
 | 	if(isStep) | 
 | 	{ | 
 | 		auto oldLocation = frame.location; | 
 | 		f(frame); | 
 | 		if(frame.location != oldLocation) | 
 | 		{ | 
 | 			onLocationUpdate(lock); | 
 | 		} | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		f(frame); | 
 | 	} | 
 | } | 
 |  | 
 | Frame Thread::frame() const | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	return *frames.back(); | 
 | } | 
 |  | 
 | std::vector<Frame> Thread::stack() const | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	std::vector<Frame> out; | 
 | 	out.reserve(frames.size()); | 
 | 	for(auto frame : frames) | 
 | 	{ | 
 | 		out.push_back(*frame); | 
 | 	} | 
 | 	return out; | 
 | } | 
 |  | 
 | size_t Thread::depth() const | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	return frames.size(); | 
 | } | 
 |  | 
 | Thread::State Thread::state() const | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	return state_; | 
 | } | 
 |  | 
 | void Thread::resume() | 
 | { | 
 | 	{ | 
 | 		marl::lock lock(mutex); | 
 | 		state_ = State::Running; | 
 | 	} | 
 | 	stateCV.notify_all(); | 
 | } | 
 |  | 
 | void Thread::pause() | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	state_ = State::Paused; | 
 | } | 
 |  | 
 | void Thread::stepIn() | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	state_ = State::Stepping; | 
 | 	pauseAtFrame.reset(); | 
 | 	stateCV.notify_all(); | 
 | } | 
 |  | 
 | void Thread::stepOver() | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	state_ = State::Stepping; | 
 | 	pauseAtFrame = frames.back(); | 
 | 	stateCV.notify_all(); | 
 | } | 
 |  | 
 | void Thread::stepOut() | 
 | { | 
 | 	marl::lock lock(mutex); | 
 | 	state_ = State::Stepping; | 
 | 	pauseAtFrame = (frames.size() > 1) ? frames[frames.size() - 2] : nullptr; | 
 | 	stateCV.notify_all(); | 
 | } | 
 |  | 
 | }  // namespace dbg | 
 | }  // namespace vk |