<rdar://problem/12491387>

I added the ability for a process plug-in to implement custom commands. All the lldb_private::Process plug-in has to do is override:

virtual CommandObject *
GetPluginCommandObject();

This object returned should be a multi-word command that vends LLDB commands. There is a sample implementation in ProcessGDBRemote that is hollowed out. It is intended to be used for sending a custom packet, though the body of the command execute function has yet to be implemented! 

llvm-svn: 165861
This commit is contained in:
Greg Clayton 2012-10-13 02:07:45 +00:00
parent 88db3171dd
commit 998255bfe8
14 changed files with 491 additions and 70 deletions

View File

@ -412,13 +412,6 @@ public:
FindCommandsForApropos (const char *word,
StringList &commands_found,
StringList &commands_help);
void
AproposAllSubCommands (CommandObject *cmd_obj,
const char *prefix,
const char *search_word,
StringList &commands_found,
StringList &commands_help);
bool
GetBatchCommandMode () { return m_batch_command_mode; }

View File

@ -144,6 +144,31 @@ public:
virtual bool
IsMultiwordObject () { return false; }
virtual lldb::CommandObjectSP
GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL)
{
return lldb::CommandObjectSP();
}
virtual CommandObject *
GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL)
{
return NULL;
}
virtual void
AproposAllSubCommands (const char *prefix,
const char *search_word,
StringList &commands_found,
StringList &commands_help)
{
}
virtual void
GenerateHelpText (CommandReturnObject &result)
{
}
// this is needed in order to allow the SBCommand class to
// transparently try and load subcommands - it will fail on
// anything but a multiword command, but it avoids us doing

View File

@ -34,7 +34,7 @@ public:
virtual
~CommandObjectCrossref ();
void
virtual void
GenerateHelpText (CommandReturnObject &result);
virtual bool

View File

@ -46,15 +46,21 @@ public:
LoadSubCommand (const char *cmd_name,
const lldb::CommandObjectSP& command_obj);
void
virtual void
GenerateHelpText (CommandReturnObject &result);
lldb::CommandObjectSP
virtual lldb::CommandObjectSP
GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL);
CommandObject *
virtual CommandObject *
GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL);
virtual void
AproposAllSubCommands (const char *prefix,
const char *search_word,
StringList &commands_found,
StringList &commands_help);
virtual bool
WantsRawCommandString() { return false; };
@ -88,6 +94,100 @@ protected:
bool m_can_be_removed;
};
class CommandObjectProxy : public CommandObject
{
public:
CommandObjectProxy (CommandInterpreter &interpreter,
const char *name,
const char *help = NULL,
const char *syntax = NULL,
uint32_t flags = 0);
virtual
~CommandObjectProxy ();
// Subclasses must provide a command object that will be transparently
// used for this object.
virtual CommandObject *
GetProxyCommandObject() = 0;
virtual const char *
GetHelpLong ();
virtual void
AddObject (const char *obj_name);
virtual bool
IsCrossRefObject ();
virtual bool
IsRemovable() const;
virtual bool
IsMultiwordObject ();
virtual lldb::CommandObjectSP
GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL);
virtual CommandObject *
GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL);
virtual void
AproposAllSubCommands (const char *prefix,
const char *search_word,
StringList &commands_found,
StringList &commands_help);
virtual bool
LoadSubCommand (const char *cmd_name,
const lldb::CommandObjectSP& command_obj);
virtual bool
WantsRawCommandString();
virtual bool
WantsCompletion();
virtual Options *
GetOptions ();
virtual int
HandleCompletion (Args &input,
int &cursor_index,
int &cursor_char_position,
int match_start_point,
int max_return_elements,
bool &word_complete,
StringList &matches);
virtual int
HandleArgumentCompletion (Args &input,
int &cursor_index,
int &cursor_char_position,
OptionElementVector &opt_element_vector,
int match_start_point,
int max_return_elements,
bool &word_complete,
StringList &matches);
virtual const char *
GetRepeatCommand (Args &current_command_args,
uint32_t index);
virtual bool
Execute (const char *args_string,
CommandReturnObject &result);
protected:
// These two want to iterate over the subcommand dictionary.
friend class CommandInterpreter;
friend class CommandObjectSyntax;
};
} // namespace lldb_private
#endif // liblldb_CommandObjectMultiword_h_

View File

