[Lldb-commits] [lldb] [lldb] Improve frame variable handling of recognizer arguments (PR #200084)
Dave Lee via lldb-commits
lldb-commits at lists.llvm.org
Wed May 27 18:23:31 PDT 2026
https://github.com/kastiglione updated https://github.com/llvm/llvm-project/pull/200084
>From 9fa77da8e42e4ebf693bcc54c6295880dfadc06c Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 27 May 2026 16:41:08 -0700
Subject: [PATCH 1/3] [lldb] Improve frame variable handling of recognizer
arguments
Fixes two bugs. The bug which prompted this change is that `frame variable name` would
print all regcognizer arguments, even though only "name" was requested.
While making a test for the first bug, I found that `frame variable recognizer_arg` will
both successfully print the variable, and also report a false positive error:
```
error: <user expression>:1:1: use of undeclared identifier 'recognizer_arg'
1 | recognizer_arg
``
This change fixes both bugs, including when using `--regex`.
---
lldb/source/Commands/CommandObjectFrame.cpp | 106 ++++++++++--------
.../frame/recognizer/TestFrameRecognizer.py | 21 ++++
2 files changed, 79 insertions(+), 48 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index b9e6130831b92..7771dd67c64e6 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -642,10 +642,21 @@ may even involve JITing and running code in the target program.)");
if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())
m_option_variable.show_globals = true;
- if (variable_list) {
- const Format format = m_option_format.GetFormat();
- options.SetFormat(format);
+ ValueObjectListSP recognized_arg_list;
+ if (m_option_variable.show_recognized_args)
+ if (auto recognized_frame = frame->GetRecognizedFrame())
+ recognized_arg_list = recognized_frame->GetRecognizedArguments();
+
+ const Format format = m_option_format.GetFormat();
+ options.SetFormat(format);
+
+ auto print_value = [&result, options](ValueObjectSP valobj_sp) {
+ result.GetValueObjectList().Append(valobj_sp);
+ if (auto error = valobj_sp->Dump(result.GetOutputStream(), options))
+ result.AppendError(toString(std::move(error)));
+ };
+ if (variable_list) {
if (!command.empty()) {
VariableList regex_var_list;
@@ -659,9 +670,21 @@ may even involve JITing and running code in the target program.)");
std::optional<llvm::ArrayRef<VariableSP>> results =
findUniqueRegexMatches(regex, regex_var_list, *variable_list);
if (!results) {
- result.AppendErrorWithFormat(
- "no variables matched the regular expression '%s'",
- entry.c_str());
+ // No variables matched. Try recognized args as fallback.
+ bool found_recognized = false;
+ if (recognized_arg_list) {
+ for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
+ if (regex.Execute(rec_value_sp->GetName())) {
+ found_recognized = true;
+ print_value(rec_value_sp);
+ }
+ }
+ }
+ if (!found_recognized) {
+ result.AppendErrorWithFormat(
+ "no variables matched the regular expression '%s'",
+ entry.c_str());
+ }
continue;
}
for (const VariableSP &var_sp : *results) {
@@ -710,7 +733,7 @@ may even involve JITing and running code in the target program.)");
valobj_sp = frame->GetValueForVariableExpressionPath(
entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
var_sp, error);
- if (valobj_sp) {
+ if (valobj_sp && error.Success()) {
result.GetValueObjectList().Append(valobj_sp);
std::string scope_string;
@@ -732,29 +755,33 @@ may even involve JITing and running code in the target program.)");
Stream &output_stream = result.GetOutputStream();
options.SetRootValueObjectName(
valobj_sp->GetParent() ? entry.c_str() : nullptr);
- // Check only the `error` argument, because doing
- // `valobj_sp->GetError()` will update the value and potentially
- // return a new error that happens during the update, even if
- // `GetValueForVariableExpressionPath` reported no errors.
- if (error.Fail()) {
- result.SetStatus(eReturnStatusFailed);
- result.SetError(error.takeError());
- } else {
- // If there is an error while updating the value, it will be
- // printed here as the contents of the value, e.g.
- // `(int) *((int*)0) = <parent is NULL>`
- if (llvm::Error error = valobj_sp->Dump(output_stream, options))
- result.AppendError(toString(std::move(error)));
- }
+ // If there is an error while updating the value, it will be
+ // printed here as the contents of the value, e.g.
+ // `(int) *((int*)0) = <parent is NULL>`
+ if (llvm::Error error = valobj_sp->Dump(output_stream, options))
+ result.AppendError(toString(std::move(error)));
} else {
- if (auto error_cstr = error.AsCString(nullptr))
- result.AppendError(error_cstr);
- else
- result.AppendErrorWithFormat(
- "unable to find any variable expression path that matches "
- "'%s'",
- entry.c_str());
+ // Variable lookup failed. Check recognized args as a fallback.
+ bool found_recognized = false;
+ if (recognized_arg_list) {
+ for (auto &obj_sp : recognized_arg_list->GetObjects()) {
+ if (obj_sp->GetName() == entry.ref()) {
+ found_recognized = true;
+ print_value(obj_sp);
+ break;
+ }
+ }
+ }
+ if (!found_recognized) {
+ if (const auto *error_cstr = error.AsCString(nullptr))
+ result.AppendError(error_cstr);
+ else
+ result.AppendErrorWithFormat(
+ "unable to find any variable expression path that "
+ "matches '%s'",
+ entry.c_str());
+ }
}
}
}
@@ -811,26 +838,9 @@ may even involve JITing and running code in the target program.)");
result.SetStatus(eReturnStatusSuccessFinishResult);
}
- if (m_option_variable.show_recognized_args) {
- auto recognized_frame = frame->GetRecognizedFrame();
- if (recognized_frame) {
- ValueObjectListSP recognized_arg_list =
- recognized_frame->GetRecognizedArguments();
- if (recognized_arg_list) {
- for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
- result.GetValueObjectList().Append(rec_value_sp);
- options.SetFormat(m_option_format.GetFormat());
- options.SetVariableFormatDisplayLanguage(
- rec_value_sp->GetPreferredDisplayLanguage());
- options.SetRootValueObjectName(
- rec_value_sp->GetName().AsCString(nullptr));
- if (llvm::Error error =
- rec_value_sp->Dump(result.GetOutputStream(), options))
- result.AppendError(toString(std::move(error)));
- }
- }
- }
- }
+ if (recognized_arg_list && (command.empty() || !variable_list))
+ for (auto &rec_value_sp : recognized_arg_list->GetObjects())
+ print_value(rec_value_sp);
m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
m_cmd_name);
diff --git a/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py b/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py
index e0c107fc51302..68df086143585 100644
--- a/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py
+++ b/lldb/test/API/commands/frame/recognizer/TestFrameRecognizer.py
@@ -162,6 +162,27 @@ def test_frame_recognizer_1(self):
substrs=['*a = 78'])
"""
+ def test_recognized_args_filtered_by_name(self):
+ """Test that 'frame variable <name>' only prints matching recognized args."""
+ self.build()
+ lldbutil.run_to_name_breakpoint(self, "foo")
+
+ self.runCmd("frame recognizer clear")
+ self.runCmd("command script import recognizer.py")
+ self.runCmd(
+ "frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo"
+ )
+
+ # With no args, both recognized args are printed.
+ self.expect("frame variable", substrs=["(int) a = 42", "(int) b = 56"])
+
+ # With a specific name, only the matching recognized arg is printed.
+ self.expect("frame variable a", substrs=["(int) a = 42"])
+ self.expect("frame variable a", matching=False, substrs=["b = 56"])
+
+ self.expect("frame variable b", substrs=["(int) b = 56"])
+ self.expect("frame variable b", matching=False, substrs=["a = 42"])
+
def test_frame_recognizer_hiding(self):
self.build()
>From 2dfff89c587d2adbf70f6b783e8922164274d5a2 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 27 May 2026 17:13:09 -0700
Subject: [PATCH 2/3] Tweak error handling
---
lldb/source/Commands/CommandObjectFrame.cpp | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 7771dd67c64e6..0569ae91ed69d 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -774,8 +774,12 @@ may even involve JITing and running code in the target program.)");
}
}
if (!found_recognized) {
- if (const auto *error_cstr = error.AsCString(nullptr))
- result.AppendError(error_cstr);
+ // Check only the `error` argument, because doing
+ // `valobj_sp->GetError()` will update the value and potentially
+ // return a new error that happens during the update, even if
+ // `GetValueForVariableExpressionPath` reported no errors.
+ if (error.Fail())
+ result.SetError(error.takeError());
else
result.AppendErrorWithFormat(
"unable to find any variable expression path that "
>From 04d9eeb5c6d0d135a01a856af6bc2db7ed41d5a3 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Wed, 27 May 2026 18:23:06 -0700
Subject: [PATCH 3/3] Remove optional {}
---
lldb/source/Commands/CommandObjectFrame.cpp | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 0569ae91ed69d..742f7026464f0 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -672,14 +672,12 @@ may even involve JITing and running code in the target program.)");
if (!results) {
// No variables matched. Try recognized args as fallback.
bool found_recognized = false;
- if (recognized_arg_list) {
- for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
+ if (recognized_arg_list)
+ for (auto &rec_value_sp : recognized_arg_list->GetObjects())
if (regex.Execute(rec_value_sp->GetName())) {
found_recognized = true;
print_value(rec_value_sp);
}
- }
- }
if (!found_recognized) {
result.AppendErrorWithFormat(
"no variables matched the regular expression '%s'",
@@ -764,15 +762,13 @@ may even involve JITing and running code in the target program.)");
} else {
// Variable lookup failed. Check recognized args as a fallback.
bool found_recognized = false;
- if (recognized_arg_list) {
- for (auto &obj_sp : recognized_arg_list->GetObjects()) {
+ if (recognized_arg_list)
+ for (auto &obj_sp : recognized_arg_list->GetObjects())
if (obj_sp->GetName() == entry.ref()) {
found_recognized = true;
print_value(obj_sp);
break;
}
- }
- }
if (!found_recognized) {
// Check only the `error` argument, because doing
// `valobj_sp->GetError()` will update the value and potentially
More information about the lldb-commits
mailing list