How to Debug LLVM Using Crash Dumps: A Step-by-Step Guide

LLVM automatically registers signal handlers via llvm::sys::PrintStackTraceOnErrorSignal that emit stack traces on fatal signals, enabling developers to load OS-generated core dumps into LLDB or GDB to inspect the complete compiler state at the moment of failure.

Debugging the LLVM compiler infrastructure requires specialized techniques when crashes occur deep within optimization passes or target code generation. When you need to debug LLVM using crash dumps, you leverage the framework's built-in crash recovery architecture to capture the full memory state of a failed compilation process. This methodology allows precise root cause analysis of segmentation faults and assertion failures by examining the exact LLVM IR and variable states present at the crash point.

Prerequisites for Capturing LLVM Crash Dumps

Before analyzing failures, you must configure your system and LLVM build to generate actionable crash data.

Building LLVM with Debug Symbols

Debug symbols allow the debugger to map machine addresses back to source lines, IR instructions, and variable names. Configure your build with assertions enabled:

cmake -S llvm -B build \
  -DCMAKE_BUILD_TYPE=Debug \
  -DLLVM_ENABLE_ASSERTIONS=ON \
  -DLLVM_ENABLE_PROJECTS="clang"
ninja -C build

Enabling System-Level Core Dumps

On Linux systems, the operating system must be configured to write core files when a process receives a fatal signal. Set this limit before running any LLVM tools:

ulimit -c unlimited

This ensures that when LLVM crashes, the system creates a core.<pid> file containing the complete memory image of the process.

How LLVM Captures Crash Information

LLVM's Support library implements a centralized crash handling infrastructure that operates automatically when integrated tools start.

Signal Handling in InitLLVM.cpp

The entry point InitLLVM in llvm/lib/Support/InitLLVM.cpp registers the fatal signal handler by calling llvm::sys::PrintStackTraceOnErrorSignal. According to the LLVM source code, this initialization routine installs handlers for SIGSEGV, SIGABRT, and other fatal signals that invoke llvm::sys::PrintStackTrace to print a human-readable backtrace to stderr before the process terminates.

The CrashRecoveryContext Mechanism

For scenarios requiring crash isolation—such as testing individual passes—the CrashRecoveryContext class in llvm/lib/Support/CrashRecoveryContext.cpp provides a RAII interface that catches crashes within a specific scope. This mechanism runs vulnerable code in a separate context, executes cleanup callbacks if a crash occurs, and optionally re-throws the signal, allowing LLVM tools to recover from failures without terminating the entire process.

Analyzing LLVM Crash Dumps Step-by-Step

Once you have generated a core dump, follow this workflow to extract diagnostic information.

Reproducing the Crash

Execute the LLVM tool with the input that triggers the failure. The PrintStackTraceOnErrorSignal handler will output the stack trace to the terminal while the OS simultaneously writes the core file:

./build/bin/opt -load-pass-plugin=MyPass.so -passes=my-pass input.ll

# Output: Stack dump:

# 0.  Running pass 'My Pass' on function '@foo'

# Segmentation fault (core dumped)

Loading the Core Dump into a Debugger

Use LLDB or GDB to restore the crashed process state. Load both the debug-instrumented binary and the core file:

lldb ./build/bin/opt -c core.<pid>

Within the debugger, analyze the failure:

(lldb) bt all              # Display all thread backtraces

(lldb) frame select 3      # Navigate to the crashing frame

(lldb) print *F            # Inspect LLVM IR function objects

(lldb) image dump sections # Verify debug info sections are loaded

Symbolizing Addresses with llvm-symbolizer

If you have a raw address from a stack trace but lack debug symbols in the core file, use the llvm-symbolizer tool to resolve instruction pointers to source locations:

llvm-symbolizer -obj ./build/bin/opt -eip 0x0000000000abcdef

This command queries the binary's debug information to return the corresponding function name, file path, and line number.

Advanced Crash Debugging Techniques

Beyond basic core dump analysis, LLVM provides facilities for automated crash reproduction and safe failure isolation.