@ -1578,6 +1578,26 @@ public:
return !m_finalize_called;
}
//------------------------------------------------------------------
/// Return a multi-word command object that can be used to expose
/// plug-in specific commands.
///
/// This object will be used to resolve plug-in commands and can be
/// triggered by a call to:
///
/// (lldb) process commmand <args>
///
/// @return
/// A CommandObject which can be one of the concrete subclasses
/// of CommandObject like CommandObjectRaw, CommandObjectParsed,
/// or CommandObjectMultiword.
//------------------------------------------------------------------
virtual CommandObject *
GetPluginCommandObject()
{
return NULL;
}
//------------------------------------------------------------------
/// Launch a new process.
///

View File

@ -599,8 +599,7 @@ protected:
{
const std::string sub_command = args.GetArgumentAtIndex(0);
assert (sub_command.length() != 0);
subcommand_obj_sp =
(((CommandObjectMultiword *) cmd_obj)->GetSubcommandSP (sub_command.c_str()));
subcommand_obj_sp = cmd_obj->GetSubcommandSP (sub_command.c_str());
if (subcommand_obj_sp.get())
{
sub_cmd_obj = subcommand_obj_sp.get();

View File

@ -103,8 +103,7 @@ CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result)
else
{
CommandObject *found_cmd;
found_cmd = ((CommandObjectMultiword *) sub_cmd_obj)->GetSubcommandObject(sub_command.c_str(),
&matches);
found_cmd = sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches);
if (found_cmd == NULL)
all_okay = false;
else if (matches.GetSize() > 1)
@ -189,7 +188,7 @@ CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result)
}
else
m_interpreter.OutputFormattedHelpText (output_strm, "", "", sub_cmd_obj->GetHelp(), 1);
((CommandObjectMultiword *) sub_cmd_obj)->GenerateHelpText (result);
sub_cmd_obj->GenerateHelpText (result);
}
else
{

View File

@ -307,3 +307,232 @@ CommandObjectMultiword::GetRepeatCommand (Args &current_command_args, uint32_t i
return sub_command_object->GetRepeatCommand(current_command_args, index);
}
void
CommandObjectMultiword::AproposAllSubCommands (const char *prefix,
const char *search_word,
StringList &commands_found,
StringList &commands_help)
{
CommandObject::CommandMap::const_iterator pos;
for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos)
{
const char * command_name = pos->first.c_str();
CommandObject *sub_cmd_obj = pos->second.get();
StreamString complete_command_name;
complete_command_name.Printf ("%s %s", prefix, command_name);
if (sub_cmd_obj->HelpTextContainsWord (search_word))
{
commands_found.AppendString (complete_command_name.GetData());
commands_help.AppendString (sub_cmd_obj->GetHelp());
}
if (sub_cmd_obj->IsMultiwordObject())
sub_cmd_obj->AproposAllSubCommands (complete_command_name.GetData(),
search_word,
commands_found,
commands_help);
}
}
CommandObjectProxy::CommandObjectProxy (CommandInterpreter &interpreter,
const char *name,
const char *help,
const char *syntax,
uint32_t flags) :
CommandObject (interpreter, name, help, syntax, flags)
{
}
CommandObjectProxy::~CommandObjectProxy ()
{
}
const char *
CommandObjectProxy::GetHelpLong ()
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->GetHelpLong();
return NULL;
}
void
CommandObjectProxy::AddObject (const char *obj_name)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->AddObject (obj_name);
}
bool
CommandObjectProxy::IsCrossRefObject ()
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->IsCrossRefObject();
return false;
}
bool
CommandObjectProxy::IsRemovable() const
{
const CommandObject *proxy_command = const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();
if (proxy_command)
return proxy_command->IsRemovable();
return false;
}
bool
CommandObjectProxy::IsMultiwordObject ()
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->IsMultiwordObject();
return false;
}
lldb::CommandObjectSP
CommandObjectProxy::GetSubcommandSP (const char *sub_cmd, StringList *matches)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->GetSubcommandSP(sub_cmd, matches);
return lldb::CommandObjectSP();
}
CommandObject *
CommandObjectProxy::GetSubcommandObject (const char *sub_cmd, StringList *matches)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->GetSubcommandObject(sub_cmd, matches);
return NULL;
}
void
CommandObjectProxy::AproposAllSubCommands (const char *prefix,
const char *search_word,
StringList &commands_found,
StringList &commands_help)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->AproposAllSubCommands (prefix,
search_word,
commands_found,
commands_help);
}
bool
CommandObjectProxy::LoadSubCommand (const char *cmd_name,
const lldb::CommandObjectSP& command_sp)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->LoadSubCommand (cmd_name, command_sp);
return false;
}
bool
CommandObjectProxy::WantsRawCommandString()
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->WantsRawCommandString();
return false;
}
bool
CommandObjectProxy::WantsCompletion()
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->WantsCompletion();
return false;
}
Options *
CommandObjectProxy::GetOptions ()
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->GetOptions ();
return NULL;
}
int
CommandObjectProxy::HandleCompletion (Args &input,
int &cursor_index,
int &cursor_char_position,
int match_start_point,
int max_return_elements,
bool &word_complete,
StringList &matches)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->HandleCompletion (input,
cursor_index,
cursor_char_position,
match_start_point,
max_return_elements,
word_complete,
matches);
matches.Clear();
return 0;
}
int
CommandObjectProxy::HandleArgumentCompletion (Args &input,
int &cursor_index,
int &cursor_char_position,
OptionElementVector &opt_element_vector,
int match_start_point,
int max_return_elements,
bool &word_complete,
StringList &matches)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->HandleArgumentCompletion (input,
cursor_index,
cursor_char_position,
opt_element_vector,
match_start_point,
max_return_elements,
word_complete,
matches);
matches.Clear();
return 0;
}
const char *
CommandObjectProxy::GetRepeatCommand (Args &current_command_args,
uint32_t index)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->GetRepeatCommand (current_command_args, index);
return NULL;
}
bool
CommandObjectProxy::Execute (const char *args_string,
CommandReturnObject &result)
{
CommandObject *proxy_command = GetProxyCommandObject();
if (proxy_command)
return proxy_command->Execute (args_string, result);
result.AppendError ("command is not implemented");
result.SetStatus (eReturnStatusFailed);
return false;
}

