Debugging in JIT Compiled Code¶
C++ Language Interoperability Layer - Debugging Guide¶
Overview¶
This guide provides comprehensive instructions for debugging CppInterOp applications using LLDB.
Prerequisites¶
Before proceeding with debugging, ensure you have the following tools installed:
LLDB: The LLVM debugger
LLVM/Clang: The C++ compiler and toolchain
CppInterOp: The C++ language interoperability library
The debugging tools should be available in your LLVM toolchain. On most systems, these are installed alongside your LLVM/Clang installation.
Setting Up Debug Environment¶
To effectively debug CppInterOp applications, you need to compile your code with debugging symbols and disable optimizations. This ensures that the debugger can accurately map between source code and machine instructions.
Compilation Flags
When compiling your CppInterOp application, use the following essential flags:
$CXX -I$CPPINTEROP_DIR/include -g -O0 -lclangCppInterOp -Wl,-rpath,$CPPINTEROP_DIR/build/lib
Flag Explanation:
-g: Includes debugging information in the executable-O0: Disables compiler optimizations for clearer debugging-I$CPPINTEROP_DIR/include: Includes CppInterOp headers-lclangCppInterOp: Links against the CppInterOp library-Wl,-rpath,$CPPINTEROP_DIR/build/lib: Sets runtime library path
Creating a Debug-Ready Test Program¶
Here’s a comprehensive example that demonstrates common CppInterOp usage patterns suitable for debugging:
#include <CppInterOp/CppInterOp.h>
#include <iostream>
void run_code(std::string code) {
Cpp::Declare(code.c_str());
}
int main(int argc, char *argv[]) {
Cpp::CreateInterpreter({"-gdwarf-4", "-O0"});
std::vector<Cpp::TCppScope_t> Decls;
std::string code = R"(
#include <iostream>
void f1() {
std::cout << "in f1 function" << std::endl;
}
std::cout << "In codeblock 1" << std::endl;
int a = 100;
int b = 1000;
)";
run_code(code);
code = R"(
f1();
)";
run_code(code);
return 0;
}
Program Structure Explanation:
This example demonstrates key debugging scenarios:
Interpreter Initialization: The
Cpp::CreateInterpretercall with debug flagsCode Declaration: Dynamic C++ code execution through
Cpp::DeclareMixed Execution: Combination of compiled and interpreted code paths
Variable Scoping: Local variables in both compiled and interpreted contexts
Debugging Strategies¶
Debugging Compiled Code
For debugging the main executable and compiled portions of your CppInterOp application:
lldb /path/to/executable
(lldb) settings set plugin.jit-loader.gdb.enable on
(lldb) breakpoint set --name f1
(lldb) r
1 location added to breakpoint 1
In codeblock 1
Process 49132 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x000000010217c008 JIT(0x10215c218) f1() at input_line_1:4:13
Note
Ensure the JIT loader is enabled to allow LLDB to debug dynamically generated code.
Use
settings set plugin.jit-loader.gdb.enable onto enable JIT debugging.Set breakpoints in both compiled and interpreted code using
breakpoint set --name function_name.
Some Caveats
For each block of code, there is a file named
input_line_<execution_number>that contains the code block. This file is in-memory and thus cannot be directly accessed.However, generating actual input_line_<number> files on disk will let LLDB pick them up and render the source content correctly during debugging. This can be achieved by modifying run_code as follows:
void run_code(std::string code) {
static size_t i = 0;
i++;
std::string filename = "input_line_" + std::to_string(i);
std::ofstream file(filename);
file << code;
file.close();
Cpp::Declare(code.c_str());
}
Note
You’ll need to manually delete these files later to avoid cluttering the filesystem.
If a function is called from different cell, then it may take multiple step-ins to reach the function definition due to the way CppInterOp handles code blocks.
Advanced Debugging Techniques¶
Using LLDB with VS Code
For IDE-based debugging:
Install the LLDB extension in VS Code
Create a
launch.jsonconfiguration:
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb-dap",
"request": "launch",
"name": "Debug",
"program": "/path/to/executable",
"sourcePath" : ["${workspaceFolder}"],
"cwd": "${workspaceFolder}",
"initCommands": [
"settings set plugin.jit-loader.gdb.enable on", // This is crucial
]
},
]
}
Further Reading¶
LLDB Documentation: LLDB Debugger
CppInterOp Source: CppInterOp Repository
Clang Documentation: Clang Compiler
LLVM Debugging Guide: LLVM Debug Info