- needed for faster Node.js builds
brew install ninja
- cloning latest and checking out
v0.12
branch to get the JIT goodies that were available with v8v3.25.30
which came with nodev0.11.13
git clone https://github.com/joyent/node && cd node
git checkout v0.12
./configure --gdb --without-snapshot --ninja
- configuring to include
--gdb
in order to enable gdb jit interface - configuring
--without-snapshot
in order to build without snapshotting V8 libraries --ninja
is optional, but recommended if we want faster builds
ninja -C out/Debug
ln -s out/Debug/node node_g
- building in debug mode in order to prevent function inlining, etc.
- follow these steps to create
lldb_codesign
certificate
git clone https://github.com/llvm-mirror/lldb.git && cd lldb
xcodebuild -parallelizeTargets -jobs 8 -configuration Debug
- on Mac, the easiest way to build is via
xcodebuild
from thelldb
repo - it will pull down the remaining llvm pieces it needs to build
- note that it expects the
lldb_codesign
certificate to be available - building lldb in debug mode as well for maximum insight
0001-jit-patching-in-order-to-support-JIT-on-OSX.patch
diff --git a/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp
index cd303cb..06da78c 100644
--- a/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp
+++ b/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp
@@ -377,9 +377,7 @@ JITLoaderSP
JITLoaderGDB::CreateInstance(Process *process, bool force)
{
JITLoaderSP jit_loader_sp;
- ArchSpec arch (process->GetTarget().GetArchitecture());
- if (arch.GetTriple().getVendor() != llvm::Triple::Apple)
- jit_loader_sp.reset(new JITLoaderGDB(process));
+ jit_loader_sp.reset(new JITLoaderGDB(process));
return jit_loader_sp;
}
- this patch removes the line that disables JIT support for OSX
➝ ./build/Debug/lldb
(lldb) log enable lldb jit
(lldb) target create "/Users/thlorenz/dev/js/node/node_g"
(lldb) settings set -- target.run-args "--gdbjit" "--gdbjit-full" "/Users/thlorenz/dev/js/node/benchmark/http_simple.js"
(lldb) b -n uv_fs_read -c cb==NULL
(lldb) r
- enable logging related to JIT in order to confirm that
lldb
does look for and find the JIT register hook and that v8 provides it and then calls that hook with JIT information - then we create the
node
target and provide--gdbjit --gdbjit-full
flags as well as a script to run - finally we run the target
➝ ./build/Debug/lldb -S ./jit.txt
(lldb) command source -s 1 './jit.txt'
(lldb) r
- in order to not have to type this in manually every time, add the below to
jit.txt
and invokelldb
via➝ ./build/Debug/lldb -S ./jit.txt
log enable lldb jit
target create "/Users/thlorenz/dev/js/node/node_g"
settings set -- target.run-args "--gdbjit" "--gdbjit-full" "/Users/thlorenz/dev/js/node/benchmark/http_simple.js"
b -n uv_fs_read -c cb==NULL
- when we run the target the output looks similar to the below
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
Process 65921 launched: '/Users/thlorenz/dev/js/node/node_g' (x86_64)
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
JITLoaderGDB::SetJITBreakpoint looking for JIT register hook
[ .. ]
JITLoaderGDB::SetJITBreakpoint setting JIT breakpoint
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x101d0b710 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x101d0b330 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x101d0b4c0 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x1032007b0 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x101d0c210 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x101d0ba80 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x103300640 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x103200320 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x101d0bc50 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x1030016e0 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x101d0c3a0 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
JITLoaderGDB::ReadJITDescriptorImpl registering JIT entry at 0x1032000f0 (368 bytes)
JITLoaderGDB::JITDebugBreakpointHit hit JIT breakpoint
[..]
- eventually the breakpoint hits and the last frame (frame 7) shows a
JIT
address
Process 84539 stopped
* thread #1: tid = 0x36ec50, 0x000000010097af1c node_g`uv_fs_read(loop=0x0000000100eab710, req=0x00007fff5fbfe6c0, file=10, bufs=0x00007fff5fbfe8c8, nbufs=1, off=-1, cb=0x0000000000000000) + 44 at fs.c:1037, queue = 'com.apple.main-thread, stop reason = breakpoint 1.1
frame #0: 0x000000010097af1c node_g`uv_fs_read(loop=0x0000000100eab710, req=0x00007fff5fbfe6c0, file=10, bufs=0x00007fff5fbfe8c8, nbufs=1, off=-1, cb=0x0000000000000000) + 44 at fs.c:1037
1034 unsigned int nbufs,
1035 int64_t off,
1036 uv_fs_cb cb) {
-> 1037 INIT(READ);
1038 req->file = file;
1039
1040 req->nbufs = nbufs;
(lldb) bt
* thread #1: tid = 0x36ec50, 0x000000010097af1c node_g`uv_fs_read(loop=0x0000000100eab710, req=0x00007fff5fbfe6c0, file=10, bufs=0x00007fff5fbfe8c8, nbufs=1, off=-1, cb=0x0000000000000000) + 44 at fs.c:1037, queue = 'com.apple.main-thread, stop reason = breakpoint 1.1
* frame #0: 0x000000010097af1c node_g`uv_fs_read(loop=0x0000000100eab710, req=0x00007fff5fbfe6c0, file=10, bufs=0x00007fff5fbfe8c8, nbufs=1, off=-1, cb=0x0000000000000000) + 44 at fs.c:1037
frame #1: 0x00000001008ea2ed node_g`node::Read(args=0x00007fff5fbfe9c0) + 1565 at node_file.cc:953
frame #2: 0x000000010024bf1c node_g`v8::internal::FunctionCallbackArguments::Call(this=0x00007fff5fbfeac8, f=0x00000001008e9cd0)(v8::FunctionCallbackInfo<v8::Value> const&)) + 124 at arguments.cc:33
frame #3: 0x0000000100289afc node_g`v8::internal::Object* v8::internal::HandleApiCallHelper<false>(args=BuiltinArguments<1> at 0x00007fff5fbfebd8, isolate=0x0000000102004c00)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) + 1196 at builtins.cc:1144
frame #4: 0x0000000100289645 node_g`v8::internal::Builtin_Impl_HandleApiCall(args=v8::internal::(anonymous namespace)::HandleApiCallArgumentsType at 0x00007fff5fbfec20, isolate=0x0000000102004c00)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) + 53 at builtins.cc:1161
frame #5: 0x0000000100283590 node_g`v8::internal::Builtin_HandleApiCall(args_length=7, args_object=0x00007fff5fbfece0, isolate=0x0000000102004c00) + 80 at builtins.cc:1160
frame #6: 0x0000236a109060a2
frame #7: 0x0000236a1099ff0a JIT(0x101d0c750)
- we can now dump the memory at this address to a file
(lldb) memory read --outfile /tmp/jit.o -c 368 --binary 0x101d0c750
368 bytes written to '/tmp/jit.o'
- and dwarfdump it only to find no useful info :(
➝ dwarfdump /tmp/jit.o
----------------------------------------------------------------------
File: /tmp/jit.o (x86_64)
----------------------------------------------------------------------
.debug_info contents:
< EMPTY >
On both systems I built from master of the github mirror.
- version used on Linux
lldb version 3.6.0 ([email protected]:llvm-mirror/lldb.git revision 9e13102328c4e59b6f6a29a8fceae863e0061d8f clang revision f5869c4ac58bc8d1ec1226a8d2cbf6ba8f7f83d1 llvm revision 980495660946c35da9cd53a9cfbb1e7cced892e3)
- version used on OSX
lldb-330.99.0 -- 9e13102328c4e59b6f6a29a8fceae863e0061d8f
- on Linux instead of showing
JIT(0x...)
it did actually show the JavaScript function name and if I jump to that frame I was able to list the source code - at one point on Linux (not anymore) it showed filename and I was even able to step through the JavaScript on Linux
- currently on Linux with the version listed above I get output similar to
frame #10: 0x000006b5fd5de40f JIT(0x1776050) createWriteReq + 815
which is more informative, but not quite what it used to be when I tried this for the first time - why is this different on OSX and why are all the Mach-O files written to the JIT memory address without any useful information?
- is the above expected or is there a bug inside the v8 JIT info provider?
- for reference the v8 JIT implementation can be found inside
gdb-jit.cc
Hi Thorsten. This might be interesting in this context nodejs/node#5577.
--gdbjit
was kind of brokenfor embedders, if I read it right. I findxcode
/lldb
debugging already really nice and hope there will be some more work of the community to hook in the JS side of things.