Skip to content

Conversation

@pcc
Copy link
Contributor

@pcc pcc commented Aug 30, 2025

No description provided.

Created using spr 1.3.6-beta.1
@pcc pcc requested review from fmayer and nikic August 30, 2025 00:31
@llvmbot
Copy link
Member

llvmbot commented Aug 30, 2025

@llvm/pr-subscribers-llvm-ir

Author: Peter Collingbourne (pcc)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/156128.diff

4 Files Affected:

  • (added) llvm/docs/DebuggingLLVM.rst (+110)
  • (modified) llvm/docs/GettingStartedTutorials.rst (+4)
  • (modified) llvm/docs/ProgrammersManual.rst (+1-10)
  • (modified) llvm/lib/IR/AsmWriter.cpp (+2)
diff --git a/llvm/docs/DebuggingLLVM.rst b/llvm/docs/DebuggingLLVM.rst
new file mode 100644
index 0000000000000..a348ad8186ed1
--- /dev/null
+++ b/llvm/docs/DebuggingLLVM.rst
@@ -0,0 +1,110 @@
+==============
+Debugging LLVM
+==============
+
+This document is a collection of tips and tricks for debugging LLVM
+using a source-level debugger. The assumption is that you are trying to
+figure out the root cause of a miscompilation in the program that you
+are compiling.
+
+Extract and rerun the compile command
+=====================================
+
+Extract the Clang command that produces the buggy code. The way to do
+this depends on the build system used by your program.
+
+- For Ninja-based build systems, you can pass ``-t commands`` to Ninja
+  and filter the output by the targeted source file name. For example:
+  ``ninja -t commands myprogram | grep path/to/file.cpp``.
+
+- For Bazel-based build systems using Bazel 9 or newer (not released yet
+  as of this writing), you can pass ``--output=commands`` to the ``bazel
+  aquery`` subcommand for a similar result. For example: ``bazel aquery
+  --output=commands 'deps(//myprogram)' | grep path/to/file.cpp``. Build
+  commands must generally be run from a subdirectory of the source
+  directory named ``bazel-$PROJECTNAME``. Bazel typically makes the target
+  paths of ``-o`` and ``-MF`` read-only when running commands outside
+  of a build, so it may be necessary to change or remove these flags.
+
+- A method that should work with any build system is to build your program
+  under `Bear <https://github.com/rizsotto/Bear>`_ and look for the
+  compile command in the resulting ``compile_commands.json`` file.
+
+Once you have the command you can use the following steps to debug
+it. Note that any flags mentioned later in this document are LLVM flags
+so they must be prefixed with ``-mllvm`` when passed to the Clang driver,
+e.g. ``-mllvm -print-after-all``.
+
+Understanding the source of the issue
+=====================================
+
+If you have a miscompilation introduced by a pass, it is frequently
+possible to identify the pass where things go wrong by searching a
+pass-by-pass printout, which is enabled using the ``-print-after-all``
+flag. Pipe stderr into ``less`` (append ``2>&1 | less`` to command
+line) and use text search to move between passes (e.g. type ``/Dump
+After<Enter>``, ``n`` to move to next pass, ``N`` to move to previous
+pass). If the name of the function containing the buggy IR is known, you
+can filter the output by passing ``-filter-print-funcs=functionname``. You
+can sometimes pass ``-debug`` to get useful details about what passes
+are doing.
+
+Creating a debug build of LLVM
+==============================
+
+The subsequent debugging steps require a debug build of LLVM. Pass the
+``-DCMAKE_BUILD_TYPE=Debug`` to CMake in a separate build tree to create
+a debug build.
+
+Understanding where an instruction came from
+============================================
+
+A common debugging task involves understanding which part of the code
+introduced a buggy instruction. The pass-by-pass dump is sometimes enough,
+but for complex or unfamiliar passes, more information is often required.
+
+The first step is to record a run of the debug build of Clang under `rr
+<https://rr-project.org>`_ passing the LLVM flag ``-print-inst-addrs``
+together with ``-print-after-all`` and any desired filters. This will
+cause each instruction printed by LLVM to be suffixed with a comment
+showing the address of the ``Instruction`` object. You can then replay
+the run of Clang with ``rr replay``. Because ``rr`` is deterministic,
+the instruction will receive the same address during the replay, so
+you can break on the instruction's construction using a conditional
+breakpoint that checks for the address printed by LLVM, with commands
+such as the following:
+
+.. code-block:: text
+
+    b Instruction::Instruction
+    cond 1 this == 0x12345678
+
+When the breakpoint is hit, you will likely be at the location where
+the instruction was created, so you can unwind the stack with ``bt``
+to see the stack trace. It is also possible that an instruction was
+created multiple times at the same address, so you may need to continue
+until reaching the desired location, but in the author's experience this
+is unlikely to occur.
+
+Identifying the source locations of instructions
+================================================
+
+To identify the source location that caused a particular instruction
+to be created, you can pass the LLVM flag ``-print-inst-debug-locs``
+and each instruction printed by LLVM is suffixed with the file and line
+number of the instruction according to the debug information. Note that
+this requires debug information to be enabled (e.g. pass ``-g`` to Clang).
+
+GDB pretty printers
+===================
+
+A handful of `GDB pretty printers
+<https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html>`__ are
+provided for some of the core LLVM libraries. To use them, execute the
+following (or add it to your ``~/.gdbinit``)::
+
+  source /path/to/llvm/src/utils/gdb-scripts/prettyprinters.py
+
+It also might be handy to enable the `print pretty
+<http://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_57.html>`__ option to
+avoid data structures being printed as a big block of text.
diff --git a/llvm/docs/GettingStartedTutorials.rst b/llvm/docs/GettingStartedTutorials.rst
index 55060343ba361..61253e39c34d4 100644
--- a/llvm/docs/GettingStartedTutorials.rst
+++ b/llvm/docs/GettingStartedTutorials.rst
@@ -11,6 +11,7 @@ For those new to the LLVM system.
    GettingStarted
    GettingStartedVS
    ProgrammersManual
