diff --git a/lldb/include/lldb/Interpreter/OptionGroupFormat.h b/lldb/include/lldb/Interpreter/OptionGroupFormat.h --- a/lldb/include/lldb/Interpreter/OptionGroupFormat.h +++ b/lldb/include/lldb/Interpreter/OptionGroupFormat.h @@ -63,6 +63,10 @@ bool HasGDBFormat() const { return m_has_gdb_format; } + /// Return the GDB format letter that identifies this format. + /// Valid only when `HasGDBFormat` is true. + char GetGDBFormat() const; + bool AnyOptionWasSet() const { return m_format.OptionWasSet() || m_byte_size.OptionWasSet() || m_count.OptionWasSet(); diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.h b/lldb/source/Commands/CommandObjectDWIMPrint.h --- a/lldb/source/Commands/CommandObjectDWIMPrint.h +++ b/lldb/source/Commands/CommandObjectDWIMPrint.h @@ -10,6 +10,9 @@ #define LLDB_SOURCE_COMMANDS_COMMANDOBJECTDWIMPRINT_H #include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Interpreter/OptionValueFormat.h" namespace lldb_private { @@ -31,8 +34,14 @@ ~CommandObjectDWIMPrint() override = default; + Options *GetOptions() override; + private: bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override; + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options = lldb::eFormatDefault; + OptionGroupValueObjectDisplay m_varobj_options; }; } // namespace lldb_private diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp --- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp +++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp @@ -9,13 +9,19 @@ #include "CommandObjectDWIMPrint.h" #include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/DumpValueObjectOptions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Target/StackFrame.h" #include "lldb/Utility/ConstString.h" +#include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FormatVariadic.h" using namespace llvm; using namespace lldb; @@ -26,14 +32,27 @@ "Print a variable or expression.", "dwim-print [ | ]", eCommandProcessMustBePaused | eCommandTryTargetAPILock) { + m_option_group.Append(&m_format_options, + OptionGroupFormat::OPTION_GROUP_FORMAT | + OptionGroupFormat::OPTION_GROUP_GDB_FMT, + LLDB_OPT_SET_1); + m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); } -bool CommandObjectDWIMPrint::DoExecute(StringRef expr, - CommandReturnObject &result) { - // Ignore leading and trailing whitespace. - expr = expr.trim(); +Options *CommandObjectDWIMPrint::GetOptions() { return &m_option_group; } - if (expr.empty()) { +bool CommandObjectDWIMPrint::DoExecute(StringRef command, + CommandReturnObject &result) { + m_option_group.NotifyOptionParsingStarting(&m_exe_ctx); + OptionsWithRaw args{command}; + StringRef expr = args.GetRawPart(); + + if (args.HasArgs()) { + if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group, + m_exe_ctx)) + return false; + } else if (command.empty()) { result.AppendErrorWithFormatv("'{0}' takes a variable or expression", m_cmd_name); return false; @@ -41,13 +60,22 @@ auto verbosity = GetDebugger().GetDWIMPrintVerbosity(); + DumpValueObjectOptions dump_options = m_varobj_options.GetAsDumpOptions( + eLanguageRuntimeDescriptionDisplayVerbosityFull, + m_format_options.GetFormat()); + + std::string format_flag; + if (m_format_options.HasGDBFormat()) + format_flag = llvm::formatv(" -G {0}", m_format_options.GetGDBFormat()); + // First, try `expr` as the name of a frame variable. if (StackFrame *frame = m_exe_ctx.GetFramePtr()) { auto valobj_sp = frame->FindVariable(ConstString(expr)); if (valobj_sp && valobj_sp->GetError().Success()) { if (verbosity == eDWIMPrintVerbosityFull) - result.AppendMessageWithFormatv("note: ran `frame variable {0}`", expr); - valobj_sp->Dump(result.GetOutputStream()); + result.AppendMessageWithFormatv("note: ran `frame variable{0} {1}`", + format_flag, expr); + valobj_sp->Dump(result.GetOutputStream(), dump_options); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } @@ -64,8 +92,9 @@ if (target.EvaluateExpression(expr, exe_scope, valobj_sp) == eExpressionCompleted) { if (verbosity != eDWIMPrintVerbosityNone) - result.AppendMessageWithFormatv("note: ran `expression -- {0}`", expr); - valobj_sp->Dump(result.GetOutputStream()); + result.AppendMessageWithFormatv("note: ran `expression{0} -- {1}`", + format_flag, expr); + valobj_sp->Dump(result.GetOutputStream(), dump_options); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } else { diff --git a/lldb/source/Interpreter/OptionGroupFormat.cpp b/lldb/source/Interpreter/OptionGroupFormat.cpp --- a/lldb/source/Interpreter/OptionGroupFormat.cpp +++ b/lldb/source/Interpreter/OptionGroupFormat.cpp @@ -12,6 +12,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Target.h" +#include "lldb/lldb-enumerations.h" using namespace lldb; using namespace lldb_private; @@ -274,6 +275,37 @@ return false; } +char OptionGroupFormat::GetGDBFormat() const { + switch (GetFormat()) { + case lldb::eFormatOctal: + return 'o'; + case lldb::eFormatHex: + return 'x'; + case lldb::eFormatDecimal: + return 'd'; + case lldb::eFormatUnsigned: + return 'u'; + case lldb::eFormatBinary: + return 't'; + case lldb::eFormatFloat: + return 'f'; + case lldb::eFormatAddressInfo: + return 'a'; + case lldb::eFormatInstruction: + return 'i'; + case lldb::eFormatChar: + return 'c'; + case lldb::eFormatCString: + return 's'; + case lldb::eFormatOSType: + return 'T'; + case lldb::eFormatHexFloat: + return 'A'; + default: + llvm_unreachable("format is not a valid gdb-format"); + } +} + void OptionGroupFormat::OptionParsingStarting( ExecutionContext *execution_context) { m_format.Clear(); diff --git a/lldb/test/API/commands/dwim-print/TestDWIMPrint.py b/lldb/test/API/commands/dwim-print/TestDWIMPrint.py --- a/lldb/test/API/commands/dwim-print/TestDWIMPrint.py +++ b/lldb/test/API/commands/dwim-print/TestDWIMPrint.py @@ -27,9 +27,25 @@ before, after = self.PERSISTENT_VAR.split(string, maxsplit=1) return re.escape(before) + r"\$\d+" + re.escape(after) - def _expect_cmd(self, expr: str, base_cmd: str) -> None: + def _expect_cmd( + self, + dwim_cmd: str, + base_cmd: str, + ) -> None: """Run dwim-print and verify the output against the expected command.""" - cmd = f"{base_cmd} {expr}" + match = re.match("dwim-print(?:/(.))? (.+)", dwim_cmd) + format = match.group(1) + expr = match.group(2) + + cmd_args = [] + if format: + cmd_args.append(f"-G {format}") + if base_cmd == "expression": + cmd_args.append("--") + cmd_args.append(expr) + + cmd_argstr = " ".join(cmd_args) + cmd = f"{base_cmd} {cmd_argstr}" cmd_output = self._run_cmd(cmd) # Verify dwim-print chose the expected command. @@ -37,12 +53,12 @@ substrs = [f"note: ran `{cmd}`"] patterns = [] - if base_cmd == "expression --" and self.PERSISTENT_VAR.search(cmd_output): + if base_cmd == "expression" and self.PERSISTENT_VAR.search(cmd_output): patterns.append(self._mask_persistent_var(cmd_output)) else: substrs.append(cmd_output) - self.expect(f"dwim-print {expr}", substrs=substrs, patterns=patterns) + self.expect(dwim_cmd, substrs=substrs, patterns=patterns) def test_variables(self): """Test dwim-print with variables.""" @@ -50,7 +66,7 @@ lldbutil.run_to_name_breakpoint(self, "main") vars = ("argc", "argv") for var in vars: - self._expect_cmd(var, "frame variable") + self._expect_cmd(f"dwim-print {var}", "frame variable") def test_variable_paths(self): """Test dwim-print with variable path expressions.""" @@ -58,7 +74,7 @@ lldbutil.run_to_name_breakpoint(self, "main") exprs = ("&argc", "*argv", "argv[0]") for expr in exprs: - self._expect_cmd(expr, "expression --") + self._expect_cmd(f"dwim-print {expr}", "expression") def test_expressions(self): """Test dwim-print with expressions.""" @@ -66,8 +82,14 @@ lldbutil.run_to_name_breakpoint(self, "main") exprs = ("argc + 1", "(void)argc", "(int)abs(argc)") for expr in exprs: - self._expect_cmd(expr, "expression --") + self._expect_cmd(f"dwim-print {expr}", "expression") def test_dummy_target_expressions(self): """Test dwim-print's ability to evaluate expressions without a target.""" - self._expect_cmd("1 + 2", "expression --") + self._expect_cmd("dwim-print 1 + 2", "expression") + + def test_gdb_format(self): + self.build() + lldbutil.run_to_name_breakpoint(self, "main") + self._expect_cmd(f"dwim-print/x argc", "frame variable") + self._expect_cmd(f"dwim-print/x argc + 1", "expression")