[lldb-dev] SymbolFile::FindGlobalVariables

Zachary Turner via lldb-dev lldb-dev at lists.llvm.org
Thu Mar 17 11:09:40 PDT 2016


Thanks Greg.  It sounds like for now I will need to implement FindTypes and
ResolveTypeUID.  ParseTypes I can skip and CompleteType can be re-used from
the DWARF implementation (it doesn't actually do anything DWARF specific).
And FindTypes can probably just re-use ResolveTypeUID and the real work is
done in ResolveTypeUID.

Sadly PDB has no good way to do filtered / scope limited searches except by
name.  So we will probably have no choice in any case except to do the name
search, and hope that it's sufficiently narrow as to not make the
subsequent O(n) filtering a huge problem (we could of course re-index
everything in memory, but you'd still get the one time hit).



On Thu, Mar 17, 2016 at 10:40 AM Greg Clayton <gclayton at apple.com> wrote:

>
> > On Mar 16, 2016, at 3:34 PM, Zachary Turner <zturner at google.com> wrote:
> >
> > Hi Greg,
> >
> > could you clarify the difference between the functions ParseTypes,
>
> SymbolFile::ParseTypes() is only used for debugging, don't worry about
> this one unless you need a way to say "parse all types in the supplied
> context". It can help you debug things. It is only called by
> Module::ParseAllDebugSymbols() and this is only used for debugging and it
> isn't exposed anywhere or used by anyone. Just used for initial
> implementation. Again, see Module::ParseAllDebugSymbols() for all the
> details, but not one calls Module::ParseAllDebugSymbols().
>
> > FindTypes
>
> FindType is for finding types by name:
>
> virtual uint32_t
> FindTypes (const SymbolContext& sc,
>            const ConstString &name,
>            const CompilerDeclContext *parent_decl_ctx,
>            bool append,
>            uint32_t max_matches,
>            llvm::DenseSet<lldb_private::SymbolFile *>
> &searched_symbol_files,
>            TypeMap& types);
>
> The symbol context can limit the scope of your search if needed. Why?
> Because you might be stopped inside a "test" module that has 100 compile
> units, and you are stopped in compile unit "foo.cpp" in function "bar()".
> The symbol context can specify a scope in which to search. If a function or
> block is specified, you should find the any types in the the block, if you
> don't find one, proceed to the parent block. Keep going up the blocks, then
> search the class if "bar()" is a method, then search the compile unit for
> any types that match, and then fall back to the module. It is usually
> easiest to just lookup the type and come up with N results, and pare down
> the results after you find them as most times you will only find one type
> so there is no need.
>
> If "parent_decl_ctx" is not NULL, you take all results that made it
> through the "sc" filter and then filter out any results that are not in
> this "parent_decl_ctx". The expression parser often says "I am looking for
> "basic_string" in the parent_decl_ctx that represents "namespace std".
>
> You always check to see if "this" is in the searched_symbol_files set and
> only search if you aren't. This is for module debugging where DWARF files
> refer to types from other DWARF files.
>
>
> And there is one that is specific to module debugging:
>
>     virtual size_t          FindTypes (const std::vector<CompilerContext>
> &context, bool append, TypeMap& types);
>
> You don't need to implement this one unless you have one PDB file that
> refers to a type in another...
>
>
>
>
> > ResolveTypeUID
>
> Often types you might have a variable whose type id "typedef FooType
> ...;". Because of this, you can give your lldb_private::Variable a
> lldb_private::Type that says "I am a typedef named 'FooType' to the type
> with UID 0x1234. We don't need to resolve the type just yet and if no one
> needs to explore the type, we don't need to resolve the type right away. If
> anyone does ask for the CompilerType from a lldb_private::Type, we can then
> do a "m_symfile->ResolveTypeUID(m_encoding_uid);". So it allows us to just
> say "your type is a type from a symbol file with a user ID of 0x1234" and
> we can resolve that lazily if any only if we ever need to. Another instance
> where this is useful is when you create a lldb_private::Function:
>
> Function (CompileUnit *comp_unit,
>           lldb::user_id_t func_uid,
>           lldb::user_id_t func_type_uid,
>           const Mangled &mangled,
>           Type * func_type,
>           const AddressRange& range);
>
> Note that you don't need to parse the function type up front, you can just
> make a function with:
>
> SymbolFilePDB::ParseFunction(...)
> {
>     lldb::user_id_t func_uid = 0x1234;
>     Mangled mangled("_Z3foo", true);
>     AddressRange func_range = ...;
>     Function *func = new Function(m_cu, func_uid, func_uid, mangled,
> nullptr, func_range);
> }
>
> Later if anyone calls:
>
>     Type* Function::GetType();
>
> You can see if can lazily resolve its function type using ResolveTypeUID:
>
> Type*
> Function::GetType()
> {
>     if (m_type == nullptr)
>     {
>         SymbolContext sc;
>
>         CalculateSymbolContext (&sc);
>
>         if (!sc.module_sp)
>             return nullptr;
>
>         SymbolVendor *sym_vendor = sc.module_sp->GetSymbolVendor();
>
>         if (sym_vendor == nullptr)
>             return nullptr;
>
>         SymbolFile *sym_file = sym_vendor->GetSymbolFile();
>
>         if (sym_file == nullptr)
>             return nullptr;
>
>         m_type = sym_file->ResolveTypeUID(m_type_uid);
>     }
>     return m_type;
> }
>
> > , and CompleteType
>
>     virtual bool SymbolFile::CompleteType (CompilerType &compiler_type);
>
> This allows you to say "I have a class A type that is very complex and
> until someone needs to know the details about this class I will have the
> type represented as 'class A;'. Once someone needs to know the details they
> can lazily complete the type. This is very useful when creating types in
> clang::ASTContext objects because we can make a forward declaration that
> knows how to complete itself when you hook into the
> clang::ExternalASTSource. This is some of the factoring you have already
> been doing. If you always just want make all of your types completely when
> you hand them out, then you don't need to do this. But since clang types
> already have all the hooks needed to lazily complete types, we take
> advantage of this. This way you can hand an expression a "class A;" as the
> type for a variable, and if clang ever needs to know about the details
> inside of A, the clang::ExternalASTSource hooks will allow the type to
> lazily complete itself _only_ if ever needed. So if you have an expression
> like "A* a = GetA(); printf("%p\n", a)", clang never need to know what is
> inside of A so it never asks us to complete it. But if you do "A* a = ...;
> a->DoSomething()", clang will ask us to expand the type. Another example is
> imagine you have a local variable in your frame whose type is "A* a;". If
> we display the type in the debugger's variable view, we know this is a
> pointer and unless someone clicks on the disclosure triangle to expand the
> type, we don't need to know what is inside of "A". Variable display is done
> by using CompilerType methods like:
>
> CompilerType t;
>
> const bool omit_empty_base_classes = true;
> uint32_t num_children = t.GetNumChildren (omit_empty_base_classes);
> for (i in num_children)
> {
>     CompilerType child_type = t.GetChildCompilerTypeAtIndex (exe_ctx, i,
> ...);
> }
>
> When we call CompilerType::GetNumChildren(), we call into
> ClangASTContext::GetNumChildren(...), which knows we have clang types and
> it also knows that if the type isn't complete and it can complete itself,
> that we can complete the type:
>
> uint32_t
> ClangASTContext::GetNumChildren (lldb::opaque_compiler_type_t type, bool
> omit_empty_base_classes)
> {
>     uint32_t num_children = 0;
>     clang::QualType qual_type(GetQualType(type));
>     const clang::Type::TypeClass type_class = qual_type->getTypeClass();
>     switch (type_class)
>     {
>         case clang::Type::Record:
>             if (GetCompleteQualType (getASTContext(), qual_type))
>             {
>
>
> Note that we are smart and only try to complete types that we know can be
> complete, like Record types for clang. See the function
> GetCompleteQualType(). So the key is here: both LLDB code and the actual
> clang compiler itself as it is compiling expressions has the ability to
> deal with incomplete types and complete them only when necessary.
>
> > from the SymbolFile plugin?
>
> Let me know if you have any questions.
>
> Greg
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/lldb-dev/attachments/20160317/b4d9f073/attachment.html>


More information about the lldb-dev mailing list