[Lldb-commits] [lldb] [lldb] Enable caching for BytecodeSyntheticChildren::FrontEnd::Update (PR #181199)
Dave Lee via lldb-commits
lldb-commits at lists.llvm.org
Thu Feb 12 10:33:52 PST 2026
https://github.com/kastiglione updated https://github.com/llvm/llvm-project/pull/181199
>From 40eabae47e330e17a5e1419919f786d4840654ff Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Thu, 12 Feb 2026 10:29:20 -0800
Subject: [PATCH 1/2] [lldb] Enable caching for
BytecodeSyntheticChildren::FrontEnd::Update
---
lldb/source/DataFormatters/TypeSynthetic.cpp | 34 ++++++++++++++++++-
.../TestBytecodeSynthetic.py | 21 +++++++++++-
.../bytecode-synthetic/main.cpp | 5 ++-
3 files changed, 57 insertions(+), 3 deletions(-)
diff --git a/lldb/source/DataFormatters/TypeSynthetic.cpp b/lldb/source/DataFormatters/TypeSynthetic.cpp
index 10655cdca6f05..32d035eb8a90b 100644
--- a/lldb/source/DataFormatters/TypeSynthetic.cpp
+++ b/lldb/source/DataFormatters/TypeSynthetic.cpp
@@ -22,6 +22,7 @@
#include "lldb/Utility/StreamString.h"
#include "llvm/Support/Error.h"
#include <cstdint>
+#include <optional>
using namespace lldb;
using namespace lldb_private;
@@ -291,10 +292,41 @@ lldb::ChildCacheState BytecodeSyntheticChildren::FrontEnd::Update() {
return ChildCacheState::eRefetch;
}
+ // If the top of the stack is a 0 or 1, that value is popped of the stack and
+ // treated as a return value of eRefetch or eReuse respectively. If the top of
+ // the stack is any other value, it stays on the stack and becomes part of
+ // `self`.
+ //
+ // This dynamic logic can lead to errors for synthetic formatter authors.
+ // Consider the case where an `update` implementation places the number of
+ // children last on the stack. LLDB will _sometimes_ (but not always) consume
+ // that value as a ChildCacheState value. This would cause downstream problems
+ // in `num_children`, because the count won't be on the stack.
+ //
+ // Bytecode authors are encouraged to explicitly push a ChildCacheState value
+ // on to the stack.
+ std::optional<ChildCacheState> cache_state = std::nullopt;
+ const FormatterBytecode::DataStackElement &top = data.back();
+ if (auto *u = std::get_if<uint64_t>(&top))
+ if (*u == 0 || *u == 1)
+ cache_state = static_cast<ChildCacheState>(*u);
+ if (auto *i = std::get_if<int64_t>(&top))
+ if (*i == 0 || *i == 1)
+ cache_state = static_cast<ChildCacheState>(*i);
+
+ if (cache_state) {
+ data.pop_back();
+ if (cache_state == ChildCacheState::eReuse)
+ LLDB_LOG(GetLog(LLDBLog::DataFormatters),
+ "Bytecode formatter returned eReuse from `update` (type: `{0}`, "
+ "name: `{1}`)",
+ m_backend.GetDisplayTypeName(), m_backend.GetName());
+ }
+
if (data.size() > 0)
m_self = std::move(data);
- return ChildCacheState::eRefetch;
+ return cache_state.value_or(ChildCacheState::eRefetch);
}
llvm::Expected<uint32_t>
diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py
index c5c2811c59439..cd4be83ad8563 100644
--- a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py
+++ b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py
@@ -5,8 +5,9 @@
class TestCase(TestBase):
+
@skipUnlessDarwin
- def test(self):
+ def test_synthetic(self):
self.build()
if self.TraceOn():
self.expect("log enable -v lldb formatters")
@@ -21,3 +22,21 @@ def test(self):
self.assertEqual(account.child[0].name, "username")
self.expect("v acc", matching=False, substrs=["password"])
+
+ @skipUnlessDarwin
+ def test_update_reuse(self):
+ self.build()
+
+ log = self.getBuildArtifact("formatter.log")
+ self.runCmd(f"log enable lldb formatters -f {log}")
+
+ _, _, thread, _ = lldbutil.run_to_source_breakpoint(
+ self, "break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ frame = thread.selected_frame
+ account = frame.var("acc")
+ self.assertEqual(account.num_children, 1)
+
+ self.filecheck(f"platform shell cat {log}", __file__)
+ # CHECK: Bytecode formatter returned eReuse from `update` (type: `Account`, name: `acc`)
diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp
index 3e6ae77bbca83..051e44c937bed 100644
--- a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp
@@ -16,10 +16,13 @@ int main(int argc, char **argv) {
__attribute__((used, section("__DATA_CONST,__lldbformatters"))) unsigned char
_Account_synthetic[] =
"\x01" // version
- "\x15" // remaining record size
+ "\x19" // remaining record size
"\x07" // type name size
"Account" // type name
"\x00" // flags
+ "\x06" // sig_update
+ "\x02" // program size
+ "\x20\x01" // `return eReuse`
"\x02" // sig_get_num_children
"\x02" // program size
"\x20\x01" // `return 1`
>From 8b2f81906d427b2399f0db9a5bd5ecd34f6c9e35 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Thu, 12 Feb 2026 10:33:26 -0800
Subject: [PATCH 2/2] python formatting
---
.../data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py
index cd4be83ad8563..a90415a02f99e 100644
--- a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py
+++ b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py
@@ -5,7 +5,6 @@
class TestCase(TestBase):
-
@skipUnlessDarwin
def test_synthetic(self):
self.build()
More information about the lldb-commits
mailing list