xref: /aosp_15_r20/external/swiftshader/src/Vulkan/Debug/Server.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
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 ServerEventListener
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(Context::Lock &lock, 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 
104 		    dap::SetFunctionBreakpointsResponse response;
105 		    for(const auto &reqBP : req.breakpoints)
106 		    {
107 			    DAP_LOG("Setting breakpoint for function '%s'", reqBP.name.c_str());
108 
109 			    bool verified = false;
110 			    ctx->clientEventBroadcast()->onSetBreakpoint(reqBP.name, verified);
111 
112 			    dap::Breakpoint resBP{};
113 			    resBP.verified = verified;
114 			    response.breakpoints.emplace_back(std::move(resBP));
115 		    }
116 		    {
117 			    auto lock = ctx->lock();
118 			    lock.clearFunctionBreakpoints();
119 			    for(const auto &reqBP : req.breakpoints)
120 			    {
121 				    lock.addFunctionBreakpoint(reqBP.name.c_str());
122 			    }
123 		    }
124 		    ctx->clientEventBroadcast()->onBreakpointsChanged();
125 		    return response;
126 	    });
127 
128 	session->registerHandler(
129 	    [this](const dap::SetBreakpointsRequest &req)
130 	        -> dap::ResponseOrError<dap::SetBreakpointsResponse> {
131 		    DAP_LOG("SetBreakpointsRequest receieved");
132 
133 		    size_t numBreakpoints = 0;
134 		    if(req.breakpoints.has_value())
135 		    {
136 			    const auto &breakpoints = req.breakpoints.value();
137 			    numBreakpoints = breakpoints.size();
138 			    if(auto file = this->file(req.source))
139 			    {
140 				    dap::SetBreakpointsResponse response;
141 				    file->clearBreakpoints();
142 				    for(size_t i = 0; i < numBreakpoints; i++)
143 				    {
144 					    auto &reqBP = breakpoints[i];
145 					    Location location{ file, static_cast<int>(reqBP.line) };
146 					    file->addBreakpoint(location.line);
147 
148 					    bool verified = false;
149 					    ctx->clientEventBroadcast()->onSetBreakpoint(location, verified);
150 
151 					    dap::Breakpoint respBP;
152 					    respBP.verified = verified;
153 					    respBP.source = req.source;
154 					    response.breakpoints.push_back(respBP);
155 				    }
156 				    ctx->clientEventBroadcast()->onBreakpointsChanged();
157 				    return response;
158 			    }
159 
160 			    if(req.source.name.has_value())
161 			    {
162 				    std::vector<int> lines;
163 				    lines.reserve(breakpoints.size());
164 				    for(const auto &bp : breakpoints)
165 				    {
166 					    lines.push_back(bp.line);
167 				    }
168 				    ctx->lock().addPendingBreakpoints(req.source.name.value(),
169 				                                      lines);
170 			    }
171 		    }
172 
173 		    // Generic response.
174 		    dap::SetBreakpointsResponse response;
175 		    for(size_t i = 0; i < numBreakpoints; i++)
176 		    {
177 			    dap::Breakpoint bp;
178 			    bp.verified = false;
179 			    bp.source = req.source;
180 			    response.breakpoints.push_back(bp);
181 		    }
182 		    ctx->clientEventBroadcast()->onBreakpointsChanged();
183 		    return response;
184 	    });
185 
186 	session->registerHandler([this](const dap::ThreadsRequest &req) {
187 		DAP_LOG("ThreadsRequest receieved");
188 		auto lock = ctx->lock();
189 		dap::ThreadsResponse response;
190 		for(auto thread : lock.threads())
191 		{
192 			std::string name = thread->name();
193 			if(clientIsVisualStudio)
194 			{
195 				// WORKAROUND: https://github.com/microsoft/VSDebugAdapterHost/issues/15
196 				for(size_t i = 0; i < name.size(); i++)
197 				{
198 					if(name[i] == '.')
199 					{
200 						name[i] = '_';
201 					}
202 				}
203 			}
204 
205 			dap::Thread out;
206 			out.id = thread->id.value();
207 			out.name = name;
208 			response.threads.push_back(out);
209 		};
210 		return response;
211 	});
212 
213 	session->registerHandler(
214 	    [this](const dap::StackTraceRequest &req)
215 	        -> dap::ResponseOrError<dap::StackTraceResponse> {
216 		    DAP_LOG("StackTraceRequest receieved");
217 
218 		    auto lock = ctx->lock();
219 		    auto thread = lock.get(Thread::ID(req.threadId));
220 		    if(!thread)
221 		    {
222 			    return dap::Error("Thread %d not found", req.threadId);
223 		    }
224 
225 		    auto stack = thread->stack();
226 
227 		    dap::StackTraceResponse response;
228 		    response.totalFrames = stack.size();
229 		    response.stackFrames.reserve(stack.size());
230 		    for(int i = static_cast<int>(stack.size()) - 1; i >= 0; i--)
231 		    {
232 			    const auto &frame = stack[i];
233 			    const auto &loc = frame.location;
234 			    dap::StackFrame sf;
235 			    sf.column = 0;
236 			    sf.id = frame.id.value();
237 			    sf.name = frame.function;
238 			    sf.line = loc.line;
239 			    if(loc.file)
240 			    {
241 				    sf.source = source(loc.file.get());
242 			    }
243 			    response.stackFrames.emplace_back(std::move(sf));
244 		    }
245 		    return response;
246 	    });
247 
248 	session->registerHandler([this](const dap::ScopesRequest &req)
249 	                             -> dap::ResponseOrError<dap::ScopesResponse> {
250 		DAP_LOG("ScopesRequest receieved");
251 
252 		auto lock = ctx->lock();
253 		auto frame = lock.get(Frame::ID(req.frameId));
254 		if(!frame)
255 		{
256 			return dap::Error("Frame %d not found", req.frameId);
257 		}
258 
259 		dap::ScopesResponse response;
260 		response.scopes = {
261 			scope(lock, "locals", frame->locals.get()),
262 			scope(lock, "arguments", frame->arguments.get()),
263 			scope(lock, "registers", frame->registers.get()),
264 		};
265 		return response;
266 	});
267 
268 	session->registerHandler([this](const dap::VariablesRequest &req)
269 	                             -> dap::ResponseOrError<dap::VariablesResponse> {
270 		DAP_LOG("VariablesRequest receieved");
271 
272 		auto lock = ctx->lock();
273 		auto vars = lock.get(Variables::ID(req.variablesReference));
274 		if(!vars)
275 		{
276 			return dap::Error("VariablesReference %d not found",
277 			                  int(req.variablesReference));
278 		}
279 
280 		dap::VariablesResponse response;
281 		vars->foreach(req.start.value(0), req.count.value(~0), [&](const Variable &v) {
282 			dap::Variable out;
283 			out.evaluateName = v.name;
284 			out.name = v.name;
285 			out.type = v.value->type();
286 			out.value = v.value->get();
287 			if(auto children = v.value->children())
288 			{
289 				out.variablesReference = children->id.value();
290 				lock.track(children);
291 			}
292 			response.variables.push_back(out);
293 			return true;
294 		});
295 		return response;
296 	});
297 
298 	session->registerHandler([this](const dap::SourceRequest &req)
299 	                             -> dap::ResponseOrError<dap::SourceResponse> {
300 		DAP_LOG("SourceRequest receieved");
301 
302 		dap::SourceResponse response;
303 		uint64_t id = req.sourceReference;
304 
305 		auto lock = ctx->lock();
306 		auto file = lock.get(File::ID(id));
307 		if(!file)
308 		{
309 			return dap::Error("Source %d not found", id);
310 		}
311 		response.content = file->source;
312 		return response;
313 	});
314 
315 	session->registerHandler([this](const dap::PauseRequest &req)
316 	                             -> dap::ResponseOrError<dap::PauseResponse> {
317 		DAP_LOG("PauseRequest receieved");
318 
319 		dap::StoppedEvent event;
320 		event.reason = "pause";
321 
322 		auto lock = ctx->lock();
323 		if(auto thread = lock.get(Thread::ID(req.threadId)))
324 		{
325 			thread->pause();
326 			event.threadId = req.threadId;
327 		}
328 		else
329 		{
330 			auto threads = lock.threads();
331 			for(auto thread : threads)
332 			{
333 				thread->pause();
334 			}
335 			event.allThreadsStopped = true;
336 
337 			// Workaround for
338 			// https://github.com/microsoft/VSDebugAdapterHost/issues/11
339 			if(clientIsVisualStudio && !threads.empty())
340 			{
341 				event.threadId = threads.front()->id.value();
342 			}
343 		}
344 
345 		session->send(event);
346 
347 		dap::PauseResponse response;
348 		return response;
349 	});
350 
351 	session->registerHandler([this](const dap::ContinueRequest &req)
352 	                             -> dap::ResponseOrError<dap::ContinueResponse> {
353 		DAP_LOG("ContinueRequest receieved");
354 
355 		dap::ContinueResponse response;
356 
357 		auto lock = ctx->lock();
358 		if(auto thread = lock.get(Thread::ID(req.threadId)))
359 		{
360 			thread->resume();
361 			response.allThreadsContinued = false;
362 		}
363 		else
364 		{
365 			for(auto it : lock.threads())
366 			{
367 				thread->resume();
368 			}
369 			response.allThreadsContinued = true;
370 		}
371 
372 		return response;
373 	});
374 
375 	session->registerHandler([this](const dap::NextRequest &req)
376 	                             -> dap::ResponseOrError<dap::NextResponse> {
377 		DAP_LOG("NextRequest receieved");
378 
379 		auto lock = ctx->lock();
380 		auto thread = lock.get(Thread::ID(req.threadId));
381 		if(!thread)
382 		{
383 			return dap::Error("Unknown thread %d", int(req.threadId));
384 		}
385 
386 		thread->stepOver();
387 		return dap::NextResponse();
388 	});
389 
390 	session->registerHandler([this](const dap::StepInRequest &req)
391 	                             -> dap::ResponseOrError<dap::StepInResponse> {
392 		DAP_LOG("StepInRequest receieved");
393 
394 		auto lock = ctx->lock();
395 		auto thread = lock.get(Thread::ID(req.threadId));
396 		if(!thread)
397 		{
398 			return dap::Error("Unknown thread %d", int(req.threadId));
399 		}
400 
401 		thread->stepIn();
402 		return dap::StepInResponse();
403 	});
404 
405 	session->registerHandler([this](const dap::StepOutRequest &req)
406 	                             -> dap::ResponseOrError<dap::StepOutResponse> {
407 		DAP_LOG("StepOutRequest receieved");
408 
409 		auto lock = ctx->lock();
410 		auto thread = lock.get(Thread::ID(req.threadId));
411 		if(!thread)
412 		{
413 			return dap::Error("Unknown thread %d", int(req.threadId));
414 		}
415 
416 		thread->stepOut();
417 		return dap::StepOutResponse();
418 	});
419 
420 	session->registerHandler([this](const dap::EvaluateRequest &req)
421 	                             -> dap::ResponseOrError<dap::EvaluateResponse> {
422 		DAP_LOG("EvaluateRequest receieved");
423 
424 		auto lock = ctx->lock();
425 		if(req.frameId.has_value())
426 		{
427 			auto frame = lock.get(Frame::ID(req.frameId.value(0)));
428 			if(!frame)
429 			{
430 				return dap::Error("Unknown frame %d", int(req.frameId.value()));
431 			}
432 
433 			auto fmt = FormatFlags::Default;
434 			auto subfmt = FormatFlags::Default;
435 
436 			if(req.context.value("") == "hover")
437 			{
438 				subfmt.listPrefix = "\n";
439 				subfmt.listSuffix = "";
440 				subfmt.listDelimiter = "\n";
441 				subfmt.listIndent = "  ";
442 				fmt.listPrefix = "";
443 				fmt.listSuffix = "";
444 				fmt.listDelimiter = "\n";
445 				fmt.listIndent = "";
446 				fmt.subListFmt = &subfmt;
447 			}
448 
449 			dap::EvaluateResponse response;
450 
451 			std::vector<std::shared_ptr<vk::dbg::Variables>> variables = {
452 				frame->locals->variables,
453 				frame->arguments->variables,
454 				frame->registers->variables,
455 				frame->hovers->variables,
456 			};
457 
458 			for(const auto &vars : variables)
459 			{
460 				if(auto val = vars->get(req.expression))
461 				{
462 					response.result = val->get(fmt);
463 					response.type = val->type();
464 					return response;
465 				}
466 			}
467 
468 			// HACK: VSCode does not appear to include the % in %123 tokens
469 			// TODO: This might be a configuration problem of the SPIRV-Tools
470 			// spirv-ls plugin. Investigate.
471 			auto withPercent = "%" + req.expression;
472 			for(const auto &vars : variables)
473 			{
474 				if(auto val = vars->get(withPercent))
475 				{
476 					response.result = val->get(fmt);
477 					response.type = val->type();
478 					return response;
479 				}
480 			}
481 		}
482 
483 		return dap::Error("Could not evaluate expression");
484 	});
485 
486 	session->registerHandler([](const dap::LaunchRequest &req) {
487 		DAP_LOG("LaunchRequest receieved");
488 		return dap::LaunchResponse();
489 	});
490 
491 	marl::WaitGroup configurationDone(1);
492 	session->registerHandler([=](const dap::ConfigurationDoneRequest &req) {
493 		DAP_LOG("ConfigurationDoneRequest receieved");
494 		configurationDone.done();
495 		return dap::ConfigurationDoneResponse();
496 	});
497 
498 	server->start(port, [&](const std::shared_ptr<dap::ReaderWriter> &rw) {
499 		session->bind(rw);
500 		ctx->addListener(this);
501 	});
502 
503 	static bool waitForDebugger = getenv("VK_WAIT_FOR_DEBUGGER") != nullptr;
504 	if(waitForDebugger)
505 	{
506 		printf("Waiting for debugger connection...\n");
507 		configurationDone.wait();
508 		printf("Debugger connection established\n");
509 	}
510 }
511 
~Impl()512 Server::Impl::~Impl()
513 {
514 	ctx->removeListener(this);
515 	server->stop();
516 }
517 
onThreadStarted(ID<Thread> id)518 void Server::Impl::onThreadStarted(ID<Thread> id)
519 {
520 	dap::ThreadEvent event;
521 	event.reason = "started";
522 	event.threadId = id.value();
523 	session->send(event);
524 }
525 
onThreadStepped(ID<Thread> id)526 void Server::Impl::onThreadStepped(ID<Thread> id)
527 {
528 	dap::StoppedEvent event;
529 	event.threadId = id.value();
530 	event.reason = "step";
531 	session->send(event);
532 }
533 
onLineBreakpointHit(ID<Thread> id)534 void Server::Impl::onLineBreakpointHit(ID<Thread> id)
535 {
536 	dap::StoppedEvent event;
537 	event.threadId = id.value();
538 	event.reason = "breakpoint";
539 	session->send(event);
540 }
541 
onFunctionBreakpointHit(ID<Thread> id)542 void Server::Impl::onFunctionBreakpointHit(ID<Thread> id)
543 {
544 	dap::StoppedEvent event;
545 	event.threadId = id.value();
546 	event.reason = "function breakpoint";
547 	session->send(event);
548 }
549 
scope(Context::Lock & lock,const char * type,Scope * s)550 dap::Scope Server::Impl::scope(Context::Lock &lock, const char *type, Scope *s)
551 {
552 	dap::Scope out;
553 	// out.line = s->startLine;
554 	// out.endLine = s->endLine;
555 	out.source = source(s->file.get());
556 	out.name = type;
557 	out.presentationHint = type;
558 	out.variablesReference = s->variables->id.value();
559 	lock.track(s->variables);
560 	return out;
561 }
562 
source(File * file)563 dap::Source Server::Impl::source(File *file)
564 {
565 	dap::Source out;
566 	out.name = file->name;
567 	if(file->isVirtual())
568 	{
569 		out.sourceReference = file->id.value();
570 	}
571 	out.path = file->path();
572 	return out;
573 }
574 
file(const dap::Source & source)575 std::shared_ptr<File> Server::Impl::file(const dap::Source &source)
576 {
577 	auto lock = ctx->lock();
578 	if(source.sourceReference.has_value())
579 	{
580 		auto id = source.sourceReference.value();
581 		if(auto file = lock.get(File::ID(id)))
582 		{
583 			return file;
584 		}
585 	}
586 
587 	auto files = lock.files();
588 	if(source.path.has_value())
589 	{
590 		auto path = source.path.value();
591 		std::shared_ptr<File> out;
592 		for(auto file : files)
593 		{
594 			if(file->path() == path)
595 			{
596 				out = file;
597 				break;
598 			}
599 		}
600 		return out;
601 	}
602 
603 	if(source.name.has_value())
604 	{
605 		auto name = source.name.value();
606 		std::shared_ptr<File> out;
607 		for(auto file : files)
608 		{
609 			if(file->name == name)
610 			{
611 				out = file;
612 				break;
613 			}
614 		}
615 		return out;
616 	}
617 
618 	return nullptr;
619 }
620 
create(const std::shared_ptr<Context> & ctx,int port)621 std::shared_ptr<Server> Server::create(const std::shared_ptr<Context> &ctx, int port)
622 {
623 	return std::make_shared<Server::Impl>(ctx, port);
624 }
625 
626 }  // namespace dbg
627 }  // namespace vk
628