• 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 "Server.hpp"
16 
17 #include "Context.hpp"
18 #include "EventListener.hpp"
19 #include "File.hpp"
20 #include "Thread.hpp"
21 #include "Variable.hpp"
22 
23 #include "dap/network.h"
24 #include "dap/protocol.h"
25 #include "dap/session.h"
26 #include "marl/waitgroup.h"
27 
28 #include <thread>
29 #include <unordered_set>
30 
31 // Switch for controlling DAP debug logging
32 #define ENABLE_DAP_LOGGING 0
33 
34 #if ENABLE_DAP_LOGGING
35 #	define DAP_LOG(msg, ...) printf(msg "\n", ##__VA_ARGS__)
36 #else
37 #	define DAP_LOG(...) \
38 		do               \
39 		{                \
40 		} while(false)
41 #endif
42 
43 namespace vk {
44 namespace dbg {
45 
46 class Server::Impl : public Server, public EventListener
47 {
48 public:
49 	Impl(const std::shared_ptr<Context> &ctx, int port);
50 	~Impl();
51 
52 	// EventListener
53 	void onThreadStarted(ID<Thread>) override;
54 	void onThreadStepped(ID<Thread>) override;
55 	void onLineBreakpointHit(ID<Thread>) override;
56 	void onFunctionBreakpointHit(ID<Thread>) override;
57 
58 	dap::Scope scope(const char *type, Scope *);
59 	dap::Source source(File *);
60 	std::shared_ptr<File> file(const dap::Source &source);
61 
62 	const std::shared_ptr<Context> ctx;
63 	const std::unique_ptr<dap::net::Server> server;
64 	const std::unique_ptr<dap::Session> session;
65 	std::atomic<bool> clientIsVisualStudio = { false };
66 };
67 
Impl(const std::shared_ptr<Context> & context,int port)68 Server::Impl::Impl(const std::shared_ptr<Context> &context, int port)
69     : ctx(context)
70     , server(dap::net::Server::create())
71     , session(dap::Session::create())
72 {
73 	session->registerHandler([](const dap::DisconnectRequest &req) {
74 		DAP_LOG("DisconnectRequest receieved");
75 		return dap::DisconnectResponse();
76 	});
77 
78 	session->registerHandler([&](const dap::InitializeRequest &req) {
79 		DAP_LOG("InitializeRequest receieved");
80 		dap::InitializeResponse response;
81 		response.supportsFunctionBreakpoints = true;
82 		response.supportsConfigurationDoneRequest = true;
83 		response.supportsEvaluateForHovers = true;
84 		clientIsVisualStudio = (req.clientID.value("") == "visualstudio");
85 		return response;
86 	});
87 
88 	session->registerSentHandler(
89 	    [&](const dap::ResponseOrError<dap::InitializeResponse> &response) {
90 		    DAP_LOG("InitializeResponse sent");
91 		    session->send(dap::InitializedEvent());
92 	    });
93 
94 	session->registerHandler([](const dap::SetExceptionBreakpointsRequest &req) {
95 		DAP_LOG("SetExceptionBreakpointsRequest receieved");
96 		dap::SetExceptionBreakpointsResponse response;
97 		return response;
98 	});
99 
100 	session->registerHandler(
101 	    [this](const dap::SetFunctionBreakpointsRequest &req) {
102 		    DAP_LOG("SetFunctionBreakpointsRequest receieved");
103 		    auto lock = ctx->lock();
104 		    dap::SetFunctionBreakpointsResponse response;
105 		    for(auto const &bp : req.breakpoints)
106 		    {
107 			    DAP_LOG("Setting breakpoint for function '%s'", bp.name.c_str());
108 			    lock.addFunctionBreakpoint(bp.name.c_str());
109 			    response.breakpoints.push_back({});
110 		    }
111 		    return response;
112 	    });
113 
114 	session->registerHandler(
115 	    [this](const dap::SetBreakpointsRequest &req)
116 	        -> dap::ResponseOrError<dap::SetBreakpointsResponse> {
117 		    DAP_LOG("SetBreakpointsRequest receieved");
118 		    bool verified = false;
119 
120 		    size_t numBreakpoints = 0;
121 		    if(req.breakpoints.has_value())
122 		    {
123 			    auto const &breakpoints = req.breakpoints.value();
124 			    numBreakpoints = breakpoints.size();
125 			    if(auto file = this->file(req.source))
126 			    {
127 				    file->clearBreakpoints();
128 				    for(auto const &bp : breakpoints)
129 				    {
130 					    file->addBreakpoint(bp.line);
131 				    }
132 				    verified = true;
133 			    }
134 			    else if(req.source.name.has_value())
135 			    {
136 				    std::vector<int> lines;
137 				    lines.reserve(breakpoints.size());
138 				    for(auto const &bp : breakpoints)
139 				    {
140 					    lines.push_back(bp.line);
141 				    }
142 				    ctx->lock().addPendingBreakpoints(req.source.name.value(),
143 				                                      lines);
144 			    }
145 		    }
146 
147 		    dap::SetBreakpointsResponse response;
148 		    for(size_t i = 0; i < numBreakpoints; i++)
149 		    {
150 			    dap::Breakpoint bp;
151 			    bp.verified = verified;
152 			    bp.source = req.source;
153 			    response.breakpoints.push_back(bp);
154 		    }
155 		    return response;
156 	    });
157 
158 	session->registerHandler([this](const dap::ThreadsRequest &req) {
159 		DAP_LOG("ThreadsRequest receieved");
160 		auto lock = ctx->lock();
161 		dap::ThreadsResponse response;
162 		for(auto thread : lock.threads())
163 		{
164 			std::string name = thread->name();
165 			if(clientIsVisualStudio)
166 			{
167 				// WORKAROUND: https://github.com/microsoft/VSDebugAdapterHost/issues/15
168 				for(size_t i = 0; i < name.size(); i++)
169 				{
170 					if(name[i] == '.')
171 					{
172 						name[i] = '_';
173 					}
174 				}
175 			}
176 
177 			dap::Thread out;
178 			out.id = thread->id.value();
179 			out.name = name;
180 			response.threads.push_back(out);
181 		};
182 		return response;
183 	});
184 
185 	session->registerHandler(
186 	    [this](const dap::StackTraceRequest &req)
187 	        -> dap::ResponseOrError<dap::StackTraceResponse> {
188 		    DAP_LOG("StackTraceRequest receieved");
189 
190 		    auto lock = ctx->lock();
191 		    auto thread = lock.get(Thread::ID(req.threadId));
192 		    if(!thread)
193 		    {
194 			    return dap::Error("Thread %d not found", req.threadId);
195 		    }
196 
197 		    auto stack = thread->stack();
198 
199 		    dap::StackTraceResponse response;
200 		    response.totalFrames = stack.size();
201 		    response.stackFrames.reserve(stack.size());
202 		    for(int i = static_cast<int>(stack.size()) - 1; i >= 0; i--)
203 		    {
204 			    auto const &frame = stack[i];
205 			    auto const &loc = frame.location;
206 			    dap::StackFrame sf;
207 			    sf.column = 0;
208 			    sf.id = frame.id.value();
209 			    sf.name = frame.function;
210 			    sf.line = loc.line;
211 			    if(loc.file)
212 			    {
213 				    sf.source = source(loc.file.get());
214 			    }
215 			    response.stackFrames.emplace_back(std::move(sf));
216 		    }
217 		    return response;
218 	    });
219 
220 	session->registerHandler([this](const dap::ScopesRequest &req)
221 	                             -> dap::ResponseOrError<dap::ScopesResponse> {
222 		DAP_LOG("ScopesRequest receieved");
223 
224 		auto lock = ctx->lock();
225 		auto frame = lock.get(Frame::ID(req.frameId));
226 		if(!frame)
227 		{
228 			return dap::Error("Frame %d not found", req.frameId);
229 		}
230 
231 		dap::ScopesResponse response;
232 		response.scopes = {
233 			scope("locals", frame->locals.get()),
234 			scope("arguments", frame->arguments.get()),
235 			scope("registers", frame->registers.get()),
236 		};
237 		return response;
238 	});
239 
240 	session->registerHandler([this](const dap::VariablesRequest &req)
241 	                             -> dap::ResponseOrError<dap::VariablesResponse> {
242 		DAP_LOG("VariablesRequest receieved");
243 
244 		auto lock = ctx->lock();
245 		auto vars = lock.get(VariableContainer::ID(req.variablesReference));
246 		if(!vars)
247 		{
248 			return dap::Error("VariablesReference %d not found",
249 			                  int(req.variablesReference));
250 		}
251 
252 		dap::VariablesResponse response;
253 		vars->foreach(req.start.value(0), req.count.value(~0), [&](const Variable &v) {
254 			dap::Variable out;
255 			out.evaluateName = v.name;
256 			out.name = v.name;
257 			out.type = v.value->type()->string();
258 			out.value = v.value->string();
259 			if(v.value->type()->kind == Kind::VariableContainer)
260 			{
261 				auto const vc = static_cast<const VariableContainer *>(v.value.get());
262 				out.variablesReference = vc->id.value();
263 			}
264 			response.variables.push_back(out);
265 		});
266 		return response;
267 	});
268 
269 	session->registerHandler([this](const dap::SourceRequest &req)
270 	                             -> dap::ResponseOrError<dap::SourceResponse> {
271 		DAP_LOG("SourceRequest receieved");
272 
273 		dap::SourceResponse response;
274 		uint64_t id = req.sourceReference;
275 
276 		auto lock = ctx->lock();
277 		auto file = lock.get(File::ID(id));
278 		if(!file)
279 		{
280 			return dap::Error("Source %d not found", id);
281 		}
282 		response.content = file->source;
283 		return response;
284 	});
285 
286 	session->registerHandler([this](const dap::PauseRequest &req)
287 	                             -> dap::ResponseOrError<dap::PauseResponse> {
288 		DAP_LOG("PauseRequest receieved");
289 
290 		dap::StoppedEvent event;
291 		event.reason = "pause";
292 
293 		auto lock = ctx->lock();
294 		if(auto thread = lock.get(Thread::ID(req.threadId)))
295 		{
296 			thread->pause();
297 			event.threadId = req.threadId;
298 		}
299 		else
300 		{
301 			auto threads = lock.threads();
302 			for(auto thread : threads)
303 			{
304 				thread->pause();
305 			}
306 			event.allThreadsStopped = true;
307 
308 			// Workaround for
309 			// https://github.com/microsoft/VSDebugAdapterHost/issues/11
310 			if(clientIsVisualStudio)
311 			{
312 				for(auto thread : threads)
313 				{
314 					event.threadId = thread->id.value();
315 					break;
316 				}
317 			}
318 		}
319 
320 		session->send(event);
321 
322 		dap::PauseResponse response;
323 		return response;
324 	});
325 
326 	session->registerHandler([this](const dap::ContinueRequest &req)
327 	                             -> dap::ResponseOrError<dap::ContinueResponse> {
328 		DAP_LOG("ContinueRequest receieved");
329 
330 		dap::ContinueResponse response;
331 
332 		auto lock = ctx->lock();
333 		if(auto thread = lock.get(Thread::ID(req.threadId)))
334 		{
335 			thread->resume();
336 			response.allThreadsContinued = false;
337 		}
338 		else
339 		{
340 			for(auto it : lock.threads())
341 			{
342 				thread->resume();
343 			}
344 			response.allThreadsContinued = true;
345 		}
346 
347 		return response;
348 	});
349 
350 	session->registerHandler([this](const dap::NextRequest &req)
351 	                             -> dap::ResponseOrError<dap::NextResponse> {
352 		DAP_LOG("NextRequest receieved");
353 
354 		auto lock = ctx->lock();
355 		auto thread = lock.get(Thread::ID(req.threadId));
356 		if(!thread)
357 		{
358 			return dap::Error("Unknown thread %d", int(req.threadId));
359 		}
360 
361 		thread->stepOver();
362 		return dap::NextResponse();
363 	});
364 
365 	session->registerHandler([this](const dap::StepInRequest &req)
366 	                             -> dap::ResponseOrError<dap::StepInResponse> {
367 		DAP_LOG("StepInRequest receieved");
368 
369 		auto lock = ctx->lock();
370 		auto thread = lock.get(Thread::ID(req.threadId));
371 		if(!thread)
372 		{
373 			return dap::Error("Unknown thread %d", int(req.threadId));
374 		}
375 
376 		thread->stepIn();
377 		return dap::StepInResponse();
378 	});
379 
380 	session->registerHandler([this](const dap::StepOutRequest &req)
381 	                             -> dap::ResponseOrError<dap::StepOutResponse> {
382 		DAP_LOG("StepOutRequest receieved");
383 
384 		auto lock = ctx->lock();
385 		auto thread = lock.get(Thread::ID(req.threadId));
386 		if(!thread)
387 		{
388 			return dap::Error("Unknown thread %d", int(req.threadId));
389 		}
390 
391 		thread->stepOut();
392 		return dap::StepOutResponse();
393 	});
394 
395 	session->registerHandler([this](const dap::EvaluateRequest &req)
396 	                             -> dap::ResponseOrError<dap::EvaluateResponse> {
397 		DAP_LOG("EvaluateRequest receieved");
398 
399 		auto lock = ctx->lock();
400 		if(req.frameId.has_value())
401 		{
402 			auto frame = lock.get(Frame::ID(req.frameId.value(0)));
403 			if(!frame)
404 			{
405 				return dap::Error("Unknown frame %d", int(req.frameId.value()));
406 			}
407 
408 			auto fmt = FormatFlags::Default;
409 			auto subfmt = FormatFlags::Default;
410 
411 			if(req.context.value("") == "hover")
412 			{
413 				subfmt.listPrefix = "\n";
414 				subfmt.listSuffix = "";
415 				subfmt.listDelimiter = "\n";
416 				subfmt.listIndent = "  ";
417 				fmt.listPrefix = "";
418 				fmt.listSuffix = "";
419 				fmt.listDelimiter = "\n";
420 				fmt.listIndent = "";
421 				fmt.subListFmt = &subfmt;
422 			}
423 
424 			dap::EvaluateResponse response;
425 			auto findHandler = [&](const Variable &var) {
426 				response.result = var.value->string(fmt);
427 				response.type = var.value->type()->string();
428 			};
429 
430 			std::vector<std::shared_ptr<vk::dbg::VariableContainer>> variables = {
431 				frame->locals->variables,
432 				frame->arguments->variables,
433 				frame->registers->variables,
434 				frame->hovers->variables,
435 			};
436 
437 			for(auto const &vars : variables)
438 			{
439 				if(vars->find(req.expression, findHandler)) { return response; }
440 			}
441 
442 			// HACK: VSCode does not appear to include the % in %123 tokens
443 			// TODO: This might be a configuration problem of the SPIRV-Tools
444 			// spirv-ls plugin. Investigate.
445 			auto withPercent = "%" + req.expression;
446 			for(auto const &vars : variables)
447 			{
448 				if(vars->find(withPercent, findHandler)) { return response; }
449 			}
450 		}
451 
452 		return dap::Error("Could not evaluate expression");
453 	});
454 
455 	session->registerHandler([](const dap::LaunchRequest &req) {
456 		DAP_LOG("LaunchRequest receieved");
457 		return dap::LaunchResponse();
458 	});
459 
460 	marl::WaitGroup configurationDone(1);
461 	session->registerHandler([=](const dap::ConfigurationDoneRequest &req) {
462 		DAP_LOG("ConfigurationDoneRequest receieved");
463 		configurationDone.done();
464 		return dap::ConfigurationDoneResponse();
465 	});
466 
467 	server->start(port, [&](const std::shared_ptr<dap::ReaderWriter> &rw) {
468 		session->bind(rw);
469 		ctx->addListener(this);
470 	});
471 
472 	static bool waitForDebugger = getenv("VK_WAIT_FOR_DEBUGGER") != nullptr;
473 	if(waitForDebugger)
474 	{
475 		printf("Waiting for debugger connection...\n");
476 		configurationDone.wait();
477 		printf("Debugger connection established\n");
478 	}
479 }
480 
~Impl()481 Server::Impl::~Impl()
482 {
483 	ctx->removeListener(this);
484 	server->stop();
485 }
486 
onThreadStarted(ID<Thread> id)487 void Server::Impl::onThreadStarted(ID<Thread> id)
488 {
489 	dap::ThreadEvent event;
490 	event.reason = "started";
491 	event.threadId = id.value();
492 	session->send(event);
493 }
494 
onThreadStepped(ID<Thread> id)495 void Server::Impl::onThreadStepped(ID<Thread> id)
496 {
497 	dap::StoppedEvent event;
498 	event.threadId = id.value();
499 	event.reason = "step";
500 	session->send(event);
501 }
502 
onLineBreakpointHit(ID<Thread> id)503 void Server::Impl::onLineBreakpointHit(ID<Thread> id)
504 {
505 	dap::StoppedEvent event;
506 	event.threadId = id.value();
507 	event.reason = "breakpoint";
508 	session->send(event);
509 }
510 
onFunctionBreakpointHit(ID<Thread> id)511 void Server::Impl::onFunctionBreakpointHit(ID<Thread> id)
512 {
513 	dap::StoppedEvent event;
514 	event.threadId = id.value();
515 	event.reason = "function breakpoint";
516 	session->send(event);
517 }
518 
scope(const char * type,Scope * s)519 dap::Scope Server::Impl::scope(const char *type, Scope *s)
520 {
521 	dap::Scope out;
522 	// out.line = s->startLine;
523 	// out.endLine = s->endLine;
524 	out.source = source(s->file.get());
525 	out.name = type;
526 	out.presentationHint = type;
527 	out.variablesReference = s->variables->id.value();
528 	return out;
529 }
530 
source(File * file)531 dap::Source Server::Impl::source(File *file)
532 {
533 	dap::Source out;
534 	out.name = file->name;
535 	if(file->isVirtual())
536 	{
537 		out.sourceReference = file->id.value();
538 	}
539 	else
540 	{
541 		out.path = file->path();
542 	}
543 	return out;
544 }
545 
file(const dap::Source & source)546 std::shared_ptr<File> Server::Impl::file(const dap::Source &source)
547 {
548 	auto lock = ctx->lock();
549 	if(source.sourceReference.has_value())
550 	{
551 		auto id = source.sourceReference.value();
552 		if(auto file = lock.get(File::ID(id)))
553 		{
554 			return file;
555 		}
556 	}
557 
558 	auto files = lock.files();
559 	if(source.path.has_value())
560 	{
561 		auto path = source.path.value();
562 		std::shared_ptr<File> out;
563 		for(auto file : files)
564 		{
565 			if(file->path() == path)
566 			{
567 				out = file;
568 				break;
569 			}
570 		}
571 		return out;
572 	}
573 
574 	if(source.name.has_value())
575 	{
576 		auto name = source.name.value();
577 		std::shared_ptr<File> out;
578 		for(auto file : files)
579 		{
580 			if(file->name == name)
581 			{
582 				out = file;
583 				break;
584 			}
585 		}
586 		return out;
587 	}
588 
589 	return nullptr;
590 }
591 
create(const std::shared_ptr<Context> & ctx,int port)592 std::shared_ptr<Server> Server::create(const std::shared_ptr<Context> &ctx, int port)
593 {
594 	return std::make_shared<Server::Impl>(ctx, port);
595 }
596 
597 }  // namespace dbg
598 }  // namespace vk