> On Sep 28, 2019, at 4:00 PM, Vadim Chugunov via lldb-dev 
> <lldb-dev@lists.llvm.org> wrote:
> 
> Hi,
> Last year there was an effort led by Tom Tromey to add Rust language support 
> into LLDB.  He had implemented a fairly complete language plugin, however it 
> was not accepted into mainline because of supportability concerns.    I guess 
> these concerns had some merit, because this change did not survive even in 
> Rust's private branch <https://github.com/rust-lang/llvm-project/pull/19> due 
> to the difficulty of rebasing on top of LLVM 9.
> 
> I am wondering if there's a more limited version of this, that can be merged 
> into mainline:
> In terms of its memory model, Rust is not that far off from C++, so treating 
> Rust types is if they were C++ types basically works.  There is only one 
> major problem: currently LLDB cannot deal with tagged unions, which Rust code 
> uses quite heavily.   When such a type is encountered, LLDB just emits an 
> empty struct, which makes it impossible to examine the contents.
> 
> My tentative proposal is to modify LLDB's DWARFASTParserClang to handle 
> DW_TAG_variant et al, and create a C++ approximation of these types, e.g. as 
> a polymorphic class, or just an untagged union.   This would provide at least 
> a minimal level of functionality for Rust (and possibly other languages) and 
> be a much lesser maintenance burden on LLDB core team.  
> What would y'all say?

So if Rust actually uses llvm and clang and Rust is supported by llvm and 
clang, this shouldn't be an issue and should already work. But if you are 
having problems, then I am guessing that you have a compiler that isn't based 
on llvm and clang? If that is the case, the _best_ thing you can do is write a 
new TypeSystem subclass. Everywhere in LLDB, anytime we want to get type 
information or run an expression, we grab a TypeSytem for a given language 
enumeration. When we are stopped in a Rust stack frame, we will ask for the 
type system for the Rust language and hopefully we get something back. 

For viewing types in a variable view, you _can_ go the route of letting LLDB 
convert DWARF into clang AST types and letting that infrastructure display 
those types. But you can often run into issues, like you have seen with your 
DW_TAG_variant. If a user then types "p foo->bar", it will invoke the clang 
expression parser and it will then play with the types that you have created. 
Clang has a lot of asserts and other things that can crash your debug session 
if you do anything to weird in your clang AST context.

So if Rust doesn't use clang in its compiler
- create a new TypeSystem for Rust that would convert DWARF into Rust AST types 
that are native to your Rust compiler that uses as much of the Rust compiler 
sources as possible
- write a native Rust expression parser which hopefully uses your Rust compiler 
sources to evaluate and run your expression

It is good to note how the Swift language decided to do things differently. 
Swift decided that they would have the compiler/linker generate a blob of data 
that is embedded into the executable or stand alone debug information that 
contains a serialized AST of the program. The benefit of this approach is that 
when you debug your program, LLDB will hand this serialized blob back to the 
compiler. The DWARF information for Swift doesn't need to encode the full type 
information in this case. It just has mangled names that uniquely identify the 
types. LLDB can then pass this mangled name to the compiler and say "please 
give me a type the '_SC3FooS3Bar'". The other benefit if this approach is that 
the compiler can rapidly change language features and the debugger can keep up 
by recompiling. Any new language features or types are encoded in the data blob 
and the compiler can then extract them. The serialized Swift AST contexts are 
not portable between compiler versions though, and this is the drawback of this 
approach. The LLDB must be perfectly in sync with the tools that produce the 
binaries. Another benefit of this approach is that the entire AST of all types 
gets encoded. Many compilers will limit the amount of DWARF debug info they 
emit which means that they don't emit every type, they try to only emit the 
types that are used. DWARF also doesn't have great template support, so any 
templates that aren't used, or code that is only inlined 
(std::vector<int>::size() for example) won't be callable in an expression. If 
you have the entire AST, you can synthesize these inlined functions and use all 
types that your program knew about when it was compiled. If you convert reduced 
DWARF into ASTs, you only have the information that is represented by the DWARF 
itself and nothing more.

All other languages convert DWARF back into clang AST types and then let the 
clang compiler evaluate expression using native clang AST types. The C and C++ 
languages have been pretty stable so this approach works well for C/C++/ObjC 
and more.

So the right answer depends on what the Rust community wants. Is the language 
changing rapidly where the debugger must be in sync to take advantage and use 
the latest language features? Or is it stable?

The other nice things about creating a new TypeSystem, is that it is a plugin 
and you don't need to compile it in. cmake can be taught to conditionally 
compile in your type system if you want it. It would also allow you to have 
more than one Rust type system if needed for different Rust language versions 
that each could be exclusively compiled in. Having your sources be in a new 
TypeSystem plug-in ensure easy merging when it comes to different repositories.

Let us know about which approach sounds better to the Rust community and we can 
proceed from there!


> 
> _______________________________________________
> lldb-dev mailing list
> lldb-dev@lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-dev

_______________________________________________
lldb-dev mailing list
lldb-dev@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-dev

Reply via email to