View File

@ -1071,7 +1071,6 @@ protected:
CommandOptions m_options;
};
OptionDefinition
CommandObjectProcessConnect::CommandOptions::g_option_table[] =
{
@ -1079,6 +1078,39 @@ CommandObjectProcessConnect::CommandOptions::g_option_table[] =
{ 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL }
};
//-------------------------------------------------------------------------
// CommandObjectProcessPlugin
//-------------------------------------------------------------------------
#pragma mark CommandObjectProcessPlugin
class CommandObjectProcessPlugin : public CommandObjectProxy
{
public:
CommandObjectProcessPlugin (CommandInterpreter &interpreter) :
CommandObjectProxy (interpreter,
"process plugin",
"Send a custom command to the current process plug-in.",
"process plugin <args>",
0)
{
}
~CommandObjectProcessPlugin ()
{
}
virtual CommandObject *
GetProxyCommandObject()
{
Process *process = m_interpreter.GetExecutionContext().GetProcessPtr();
if (process)
return process->GetPluginCommandObject();
return NULL;
}
};
//-------------------------------------------------------------------------
// CommandObjectProcessLoad
//-------------------------------------------------------------------------
@ -1794,6 +1826,7 @@ CommandObjectMultiwordProcess::CommandObjectMultiwordProcess (CommandInterpreter
LoadSubCommand ("status", CommandObjectSP (new CommandObjectProcessStatus (interpreter)));
LoadSubCommand ("interrupt", CommandObjectSP (new CommandObjectProcessInterrupt (interpreter)));
LoadSubCommand ("kill", CommandObjectSP (new CommandObjectProcessKill (interpreter)));
LoadSubCommand ("plugin", CommandObjectSP (new CommandObjectProcessPlugin (interpreter)));
}
CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess ()

View File

@ -66,14 +66,12 @@ CommandObjectSyntax::DoExecute (Args& command, CommandReturnObject &result)
for (int i = 1; i < argc; ++i)
{
std::string sub_command = command.GetArgumentAtIndex (i);
if (! cmd_obj->IsMultiwordObject())
if (!cmd_obj->IsMultiwordObject())
all_okay = false;
else
{
pos = ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict.find (sub_command);
if (pos != ((CommandObjectMultiword *) cmd_obj)->m_subcommand_dict.end())
cmd_obj = pos->second.get();
else
cmd_obj = cmd_obj->GetSubcommandObject(sub_command.c_str());
if (!cmd_obj)
all_okay = false;
}
}

View File

