Guilherme, Could you please check if you have any structural differences between the ASTs you see when 1) you first evaluate your expression at breakpoint A 2) you evaluate your expression the second time at breakpoint A ? The AST of the expression evaluator's context is dumped once a TagDecl is completed, but you need to enable a specific logging: (log enable lldb ast).
I have a theory about the root cause of the slowness you experience, but it requires proof from your test scenario. There are known problems with the lookup we use in LLDB [1]. If the lookup mechanism cannot find a symbol then clang::ASTImporter will create a new AST node. Thus I am expecting a growing AST in your case at 2) with duplicated and redundant AST nodes. Why would the grown AST results in any slowness? Because the lookup we use is in `clang::DeclContext::localUncachedLookup()` and it has a fallback branch which I assume is executed in your case: ``` // Slow case: grovel through the declarations in our chain looking for // matches. // FIXME: If we have lazy external declarations, this will not find them! // FIXME: Should we CollectAllContexts and walk them all here? for (Decl *D = FirstDecl; D; D = D->getNextDeclInContext()) { if (auto *ND = dyn_cast<NamedDecl>(D)) if (ND->getDeclName() == Name) Results.push_back(ND); } ``` This for loop does a linear search on the list of the decls of a decl context, which explains the slowing factor as the AST grows. I wounder if you could help proving this theorem by first checking if the AST grows and if yes then checking if this linear search is executed or not. [1] If a symbol is in an `extern "C"` block then the existing lookup fails to find it. I try to fix it in https://reviews.llvm.org/D61333 Thanks, Gabor On Thu, Jul 11, 2019 at 8:24 PM Jim Ingham via lldb-dev < lldb-dev@lists.llvm.org> wrote: > lldb realizes types from DWARF lazily. So for instance, if an expression > refers to a pointer to type Foo, we won't necessarily realize the full type > of Foo from DWARF to parse that expression. Then if you write a second > expression that accesses a member of an object of type Foo, we will realize > the full type for Foo. Then if you run the first expression again, the > pointer to Foo type in the lldb type system will now point to a realized > type of Foo. That should not make any difference, since if we were right > the first time that we didn't need to know anything about Foo, it shouldn't > matter whether the full type is realized or not. > > Similarly, the "expression with no side effects" could have also caused > lldb to realize any number of other types. We find names from type > information in two ways: looking in the realized types, and looking in the > name indexes we read from DWARF or (on systems without accelerator tables) > from indexing the DWARF manually. So the expression with no side effects > will change the types in the realized types pool. That also "should not > matter" because if the expression X was looking up by name, the lookup > through the name indexes when the type wasn't realized yet should be > equivalent to the lookup in the realized types. > > We really have no choice but to realize types lazily in this way, the > debugger would just run way too slowly on big projects if we didn't. So > the fact that one expression can change the state of the type system for a > subsequent expression is a fact of life for lldb. But if the lookup > mechanism is working properly, then these changes shouldn't matter. OTOH > shouldn't and don't are not quite the same... > > Jim > > > > On Jul 11, 2019, at 9:53 AM, Guilherme Andrade <guiandr...@google.com> > wrote: > > > > Speaking more generally, can evaluating an expression with no side > effects affect the outcome of subsequent evaluations? I.e, is it possible > for the expression evaluation X to produce different results in the > following scenarios? > > > > Scenario 1: <set of instructions A> <expression evaluation with no side > effects> <set of instructions B> <expression evaluation X> > > Scenario 2: <set of instructions A> <set of instructions B> <expression > evaluation X> > > > > Thanks! > > > > On Tue, Jul 9, 2019 at 2:52 PM Guilherme Andrade <guiandr...@google.com> > wrote: > > Thanks for looking into this, Jason. I ended up realizing that it isn't > about the number of evaluations; it is the exact location of the > breakpoints that matters. > > > > I'm being able to consistently reproduce this by having two breakpoints > (A and B) inside the same function, but B being inside the scope of a for > loop that defines a couple extra variables. The first evaluation of > 'undefinedVariable' at breakpoint A generates the following > FindExternalLexicalDecls calls: > > > > FindExternalLexicalDecls[5] on (ASTContext*)000001566104F030 in 'Type0' > (CXXRecordDecl*)0000015668964438 > > FindExternalLexicalDecls[6] on (ASTContext*)000001566104F030 in 'Type6' > (CXXRecordDecl*)0000015668961BA8 > > FindExternalLexicalDecls[7] on (ASTContext*)000001566104F030 in 'Type7' > (CXXRecordDecl*)0000015668961A68 > > FindExternalLexicalDecls[8] on (ASTContext*)000001566104F030 in 'Type8' > (CXXRecordDecl*)0000015674F56208 > > FindExternalLexicalDecls[9] on (ASTContext*)000001566104F030 in 'Type9' > (ClassTemplateSpecializationDecl*)0000015674F56CD0 > > FindExternalLexicalDecls[10] on (ASTContext*)000001566104F030 in > 'Type10' (CXXRecordDecl*)0000015668964300 > > FindExternalLexicalDecls[11] on (ASTContext*)000001566104F030 in > 'Type10' (CXXRecordDecl*)0000015668964300 > > FindExternalLexicalDecls[12] on (ASTContext*)000001566104F030 in 'Type0' > (CXXRecordDecl*)0000015668964438 > > FindExternalLexicalDecls[13] on (ASTContext*)000001566104F030 in > 'Type11' (EnumDecl*)0000015674F62778 > > > > Then, the evaluation at breakpoint B generates: > > > > FindExternalLexicalDecls[19] on (ASTContext*)000001F1F6275320 in 'Type0' > (CXXRecordDecl*)000001F1EA798868 > > FindExternalLexicalDecls[20] on (ASTContext*)000001F1F6275320 in 'Type1' > (CXXRecordDecl*)000001F1EA799098 > > FindExternalLexicalDecls[21] on (ASTContext*)000001F1F6275320 in 'Type2' > (ClassTemplateSpecializationDecl*)000001F1EA7A0D50 > > FindExternalLexicalDecls[22] on (ASTContext*)000001F1F6275320 in 'Type3' > (CXXRecordDecl*)000001F1EA79D818 > > FindExternalLexicalDecls[23] on (ASTContext*)000001F1F6275320 in 'Type4' > (CXXRecordDecl*)000001F1EA79D548 > > FindExternalLexicalDecls[24] on (ASTContext*)000001F1F6275320 in 'Type5' > (CXXRecordDecl*)000001F1EA7A2A08 > > FindExternalLexicalDecls[25] on (ASTContext*)000001F1F6275320 in 'Type6' > (CXXRecordDecl*)000001F1EA7BB368 > > FindExternalLexicalDecls[26] on (ASTContext*)000001F1F6275320 in 'Type7' > (CXXRecordDecl*)000001F1EA7BB228 > > FindExternalLexicalDecls[27] on (ASTContext*)000001F1F6275320 in 'Type8' > (CXXRecordDecl*)000001F1F68B9088 > > FindExternalLexicalDecls[28] on (ASTContext*)000001F1F6275320 in 'Type9' > (ClassTemplateSpecializationDecl*)000001F1F68B2AC0 > > FindExternalLexicalDecls[29] on (ASTContext*)000001F1F6275320 in > 'Type10' (CXXRecordDecl*)000001F1EA798730 > > FindExternalLexicalDecls[30] on (ASTContext*)000001F1F6275320 in > 'Type10' (CXXRecordDecl*)000001F1EA798730 > > FindExternalLexicalDecls[31] on (ASTContext*)000001F1F6275320 in 'Type0' > (CXXRecordDecl*)000001F1EA798868 > > FindExternalLexicalDecls[32] on (ASTContext*)000001F1F6275320 in > 'Type11' (EnumDecl*)000001F1F68CB3E0 > > FindExternalLexicalDecls[33] on (ASTContext*)000001F1F6275320 in > 'Type12' (CXXRecordDecl*)000001F1E2351F48 > > > > And the subsequent evaluations at breakpoint A, which are slower, > produce: > > > > FindExternalLexicalDecls[91] on (ASTContext*)000001566104F030 in 'Type0' > (CXXRecordDecl*)000001566895C3B8 > > FindExternalLexicalDecls[92] on (ASTContext*)000001566104F030 in 'Type1' > (CXXRecordDecl*)000001566895CBE8 > > FindExternalLexicalDecls[93] on (ASTContext*)000001566104F030 in 'Type2' > (ClassTemplateSpecializationDecl*)00000156689678D0 > > FindExternalLexicalDecls[94] on (ASTContext*)000001566104F030 in 'Type3' > (CXXRecordDecl*)0000015668964398 > > FindExternalLexicalDecls[95] on (ASTContext*)000001566104F030 in 'Type4' > (CXXRecordDecl*)00000156689640C8 > > FindExternalLexicalDecls[96] on (ASTContext*)000001566104F030 in 'Type5' > (CXXRecordDecl*)0000015674F608B8 > > FindExternalLexicalDecls[97] on (ASTContext*)000001566104F030 in 'Type6' > (CXXRecordDecl*)0000015674F731B8 > > FindExternalLexicalDecls[98] on (ASTContext*)000001566104F030 in 'Type7' > (CXXRecordDecl*)0000015674F73078 > > FindExternalLexicalDecls[99] on (ASTContext*)000001566104F030 in 'Type8' > (CXXRecordDecl*)0000015674F7B278 > > FindExternalLexicalDecls[100] on (ASTContext*)000001566104F030 in > 'Type9' (ClassTemplateSpecializationDecl*)0000015674F78CF0 > > FindExternalLexicalDecls[101] on (ASTContext*)000001566104F030 in > 'Type10' (CXXRecordDecl*)000001566895C280 > > FindExternalLexicalDecls[102] on (ASTContext*)000001566104F030 in > 'Type10' (CXXRecordDecl*)000001566895C280 > > FindExternalLexicalDecls[103] on (ASTContext*)000001566104F030 in > 'Type0' (CXXRecordDecl*)000001566895C3B8 > > FindExternalLexicalDecls[104] on (ASTContext*)000001566104F030 in > 'Type11' (EnumDecl*)0000015674F81510 > > > > The FindExternalVisibleDecls entries remain the same among all > evaluations at breakpoint A. > > > > So, Jim, if symbols are getting added at B or types are being completed, > then the extra FindExternalLexicalDecls calls for Type1-5 is intended > behavior? > > > > On Mon, Jul 8, 2019 at 7:04 PM Jim Ingham <jing...@apple.com> wrote: > > Are any sources for symbols getting added to the process as you go > along, like new shared library loads? You are asking for a non-existent > name, so every new source of debug info is a new place to look. > > > > It's also possible that we are completing some types during the course > of the expression evaluation, and then the newly completed types become > themselves new places to look for this non-existent name. > > > > You can see what is getting looked up by turning in the expression log > (log enable lldb expr) then looking for the FindExternalVisibleDecls > entries. > > > > Jim > > > > > > > On Jul 8, 2019, at 3:33 PM, Jason Molenda via lldb-dev < > lldb-dev@lists.llvm.org> wrote: > > > > > > Hm, that's interesting. > > > > > > I tried running a debug lldb on /bin/ls. then I attached from another > lldb. I put a breakpoint on CommandObjectTargetModulesLookup::DoExecute > and resumed execution. In the debuggee lldb, I did > > > > > > tar mod loo -a 0 > > > > > > > > > and auto-repeated return so the same command would be executed over > and over. > > > > > > In the debugger lldb, I tried adding a command to the breakpoint, > > > > > > br comm add > > > p does_not_exist > > > DONE > > > > > > and continuing - after a couple dozen times, I didn't see a slowdown. > I tried adding a breakpoint condition, > > > > > > br mod -c 'doesNotExist == 1' 1 > > > > > > and continuing, and didn't see a slowdown after a few dozen > repetitions. > > > > > > I'm on macOS using .o file DWARF debugging. > > > > > > I'm sure there's a bug here, but it may be more specific to the > platform and type of debug info that you're using? It could be that lldb is > too small of a project to repo this problem too. > > > > > > > > > > > >> On Jul 4, 2019, at 11:38 AM, Guilherme Andrade via lldb-dev < > lldb-dev@lists.llvm.org> wrote: > > >> > > >> I have two breakpoint inside methods that are called every frame (C++ > project using Unreal), and every time one of them is reached, I evaluate > one expression (I'm being able to reproduce this using an undefined name, > say "undefinedVariable"). After a few iterations (usually tens), the time > it takes for LLDB to say that name doesn't exist increases, despite being > the same expression, at the same breakpoint and the call stack remaining > unchanged. > > >> > > >> I've noticed that the number of lexical Decl queries and imports > conducted by Clang reported in 'Local metrics' increase. > > >> > > >> They go from: > > >> Number of visible Decl queries by name : 29 > > >> Number of lexical Decl queries : 9 > > >> Number of imports initiated by LLDB : 15 > > >> Number of imports conducted by Clang : 827 > > >> Number of Decls completed : 5 > > >> Number of records laid out : 2 > > >> > > >> To: > > >> Number of visible Decl queries by name : 29 > > >> Number of lexical Decl queries : 14 > > >> Number of imports initiated by LLDB : 15 > > >> Number of imports conducted by Clang : 1342 > > >> Number of Decls completed : 5 > > >> Number of records laid out : 2 > > >> > > >> Also, the number of SymbolFileDWARF operations in the logs jumps from > 366 to 406. > > >> > > >> So, I've got two questions. 1) Is it safe to say that those extra > imports and Decl queries are responsible for the performance loss? 2) Why > do they happen? > > >> > > >> Thanks! > > >> _______________________________________________ > > >> 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 > > > > > > -- > > Guilherme Andrade | Software Engineer | guiandr...@google.com | Google > Waterloo, Canada > > _______________________________________________ > 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