Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy deference underlying object for shared/weak/unique_ptr synthetic… #67069

Merged

Conversation

jeffreytan81
Copy link
Contributor

We noticed some performance issue while in lldb-vscode for grabing the name of the SBValue. Profiling shows SBValue::GetName() can cause synthetic children provider of shared/unique_ptr to deference underlying object and complete it type.

This patch lazily moves the dereference from synthetic child provider's Update() method to GetChildAtIndex() so that SBValue::GetName() won't trigger the slow code path.

Here is the culprit slow code path:

...
frame #59: 0x00007ff4102e0660 liblldb.so.15`SymbolFileDWARF::CompleteType(this=<unavailable>, compiler_type=0x00007ffdd9829450) at SymbolFileDWARF.cpp:1567:25 [opt]
...
frame #67: 0x00007ff40fdf9bd4 liblldb.so.15`lldb_private::ValueObject::Dereference(this=0x0000022bb5dfe980, error=0x00007ffdd9829970) at ValueObject.cpp:2672:41 [opt]
frame #68: 0x00007ff41011bb0a liblldb.so.15`(anonymous namespace)::LibStdcppSharedPtrSyntheticFrontEnd::Update(this=0x000002298fb94380) at LibStdcpp.cpp:403:40 [opt]
frame #69: 0x00007ff41011af9a liblldb.so.15`lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>) [inlined] (anonymous namespace)::LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(this=0x000002298fb94380, valobj_sp=<unavailable>) at LibStdcpp.cpp:371:5 [opt]
...
frame #78: 0x00007ff40fdf6e42 liblldb.so.15`lldb_private::ValueObject::CalculateSyntheticValue(this=0x000002296c66a500) at ValueObject.cpp:1836:27 [opt]
frame #79: 0x00007ff40fdf1939 liblldb.so.15`lldb_private::ValueObject::GetSyntheticValue(this=<unavailable>) at ValueObject.cpp:1867:3 [opt]
frame #80: 0x00007ff40fc89008 liblldb.so.15`ValueImpl::GetSP(this=0x0000022c71b90de0, stop_locker=0x00007ffdd9829d00, lock=0x00007ffdd9829d08, error=0x00007ffdd9829d18) at SBValue.cpp:141:46 [opt]
frame #81: 0x00007ff40fc7d82a liblldb.so.15`lldb::SBValue::GetSP(ValueLocker&) const [inlined] ValueLocker::GetLockedSP(this=0x00007ffdd9829d00, in_value=<unavailable>) at SBValue.cpp:208:21 [opt]
frame #82: 0x00007ff40fc7d817 liblldb.so.15`lldb::SBValue::GetSP(this=0x00007ffdd9829d90, locker=0x00007ffdd9829d00) const at SBValue.cpp:1047:17 [opt]
frame #83: 0x00007ff40fc7da6f liblldb.so.15`lldb::SBValue::GetName(this=0x00007ffdd9829d90) at SBValue.cpp:294:32 [opt]
...

Differential Revision: https://reviews.llvm.org/D159542

… provider

We noticed some performance issue while in lldb-vscode for grabing the name of the SBValue.
Profiling shows SBValue::GetName() can cause synthetic children provider of shared/unique_ptr
to deference underlying object and complete it type.

This patch lazily moves the dereference from synthetic child provider's Update() method to
GetChildAtIndex() so that SBValue::GetName() won't trigger the slow code path.