@ -763,8 +763,7 @@ CommandInterpreter::GetCommandSPExact (const char *cmd_cstr, bool include_aliase
{
if (cmd_obj_sp->IsMultiwordObject())
{
cmd_obj_sp = ((CommandObjectMultiword *) cmd_obj_sp.get())->GetSubcommandSP
(cmd_words.GetArgumentAtIndex (j));
cmd_obj_sp = cmd_obj_sp->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;
@ -1049,8 +1048,7 @@ CommandInterpreter::GetCommandObjectForCommand (std::string &command_string)
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());
CommandObject *sub_cmd_obj = 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
@ -1547,7 +1545,7 @@ CommandInterpreter::HandleCommand (const char *command_line,
{
if (cmd_obj->IsMultiwordObject ())
{
CommandObject *sub_cmd_obj = ((CommandObjectMultiword *) cmd_obj)->GetSubcommandObject (next_word.c_str());
CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject (next_word.c_str());
if (sub_cmd_obj)
{
actual_cmd_name_len += next_word.length() + 1;
@ -2721,35 +2719,6 @@ CommandInterpreter::OutputHelpText (Stream &strm,
strm.IndentLess(indent_size);
}
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);
if (sub_cmd_obj->HelpTextContainsWord (search_word))
{
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)
@ -2768,7 +2737,10 @@ CommandInterpreter::FindCommandsForApropos (const char *search_word, StringList
}
if (cmd_obj->IsMultiwordObject())
AproposAllSubCommands (cmd_obj, command_name, search_word, commands_found, commands_help);
cmd_obj->AproposAllSubCommands (command_name,
search_word,
commands_found,
commands_help);
}
}

View File

@ -377,23 +377,19 @@ CommandObject::HandleCompletion
bool
CommandObject::HelpTextContainsWord (const char *search_word)
{
const char *short_help;
const char *long_help;
const char *syntax_help;
std::string options_usage_help;
bool found_word = false;
short_help = GetHelp();
long_help = GetHelpLong();
syntax_help = GetSyntax();
const char *short_help = GetHelp();
const char *long_help = GetHelpLong();
const char *syntax_help = GetSyntax();
if (strcasestr (short_help, search_word))
if (short_help && strcasestr (short_help, search_word))
found_word = true;
else if (strcasestr (long_help, search_word))
else if (long_help && strcasestr (long_help, search_word))
found_word = true;
else if (strcasestr (syntax_help, search_word))
else if (syntax_help && strcasestr (syntax_help, search_word))
found_word = true;
if (!found_word

View File

@ -196,7 +196,8 @@ ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) :
m_waiting_for_attach (false),
m_destroy_tried_resuming (false),
m_dyld_plugin_name(),
m_kernel_load_addr (LLDB_INVALID_ADDRESS)
m_kernel_load_addr (LLDB_INVALID_ADDRESS),
m_command_sp ()
{
m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit");
m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue");
@ -3013,3 +3014,58 @@ ProcessGDBRemote::GetDynamicLoader ()
return m_dyld_ap.get();
}
#include "lldb/Interpreter/CommandObject.h"
#include "lldb/Interpreter/CommandObjectMultiword.h"
class CommandObjectProcessGDBRemotePacket : public CommandObjectParsed
{
private:
public:
CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) :
CommandObjectParsed (interpreter,
"process plugin packet",
"Send a custom packet through the GDB remote protocol and print the answer.",
NULL)
{
}
~CommandObjectProcessGDBRemotePacket ()
{
}
bool
DoExecute (Args& command, CommandReturnObject &result)
{
printf ("CommandObjectProcessGDBRemotePacket::DoExecute() called!!!\n");
return true;
}
};
class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword
{
public:
CommandObjectMultiwordProcessGDBRemote (CommandInterpreter &interpreter) :
CommandObjectMultiword (interpreter,
"process plugin",
"A set of commands for operating on a ProcessGDBRemote process.",
"process plugin <subcommand> [<subcommand-options>]")
{
LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessGDBRemotePacket (interpreter)));
}
~CommandObjectMultiwordProcessGDBRemote ()
{
}
};
CommandObject *
ProcessGDBRemote::GetPluginCommandObject()
{
if (!m_command_sp)
m_command_sp.reset (new CommandObjectMultiwordProcessGDBRemote (GetTarget().GetDebugger().GetCommandInterpreter()));
return m_command_sp.get();
}

View File

@ -71,8 +71,8 @@ public:
CanDebug (lldb_private::Target &target,
bool plugin_specified_by_name);
// virtual uint32_t
// ListProcessesMatchingName (const char *name, lldb_private::StringList &matches, std::vector<lldb::pid_t> &pids);
virtual lldb_private::CommandObject *
GetPluginCommandObject();
//------------------------------------------------------------------
// Creating a new process, or attaching to an existing one
@ -331,6 +331,7 @@ protected:
bool m_destroy_tried_resuming;
std::string m_dyld_plugin_name;
lldb::addr_t m_kernel_load_addr;
lldb::CommandObjectSP m_command_sp;
bool
StartAsyncThread ();