// 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 "marl/mutex.h" #include "marl/tsa.h" #include #include #include #include namespace vk { namespace dbg { class File; class VariableContainer; class ServerEventListener; // 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; inline Scope(ID id, const std::shared_ptr &file, const std::shared_ptr &variables); // The unique identifier of the scope. const ID id; // The file this scope is associated with. const std::shared_ptr file; // The scope's variables. const std::shared_ptr variables; }; Scope::Scope(ID id, const std::shared_ptr &file, const std::shared_ptr &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; 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. const std::string function; // The current execution location within the stack frame. Location location; // The scope for the frame's arguments. std::shared_ptr arguments; // The scope for the frame's locals. std::shared_ptr locals; // The scope for the frame's registers. std::shared_ptr registers; // The scope for variables that should only appear in hover tooltips. std::shared_ptr hovers; }; Frame::Frame(ID id, std::string function) : id(id) , function(std::move(function)) {} // Thread holds the state for a single thread of execution. class Thread { public: using ID = dbg::ID; using UpdateFrame = std::function; // 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, then calls f to modify the new frame of the stack. void enter(const std::shared_ptr &file, const std::string &function, const UpdateFrame &f = nullptr); // exit() pops the thread's stack frame. // If isStep is true, then this will be considered a line-steppable event, // and the debugger may pause at the function call at the new top most stack // frame. void exit(bool isStep = false); // 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 stack() const; // depth() returns the number of stack frames. size_t depth() const; // 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 and isStep is true, update() // potentially blocks until the thread is resumed with one of the methods // below. // isStep is used to distinguish same-statement column position updates // from full line updates. Note that we cannot simply examine line position // changes as single-line loops such as `while(true) { foo(); }` would not // be correctly steppable. void update(bool isStep, const UpdateFrame &f); // 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(); // The unique identifier of the thread. const ID id; private: Context *const ctx; void onLocationUpdate(marl::lock &lock) REQUIRES(mutex); mutable marl::mutex mutex; std::string name_ GUARDED_BY(mutex); std::vector> frames GUARDED_BY(mutex); State state_ GUARDED_BY(mutex) = State::Running; std::weak_ptr pauseAtFrame GUARDED_BY(mutex); std::condition_variable stateCV; }; } // namespace dbg } // namespace vk #endif // VK_DEBUG_THREAD_HPP_