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