[lldb-dev] RFC: -flimit-debug-info + frame variable

Pavel Labath via lldb-dev lldb-dev at lists.llvm.org
Thu Jul 23 05:15:42 PDT 2020


On 22/07/2020 01:31, Jim Ingham wrote:
> 
> 
>> On Jul 21, 2020, at 9:27 AM, Pavel Labath <pavel at labath.sk
>> <mailto:pavel at labath.sk>> wrote:
>> I do see the attractiveness of constructing of a full compiler type. The
>> reason I am hesitant to go that way, because it seems to me that this
>> would negate the two main benefits of the frame variable command over
>> the expression evaluator: a) it's fast; b) it's less likely to crash.
>>
>> And while I don't think it will be as slow or as crashy as the
>> expression evaluator, the usage of the ast importer will force a lot
>> more types to be parsed than are strictly needed for this functionality.
>> And the insertion of all potentially conflicting types from different
>> modules into a single ast context is also somewhat worrying.
> 
> Importation should be incremental as well, so this shouldn’t make things
> that much slower.  And you shouldn’t ever be looking things up by name
> in this AST so you wouldn’t be led astray that way.  You also are going
> to have to do pretty much the same job for “expr”, right?  So you
> wouldn’t be opening new dangerous pathways.

The import is not as incremental as we might want, and it actually sort
of depends on what is the state of the source ast. Let's the source AST
has types A and B, and A depends on B in some way (say as a method
argument). Let's say that A is complete (parsed) and B isn't. While
importing A, the ast importer will import the method which has the B
argument, but whether it will not descend into B (and cause us to parse it).
If however, B happens to be B already parsed then it will import B and
all of its base classes (but not fields and methods).

On top of that we also have our own additions -- whenever we encounter a
method returning a pointer, we import the pointer target type (this has
to do with covariant return types). These things compound and so even a
simple import can end up importing quite a lot.

I actually tried making the ast importer more lazy -- I have a proof of
concept, but it required adding more explicit lookups into clang's Sema,
so that's why I haven't pursued it yet.

I could also try to disable some of these things for these frame
variable imports (they don't need methods at all), but then I would be
opening new dangerous pathways...


> 
> OTOH, the AST’s are complex beasts, so I am not unmoved by your worries...

Yeah... :)

>> The dlclose issue is an interesting one. Presumably, we could ensure
>> that the module does not go away by storing a module shared (or weak?)
>> pointer somewhere inside the value object. BTW, how does this work with
>> ValueObject casts right now? If I cast a ValueObject to a CompilerType
>> belonging to a different module, does anything ensure this module does
>> not go away? Or when dereferencing a pointer to an type which is not
>> complete in the current module?
> 
> I don’t think at present we do anything smart about this.  It’s just
> always bugged me at the back of my brain that we could get into trouble
> with this, and so I don’t want to do something that would make it worse,
> especially in a systemic way.

Is there a reason we don't store a pointer to the module where the
TypeSystem came from? We could do either do that for all ValueObjects,
or just when the type system changes (casts, dereferences of incomplete
types, and now -flimit-debug-info) ?

> 
>>
>> I'm hoping that this stuff won't be "hard work". I haven't prototyped
>> the code yet, but I am hoping to keep this lookup code in under 200 LOC.
>> And as Greg points out, there are ways to put this stuff into the type
>> system -- I'm just not sure whether that is needed given that the
>> ValueObject class is the only user of the GetIndexOfChildMemberWithName
>> interface. The whole function is pretty clearly designed with
>> ValueObject::GetChildMemberWithName in mind.
> 
> It seems fine to me to proceed along the lines you propose.  If it ends
> up being smooth sailing, I can’t see any reason not to do it this way.
>  When/If you end up having lots of corner cases to manage, would be the
> time to consider cutting back to using the real type system to back
> these computations.

Ok, sounds good. Let me create a prototype for this, and we'll see how
it goes from there. It may take a while because I'm now entangled in
some line table stuff.


On 21/07/2020 23:23, Greg Clayton wrote:
>> On Jul 21, 2020, at 9:27 AM, Pavel Labath <pavel at labath.sk> wrote:
>> The dlclose issue is an interesting one. Presumably, we could ensure
>> that the module does not go away by storing a module shared (or weak?)
>> pointer somewhere inside the value object. BTW, how does this work with
>> ValueObject casts right now? If I cast a ValueObject to a CompilerType
>> belonging to a different module, does anything ensure this module does
>> not go away? Or when dereferencing a pointer to an type which is not
>> complete in the current module?
>
> I am not sure dlclose is a problem, the module won't usually be
cleaned up. And that shared library shouldn't have the definition we
need and be able to be unloaded IIUC how the -flimit-debug-info stuff works.
>

In a well-behaved application, I think it shouldn't be possible to
dlclose a library if a library inheriting a type from it is still
loaded. However, there's no way to really guarantee that.

For example, and application might have two libraries with different
defintions of a class A, which don't cause conflict because the relevant
symbols are hidden. But when searching for a base class A from a third
library, we end up picking the wrong one. Or the same (odr) class is
defined in two libraries, and we pick the one which gets unloaded,
although the application actually uses the code from the other library.

Now we could try to be fancy and analyze module dependencies, symbol
visibility, etc. but it would still be pretty hard to guarantee that
this really always is the case.

pl


More information about the lldb-dev mailing list