• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Context.hpp"
16 
17 #include "EventListener.hpp"
18 #include "File.hpp"
19 #include "Thread.hpp"
20 #include "Variable.hpp"
21 #include "WeakMap.hpp"
22 
23 #include "System/Debug.hpp"
24 
25 #include <memory>
26 #include <mutex>
27 #include <thread>
28 #include <unordered_map>
29 #include <unordered_set>
30 
31 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
32 #	define CHECK_REENTRANT_CONTEXT_LOCKS 1
33 #else
34 #	define CHECK_REENTRANT_CONTEXT_LOCKS 0
35 #endif
36 
37 namespace {
38 
39 #if CHECK_REENTRANT_CONTEXT_LOCKS
40 thread_local std::unordered_set<const void *> contextsWithLock;
41 #endif
42 
43 ////////////////////////////////////////////////////////////////////////////////
44 // Broadcaster - template base class for ServerEventBroadcaster and
45 // ClientEventBroadcaster
46 ////////////////////////////////////////////////////////////////////////////////
47 template<typename Listener>
48 class Broadcaster : public Listener
49 {
50 public:
51 	void add(Listener *);
52 	void remove(Listener *);
53 
54 protected:
55 	template<typename F>
56 	inline void foreach(F &&);
57 
58 	template<typename F>
59 	inline void modify(F &&);
60 
61 	using ListenerSet = std::unordered_set<Listener *>;
62 	std::recursive_mutex mutex;
63 	std::shared_ptr<ListenerSet> listeners = std::make_shared<ListenerSet>();
64 	int listenersInUse = 0;
65 };
66 
67 template<typename Listener>
add(Listener * l)68 void Broadcaster<Listener>::add(Listener *l)
69 {
70 	modify([&]() { listeners->emplace(l); });
71 }
72 
73 template<typename Listener>
remove(Listener * l)74 void Broadcaster<Listener>::remove(Listener *l)
75 {
76 	modify([&]() { listeners->erase(l); });
77 }
78 
79 template<typename Listener>
80 template<typename F>
foreach(F && f)81 void Broadcaster<Listener>::foreach(F &&f)
82 {
83 	std::unique_lock<std::recursive_mutex> lock(mutex);
84 	++listenersInUse;
85 	auto copy = listeners;
86 	for(auto l : *copy) { f(l); }
87 	--listenersInUse;
88 }
89 
90 template<typename Listener>
91 template<typename F>
modify(F && f)92 void Broadcaster<Listener>::modify(F &&f)
93 {
94 	std::unique_lock<std::recursive_mutex> lock(mutex);
95 	if(listenersInUse > 0)
96 	{
97 		// The listeners map is current being iterated over.
98 		// Make a copy before making the edit.
99 		listeners = std::make_shared<ListenerSet>(*listeners);
100 	}
101 	f();
102 }
103 
104 ////////////////////////////////////////////////////////////////////////////////
105 // ServerEventBroadcaster
106 ////////////////////////////////////////////////////////////////////////////////
107 class ServerEventBroadcaster : public Broadcaster<vk::dbg::ServerEventListener>
108 {
109 public:
110 	using Thread = vk::dbg::Thread;
111 
onThreadStarted(Thread::ID id)112 	void onThreadStarted(Thread::ID id) override
113 	{
114 		foreach([&](auto *l) { l->onThreadStarted(id); });
115 	}
116 
onThreadStepped(Thread::ID id)117 	void onThreadStepped(Thread::ID id) override
118 	{
119 		foreach([&](auto *l) { l->onThreadStepped(id); });
120 	}
121 
onLineBreakpointHit(Thread::ID id)122 	void onLineBreakpointHit(Thread::ID id) override
123 	{
124 		foreach([&](auto *l) { l->onLineBreakpointHit(id); });
125 	}
126 
onFunctionBreakpointHit(Thread::ID id)127 	void onFunctionBreakpointHit(Thread::ID id) override
128 	{
129 		foreach([&](auto *l) { l->onFunctionBreakpointHit(id); });
130 	}
131 };
132 
133 ////////////////////////////////////////////////////////////////////////////////
134 // ClientEventBroadcaster
135 ////////////////////////////////////////////////////////////////////////////////
136 class ClientEventBroadcaster : public Broadcaster<vk::dbg::ClientEventListener>
137 {
138 public:
onSetBreakpoint(const vk::dbg::Location & location,bool & handled)139 	void onSetBreakpoint(const vk::dbg::Location &location, bool &handled) override
140 	{
141 		foreach([&](auto *l) { l->onSetBreakpoint(location, handled); });
142 	}
143 
onSetBreakpoint(const std::string & func,bool & handled)144 	void onSetBreakpoint(const std::string &func, bool &handled) override
145 	{
146 		foreach([&](auto *l) { l->onSetBreakpoint(func, handled); });
147 	}
148 
onBreakpointsChanged()149 	void onBreakpointsChanged() override
150 	{
151 		foreach([&](auto *l) { l->onBreakpointsChanged(); });
152 	}
153 };
154 
155 }  // namespace
156 
157 namespace vk {
158 namespace dbg {
159 
160 ////////////////////////////////////////////////////////////////////////////////
161 // Context::Impl
162 ////////////////////////////////////////////////////////////////////////////////
163 class Context::Impl : public Context
164 {
165 public:
166 	// Context compliance
167 	Lock lock() override;
168 	void addListener(ClientEventListener *) override;
169 	void removeListener(ClientEventListener *) override;
170 	ClientEventListener *clientEventBroadcast() override;
171 	void addListener(ServerEventListener *) override;
172 	void removeListener(ServerEventListener *) override;
173 	ServerEventListener *serverEventBroadcast() override;
174 
175 	void addFile(const std::shared_ptr<File> &file);
176 
177 	ServerEventBroadcaster serverEventBroadcaster;
178 	ClientEventBroadcaster clientEventBroadcaster;
179 
180 	std::mutex mutex;
181 	std::unordered_map<std::thread::id, std::shared_ptr<Thread>> threadsByStdId;
182 	std::unordered_set<std::string> functionBreakpoints;
183 	std::unordered_map<std::string, std::vector<int>> pendingBreakpoints;
184 	WeakMap<Thread::ID, Thread> threads;
185 	WeakMap<File::ID, File> files;
186 	WeakMap<Frame::ID, Frame> frames;
187 	WeakMap<Scope::ID, Scope> scopes;
188 	WeakMap<Variables::ID, Variables> variables;
189 	Thread::ID nextThreadID = 1;
190 	File::ID nextFileID = 1;
191 	Frame::ID nextFrameID = 1;
192 	Scope::ID nextScopeID = 1;
193 };
194 
lock()195 Context::Lock Context::Impl::lock()
196 {
197 	return Lock(this);
198 }
199 
addListener(ClientEventListener * l)200 void Context::Impl::addListener(ClientEventListener *l)
201 {
202 	clientEventBroadcaster.add(l);
203 }
204 
removeListener(ClientEventListener * l)205 void Context::Impl::removeListener(ClientEventListener *l)
206 {
207 	clientEventBroadcaster.remove(l);
208 }
209 
clientEventBroadcast()210 ClientEventListener *Context::Impl::clientEventBroadcast()
211 {
212 	return &clientEventBroadcaster;
213 }
214 
addListener(ServerEventListener * l)215 void Context::Impl::addListener(ServerEventListener *l)
216 {
217 	serverEventBroadcaster.add(l);
218 }
219 
removeListener(ServerEventListener * l)220 void Context::Impl::removeListener(ServerEventListener *l)
221 {
222 	serverEventBroadcaster.remove(l);
223 }
224 
serverEventBroadcast()225 ServerEventListener *Context::Impl::serverEventBroadcast()
226 {
227 	return &serverEventBroadcaster;
228 }
229 
addFile(const std::shared_ptr<File> & file)230 void Context::Impl::addFile(const std::shared_ptr<File> &file)
231 {
232 	files.add(file->id, file);
233 
234 	auto it = pendingBreakpoints.find(file->name);
235 	if(it != pendingBreakpoints.end())
236 	{
237 		for(auto line : it->second)
238 		{
239 			file->addBreakpoint(line);
240 		}
241 	}
242 }
243 
244 ////////////////////////////////////////////////////////////////////////////////
245 // Context
246 ////////////////////////////////////////////////////////////////////////////////
create()247 std::shared_ptr<Context> Context::create()
248 {
249 	return std::shared_ptr<Context>(new Context::Impl());
250 }
251 
252 ////////////////////////////////////////////////////////////////////////////////
253 // Context::Lock
254 ////////////////////////////////////////////////////////////////////////////////
Lock(Impl * ctx)255 Context::Lock::Lock(Impl *ctx)
256     : ctx(ctx)
257 {
258 #if CHECK_REENTRANT_CONTEXT_LOCKS
259 	ASSERT_MSG(contextsWithLock.count(ctx) == 0, "Attempting to acquire Context lock twice on same thread. This will deadlock");
260 	contextsWithLock.emplace(ctx);
261 #endif
262 	ctx->mutex.lock();
263 }
264 
Lock(Lock && o)265 Context::Lock::Lock(Lock &&o)
266     : ctx(o.ctx)
267 {
268 	o.ctx = nullptr;
269 }
270 
~Lock()271 Context::Lock::~Lock()
272 {
273 	unlock();
274 }
275 
operator =(Lock && o)276 Context::Lock &Context::Lock::operator=(Lock &&o)
277 {
278 	ctx = o.ctx;
279 	o.ctx = nullptr;
280 	return *this;
281 }
282 
unlock()283 void Context::Lock::unlock()
284 {
285 	if(ctx)
286 	{
287 #if CHECK_REENTRANT_CONTEXT_LOCKS
288 		contextsWithLock.erase(ctx);
289 #endif
290 
291 		ctx->mutex.unlock();
292 		ctx = nullptr;
293 	}
294 }
295 
currentThread()296 std::shared_ptr<Thread> Context::Lock::currentThread()
297 {
298 	auto threadIt = ctx->threadsByStdId.find(std::this_thread::get_id());
299 	if(threadIt != ctx->threadsByStdId.end())
300 	{
301 		return threadIt->second;
302 	}
303 	auto id = ++ctx->nextThreadID;
304 	char name[256];
305 	snprintf(name, sizeof(name), "Thread<0x%x>", id.value());
306 
307 	auto thread = std::make_shared<Thread>(id, ctx);
308 	ctx->threads.add(id, thread);
309 	thread->setName(name);
310 	ctx->threadsByStdId.emplace(std::this_thread::get_id(), thread);
311 
312 	ctx->serverEventBroadcast()->onThreadStarted(id);
313 
314 	return thread;
315 }
316 
get(Thread::ID id)317 std::shared_ptr<Thread> Context::Lock::get(Thread::ID id)
318 {
319 	return ctx->threads.get(id);
320 }
321 
threads()322 std::vector<std::shared_ptr<Thread>> Context::Lock::threads()
323 {
324 	std::vector<std::shared_ptr<Thread>> out;
325 	out.reserve(ctx->threads.approx_size());
326 	for(auto it : ctx->threads)
327 	{
328 		out.push_back(it.second);
329 	}
330 	return out;
331 }
332 
createVirtualFile(const std::string & name,const std::string & source)333 std::shared_ptr<File> Context::Lock::createVirtualFile(const std::string &name,
334                                                        const std::string &source)
335 {
336 	auto file = File::createVirtual(ctx->nextFileID++, name, source);
337 	ctx->addFile(file);
338 	return file;
339 }
340 
createPhysicalFile(const std::string & path)341 std::shared_ptr<File> Context::Lock::createPhysicalFile(const std::string &path)
342 {
343 	auto file = File::createPhysical(ctx->nextFileID++, path);
344 	ctx->addFile(file);
345 	return file;
346 }
347 
get(File::ID id)348 std::shared_ptr<File> Context::Lock::get(File::ID id)
349 {
350 	return ctx->files.get(id);
351 }
352 
findFile(const std::string & path)353 std::shared_ptr<File> Context::Lock::findFile(const std::string &path)
354 {
355 	for(auto it : ctx->files)
356 	{
357 		auto &file = it.second;
358 		if(file->path() == path)
359 		{
360 			return file;
361 		}
362 	}
363 	return nullptr;
364 }
365 
files()366 std::vector<std::shared_ptr<File>> Context::Lock::files()
367 {
368 	std::vector<std::shared_ptr<File>> out;
369 	out.reserve(ctx->files.approx_size());
370 	for(auto it : ctx->files)
371 	{
372 		out.push_back(it.second);
373 	}
374 	return out;
375 }
376 
createFrame(const std::shared_ptr<File> & file,std::string function)377 std::shared_ptr<Frame> Context::Lock::createFrame(
378     const std::shared_ptr<File> &file, std::string function)
379 {
380 	auto frame = std::make_shared<Frame>(ctx->nextFrameID++, std::move(function));
381 	ctx->frames.add(frame->id, frame);
382 	frame->arguments = createScope(file);
383 	frame->locals = createScope(file);
384 	frame->registers = createScope(file);
385 	frame->hovers = createScope(file);
386 	frame->location.file = file;
387 	return frame;
388 }
389 
get(Frame::ID id)390 std::shared_ptr<Frame> Context::Lock::get(Frame::ID id)
391 {
392 	return ctx->frames.get(id);
393 }
394 
createScope(const std::shared_ptr<File> & file)395 std::shared_ptr<Scope> Context::Lock::createScope(
396     const std::shared_ptr<File> &file)
397 {
398 	auto scope = std::make_shared<Scope>(ctx->nextScopeID++, file, std::make_shared<VariableContainer>());
399 	ctx->scopes.add(scope->id, scope);
400 	return scope;
401 }
402 
get(Scope::ID id)403 std::shared_ptr<Scope> Context::Lock::get(Scope::ID id)
404 {
405 	return ctx->scopes.get(id);
406 }
407 
track(const std::shared_ptr<Variables> & vars)408 void Context::Lock::track(const std::shared_ptr<Variables> &vars)
409 {
410 	ctx->variables.add(vars->id, vars);
411 }
412 
get(Variables::ID id)413 std::shared_ptr<Variables> Context::Lock::get(Variables::ID id)
414 {
415 	return ctx->variables.get(id);
416 }
417 
clearFunctionBreakpoints()418 void Context::Lock::clearFunctionBreakpoints()
419 {
420 	ctx->functionBreakpoints.clear();
421 }
422 
addFunctionBreakpoint(const std::string & name)423 void Context::Lock::addFunctionBreakpoint(const std::string &name)
424 {
425 	ctx->functionBreakpoints.emplace(name);
426 }
427 
addPendingBreakpoints(const std::string & filename,const std::vector<int> & lines)428 void Context::Lock::addPendingBreakpoints(const std::string &filename, const std::vector<int> &lines)
429 {
430 	ctx->pendingBreakpoints.emplace(filename, lines);
431 }
432 
isFunctionBreakpoint(const std::string & name)433 bool Context::Lock::isFunctionBreakpoint(const std::string &name)
434 {
435 	return ctx->functionBreakpoints.count(name) > 0;
436 }
437 
getFunctionBreakpoints()438 std::unordered_set<std::string> Context::Lock::getFunctionBreakpoints()
439 {
440 	return ctx->functionBreakpoints;
441 }
442 
443 }  // namespace dbg
444 }  // namespace vk
445