Skip to content

Instantly share code, notes, and snippets.

@thlorenz
Last active April 21, 2021 09:56
Show Gist options
  • Save thlorenz/30bf0a3f67b1d97b2945 to your computer and use it in GitHub Desktop.
Save thlorenz/30bf0a3f67b1d97b2945 to your computer and use it in GitHub Desktop.
lldb JIT on OSX (showing no useful info at this point)

Installation

Ninja

  • needed for faster Node.js builds
brew install ninja

Node.js

  • cloning latest and checking out v0.12 branch to get the JIT goodies that were available with v8 v3.25.30 which came with node v0.11.13

Clone

git clone https://github.com/joyent/node && cd node
git checkout v0.12

Configure

./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

Build

ninja -C out/Debug
ln -s out/Debug/node node_g
  • building in debug mode in order to prevent function inlining, etc.

lldb

Prepare Code Signing

Cloning and Building

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 the lldb 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

Patching and Rebuilding

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

lldb steps

Loading and Preparing Target

➝  ./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 invoke lldb 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

Running the Target

  • 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
[..]

Showing Backtrace and dumping JIT info

  • 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 >

lldb version

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

Questions

  • 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?

Reference

  • for reference the v8 JIT implementation can be found inside gdb-jit.cc
@eljefedelrodeodeljefe
Copy link

Hi Thorsten. This might be interesting in this context nodejs/node#5577. --gdbjit was kind of brokenfor embedders, if I read it right. I find xcode / lldb debugging already really nice and hope there will be some more work of the community to hook in the JS side of things.

@yurydelendik
Copy link

Submitted and landed https://reviews.llvm.org/D57689

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment