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