// 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, const std::string &function, const UpdateFrame &f) { std::shared_ptr 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 Thread::stack() const { marl::lock lock(mutex); std::vector 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