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 #include "Thread.hpp"
16 
17 #include "Context.hpp"
18 #include "EventListener.hpp"
19 #include "File.hpp"
20 
21 namespace vk {
22 namespace dbg {
23 
Thread(ID id,Context * ctx)24 Thread::Thread(ID id, Context *ctx)
25     : id(id)
26     , ctx(ctx)
27 {}
28 
setName(const std::string & name)29 void Thread::setName(const std::string &name)
30 {
31 	marl::lock lock(mutex);
32 	name_ = name;
33 }
34 
name() const35 std::string Thread::name() const
36 {
37 	marl::lock lock(mutex);
38 	return name_;
39 }
40 
onLocationUpdate(marl::lock & lock)41 void Thread::onLocationUpdate(marl::lock &lock)
42 {
43 	auto location = frames.back()->location;
44 
45 	if(state_ == State::Running)
46 	{
47 		if(location.file->hasBreakpoint(location.line))
48 		{
49 			ctx->serverEventBroadcast()->onLineBreakpointHit(id);
50 			state_ = State::Paused;
51 		}
52 	}
53 
54 	switch(state_)
55 	{
56 		case State::Paused:
57 		{
58 			lock.wait(stateCV, [this]() REQUIRES(mutex) { return state_ != State::Paused; });
59 			break;
60 		}
61 
62 		case State::Stepping:
63 		{
64 			bool pause = false;
65 
66 			{
67 				auto frame = pauseAtFrame.lock();
68 				pause = !frame;             // Pause if there's no pause-at-frame...
69 				if(frame == frames.back())  // ... or if we've reached the pause-at-frame
70 				{
71 					pause = true;
72 					pauseAtFrame.reset();
73 				}
74 			}
75 
76 			if(pause)
77 			{
78 				ctx->serverEventBroadcast()->onThreadStepped(id);
79 				state_ = State::Paused;
80 				lock.wait(stateCV, [this]() REQUIRES(mutex) { return state_ != State::Paused; });
81 			}
82 			break;
83 		}
84 
85 		case State::Running:
86 			break;
87 	}
88 }
89 
enter(const std::shared_ptr<File> & file,const std::string & function,const UpdateFrame & f)90 void Thread::enter(const std::shared_ptr<File> &file, const std::string &function, const UpdateFrame &f)
91 {
92 	std::shared_ptr<Frame> frame;
93 	bool isFunctionBreakpoint;
94 	{
95 		auto lock = ctx->lock();
96 		frame = lock.createFrame(file, function);
97 		isFunctionBreakpoint = lock.isFunctionBreakpoint(function);
98 	}
99 
100 	{
101 		marl::lock lock(mutex);
102 		frames.push_back(frame);
103 
104 		if(f) { f(*frame); }
105 
106 		if(isFunctionBreakpoint)
107 		{
108 			ctx->serverEventBroadcast()->onFunctionBreakpointHit(id);
109 			state_ = State::Paused;
110 		}
111 
112 		onLocationUpdate(lock);
113 	}
114 }
115 
exit(bool isStep)116 void Thread::exit(bool isStep /* = false */)
117 {
118 	marl::lock lock(mutex);
119 	frames.pop_back();
120 	if(isStep)
121 	{
122 		onLocationUpdate(lock);
123 	}
124 }
125 
update(bool isStep,const UpdateFrame & f)126 void Thread::update(bool isStep, const UpdateFrame &f)
127 {
128 	marl::lock lock(mutex);
129 	auto &frame = *frames.back();
130 	if(isStep)
131 	{
132 		auto oldLocation = frame.location;
133 		f(frame);
134 		if(frame.location != oldLocation)
135 		{
136 			onLocationUpdate(lock);
137 		}
138 	}
139 	else
140 	{
141 		f(frame);
142 	}
143 }
144 
frame() const145 Frame Thread::frame() const
146 {
147 	marl::lock lock(mutex);
148 	return *frames.back();
149 }
150 
stack() const151 std::vector<Frame> Thread::stack() const
152 {
153 	marl::lock lock(mutex);
154 	std::vector<Frame> out;
155 	out.reserve(frames.size());
156 	for(auto frame : frames)
157 	{
158 		out.push_back(*frame);
159 	}
160 	return out;
161 }
162 
depth() const163 size_t Thread::depth() const
164 {
165 	marl::lock lock(mutex);
166 	return frames.size();
167 }
168 
state() const169 Thread::State Thread::state() const
170 {
171 	marl::lock lock(mutex);
172 	return state_;
173 }
174 
resume()175 void Thread::resume()
176 {
177 	{
178 		marl::lock lock(mutex);
179 		state_ = State::Running;
180 	}
181 	stateCV.notify_all();
182 }
183 
pause()184 void Thread::pause()
185 {
186 	marl::lock lock(mutex);
187 	state_ = State::Paused;
188 }
189 
stepIn()190 void Thread::stepIn()
191 {
192 	marl::lock lock(mutex);
193 	state_ = State::Stepping;
194 	pauseAtFrame.reset();
195 	stateCV.notify_all();
196 }
197 
stepOver()198 void Thread::stepOver()
199 {
200 	marl::lock lock(mutex);
201 	state_ = State::Stepping;
202 	pauseAtFrame = frames.back();
203 	stateCV.notify_all();
204 }
205 
stepOut()206 void Thread::stepOut()
207 {
208 	marl::lock lock(mutex);
209 	state_ = State::Stepping;
210 	pauseAtFrame = (frames.size() > 1) ? frames[frames.size() - 2] : nullptr;
211 	stateCV.notify_all();
212 }
213 
214 }  // namespace dbg
215 }  // namespace vk
216