Fixed an issue where "command source" would not do the right thing:

- empty lines in init files would repeat previous command and cause errors to be displayed
- all options to control showing the command, its output, if it should stop on error or continue, weren't being obeyed.

llvm-svn: 200860
This commit is contained in:
Greg Clayton 2014-02-05 17:57:57 +00:00
parent 9b8aa38e45
commit 340b0309b5
7 changed files with 342 additions and 102 deletions

View File

@ -18,6 +18,7 @@
#include "lldb/lldb-enumerations.h"
#include "lldb/Core/ConstString.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Flags.h"
#include "lldb/Core/StringList.h"
#include "lldb/Core/ValueObjectList.h"
#include "lldb/Host/Mutex.h"
@ -38,7 +39,8 @@ namespace lldb_private {
IOHandler (Debugger &debugger,
const lldb::StreamFileSP &input_sp,
const lldb::StreamFileSP &output_sp,
const lldb::StreamFileSP &error_sp);
const lldb::StreamFileSP &error_sp,
uint32_t flags);
virtual
~IOHandler ();
@ -162,11 +164,45 @@ namespace lldb_private {
m_user_data = user_data;
}
Flags &
GetFlags ()
{
return m_flags;
}
const Flags &
GetFlags () const
{
return m_flags;
}
//------------------------------------------------------------------
/// Check if the input is being supplied interactively by a user
///
/// This will return true if the input stream is a terminal (tty or
/// pty) and can cause IO handlers to do different things (like
/// for a comfirmation when deleting all breakpoints).
//------------------------------------------------------------------
bool
GetIsInteractive ();
//------------------------------------------------------------------
/// Check if the input is coming from a real terminal.
///
/// A real terminal has a valid size with a certain number of rows
/// and colums. If this function returns true, then terminal escape
/// sequences are expected to work (cursor movement escape sequences,
/// clearning lines, etc).
//------------------------------------------------------------------
bool
GetIsRealTerminal ();
protected:
Debugger &m_debugger;
lldb::StreamFileSP m_input_sp;
lldb::StreamFileSP m_output_sp;
lldb::StreamFileSP m_error_sp;
Flags m_flags;
void *m_user_data;
bool m_done;
bool m_active;
@ -340,6 +376,7 @@ namespace lldb_private {
const lldb::StreamFileSP &input_sp,
const lldb::StreamFileSP &output_sp,
const lldb::StreamFileSP &error_sp,
uint32_t flags,
const char *editline_name, // Used for saving history files
const char *prompt,
bool multi_line,
@ -408,9 +445,7 @@ namespace lldb_private {
std::unique_ptr<Editline> m_editline_ap;
IOHandlerDelegate &m_delegate;
std::string m_prompt;
bool m_multi_line;
bool m_interactive;
bool m_multi_line;
};
class IOHandlerConfirm :

View File

@ -52,7 +52,9 @@ public:
m_stream (kInvalidStream),
m_options (0),
m_own_stream (false),
m_own_descriptor (false)
m_own_descriptor (false),
m_is_interactive (eLazyBoolCalculate),
m_is_real_terminal (eLazyBoolCalculate)
{
}
@ -61,7 +63,9 @@ public:
m_stream (fh),
m_options (0),
m_own_stream (transfer_ownership),
m_own_descriptor (false)
m_own_descriptor (false),
m_is_interactive (eLazyBoolCalculate),
m_is_real_terminal (eLazyBoolCalculate)
{
}
@ -462,6 +466,32 @@ public:
static uint32_t
GetPermissions (const char *path, Error &error);
//------------------------------------------------------------------
/// Return true if this file is interactive.
///
/// @return
/// True if this file is a terminal (tty or pty), false
/// otherwise.
//------------------------------------------------------------------
bool
GetIsInteractive ();
//------------------------------------------------------------------
/// Return true if this file from a real terminal.
///
/// Just knowing a file is a interactive isn't enough, we also need
/// to know if the terminal has a width and height so we can do
/// cursor movement and other terminal maninpulations by sending
/// escape sequences.
///
/// @return
/// True if this file is a terminal (tty, not a pty) that has
/// a non-zero width and height, false otherwise.
//------------------------------------------------------------------
bool
GetIsRealTerminal ();
//------------------------------------------------------------------
/// Output printf formatted output to the stream.
///
@ -501,6 +531,9 @@ protected:
return m_stream != kInvalidStream;
}
void
CalculateInteractiveAndTerminal ();
//------------------------------------------------------------------
// Member variables
//------------------------------------------------------------------
@ -509,6 +542,8 @@ protected:
uint32_t m_options;
bool m_own_stream;
bool m_own_descriptor;
LazyBool m_is_interactive;
LazyBool m_is_real_terminal;
};
} // namespace lldb_private