+   DebuggingLLVM
    tutorial/index
    MyFirstTypoFix
 
@@ -27,6 +28,9 @@ For those new to the LLVM system.
   Introduction to the general layout of the LLVM sourcebase, important classes
   and APIs, and some tips & tricks.
 
+:doc:`DebuggingLLVM`
+  Provides information about how to debug LLVM.
+
 :doc:`Frontend/PerformanceTips`
    A collection of tips for frontend authors on how to generate IR
    which LLVM is able to effectively optimize.
diff --git a/llvm/docs/ProgrammersManual.rst b/llvm/docs/ProgrammersManual.rst
index 1e1e5b3e55b0b..8c50a047706fd 100644
--- a/llvm/docs/ProgrammersManual.rst
+++ b/llvm/docs/ProgrammersManual.rst
@@ -2641,16 +2641,7 @@ with an ``assert``).
 Debugging
 =========
 
-A handful of `GDB pretty printers
-<https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html>`__ are
-provided for some of the core LLVM libraries. To use them, execute the
-following (or add it to your ``~/.gdbinit``)::
-
-  source /path/to/llvm/src/utils/gdb-scripts/prettyprinters.py
-
-It also might be handy to enable the `print pretty
-<http://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_57.html>`__ option to
-avoid data structures being printed as a big block of text.
+See :doc:`Debugging LLVM <DebuggingLLVM>`.
 
 .. _common:
 
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index dc6d599fa9585..094678f32af2b 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -88,6 +88,8 @@
 
 using namespace llvm;
 
+// See https://llvm.org/docs/DebuggingLLVM.html for why these flags are useful.
+
 static cl::opt<bool>
     PrintInstAddrs("print-inst-addrs", cl::Hidden,
                    cl::desc("Print addresses of instructions when dumping"));


If you have a miscompilation introduced by a pass, it is frequently
possible to identify the pass where things go wrong by searching a
pass-by-pass printout, which is enabled using the ``-print-after-all``
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention -print-changed (and maybe -print-module-scope)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a link to PrintPasses.cpp for the list of options. I think the options I mentioned are the most important ones.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fwiw I would say -print-changed is more useful than -print-after-all (kind of the same but more compressed output).


.. code-block:: text

b Instruction::Instruction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b llvm::Value::Value if this == 0x12345678 seems neater.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or llvm:: Instruction:: Instruction)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL that you can specify the condition when you set the breakpoint. Done.

I've never needed to specify llvm::.

introduced a buggy instruction. The pass-by-pass dump is sometimes enough,
but for complex or unfamiliar passes, more information is often required.

The first step is to record a run of the debug build of Clang under `rr
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on what you need, RelWithDebInfo might be enough as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but you can't usually tell ahead of time that you're not going to hit an <optimized out> so it seems simplest to use a full debug build for this.

At some point I want other build systems to get something like Bazel's --per_file_copt for easily opting TUs into debug info without having to rebuild the whole thing but that's getting a bit off topic.

source /path/to/llvm/src/utils/gdb-scripts/prettyprinters.py

It also might be handy to enable the `print pretty
<http://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_57.html>`__ option to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Created using spr 1.3.6-beta.1
@pcc pcc merged commit 82978df into main Sep 3, 2025
10 checks passed
@pcc pcc deleted the users/pcc/spr/add-documentation-on-debugging-llvm branch September 3, 2025 21:53
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Sep 3, 2025
Reviewers: fmayer, nikic

Reviewed By: fmayer

Pull Request: llvm/llvm-project#156128
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants