• 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 "LLVMReactorDebugInfo.hpp"
16 
17 #ifdef ENABLE_RR_DEBUG_INFO
18 
19 #	include "LLVMReactor.hpp"
20 #	include "Reactor.hpp"
21 #	include "Print.hpp"
22 
23 #	include "boost/stacktrace.hpp"
24 
25 // TODO(b/143539525): Eliminate when warning has been fixed.
26 #	ifdef _MSC_VER
27 __pragma(warning(push))
28     __pragma(warning(disable : 4146))  // unary minus operator applied to unsigned type, result still unsigned
29 #	endif
30 
31 #	include "llvm/Demangle/Demangle.h"
32 #	include "llvm/ExecutionEngine/JITEventListener.h"
33 #	include "llvm/IR/DIBuilder.h"
34 #	include "llvm/IR/IRBuilder.h"
35 #	include "llvm/IR/Intrinsics.h"
36 
37 #	ifdef _MSC_VER
38     __pragma(warning(pop))
39 #	endif
40 
41 #	include <cctype>
42 #	include <fstream>
43 #	include <mutex>
44 #	include <regex>
45 #	include <sstream>
46 #	include <string>
47 
48 #	if 0
49 #		define LOG(msg, ...) printf(msg "\n", ##__VA_ARGS__)
50 #	else
51 #		define LOG(msg, ...)
52 #	endif
53 
54         namespace
55 {
56 
splitPath(const char * path)57 	std::pair<llvm::StringRef, llvm::StringRef> splitPath(const char *path)
58 	{
59 		return llvm::StringRef(path).rsplit('/');
60 	}
61 
62 	// Note: createGDBRegistrationListener() returns a pointer to a singleton.
63 	// Nothing is actually created.
64 	auto jitEventListener = llvm::JITEventListener::createGDBRegistrationListener();  // guarded by jitEventListenerMutex
65 	std::mutex jitEventListenerMutex;
66 
67 }  // namespace
68 
69 namespace rr {
70 
DebugInfo(llvm::IRBuilder<> * builder,llvm::LLVMContext * context,llvm::Module * module,llvm::Function * function)71 DebugInfo::DebugInfo(
72     llvm::IRBuilder<> *builder,
73     llvm::LLVMContext *context,
74     llvm::Module *module,
75     llvm::Function *function)
76     : builder(builder)
77     , context(context)
78     , module(module)
79     , function(function)
80 {
81 	using namespace ::llvm;
82 
83 	auto location = getCallerLocation();
84 
85 	auto fileAndDir = splitPath(location.function.file.c_str());
86 	diBuilder.reset(new llvm::DIBuilder(*module));
87 	diCU = diBuilder->createCompileUnit(
88 	    llvm::dwarf::DW_LANG_C,
89 	    diBuilder->createFile(fileAndDir.first, fileAndDir.second),
90 	    "Reactor",
91 	    0, "", 0);
92 
93 	registerBasicTypes();
94 
95 	SmallVector<Metadata *, 8> EltTys;
96 	auto funcTy = diBuilder->createSubroutineType(diBuilder->getOrCreateTypeArray(EltTys));
97 
98 	auto file = getOrCreateFile(location.function.file.c_str());
99 	auto sp = diBuilder->createFunction(
100 	    file,                    // scope
101 	    "ReactorFunction",       // function name
102 	    "ReactorFunction",       // linkage
103 	    file,                    // file
104 	    location.line,           // line
105 	    funcTy,                  // type
106 	    false,                   // internal linkage
107 	    true,                    // definition
108 	    location.line,           // scope line
109 	    DINode::FlagPrototyped,  // flags
110 	    false                    // is optimized
111 	);
112 	diSubprogram = sp;
113 	function->setSubprogram(sp);
114 	diRootLocation = DILocation::get(*context, location.line, 0, sp);
115 	builder->SetCurrentDebugLocation(diRootLocation);
116 }
117 
118 DebugInfo::~DebugInfo() = default;
119 
Finalize()120 void DebugInfo::Finalize()
121 {
122 	while(diScope.size() > 0)
123 	{
124 		emitPending(diScope.back(), builder);
125 		diScope.pop_back();
126 	}
127 	diBuilder->finalize();
128 }
129 
EmitLocation()130 void DebugInfo::EmitLocation()
131 {
132 	auto const &backtrace = getCallerBacktrace();
133 	syncScope(backtrace);
134 	builder->SetCurrentDebugLocation(getLocation(backtrace, backtrace.size() - 1));
135 
136 #	ifdef ENABLE_RR_EMIT_PRINT_LOCATION
137 	emitPrintLocation(backtrace);
138 #	endif  // ENABLE_RR_EMIT_PRINT_LOCATION
139 }
140 
Flush()141 void DebugInfo::Flush()
142 {
143 	emitPending(diScope.back(), builder);
144 }
145 
syncScope(Backtrace const & backtrace)146 void DebugInfo::syncScope(Backtrace const &backtrace)
147 {
148 	auto shrink = [this](size_t newsize) {
149 		while(diScope.size() > newsize)
150 		{
151 			auto &scope = diScope.back();
152 			LOG("- STACK(%d): di: %p, location: %s:%d",
153 			    int(diScope.size() - 1), scope.di,
154 			    scope.location.function.file.c_str(),
155 			    int(scope.location.line));
156 			emitPending(scope, builder);
157 			diScope.pop_back();
158 		}
159 	};
160 
161 	if(backtrace.size() < diScope.size())
162 	{
163 		shrink(backtrace.size());
164 	}
165 
166 	for(size_t i = 0; i < diScope.size(); i++)
167 	{
168 		auto &scope = diScope[i];
169 		auto const &oldLocation = scope.location;
170 		auto const &newLocation = backtrace[i];
171 
172 		if(oldLocation.function != newLocation.function)
173 		{
174 			LOG("  STACK(%d): Changed function %s -> %s", int(i),
175 			    oldLocation.function.name.c_str(), newLocation.function.name.c_str());
176 			shrink(i);
177 			break;
178 		}
179 
180 		if(oldLocation.line > newLocation.line)
181 		{
182 			// Create a new di block to shadow all the variables in the loop.
183 			auto file = getOrCreateFile(newLocation.function.file.c_str());
184 			auto di = diBuilder->createLexicalBlock(scope.di, file, newLocation.line, 0);
185 			LOG("  STACK(%d): Jumped backwards %d -> %d. di: %p -> %p", int(i),
186 			    oldLocation.line, newLocation.line, scope.di, di);
187 			emitPending(scope, builder);
188 			scope = { newLocation, di };
189 			shrink(i + 1);
190 			break;
191 		}
192 
193 		scope.location = newLocation;
194 	}
195 
196 	while(backtrace.size() > diScope.size())
197 	{
198 		auto i = diScope.size();
199 		auto location = backtrace[i];
200 		auto file = getOrCreateFile(location.function.file.c_str());
201 		auto funcTy = diBuilder->createSubroutineType(diBuilder->getOrCreateTypeArray({}));
202 
203 		char buf[1024];
204 		size_t size = sizeof(buf);
205 		int status = 0;
206 		llvm::itaniumDemangle(location.function.name.c_str(), buf, &size, &status);
207 		auto name = "jit!" + (status == 0 ? std::string(buf) : location.function.name);
208 
209 		auto func = diBuilder->createFunction(
210 		    file,                          // scope
211 		    name,                          // function name
212 		    "",                            // linkage
213 		    file,                          // file
214 		    location.line,                 // line
215 		    funcTy,                        // type
216 		    false,                         // internal linkage
217 		    true,                          // definition
218 		    location.line,                 // scope line
219 		    llvm::DINode::FlagPrototyped,  // flags
220 		    false                          // is optimized
221 		);
222 		diScope.push_back({ location, func });
223 		LOG("+ STACK(%d): di: %p, location: %s:%d", int(i), di,
224 		    location.function.file.c_str(), int(location.line));
225 	}
226 }
227 
getLocation(const Backtrace & backtrace,size_t i)228 llvm::DILocation *DebugInfo::getLocation(const Backtrace &backtrace, size_t i)
229 {
230 	if(backtrace.size() == 0) { return nullptr; }
231 	assert(backtrace.size() == diScope.size());
232 	return llvm::DILocation::get(
233 	    *context,
234 	    backtrace[i].line,
235 	    0,
236 	    diScope[i].di,
237 	    i > 0 ? getLocation(backtrace, i - 1) : diRootLocation);
238 }
239 
EmitVariable(Value * variable)240 void DebugInfo::EmitVariable(Value *variable)
241 {
242 	auto const &backtrace = getCallerBacktrace();
243 	syncScope(backtrace);
244 
245 	for(int i = backtrace.size() - 1; i >= 0; i--)
246 	{
247 		auto const &location = backtrace[i];
248 		auto tokens = getOrParseFileTokens(location.function.file.c_str());
249 		auto tokIt = tokens->find(location.line);
250 		if(tokIt == tokens->end())
251 		{
252 			break;
253 		}
254 		auto token = tokIt->second;
255 		auto name = token.identifier;
256 		if(token.kind == Token::Return)
257 		{
258 			// This is a:
259 			//
260 			//   return <expr>;
261 			//
262 			// Emit this expression as two variables -
263 			// Once as a synthetic 'return_value' variable at this scope.
264 			// Again by bubbling the expression value up the callstack as
265 			// Return Value Optimizations (RVOs) are likely to carry across
266 			// the value to a local without calling a constructor in
267 			// statements like:
268 			//
269 			//   auto val = foo();
270 			//
271 			name = "return_value";
272 		}
273 
274 		auto &scope = diScope[i];
275 		if(scope.pending.location != location)
276 		{
277 			emitPending(scope, builder);
278 		}
279 
280 		auto value = V(variable);
281 		auto block = builder->GetInsertBlock();
282 
283 		auto insertAfter = block->size() > 0 ? &block->back() : nullptr;
284 		while(insertAfter != nullptr && insertAfter->isTerminator())
285 		{
286 			insertAfter = insertAfter->getPrevNode();
287 		}
288 
289 		scope.pending = Pending{};
290 		scope.pending.name = name;
291 		scope.pending.location = location;
292 		scope.pending.diLocation = getLocation(backtrace, i);
293 		scope.pending.value = value;
294 		scope.pending.block = block;
295 		scope.pending.insertAfter = insertAfter;
296 		scope.pending.scope = scope.di;
297 
298 		if(token.kind == Token::Return)
299 		{
300 			// Insert a noop instruction so the debugger can inspect the
301 			// return value before the function scope closes.
302 			scope.pending.addNopOnNextLine = true;
303 		}
304 		else
305 		{
306 			break;
307 		}
308 	}
309 }
310 
emitPending(Scope & scope,IRBuilder * builder)311 void DebugInfo::emitPending(Scope &scope, IRBuilder *builder)
312 {
313 	auto const &pending = scope.pending;
314 	if(pending.value == nullptr)
315 	{
316 		return;
317 	}
318 
319 	if(!scope.symbols.emplace(pending.name).second)
320 	{
321 		return;
322 	}
323 
324 	bool isAlloca = llvm::isa<llvm::AllocaInst>(pending.value);
325 
326 	LOG("  EMIT(%s): di: %p, location: %s:%d, isAlloca: %s", pending.name.c_str(), scope.di,
327 	    pending.location.function.file.c_str(), pending.location.line, isAlloca ? "true" : "false");
328 
329 	auto value = pending.value;
330 
331 	IRBuilder::InsertPointGuard guard(*builder);
332 	if(pending.insertAfter != nullptr)
333 	{
334 		builder->SetInsertPoint(pending.block, ++pending.insertAfter->getIterator());
335 	}
336 	else
337 	{
338 		builder->SetInsertPoint(pending.block);
339 	}
340 	builder->SetCurrentDebugLocation(pending.diLocation);
341 
342 	if(!isAlloca)
343 	{
344 		// While insertDbgValueIntrinsic should be enough to declare a
345 		// variable with no storage, variables of RValues can share the same
346 		// llvm::Value, and only one can be named. Take for example:
347 		//
348 		//   Int a = 42;
349 		//   RValue<Int> b = a;
350 		//   RValue<Int> c = b;
351 		//
352 		// To handle this, always promote named RValues to an alloca.
353 
354 		llvm::BasicBlock &entryBlock = function->getEntryBlock();
355 		auto alloca = new llvm::AllocaInst(value->getType(), 0, pending.name);
356 		entryBlock.getInstList().push_front(alloca);
357 		builder->CreateStore(value, alloca);
358 		value = alloca;
359 	}
360 
361 	value->setName(pending.name);
362 
363 	auto diFile = getOrCreateFile(pending.location.function.file.c_str());
364 	auto diType = getOrCreateType(value->getType()->getPointerElementType());
365 	auto diVar = diBuilder->createAutoVariable(scope.di, pending.name, diFile, pending.location.line, diType);
366 
367 	auto di = diBuilder->insertDeclare(value, diVar, diBuilder->createExpression(), pending.diLocation, pending.block);
368 	if(pending.insertAfter != nullptr) { di->moveAfter(pending.insertAfter); }
369 
370 	if(pending.addNopOnNextLine)
371 	{
372 		builder->SetCurrentDebugLocation(llvm::DILocation::get(
373 		    *context,
374 		    pending.diLocation->getLine() + 1,
375 		    0,
376 		    pending.diLocation->getScope(),
377 		    pending.diLocation->getInlinedAt()));
378 		Nop();
379 	}
380 
381 	scope.pending = Pending{};
382 }
383 
NotifyObjectEmitted(const llvm::object::ObjectFile & Obj,const llvm::LoadedObjectInfo & L)384 void DebugInfo::NotifyObjectEmitted(const llvm::object::ObjectFile &Obj, const llvm::LoadedObjectInfo &L)
385 {
386 	std::unique_lock<std::mutex> lock(jitEventListenerMutex);
387 	jitEventListener->NotifyObjectEmitted(Obj, static_cast<const llvm::RuntimeDyld::LoadedObjectInfo &>(L));
388 }
389 
NotifyFreeingObject(const llvm::object::ObjectFile & Obj)390 void DebugInfo::NotifyFreeingObject(const llvm::object::ObjectFile &Obj)
391 {
392 	std::unique_lock<std::mutex> lock(jitEventListenerMutex);
393 	jitEventListener->NotifyFreeingObject(Obj);
394 }
395 
registerBasicTypes()396 void DebugInfo::registerBasicTypes()
397 {
398 	using namespace rr;
399 	using namespace llvm;
400 
401 	auto vec4 = diBuilder->getOrCreateArray(diBuilder->getOrCreateSubrange(0, 4));
402 	auto vec8 = diBuilder->getOrCreateArray(diBuilder->getOrCreateSubrange(0, 8));
403 	auto vec16 = diBuilder->getOrCreateArray(diBuilder->getOrCreateSubrange(0, 16));
404 
405 	diTypes.emplace(T(Bool::getType()), diBuilder->createBasicType("Bool", sizeof(bool), dwarf::DW_ATE_boolean));
406 	diTypes.emplace(T(Byte::getType()), diBuilder->createBasicType("Byte", 8, dwarf::DW_ATE_unsigned_char));
407 	diTypes.emplace(T(SByte::getType()), diBuilder->createBasicType("SByte", 8, dwarf::DW_ATE_signed_char));
408 	diTypes.emplace(T(Short::getType()), diBuilder->createBasicType("Short", 16, dwarf::DW_ATE_signed));
409 	diTypes.emplace(T(UShort::getType()), diBuilder->createBasicType("UShort", 16, dwarf::DW_ATE_unsigned));
410 	diTypes.emplace(T(Int::getType()), diBuilder->createBasicType("Int", 32, dwarf::DW_ATE_signed));
411 	diTypes.emplace(T(UInt::getType()), diBuilder->createBasicType("UInt", 32, dwarf::DW_ATE_unsigned));
412 	diTypes.emplace(T(Long::getType()), diBuilder->createBasicType("Long", 64, dwarf::DW_ATE_signed));
413 	diTypes.emplace(T(Half::getType()), diBuilder->createBasicType("Half", 16, dwarf::DW_ATE_float));
414 	diTypes.emplace(T(Float::getType()), diBuilder->createBasicType("Float", 32, dwarf::DW_ATE_float));
415 
416 	diTypes.emplace(T(Byte4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Byte::getType())], { vec16 }));
417 	diTypes.emplace(T(SByte4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(SByte::getType())], { vec16 }));
418 	diTypes.emplace(T(Byte8::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Byte::getType())], { vec16 }));
419 	diTypes.emplace(T(SByte8::getType()), diBuilder->createVectorType(128, 128, diTypes[T(SByte::getType())], { vec16 }));
420 	diTypes.emplace(T(Byte16::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Byte::getType())], { vec16 }));
421 	diTypes.emplace(T(SByte16::getType()), diBuilder->createVectorType(128, 128, diTypes[T(SByte::getType())], { vec16 }));
422 	diTypes.emplace(T(Short2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Short::getType())], { vec8 }));
423 	diTypes.emplace(T(UShort2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UShort::getType())], { vec8 }));
424 	diTypes.emplace(T(Short4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Short::getType())], { vec8 }));
425 	diTypes.emplace(T(UShort4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UShort::getType())], { vec8 }));
426 	diTypes.emplace(T(Short8::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Short::getType())], { vec8 }));
427 	diTypes.emplace(T(UShort8::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UShort::getType())], { vec8 }));
428 	diTypes.emplace(T(Int2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Int::getType())], { vec4 }));
429 	diTypes.emplace(T(UInt2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UInt::getType())], { vec4 }));
430 	diTypes.emplace(T(Int4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Int::getType())], { vec4 }));
431 	diTypes.emplace(T(UInt4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(UInt::getType())], { vec4 }));
432 	diTypes.emplace(T(Float2::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Float::getType())], { vec4 }));
433 	diTypes.emplace(T(Float4::getType()), diBuilder->createVectorType(128, 128, diTypes[T(Float::getType())], { vec4 }));
434 }
435 
getCallerLocation() const436 Location DebugInfo::getCallerLocation() const
437 {
438 	return getCallerBacktrace(1)[0];
439 }
440 
getCallerBacktrace(size_t limit) const441 Backtrace DebugInfo::getCallerBacktrace(size_t limit /* = 0 */) const
442 {
443 	return rr::getCallerBacktrace(limit);
444 }
445 
getOrCreateType(llvm::Type * type)446 llvm::DIType *DebugInfo::getOrCreateType(llvm::Type *type)
447 {
448 	auto it = diTypes.find(type);
449 	if(it != diTypes.end()) { return it->second; }
450 
451 	if(type->isPointerTy())
452 	{
453 		auto dbgTy = diBuilder->createPointerType(
454 		    getOrCreateType(type->getPointerElementType()),
455 		    sizeof(void *) * 8, alignof(void *) * 8);
456 		diTypes.emplace(type, dbgTy);
457 		return dbgTy;
458 	}
459 	llvm::errs() << "Unimplemented debug type: " << type << "\n";
460 	assert(false);
461 	return nullptr;
462 }
463 
getOrCreateFile(const char * path)464 llvm::DIFile *DebugInfo::getOrCreateFile(const char *path)
465 {
466 	auto it = diFiles.find(path);
467 	if(it != diFiles.end()) { return it->second; }
468 	auto dirAndName = splitPath(path);
469 	auto file = diBuilder->createFile(dirAndName.second, dirAndName.first);
470 	diFiles.emplace(path, file);
471 	return file;
472 }
473 
getOrParseFileTokens(const char * path)474 DebugInfo::LineTokens const *DebugInfo::getOrParseFileTokens(const char *path)
475 {
476 	static std::regex reLocalDecl(
477 	    "^"                                              // line start
478 	    "\\s*"                                           // initial whitespace
479 	    "(?:For\\s*\\(\\s*)?"                            // optional 'For('
480 	    "((?:\\w+(?:<[^>]+>)?)(?:::\\w+(?:<[^>]+>)?)*)"  // type (match group 1)
481 	    "\\s+"                                           // whitespace between type and name
482 	    "(\\w+)"                                         // identifier (match group 2)
483 	    "\\s*"                                           // whitespace after identifier
484 	    "(\\[.*\\])?");                                  // optional array suffix (match group 3)
485 
486 	auto it = fileTokens.find(path);
487 	if(it != fileTokens.end())
488 	{
489 		return it->second.get();
490 	}
491 
492 	auto tokens = std::make_unique<LineTokens>();
493 
494 	std::ifstream file(path);
495 	std::string line;
496 	int lineCount = 0;
497 	while(std::getline(file, line))
498 	{
499 		lineCount++;
500 		std::smatch match;
501 		if(std::regex_search(line, match, reLocalDecl) && match.size() > 3)
502 		{
503 			bool isArray = match.str(3) != "";
504 			if(!isArray)  // Cannot deal with C-arrays of values.
505 			{
506 				if(match.str(1) == "return")
507 				{
508 					(*tokens)[lineCount] = Token{ Token::Return };
509 				}
510 				else
511 				{
512 					(*tokens)[lineCount] = Token{ Token::Identifier, match.str(2) };
513 				}
514 			}
515 		}
516 	}
517 
518 	auto out = tokens.get();
519 	fileTokens.emplace(path, std::move(tokens));
520 	return out;
521 }
522 
523 }  // namespace rr
524 
525 #endif  // ENABLE_RR_DEBUG_INFO
526