Implement the JIT side of the GDB JIT debugging interface. To enable this
feature, either build the JIT in debug mode to enable it by default or pass
-jit-emit-debug to lli.
Right now, the only debug information that this communicates to GDB is call
frame information, since it's already being generated to support exceptions in
the JIT. Eventually, when DWARF generation isn't tied so tightly to AsmPrinter,
it will be easy to push that information to GDB through this interface.
Here's a step-by-step breakdown of how the feature works:
- The JIT generates the machine code and DWARF call frame info
(.eh_frame/.debug_frame) for a function into memory.
- The JIT copies that info into an in-memory ELF file with a symbol for the
function.
- The JIT creates a code entry pointing to the ELF buffer and adds it to a
linked list hanging off of a global descriptor at a special symbol that GDB
knows about.
- The JIT calls a function marked noinline that GDB knows about and has put an
internal breakpoint in.
- GDB catches the breakpoint and reads the global descriptor to look for new
code.
- When sees there is new code, it reads the ELF from the inferior's memory and
adds it to itself as an object file.
- The JIT continues, and the next time we stop the program, we are able to
produce a proper backtrace.
Consider running the following program through the JIT:
#include <stdio.h>
void baz(short z) {
long w = z + 1;
printf("%d, %x\n", w, *((int*)NULL)); // SEGFAULT here
}
void bar(short y) {
int z = y + 1;
baz(z);
}
void foo(char x) {
short y = x + 1;
bar(y);
}
int main(int argc, char** argv) {
char x = 1;
foo(x);
}
Here is a backtrace before this patch:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x2aaaabdfbd10 (LWP 25476)]
0x00002aaaabe7d1a8 in ?? ()
(gdb) bt
#0 0x00002aaaabe7d1a8 in ?? ()
#1 0x0000000000000003 in ?? ()
#2 0x0000000000000004 in ?? ()
#3 0x00032aaaabe7cfd0 in ?? ()
#4 0x00002aaaabe7d12c in ?? ()
#5 0x00022aaa00000003 in ?? ()
#6 0x00002aaaabe7d0aa in ?? ()
#7 0x01000002abe7cff0 in ?? ()
#8 0x00002aaaabe7d02c in ?? ()
#9 0x0100000000000001 in ?? ()
#10 0x00000000014388e0 in ?? ()
#11 0x00007fff00000001 in ?? ()
#12 0x0000000000b870a2 in llvm::JIT::runFunction (this=0x1405b70,
F=0x14024e0, ArgValues=@0x7fffffffe050)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:395
#13 0x0000000000baa4c5 in llvm::ExecutionEngine::runFunctionAsMain
(this=0x1405b70, Fn=0x14024e0, argv=@0x13f06f8, envp=0x7fffffffe3b0)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:377
#14 0x00000000007ebd52 in main (argc=2, argv=0x7fffffffe398,
envp=0x7fffffffe3b0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:208
And a backtrace after this patch:
Program received signal SIGSEGV, Segmentation fault.
0x00002aaaabe7d1a8 in baz ()
(gdb) bt
#0 0x00002aaaabe7d1a8 in baz ()
#1 0x00002aaaabe7d12c in bar ()
#2 0x00002aaaabe7d0aa in foo ()
#3 0x00002aaaabe7d02c in main ()
#4 0x0000000000b870a2 in llvm::JIT::runFunction (this=0x1405b70,
F=0x14024e0, ArgValues=...)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:395
#5 0x0000000000baa4c5 in llvm::ExecutionEngine::runFunctionAsMain
(this=0x1405b70, Fn=0x14024e0, argv=..., envp=0x7fffffffe3c0)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:377
#6 0x00000000007ebd52 in main (argc=2, argv=0x7fffffffe3a8,
envp=0x7fffffffe3c0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:208
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@82418 91177308-0d34-0410-b5e6-96231b3b80d8
2009-09-20 23:52:43 +00:00
|
|
|
//===-- JITDebugRegisterer.h - Register debug symbols for JIT -------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file defines a JITDebugRegisterer object that is used by the JIT to
|
|
|
|
// register debug info with debuggers like GDB.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#ifndef LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H
|
|
|
|
#define LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H
|
|
|
|
|
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/Support/DataTypes.h"
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
// This must be kept in sync with gdb/gdb/jit.h .
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
JIT_NOACTION = 0,
|
|
|
|
JIT_REGISTER_FN,
|
|
|
|
JIT_UNREGISTER_FN
|
|
|
|
} jit_actions_t;
|
|
|
|
|
|
|
|
struct jit_code_entry {
|
|
|
|
struct jit_code_entry *next_entry;
|
|
|
|
struct jit_code_entry *prev_entry;
|
|
|
|
const char *symfile_addr;
|
|
|
|
uint64_t symfile_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct jit_descriptor {
|
|
|
|
uint32_t version;
|
|
|
|
// This should be jit_actions_t, but we want to be specific about the
|
|
|
|
// bit-width.
|
|
|
|
uint32_t action_flag;
|
|
|
|
struct jit_code_entry *relevant_entry;
|
|
|
|
struct jit_code_entry *first_entry;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace llvm {
|
|
|
|
|
2009-09-23 11:48:57 +00:00
|
|
|
class ELFSection;
|
Implement the JIT side of the GDB JIT debugging interface. To enable this
feature, either build the JIT in debug mode to enable it by default or pass
-jit-emit-debug to lli.
Right now, the only debug information that this communicates to GDB is call
frame information, since it's already being generated to support exceptions in
the JIT. Eventually, when DWARF generation isn't tied so tightly to AsmPrinter,
it will be easy to push that information to GDB through this interface.
Here's a step-by-step breakdown of how the feature works:
- The JIT generates the machine code and DWARF call frame info
(.eh_frame/.debug_frame) for a function into memory.
- The JIT copies that info into an in-memory ELF file with a symbol for the
function.
- The JIT creates a code entry pointing to the ELF buffer and adds it to a
linked list hanging off of a global descriptor at a special symbol that GDB
knows about.
- The JIT calls a function marked noinline that GDB knows about and has put an
internal breakpoint in.
- GDB catches the breakpoint and reads the global descriptor to look for new
code.
- When sees there is new code, it reads the ELF from the inferior's memory and
adds it to itself as an object file.
- The JIT continues, and the next time we stop the program, we are able to
produce a proper backtrace.
Consider running the following program through the JIT:
#include <stdio.h>
void baz(short z) {
long w = z + 1;
printf("%d, %x\n", w, *((int*)NULL)); // SEGFAULT here
}
void bar(short y) {
int z = y + 1;
baz(z);
}
void foo(char x) {
short y = x + 1;
bar(y);
}
int main(int argc, char** argv) {
char x = 1;
foo(x);
}
Here is a backtrace before this patch:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x2aaaabdfbd10 (LWP 25476)]
0x00002aaaabe7d1a8 in ?? ()
(gdb) bt
#0 0x00002aaaabe7d1a8 in ?? ()
#1 0x0000000000000003 in ?? ()
#2 0x0000000000000004 in ?? ()
#3 0x00032aaaabe7cfd0 in ?? ()
#4 0x00002aaaabe7d12c in ?? ()
#5 0x00022aaa00000003 in ?? ()
#6 0x00002aaaabe7d0aa in ?? ()
#7 0x01000002abe7cff0 in ?? ()
#8 0x00002aaaabe7d02c in ?? ()
#9 0x0100000000000001 in ?? ()
#10 0x00000000014388e0 in ?? ()
#11 0x00007fff00000001 in ?? ()
#12 0x0000000000b870a2 in llvm::JIT::runFunction (this=0x1405b70,
F=0x14024e0, ArgValues=@0x7fffffffe050)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:395
#13 0x0000000000baa4c5 in llvm::ExecutionEngine::runFunctionAsMain
(this=0x1405b70, Fn=0x14024e0, argv=@0x13f06f8, envp=0x7fffffffe3b0)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:377
#14 0x00000000007ebd52 in main (argc=2, argv=0x7fffffffe398,
envp=0x7fffffffe3b0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:208
And a backtrace after this patch:
Program received signal SIGSEGV, Segmentation fault.
0x00002aaaabe7d1a8 in baz ()
(gdb) bt
#0 0x00002aaaabe7d1a8 in baz ()
#1 0x00002aaaabe7d12c in bar ()
#2 0x00002aaaabe7d0aa in foo ()
#3 0x00002aaaabe7d02c in main ()
#4 0x0000000000b870a2 in llvm::JIT::runFunction (this=0x1405b70,
F=0x14024e0, ArgValues=...)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/JIT/JIT.cpp:395
#5 0x0000000000baa4c5 in llvm::ExecutionEngine::runFunctionAsMain
(this=0x1405b70, Fn=0x14024e0, argv=..., envp=0x7fffffffe3c0)
at /home/rnk/llvm-gdb/lib/ExecutionEngine/ExecutionEngine.cpp:377
#6 0x00000000007ebd52 in main (argc=2, argv=0x7fffffffe3a8,
envp=0x7fffffffe3c0) at /home/rnk/llvm-gdb/tools/lli/lli.cpp:208
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@82418 91177308-0d34-0410-b5e6-96231b3b80d8
2009-09-20 23:52:43 +00:00
|
|
|
class Function;
|
|
|
|
class TargetMachine;
|
|
|
|
|
|
|
|
|
|
|
|
/// This class encapsulates information we want to send to the debugger.
|
|
|
|
///
|
|
|
|
struct DebugInfo {
|
|
|
|
uint8_t *FnStart;
|
|
|
|
uint8_t *FnEnd;
|
|
|
|
uint8_t *EhStart;
|
|
|
|
uint8_t *EhEnd;
|
|
|
|
|
|
|
|
DebugInfo() : FnStart(0), FnEnd(0), EhStart(0), EhEnd(0) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef DenseMap< const Function*, std::pair<std::string, jit_code_entry*> >
|
|
|
|
RegisteredFunctionsMap;
|
|
|
|
|
|
|
|
/// This class registers debug info for JITed code with an attached debugger.
|
|
|
|
/// Without proper debug info, GDB can't do things like source level debugging
|
|
|
|
/// or even produce a proper stack trace on linux-x86_64. To use this class,
|
|
|
|
/// whenever a function is JITed, create a DebugInfo struct and pass it to the
|
|
|
|
/// RegisterFunction method. The method will then do whatever is necessary to
|
|
|
|
/// inform the debugger about the JITed function.
|
|
|
|
class JITDebugRegisterer {
|
|
|
|
|
|
|
|
TargetMachine &TM;
|
|
|
|
|
|
|
|
/// FnMap - A map of functions that have been registered to the associated
|
|
|
|
/// temporary files. Used for cleanup.
|
|
|
|
RegisteredFunctionsMap FnMap;
|
|
|
|
|
|
|
|
/// MakeELF - Builds the ELF file in memory and returns a std::string that
|
|
|
|
/// contains the ELF.
|
|
|
|
std::string MakeELF(const Function *F, DebugInfo &I);
|
|
|
|
|
|
|
|
public:
|
|
|
|
JITDebugRegisterer(TargetMachine &tm);
|
|
|
|
|
|
|
|
/// ~JITDebugRegisterer - Unregisters all code and frees symbol files.
|
|
|
|
///
|
|
|
|
~JITDebugRegisterer();
|
|
|
|
|
|
|
|
/// RegisterFunction - Register debug info for the given function with an
|
|
|
|
/// attached debugger. Clients must call UnregisterFunction on all
|
|
|
|
/// registered functions before deleting them to free the associated symbol
|
|
|
|
/// file and unregister it from the debugger.
|
|
|
|
void RegisterFunction(const Function *F, DebugInfo &I);
|
|
|
|
|
|
|
|
/// UnregisterFunction - Unregister the debug info for the given function
|
|
|
|
/// from the debugger and free associated memory.
|
|
|
|
void UnregisterFunction(const Function *F);
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// UnregisterFunctionInternal - Unregister the debug info for the given
|
|
|
|
/// function from the debugger and delete any temporary files. The private
|
|
|
|
/// version of this method does not remove the function from FnMap so that it
|
|
|
|
/// can be called while iterating over FnMap.
|
|
|
|
void UnregisterFunctionInternal(RegisteredFunctionsMap::iterator I);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
} // end namespace llvm
|
|
|
|
|
|
|
|
#endif // LLVM_EXECUTION_ENGINE_JIT_DEBUGREGISTERER_H
|