Using CrashRecoveryContext in Custom Passes

When developing custom LLVM passes, wrap potentially unsafe operations in a CrashRecoveryContext to prevent the entire compiler from crashing on unexpected inputs. The implementation in llvm/lib/Support/CrashRecoveryContext.cpp allows you to attempt dangerous transformations safely:

#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/IR/PassManager.h"

struct SafePass : PassInfoMixin<SafePass> {
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
    llvm::CrashRecoveryContext CRC;
    bool Success = CRC.RunSafely([&] {
      // Attempt operation that might dereference null or corrupt memory
      processUnsafeTransformation(F);
    });
    
    if (!Success) {
      llvm::errs() << "Crash detected in pass – aborting safely.\n";
      return PreservedAnalyses::none();
    }
    return PreservedAnalyses::all();
  }
};

This pattern is extensively used in the MLIR PassManager and LLVM unit tests to ensure flaky transformations do not crash the test harness.

Generating Crash Reproducers

To create minimal test cases that reproduce crashes, LLVM offers crash-reproducer generation integrated into the PassManager. As implemented in mlir/lib/Pass/PassCrashRecovery.cpp, you can enable the PassCrashReproducerGenerator by passing:

./build/bin/opt -enable-crash-reproducer-generation=repro.mlir \
  -passes=my-pass input.ll

When a pass crashes, this facility dumps a minimal IR file containing only the constructs necessary to trigger the failure. This reproducer file is invaluable for filing detailed bug reports or creating regression tests.

Summary

  • Debug builds are essential: Compile LLVM with -DCMAKE_BUILD_TYPE=Debug and -DLLVM_ENABLE_ASSERTIONS=ON to ensure line number and IR mapping information is available in crash dumps.
  • Core dumps capture complete state: Enable ulimit -c unlimited to preserve the full process memory image when PrintStackTraceOnErrorSignal fires.
  • Debuggers restore context: Load the binary and core file into LLDB or GDB to inspect stack frames, LLVM IR values, and local variables at the exact crash point.
  • Use llvm-symbolizer to resolve raw addresses from stack traces into human-readable function names and source locations.
  • Leverage CrashRecoveryContext in custom passes to isolate failures and prevent complete compiler crashes during development.
  • Generate crash reproducers using the PassManager's built-in facilities to create minimal test cases automatically.

Frequently Asked Questions

How do I enable core dumps on Linux when debugging LLVM?

Run ulimit -c unlimited in your shell before executing the LLVM tool. This removes the size limit on core files, ensuring the operating system writes a complete memory dump to core.<pid> when LLVM receives a fatal signal. You may also need to configure /proc/sys/kernel/core_pattern to control where core files are saved.

What is the difference between PrintStackTraceOnErrorSignal and CrashRecoveryContext?

PrintStackTraceOnErrorSignal, registered in llvm/lib/Support/InitLLVM.cpp, is a global signal handler that prints a backtrace and terminates the process when a fatal signal occurs. In contrast, CrashRecoveryContext in llvm/lib/Support/CrashRecoveryContext.cpp is a scoped recovery mechanism that catches crashes within a specific code block, executes cleanup code, and allows the program to continue execution rather than terminating.

How can I generate a minimal test case from an LLVM crash?

Pass the -enable-crash-reproducer-generation=<filename> option to the PassManager when running opt or MLIR tools. As implemented in mlir/lib/Pass/PassCrashRecovery.cpp, this triggers the PassCrashReproducerGenerator to dump a reduced IR file upon crash, containing only the necessary instructions to reproduce the failure.

Which debugger works best with LLVM crash dumps?

Both LLDB and GDB work effectively with LLVM core dumps. LLDB often provides better integration with LLVM's data formatters for SmallVector, StringRef, and other LLVM-specific types. Ensure your debugger matches the architecture of the LLVM binary (e.g., 64-bit debugger for 64-bit builds) and that the binary was compiled with debug symbols to resolve source-level information.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →