2010-06-09 00:52:24 +08:00
|
|
|
//===-- CommandInterpreter.cpp ----------------------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include <string>
|
2010-10-13 05:57:09 +08:00
|
|
|
#include <vector>
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2011-06-24 01:59:56 +08:00
|
|
|
#include "CommandObjectScript.h"
|
2011-06-24 04:37:26 +08:00
|
|
|
#include "lldb/Interpreter/CommandObjectRegexCommand.h"
|
2011-06-24 01:59:56 +08:00
|
|
|
|
2010-06-13 10:17:17 +08:00
|
|
|
#include "../Commands/CommandObjectApropos.h"
|
|
|
|
#include "../Commands/CommandObjectArgs.h"
|
|
|
|
#include "../Commands/CommandObjectBreakpoint.h"
|
|
|
|
#include "../Commands/CommandObjectDisassemble.h"
|
|
|
|
#include "../Commands/CommandObjectExpression.h"
|
|
|
|
#include "../Commands/CommandObjectFrame.h"
|
|
|
|
#include "../Commands/CommandObjectHelp.h"
|
|
|
|
#include "../Commands/CommandObjectLog.h"
|
|
|
|
#include "../Commands/CommandObjectMemory.h"
|
2011-03-19 09:12:21 +08:00
|
|
|
#include "../Commands/CommandObjectPlatform.h"
|
2010-06-13 10:17:17 +08:00
|
|
|
#include "../Commands/CommandObjectProcess.h"
|
|
|
|
#include "../Commands/CommandObjectQuit.h"
|
|
|
|
#include "../Commands/CommandObjectRegister.h"
|
|
|
|
#include "../Commands/CommandObjectSettings.h"
|
|
|
|
#include "../Commands/CommandObjectSource.h"
|
2010-07-07 11:36:20 +08:00
|
|
|
#include "../Commands/CommandObjectCommands.h"
|
2010-06-13 10:17:17 +08:00
|
|
|
#include "../Commands/CommandObjectSyntax.h"
|
|
|
|
#include "../Commands/CommandObjectTarget.h"
|
|
|
|
#include "../Commands/CommandObjectThread.h"
|
2011-06-24 01:59:56 +08:00
|
|
|
#include "../Commands/CommandObjectType.h"
|
2010-12-24 04:21:44 +08:00
|
|
|
#include "../Commands/CommandObjectVersion.h"
|
2011-09-23 02:04:58 +08:00
|
|
|
#include "../Commands/CommandObjectWatchpoint.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2010-06-16 03:49:27 +08:00
|
|
|
#include "lldb/Interpreter/Args.h"
|
2011-05-07 05:37:15 +08:00
|
|
|
#include "lldb/Interpreter/Options.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Core/Debugger.h"
|
2010-10-05 03:49:29 +08:00
|
|
|
#include "lldb/Core/InputReader.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Core/Stream.h"
|
|
|
|
#include "lldb/Core/Timer.h"
|
2011-02-01 09:31:41 +08:00
|
|
|
#include "lldb/Host/Host.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Target/Process.h"
|
|
|
|
#include "lldb/Target/Thread.h"
|
|
|
|
#include "lldb/Target/TargetList.h"
|
2010-11-10 12:57:04 +08:00
|
|
|
#include "lldb/Utility/CleanUp.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
#include "lldb/Interpreter/CommandReturnObject.h"
|
|
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
2011-01-14 08:29:16 +08:00
|
|
|
#include "lldb/Interpreter/ScriptInterpreterNone.h"
|
|
|
|
#include "lldb/Interpreter/ScriptInterpreterPython.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
using namespace lldb;
|
|
|
|
using namespace lldb_private;
|
|
|
|
|
|
|
|
CommandInterpreter::CommandInterpreter
|
|
|
|
(
|
2010-06-23 09:19:29 +08:00
|
|
|
Debugger &debugger,
|
2010-06-09 00:52:24 +08:00
|
|
|
ScriptLanguage script_language,
|
2010-06-23 09:19:29 +08:00
|
|
|
bool synchronous_execution
|
2010-06-09 00:52:24 +08:00
|
|
|
) :
|
2010-10-31 11:01:06 +08:00
|
|
|
Broadcaster ("lldb.command-interpreter"),
|
2010-06-23 09:19:29 +08:00
|
|
|
m_debugger (debugger),
|
2010-10-11 09:05:37 +08:00
|
|
|
m_synchronous_execution (synchronous_execution),
|
2011-01-14 08:29:16 +08:00
|
|
|
m_skip_lldbinit_files (false),
|
2011-08-13 07:34:31 +08:00
|
|
|
m_skip_app_init_files (false),
|
2011-02-18 08:54:25 +08:00
|
|
|
m_script_interpreter_ap (),
|
2011-06-17 00:27:19 +08:00
|
|
|
m_comment_char ('#'),
|
2011-07-12 11:12:18 +08:00
|
|
|
m_repeat_char ('!'),
|
2011-08-16 03:24:02 +08:00
|
|
|
m_batch_command_mode (false),
|
|
|
|
m_truncation_warning(eNoTruncation)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-09-04 08:03:46 +08:00
|
|
|
const char *dbg_name = debugger.GetInstanceName().AsCString();
|
|
|
|
std::string lang_name = ScriptInterpreter::LanguageToString (script_language);
|
|
|
|
StreamString var_name;
|
|
|
|
var_name.Printf ("[%s].script-lang", dbg_name);
|
2010-09-09 14:25:08 +08:00
|
|
|
debugger.GetSettingsController()->SetVariable (var_name.GetData(), lang_name.c_str(),
|
2011-03-25 05:19:54 +08:00
|
|
|
eVarSetOperationAssign, false,
|
2010-10-31 11:01:06 +08:00
|
|
|
m_debugger.GetInstanceName().AsCString());
|
|
|
|
SetEventName (eBroadcastBitThreadShouldExit, "thread-should-exit");
|
|
|
|
SetEventName (eBroadcastBitResetPrompt, "reset-prompt");
|
|
|
|
SetEventName (eBroadcastBitQuitCommandReceived, "quit");
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::Initialize ()
|
|
|
|
{
|
|
|
|
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
|
|
|
|
|
|
|
|
CommandReturnObject result;
|
|
|
|
|
|
|
|
LoadCommandDictionary ();
|
|
|
|
|
|
|
|
// Set up some initial aliases.
|
2011-05-07 05:37:15 +08:00
|
|
|
CommandObjectSP cmd_obj_sp = GetCommandSPExact ("quit", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
{
|
|
|
|
AddAlias ("q", cmd_obj_sp);
|
|
|
|
AddAlias ("exit", cmd_obj_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("process continue", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
{
|
|
|
|
AddAlias ("c", cmd_obj_sp);
|
|
|
|
AddAlias ("continue", cmd_obj_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("_regexp-break",false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
AddAlias ("b", cmd_obj_sp);
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("thread backtrace", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
AddAlias ("bt", cmd_obj_sp);
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("thread step-inst", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
AddAlias ("si", cmd_obj_sp);
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("thread step-in", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
{
|
|
|
|
AddAlias ("s", cmd_obj_sp);
|
|
|
|
AddAlias ("step", cmd_obj_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("thread step-over", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
{
|
|
|
|
AddAlias ("n", cmd_obj_sp);
|
|
|
|
AddAlias ("next", cmd_obj_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("thread step-out", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
{
|
|
|
|
AddAlias ("f", cmd_obj_sp);
|
|
|
|
AddAlias ("finish", cmd_obj_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("source list", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
{
|
|
|
|
AddAlias ("l", cmd_obj_sp);
|
|
|
|
AddAlias ("list", cmd_obj_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("memory read", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
AddAlias ("x", cmd_obj_sp);
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("_regexp-up", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
AddAlias ("up", cmd_obj_sp);
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("_regexp-down", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
AddAlias ("down", cmd_obj_sp);
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("target create", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
AddAlias ("file", cmd_obj_sp);
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("target modules", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
AddAlias ("image", cmd_obj_sp);
|
|
|
|
|
|
|
|
|
|
|
|
OptionArgVectorSP alias_arguments_vector_sp (new OptionArgVector);
|
2011-03-22 10:29:32 +08:00
|
|
|
|
2011-05-07 05:37:15 +08:00
|
|
|
cmd_obj_sp = GetCommandSPExact ("expression", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
{
|
|
|
|
AddAlias ("expr", cmd_obj_sp);
|
|
|
|
|
|
|
|
ProcessAliasOptionsArgs (cmd_obj_sp, "--", alias_arguments_vector_sp);
|
|
|
|
AddAlias ("p", cmd_obj_sp);
|
|
|
|
AddAlias ("print", cmd_obj_sp);
|
|
|
|
AddOrReplaceAliasOptions ("p", alias_arguments_vector_sp);
|
|
|
|
AddOrReplaceAliasOptions ("print", alias_arguments_vector_sp);
|
|
|
|
|
|
|
|
alias_arguments_vector_sp.reset (new OptionArgVector);
|
|
|
|
ProcessAliasOptionsArgs (cmd_obj_sp, "-o --", alias_arguments_vector_sp);
|
|
|
|
AddAlias ("po", cmd_obj_sp);
|
|
|
|
AddOrReplaceAliasOptions ("po", alias_arguments_vector_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_obj_sp = GetCommandSPExact ("process launch", false);
|
|
|
|
if (cmd_obj_sp)
|
|
|
|
{
|
|
|
|
alias_arguments_vector_sp.reset (new OptionArgVector);
|
|
|
|
ProcessAliasOptionsArgs (cmd_obj_sp, "--", alias_arguments_vector_sp);
|
|
|
|
AddAlias ("r", cmd_obj_sp);
|
|
|
|
AddAlias ("run", cmd_obj_sp);
|
|
|
|
AddOrReplaceAliasOptions ("r", alias_arguments_vector_sp);
|
|
|
|
AddOrReplaceAliasOptions ("run", alias_arguments_vector_sp);
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
CommandInterpreter::ProcessEmbeddedScriptCommands (const char *arg)
|
|
|
|
{
|
|
|
|
// This function has not yet been implemented.
|
|
|
|
|
|
|
|
// Look for any embedded script command
|
|
|
|
// If found,
|
|
|
|
// get interpreter object from the command dictionary,
|
|
|
|
// call execute_one_command on it,
|
|
|
|
// get the results as a string,
|
|
|
|
// substitute that string for current stuff.
|
|
|
|
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::LoadCommandDictionary ()
|
|
|
|
{
|
|
|
|
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
|
|
|
|
|
|
|
|
// **** IMPORTANT **** IMPORTANT *** IMPORTANT *** **** IMPORTANT **** IMPORTANT *** IMPORTANT ***
|
|
|
|
//
|
|
|
|
// Command objects that are used as cross reference objects (i.e. they inherit from CommandObjectCrossref)
|
|
|
|
// *MUST* be created and put into the command dictionary *BEFORE* any multi-word commands (which may use
|
|
|
|
// the cross-referencing stuff) are created!!!
|
|
|
|
//
|
|
|
|
// **** IMPORTANT **** IMPORTANT *** IMPORTANT *** **** IMPORTANT **** IMPORTANT *** IMPORTANT ***
|
|
|
|
|
|
|
|
|
|
|
|
// Command objects that inherit from CommandObjectCrossref must be created before other command objects
|
|
|
|
// are created. This is so that when another command is created that needs to go into a crossref object,
|
|
|
|
// the crossref object exists and is ready to take the cross reference. Put the cross referencing command
|
|
|
|
// objects into the CommandDictionary now, so they are ready for use when the other commands get created.
|
|
|
|
|
|
|
|
// Non-CommandObjectCrossref commands can now be created.
|
|
|
|
|
2010-09-21 04:44:43 +08:00
|
|
|
lldb::ScriptLanguage script_language = m_debugger.GetScriptLanguage();
|
2010-09-04 08:03:46 +08:00
|
|
|
|
2010-09-18 09:14:36 +08:00
|
|
|
m_command_dict["apropos"] = CommandObjectSP (new CommandObjectApropos (*this));
|
2010-06-23 09:19:29 +08:00
|
|
|
m_command_dict["breakpoint"]= CommandObjectSP (new CommandObjectMultiwordBreakpoint (*this));
|
2010-09-18 09:14:36 +08:00
|
|
|
//m_command_dict["call"] = CommandObjectSP (new CommandObjectCall (*this));
|
2011-04-21 08:39:18 +08:00
|
|
|
m_command_dict["command"] = CommandObjectSP (new CommandObjectMultiwordCommands (*this));
|
2010-09-18 09:14:36 +08:00
|
|
|
m_command_dict["disassemble"] = CommandObjectSP (new CommandObjectDisassemble (*this));
|
|
|
|
m_command_dict["expression"]= CommandObjectSP (new CommandObjectExpression (*this));
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 16:33:37 +08:00
|
|
|
// m_command_dict["file"] = CommandObjectSP (new CommandObjectFile (*this));
|
2010-06-23 09:19:29 +08:00
|
|
|
m_command_dict["frame"] = CommandObjectSP (new CommandObjectMultiwordFrame (*this));
|
2010-09-18 09:14:36 +08:00
|
|
|
m_command_dict["help"] = CommandObjectSP (new CommandObjectHelp (*this));
|
2011-05-04 06:09:39 +08:00
|
|
|
/// m_command_dict["image"] = CommandObjectSP (new CommandObjectImage (*this));
|
2010-06-23 09:19:29 +08:00
|
|
|
m_command_dict["log"] = CommandObjectSP (new CommandObjectLog (*this));
|
|
|
|
m_command_dict["memory"] = CommandObjectSP (new CommandObjectMemory (*this));
|
2011-03-19 09:12:21 +08:00
|
|
|
m_command_dict["platform"] = CommandObjectSP (new CommandObjectPlatform (*this));
|
2010-06-23 09:19:29 +08:00
|
|
|
m_command_dict["process"] = CommandObjectSP (new CommandObjectMultiwordProcess (*this));
|
2010-09-18 09:14:36 +08:00
|
|
|
m_command_dict["quit"] = CommandObjectSP (new CommandObjectQuit (*this));
|
2010-06-23 09:19:29 +08:00
|
|
|
m_command_dict["register"] = CommandObjectSP (new CommandObjectRegister (*this));
|
2010-09-18 09:14:36 +08:00
|
|
|
m_command_dict["script"] = CommandObjectSP (new CommandObjectScript (*this, script_language));
|
2010-09-04 08:03:46 +08:00
|
|
|
m_command_dict["settings"] = CommandObjectSP (new CommandObjectMultiwordSettings (*this));
|
2010-07-07 11:36:20 +08:00
|
|
|
m_command_dict["source"] = CommandObjectSP (new CommandObjectMultiwordSource (*this));
|
2010-06-23 09:19:29 +08:00
|
|
|
m_command_dict["target"] = CommandObjectSP (new CommandObjectMultiwordTarget (*this));
|
|
|
|
m_command_dict["thread"] = CommandObjectSP (new CommandObjectMultiwordThread (*this));
|
2011-08-17 07:24:13 +08:00
|
|
|
m_command_dict["type"] = CommandObjectSP (new CommandObjectType (*this));
|
2010-12-24 04:21:44 +08:00
|
|
|
m_command_dict["version"] = CommandObjectSP (new CommandObjectVersion (*this));
|
2011-09-23 02:04:58 +08:00
|
|
|
m_command_dict["watchpoint"]= CommandObjectSP (new CommandObjectMultiwordWatchpoint (*this));
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
std::auto_ptr<CommandObjectRegexCommand>
|
2010-09-18 09:14:36 +08:00
|
|
|
break_regex_cmd_ap(new CommandObjectRegexCommand (*this,
|
2011-04-12 13:54:46 +08:00
|
|
|
"_regexp-break",
|
2010-09-08 06:38:08 +08:00
|
|
|
"Set a breakpoint using a regular expression to specify the location.",
|
2011-04-12 13:54:46 +08:00
|
|
|
"_regexp-break [<filename>:<linenum>]\n_regexp-break [<address>]\n_regexp-break <...>", 2));
|
2010-06-09 00:52:24 +08:00
|
|
|
if (break_regex_cmd_ap.get())
|
|
|
|
{
|
|
|
|
if (break_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "breakpoint set --file '%1' --line %2") &&
|
|
|
|
break_regex_cmd_ap->AddRegexCommand("^(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1") &&
|
|
|
|
break_regex_cmd_ap->AddRegexCommand("^[\"']?([-+]\\[.*\\])[\"']?[[:space:]]*$", "breakpoint set --name '%1'") &&
|
2011-04-12 13:54:46 +08:00
|
|
|
break_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full") &&
|
2010-06-09 00:52:24 +08:00
|
|
|
break_regex_cmd_ap->AddRegexCommand("^(-.*)$", "breakpoint set %1") &&
|
A few of the issue I have been trying to track down and fix have been due to
the way LLDB lazily gets complete definitions for types within the debug info.
When we run across a class/struct/union definition in the DWARF, we will only
parse the full definition if we need to. This works fine for top level types
that are assigned directly to variables and arguments, but when we have a
variable with a class, lets say "A" for this example, that has a member:
"B *m_b". Initially we don't need to hunt down a definition for this class
unless we are ever asked to do something with it ("expr m_b->getDecl()" for
example). With my previous approach to lazy type completion, we would be able
to take a "A *a" and get a complete type for it, but we wouldn't be able to
then do an "a->m_b->getDecl()" unless we always expanded all types within a
class prior to handing out the type. Expanding everything is very costly and
it would be great if there were a better way.
A few months ago I worked with the llvm/clang folks to have the
ExternalASTSource class be able to complete classes if there weren't completed
yet:
class ExternalASTSource {
....
virtual void
CompleteType (clang::TagDecl *Tag);
virtual void
CompleteType (clang::ObjCInterfaceDecl *Class);
};
This was great, because we can now have the class that is producing the AST
(SymbolFileDWARF and SymbolFileDWARFDebugMap) sign up as external AST sources
and the object that creates the forward declaration types can now also
complete them anywhere within the clang type system.
This patch makes a few major changes:
- lldb_private::Module classes now own the AST context. Previously the TypeList
objects did.
- The DWARF parsers now sign up as an external AST sources so they can complete
types.
- All of the pure clang type system wrapper code we have in LLDB (ClangASTContext,
ClangASTType, and more) can now be iterating through children of any type,
and if a class/union/struct type (clang::RecordType or ObjC interface)
is found that is incomplete, we can ask the AST to get the definition.
- The SymbolFileDWARFDebugMap class now will create and use a single AST that
all child SymbolFileDWARF classes will share (much like what happens when
we have a complete linked DWARF for an executable).
We will need to modify some of the ClangUserExpression code to take more
advantage of this completion ability in the near future. Meanwhile we should
be better off now that we can be accessing any children of variables through
pointers and always be able to resolve the clang type if needed.
llvm-svn: 123613
2011-01-17 11:46:26 +08:00
|
|
|
break_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%2' --shlib '%1'") &&
|
2010-06-09 00:52:24 +08:00
|
|
|
break_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%1'"))
|
|
|
|
{
|
|
|
|
CommandObjectSP break_regex_cmd_sp(break_regex_cmd_ap.release());
|
|
|
|
m_command_dict[break_regex_cmd_sp->GetCommandName ()] = break_regex_cmd_sp;
|
|
|
|
}
|
|
|
|
}
|
2011-03-22 10:29:32 +08:00
|
|
|
|
|
|
|
std::auto_ptr<CommandObjectRegexCommand>
|
|
|
|
down_regex_cmd_ap(new CommandObjectRegexCommand (*this,
|
2011-04-12 13:54:46 +08:00
|
|
|
"_regexp-down",
|
|
|
|
"Go down \"n\" frames in the stack (1 frame by default).",
|
|
|
|
"_regexp-down [n]", 2));
|
2011-03-22 10:29:32 +08:00
|
|
|
if (down_regex_cmd_ap.get())
|
|
|
|
{
|
|
|
|
if (down_regex_cmd_ap->AddRegexCommand("^$", "frame select -r -1") &&
|
|
|
|
down_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r -%1"))
|
|
|
|
{
|
|
|
|
CommandObjectSP down_regex_cmd_sp(down_regex_cmd_ap.release());
|
|
|
|
m_command_dict[down_regex_cmd_sp->GetCommandName ()] = down_regex_cmd_sp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::auto_ptr<CommandObjectRegexCommand>
|
|
|
|
up_regex_cmd_ap(new CommandObjectRegexCommand (*this,
|
2011-04-12 13:54:46 +08:00
|
|
|
"_regexp-up",
|
|
|
|
"Go up \"n\" frames in the stack (1 frame by default).",
|
|
|
|
"_regexp-up [n]", 2));
|
2011-03-22 10:29:32 +08:00
|
|
|
if (up_regex_cmd_ap.get())
|
|
|
|
{
|
|
|
|
if (up_regex_cmd_ap->AddRegexCommand("^$", "frame select -r 1") &&
|
|
|
|
up_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r %1"))
|
|
|
|
{
|
|
|
|
CommandObjectSP up_regex_cmd_sp(up_regex_cmd_ap.release());
|
|
|
|
m_command_dict[up_regex_cmd_sp->GetCommandName ()] = up_regex_cmd_sp;
|
|
|
|
}
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CommandInterpreter::GetCommandNamesMatchingPartialString (const char *cmd_str, bool include_aliases,
|
|
|
|
StringList &matches)
|
|
|
|
{
|
|
|
|
CommandObject::AddNamesMatchingPartialString (m_command_dict, cmd_str, matches);
|
|
|
|
|
|
|
|
if (include_aliases)
|
|
|
|
{
|
|
|
|
CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd_str, matches);
|
|
|
|
}
|
|
|
|
|
|
|
|
return matches.GetSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
CommandObjectSP
|
|
|
|
CommandInterpreter::GetCommandSP (const char *cmd_cstr, bool include_aliases, bool exact, StringList *matches)
|
|
|
|
{
|
|
|
|
CommandObject::CommandMap::iterator pos;
|
|
|
|
CommandObjectSP ret_val;
|
|
|
|
|
|
|
|
std::string cmd(cmd_cstr);
|
|
|
|
|
|
|
|
if (HasCommands())
|
|
|
|
{
|
|
|
|
pos = m_command_dict.find(cmd);
|
|
|
|
if (pos != m_command_dict.end())
|
|
|
|
ret_val = pos->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (include_aliases && HasAliases())
|
|
|
|
{
|
|
|
|
pos = m_alias_dict.find(cmd);
|
|
|
|
if (pos != m_alias_dict.end())
|
|
|
|
ret_val = pos->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HasUserCommands())
|
|
|
|
{
|
|
|
|
pos = m_user_dict.find(cmd);
|
|
|
|
if (pos != m_user_dict.end())
|
|
|
|
ret_val = pos->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!exact && ret_val == NULL)
|
|
|
|
{
|
2010-07-07 06:46:59 +08:00
|
|
|
// We will only get into here if we didn't find any exact matches.
|
|
|
|
|
|
|
|
CommandObjectSP user_match_sp, alias_match_sp, real_match_sp;
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
StringList local_matches;
|
|
|
|
if (matches == NULL)
|
|
|
|
matches = &local_matches;
|
|
|
|
|
2010-07-07 06:46:59 +08:00
|
|
|
unsigned int num_cmd_matches = 0;
|
|
|
|
unsigned int num_alias_matches = 0;
|
|
|
|
unsigned int num_user_matches = 0;
|
|
|
|
|
|
|
|
// Look through the command dictionaries one by one, and if we get only one match from any of
|
|
|
|
// them in toto, then return that, otherwise return an empty CommandObjectSP and the list of matches.
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
if (HasCommands())
|
|
|
|
{
|
|
|
|
num_cmd_matches = CommandObject::AddNamesMatchingPartialString (m_command_dict, cmd_cstr, *matches);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_cmd_matches == 1)
|
|
|
|
{
|
|
|
|
cmd.assign(matches->GetStringAtIndex(0));
|
|
|
|
pos = m_command_dict.find(cmd);
|
|
|
|
if (pos != m_command_dict.end())
|
2010-07-07 06:46:59 +08:00
|
|
|
real_match_sp = pos->second;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2010-06-25 04:28:42 +08:00
|
|
|
if (include_aliases && HasAliases())
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
num_alias_matches = CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd_cstr, *matches);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-07-07 06:46:59 +08:00
|
|
|
if (num_alias_matches == 1)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
cmd.assign(matches->GetStringAtIndex (num_cmd_matches));
|
|
|
|
pos = m_alias_dict.find(cmd);
|
|
|
|
if (pos != m_alias_dict.end())
|
2010-07-07 06:46:59 +08:00
|
|
|
alias_match_sp = pos->second;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2010-06-25 04:28:42 +08:00
|
|
|
if (HasUserCommands())
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
num_user_matches = CommandObject::AddNamesMatchingPartialString (m_user_dict, cmd_cstr, *matches);
|
|
|
|
}
|
|
|
|
|
2010-07-07 06:46:59 +08:00
|
|
|
if (num_user_matches == 1)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
cmd.assign (matches->GetStringAtIndex (num_cmd_matches + num_alias_matches));
|
|
|
|
|
|
|
|
pos = m_user_dict.find (cmd);
|
|
|
|
if (pos != m_user_dict.end())
|
2010-07-07 06:46:59 +08:00
|
|
|
user_match_sp = pos->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got exactly one match, return that, otherwise return the match list.
|
|
|
|
|
|
|
|
if (num_user_matches + num_cmd_matches + num_alias_matches == 1)
|
|
|
|
{
|
|
|
|
if (num_cmd_matches)
|
|
|
|
return real_match_sp;
|
|
|
|
else if (num_alias_matches)
|
|
|
|
return alias_match_sp;
|
|
|
|
else
|
|
|
|
return user_match_sp;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
}
|
2010-07-07 06:46:59 +08:00
|
|
|
else if (matches && ret_val != NULL)
|
|
|
|
{
|
|
|
|
matches->AppendString (cmd_cstr);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
|
2011-04-21 00:37:46 +08:00
|
|
|
bool
|
|
|
|
CommandInterpreter::AddCommand (const char *name, const lldb::CommandObjectSP &cmd_sp, bool can_replace)
|
|
|
|
{
|
|
|
|
if (name && name[0])
|
|
|
|
{
|
|
|
|
std::string name_sstr(name);
|
|
|
|
if (!can_replace)
|
|
|
|
{
|
|
|
|
if (m_command_dict.find (name_sstr) != m_command_dict.end())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_command_dict[name_sstr] = cmd_sp;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-08-17 07:24:13 +08:00
|
|
|
bool
|
|
|
|
CommandInterpreter::AddUserCommand (const char *name,
|
|
|
|
const lldb::CommandObjectSP &cmd_sp,
|
|
|
|
bool can_replace)
|
|
|
|
{
|
|
|
|
if (name && name[0])
|
|
|
|
{
|
|
|
|
std::string name_sstr(name);
|
|
|
|
if (!can_replace)
|
|
|
|
{
|
|
|
|
if (m_user_dict.find (name_sstr) != m_user_dict.end())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_user_dict[name_sstr] = cmd_sp;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2011-04-21 00:37:46 +08:00
|
|
|
|
2010-07-07 06:46:59 +08:00
|
|
|
CommandObjectSP
|
|
|
|
CommandInterpreter::GetCommandSPExact (const char *cmd_cstr, bool include_aliases)
|
|
|
|
{
|
2010-12-15 02:51:39 +08:00
|
|
|
Args cmd_words (cmd_cstr); // Break up the command string into words, in case it's a multi-word command.
|
|
|
|
CommandObjectSP ret_val; // Possibly empty return value.
|
|
|
|
|
|
|
|
if (cmd_cstr == NULL)
|
|
|
|
return ret_val;
|
|
|
|
|
|
|
|
if (cmd_words.GetArgumentCount() == 1)
|
|
|
|
return GetCommandSP(cmd_cstr, include_aliases, true, NULL);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We have a multi-word command (seemingly), so we need to do more work.
|
|
|
|
// First, get the cmd_obj_sp for the first word in the command.
|
|
|
|
CommandObjectSP cmd_obj_sp = GetCommandSP (cmd_words.GetArgumentAtIndex (0), include_aliases, true, NULL);
|
|
|
|
if (cmd_obj_sp.get() != NULL)
|
|
|
|
{
|
|
|
|
// Loop through the rest of the words in the command (everything passed in was supposed to be part of a
|
|
|
|
// command name), and find the appropriate sub-command SP for each command word....
|
|
|
|
size_t end = cmd_words.GetArgumentCount();
|
|
|
|
for (size_t j= 1; j < end; ++j)
|
|
|
|
{
|
|
|
|
if (cmd_obj_sp->IsMultiwordObject())
|
|
|
|
{
|
|
|
|
cmd_obj_sp = ((CommandObjectMultiword *) cmd_obj_sp.get())->GetSubcommandSP
|
|
|
|
(cmd_words.GetArgumentAtIndex (j));
|
|
|
|
if (cmd_obj_sp.get() == NULL)
|
|
|
|
// The sub-command name was invalid. Fail and return the empty 'ret_val'.
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// We have more words in the command name, but we don't have a multiword object. Fail and return
|
|
|
|
// empty 'ret_val'.
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
// We successfully looped through all the command words and got valid command objects for them. Assign the
|
|
|
|
// last object retrieved to 'ret_val'.
|
|
|
|
ret_val = cmd_obj_sp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret_val;
|
2010-07-07 06:46:59 +08:00
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
CommandObject *
|
2010-07-07 06:46:59 +08:00
|
|
|
CommandInterpreter::GetCommandObjectExact (const char *cmd_cstr, bool include_aliases)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-07-07 06:46:59 +08:00
|
|
|
return GetCommandSPExact (cmd_cstr, include_aliases).get();
|
|
|
|
}
|
|
|
|
|
|
|
|
CommandObject *
|
|
|
|
CommandInterpreter::GetCommandObject (const char *cmd_cstr, StringList *matches)
|
|
|
|
{
|
|
|
|
CommandObject *command_obj = GetCommandSP (cmd_cstr, false, true, matches).get();
|
|
|
|
|
|
|
|
// If we didn't find an exact match to the command string in the commands, look in
|
|
|
|
// the aliases.
|
|
|
|
|
|
|
|
if (command_obj == NULL)
|
|
|
|
{
|
|
|
|
command_obj = GetCommandSP (cmd_cstr, true, true, matches).get();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, if there wasn't an exact match among the aliases, look for an inexact match
|
|
|
|
// in both the commands and the aliases.
|
|
|
|
|
|
|
|
if (command_obj == NULL)
|
|
|
|
command_obj = GetCommandSP(cmd_cstr, true, false, matches).get();
|
|
|
|
|
|
|
|
return command_obj;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::CommandExists (const char *cmd)
|
|
|
|
{
|
|
|
|
return m_command_dict.find(cmd) != m_command_dict.end();
|
|
|
|
}
|
|
|
|
|
2011-05-07 05:37:15 +08:00
|
|
|
bool
|
|
|
|
CommandInterpreter::ProcessAliasOptionsArgs (lldb::CommandObjectSP &cmd_obj_sp,
|
|
|
|
const char *options_args,
|
|
|
|
OptionArgVectorSP &option_arg_vector_sp)
|
|
|
|
{
|
|
|
|
bool success = true;
|
|
|
|
OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
|
|
|
|
|
|
|
|
if (!options_args || (strlen (options_args) < 1))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
std::string options_string (options_args);
|
|
|
|
Args args (options_args);
|
|
|
|
CommandReturnObject result;
|
|
|
|
// Check to see if the command being aliased can take any command options.
|
|
|
|
Options *options = cmd_obj_sp->GetOptions ();
|
|
|
|
if (options)
|
|
|
|
{
|
|
|
|
// See if any options were specified as part of the alias; if so, handle them appropriately.
|
|
|
|
options->NotifyOptionParsingStarting ();
|
|
|
|
args.Unshift ("dummy_arg");
|
|
|
|
args.ParseAliasOptions (*options, result, option_arg_vector, options_string);
|
|
|
|
args.Shift ();
|
|
|
|
if (result.Succeeded())
|
|
|
|
options->VerifyPartialOptions (result);
|
|
|
|
if (!result.Succeeded() && result.GetStatus() != lldb::eReturnStatusStarted)
|
|
|
|
{
|
|
|
|
result.AppendError ("Unable to create requested alias.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options_string.size() > 0)
|
|
|
|
{
|
|
|
|
if (cmd_obj_sp->WantsRawCommandString ())
|
|
|
|
option_arg_vector->push_back (OptionArgPair ("<argument>",
|
|
|
|
OptionArgValue (-1,
|
|
|
|
options_string)));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int argc = args.GetArgumentCount();
|
|
|
|
for (size_t i = 0; i < argc; ++i)
|
|
|
|
if (strcmp (args.GetArgumentAtIndex (i), "") != 0)
|
|
|
|
option_arg_vector->push_back
|
|
|
|
(OptionArgPair ("<argument>",
|
|
|
|
OptionArgValue (-1,
|
|
|
|
std::string (args.GetArgumentAtIndex (i)))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
bool
|
|
|
|
CommandInterpreter::AliasExists (const char *cmd)
|
|
|
|
{
|
|
|
|
return m_alias_dict.find(cmd) != m_alias_dict.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::UserCommandExists (const char *cmd)
|
|
|
|
{
|
|
|
|
return m_user_dict.find(cmd) != m_user_dict.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::AddAlias (const char *alias_name, CommandObjectSP& command_obj_sp)
|
|
|
|
{
|
2010-07-07 06:46:59 +08:00
|
|
|
command_obj_sp->SetIsAlias (true);
|
2010-06-09 00:52:24 +08:00
|
|
|
m_alias_dict[alias_name] = command_obj_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::RemoveAlias (const char *alias_name)
|
|
|
|
{
|
|
|
|
CommandObject::CommandMap::iterator pos = m_alias_dict.find(alias_name);
|
|
|
|
if (pos != m_alias_dict.end())
|
|
|
|
{
|
|
|
|
m_alias_dict.erase(pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool
|
|
|
|
CommandInterpreter::RemoveUser (const char *alias_name)
|
|
|
|
{
|
|
|
|
CommandObject::CommandMap::iterator pos = m_user_dict.find(alias_name);
|
|
|
|
if (pos != m_user_dict.end())
|
|
|
|
{
|
|
|
|
m_user_dict.erase(pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::GetAliasHelp (const char *alias_name, const char *command_name, StreamString &help_string)
|
|
|
|
{
|
|
|
|
help_string.Printf ("'%s", command_name);
|
|
|
|
OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name);
|
|
|
|
|
|
|
|
if (option_arg_vector_sp != NULL)
|
|
|
|
{
|
|
|
|
OptionArgVector *options = option_arg_vector_sp.get();
|
|
|
|
for (int i = 0; i < options->size(); ++i)
|
|
|
|
{
|
|
|
|
OptionArgPair cur_option = (*options)[i];
|
|
|
|
std::string opt = cur_option.first;
|
2010-12-08 03:58:26 +08:00
|
|
|
OptionArgValue value_pair = cur_option.second;
|
|
|
|
std::string value = value_pair.second;
|
2010-06-09 00:52:24 +08:00
|
|
|
if (opt.compare("<argument>") == 0)
|
|
|
|
{
|
|
|
|
help_string.Printf (" %s", value.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
help_string.Printf (" %s", opt.c_str());
|
|
|
|
if ((value.compare ("<no-argument>") != 0)
|
|
|
|
&& (value.compare ("<need-argument") != 0))
|
|
|
|
{
|
|
|
|
help_string.Printf (" %s", value.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
help_string.Printf ("'");
|
|
|
|
}
|
|
|
|
|
2010-08-27 06:05:43 +08:00
|
|
|
size_t
|
2010-06-09 00:52:24 +08:00
|
|
|
CommandInterpreter::FindLongestCommandWord (CommandObject::CommandMap &dict)
|
|
|
|
{
|
|
|
|
CommandObject::CommandMap::const_iterator pos;
|
2010-08-27 06:05:43 +08:00
|
|
|
CommandObject::CommandMap::const_iterator end = dict.end();
|
|
|
|
size_t max_len = 0;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2010-08-27 06:05:43 +08:00
|
|
|
for (pos = dict.begin(); pos != end; ++pos)
|
|
|
|
{
|
|
|
|
size_t len = pos->first.size();
|
|
|
|
if (max_len < len)
|
|
|
|
max_len = len;
|
|
|
|
}
|
|
|
|
return max_len;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-08-17 07:24:13 +08:00
|
|
|
CommandInterpreter::GetHelp (CommandReturnObject &result,
|
2011-09-10 01:49:36 +08:00
|
|
|
uint32_t cmd_types)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
CommandObject::CommandMap::const_iterator pos;
|
2010-08-27 06:05:43 +08:00
|
|
|
uint32_t max_len = FindLongestCommandWord (m_command_dict);
|
2011-08-17 07:24:13 +08:00
|
|
|
|
|
|
|
if ( (cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin )
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-08-17 07:24:13 +08:00
|
|
|
|
|
|
|
result.AppendMessage("The following is a list of built-in, permanent debugger commands:");
|
|
|
|
result.AppendMessage("");
|
|
|
|
|
|
|
|
for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos)
|
|
|
|
{
|
|
|
|
OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", pos->second->GetHelp(),
|
|
|
|
max_len);
|
|
|
|
}
|
|
|
|
result.AppendMessage("");
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2011-08-17 07:24:13 +08:00
|
|
|
if (m_alias_dict.size() > 0 && ( (cmd_types & eCommandTypesAliases) == eCommandTypesAliases ))
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-10-23 02:47:16 +08:00
|
|
|
result.AppendMessage("The following is a list of your current command abbreviations "
|
2011-04-21 08:39:18 +08:00
|
|
|
"(see 'help command alias' for more info):");
|
2010-06-09 00:52:24 +08:00
|
|
|
result.AppendMessage("");
|
2010-08-27 06:05:43 +08:00
|
|
|
max_len = FindLongestCommandWord (m_alias_dict);
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
for (pos = m_alias_dict.begin(); pos != m_alias_dict.end(); ++pos)
|
|
|
|
{
|
|
|
|
StreamString sstr;
|
|
|
|
StreamString translation_and_help;
|
|
|
|
std::string entry_name = pos->first;
|
|
|
|
std::string second_entry = pos->second.get()->GetCommandName();
|
|
|
|
GetAliasHelp (pos->first.c_str(), pos->second->GetCommandName(), sstr);
|
|
|
|
|
|
|
|
translation_and_help.Printf ("(%s) %s", sstr.GetData(), pos->second->GetHelp());
|
|
|
|
OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--",
|
|
|
|
translation_and_help.GetData(), max_len);
|
|
|
|
}
|
|
|
|
result.AppendMessage("");
|
|
|
|
}
|
|
|
|
|
2011-08-17 07:24:13 +08:00
|
|
|
if (m_user_dict.size() > 0 && ( (cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef ))
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
result.AppendMessage ("The following is a list of your current user-defined commands:");
|
|
|
|
result.AppendMessage("");
|
2011-08-17 07:24:13 +08:00
|
|
|
max_len = FindLongestCommandWord (m_user_dict);
|
2010-06-09 00:52:24 +08:00
|
|
|
for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos)
|
|
|
|
{
|
2011-08-17 07:24:13 +08:00
|
|
|
OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", pos->second->GetHelp(),
|
|
|
|
max_len);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
result.AppendMessage("");
|
|
|
|
}
|
|
|
|
|
|
|
|
result.AppendMessage("For more information on any particular command, try 'help <command-name>'.");
|
|
|
|
}
|
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
CommandObject *
|
|
|
|
CommandInterpreter::GetCommandObjectForCommand (std::string &command_string)
|
|
|
|
{
|
|
|
|
// This function finds the final, lowest-level, alias-resolved command object whose 'Execute' function will
|
|
|
|
// eventually be invoked by the given command line.
|
|
|
|
|
|
|
|
CommandObject *cmd_obj = NULL;
|
|
|
|
std::string white_space (" \t\v");
|
|
|
|
size_t start = command_string.find_first_not_of (white_space);
|
|
|
|
size_t end = 0;
|
|
|
|
bool done = false;
|
|
|
|
while (!done)
|
|
|
|
{
|
|
|
|
if (start != std::string::npos)
|
|
|
|
{
|
|
|
|
// Get the next word from command_string.
|
|
|
|
end = command_string.find_first_of (white_space, start);
|
|
|
|
if (end == std::string::npos)
|
|
|
|
end = command_string.size();
|
|
|
|
std::string cmd_word = command_string.substr (start, end - start);
|
|
|
|
|
|
|
|
if (cmd_obj == NULL)
|
|
|
|
// Since cmd_obj is NULL we are on our first time through this loop. Check to see if cmd_word is a valid
|
|
|
|
// command or alias.
|
|
|
|
cmd_obj = GetCommandObject (cmd_word.c_str());
|
|
|
|
else if (cmd_obj->IsMultiwordObject ())
|
|
|
|
{
|
|
|
|
// Our current object is a multi-word object; see if the cmd_word is a valid sub-command for our object.
|
|
|
|
CommandObject *sub_cmd_obj =
|
|
|
|
((CommandObjectMultiword *) cmd_obj)->GetSubcommandObject (cmd_word.c_str());
|
|
|
|
if (sub_cmd_obj)
|
|
|
|
cmd_obj = sub_cmd_obj;
|
|
|
|
else // cmd_word was not a valid sub-command word, so we are donee
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// We have a cmd_obj and it is not a multi-word object, so we are done.
|
|
|
|
done = true;
|
|
|
|
|
|
|
|
// If we didn't find a valid command object, or our command object is not a multi-word object, or
|
|
|
|
// we are at the end of the command_string, then we are done. Otherwise, find the start of the
|
|
|
|
// next word.
|
|
|
|
|
|
|
|
if (!cmd_obj || !cmd_obj->IsMultiwordObject() || end >= command_string.size())
|
|
|
|
done = true;
|
|
|
|
else
|
|
|
|
start = command_string.find_first_not_of (white_space, end);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// Unable to find any more words.
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end == command_string.size())
|
|
|
|
command_string.clear();
|
|
|
|
else
|
|
|
|
command_string = command_string.substr(end);
|
|
|
|
|
|
|
|
return cmd_obj;
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
bool
|
2011-05-12 00:07:06 +08:00
|
|
|
CommandInterpreter::StripFirstWord (std::string &command_string, std::string &word, bool &was_quoted, char "e_char)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-12-10 06:52:49 +08:00
|
|
|
std::string white_space (" \t\v");
|
|
|
|
size_t start;
|
|
|
|
size_t end;
|
|
|
|
|
|
|
|
start = command_string.find_first_not_of (white_space);
|
|
|
|
if (start != std::string::npos)
|
|
|
|
{
|
2011-05-12 00:07:06 +08:00
|
|
|
size_t len = command_string.size() - start;
|
|
|
|
if (len >= 2
|
|
|
|
&& ((command_string[start] == '\'') || (command_string[start] == '"')))
|
2010-12-10 06:52:49 +08:00
|
|
|
{
|
2011-05-12 00:07:06 +08:00
|
|
|
was_quoted = true;
|
|
|
|
quote_char = command_string[start];
|
|
|
|
std::string quote_string = command_string.substr (start, 1);
|
|
|
|
start = start + 1;
|
|
|
|
end = command_string.find (quote_string, start);
|
|
|
|
if (end != std::string::npos)
|
|
|
|
{
|
|
|
|
word = command_string.substr (start, end - start);
|
|
|
|
if (end + 1 < len)
|
|
|
|
command_string = command_string.substr (end+1);
|
|
|
|
else
|
|
|
|
command_string.erase ();
|
|
|
|
size_t pos = command_string.find_first_not_of (white_space);
|
|
|
|
if ((pos != 0) && (pos != std::string::npos))
|
|
|
|
command_string = command_string.substr (pos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
word = command_string.substr (start - 1);
|
|
|
|
command_string.erase ();
|
|
|
|
}
|
2010-12-10 06:52:49 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-05-12 00:07:06 +08:00
|
|
|
end = command_string.find_first_of (white_space, start);
|
|
|
|
if (end != std::string::npos)
|
|
|
|
{
|
|
|
|
word = command_string.substr (start, end - start);
|
|
|
|
command_string = command_string.substr (end);
|
|
|
|
size_t pos = command_string.find_first_not_of (white_space);
|
|
|
|
if ((pos != 0) && (pos != std::string::npos))
|
|
|
|
command_string = command_string.substr (pos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
word = command_string.substr (start);
|
|
|
|
command_string.erase();
|
|
|
|
}
|
2010-12-10 06:52:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::BuildAliasResult (const char *alias_name, std::string &raw_input_string, std::string &alias_result,
|
|
|
|
CommandObject *&alias_cmd_obj, CommandReturnObject &result)
|
|
|
|
{
|
|
|
|
Args cmd_args (raw_input_string.c_str());
|
|
|
|
alias_cmd_obj = GetCommandObject (alias_name);
|
|
|
|
StreamString result_str;
|
|
|
|
|
|
|
|
if (alias_cmd_obj)
|
|
|
|
{
|
|
|
|
std::string alias_name_str = alias_name;
|
|
|
|
if ((cmd_args.GetArgumentCount() == 0)
|
|
|
|
|| (alias_name_str.compare (cmd_args.GetArgumentAtIndex(0)) != 0))
|
|
|
|
cmd_args.Unshift (alias_name);
|
|
|
|
|
|
|
|
result_str.Printf ("%s", alias_cmd_obj->GetCommandName ());
|
|
|
|
OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
if (option_arg_vector_sp.get())
|
|
|
|
{
|
|
|
|
OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
|
|
|
|
|
|
|
|
for (int i = 0; i < option_arg_vector->size(); ++i)
|
|
|
|
{
|
|
|
|
OptionArgPair option_pair = (*option_arg_vector)[i];
|
|
|
|
OptionArgValue value_pair = option_pair.second;
|
|
|
|
int value_type = value_pair.first;
|
|
|
|
std::string option = option_pair.first;
|
|
|
|
std::string value = value_pair.second;
|
|
|
|
if (option.compare ("<argument>") == 0)
|
|
|
|
result_str.Printf (" %s", value.c_str());
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result_str.Printf (" %s", option.c_str());
|
|
|
|
if (value_type != optional_argument)
|
|
|
|
result_str.Printf (" ");
|
|
|
|
if (value.compare ("<no_argument>") != 0)
|
|
|
|
{
|
|
|
|
int index = GetOptionArgumentPosition (value.c_str());
|
|
|
|
if (index == 0)
|
|
|
|
result_str.Printf ("%s", value.c_str());
|
|
|
|
else if (index >= cmd_args.GetArgumentCount())
|
|
|
|
{
|
|
|
|
|
|
|
|
result.AppendErrorWithFormat
|
|
|
|
("Not enough arguments provided; you need at least %d arguments to use this alias.\n",
|
|
|
|
index);
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
size_t strpos = raw_input_string.find (cmd_args.GetArgumentAtIndex (index));
|
|
|
|
if (strpos != std::string::npos)
|
|
|
|
raw_input_string = raw_input_string.erase (strpos,
|
|
|
|
strlen (cmd_args.GetArgumentAtIndex (index)));
|
|
|
|
result_str.Printf ("%s", cmd_args.GetArgumentAtIndex (index));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
alias_result = result_str.GetData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-14 15:41:33 +08:00
|
|
|
Error
|
|
|
|
CommandInterpreter::PreprocessCommand (std::string &command)
|
|
|
|
{
|
|
|
|
// The command preprocessor needs to do things to the command
|
|
|
|
// line before any parsing of arguments or anything else is done.
|
|
|
|
// The only current stuff that gets proprocessed is anyting enclosed
|
|
|
|
// in backtick ('`') characters is evaluated as an expression and
|
|
|
|
// the result of the expression must be a scalar that can be substituted
|
|
|
|
// into the command. An example would be:
|
|
|
|
// (lldb) memory read `$rsp + 20`
|
|
|
|
Error error; // Error for any expressions that might not evaluate
|
|
|
|
size_t start_backtick;
|
|
|
|
size_t pos = 0;
|
|
|
|
while ((start_backtick = command.find ('`', pos)) != std::string::npos)
|
|
|
|
{
|
|
|
|
if (start_backtick > 0 && command[start_backtick-1] == '\\')
|
|
|
|
{
|
|
|
|
// The backtick was preceeded by a '\' character, remove the slash
|
|
|
|
// and don't treat the backtick as the start of an expression
|
|
|
|
command.erase(start_backtick-1, 1);
|
|
|
|
// No need to add one to start_backtick since we just deleted a char
|
|
|
|
pos = start_backtick;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const size_t expr_content_start = start_backtick + 1;
|
|
|
|
const size_t end_backtick = command.find ('`', expr_content_start);
|
|
|
|
if (end_backtick == std::string::npos)
|
|
|
|
return error;
|
|
|
|
else if (end_backtick == expr_content_start)
|
|
|
|
{
|
|
|
|
// Empty expression (two backticks in a row)
|
|
|
|
command.erase (start_backtick, 2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string expr_str (command, expr_content_start, end_backtick - expr_content_start);
|
|
|
|
|
|
|
|
Target *target = m_exe_ctx.GetTargetPtr();
|
|
|
|
if (target)
|
|
|
|
{
|
|
|
|
const bool unwind_on_error = true;
|
|
|
|
const bool keep_in_memory = false;
|
|
|
|
ValueObjectSP expr_result_valobj_sp;
|
|
|
|
ExecutionResults expr_result = target->EvaluateExpression (expr_str.c_str(),
|
|
|
|
m_exe_ctx.GetFramePtr(),
|
|
|
|
eExecutionPolicyOnlyWhenNeeded,
|
|
|
|
unwind_on_error, keep_in_memory,
|
|
|
|
eNoDynamicValues,
|
|
|
|
expr_result_valobj_sp);
|
|
|
|
if (expr_result == eExecutionCompleted)
|
|
|
|
{
|
|
|
|
Scalar scalar;
|
|
|
|
if (expr_result_valobj_sp->ResolveValue (scalar))
|
|
|
|
{
|
|
|
|
command.erase (start_backtick, end_backtick - start_backtick + 1);
|
|
|
|
StreamString value_strm;
|
|
|
|
const bool show_type = false;
|
|
|
|
scalar.GetValue (&value_strm, show_type);
|
|
|
|
size_t value_string_size = value_strm.GetSize();
|
|
|
|
if (value_string_size)
|
|
|
|
{
|
|
|
|
command.insert (start_backtick, value_strm.GetData(), value_string_size);
|
|
|
|
pos = start_backtick + value_string_size;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error.SetErrorStringWithFormat("expression value didn't result in a scalar value for the expression '%s'", expr_str.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error.SetErrorStringWithFormat("expression value didn't result in a scalar value for the expression '%s'", expr_str.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (expr_result_valobj_sp)
|
|
|
|
error = expr_result_valobj_sp->GetError();
|
|
|
|
if (error.Success())
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (expr_result)
|
|
|
|
{
|
|
|
|
case eExecutionSetupError:
|
|
|
|
error.SetErrorStringWithFormat("expression setup error for the expression '%s'", expr_str.c_str());
|
|
|
|
break;
|
|
|
|
case eExecutionCompleted:
|
|
|
|
break;
|
|
|
|
case eExecutionDiscarded:
|
|
|
|
error.SetErrorStringWithFormat("expression discarded for the expression '%s'", expr_str.c_str());
|
|
|
|
break;
|
|
|
|
case eExecutionInterrupted:
|
|
|
|
error.SetErrorStringWithFormat("expression interrupted for the expression '%s'", expr_str.c_str());
|
|
|
|
break;
|
|
|
|
case eExecutionTimedOut:
|
|
|
|
error.SetErrorStringWithFormat("expression timed out for the expression '%s'", expr_str.c_str());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (error.Fail())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
bool
|
|
|
|
CommandInterpreter::HandleCommand (const char *command_line,
|
|
|
|
bool add_to_history,
|
|
|
|
CommandReturnObject &result,
|
2011-02-18 08:54:25 +08:00
|
|
|
ExecutionContext *override_context,
|
2011-10-05 08:42:59 +08:00
|
|
|
bool repeat_on_empty_command,
|
|
|
|
bool no_context_switching)
|
2011-02-18 08:54:25 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
{
|
2011-02-18 08:54:25 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
bool done = false;
|
|
|
|
CommandObject *cmd_obj = NULL;
|
|
|
|
std::string next_word;
|
|
|
|
bool wants_raw_input = false;
|
|
|
|
std::string command_string (command_line);
|
2011-07-12 11:12:18 +08:00
|
|
|
std::string original_command_string (command_line);
|
2010-12-10 06:52:49 +08:00
|
|
|
|
|
|
|
LogSP log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMANDS));
|
2010-11-10 12:57:04 +08:00
|
|
|
Host::SetCrashDescriptionWithFormat ("HandleCommand(command = \"%s\")", command_line);
|
|
|
|
|
|
|
|
// Make a scoped cleanup object that will clear the crash description string
|
|
|
|
// on exit of this function.
|
2011-07-12 08:18:11 +08:00
|
|
|
lldb_utility::CleanUp <const char *> crash_description_cleanup(NULL, Host::SetCrashDescription);
|
2010-11-10 12:57:04 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf ("Processing command: %s", command_line);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2010-11-05 07:08:45 +08:00
|
|
|
Timer scoped_timer (__PRETTY_FUNCTION__, "Handling command: %s.", command_line);
|
|
|
|
|
2011-10-05 08:42:59 +08:00
|
|
|
if (!no_context_switching)
|
|
|
|
UpdateExecutionContext (override_context);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2011-02-18 08:54:25 +08:00
|
|
|
bool empty_command = false;
|
|
|
|
bool comment_command = false;
|
|
|
|
if (command_string.empty())
|
|
|
|
empty_command = true;
|
|
|
|
else
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-02-18 08:54:25 +08:00
|
|
|
const char *k_space_characters = "\t\n\v\f\r ";
|
|
|
|
|
|
|
|
size_t non_space = command_string.find_first_not_of (k_space_characters);
|
|
|
|
// Check for empty line or comment line (lines whose first
|
|
|
|
// non-space character is the comment character for this interpreter)
|
|
|
|
if (non_space == std::string::npos)
|
|
|
|
empty_command = true;
|
|
|
|
else if (command_string[non_space] == m_comment_char)
|
|
|
|
comment_command = true;
|
2011-07-12 11:12:18 +08:00
|
|
|
else if (command_string[non_space] == m_repeat_char)
|
|
|
|
{
|
|
|
|
const char *history_string = FindHistoryString (command_string.c_str() + non_space);
|
|
|
|
if (history_string == NULL)
|
|
|
|
{
|
|
|
|
result.AppendErrorWithFormat ("Could not find entry: %s in history", command_string.c_str());
|
|
|
|
result.SetStatus(eReturnStatusFailed);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
add_to_history = false;
|
|
|
|
command_string = history_string;
|
|
|
|
original_command_string = history_string;
|
|
|
|
}
|
2011-02-18 08:54:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (empty_command)
|
|
|
|
{
|
|
|
|
if (repeat_on_empty_command)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-02-18 08:54:25 +08:00
|
|
|
if (m_command_history.empty())
|
2010-07-07 07:48:33 +08:00
|
|
|
{
|
2011-02-18 08:54:25 +08:00
|
|
|
result.AppendError ("empty command");
|
|
|
|
result.SetStatus(eReturnStatusFailed);
|
2010-07-07 07:48:33 +08:00
|
|
|
return false;
|
|
|
|
}
|
2011-02-18 08:54:25 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
command_line = m_repeat_command.c_str();
|
|
|
|
command_string = command_line;
|
2011-07-12 11:12:18 +08:00
|
|
|
original_command_string = command_line;
|
2011-02-18 08:54:25 +08:00
|
|
|
if (m_repeat_command.empty())
|
|
|
|
{
|
|
|
|
result.AppendErrorWithFormat("No auto repeat.\n");
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
add_to_history = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
|
|
return true;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2011-02-18 08:54:25 +08:00
|
|
|
}
|
|
|
|
else if (comment_command)
|
|
|
|
{
|
|
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
|
|
return true;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2011-05-12 00:07:06 +08:00
|
|
|
|
2011-10-14 15:41:33 +08:00
|
|
|
|
|
|
|
Error error (PreprocessCommand (command_string));
|
|
|
|
|
|
|
|
if (error.Fail())
|
|
|
|
{
|
|
|
|
result.AppendError (error.AsCString());
|
|
|
|
result.SetStatus(eReturnStatusFailed);
|
|
|
|
return false;
|
|
|
|
}
|
2010-12-10 06:52:49 +08:00
|
|
|
// Phase 1.
|
|
|
|
|
|
|
|
// Before we do ANY kind of argument processing, etc. we need to figure out what the real/final command object
|
|
|
|
// is for the specified command, and whether or not it wants raw input. This gets complicated by the fact that
|
|
|
|
// the user could have specified an alias, and in translating the alias there may also be command options and/or
|
|
|
|
// even data (including raw text strings) that need to be found and inserted into the command line as part of
|
|
|
|
// the translation. So this first step is plain look-up & replacement, resulting in three things: 1). the command
|
2011-01-09 04:28:42 +08:00
|
|
|
// object whose Execute method will actually be called; 2). a revised command string, with all substitutions &
|
2010-12-10 06:52:49 +08:00
|
|
|
// replacements taken care of; 3). whether or not the Execute function wants raw input or not.
|
|
|
|
|
|
|
|
StreamString revised_command_line;
|
2010-12-11 16:16:56 +08:00
|
|
|
size_t actual_cmd_name_len = 0;
|
2010-12-10 06:52:49 +08:00
|
|
|
while (!done)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-05-12 00:07:06 +08:00
|
|
|
bool was_quoted = false;
|
|
|
|
char quote_char = '\0';
|
|
|
|
StripFirstWord (command_string, next_word, was_quoted, quote_char);
|
2010-12-10 06:52:49 +08:00
|
|
|
if (!cmd_obj && AliasExists (next_word.c_str()))
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-12-10 06:52:49 +08:00
|
|
|
std::string alias_result;
|
|
|
|
BuildAliasResult (next_word.c_str(), command_string, alias_result, cmd_obj, result);
|
|
|
|
revised_command_line.Printf ("%s", alias_result.c_str());
|
|
|
|
if (cmd_obj)
|
2010-12-15 02:51:39 +08:00
|
|
|
{
|
2010-12-10 06:52:49 +08:00
|
|
|
wants_raw_input = cmd_obj->WantsRawCommandString ();
|
2010-12-15 02:51:39 +08:00
|
|
|
actual_cmd_name_len = strlen (cmd_obj->GetCommandName());
|
|
|
|
}
|
2010-12-10 06:52:49 +08:00
|
|
|
}
|
|
|
|
else if (!cmd_obj)
|
|
|
|
{
|
|
|
|
cmd_obj = GetCommandObject (next_word.c_str());
|
|
|
|
if (cmd_obj)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-12-11 16:16:56 +08:00
|
|
|
actual_cmd_name_len += next_word.length();
|
2010-12-10 06:52:49 +08:00
|
|
|
revised_command_line.Printf ("%s", next_word.c_str());
|
|
|
|
wants_raw_input = cmd_obj->WantsRawCommandString ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
revised_command_line.Printf ("%s", next_word.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (cmd_obj->IsMultiwordObject ())
|
|
|
|
{
|
|
|
|
CommandObject *sub_cmd_obj = ((CommandObjectMultiword *) cmd_obj)->GetSubcommandObject (next_word.c_str());
|
|
|
|
if (sub_cmd_obj)
|
|
|
|
{
|
2010-12-11 16:16:56 +08:00
|
|
|
actual_cmd_name_len += next_word.length() + 1;
|
2010-12-10 06:52:49 +08:00
|
|
|
revised_command_line.Printf (" %s", next_word.c_str());
|
|
|
|
cmd_obj = sub_cmd_obj;
|
|
|
|
wants_raw_input = cmd_obj->WantsRawCommandString ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-05-12 00:07:06 +08:00
|
|
|
if (was_quoted)
|
|
|
|
{
|
|
|
|
if (quote_char == '"')
|
|
|
|
revised_command_line.Printf (" \"%s\"", next_word.c_str());
|
|
|
|
else
|
|
|
|
revised_command_line.Printf (" '%s'", next_word.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
revised_command_line.Printf (" %s", next_word.c_str());
|
2010-12-10 06:52:49 +08:00
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-05-12 00:07:06 +08:00
|
|
|
if (was_quoted)
|
|
|
|
{
|
|
|
|
if (quote_char == '"')
|
|
|
|
revised_command_line.Printf (" \"%s\"", next_word.c_str());
|
|
|
|
else
|
|
|
|
revised_command_line.Printf (" '%s'", next_word.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
revised_command_line.Printf (" %s", next_word.c_str());
|
2010-12-10 06:52:49 +08:00
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd_obj == NULL)
|
|
|
|
{
|
|
|
|
result.AppendErrorWithFormat ("'%s' is not a valid command.\n", next_word.c_str());
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
next_word.erase ();
|
|
|
|
if (command_string.length() == 0)
|
|
|
|
done = true;
|
2010-12-08 03:58:26 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
if (command_string.size() > 0)
|
|
|
|
revised_command_line.Printf (" %s", command_string.c_str());
|
2010-07-07 07:48:33 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
// End of Phase 1.
|
|
|
|
// At this point cmd_obj should contain the CommandObject whose Execute method will be called, if the command
|
|
|
|
// specified was valid; revised_command_line contains the complete command line (including command name(s)),
|
|
|
|
// fully translated with all substitutions & translations taken care of (still in raw text format); and
|
|
|
|
// wants_raw_input specifies whether the Execute method expects raw input or not.
|
2010-07-07 07:48:33 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
|
|
|
|
if (log)
|
|
|
|
{
|
|
|
|
log->Printf ("HandleCommand, cmd_obj : '%s'", cmd_obj ? cmd_obj->GetCommandName() : "<not found>");
|
|
|
|
log->Printf ("HandleCommand, revised_command_line: '%s'", revised_command_line.GetData());
|
|
|
|
log->Printf ("HandleCommand, wants_raw_input:'%s'", wants_raw_input ? "True" : "False");
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
// Phase 2.
|
|
|
|
// Take care of things like setting up the history command & calling the appropriate Execute method on the
|
|
|
|
// CommandObject, with the appropriate arguments.
|
|
|
|
|
|
|
|
if (cmd_obj != NULL)
|
|
|
|
{
|
|
|
|
if (add_to_history)
|
|
|
|
{
|
|
|
|
Args command_args (revised_command_line.GetData());
|
|
|
|
const char *repeat_command = cmd_obj->GetRepeatCommand(command_args, 0);
|
|
|
|
if (repeat_command != NULL)
|
|
|
|
m_repeat_command.assign(repeat_command);
|
|
|
|
else
|
2011-07-12 11:12:18 +08:00
|
|
|
m_repeat_command.assign(original_command_string.c_str());
|
2010-12-10 06:52:49 +08:00
|
|
|
|
2011-07-12 11:12:18 +08:00
|
|
|
// Don't keep pushing the same command onto the history...
|
|
|
|
if (m_command_history.size() == 0 || m_command_history.back() != original_command_string)
|
|
|
|
m_command_history.push_back (original_command_string);
|
2010-12-10 06:52:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
command_string = revised_command_line.GetData();
|
|
|
|
std::string command_name (cmd_obj->GetCommandName());
|
2010-12-11 16:16:56 +08:00
|
|
|
std::string remainder;
|
|
|
|
if (actual_cmd_name_len < command_string.length())
|
|
|
|
remainder = command_string.substr (actual_cmd_name_len); // Note: 'actual_cmd_name_len' may be considerably shorter
|
|
|
|
// than cmd_obj->GetCommandName(), because name completion
|
|
|
|
// allows users to enter short versions of the names,
|
|
|
|
// e.g. 'br s' for 'breakpoint set'.
|
2010-12-10 06:52:49 +08:00
|
|
|
|
|
|
|
// Remove any initial spaces
|
|
|
|
std::string white_space (" \t\v");
|
|
|
|
size_t pos = remainder.find_first_not_of (white_space);
|
|
|
|
if (pos != 0 && pos != std::string::npos)
|
2011-04-23 04:58:45 +08:00
|
|
|
remainder.erase(0, pos);
|
2010-12-10 06:52:49 +08:00
|
|
|
|
|
|
|
if (log)
|
2011-08-25 08:20:04 +08:00
|
|
|
log->Printf ("HandleCommand, command line after removing command name(s): '%s'", remainder.c_str());
|
2010-12-10 06:52:49 +08:00
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2010-12-10 06:52:49 +08:00
|
|
|
if (wants_raw_input)
|
|
|
|
cmd_obj->ExecuteRawCommandString (remainder.c_str(), result);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Args cmd_args (remainder.c_str());
|
|
|
|
cmd_obj->ExecuteWithOptions (cmd_args, result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We didn't find the first command object, so complete the first argument.
|
|
|
|
Args command_args (revised_command_line.GetData());
|
|
|
|
StringList matches;
|
|
|
|
int num_matches;
|
|
|
|
int cursor_index = 0;
|
|
|
|
int cursor_char_position = strlen (command_args.GetArgumentAtIndex(0));
|
|
|
|
bool word_complete;
|
|
|
|
num_matches = HandleCompletionMatches (command_args,
|
|
|
|
cursor_index,
|
|
|
|
cursor_char_position,
|
|
|
|
0,
|
|
|
|
-1,
|
|
|
|
word_complete,
|
|
|
|
matches);
|
|
|
|
|
|
|
|
if (num_matches > 0)
|
|
|
|
{
|
|
|
|
std::string error_msg;
|
|
|
|
error_msg.assign ("ambiguous command '");
|
|
|
|
error_msg.append(command_args.GetArgumentAtIndex(0));
|
|
|
|
error_msg.append ("'.");
|
|
|
|
|
|
|
|
error_msg.append (" Possible completions:");
|
|
|
|
for (int i = 0; i < num_matches; i++)
|
|
|
|
{
|
|
|
|
error_msg.append ("\n\t");
|
|
|
|
error_msg.append (matches.GetStringAtIndex (i));
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2010-12-10 06:52:49 +08:00
|
|
|
error_msg.append ("\n");
|
|
|
|
result.AppendRawError (error_msg.c_str(), error_msg.size());
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2010-12-10 06:52:49 +08:00
|
|
|
else
|
|
|
|
result.AppendErrorWithFormat ("Unrecognized command '%s'.\n", command_args.GetArgumentAtIndex (0));
|
|
|
|
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2010-12-10 06:52:49 +08:00
|
|
|
|
2011-08-25 08:20:04 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf ("HandleCommand, command %s", (result.Succeeded() ? "succeeded" : "did not succeed"));
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
return result.Succeeded();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CommandInterpreter::HandleCompletionMatches (Args &parsed_line,
|
|
|
|
int &cursor_index,
|
|
|
|
int &cursor_char_position,
|
|
|
|
int match_start_point,
|
|
|
|
int max_return_elements,
|
2010-06-30 13:02:46 +08:00
|
|
|
bool &word_complete,
|
2010-06-09 00:52:24 +08:00
|
|
|
StringList &matches)
|
|
|
|
{
|
|
|
|
int num_command_matches = 0;
|
|
|
|
bool look_for_subcommand = false;
|
2010-06-30 13:02:46 +08:00
|
|
|
|
|
|
|
// For any of the command completions a unique match will be a complete word.
|
|
|
|
word_complete = true;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
if (cursor_index == -1)
|
|
|
|
{
|
|
|
|
// We got nothing on the command line, so return the list of commands
|
2010-07-07 06:46:59 +08:00
|
|
|
bool include_aliases = true;
|
2010-06-09 00:52:24 +08:00
|
|
|
num_command_matches = GetCommandNamesMatchingPartialString ("", include_aliases, matches);
|
|
|
|
}
|
|
|
|
else if (cursor_index == 0)
|
|
|
|
{
|
|
|
|
// The cursor is in the first argument, so just do a lookup in the dictionary.
|
2010-07-07 06:46:59 +08:00
|
|
|
CommandObject *cmd_obj = GetCommandObject (parsed_line.GetArgumentAtIndex(0), &matches);
|
2010-06-09 00:52:24 +08:00
|
|
|
num_command_matches = matches.GetSize();
|
|
|
|
|
|
|
|
if (num_command_matches == 1
|
|
|
|
&& cmd_obj && cmd_obj->IsMultiwordObject()
|
|
|
|
&& matches.GetStringAtIndex(0) != NULL
|
|
|
|
&& strcmp (parsed_line.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0)
|
|
|
|
{
|
|
|
|
look_for_subcommand = true;
|
|
|
|
num_command_matches = 0;
|
|
|
|
matches.DeleteStringAtIndex(0);
|
|
|
|
parsed_line.AppendArgument ("");
|
|
|
|
cursor_index++;
|
|
|
|
cursor_char_position = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cursor_index > 0 || look_for_subcommand)
|
|
|
|
{
|
|
|
|
// We are completing further on into a commands arguments, so find the command and tell it
|
|
|
|
// to complete the command.
|
|
|
|
// First see if there is a matching initial command:
|
2010-07-07 06:46:59 +08:00
|
|
|
CommandObject *command_object = GetCommandObject (parsed_line.GetArgumentAtIndex(0));
|
2010-06-09 00:52:24 +08:00
|
|
|
if (command_object == NULL)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parsed_line.Shift();
|
|
|
|
cursor_index--;
|
2010-09-18 09:14:36 +08:00
|
|
|
num_command_matches = command_object->HandleCompletion (parsed_line,
|
2010-06-23 09:19:29 +08:00
|
|
|
cursor_index,
|
|
|
|
cursor_char_position,
|
|
|
|
match_start_point,
|
2010-06-30 13:02:46 +08:00
|
|
|
max_return_elements,
|
|
|
|
word_complete,
|
2010-06-09 00:52:24 +08:00
|
|
|
matches);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return num_command_matches;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
CommandInterpreter::HandleCompletion (const char *current_line,
|
|
|
|
const char *cursor,
|
|
|
|
const char *last_char,
|
|
|
|
int match_start_point,
|
|
|
|
int max_return_elements,
|
|
|
|
StringList &matches)
|
|
|
|
{
|
|
|
|
// We parse the argument up to the cursor, so the last argument in parsed_line is
|
|
|
|
// the one containing the cursor, and the cursor is after the last character.
|
|
|
|
|
|
|
|
Args parsed_line(current_line, last_char - current_line);
|
|
|
|
Args partial_parsed_line(current_line, cursor - current_line);
|
|
|
|
|
2011-07-12 11:12:18 +08:00
|
|
|
// Don't complete comments, and if the line we are completing is just the history repeat character,
|
|
|
|
// substitute the appropriate history line.
|
|
|
|
const char *first_arg = parsed_line.GetArgumentAtIndex(0);
|
|
|
|
if (first_arg)
|
|
|
|
{
|
|
|
|
if (first_arg[0] == m_comment_char)
|
|
|
|
return 0;
|
|
|
|
else if (first_arg[0] == m_repeat_char)
|
|
|
|
{
|
|
|
|
const char *history_string = FindHistoryString (first_arg);
|
|
|
|
if (history_string != NULL)
|
|
|
|
{
|
|
|
|
matches.Clear();
|
|
|
|
matches.InsertStringAtIndex(0, history_string);
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
int num_args = partial_parsed_line.GetArgumentCount();
|
|
|
|
int cursor_index = partial_parsed_line.GetArgumentCount() - 1;
|
|
|
|
int cursor_char_position;
|
|
|
|
|
|
|
|
if (cursor_index == -1)
|
|
|
|
cursor_char_position = 0;
|
|
|
|
else
|
|
|
|
cursor_char_position = strlen (partial_parsed_line.GetArgumentAtIndex(cursor_index));
|
2010-12-15 03:56:01 +08:00
|
|
|
|
|
|
|
if (cursor > current_line && cursor[-1] == ' ')
|
|
|
|
{
|
|
|
|
// We are just after a space. If we are in an argument, then we will continue
|
|
|
|
// parsing, but if we are between arguments, then we have to complete whatever the next
|
|
|
|
// element would be.
|
|
|
|
// We can distinguish the two cases because if we are in an argument (e.g. because the space is
|
|
|
|
// protected by a quote) then the space will also be in the parsed argument...
|
|
|
|
|
|
|
|
const char *current_elem = partial_parsed_line.GetArgumentAtIndex(cursor_index);
|
|
|
|
if (cursor_char_position == 0 || current_elem[cursor_char_position - 1] != ' ')
|
|
|
|
{
|
|
|
|
parsed_line.InsertArgumentAtIndex(cursor_index + 1, "", '"');
|
|
|
|
cursor_index++;
|
|
|
|
cursor_char_position = 0;
|
|
|
|
}
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
int num_command_matches;
|
|
|
|
|
|
|
|
matches.Clear();
|
|
|
|
|
|
|
|
// Only max_return_elements == -1 is supported at present:
|
|
|
|
assert (max_return_elements == -1);
|
2010-06-30 13:02:46 +08:00
|
|
|
bool word_complete;
|
2010-06-23 09:19:29 +08:00
|
|
|
num_command_matches = HandleCompletionMatches (parsed_line,
|
|
|
|
cursor_index,
|
|
|
|
cursor_char_position,
|
|
|
|
match_start_point,
|
2010-06-30 13:02:46 +08:00
|
|
|
max_return_elements,
|
|
|
|
word_complete,
|
2010-06-23 09:19:29 +08:00
|
|
|
matches);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
if (num_command_matches <= 0)
|
|
|
|
return num_command_matches;
|
|
|
|
|
|
|
|
if (num_args == 0)
|
|
|
|
{
|
|
|
|
// If we got an empty string, insert nothing.
|
|
|
|
matches.InsertStringAtIndex(0, "");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Now figure out if there is a common substring, and if so put that in element 0, otherwise
|
|
|
|
// put an empty string in element 0.
|
|
|
|
std::string command_partial_str;
|
|
|
|
if (cursor_index >= 0)
|
2010-10-23 02:47:16 +08:00
|
|
|
command_partial_str.assign(parsed_line.GetArgumentAtIndex(cursor_index),
|
|
|
|
parsed_line.GetArgumentAtIndex(cursor_index) + cursor_char_position);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
std::string common_prefix;
|
|
|
|
matches.LongestCommonPrefix (common_prefix);
|
|
|
|
int partial_name_len = command_partial_str.size();
|
|
|
|
|
|
|
|
// If we matched a unique single command, add a space...
|
2010-06-30 13:02:46 +08:00
|
|
|
// Only do this if the completer told us this was a complete word, however...
|
|
|
|
if (num_command_matches == 1 && word_complete)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
char quote_char = parsed_line.GetArgumentQuoteCharAtIndex(cursor_index);
|
|
|
|
if (quote_char != '\0')
|
|
|
|
common_prefix.push_back(quote_char);
|
|
|
|
|
|
|
|
common_prefix.push_back(' ');
|
|
|
|
}
|
|
|
|
common_prefix.erase (0, partial_name_len);
|
|
|
|
matches.InsertStringAtIndex(0, common_prefix.c_str());
|
|
|
|
}
|
|
|
|
return num_command_matches;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CommandInterpreter::~CommandInterpreter ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
CommandInterpreter::GetPrompt ()
|
|
|
|
{
|
2010-09-21 04:44:43 +08:00
|
|
|
return m_debugger.GetPrompt();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::SetPrompt (const char *new_prompt)
|
|
|
|
{
|
2010-09-21 04:44:43 +08:00
|
|
|
m_debugger.SetPrompt (new_prompt);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2010-10-05 03:49:29 +08:00
|
|
|
size_t
|
2011-02-09 09:08:52 +08:00
|
|
|
CommandInterpreter::GetConfirmationInputReaderCallback
|
|
|
|
(
|
|
|
|
void *baton,
|
|
|
|
InputReader &reader,
|
|
|
|
lldb::InputReaderAction action,
|
|
|
|
const char *bytes,
|
|
|
|
size_t bytes_len
|
|
|
|
)
|
2010-10-05 03:49:29 +08:00
|
|
|
{
|
2011-02-09 09:08:52 +08:00
|
|
|
File &out_file = reader.GetDebugger().GetOutputFile();
|
2010-10-05 03:49:29 +08:00
|
|
|
bool *response_ptr = (bool *) baton;
|
|
|
|
|
|
|
|
switch (action)
|
|
|
|
{
|
|
|
|
case eInputReaderActivate:
|
2011-02-09 09:08:52 +08:00
|
|
|
if (out_file.IsValid())
|
2010-10-05 03:49:29 +08:00
|
|
|
{
|
|
|
|
if (reader.GetPrompt())
|
2011-02-02 09:17:56 +08:00
|
|
|
{
|
2011-02-09 09:08:52 +08:00
|
|
|
out_file.Printf ("%s", reader.GetPrompt());
|
|
|
|
out_file.Flush ();
|
2011-02-02 09:17:56 +08:00
|
|
|
}
|
2010-10-05 03:49:29 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eInputReaderDeactivate:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eInputReaderReactivate:
|
2011-02-09 09:08:52 +08:00
|
|
|
if (out_file.IsValid() && reader.GetPrompt())
|
2011-02-02 09:17:56 +08:00
|
|
|
{
|
2011-02-09 09:08:52 +08:00
|
|
|
out_file.Printf ("%s", reader.GetPrompt());
|
|
|
|
out_file.Flush ();
|
2011-02-02 09:17:56 +08:00
|
|
|
}
|
2010-10-05 03:49:29 +08:00
|
|
|
break;
|
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
|
|
|
|
|
|
|
case eInputReaderAsynchronousOutputWritten:
|
|
|
|
break;
|
|
|
|
|
2010-10-05 03:49:29 +08:00
|
|
|
case eInputReaderGotToken:
|
|
|
|
if (bytes_len == 0)
|
|
|
|
{
|
|
|
|
reader.SetIsDone(true);
|
|
|
|
}
|
|
|
|
else if (bytes[0] == 'y')
|
|
|
|
{
|
|
|
|
*response_ptr = true;
|
|
|
|
reader.SetIsDone(true);
|
|
|
|
}
|
|
|
|
else if (bytes[0] == 'n')
|
|
|
|
{
|
|
|
|
*response_ptr = false;
|
|
|
|
reader.SetIsDone(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-02-09 09:08:52 +08:00
|
|
|
if (out_file.IsValid() && !reader.IsDone() && reader.GetPrompt())
|
2010-10-05 03:49:29 +08:00
|
|
|
{
|
2011-02-09 09:08:52 +08:00
|
|
|
out_file.Printf ("Please answer \"y\" or \"n\"\n%s", reader.GetPrompt());
|
|
|
|
out_file.Flush ();
|
2010-10-05 03:49:29 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-11-20 04:47:54 +08:00
|
|
|
case eInputReaderInterrupt:
|
|
|
|
case eInputReaderEndOfFile:
|
|
|
|
*response_ptr = false; // Assume ^C or ^D means cancel the proposed action
|
|
|
|
reader.SetIsDone (true);
|
|
|
|
break;
|
|
|
|
|
2010-10-05 03:49:29 +08:00
|
|
|
case eInputReaderDone:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bytes_len;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::Confirm (const char *message, bool default_answer)
|
|
|
|
{
|
2010-10-05 06:44:14 +08:00
|
|
|
// Check AutoConfirm first:
|
|
|
|
if (m_debugger.GetAutoConfirm())
|
|
|
|
return default_answer;
|
|
|
|
|
2010-10-05 03:49:29 +08:00
|
|
|
InputReaderSP reader_sp (new InputReader(GetDebugger()));
|
|
|
|
bool response = default_answer;
|
|
|
|
if (reader_sp)
|
|
|
|
{
|
|
|
|
std::string prompt(message);
|
|
|
|
prompt.append(": [");
|
|
|
|
if (default_answer)
|
|
|
|
prompt.append ("Y/n] ");
|
|
|
|
else
|
|
|
|
prompt.append ("y/N] ");
|
|
|
|
|
|
|
|
Error err (reader_sp->Initialize (CommandInterpreter::GetConfirmationInputReaderCallback,
|
|
|
|
&response, // baton
|
|
|
|
eInputReaderGranularityLine, // token size, to pass to callback function
|
|
|
|
NULL, // end token
|
|
|
|
prompt.c_str(), // prompt
|
|
|
|
true)); // echo input
|
|
|
|
if (err.Success())
|
|
|
|
{
|
|
|
|
GetDebugger().PushInputReader (reader_sp);
|
|
|
|
}
|
|
|
|
reader_sp->WaitOnReaderIsDone();
|
|
|
|
}
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
void
|
|
|
|
CommandInterpreter::CrossRegisterCommand (const char * dest_cmd, const char * object_type)
|
|
|
|
{
|
2010-07-07 06:46:59 +08:00
|
|
|
CommandObjectSP cmd_obj_sp = GetCommandSPExact (dest_cmd, true);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
if (cmd_obj_sp != NULL)
|
|
|
|
{
|
|
|
|
CommandObject *cmd_obj = cmd_obj_sp.get();
|
|
|
|
if (cmd_obj->IsCrossRefObject ())
|
|
|
|
cmd_obj->AddObject (object_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OptionArgVectorSP
|
|
|
|
CommandInterpreter::GetAliasOptions (const char *alias_name)
|
|
|
|
{
|
|
|
|
OptionArgMap::iterator pos;
|
|
|
|
OptionArgVectorSP ret_val;
|
|
|
|
|
|
|
|
std::string alias (alias_name);
|
|
|
|
|
|
|
|
if (HasAliasOptions())
|
|
|
|
{
|
|
|
|
pos = m_alias_options.find (alias);
|
|
|
|
if (pos != m_alias_options.end())
|
|
|
|
ret_val = pos->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::RemoveAliasOptions (const char *alias_name)
|
|
|
|
{
|
|
|
|
OptionArgMap::iterator pos = m_alias_options.find(alias_name);
|
|
|
|
if (pos != m_alias_options.end())
|
|
|
|
{
|
|
|
|
m_alias_options.erase (pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::AddOrReplaceAliasOptions (const char *alias_name, OptionArgVectorSP &option_arg_vector_sp)
|
|
|
|
{
|
|
|
|
m_alias_options[alias_name] = option_arg_vector_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::HasCommands ()
|
|
|
|
{
|
|
|
|
return (!m_command_dict.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::HasAliases ()
|
|
|
|
{
|
|
|
|
return (!m_alias_dict.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::HasUserCommands ()
|
|
|
|
{
|
|
|
|
return (!m_user_dict.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::HasAliasOptions ()
|
|
|
|
{
|
|
|
|
return (!m_alias_options.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-10-13 05:57:09 +08:00
|
|
|
CommandInterpreter::BuildAliasCommandArgs (CommandObject *alias_cmd_obj,
|
|
|
|
const char *alias_name,
|
|
|
|
Args &cmd_args,
|
2010-12-08 03:58:26 +08:00
|
|
|
std::string &raw_input_string,
|
2010-10-13 05:57:09 +08:00
|
|
|
CommandReturnObject &result)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name);
|
2010-12-08 03:58:26 +08:00
|
|
|
|
|
|
|
bool wants_raw_input = alias_cmd_obj->WantsRawCommandString();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2010-12-08 03:58:26 +08:00
|
|
|
// Make sure that the alias name is the 0th element in cmd_args
|
|
|
|
std::string alias_name_str = alias_name;
|
|
|
|
if (alias_name_str.compare (cmd_args.GetArgumentAtIndex(0)) != 0)
|
|
|
|
cmd_args.Unshift (alias_name);
|
|
|
|
|
|
|
|
Args new_args (alias_cmd_obj->GetCommandName());
|
|
|
|
if (new_args.GetArgumentCount() == 2)
|
|
|
|
new_args.Shift();
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
if (option_arg_vector_sp.get())
|
|
|
|
{
|
2010-12-08 03:58:26 +08:00
|
|
|
if (wants_raw_input)
|
|
|
|
{
|
|
|
|
// We have a command that both has command options and takes raw input. Make *sure* it has a
|
|
|
|
// " -- " in the right place in the raw_input_string.
|
|
|
|
size_t pos = raw_input_string.find(" -- ");
|
|
|
|
if (pos == std::string::npos)
|
|
|
|
{
|
|
|
|
// None found; assume it goes at the beginning of the raw input string
|
|
|
|
raw_input_string.insert (0, " -- ");
|
|
|
|
}
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
|
|
|
|
int old_size = cmd_args.GetArgumentCount();
|
2010-10-13 05:57:09 +08:00
|
|
|
std::vector<bool> used (old_size + 1, false);
|
|
|
|
|
|
|
|
used[0] = true;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
for (int i = 0; i < option_arg_vector->size(); ++i)
|
|
|
|
{
|
|
|
|
OptionArgPair option_pair = (*option_arg_vector)[i];
|
2010-12-08 03:58:26 +08:00
|
|
|
OptionArgValue value_pair = option_pair.second;
|
|
|
|
int value_type = value_pair.first;
|
2010-06-09 00:52:24 +08:00
|
|
|
std::string option = option_pair.first;
|
2010-12-08 03:58:26 +08:00
|
|
|
std::string value = value_pair.second;
|
2010-06-09 00:52:24 +08:00
|
|
|
if (option.compare ("<argument>") == 0)
|
2010-12-08 03:58:26 +08:00
|
|
|
{
|
|
|
|
if (!wants_raw_input
|
|
|
|
|| (value.compare("--") != 0)) // Since we inserted this above, make sure we don't insert it twice
|
|
|
|
new_args.AppendArgument (value.c_str());
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
else
|
|
|
|
{
|
2010-12-08 03:58:26 +08:00
|
|
|
if (value_type != optional_argument)
|
|
|
|
new_args.AppendArgument (option.c_str());
|
2010-06-09 00:52:24 +08:00
|
|
|
if (value.compare ("<no-argument>") != 0)
|
|
|
|
{
|
|
|
|
int index = GetOptionArgumentPosition (value.c_str());
|
|
|
|
if (index == 0)
|
2010-12-08 03:58:26 +08:00
|
|
|
{
|
2010-06-09 00:52:24 +08:00
|
|
|
// value was NOT a positional argument; must be a real value
|
2010-12-08 03:58:26 +08:00
|
|
|
if (value_type != optional_argument)
|
|
|
|
new_args.AppendArgument (value.c_str());
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char buffer[255];
|
|
|
|
::snprintf (buffer, sizeof (buffer), "%s%s", option.c_str(), value.c_str());
|
|
|
|
new_args.AppendArgument (buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
else if (index >= cmd_args.GetArgumentCount())
|
|
|
|
{
|
|
|
|
result.AppendErrorWithFormat
|
|
|
|
("Not enough arguments provided; you need at least %d arguments to use this alias.\n",
|
|
|
|
index);
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-12-08 03:58:26 +08:00
|
|
|
// Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string
|
|
|
|
size_t strpos = raw_input_string.find (cmd_args.GetArgumentAtIndex (index));
|
|
|
|
if (strpos != std::string::npos)
|
|
|
|
{
|
|
|
|
raw_input_string = raw_input_string.erase (strpos, strlen (cmd_args.GetArgumentAtIndex (index)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value_type != optional_argument)
|
|
|
|
new_args.AppendArgument (cmd_args.GetArgumentAtIndex (index));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char buffer[255];
|
|
|
|
::snprintf (buffer, sizeof(buffer), "%s%s", option.c_str(),
|
|
|
|
cmd_args.GetArgumentAtIndex (index));
|
|
|
|
new_args.AppendArgument (buffer);
|
|
|
|
}
|
2010-10-13 05:57:09 +08:00
|
|
|
used[index] = true;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = 0; j < cmd_args.GetArgumentCount(); ++j)
|
|
|
|
{
|
2010-12-08 03:58:26 +08:00
|
|
|
if (!used[j] && !wants_raw_input)
|
2010-06-09 00:52:24 +08:00
|
|
|
new_args.AppendArgument (cmd_args.GetArgumentAtIndex (j));
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd_args.Clear();
|
|
|
|
cmd_args.SetArguments (new_args.GetArgumentCount(), (const char **) new_args.GetArgumentVector());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
2010-12-08 03:58:26 +08:00
|
|
|
// This alias was not created with any options; nothing further needs to be done, unless it is a command that
|
|
|
|
// wants raw input, in which case we need to clear the rest of the data from cmd_args, since its in the raw
|
|
|
|
// input string.
|
|
|
|
if (wants_raw_input)
|
|
|
|
{
|
|
|
|
cmd_args.Clear();
|
|
|
|
cmd_args.SetArguments (new_args.GetArgumentCount(), (const char **) new_args.GetArgumentVector());
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
CommandInterpreter::GetOptionArgumentPosition (const char *in_string)
|
|
|
|
{
|
|
|
|
int position = 0; // Any string that isn't an argument position, i.e. '%' followed by an integer, gets a position
|
|
|
|
// of zero.
|
|
|
|
|
|
|
|
char *cptr = (char *) in_string;
|
|
|
|
|
|
|
|
// Does it start with '%'
|
|
|
|
if (cptr[0] == '%')
|
|
|
|
{
|
|
|
|
++cptr;
|
|
|
|
|
|
|
|
// Is the rest of it entirely digits?
|
|
|
|
if (isdigit (cptr[0]))
|
|
|
|
{
|
|
|
|
const char *start = cptr;
|
|
|
|
while (isdigit (cptr[0]))
|
|
|
|
++cptr;
|
|
|
|
|
|
|
|
// We've gotten to the end of the digits; are we at the end of the string?
|
|
|
|
if (cptr[0] == '\0')
|
|
|
|
position = atoi (start);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::SourceInitFile (bool in_cwd, CommandReturnObject &result)
|
|
|
|
{
|
2011-08-13 07:34:31 +08:00
|
|
|
FileSpec init_file;
|
2011-09-11 08:01:44 +08:00
|
|
|
if (in_cwd)
|
2011-08-13 07:34:31 +08:00
|
|
|
{
|
2011-09-11 08:01:44 +08:00
|
|
|
// In the current working directory we don't load any program specific
|
|
|
|
// .lldbinit files, we only look for a "./.lldbinit" file.
|
|
|
|
if (m_skip_lldbinit_files)
|
|
|
|
return;
|
|
|
|
|
|
|
|
init_file.SetFile ("./.lldbinit", true);
|
2011-08-13 07:34:31 +08:00
|
|
|
}
|
2011-09-11 08:01:44 +08:00
|
|
|
else
|
2011-08-13 07:34:31 +08:00
|
|
|
{
|
2011-09-11 08:01:44 +08:00
|
|
|
// If we aren't looking in the current working directory we are looking
|
|
|
|
// in the home directory. We will first see if there is an application
|
|
|
|
// specific ".lldbinit" file whose name is "~/.lldbinit" followed by a
|
|
|
|
// "-" and the name of the program. If this file doesn't exist, we fall
|
|
|
|
// back to just the "~/.lldbinit" file. We also obey any requests to not
|
|
|
|
// load the init files.
|
|
|
|
const char *init_file_path = "~/.lldbinit";
|
|
|
|
|
|
|
|
if (m_skip_app_init_files == false)
|
|
|
|
{
|
|
|
|
FileSpec program_file_spec (Host::GetProgramFileSpec());
|
|
|
|
const char *program_name = program_file_spec.GetFilename().AsCString();
|
2011-08-13 07:34:31 +08:00
|
|
|
|
2011-09-11 08:01:44 +08:00
|
|
|
if (program_name)
|
|
|
|
{
|
|
|
|
char program_init_file_name[PATH_MAX];
|
|
|
|
::snprintf (program_init_file_name, sizeof(program_init_file_name), "%s-%s", init_file_path, program_name);
|
|
|
|
init_file.SetFile (program_init_file_name, true);
|
|
|
|
if (!init_file.Exists())
|
|
|
|
init_file.Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!init_file && !m_skip_lldbinit_files)
|
|
|
|
init_file.SetFile (init_file_path, true);
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
// If the file exists, tell HandleCommand to 'source' it; this will do the actual broadcasting
|
|
|
|
// of the commands back to any appropriate listener (see CommandObjectSource::Execute for more details).
|
|
|
|
|
|
|
|
if (init_file.Exists())
|
|
|
|
{
|
2011-02-18 08:54:25 +08:00
|
|
|
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;
|
|
|
|
|
|
|
|
HandleCommandsFromFile (init_file, exe_ctx, stop_on_continue, stop_on_error, echo_commands, print_results, result);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// nothing to be done if the file doesn't exist
|
|
|
|
result.SetStatus(eReturnStatusSuccessFinishNoResult);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-12 13:54:46 +08:00
|
|
|
PlatformSP
|
|
|
|
CommandInterpreter::GetPlatform (bool prefer_target_platform)
|
|
|
|
{
|
|
|
|
PlatformSP platform_sp;
|
2011-09-22 12:58:26 +08:00
|
|
|
if (prefer_target_platform)
|
|
|
|
{
|
|
|
|
Target *target = m_exe_ctx.GetTargetPtr();
|
|
|
|
if (target)
|
|
|
|
platform_sp = target->GetPlatform();
|
|
|
|
}
|
2011-04-12 13:54:46 +08:00
|
|
|
|
|
|
|
if (!platform_sp)
|
|
|
|
platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform();
|
|
|
|
return platform_sp;
|
|
|
|
}
|
|
|
|
|
2011-02-18 08:54:25 +08:00
|
|
|
void
|
2011-03-11 09:51:49 +08:00
|
|
|
CommandInterpreter::HandleCommands (const StringList &commands,
|
2011-02-18 08:54:25 +08:00
|
|
|
ExecutionContext *override_context,
|
|
|
|
bool stop_on_continue,
|
|
|
|
bool stop_on_error,
|
|
|
|
bool echo_commands,
|
|
|
|
bool print_results,
|
|
|
|
CommandReturnObject &result)
|
|
|
|
{
|
|
|
|
size_t num_lines = commands.GetSize();
|
|
|
|
|
|
|
|
// If we are going to continue past a "continue" then we need to run the commands synchronously.
|
|
|
|
// Make sure you reset this value anywhere you return from the function.
|
|
|
|
|
|
|
|
bool old_async_execution = m_debugger.GetAsyncExecution();
|
|
|
|
|
|
|
|
// If we've been given an execution context, set it at the start, but don't keep resetting it or we will
|
|
|
|
// cause series of commands that change the context, then do an operation that relies on that context to fail.
|
|
|
|
|
|
|
|
if (override_context != NULL)
|
2011-04-12 13:54:46 +08:00
|
|
|
UpdateExecutionContext (override_context);
|
2011-02-18 08:54:25 +08:00
|
|
|
|
|
|
|
if (!stop_on_continue)
|
|
|
|
{
|
|
|
|
m_debugger.SetAsyncExecution (false);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int idx = 0; idx < num_lines; idx++)
|
|
|
|
{
|
|
|
|
const char *cmd = commands.GetStringAtIndex(idx);
|
|
|
|
if (cmd[0] == '\0')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (echo_commands)
|
|
|
|
{
|
|
|
|
result.AppendMessageWithFormat ("%s %s\n",
|
|
|
|
GetPrompt(),
|
|
|
|
cmd);
|
|
|
|
}
|
|
|
|
|
2011-02-20 10:15:07 +08:00
|
|
|
CommandReturnObject tmp_result;
|
2011-10-05 08:42:59 +08:00
|
|
|
// If override_context is not NULL, pass no_context_switching = true for
|
|
|
|
// HandleCommand() since we updated our context already.
|
|
|
|
bool success = HandleCommand(cmd, false, tmp_result,
|
|
|
|
NULL, /* override_context */
|
|
|
|
true, /* repeat_on_empty_command */
|
|
|
|
override_context != NULL /* no_context_switching */);
|
2011-02-18 08:54:25 +08:00
|
|
|
|
|
|
|
if (print_results)
|
|
|
|
{
|
|
|
|
if (tmp_result.Succeeded())
|
2011-02-19 10:53:09 +08:00
|
|
|
result.AppendMessageWithFormat("%s", tmp_result.GetOutputData());
|
2011-02-18 08:54:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!success || !tmp_result.Succeeded())
|
|
|
|
{
|
|
|
|
if (stop_on_error)
|
|
|
|
{
|
|
|
|
result.AppendErrorWithFormat("Aborting reading of commands after command #%d: '%s' failed.\n",
|
|
|
|
idx, cmd);
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
|
|
|
m_debugger.SetAsyncExecution (old_async_execution);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (print_results)
|
|
|
|
{
|
|
|
|
result.AppendMessageWithFormat ("Command #%d '%s' failed with error: %s.\n",
|
|
|
|
idx + 1,
|
|
|
|
cmd,
|
2011-02-19 10:53:09 +08:00
|
|
|
tmp_result.GetErrorData());
|
2011-02-18 08:54:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
|
|
|
if (result.GetImmediateOutputStream())
|
|
|
|
result.GetImmediateOutputStream()->Flush();
|
|
|
|
|
|
|
|
if (result.GetImmediateErrorStream())
|
|
|
|
result.GetImmediateErrorStream()->Flush();
|
|
|
|
|
2011-02-18 08:54:25 +08:00
|
|
|
// N.B. Can't depend on DidChangeProcessState, because the state coming into the command execution
|
|
|
|
// could be running (for instance in Breakpoint Commands.
|
|
|
|
// So we check the return value to see if it is has running in it.
|
|
|
|
if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult)
|
|
|
|
|| (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult))
|
|
|
|
{
|
|
|
|
if (stop_on_continue)
|
|
|
|
{
|
|
|
|
// If we caused the target to proceed, and we're going to stop in that case, set the
|
|
|
|
// status in our real result before returning. This is an error if the continue was not the
|
|
|
|
// last command in the set of commands to be run.
|
|
|
|
if (idx != num_lines - 1)
|
|
|
|
result.AppendErrorWithFormat("Aborting reading of commands after command #%d: '%s' continued the target.\n",
|
|
|
|
idx + 1, cmd);
|
|
|
|
else
|
|
|
|
result.AppendMessageWithFormat ("Command #%d '%s' continued the target.\n", idx + 1, cmd);
|
|
|
|
|
|
|
|
result.SetStatus(tmp_result.GetStatus());
|
|
|
|
m_debugger.SetAsyncExecution (old_async_execution);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
result.SetStatus (eReturnStatusSuccessFinishResult);
|
|
|
|
m_debugger.SetAsyncExecution (old_async_execution);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file,
|
|
|
|
ExecutionContext *context,
|
|
|
|
bool stop_on_continue,
|
|
|
|
bool stop_on_error,
|
|
|
|
bool echo_command,
|
|
|
|
bool print_result,
|
|
|
|
CommandReturnObject &result)
|
|
|
|
{
|
|
|
|
if (cmd_file.Exists())
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
HandleCommands (commands, context, stop_on_continue, stop_on_error, echo_command, print_result, result);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.AppendErrorWithFormat ("Error reading commands from file %s - file not found.\n",
|
|
|
|
cmd_file.GetFilename().AsCString());
|
|
|
|
result.SetStatus (eReturnStatusFailed);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
ScriptInterpreter *
|
|
|
|
CommandInterpreter::GetScriptInterpreter ()
|
|
|
|
{
|
2011-01-14 08:29:16 +08:00
|
|
|
if (m_script_interpreter_ap.get() != NULL)
|
|
|
|
return m_script_interpreter_ap.get();
|
2010-06-23 09:19:29 +08:00
|
|
|
|
2011-01-14 08:29:16 +08:00
|
|
|
lldb::ScriptLanguage script_lang = GetDebugger().GetScriptLanguage();
|
|
|
|
switch (script_lang)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-01-14 08:29:16 +08:00
|
|
|
case eScriptLanguageNone:
|
|
|
|
m_script_interpreter_ap.reset (new ScriptInterpreterNone (*this));
|
|
|
|
break;
|
|
|
|
case eScriptLanguagePython:
|
|
|
|
m_script_interpreter_ap.reset (new ScriptInterpreterPython (*this));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
return m_script_interpreter_ap.get();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
CommandInterpreter::GetSynchronous ()
|
|
|
|
{
|
|
|
|
return m_synchronous_execution;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::SetSynchronous (bool value)
|
|
|
|
{
|
2010-10-14 09:22:03 +08:00
|
|
|
m_synchronous_execution = value;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::OutputFormattedHelpText (Stream &strm,
|
|
|
|
const char *word_text,
|
|
|
|
const char *separator,
|
|
|
|
const char *help_text,
|
|
|
|
uint32_t max_word_len)
|
|
|
|
{
|
2010-09-18 09:14:36 +08:00
|
|
|
const uint32_t max_columns = m_debugger.GetTerminalWidth();
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
int indent_size = max_word_len + strlen (separator) + 2;
|
|
|
|
|
|
|
|
strm.IndentMore (indent_size);
|
2011-02-18 09:44:25 +08:00
|
|
|
|
|
|
|
StreamString text_strm;
|
|
|
|
text_strm.Printf ("%-*s %s %s", max_word_len, word_text, separator, help_text);
|
|
|
|
|
|
|
|
size_t len = text_strm.GetSize();
|
|
|
|
const char *text = text_strm.GetData();
|
2010-06-09 00:52:24 +08:00
|
|
|
if (text[len - 1] == '\n')
|
2011-02-18 09:44:25 +08:00
|
|
|
{
|
|
|
|
text_strm.EOL();
|
|
|
|
len = text_strm.GetSize();
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
if (len < max_columns)
|
|
|
|
{
|
|
|
|
// Output it as a single line.
|
|
|
|
strm.Printf ("%s", text);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We need to break it up into multiple lines.
|
|
|
|
bool first_line = true;
|
|
|
|
int text_width;
|
|
|
|
int start = 0;
|
|
|
|
int end = start;
|
|
|
|
int final_end = strlen (text);
|
|
|
|
int sub_len;
|
|
|
|
|
|
|
|
while (end < final_end)
|
|
|
|
{
|
|
|
|
if (first_line)
|
|
|
|
text_width = max_columns - 1;
|
|
|
|
else
|
|
|
|
text_width = max_columns - indent_size - 1;
|
|
|
|
|
|
|
|
// Don't start the 'text' on a space, since we're already outputting the indentation.
|
|
|
|
if (!first_line)
|
|
|
|
{
|
|
|
|
while ((start < final_end) && (text[start] == ' '))
|
|
|
|
start++;
|
|
|
|
}
|
|
|
|
|
|
|
|
end = start + text_width;
|
|
|
|
if (end > final_end)
|
|
|
|
end = final_end;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If we're not at the end of the text, make sure we break the line on white space.
|
|
|
|
while (end > start
|
|
|
|
&& text[end] != ' ' && text[end] != '\t' && text[end] != '\n')
|
|
|
|
end--;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub_len = end - start;
|
|
|
|
if (start != 0)
|
|
|
|
strm.EOL();
|
|
|
|
if (!first_line)
|
|
|
|
strm.Indent();
|
|
|
|
else
|
|
|
|
first_line = false;
|
|
|
|
assert (start <= final_end);
|
|
|
|
assert (start + sub_len <= final_end);
|
|
|
|
if (sub_len > 0)
|
|
|
|
strm.Write (text + start, sub_len);
|
|
|
|
start = end + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
strm.EOL();
|
|
|
|
strm.IndentLess(indent_size);
|
|
|
|
}
|
|
|
|
|
2011-07-07 08:38:40 +08:00
|
|
|
void
|
|
|
|
CommandInterpreter::OutputHelpText (Stream &strm,
|
|
|
|
const char *word_text,
|
|
|
|
const char *separator,
|
|
|
|
const char *help_text,
|
|
|
|
uint32_t max_word_len)
|
|
|
|
{
|
|
|
|
int indent_size = max_word_len + strlen (separator) + 2;
|
|
|
|
|
|
|
|
strm.IndentMore (indent_size);
|
|
|
|
|
|
|
|
StreamString text_strm;
|
|
|
|
text_strm.Printf ("%-*s %s %s", max_word_len, word_text, separator, help_text);
|
|
|
|
|
|
|
|
const uint32_t max_columns = m_debugger.GetTerminalWidth();
|
|
|
|
bool first_line = true;
|
|
|
|
|
|
|
|
size_t len = text_strm.GetSize();
|
|
|
|
const char *text = text_strm.GetData();
|
|
|
|
|
|
|
|
uint32_t chars_left = max_columns;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
if ((text[i] == ' ' && ::strchr((text+i+1), ' ') && chars_left < ::strchr((text+i+1), ' ')-(text+i)) || text[i] == '\n')
|
|
|
|
{
|
|
|
|
first_line = false;
|
|
|
|
chars_left = max_columns - indent_size;
|
|
|
|
strm.EOL();
|
|
|
|
strm.Indent();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
strm.PutChar(text[i]);
|
|
|
|
chars_left--;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
strm.EOL();
|
|
|
|
strm.IndentLess(indent_size);
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
void
|
|
|
|
CommandInterpreter::AproposAllSubCommands (CommandObject *cmd_obj, const char *prefix, const char *search_word,
|
|
|
|
StringList &commands_found, StringList &commands_help)
|
|
|
|
{
|
|
|
|
CommandObject::CommandMap::const_iterator pos;
|
|
|
|
CommandObject::CommandMap sub_cmd_dict = ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict;
|
|
|
|
CommandObject *sub_cmd_obj;
|
|
|
|
|
|
|
|
for (pos = sub_cmd_dict.begin(); pos != sub_cmd_dict.end(); ++pos)
|
|
|
|
{
|
|
|
|
const char * command_name = pos->first.c_str();
|
|
|
|
sub_cmd_obj = pos->second.get();
|
|
|
|
StreamString complete_command_name;
|
|
|
|
|
|
|
|
complete_command_name.Printf ("%s %s", prefix, command_name);
|
|
|
|
|
2010-09-18 09:14:36 +08:00
|
|
|
if (sub_cmd_obj->HelpTextContainsWord (search_word))
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
commands_found.AppendString (complete_command_name.GetData());
|
|
|
|
commands_help.AppendString (sub_cmd_obj->GetHelp());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sub_cmd_obj->IsMultiwordObject())
|
|
|
|
AproposAllSubCommands (sub_cmd_obj, complete_command_name.GetData(), search_word, commands_found,
|
|
|
|
commands_help);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::FindCommandsForApropos (const char *search_word, StringList &commands_found,
|
|
|
|
StringList &commands_help)
|
|
|
|
{
|
|
|
|
CommandObject::CommandMap::const_iterator pos;
|
|
|
|
|
|
|
|
for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos)
|
|
|
|
{
|
|
|
|
const char *command_name = pos->first.c_str();
|
|
|
|
CommandObject *cmd_obj = pos->second.get();
|
|
|
|
|
2010-09-18 09:14:36 +08:00
|
|
|
if (cmd_obj->HelpTextContainsWord (search_word))
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
commands_found.AppendString (command_name);
|
|
|
|
commands_help.AppendString (cmd_obj->GetHelp());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd_obj->IsMultiwordObject())
|
|
|
|
AproposAllSubCommands (cmd_obj, command_name, search_word, commands_found, commands_help);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2011-04-12 13:54:46 +08:00
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::UpdateExecutionContext (ExecutionContext *override_context)
|
|
|
|
{
|
|
|
|
m_exe_ctx.Clear();
|
|
|
|
|
|
|
|
if (override_context != NULL)
|
|
|
|
{
|
2011-09-22 12:58:26 +08:00
|
|
|
m_exe_ctx = *override_context;
|
2011-04-12 13:54:46 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TargetSP target_sp (m_debugger.GetSelectedTarget());
|
|
|
|
if (target_sp)
|
|
|
|
{
|
2011-09-22 12:58:26 +08:00
|
|
|
m_exe_ctx.SetTargetSP (target_sp);
|
|
|
|
ProcessSP process_sp (target_sp->GetProcessSP());
|
|
|
|
m_exe_ctx.SetProcessSP (process_sp);
|
|
|
|
if (process_sp && process_sp->IsAlive() && !process_sp->IsRunning())
|
2011-04-12 13:54:46 +08:00
|
|
|
{
|
2011-09-22 12:58:26 +08:00
|
|
|
ThreadSP thread_sp (process_sp->GetThreadList().GetSelectedThread().get());
|
|
|
|
if (thread_sp)
|
2011-04-12 13:54:46 +08:00
|
|
|
{
|
2011-09-22 12:58:26 +08:00
|
|
|
m_exe_ctx.SetThreadSP (thread_sp);
|
|
|
|
StackFrameSP frame_sp (thread_sp->GetSelectedFrame());
|
|
|
|
if (!frame_sp)
|
2011-04-12 13:54:46 +08:00
|
|
|
{
|
2011-09-22 12:58:26 +08:00
|
|
|
frame_sp = thread_sp->GetStackFrameAtIndex (0);
|
2011-04-12 13:54:46 +08:00
|
|
|
// If we didn't have a selected frame select one here.
|
2011-09-22 12:58:26 +08:00
|
|
|
if (frame_sp)
|
|
|
|
thread_sp->SetSelectedFrame(frame_sp.get());
|
2011-04-12 13:54:46 +08:00
|
|
|
}
|
2011-09-22 12:58:26 +08:00
|
|
|
if (frame_sp)
|
|
|
|
m_exe_ctx.SetFrameSP (frame_sp);
|
2011-04-12 13:54:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-12 11:12:18 +08:00
|
|
|
void
|
|
|
|
CommandInterpreter::DumpHistory (Stream &stream, uint32_t count) const
|
|
|
|
{
|
|
|
|
DumpHistory (stream, 0, count - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CommandInterpreter::DumpHistory (Stream &stream, uint32_t start, uint32_t end) const
|
|
|
|
{
|
|
|
|
size_t num_history_elements = m_command_history.size();
|
|
|
|
if (start > num_history_elements)
|
|
|
|
return;
|
|
|
|
for (uint32_t i = start; i < num_history_elements && i <= end; i++)
|
|
|
|
{
|
|
|
|
if (!m_command_history[i].empty())
|
|
|
|
{
|
|
|
|
stream.Indent();
|
|
|
|
stream.Printf ("%4d: %s\n", i, m_command_history[i].c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
CommandInterpreter::FindHistoryString (const char *input_str) const
|
|
|
|
{
|
|
|
|
if (input_str[0] != m_repeat_char)
|
|
|
|
return NULL;
|
|
|
|
if (input_str[1] == '-')
|
|
|
|
{
|
|
|
|
bool success;
|
|
|
|
uint32_t idx = Args::StringToUInt32 (input_str+2, 0, 0, &success);
|
|
|
|
if (!success)
|
|
|
|
return NULL;
|
|
|
|
if (idx > m_command_history.size())
|
|
|
|
return NULL;
|
|
|
|
idx = m_command_history.size() - idx;
|
|
|
|
return m_command_history[idx].c_str();
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (input_str[1] == m_repeat_char)
|
|
|
|
{
|
|
|
|
if (m_command_history.empty())
|
|
|
|
return NULL;
|
|
|
|
else
|
|
|
|
return m_command_history.back().c_str();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool success;
|
|
|
|
uint32_t idx = Args::StringToUInt32 (input_str+1, 0, 0, &success);
|
|
|
|
if (!success)
|
|
|
|
return NULL;
|
|
|
|
if (idx >= m_command_history.size())
|
|
|
|
return NULL;
|
|
|
|
return m_command_history[idx].c_str();
|
|
|
|
}
|
|
|
|
}
|