Here is the culprit slow code path:
```
...
frame llvm#59: 0x00007ff4102e0660 liblldb.so.15`SymbolFileDWARF::CompleteType(this=<unavailable>, compiler_type=0x00007ffdd9829450) at SymbolFileDWARF.cpp:1567:25 [opt]
...
frame llvm#67: 0x00007ff40fdf9bd4 liblldb.so.15`lldb_private::ValueObject::Dereference(this=0x0000022bb5dfe980, error=0x00007ffdd9829970) at ValueObject.cpp:2672:41 [opt]
frame llvm#68: 0x00007ff41011bb0a liblldb.so.15`(anonymous namespace)::LibStdcppSharedPtrSyntheticFrontEnd::Update(this=0x000002298fb94380) at LibStdcpp.cpp:403:40 [opt]
frame llvm#69: 0x00007ff41011af9a liblldb.so.15`lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(lldb_private::CXXSyntheticChildren*, std::shared_ptr<lldb_private::ValueObject>) [inlined] (anonymous namespace)::LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(this=0x000002298fb94380, valobj_sp=<unavailable>) at LibStdcpp.cpp:371:5 [opt]
...
frame llvm#78: 0x00007ff40fdf6e42 liblldb.so.15`lldb_private::ValueObject::CalculateSyntheticValue(this=0x000002296c66a500) at ValueObject.cpp:1836:27 [opt]
frame llvm#79: 0x00007ff40fdf1939 liblldb.so.15`lldb_private::ValueObject::GetSyntheticValue(this=<unavailable>) at ValueObject.cpp:1867:3 [opt]
frame llvm#80: 0x00007ff40fc89008 liblldb.so.15`ValueImpl::GetSP(this=0x0000022c71b90de0, stop_locker=0x00007ffdd9829d00, lock=0x00007ffdd9829d08, error=0x00007ffdd9829d18) at SBValue.cpp:141:46 [opt]
frame llvm#81: 0x00007ff40fc7d82a liblldb.so.15`lldb::SBValue::GetSP(ValueLocker&) const [inlined] ValueLocker::GetLockedSP(this=0x00007ffdd9829d00, in_value=<unavailable>) at SBValue.cpp:208:21 [opt]
frame llvm#82: 0x00007ff40fc7d817 liblldb.so.15`lldb::SBValue::GetSP(this=0x00007ffdd9829d90, locker=0x00007ffdd9829d00) const at SBValue.cpp:1047:17 [opt]
frame llvm#83: 0x00007ff40fc7da6f liblldb.so.15`lldb::SBValue::GetName(this=0x00007ffdd9829d90) at SBValue.cpp:294:32 [opt]
...
```

Differential Revision: https://reviews.llvm.org/D159542
@llvmbot
Copy link

llvmbot commented Sep 21, 2023

@llvm/pr-subscribers-lldb

Changes

We noticed some performance issue while in lldb-vscode for grabing the name of the SBValue. Profiling shows SBValue::GetName() can cause synthetic children provider of shared/unique_ptr to deference underlying object and complete it type.

This patch lazily moves the dereference from synthetic child provider's Update() method to GetChildAtIndex() so that SBValue::GetName() won't trigger the slow code path.

Here is the culprit slow code path:

...
frame #<!-- -->59: 0x00007ff4102e0660 liblldb.so.15`SymbolFileDWARF::CompleteType(this=&lt;unavailable&gt;, compiler_type=0x00007ffdd9829450) at SymbolFileDWARF.cpp:1567:25 [opt]
...
frame #<!-- -->67: 0x00007ff40fdf9bd4 liblldb.so.15`lldb_private::ValueObject::Dereference(this=0x0000022bb5dfe980, error=0x00007ffdd9829970) at ValueObject.cpp:2672:41 [opt]
frame #<!-- -->68: 0x00007ff41011bb0a liblldb.so.15`(anonymous namespace)::LibStdcppSharedPtrSyntheticFrontEnd::Update(this=0x000002298fb94380) at LibStdcpp.cpp:403:40 [opt]
frame #<!-- -->69: 0x00007ff41011af9a liblldb.so.15`lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(lldb_private::CXXSyntheticChildren*, std::shared_ptr&lt;lldb_private::ValueObject&gt;) [inlined] (anonymous namespace)::LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(this=0x000002298fb94380, valobj_sp=&lt;unavailable&gt;) at LibStdcpp.cpp:371:5 [opt]
...
frame #<!-- -->78: 0x00007ff40fdf6e42 liblldb.so.15`lldb_private::ValueObject::CalculateSyntheticValue(this=0x000002296c66a500) at ValueObject.cpp:1836:27 [opt]
frame #<!-- -->79: 0x00007ff40fdf1939 liblldb.so.15`lldb_private::ValueObject::GetSyntheticValue(this=&lt;unavailable&gt;) at ValueObject.cpp:1867:3 [opt]
frame #<!-- -->80: 0x00007ff40fc89008 liblldb.so.15`ValueImpl::GetSP(this=0x0000022c71b90de0, stop_locker=0x00007ffdd9829d00, lock=0x00007ffdd9829d08, error=0x00007ffdd9829d18) at SBValue.cpp:141:46 [opt]
frame #<!-- -->81: 0x00007ff40fc7d82a liblldb.so.15`lldb::SBValue::GetSP(ValueLocker&amp;) const [inlined] ValueLocker::GetLockedSP(this=0x00007ffdd9829d00, in_value=&lt;unavailable&gt;) at SBValue.cpp:208:21 [opt]
frame #<!-- -->82: 0x00007ff40fc7d817 liblldb.so.15`lldb::SBValue::GetSP(this=0x00007ffdd9829d90, locker=0x00007ffdd9829d00) const at SBValue.cpp:1047:17 [opt]
frame #<!-- -->83: 0x00007ff40fc7da6f liblldb.so.15`lldb::SBValue::GetName(this=0x00007ffdd9829d90) at SBValue.cpp:294:32 [opt]
...

Differential Revision: https://reviews.llvm.org/D159542


Full diff: https://github.com/llvm/llvm-project/pull/67069.diff

2 Files Affected:

  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp (+11-11)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp (+12-10)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
index c65bb9b6bc9b187..23af50fdb7124ec 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
@@ -377,9 +377,16 @@ lldb::ValueObjectSP
 LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
   if (idx == 0)
     return m_ptr_obj->GetSP();
-  if (idx == 1)
-    return m_obj_obj->GetSP();
-
+  if (idx == 1) {
+    if (m_ptr_obj && !m_obj_obj) {
+      Status error;
+      ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
+      if (error.Success())
+        m_obj_obj = obj_obj->Clone(ConstString("object")).get();
+    }
+    if (m_obj_obj)
+      return m_obj_obj->GetSP();
+  }
   return lldb::ValueObjectSP();
 }
 