View File

@ -215,10 +215,10 @@ public:
void
HandleCommandsFromFile (FileSpec &file,
ExecutionContext *context,
bool stop_on_continue,
bool stop_on_error,
bool echo_commands,
bool print_results,
LazyBool stop_on_continue,
LazyBool stop_on_error,
LazyBool echo_commands,
LazyBool print_results,
LazyBool add_to_history,
CommandReturnObject &result);
@ -515,6 +515,7 @@ private:
bool m_batch_command_mode;
ChildrenTruncatedWarningStatus m_truncation_warning; // Whether we truncated children and whether the user has been told
uint32_t m_command_source_depth;
std::vector<uint32_t> m_command_source_flags;
};

View File

@ -308,7 +308,9 @@ protected:
CommandOptions (CommandInterpreter &interpreter) :
Options (interpreter),
m_stop_on_error (true)
m_stop_on_error (true),
m_silent_run (false),
m_stop_on_continue (true)
{
}
@ -320,23 +322,21 @@ protected:
{
Error error;
const int short_option = m_getopt_table[option_idx].val;
bool success;
switch (short_option)
{
case 'e':
error = m_stop_on_error.SetValueFromCString(option_arg);
break;
case 'c':
m_stop_on_continue = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("invalid value for stop-on-continue: %s", option_arg);
error = m_stop_on_continue.SetValueFromCString(option_arg);
break;
case 's':
m_silent_run = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("invalid value for silent-run: %s", option_arg);
error = m_silent_run.SetValueFromCString(option_arg);
break;
default:
error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
break;
@ -349,8 +349,8 @@ protected:
OptionParsingStarting ()
{
m_stop_on_error.Clear();
m_silent_run = false;
m_stop_on_continue = true;
m_silent_run.Clear();
m_stop_on_continue.Clear();
}
const OptionDefinition*
@ -366,8 +366,8 @@ protected:
// Instance variables to hold the values for command options.
OptionValueBoolean m_stop_on_error;
bool m_silent_run;
bool m_stop_on_continue;
OptionValueBoolean m_silent_run;
OptionValueBoolean m_stop_on_continue;
};
bool
@ -378,23 +378,42 @@ protected:
{
const char *filename = command.GetArgumentAtIndex(0);
if (!m_interpreter.GetDebugger().GetCommandInterpreter().GetBatchCommandMode())
result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename);
result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename);
FileSpec cmd_file (filename, true);
ExecutionContext *exe_ctx = NULL; // Just use the default context.
bool echo_commands = !m_options.m_silent_run;
bool print_results = true;
bool stop_on_error = m_options.m_stop_on_error.OptionWasSet() ? (bool)m_options.m_stop_on_error : m_interpreter.GetStopCmdSourceOnError();
m_interpreter.HandleCommandsFromFile (cmd_file,
exe_ctx,
m_options.m_stop_on_continue,
stop_on_error,
echo_commands,
print_results,
eLazyBoolCalculate,
result);
// If any options were set, then use them
if (m_options.m_stop_on_error.OptionWasSet() ||
m_options.m_silent_run.OptionWasSet() ||
m_options.m_stop_on_continue.OptionWasSet())
{
// Use user set settings
LazyBool print_command = m_options.m_silent_run.GetCurrentValue() ? eLazyBoolNo : eLazyBoolYes;
m_interpreter.HandleCommandsFromFile (cmd_file,
exe_ctx,
m_options.m_stop_on_continue.GetCurrentValue() ? eLazyBoolYes : eLazyBoolNo, // Stop on continue
m_options.m_stop_on_error.GetCurrentValue() ? eLazyBoolYes : eLazyBoolNo, // Stop on error
print_command, // Echo command
print_command, // Print command output
eLazyBoolCalculate, // Add to history
result);
}
else
{
// No options were set, inherit any settings from nested "command source" commands,
// or set to sane default settings...
m_interpreter.HandleCommandsFromFile (cmd_file,
exe_ctx,
eLazyBoolCalculate, // Stop on continue
eLazyBoolCalculate, // Stop on error
eLazyBoolCalculate, // Echo command
eLazyBoolCalculate, // Print command output
eLazyBoolCalculate, // Add to history
result);
}
}
else
{

View File

@ -10,12 +10,6 @@
#include "lldb/lldb-python.h"
#include <stdio.h> /* ioctl, TIOCGWINSZ */
#ifndef _MSC_VER
#include <sys/ioctl.h> /* ioctl, TIOCGWINSZ */
#endif
#include <string>
#include "lldb/Breakpoint/BreakpointLocation.h"
@ -43,9 +37,10 @@ using namespace lldb_private;
IOHandler::IOHandler (Debugger &debugger) :
IOHandler (debugger,
StreamFileSP(), // Adopt STDIN from top input reader
StreamFileSP(), // Adopt STDOUT from top input reader
StreamFileSP()) // Adopt STDERR from top input reader
StreamFileSP(), // Adopt STDIN from top input reader
StreamFileSP(), // Adopt STDOUT from top input reader
StreamFileSP(), // Adopt STDERR from top input reader
0) // Flags
{
}
@ -53,11 +48,13 @@ IOHandler::IOHandler (Debugger &debugger) :
IOHandler::IOHandler (Debugger &debugger,
const lldb::StreamFileSP &input_sp,
const lldb::StreamFileSP &output_sp,
const lldb::StreamFileSP &error_sp) :
const lldb::StreamFileSP &error_sp,
uint32_t flags) :
m_debugger (debugger),
m_input_sp (input_sp),
m_output_sp (output_sp),
m_error_sp (error_sp),
m_flags (flags),
m_user_data (NULL),
m_done (false),
m_active (false)
@ -141,6 +138,17 @@ IOHandler::GetErrorStreamFile()
return m_error_sp;
}
bool
IOHandler::GetIsInteractive ()
{
return GetInputStreamFile()->GetFile().GetIsInteractive ();
}
bool
IOHandler::GetIsRealTerminal ()
{
return GetInputStreamFile()->GetFile().GetIsRealTerminal();
}
IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger,
const char *prompt,
@ -308,6 +316,7 @@ IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
StreamFileSP(), // Inherit input from top input reader
StreamFileSP(), // Inherit output from top input reader
StreamFileSP(), // Inherit error from top input reader
0, // Flags
editline_name, // Used for saving history files
prompt,
multi_line,
@ -319,32 +328,23 @@ IOHandlerEditline::IOHandlerEditline (Debugger &debugger,
const lldb::StreamFileSP &input_sp,
const lldb::StreamFileSP &output_sp,
const lldb::StreamFileSP &error_sp,
uint32_t flags,
const char *editline_name, // Used for saving history files
const char *prompt,
bool multi_line,
IOHandlerDelegate &delegate) :
IOHandler (debugger, input_sp, output_sp, error_sp),
IOHandler (debugger, input_sp, output_sp, error_sp, flags),
m_editline_ap (),
m_delegate (delegate),
m_prompt (),
m_multi_line (multi_line),
m_interactive (false)
m_multi_line (multi_line)
{
SetPrompt(prompt);
bool use_editline = false;
#ifndef _MSC_VER
const int in_fd = GetInputFD();
struct winsize window_size;
if (isatty (in_fd))
{
m_interactive = true;
if (::ioctl (in_fd, TIOCGWINSZ, &window_size) == 0)
{
if (window_size.ws_col > 0)
use_editline = true;
}
}
use_editline = m_input_sp->GetFile().GetIsRealTerminal();
#else
use_editline = true;
#endif
@ -382,7 +382,7 @@ IOHandlerEditline::GetLine (std::string &line)
FILE *in = GetInputFILE();
if (in)
{
if (m_interactive)
if (GetIsInteractive())
{
const char *prompt = GetPrompt();
if (prompt && prompt[0])
@ -432,7 +432,7 @@ IOHandlerEditline::GetLine (std::string &line)
// No more input file, we are done...
SetIsDone(true);
}
return !line.empty();
return false;
}
}

