| # LLDB internals |
| |
| LLDB's debug info processing relies on a set of extensible interfaces largely defined in |
| [lldb/src/Plugins][lldb_plugins]. |
| These are meant to allow third-party compiler developers to add |
| language support that is loaded at run-time by LLDB, but at time of writing (Nov 2025) the public |
| API has not been settled on, so plugins exist either in LLDB itself or in standalone forks of LLDB. |
| |
| [lldb_plugins]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins |
| |
| Typically, language support will be written as a pipeline of these plugins: `*ASTParser` -> |
| `TypeSystem` -> `ExpressionParser`/`Language`. |
| |
| Here are some existing implementations of LLDB's plugin API: |
| |
| * [Apple's fork with support for Swift](https://github.com/swiftlang/llvm-project) |
| * [CodeLLDB's former fork with support for Rust](https://archive.softwareheritage.org/browse/origin/directory/?branch=refs/heads/codelldb/16.x&origin_url=https://github.com/vadimcn/llvm-project&path=lldb/source/Plugins/TypeSystem/Rust×tamp=2023-09-11T04:55:10Z) |
| * [A work in progress reimplementation of Rust support](https://github.com/Walnut356/llvm-project/tree/lldbrust/19.x) |
| * [A Rust expression parser plugin](https://github.com/tromey/lldb/tree/a0fc10ce0dacb3038b7302fff9f6cb8cb34b37c6/source/Plugins/ExpressionParser/Rust). |
| This was written before the `TypeSystem` API was created. |
| Due to the freeform nature of expression parsing, the |
| underlyng lexing, parsing, function calling, etc. should still offer valuable insights. |
| |
| ## Rust support and TypeSystemClang |
| |
| As mentioned in the debug info overview, LLDB has partial Rust support. |
| To further clarify, |
| Rust uses the plugin-pipeline that was built for C/C++ |
| (though it contains some helpers for Rust enum types), |
| which relies directly on the `clang` compiler's representation of types. |
| This imposes heavy restrictions on how much we can change |
| when LLDB's output doesn't match what we want. |
| Some workarounds can help, but at the end of the day, |
| Rust's needs are secondary compared to making sure |
| C and C++ compilation and debugging work correctly. |
| |
| LLDB is receptive to adding a `TypeSystemRust`, but it is a massive undertaking. |
| This section serves to not only document how we currently interact with [`TypeSystemClang`], |
| but also as light guidance on implementing a `TypeSystemRust` in the future. |
| |
| [`TypeSystemClang`]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/TypeSystem/Clang |
| |
| It is worth noting that a `TypeSystem` directly interacting with the target language's compiler is |
| the intention, but it is not a requirement. |
| One can create all the necessary supporting types within their plugin implementation. |
| |
| > Note: LLDB's documentation, including comments in the source code, is pretty sparse. Trying to |
| > understand how language support works by reading `TypeSystemClang`'s implementation is somewhat |
| > difficult due to the added requirement of understanding the `clang` compiler's internals. It is |
| > recommended to look at the 2 `TypeSystemRust` implementations listed above, as they are written |
| > "from scratch" without leveraging a compiler's type representation. They are relatively close to |
| > the minimum necessary to implement language support. |
| |
| ## DWARF vs PDB |
| |
| LLDB is unique in being able to handle both DWARF and PDB debug information. |
| This does come with some added complexity. |
| To complicate matters further, PDB support is split between `dia`, which |
| relies on the `msdia140.dll` library distributed with Visual Studio, and `native`, which is written |
| from scratch using publicly available information about the PDB format. |
| |
| > Note: `dia` was the default up to LLDB version 21. `native` is the new default as of |
| > LLDB 22's release. There are plans to deprecate and completely remove the `dia`-based plugins. As |
| > such, only `native` parsing will be discussed below. For progress, please see |
| > [this discourse thread][dia_discourse] and the relevant [tracking issue][dia_tracking]. |
| > |
| > `native` can be toggled via the `plugin.symbol-file.pdb.reader` setting added in LLDB 22 or using |
| > the environment variable `LLDB_USE_NATIVE_PDB_READER=0/1` |
| |
| [dia_discourse]: https://discourse.llvm.org/t/rfc-removing-the-dia-pdb-plugin-from-lldb/87827 |
| [dia_tracking]: https://github.com/llvm/llvm-project/issues/114906 |
| |
| ## Debug node parsing |
| |
| The first step is to process the raw debug nodes into something usable. |
| This primarily occurs in the [`DWARFASTParser`][dwarf_ast] and [`PdbAstBuilder`][pdb_ast] classes. |
| These classes are fed a deserialized form of the debug info |
| generated from [`SymbolFileDWARF`][sf_dwarf] and [`SymbolFileNativePDB`][sf_pdb] respectively. |
| The `SymbolFile` implementers make almost no |
| transformations to the underlying debug info before passing it to the parsers. |
| For both PDB and DWARF, the debug info is read using LLVM's debug info handlers. |
| |
| [dwarf_ast]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/SymbolFile/DWARF |
| [pdb_ast]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/SymbolFile/NativePDB |
| [sf_dwarf]: https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h |
| [sf_pdb]: https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h |
| |
| The parsers translate the nodes into more convenient formats for LLDB's purposes. |
| For `clang`, these formats are `clang::QualType`, `clang::Decl`, and `clang::DeclContext`, |
| which are the types `clang` uses internally when compiling C and C++. |
| Again, using the compiler's representation of types is not a |
| requirement, but the plugin system was built with it as a possibility. |
| |
| > Note: The above types will be referred to language-agnostically as `LangType`, `Decl`, and |
| `DeclContext` when the specific implementation details of `TypeSystemClang` are not relevant. |
| |
| `LangType` represents a type. |
| This includes information such as the name of the type, the size and |
| alignment, its classification (e.g. struct, primitive, pointer), its qualifiers (e.g. |
| `const`, `volatile`), template arguments, function argument and return types, etc. [Here][rust_type] |
| is an example of what a `RustType` might look like. |
| |
| [rust_type]: https://github.com/Walnut356/llvm-project/blob/13bcfd502452606d69faeea76aec3a06db554af9/lldb/source/Plugins/TypeSystem/Rust/TypeSystemRust.h#L618 |
| |
| `Decl` represents any kind of declaration. |
| It could be a type, a variable, a static field of a |
| struct, the value that a static or const is initialized with, etc. |
| |
| `DeclContext` more or less represents a scope. |
| `DeclContext`s typically contain `Decl`s and other |
| `DeclContexts`, though the relationship isn't that straight forward. |
| For example, a function can be both a `Decl` (because function signatures are types), |
| **and** a `DeclContext` |
| (because functions contain variable declarations, nested functions declarations, etc.). |
| |
| The translation process can be quite verbose, but is usually straightforward. |
| Much of the work here is dependant on the exact information needed to fill out `LangType`, |
| `Decl`, and `DeclContext`. |
| |
| Once a node is translated, a pointer to it is type-erased (`void*`) and wrapped in `CompilerType`, |
| `CompilerDecl`, or `CompilerDeclContext`. |
| These wrappers associate the them with the `TypeSystem` that owns them. |
| Methods on these objects delegates to the `TypeSystem`, which casts the `void*` back |
| to the appropriate `LangType*`/`Decl*`/`DeclContext*` and operates on the internals. |
| In Rust terms, |
| the relationship looks something like this: |
| |
| ```Rust |
| struct CompilerType { |
| inner_type: *mut c_void, |
| type_system: Arc<dyn TypeSystem>, |
| } |
| |
| impl CompilerType { |
| pub fn get_byte_size(&self) -> usize { |
| self.type_system.get_byte_size(self.lang_type) |
| } |
| |
| } |
| |
| ... |
| |
| impl TypeSystem for TypeSystemLang { |
| pub fn get_byte_size(lang_type: *mut c_void) -> usize { |
| let lang_type = lang_type as *mut LangType; |
| |
| // Operate on the internals of the LangType to |
| // determine its size |
| ... |
| } |
| } |
| ``` |
| |
| ## Type systems |
| |
| The [`TypeSystem` interface][ts_interface] has 3 major purposes: |
| |
| [ts_interface]: https://github.com/llvm/llvm-project/blob/main/lldb/include/lldb/Symbol/TypeSystem.h#L69 |
| |
| 1. Act as the "sole authority" of a language's types. |
| This allows the type system to be added to LLDB's "pool" of type systems. |
| When an executable is loaded, the target language is determined, and |
| the pool is queried to find a `TypeSystem` that claims it can handle the language. |
| One can also use the `TypeSystem` to retrieve the backing `SymbolFile`, |
| search for types, and synthesize basic types |
| that might not exist in the debug info (e.g. primitives, arrays-of-`T`, pointers-to-`T`). |
| 2. Manage the lifetimes of the `LangType`, `Decl`, and `DeclContext` objects |
| 3. Customize the "defaults" of how those types appear and how they can be interacted with. |
| |
| The first two functions are pretty straightforward so we will focus on the third. |
| |
| Many of the functions in the `TypeSystem` interface will look familiar if you have worked with the |
| visualizer scripts. |
| These functions underpin `SBType` the `SBValue` functions with matching names. |
| For example, `TypeSystem::GetFormat` returns the default format for the type if no custom formatter |
| has been applied to it. |
| |
| Of particular note are `GetIndexOfChildWithName` and `GetNumChildren`. |
| The `TypeSystem` versions of these functions operate on a *type*, |
| not a value like the `SBValue` versions. |
| The values returned from the `TypeSystem` functions |
| dictate what parts of the struct can be interacted with *at all* by the rest of LLDB. |
| If a field is ommitted, that field effectively no longer exists to LLDB. |
| |
| Additionally, since they do not work with objects, there is no underlying memory to inspect or |
| interpret. |
| Essentially, this means these functions do not have the same purpose as their equivalent |
| `SyntheticProvider` functions. |
| There is no way to determine how many elements a `Vec` has or what address those elements live at. |
| It is also not possible to determine the value of the discriminant of a sum-type. |
| |
| Ideally, the `TypeSystem` should expose types as they appear in the debug info with as few |
| alterations as possible. |
| LLDB's synthetics and frontend can handle making the type pretty. |
| If some piece of information is useless, |
| the Rust compiler should be altered to not output that debug info in the first place. |
| |
| ## Expression parsing |
| |
| The `TypeSystem` is typically written to have a counterpart that can handle expression parsing. |
| It requires implementing a few extra functions in the `TypeSystem` interface. |
| The bulk of the expression parsing code should live in [lldb/source/Plugins/ExpressionParser][expr]. |
| |
| [expr]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/ExpressionParser |
| |
| There isn't too much of note about the parser. |
| It requires implementing a simple interpreter that can handle (possibly simplified) Rust syntax. |
| They operate on `lldb::ValueObject`s, which are the objects that underpin `SBValue`. |
| |
| ## Language |
| |
| The [`Language` plugins][lang_plugin] are the C++ equivalent to the Python visualizer scripts. |
| They operate on `SBValue` objects for the same purpose: |
| creating synthetic children and pretty-printing. |
| The [CPlusPlusLanguage's implementations][cpp_lang] for the LibCxx types are great resources to |
| learn how visualizers should be written. |
| |
| [lang_plugin]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/Language |
| [cpp_lang]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/Language/CPlusPlus |
| |
| These plugins can access LLDB's private internals (including the underlying `TypeSystem`), so |
| synthetic/summary providers written as a `Language` plugin can provide higher quality output than |
| their python equivalent. |
| |
| While debug node parsing, type systems, and expression parsing are all closely tied to eachother, |
| the `Language` plugin is encapsulated more and thus can be written "standalone" for any language |
| that an existing type system supports. |
| Due to the lower barrier of entry, a `RustLanguage` plugin |
| may be a good stepping stone towards full language support in LLDB. |
| |
| ## Visualizers |
| |
| WIP |