@@ -397,14 +404,7 @@ bool LibStdcppSharedPtrSyntheticFrontEnd::Update() {
     return false;
 
   m_ptr_obj = ptr_obj_sp->Clone(ConstString("pointer")).get();
-
-  if (m_ptr_obj) {
-    Status error;
-    ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
-    if (error.Success()) {
-      m_obj_obj = obj_obj->Clone(ConstString("object")).get();
-    }
-  }
+  m_obj_obj = nullptr;
 
   return false;
 }
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp
index 7174e9102e1bd0a..a84d641b57bc475 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp
@@ -108,14 +108,7 @@ bool LibStdcppUniquePtrSyntheticFrontEnd::Update() {
     if (del_obj)
       m_del_obj = del_obj->Clone(ConstString("deleter")).get();
   }
-
-  if (m_ptr_obj) {
-    Status error;
-    ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
-    if (error.Success()) {
-      m_obj_obj = obj_obj->Clone(ConstString("object")).get();
-    }
-  }
+  m_obj_obj = nullptr;
 
   return false;
 }
@@ -128,8 +121,17 @@ LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
     return m_ptr_obj->GetSP();
   if (idx == 1 && m_del_obj)
     return m_del_obj->GetSP();
-  if (idx == 2 && m_obj_obj)
-    return m_obj_obj->GetSP();
+  if (idx == 2) {
+    if (m_ptr_obj && !m_obj_obj) {
+      Status error;
+      ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
+      if (error.Success()) {
+        m_obj_obj = obj_obj->Clone(ConstString("object")).get();
+      }
+    }
+    if (m_obj_obj)
+      return m_obj_obj->GetSP();
+  }
   return lldb::ValueObjectSP();
 }
 

Copy link
Collaborator

@clayborg clayborg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Anytime we can be more lazy the better.

@jeffreytan81 jeffreytan81 merged commit 9faf8b5 into llvm:main Sep 21, 2023
2 checks passed
@jimingham
Copy link
Collaborator

jimingham commented Sep 21, 2023

Supposedly, Update returns true or false based on whether the update succeeds. If we took that seriously, then it would be worrisome that this patch has moved one of the possible failure modes from Update to GetChildAtIndex - losing the chance to trap the error earlier. But both these Update functions return false no matter what happens, so I guess we must not take that return value seriously?

If we don't, we should probably stop returning it?

@walter-erquinigo
Copy link
Member

@jimingham , IIRC from old experiments regarding the synthetic formatter code, true is returned when you want to avoid caching the results of the Update, which means that if you want to persist the success or the failure, you just return false. I might be wrong but that's the behavior I recall I observed a while ago.

@jimingham
Copy link
Collaborator

jimingham commented Sep 22, 2023

How right you are.

I was thinking this was the ValueObject updated stuff, but it's the SyntheticChildrenFrontEnd::Update. If I'm reading the code in ValueObjectSyntheticFilter.cpp aright, the return for that actually means the opposite of what you remembered. If you return false from the SyntheticChildrenFrontEnd::Update, that means the VOSynthetic that uses this FrontEnd has to delete all its cached children, and recreate them when asked by GetChildAtIndex.

So for instance, in the case where a synthetic child front end just hands out a ValueObjectChild in the tree of the underlying ValueObject, then the child that gets cached in the VOSynthetic::m_synthetic_children_cache knows how to update itself from its real parent, it doesn't need help from the ValueObjectSynthetic's FrontEndProvider to get the correct value. So there's no point removing it from the cache and making a new child every time we stop, the one in the cache knows how to keep its value up to date.

But in the case where the children are, for instance, ValueObjectConstResults calculated by some means by the FrontEnd, to return true from Update, you would have to know that the FrontEnd at this stop point would produce the same VOConstResult as it did when you last stopped. Or you would have to know how to manually update each of the cached children. It would be possible for Update to do either of those things, but in general it seems like that's too much trouble and we really never try to figure that out.

I am pretty sure the "Dereferenced" child of a ValueObject can update itself from the VO that generated it, in which case, the two formatters in this patch could have returned true and saved the work of recreating the child VO as the dereference. But I haven't tested that theory, and I doubt just making up a new VO is expensive enough to make that worth the effort. The only downside is if you keep throwing the children away at every stop, the synthetic children have no hope of reporting "IsChanged" correctly.

Anyway, not relevant to this patch. I think returning false was wrong before, but that's just a small bit more work, so it's not worth worrying over.

@walter-erquinigo
Copy link
Member

Do you think it would be a good idea to provide aliases for the return values of the Update method with better names that match the behavior you just discovered? I'm not a fan of true/false in this case, because it confuses synthetic type developers, but I'm just trying to think of ways to improve this without breaking any existing code.

@jimingham
Copy link
Collaborator

jimingham commented Sep 22, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants