| // 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 |