Skip to content

Add AllowRepeats to SBCommandInterpreterRunOptions. #94786

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

Merged
merged 4 commits into from
Jun 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ A default SBCommandInterpreterRunOptions object has:
* PrintResults: true
* PrintErrors: true
* AddToHistory: true
* AllowRepeats false

Interactive debug sessions always allow repeats, the AllowRepeats
run option only affects non-interactive sessions.
") lldb::SBCommandInterpreterRunOptions;
8 changes: 8 additions & 0 deletions lldb/include/lldb/API/SBCommandInterpreterRunOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ class LLDB_API SBCommandInterpreterRunOptions {

void SetSpawnThread(bool);

bool GetAllowRepeats() const;

/// By default, RunCommandInterpreter will discard repeats if the
/// IOHandler being used is not interactive. Setting AllowRepeats to true
/// will override this behavior and always process empty lines in the input
/// as a repeat command.
void SetAllowRepeats(bool);

private:
lldb_private::CommandInterpreterRunOptions *get() const;

Expand Down
16 changes: 14 additions & 2 deletions lldb/include/lldb/Interpreter/CommandInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,20 @@ class CommandInterpreterRunOptions {
/// \param[in] add_to_history
/// If \b true add the commands to the command history. If \b false, don't
/// add them.
/// \param[in] handle_repeats
/// If \b true then treat empty lines as repeat commands even if the
/// interpreter is non-interactive.
CommandInterpreterRunOptions(LazyBool stop_on_continue,
LazyBool stop_on_error, LazyBool stop_on_crash,
LazyBool echo_commands, LazyBool echo_comments,
LazyBool print_results, LazyBool print_errors,
LazyBool add_to_history)
LazyBool add_to_history,
LazyBool handle_repeats)
: m_stop_on_continue(stop_on_continue), m_stop_on_error(stop_on_error),
m_stop_on_crash(stop_on_crash), m_echo_commands(echo_commands),
m_echo_comment_commands(echo_comments), m_print_results(print_results),
m_print_errors(print_errors), m_add_to_history(add_to_history) {}
m_print_errors(print_errors), m_add_to_history(add_to_history),
m_allow_repeats(handle_repeats) {}

CommandInterpreterRunOptions() = default;

Expand Down Expand Up @@ -183,6 +188,12 @@ class CommandInterpreterRunOptions {
m_spawn_thread = spawn_thread ? eLazyBoolYes : eLazyBoolNo;
}

bool GetAllowRepeats() const { return DefaultToNo(m_allow_repeats); }
Copy link
Member

Choose a reason for hiding this comment

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

Nit: newline between SetSpawnThread and GetAllowRepeats?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done


void SetAllowRepeats(bool allow_repeats) {
m_allow_repeats = allow_repeats ? eLazyBoolYes : eLazyBoolNo;
}

LazyBool m_stop_on_continue = eLazyBoolCalculate;
LazyBool m_stop_on_error = eLazyBoolCalculate;
LazyBool m_stop_on_crash = eLazyBoolCalculate;
Expand All @@ -193,6 +204,7 @@ class CommandInterpreterRunOptions {
LazyBool m_add_to_history = eLazyBoolCalculate;
LazyBool m_auto_handle_events;
LazyBool m_spawn_thread;
LazyBool m_allow_repeats = eLazyBoolCalculate;

private:
static bool DefaultToYes(LazyBool flag) {
Expand Down
12 changes: 12 additions & 0 deletions lldb/source/API/SBCommandInterpreterRunOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ void SBCommandInterpreterRunOptions::SetSpawnThread(bool spawn_thread) {
m_opaque_up->SetSpawnThread(spawn_thread);
}

bool SBCommandInterpreterRunOptions::GetAllowRepeats() const {
LLDB_INSTRUMENT_VA(this);

return m_opaque_up->GetAllowRepeats();
}

void SBCommandInterpreterRunOptions::SetAllowRepeats(bool allow_repeats) {
LLDB_INSTRUMENT_VA(this, allow_repeats);

m_opaque_up->SetAllowRepeats(allow_repeats);
}

lldb_private::CommandInterpreterRunOptions *
SBCommandInterpreterRunOptions::get() const {
return m_opaque_up.get();
Expand Down
14 changes: 11 additions & 3 deletions lldb/source/Interpreter/CommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2707,7 +2707,8 @@ enum {
eHandleCommandFlagEchoCommentCommand = (1u << 3),
eHandleCommandFlagPrintResult = (1u << 4),
eHandleCommandFlagPrintErrors = (1u << 5),
eHandleCommandFlagStopOnCrash = (1u << 6)
eHandleCommandFlagStopOnCrash = (1u << 6),
eHandleCommandFlagAllowRepeats = (1u << 7)
};

void CommandInterpreter::HandleCommandsFromFile(
Expand Down Expand Up @@ -3129,14 +3130,19 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
return;

const bool is_interactive = io_handler.GetIsInteractive();
if (!is_interactive) {
const bool allow_repeats =
io_handler.GetFlags().Test(eHandleCommandFlagAllowRepeats);

if (!is_interactive && !allow_repeats) {
// When we are not interactive, don't execute blank lines. This will happen
// sourcing a commands file. We don't want blank lines to repeat the
// previous command and cause any errors to occur (like redefining an
// alias, get an error and stop parsing the commands file).
// But obey the AllowRepeats flag if the user has set it.
if (line.empty())
return;

}
if (!is_interactive) {
// When using a non-interactive file handle (like when sourcing commands
// from a file) we need to echo the command out so we don't just see the
// command output and no command...
Expand Down Expand Up @@ -3388,6 +3394,8 @@ CommandInterpreter::GetIOHandler(bool force_create,
flags |= eHandleCommandFlagPrintResult;
if (options->m_print_errors != eLazyBoolNo)
flags |= eHandleCommandFlagPrintErrors;
if (options->m_allow_repeats == eLazyBoolYes)
flags |= eHandleCommandFlagAllowRepeats;
} else {
flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult |
eHandleCommandFlagPrintErrors;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,66 @@ def setUp(self):
TestBase.setUp(self)

self.stdin_path = self.getBuildArtifact("stdin.txt")
self.stdout_path = self.getBuildArtifact("stdout.txt")

def run_commands_string(
self, command_string, options=lldb.SBCommandInterpreterRunOptions()
):
"""Run the commands in command_string through RunCommandInterpreter.
Returns (n_errors, quit_requested, has_crashed, result_string)."""

with open(self.stdin_path, "w") as input_handle:
input_handle.write("nonexistingcommand\nquit")
input_handle.write(command_string)

self.dbg.SetInputFile(open(self.stdin_path, "r"))
n_errors = 0
quit_requested = False
has_crashed = False

# No need to track the output
devnull = open(os.devnull, "w")
self.dbg.SetOutputFile(devnull)
self.dbg.SetErrorFile(devnull)
with open(self.stdin_path, "r") as in_fileH, open(
self.stdout_path, "w"
) as out_fileH:
self.dbg.SetInputFile(in_fileH)

self.dbg.SetOutputFile(out_fileH)
self.dbg.SetErrorFile(out_fileH)

n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(
True, False, options, 0, False, False
)

result_string = None
with open(self.stdout_path, "r") as out_fileH:
result_string = out_fileH.read()

return (n_errors, quit_requested, has_crashed, result_string)

def test_run_session_with_error_and_quit(self):
"""Run non-existing and quit command returns appropriate values"""

n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(
True, False, lldb.SBCommandInterpreterRunOptions(), 0, False, False
n_errors, quit_requested, has_crashed, _ = self.run_commands_string(
"nonexistingcommand\nquit\n"
)

self.assertGreater(n_errors, 0)
self.assertTrue(quit_requested)
self.assertFalse(has_crashed)

def test_allow_repeat(self):
"""Try auto-repeat of process launch - the command will fail and
the auto-repeat will fail because of no auto-repeat."""
options = lldb.SBCommandInterpreterRunOptions()
options.SetEchoCommands(False)
options.SetAllowRepeats(True)

n_errors, quit_requested, has_crashed, result_str = self.run_commands_string(
"process launch\n\n", options
)
self.assertEqual(n_errors, 2)
self.assertFalse(quit_requested)
self.assertFalse(has_crashed)

self.assertIn("invalid target", result_str)
self.assertIn("No auto repeat", result_str)


class SBCommandInterpreterRunOptionsCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
Expand All @@ -86,6 +124,7 @@ def test_command_interpreter_run_options(self):
self.assertTrue(opts.GetPrintResults())
self.assertTrue(opts.GetPrintErrors())
self.assertTrue(opts.GetAddToHistory())
self.assertFalse(opts.GetAllowRepeats())

# Invert values
opts.SetStopOnContinue(not opts.GetStopOnContinue())
Expand All @@ -95,6 +134,7 @@ def test_command_interpreter_run_options(self):
opts.SetPrintResults(not opts.GetPrintResults())
opts.SetPrintErrors(not opts.GetPrintErrors())
opts.SetAddToHistory(not opts.GetAddToHistory())
opts.SetAllowRepeats(not opts.GetAllowRepeats())

# Check the value changed
self.assertTrue(opts.GetStopOnContinue())
Expand All @@ -104,3 +144,4 @@ def test_command_interpreter_run_options(self):
self.assertFalse(opts.GetPrintResults())
self.assertFalse(opts.GetPrintErrors())
self.assertFalse(opts.GetAddToHistory())
self.assertTrue(opts.GetAllowRepeats())
Loading