1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef VK_DEBUG_THREAD_HPP_
16 #define VK_DEBUG_THREAD_HPP_
17
18 #include "Context.hpp"
19 #include "ID.hpp"
20 #include "Location.hpp"
21
22 #include "marl/mutex.h"
23 #include "marl/tsa.h"
24
25 #include <condition_variable>
26 #include <memory>
27 #include <string>
28 #include <vector>
29
30 namespace vk {
31 namespace dbg {
32
33 class File;
34 class VariableContainer;
35 class ServerEventListener;
36
37 // Scope is a container for variables and is used to provide source data for the
38 // DAP 'Scope' type:
39 // https://microsoft.github.io/debug-adapter-protocol/specification#Types_Scope
40 class Scope
41 {
42 public:
43 using ID = dbg::ID<Scope>;
44
45 inline Scope(ID id,
46 const std::shared_ptr<File> &file,
47 const std::shared_ptr<VariableContainer> &variables);
48
49 // The unique identifier of the scope.
50 const ID id;
51
52 // The file this scope is associated with.
53 const std::shared_ptr<File> file;
54
55 // The scope's variables.
56 const std::shared_ptr<VariableContainer> variables;
57 };
58
Scope(ID id,const std::shared_ptr<File> & file,const std::shared_ptr<VariableContainer> & variables)59 Scope::Scope(ID id,
60 const std::shared_ptr<File> &file,
61 const std::shared_ptr<VariableContainer> &variables)
62 : id(id)
63 , file(file)
64 , variables(variables)
65 {}
66
67 // Frame holds a number of variable scopes for one of a thread's stack frame,
68 // and is used to provide source data for the DAP 'StackFrame' type:
69 // https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame
70 class Frame
71 {
72 public:
73 using ID = dbg::ID<Frame>;
74
75 inline Frame(ID id, std::string function);
76
77 // The unique identifier of the stack frame.
78 const ID id;
79
80 // The name of function for this stack frame.
81 const std::string function;
82
83 // The current execution location within the stack frame.
84 Location location;
85
86 // The scope for the frame's arguments.
87 std::shared_ptr<Scope> arguments;
88
89 // The scope for the frame's locals.
90 std::shared_ptr<Scope> locals;
91
92 // The scope for the frame's registers.
93 std::shared_ptr<Scope> registers;
94
95 // The scope for variables that should only appear in hover tooltips.
96 std::shared_ptr<Scope> hovers;
97 };
98
Frame(ID id,std::string function)99 Frame::Frame(ID id, std::string function)
100 : id(id)
101 , function(std::move(function))
102 {}
103
104 // Thread holds the state for a single thread of execution.
105 class Thread
106 {
107 public:
108 using ID = dbg::ID<Thread>;
109
110 using UpdateFrame = std::function<void(Frame &)>;
111
112 // The current execution state.
113 enum class State
114 {
115 Running, // Thread is running.
116 Stepping, // Thread is currently single line stepping.
117 Paused // Thread is currently paused.
118 };
119
120 Thread(ID id, Context *ctx);
121
122 // setName() sets the name of the thread.
123 void setName(const std::string &);
124
125 // name() returns the name of the thread.
126 std::string name() const;
127
128 // enter() pushes the thread's stack with a new frame created with the given
129 // file and function, then calls f to modify the new frame of the stack.
130 void enter(const std::shared_ptr<File> &file, const std::string &function, const UpdateFrame &f = nullptr);
131
132 // exit() pops the thread's stack frame.
133 // If isStep is true, then this will be considered a line-steppable event,
134 // and the debugger may pause at the function call at the new top most stack
135 // frame.
136 void exit(bool isStep = false);
137
138 // frame() returns a copy of the thread's top most stack frame.
139 Frame frame() const;
140
141 // stack() returns a copy of the thread's current stack frames.
142 std::vector<Frame> stack() const;
143
144 // depth() returns the number of stack frames.
145 size_t depth() const;
146
147 // state() returns the current thread's state.
148 State state() const;
149
150 // update() calls f to modify the top most frame of the stack.
151 // If the frame's location is changed and isStep is true, update()
152 // potentially blocks until the thread is resumed with one of the methods
153 // below.
154 // isStep is used to distinguish same-statement column position updates
155 // from full line updates. Note that we cannot simply examine line position
156 // changes as single-line loops such as `while(true) { foo(); }` would not
157 // be correctly steppable.
158 void update(bool isStep, const UpdateFrame &f);
159
160 // resume() resumes execution of the thread by unblocking a call to
161 // update() and setting the thread's state to State::Running.
162 void resume();
163
164 // pause() suspends execution of the thread by blocking the next call to
165 // update() and setting the thread's state to State::Paused.
166 void pause();
167
168 // stepIn() temporarily resumes execution of the thread by unblocking a
169 // call to update(), and setting the thread's state to State::Stepping.
170 // The next call to update() will suspend execution again.
171 void stepIn();
172
173 // stepOver() temporarily resumes execution of the thread by unblocking a
174 // call to update(), and setting the thread's state to State::Stepping.
175 // The next call to update() within the same stack frame will suspend
176 // execution again.
177 void stepOver();
178
179 // stepOut() temporarily resumes execution of the thread by unblocking a
180 // call to update(), and setting the thread's state to State::Stepping.
181 // The next call to update() at the stack frame above the current frame will
182 // suspend execution again.
183 void stepOut();
184
185 // The unique identifier of the thread.
186 const ID id;
187
188 private:
189 Context *const ctx;
190
191 void onLocationUpdate(marl::lock &lock) REQUIRES(mutex);
192
193 mutable marl::mutex mutex;
194 std::string name_ GUARDED_BY(mutex);
195 std::vector<std::shared_ptr<Frame>> frames GUARDED_BY(mutex);
196 State state_ GUARDED_BY(mutex) = State::Running;
197 std::weak_ptr<Frame> pauseAtFrame GUARDED_BY(mutex);
198
199 std::condition_variable stateCV;
200 };
201
202 } // namespace dbg
203 } // namespace vk
204
205 #endif // VK_DEBUG_THREAD_HPP_
206