Commit ac07ed82 by Ben Clayton

Reactor: Implement debug info generation

Currently only works on Linux + GDB. See docs/ReactorDebugInfo.md for details. Change-Id: I73d47d0492f6ccfc07eec4d4084332b4991fd515 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/27949 Presubmit-Ready: Ben Clayton <bclayton@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent 755467c4
...@@ -81,6 +81,7 @@ option (TSAN "Build with thread sanitizer" 0) ...@@ -81,6 +81,7 @@ option (TSAN "Build with thread sanitizer" 0)
option (UBSAN "Build with undefined behavior sanitizer" 0) option (UBSAN "Build with undefined behavior sanitizer" 0)
option (WARNINGS_AS_ERRORS "Treat all warnings as errors" 1) option (WARNINGS_AS_ERRORS "Treat all warnings as errors" 1)
option (DCHECK_ALWAYS_ON "Check validation macros even in release builds" 0) option (DCHECK_ALWAYS_ON "Check validation macros even in release builds" 0)
option (REACTOR_EMIT_DEBUG_INFO "Emit debug info for JIT functions" 0)
if(ARCH STREQUAL "arm") if(ARCH STREQUAL "arm")
set(DEFAULT_REACTOR_BACKEND "Subzero") set(DEFAULT_REACTOR_BACKEND "Subzero")
...@@ -205,6 +206,9 @@ else() ...@@ -205,6 +206,9 @@ else()
set(LLVM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-7.0/llvm) set(LLVM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-7.0/llvm)
set(LLVM_CONFIG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-7.0/configs) set(LLVM_CONFIG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-7.0/configs)
endif() endif()
set(LIBBACKTRACE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libbacktrace/src)
set(LIBBACKTRACE_CONFIG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libbacktrace/config)
set(LIBBACKTRACE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libbacktrace/src)
set(SUBZERO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/subzero) set(SUBZERO_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/subzero)
set(SUBZERO_LLVM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-subzero) set(SUBZERO_LLVM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-subzero)
set(TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests) set(TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests)
...@@ -293,6 +297,10 @@ else() ...@@ -293,6 +297,10 @@ else()
set_cpp_flag("-DDCHECK_ALWAYS_ON") set_cpp_flag("-DDCHECK_ALWAYS_ON")
endif() endif()
if(REACTOR_EMIT_DEBUG_INFO)
set_cpp_flag("-DENABLE_RR_DEBUG_INFO")
endif()
# Disable pedanitc warnings # Disable pedanitc warnings
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set_cpp_flag("-Wno-ignored-attributes") # ignoring attributes on template argument 'X' set_cpp_flag("-Wno-ignored-attributes") # ignoring attributes on template argument 'X'
...@@ -1762,18 +1770,28 @@ elseif(ARCH STREQUAL "arm") ...@@ -1762,18 +1770,28 @@ elseif(ARCH STREQUAL "arm")
${LLVM_DIR}/lib/Target/ARM/ARMLegalizerInfo.cpp ${LLVM_DIR}/lib/Target/ARM/ARMLegalizerInfo.cpp
${LLVM_DIR}/lib/Target/ARM/ARMOptimizeBarriersPass.cpp ${LLVM_DIR}/lib/Target/ARM/ARMOptimizeBarriersPass.cpp
) )
endif() endif()
if(REACTOR_EMIT_DEBUG_INFO)
list(APPEND LLVM_LIST
${LLVM_DIR}/lib/Demangle/ItaniumDemangle.cpp
)
endif(REACTOR_EMIT_DEBUG_INFO)
set(LLVM_INCLUDE_DIR "") set(LLVM_INCLUDE_DIR "")
if(WIN32) if(WIN32)
list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/windows/include) list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/windows/include)
list(APPEND LIBBACKTRACE_INCLUDE_DIR ${LIBBACKTRACE_CONFIG_DIR}/windows/include)
elseif(LINUX) elseif(LINUX)
list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/linux/include) list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/linux/include)
list(APPEND LIBBACKTRACE_INCLUDE_DIR ${LIBBACKTRACE_CONFIG_DIR}/linux/include)
elseif(APPLE) elseif(APPLE)
list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/darwin/include) list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/darwin/include)
list(APPEND LIBBACKTRACE_INCLUDE_DIR ${LIBBACKTRACE_CONFIG_DIR}/darwin/include)
elseif(ANDROID) elseif(ANDROID)
list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/android/include) list(APPEND LLVM_INCLUDE_DIR ${LLVM_CONFIG_DIR}/android/include)
list(APPEND LIBBACKTRACE_INCLUDE_DIR ${LIBBACKTRACE_CONFIG_DIR}/android/include)
endif() endif()
list(APPEND LLVM_INCLUDE_DIR list(APPEND LLVM_INCLUDE_DIR
...@@ -1962,6 +1980,7 @@ set(COMMON_INCLUDE_DIR ...@@ -1962,6 +1980,7 @@ set(COMMON_INCLUDE_DIR
${SOURCE_DIR} ${SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include
${LLVM_INCLUDE_DIR} ${LLVM_INCLUDE_DIR}
${LIBBACKTRACE_INCLUDE_DIR}
) )
set(OPENGL_INCLUDE_DIR set(OPENGL_INCLUDE_DIR
${OPENGL_DIR} ${OPENGL_DIR}
...@@ -2002,9 +2021,41 @@ list(REMOVE_ITEM SWIFTSHADER_LIST ...@@ -2002,9 +2021,41 @@ list(REMOVE_ITEM SWIFTSHADER_LIST
${SOURCE_DIR}/Common/GrallocAndroid.hpp ${SOURCE_DIR}/Common/GrallocAndroid.hpp
) )
if(REACTOR_EMIT_DEBUG_INFO)
set(LIBBACKTRACE_LIST
${LIBBACKTRACE_DIR}/atomic.c
${LIBBACKTRACE_DIR}/backtrace.c
${LIBBACKTRACE_DIR}/backtrace.h
${LIBBACKTRACE_DIR}/dwarf.c
${LIBBACKTRACE_DIR}/fileline.c
${LIBBACKTRACE_DIR}/filenames.h
${LIBBACKTRACE_DIR}/internal.h
${LIBBACKTRACE_DIR}/mmap.c
${LIBBACKTRACE_DIR}/mmapio.c
${LIBBACKTRACE_DIR}/posix.c
${LIBBACKTRACE_DIR}/print.c
${LIBBACKTRACE_DIR}/simple.c
${LIBBACKTRACE_DIR}/sort.c
${LIBBACKTRACE_DIR}/state.c
${LIBBACKTRACE_INCLUDE_DIR}/backtrace-supported.h
${LIBBACKTRACE_INCLUDE_DIR}/config.h
)
if(WIN32)
list(APPEND LIBBACKTRACE_LIST ${LIBBACKTRACE_DIR}/pecoff.c)
elseif(LINUX)
list(APPEND LIBBACKTRACE_LIST ${LIBBACKTRACE_DIR}/elf.c)
elseif(APPLE)
message(FATAL_ERROR "libbacktrace does not support mach-o yet")
endif()
endif(REACTOR_EMIT_DEBUG_INFO)
set(REACTOR_LLVM_LIST set(REACTOR_LLVM_LIST
${SOURCE_DIR}/Reactor/Reactor.cpp ${SOURCE_DIR}/Reactor/Reactor.cpp
${SOURCE_DIR}/Reactor/LLVMReactor.cpp ${SOURCE_DIR}/Reactor/LLVMReactor.cpp
${SOURCE_DIR}/Reactor/LLVMReactor.hpp
${SOURCE_DIR}/Reactor/LLVMReactorDebugInfo.cpp
${SOURCE_DIR}/Reactor/LLVMReactorDebugInfo.hpp
${SOURCE_DIR}/Reactor/Nucleus.hpp ${SOURCE_DIR}/Reactor/Nucleus.hpp
${SOURCE_DIR}/Reactor/Routine.cpp ${SOURCE_DIR}/Reactor/Routine.cpp
${SOURCE_DIR}/Reactor/Routine.hpp ${SOURCE_DIR}/Reactor/Routine.hpp
...@@ -2202,6 +2253,17 @@ set_target_properties(ReactorLLVM PROPERTIES ...@@ -2202,6 +2253,17 @@ set_target_properties(ReactorLLVM PROPERTIES
) )
target_link_libraries(ReactorLLVM llvm ${OS_LIBS}) target_link_libraries(ReactorLLVM llvm ${OS_LIBS})
if(REACTOR_EMIT_DEBUG_INFO)
add_library(Libbacktrace STATIC ${LIBBACKTRACE_LIST})
set_target_properties(Libbacktrace PROPERTIES
INCLUDE_DIRECTORIES "${LIBBACKTRACE_INCLUDE_DIR}"
POSITION_INDEPENDENT_CODE 1
FOLDER "Core"
)
target_link_libraries(Libbacktrace ${OS_LIBS})
target_link_libraries(ReactorLLVM Libbacktrace)
endif(REACTOR_EMIT_DEBUG_INFO)
if(${REACTOR_BACKEND} STREQUAL "LLVM") if(${REACTOR_BACKEND} STREQUAL "LLVM")
set(Reactor ReactorLLVM) set(Reactor ReactorLLVM)
elseif(${REACTOR_BACKEND} STREQUAL "Subzero") elseif(${REACTOR_BACKEND} STREQUAL "Subzero")
......
...@@ -122,7 +122,10 @@ ...@@ -122,7 +122,10 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="$(SolutionDir)src\Reactor\Reactor.cpp" /> <ClCompile Include="$(SolutionDir)src\Reactor\Reactor.cpp" />
<ClCompile Include="$(SolutionDir)src\Reactor\LLVMReactor.cpp" /> <ClCompile Include="$(SolutionDir)src\Reactor\LLVMReactor.cpp" />
<ClInclude Include="$(SolutionDir)src\Reactor\LLVMReactor.hpp" />
<ClCompile Include="$(SolutionDir)src\Reactor\LLVMReactorDebugInfo.cpp" />
<ClInclude Include="$(SolutionDir)src\Reactor\LLVMReactorDebugInfo.hpp" />
<ClInclude Include="$(SolutionDir)src\Reactor\Nucleus.hpp" /> <ClInclude Include="$(SolutionDir)src\Reactor\Nucleus.hpp" />
<ClCompile Include="$(SolutionDir)src\Reactor\Routine.cpp" /> <ClCompile Include="$(SolutionDir)src\Reactor\Routine.cpp" />
<ClInclude Include="$(SolutionDir)src\Reactor\Routine.hpp" /> <ClInclude Include="$(SolutionDir)src\Reactor\Routine.hpp" />
......
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
<ClCompile Include="$(SolutionDir)src\Reactor\LLVMReactor.cpp"> <ClCompile Include="$(SolutionDir)src\Reactor\LLVMReactor.cpp">
<Filter>src\Reactor</Filter> <Filter>src\Reactor</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="$(SolutionDir)src\Reactor\LLVMReactorDebugInfo.cpp">
<Filter>src\Reactor</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)src\Reactor\Routine.cpp"> <ClCompile Include="$(SolutionDir)src\Reactor\Routine.cpp">
<Filter>src\Reactor</Filter> <Filter>src\Reactor</Filter>
</ClCompile> </ClCompile>
...@@ -33,6 +36,12 @@ ...@@ -33,6 +36,12 @@
<ClInclude Include="$(SolutionDir)src\Reactor\Routine.hpp"> <ClInclude Include="$(SolutionDir)src\Reactor\Routine.hpp">
<Filter>src\Reactor</Filter> <Filter>src\Reactor</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="$(SolutionDir)src\Reactor\LLVMReactor.hpp">
<Filter>src\Reactor</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)src\Reactor\LLVMReactorDebugInfo.hpp">
<Filter>src\Reactor</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)src\Reactor\LLVMRoutine.hpp"> <ClInclude Include="$(SolutionDir)src\Reactor\LLVMRoutine.hpp">
<Filter>src\Reactor</Filter> <Filter>src\Reactor</Filter>
</ClInclude> </ClInclude>
......
# Reactor Debug Info Generation
## Introduction
Reactor produces Just In Time compiled dynamic executable code and can be used to JIT high performance functions specialized for runtime
configurations, or to even build a compiler.
In order to debug executable code at a higher level than disassembly, source code files are required.
Reactor has two potential sources of source code:
1. The C++ source code of the program that calls into Reactor.
2. External source files read by the program and passed to Reactor.
While case (2) is preferable for implementing a compiler, this is currently not
implemented.
Reactor implements case (1) and this can be used by GDB to single line step and
inspect variables.
## Supported Platforms
Currently:
* Debug info generation is only supported on Linux with the LLVM 7
backend.
* GDB is the only supported debugger.
* The program must be compiled with debug info iteself.
## Enabling
Debug generation is enabled with `REACTOR_EMIT_DEBUG_INFO` CMake flag (defaults
to disabled).
## Implementation details
### Source Location
All Reactor functions begin with a call to `RR_DEBUG_INFO_UPDATE_LOC()`, which calls into `rr::DebugInfo::EmitLocation()`.
`rr::DebugInfo::EmitLocation()` calls `rr::DebugInfo::getCallerBacktrace()`,
which in turn uses [`libbacktrace`](https://github.com/ianlancetaylor/libbacktrace)
to unwind the stack and find the file, function and line of the caller.
This information is passed to `llvm::IRBuilder<>::SetCurrentDebugLocation`
to emit source line information for the next LLVM instructions to be built.
### Variables
There are 3 aspects to generating variable debug information:
#### 1. Variable names
Constructing a Reactor `LValue`:
```C++
rr::Int a = 1;
```
Will emit an LLVM `alloca` instruction to allocate the storage of the variable,
and emit another to initialize it to the constant `1`. While fluent, none of the
Reactor calls see the name of the C++ local variable "`a`", and the LLVM `alloca`
value gets a meaningless numerical value.
There are two potential ways that Reactor can obtain the variable name:
1. Use the running executable's own debug information to examine the local
declaration and extract the local variable's name.
2. Use the backtrace information to parse the name from the source file.
While (1) is arguably a cleaner and more robust solution, (2) is
easier to implement and can work for the majority of use cases.
(2) is the current solution implemented.
`rr::DebugInfo::getOrParseFileTokens()` scans a source file line by line, and
uses a regular expression to look for patterns of `<type> <name>`. Matching is not
precise, but is adequate to find locals constructed with and without assignment.
#### 2. Variable binding
Given that we can find a variable name for a given source line, we need a way of
binding the LLVM values to the name.
Given our trivial example:
```C++
rr::Int a = 1
```
The `rr::Int` constructor calls `RR_DEBUG_INFO_EMIT_VAR()` passing the storage
value as single argument. `RR_DEBUG_INFO_EMIT_VAR()` performs the backtrace
to find the source file and line and uses the token information produced by
`rr::DebugInfo::getOrParseFileTokens()` to identify the variable name.
However, things get a bit more complicated when there are multiple variables
being constructed on the same line.
Take for example:
```C++
rr::Int a = rr::Int(1) + rr::Int(2)
```
Here we have 3 calls to the `rr::Int` constructor, each calling down
to `RR_DEBUG_INFO_EMIT_VAR()`.
To disambiguate which of these should be bound to the variable name "`a`",
`rr::DebugInfo::EmitVariable()` buffers the binding into
`scope.pending` and the last binding for a given line is used by
`DebugInfo::emitPending()`. For variable construction and assignment, C++
guarantees that the LHS is the last value to be constructed.
This solution is not perfect.
Multi-line expressions, multiple assignments on a single line, macro obfuscation
can all break variable bindings - however the majority of typical cases work.
#### 3. Variable scope
`rr::DebugInfo` maintains a stack of `llvm::DIScope`s and `llvm::DILocation`s
that mirrors the current backtrace for function being called.
A synthetic call stack is produced by chaining `llvm::DILocation`s with
`InlinedAt`s.
For example, at the declaration of `i`:
```C++
void B()
{
rr::Int i; // <- here
}
void A()
{
B();
}
int main(int argc, const char* argv[])
{
A();
}
```
The `DIScope` hierarchy would be:
```C++
DIFile: "foo.cpp"
rr::DebugInfo::diScope[0].di: ↳ DISubprogram: "main"
rr::DebugInfo::diScope[1].di: ↳ DISubprogram: "A"
rr::DebugInfo::diScope[2].di: ↳ DISubprogram: "B"
```
The `DILocation` hierarchy would be:
```C++
rr::DebugInfo::diRootLocation: DILocation(DISubprogram: "ReactorFunction")
rr::DebugInfo::diScope[0].location: ↳ DILocation(DISubprogram: "main")
rr::DebugInfo::diScope[1].location: ↳ DILocation(DISubprogram: "A")
rr::DebugInfo::diScope[2].location: ↳ DILocation(DISubprogram: "B")
```
Where '↳' represents an `InlinedAt`.
`rr::DebugInfo::diScope` is updated by `rr::DebugInfo::syncScope()`.
`llvm::DIScope`s typically do not nest - there is usually a separate
`llvm::DISubprogram` for each function in the callstack. All local variables
within a function will typically share the same scope, regardless of whether
they are declared within a sub-block.
Loops and jumps within a function add complexity. Consider:
```C++
void B()
{
rr::Int i = 0;
}
void A()
{
for (int i = 0; i < 3; i++)
{
rr::Int x = 0;
}
B();
}
int main(int argc, const char* argv[])
{
A();
}
```
In this particular example Reactor will not be aware of the `for` loop, and will
attempt to create three variables called "`x`" in the same function scope for `A()`.
Duplicate symbols in the same `llvm::DIScope` result in undefined behavior.
To solve this, `rr::DebugInfo::syncScope()` observes when a function jumps
backwards, and forks the current `llvm::DILexicalBlock` for the function. This
results in a number of `llvm::DILexicalBlock` chains, each declaring variables
that shadow the previous block.
At the declaration of `i`, the `DIScope` hierarchy would be:
```C++
DIFile: "foo.cpp"
rr::DebugInfo::diScope[0].di: ↳ DISubprogram: "main"
↳ DISubprogram: "A"
| ↳ DILexicalBlock: "A".1
rr::DebugInfo::diScope[1].di: | ↳ DILexicalBlock: "A".2
rr::DebugInfo::diScope[2].di: ↳ DISubprogram: "B"
```
The `DILocation` hierarchy would be:
```C++
rr::DebugInfo::diRootLocation: DILocation(DISubprogram: "ReactorFunction")
rr::DebugInfo::diScope[0].location: ↳ DILocation(DISubprogram: "main")
rr::DebugInfo::diScope[1].location: ↳ DILocation(DILexicalBlock: "A".2)
rr::DebugInfo::diScope[2].location: ↳ DILocation(DISubprogram: "B")
```
### Debugger integration
Once the debug information has been generated, it needs to be handed to the
debugger.
Reactor uses [`llvm::JITEventListener::createGDBRegistrationListener()`](http://llvm.org/doxygen/classllvm_1_1JITEventListener.html#a004abbb5a0d48ac376dfbe3e3c97c306)
to inform GDB of the JIT'd program and its debugging information.
More information [can be found here](https://llvm.org/docs/DebuggingJITedCode.html).
LLDB should be able to support this same mechanism, but at the time of writing
this does not appear to work.
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef rr_LLVMReactor_hpp
#define rr_LLVMReactor_hpp
namespace llvm
{
class Type;
class Value;
}
namespace rr
{
class Type;
class Value;
llvm::Type *T(Type *t);
inline Type *T(llvm::Type *t)
{
return reinterpret_cast<Type*>(t);
}
inline llvm::Value *V(Value *t)
{
return reinterpret_cast<llvm::Value*>(t);
}
inline Value *V(llvm::Value *t)
{
return reinterpret_cast<Value*>(t);
}
// Emits a no-op instruction that will not be optimized away.
// Useful for emitting something that can have a source location without
// effect.
void Nop();
}
#endif // rr_LLVMReactor_hpp
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef rr_LLVMReactorDebugInfo_hpp
#define rr_LLVMReactorDebugInfo_hpp
#include "Reactor.hpp"
#ifdef ENABLE_RR_DEBUG_INFO
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <memory>
// Forward declarations
namespace llvm
{
class BasicBlock;
class ConstantFolder;
class DIBuilder;
class DICompileUnit;
class DIFile;
class DILocation;
class DIScope;
class DISubprogram;
class DIType;
class Function;
class Instruction;
class IRBuilderDefaultInserter;
class JITEventListener;
class LLVMContext;
class LoadedObjectInfo;
class Module;
class Type;
class Value;
namespace object
{
class ObjectFile;
}
template <typename T, typename Inserter> class IRBuilder;
} // namespace llvm
namespace rr
{
class Type;
class Value;
// DebugInfo generates LLVM DebugInfo IR from the C++ source that calls
// into Reactor functions. See docs/ReactorDebugInfo.mk for more information.
class DebugInfo
{
public:
using IRBuilder = llvm::IRBuilder<llvm::ConstantFolder, llvm::IRBuilderDefaultInserter>;
DebugInfo(IRBuilder *builder,
llvm::LLVMContext *context,
llvm::Module *module,
llvm::Function *function);
// Finalize debug info generation. Must be called before the LLVM module
// is built.
void Finalize();
// Updates the current source location.
void EmitLocation();
// Binds the value to its symbol in the source file.
// See docs/ReactorDebugInfo.mk for more information.
void EmitVariable(Value *value);
// Forcefully flush the binding of the last variable name.
// Used for binding the initializer of `For` loops.
void Flush();
// NotifyObjectEmitted informs any attached debuggers of the JIT'd
// object.
void NotifyObjectEmitted(const llvm::object::ObjectFile &Obj, const llvm::LoadedObjectInfo &L);
// NotifyFreeingObject informs any attached debuggers that the JIT'd
// object is now invalid.
void NotifyFreeingObject(const llvm::object::ObjectFile &Obj);
private:
struct Token
{
enum Kind
{
Identifier,
Return
};
Kind kind;
std::string identifier;
};
using LineTokens = std::unordered_map<unsigned int, Token>;
struct FunctionLocation
{
std::string name;
std::string file;
bool operator == (const FunctionLocation &rhs) const { return name == rhs.name && file == rhs.file; }
bool operator != (const FunctionLocation &rhs) const { return !(*this == rhs); }
struct Hash
{
std::size_t operator()(const FunctionLocation &l) const noexcept
{
return std::hash<std::string>()(l.file) * 31 +
std::hash<std::string>()(l.name);
}
};
};
struct Location
{
FunctionLocation function;
unsigned int line = 0;
bool operator == (const Location &rhs) const { return function == rhs.function && line == rhs.line; }
bool operator != (const Location &rhs) const { return !(*this == rhs); }
struct Hash
{
std::size_t operator()(const Location &l) const noexcept
{
return FunctionLocation::Hash()(l.function) * 31 +
std::hash<unsigned int>()(l.line);
}
};
};
using Backtrace = std::vector<Location>;
struct Pending
{
std::string name;
Location location;
llvm::DILocation *diLocation = nullptr;
llvm::Value *value = nullptr;
llvm::Instruction *insertAfter = nullptr;
llvm::BasicBlock *block = nullptr;
llvm::DIScope *scope = nullptr;
bool addNopOnNextLine = false;
};
struct Scope
{
Location location;
llvm::DIScope *di;
std::unordered_set<std::string> symbols;
Pending pending;
};
void registerBasicTypes();
void emitPending(Scope &scope, IRBuilder *builder, llvm::DIBuilder *diBuilder);
// Returns the source location of the non-Reactor calling function.
Location getCallerLocation() const;
// Returns the backtrace for the callstack, starting at the first
// non-Reactor file. If limit is non-zero, then a maximum of limit
// frames will be returned.
Backtrace getCallerBacktrace(size_t limit = 0) const;
llvm::DILocation* getLocation(const Backtrace &backtrace, size_t i);
llvm::DIType *getOrCreateType(llvm::Type* type);
llvm::DIFile *getOrCreateFile(const char* path);
LineTokens const *getOrParseFileTokens(const char* path);
// Synchronizes diScope with the current backtrace.
void syncScope(Backtrace const& backtrace);
IRBuilder *builder;
llvm::LLVMContext *context;
llvm::Module *module;
llvm::Function *function;
llvm::DIBuilder *diBuilder;
llvm::DICompileUnit *diCU;
llvm::DISubprogram *diSubprogram;
llvm::DILocation *diRootLocation;
std::vector<Scope> diScope;
std::unordered_map<std::string, llvm::DIFile*> diFiles;
std::unordered_map<llvm::Type*, llvm::DIType*> diTypes;
std::unordered_map<std::string, std::unique_ptr<LineTokens>> fileTokens;
llvm::JITEventListener *jitEventListener;
std::vector<void const*> pushed;
};
} // namespace rr
#endif // ENABLE_RR_DEBUG_INFO
#endif // rr_LLVMReactorDebugInfo_hpp
...@@ -289,6 +289,7 @@ ...@@ -289,6 +289,7 @@
<ClCompile Include="LLVMRoutine.cpp" /> <ClCompile Include="LLVMRoutine.cpp" />
<ClCompile Include="LLVMRoutineManager.cpp" /> <ClCompile Include="LLVMRoutineManager.cpp" />
<ClCompile Include="LLVMReactor.cpp" /> <ClCompile Include="LLVMReactor.cpp" />
<ClCompile Include="LLVMReactorDebugInfo.cpp" />
<ClCompile Include="ExecutableMemory.cpp" /> <ClCompile Include="ExecutableMemory.cpp" />
<ClCompile Include="Reactor.cpp" /> <ClCompile Include="Reactor.cpp" />
<ClCompile Include="Routine.cpp" /> <ClCompile Include="Routine.cpp" />
...@@ -297,6 +298,8 @@ ...@@ -297,6 +298,8 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="CPUID.hpp" /> <ClInclude Include="CPUID.hpp" />
<ClInclude Include="Debug.hpp" /> <ClInclude Include="Debug.hpp" />
<ClInclude Include="LLVMReactor.hpp" />
<ClInclude Include="LLVMReactorDebugInfo.hpp" />
<ClInclude Include="LLVMRoutine.hpp" /> <ClInclude Include="LLVMRoutine.hpp" />
<ClInclude Include="LLVMRoutineManager.hpp" /> <ClInclude Include="LLVMRoutineManager.hpp" />
<ClInclude Include="ExecutableMemory.hpp" /> <ClInclude Include="ExecutableMemory.hpp" />
......
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
<ClCompile Include="LLVMReactor.cpp"> <ClCompile Include="LLVMReactor.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="LLVMReactorDebugInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CPUID.cpp"> <ClCompile Include="CPUID.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
...@@ -56,6 +59,12 @@ ...@@ -56,6 +59,12 @@
<ClInclude Include="Routine.hpp"> <ClInclude Include="Routine.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="LLVMReactor.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LLVMReactorDebugInfo.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LLVMRoutineManager.hpp"> <ClInclude Include="LLVMRoutineManager.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
......
...@@ -3425,4 +3425,8 @@ namespace rr ...@@ -3425,4 +3425,8 @@ namespace rr
RValue<Float4> Log2(RValue<Float4> x) { UNIMPLEMENTED("Subzero Log2()"); return Float4(0); } RValue<Float4> Log2(RValue<Float4> x) { UNIMPLEMENTED("Subzero Log2()"); return Float4(0); }
RValue<UInt4> Ctlz(RValue<UInt4> x, bool isZeroUndef) { UNIMPLEMENTED("Subzero Ctlz()"); return UInt4(0); } RValue<UInt4> Ctlz(RValue<UInt4> x, bool isZeroUndef) { UNIMPLEMENTED("Subzero Ctlz()"); return UInt4(0); }
RValue<UInt4> Cttz(RValue<UInt4> x, bool isZeroUndef) { UNIMPLEMENTED("Subzero Cttz()"); return UInt4(0); } RValue<UInt4> Cttz(RValue<UInt4> x, bool isZeroUndef) { UNIMPLEMENTED("Subzero Cttz()"); return UInt4(0); }
void EmitDebugLocation() {}
void EmitDebugVariable(Value* value) {}
void FlushDebug() {}
} }
/* backtrace-supported.h.in -- Whether stack backtrace is supported.
Copyright (C) 2012-2016 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* The file backtrace-supported.h.in is used by configure to generate
the file backtrace-supported.h. The file backtrace-supported.h may
be #include'd to see whether the backtrace library will be able to
get a backtrace and produce symbolic information. */
/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
should work, 0 if it will not. Libraries may #include this to make
other arrangements. */
#define BACKTRACE_SUPPORTED 1
/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace
library will call malloc as it works, 0 if it will call mmap
instead. This may be used to determine whether it is safe to call
the backtrace functions from a signal handler. In general this
only applies to calls like backtrace and backtrace_pcinfo. It does
not apply to backtrace_simple, which never calls malloc. It does
not apply to backtrace_print, which always calls fprintf and
therefore malloc. */
#define BACKTRACE_USES_MALLOC 0
/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace
library is configured with threading support, 0 if not. If this is
0, the threaded parameter to backtrace_create_state must be passed
as 0. */
#define BACKTRACE_SUPPORTS_THREADS 1
/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo
will work for variables. It will always work for functions. */
#define BACKTRACE_SUPPORTS_DATA 1
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* ELF size: 32 or 64 */
#define BACKTRACE_ELF_SIZE 64
/* XCOFF size: 32 or 64 */
#define BACKTRACE_XCOFF_SIZE unused
/* Define to 1 if you have the __atomic functions */
#define HAVE_ATOMIC_FUNCTIONS 1
/* Define to 1 if you have the `clock_gettime' function. */
#define HAVE_CLOCK_GETTIME 1
/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
don't. */
#define HAVE_DECL_STRNLEN 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define if dl_iterate_phdr is available. */
/* #undef HAVE_DL_ITERATE_PHDR */
/* Define to 1 if you have the fcntl function */
#define HAVE_FCNTL 1
/* Define if getexecname is available. */
/* #undef HAVE_GETEXECNAME */
/* Define if _Unwind_GetIPInfo is available. */
#define HAVE_GETIPINFO 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `z' library (-lz). */
#define HAVE_LIBZ 1
/* Define to 1 if you have the <link.h> header file. */
/* #undef HAVE_LINK_H */
/* Define if AIX loadquery is available. */
/* #undef HAVE_LOADQUERY */
/* Define to 1 if you have the `lstat' function. */
#define HAVE_LSTAT 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the `readlink' function. */
#define HAVE_READLINK 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the __sync functions */
#define HAVE_SYNC_FUNCTIONS 1
/* Define to 1 if you have the <sys/ldr.h> header file. */
/* #undef HAVE_SYS_LDR_H */
/* Define to 1 if you have the <sys/mman.h> header file. */
#define HAVE_SYS_MMAN_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define if -lz is available. */
#define HAVE_ZLIB 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME "package-unused"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "package-unused version-unused"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libbacktrace"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "version-unused"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# define _ALL_SOURCE 1
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# define __EXTENSIONS__ 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */
/* backtrace-supported.h.in -- Whether stack backtrace is supported.
Copyright (C) 2012-2016 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Google.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
(1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
(2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
(3) The name of the author may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* The file backtrace-supported.h.in is used by configure to generate
the file backtrace-supported.h. The file backtrace-supported.h may
be #include'd to see whether the backtrace library will be able to
get a backtrace and produce symbolic information. */
/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
should work, 0 if it will not. Libraries may #include this to make
other arrangements. */
#define BACKTRACE_SUPPORTED 1
/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace
library will call malloc as it works, 0 if it will call mmap
instead. This may be used to determine whether it is safe to call
the backtrace functions from a signal handler. In general this
only applies to calls like backtrace and backtrace_pcinfo. It does
not apply to backtrace_simple, which never calls malloc. It does
not apply to backtrace_print, which always calls fprintf and
therefore malloc. */
#define BACKTRACE_USES_MALLOC 0
/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace
library is configured with threading support, 0 if not. If this is
0, the threaded parameter to backtrace_create_state must be passed
as 0. */
#define BACKTRACE_SUPPORTS_THREADS 1
/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo
will work for variables. It will always work for functions. */
#define BACKTRACE_SUPPORTS_DATA 1
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* ELF size: 32 or 64 */
#define BACKTRACE_ELF_SIZE 64
/* XCOFF size: 32 or 64 */
#define BACKTRACE_XCOFF_SIZE unused
/* Define to 1 if you have the __atomic functions */
#define HAVE_ATOMIC_FUNCTIONS 1
/* Define to 1 if you have the `clock_gettime' function. */
#define HAVE_CLOCK_GETTIME 1
/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
don't. */
#define HAVE_DECL_STRNLEN 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define if dl_iterate_phdr is available. */
#define HAVE_DL_ITERATE_PHDR 1
/* Define to 1 if you have the fcntl function */
#define HAVE_FCNTL 1
/* Define if getexecname is available. */
/* #undef HAVE_GETEXECNAME */
/* Define if _Unwind_GetIPInfo is available. */
#define HAVE_GETIPINFO 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `z' library (-lz). */
#define HAVE_LIBZ 1
/* Define to 1 if you have the <link.h> header file. */
#define HAVE_LINK_H 1
/* Define if AIX loadquery is available. */
/* #undef HAVE_LOADQUERY */
/* Define to 1 if you have the `lstat' function. */
#define HAVE_LSTAT 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the `readlink' function. */
#define HAVE_READLINK 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the __sync functions */
#define HAVE_SYNC_FUNCTIONS 1
/* Define to 1 if you have the <sys/ldr.h> header file. */
/* #undef HAVE_SYS_LDR_H */
/* Define to 1 if you have the <sys/mman.h> header file. */
#define HAVE_SYS_MMAN_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define if -lz is available. */
#define HAVE_ZLIB 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME "package-unused"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "package-unused version-unused"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libbacktrace"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "version-unused"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# define _ALL_SOURCE 1
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# define _POSIX_PTHREAD_SEMANTICS 1
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# define _TANDEM_SOURCE 1
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# define __EXTENSIONS__ 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
/* #undef _POSIX_1_SOURCE */
/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment