1. 6

Authored by /u/werat

    1. 4

      Unlike the regular compiler, lldb-eval resolves the expression in the debugger context and uses the debug information from the target process (DWARF, PDB) to resolve the identifiers (variables, types).

      An interesting bit about debugger support in IntelliJ is that it can actually merge dynamic debugger’s view and static compiler’s view. For example, ide can ask the debugger for the name/approximate location of a variable, and than it can use internal compiler’s name resolution to find the variable exactly, get its type, etc. I don’t think this is actually used in any serious capacity for native languages, but in theory this makes an interesting approach possible, where all of the debugger’s semantic “heavy lifting” is done in the IDE using precise static analysis info, and the debugger itself is a relatively small driver which just pokes memory of the debugee.

      1. 2

        That is an interesting approach indeed. Does this “static view” come from the debug information or the source code of the process? For native languages (or at least C++) I don’t think it’s feasible to analyze the source code. You need to build it with the same build flags and defines, which might not be possible.

        In LLDB the expression evaluator uses the actual compiler (clang) to compile the expression. The AST for missing symbols is constructed from the debug info (using ASTImporter), but the irony is that only the AST created by Clang itself is officially considered “valid” :)

        1. 1

          Must these symbol tables etc still be embedded into the executable? IMO they should be separate, so you don’t have to ship bloated executables and debug info to customers, but when you build an executable you store all that data using the hash of the binary, so it can be matched back up by a debugger some time in the future.

        2. 1

          The “static view” comes from the source code, so it is indeed true that, to make the hypothetical approach feasible, there needs to be a very tight integration between the build process, compiler, debugger, and an IDE.

        3. 1

          Just in case you are curious, here’s the place where bridging of debugger and compiler view happens for the Rust language:

          https://github.com/intellij-rust/intellij-rust/blob/fda81e0ff9d4ae00d2c44175dba57526938ad78e/debugger/src/main/kotlin/org/rust/debugger/lang/RsDebuggerTypesHelper.kt#L29

          LLValue parameter is the dubuggers view of the runtime value, the returned PsiElement is the “compiler”s static view of the variable in the source text, which corresponds to the runtime value.