View File

@ -13,7 +13,9 @@
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef _WIN32
#include "lldb/Host/windows/windows.h"
@ -78,7 +80,9 @@ File::File(const char *path, uint32_t options, uint32_t permissions) :
m_stream (kInvalidStream),
m_options (),
m_own_stream (false),
m_own_descriptor (false)
m_own_descriptor (false),
m_is_interactive (eLazyBoolCalculate),
m_is_real_terminal (eLazyBoolCalculate)
{
Open (path, options, permissions);
}
@ -90,7 +94,10 @@ File::File (const FileSpec& filespec,
m_stream (kInvalidStream),
m_options (0),
m_own_stream (false),
m_own_descriptor (false)
m_own_descriptor (false),
m_is_interactive (eLazyBoolCalculate),
m_is_real_terminal (eLazyBoolCalculate)
{
if (filespec)
{
@ -103,7 +110,9 @@ File::File (const File &rhs) :
m_stream (kInvalidStream),
m_options (0),
m_own_stream (false),
m_own_descriptor (false)
m_own_descriptor (false),
m_is_interactive (eLazyBoolCalculate),
m_is_real_terminal (eLazyBoolCalculate)
{
Duplicate (rhs);
}
@ -371,6 +380,8 @@ File::Close ()
m_options = 0;
m_own_stream = false;
m_own_descriptor = false;
m_is_interactive = eLazyBoolCalculate;
m_is_real_terminal = eLazyBoolCalculate;
return error;
}
@ -866,3 +877,41 @@ File::ConvertOpenOptionsForPOSIXOpen (uint32_t open_options)
return mode;
}
void
File::CalculateInteractiveAndTerminal ()
{
const int fd = GetDescriptor();
if (fd >= 0)
{
m_is_interactive = eLazyBoolNo;
m_is_real_terminal = eLazyBoolNo;
if (isatty(fd))
{
m_is_interactive = eLazyBoolYes;
struct winsize window_size;
if (::ioctl (fd, TIOCGWINSZ, &window_size) == 0)
{
if (window_size.ws_col > 0)
m_is_real_terminal = eLazyBoolYes;
}
}
}
}
bool
File::GetIsInteractive ()
{
if (m_is_interactive == eLazyBoolCalculate)
CalculateInteractiveAndTerminal ();
return m_is_interactive == eLazyBoolYes;
}
bool
File::GetIsRealTerminal ()
{
if (m_is_real_terminal == eLazyBoolCalculate)
CalculateInteractiveAndTerminal();
return m_is_real_terminal == eLazyBoolYes;
}

View File

@ -2390,14 +2390,15 @@ CommandInterpreter::SourceInitFile (bool in_cwd, CommandReturnObject &result)
if (init_file.Exists())
{
ExecutionContext *exe_ctx = NULL; // We don't have any context yet.
bool stop_on_continue = true;
bool stop_on_error = false;
bool echo_commands = false;
bool print_results = false;
const bool saved_batch = SetBatchCommandMode (true);
HandleCommandsFromFile (init_file, exe_ctx, stop_on_continue, stop_on_error, echo_commands, print_results, eLazyBoolNo, result);
HandleCommandsFromFile (init_file,
NULL, // Execution context
eLazyBoolYes, // Stop on continue
eLazyBoolNo, // Stop on error
eLazyBoolNo, // Don't echo commands
eLazyBoolNo, // Don't print command output
eLazyBoolNo, // Don't add the commands that are sourced into the history buffer
result);
SetBatchCommandMode (saved_batch);
}
else
@ -2546,13 +2547,21 @@ CommandInterpreter::HandleCommands (const StringList &commands,
return;
}
// Make flags that we can pass into the IOHandler so our delegates can do the right thing
enum {
eHandleCommandFlagStopOnContinue = (1u << 0),
eHandleCommandFlagStopOnError = (1u << 1),
eHandleCommandFlagEchoCommand = (1u << 2),
eHandleCommandFlagPrintResult = (1u << 3)
};
void
CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file,
ExecutionContext *context,
bool stop_on_continue,
bool stop_on_error,
bool echo_command,
bool print_result,
LazyBool stop_on_continue,
LazyBool stop_on_error,
LazyBool echo_command,
LazyBool print_result,
LazyBool add_to_history,
CommandReturnObject &result)
{
@ -2567,15 +2576,93 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file,
{
Debugger &debugger = GetDebugger();
uint32_t flags = 0;
if (stop_on_continue == eLazyBoolCalculate)
{
if (m_command_source_flags.empty())
{
// Echo command by default
flags |= eHandleCommandFlagStopOnContinue;
}
else if (m_command_source_flags.back() & eHandleCommandFlagStopOnContinue)
{
flags |= eHandleCommandFlagStopOnContinue;
}
}
else if (stop_on_continue == eLazyBoolYes)
{
flags |= eHandleCommandFlagStopOnContinue;
}
if (stop_on_error == eLazyBoolCalculate)
{
if (m_command_source_flags.empty())
{
if (GetStopCmdSourceOnError())
flags |= eHandleCommandFlagStopOnError;
}
else if (m_command_source_flags.back() & eHandleCommandFlagStopOnError)
{
flags |= eHandleCommandFlagStopOnError;
}
}
else if (stop_on_error == eLazyBoolYes)
{
flags |= eHandleCommandFlagStopOnError;
}
if (echo_command == eLazyBoolCalculate)
{
if (m_command_source_flags.empty())
{
// Echo command by default
flags |= eHandleCommandFlagEchoCommand;
}
else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand)
{
flags |= eHandleCommandFlagEchoCommand;
}
}
else if (echo_command == eLazyBoolYes)
{
flags |= eHandleCommandFlagEchoCommand;
}
if (print_result == eLazyBoolCalculate)
{
if (m_command_source_flags.empty())
{
// Echo command by default
flags |= eHandleCommandFlagEchoCommand;
}
else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand)
{
flags |= eHandleCommandFlagEchoCommand;
}
}
else if (print_result == eLazyBoolYes)
{
flags |= eHandleCommandFlagEchoCommand;
}
// Used for inheriting the right settings when "command source" might have
// nested "command source" commands
m_command_source_flags.push_back(flags);
IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger,
input_file_sp,
debugger.GetOutputFile(),
debugger.GetErrorFile(),
flags,
NULL, // Pass in NULL for "editline_name" so no history is saved, or written
m_debugger.GetPrompt(),
false, // Not multi-line
*this));
m_command_source_depth++;
m_debugger.RunIOHandler(io_handler_sp);
if (!m_command_source_flags.empty())
m_command_source_flags.pop_back();
m_command_source_depth--;
result.SetStatus (eReturnStatusSuccessFinishNoResult);
}
else
@ -2583,21 +2670,6 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file,
result.AppendErrorWithFormat ("error: an error occurred read file '%s': %s\n", cmd_file_path.c_str(), error.AsCString());
result.SetStatus (eReturnStatusFailed);
}
#if 0
bool success;
StringList commands;
success = commands.ReadFileLines(cmd_file);
if (!success)
{
result.AppendErrorWithFormat ("Error reading commands from file: %s.\n", cmd_file.GetFilename().AsCString());
result.SetStatus (eReturnStatusFailed);
return;
}
m_command_source_depth++;
HandleCommands (commands, context, stop_on_continue, stop_on_error, echo_command, print_result, add_to_history, result);
m_command_source_depth--;
#endif
}
else
{
@ -2886,31 +2958,59 @@ CommandInterpreter::GetProcessOutput ()
void
CommandInterpreter::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
{
const bool is_interactive = io_handler.GetIsInteractive();
if (is_interactive == false)
{
// 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).
if (line.empty())
return;
// 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...
if (io_handler.GetFlags().Test(eHandleCommandFlagEchoCommand))
io_handler.GetOutputStreamFile()->Printf("%s%s\n", io_handler.GetPrompt(), line.c_str());
}
lldb_private::CommandReturnObject result;
HandleCommand(line.c_str(), eLazyBoolCalculate, result);
// Display any STDOUT/STDERR _prior_ to emitting the command result text
GetProcessOutput ();
// Now emit the command output text from the command we just executed
const char *output = result.GetOutputData();
if (output && output[0])
io_handler.GetOutputStreamFile()->PutCString(output);
if (io_handler.GetFlags().Test(eHandleCommandFlagPrintResult))
{
// Display any STDOUT/STDERR _prior_ to emitting the command result text
GetProcessOutput ();
const char *output = result.GetOutputData();
if (output && output[0])
io_handler.GetOutputStreamFile()->PutCString(output);
// Now emit the command error text from the command we just executed
const char *error = result.GetErrorData();
if (error && error[0])
io_handler.GetErrorStreamFile()->PutCString(error);
// Now emit the command error text from the command we just executed
const char *error = result.GetErrorData();
if (error && error[0])
io_handler.GetErrorStreamFile()->PutCString(error);
}
switch (result.GetStatus())
{
case eReturnStatusInvalid:
case eReturnStatusSuccessFinishNoResult:
case eReturnStatusSuccessFinishResult:
case eReturnStatusStarted:
break;
case eReturnStatusSuccessContinuingNoResult:
case eReturnStatusSuccessContinuingResult:
case eReturnStatusStarted:
if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnContinue))
io_handler.SetIsDone(true);
break;
case eReturnStatusFailed:
if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnError))
io_handler.SetIsDone(true);
break;
case eReturnStatusQuit:
@ -2984,6 +3084,7 @@ CommandInterpreter::RunCommandInterpreter(bool auto_handle_events,
m_debugger.GetInputFile(),
m_debugger.GetOutputFile(),
m_debugger.GetErrorFile(),
eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult,
"lldb",
m_debugger.GetPrompt(),
multiple_lines,