summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp')
-rw-r--r--contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp334
1 files changed, 179 insertions, 155 deletions
diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp
index 0d9ffda..448da0e 100644
--- a/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp
@@ -7,8 +7,6 @@
//
//===----------------------------------------------------------------------===//
-#include "lldb/lldb-python.h"
-
#include "CommandObjectTarget.h"
// C Includes
@@ -27,6 +25,7 @@
#include "lldb/Core/Timer.h"
#include "lldb/Core/ValueObjectVariable.h"
#include "lldb/DataFormatters/ValueObjectPrinter.h"
+#include "lldb/Host/StringConvert.h"
#include "lldb/Host/Symbols.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
@@ -49,6 +48,7 @@
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
@@ -159,7 +159,6 @@ public:
NULL),
m_option_group (interpreter),
m_arch_option (),
- m_platform_options(true), // Do include the "--platform" option in the platform settings by passing true
m_core_file (LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypeFilename, "Fullpath to a core file to use for this target."),
m_platform_path (LLDB_OPT_SET_1, false, "platform-path", 'P', 0, eArgTypePath, "Path to the remote file to use for this target."),
m_symbol_file (LLDB_OPT_SET_1, false, "symfile", 's', 0, eArgTypeFilename, "Fullpath to a stand alone debug symbols file for when debug symbols are not in the executable."),
@@ -180,7 +179,6 @@ public:
m_arguments.push_back (arg);
m_option_group.Append (&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
- m_option_group.Append (&m_platform_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append (&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append (&m_platform_path, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append (&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
@@ -282,70 +280,83 @@ protected:
bool must_set_platform_path = false;
Debugger &debugger = m_interpreter.GetDebugger();
- PlatformSP platform_sp(debugger.GetPlatformList().GetSelectedPlatform ());
- if (remote_file)
+ TargetSP target_sp;
+ const char *arch_cstr = m_arch_option.GetArchitectureName();
+ const bool get_dependent_files = m_add_dependents.GetOptionValue().GetCurrentValue();
+ Error error (debugger.GetTargetList().CreateTarget (debugger,
+ file_path,
+ arch_cstr,
+ get_dependent_files,
+ NULL,
+ target_sp));
+
+ if (target_sp)
{
- // I have a remote file.. two possible cases
- if (file_spec && file_spec.Exists())
+ // Only get the platform after we create the target because we might have
+ // switched platforms depending on what the arguments were to CreateTarget()
+ // we can't rely on the selected platform.
+
+ PlatformSP platform_sp = target_sp->GetPlatform();
+
+ if (remote_file)
{
- // if the remote file does not exist, push it there
- if (!platform_sp->GetFileExists (remote_file))
+ if (platform_sp)
{
- Error err = platform_sp->PutFile(file_spec, remote_file);
- if (err.Fail())
+ // I have a remote file.. two possible cases
+ if (file_spec && file_spec.Exists())
{
- result.AppendError(err.AsCString());
- result.SetStatus (eReturnStatusFailed);
- return false;
+ // if the remote file does not exist, push it there
+ if (!platform_sp->GetFileExists (remote_file))
+ {
+ Error err = platform_sp->PutFile(file_spec, remote_file);
+ if (err.Fail())
+ {
+ result.AppendError(err.AsCString());
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ }
}
- }
- }
- else
- {
- // there is no local file and we need one
- // in order to make the remote ---> local transfer we need a platform
- // TODO: if the user has passed in a --platform argument, use it to fetch the right platform
- if (!platform_sp)
- {
- result.AppendError("unable to perform remote debugging without a platform");
- result.SetStatus (eReturnStatusFailed);
- return false;
- }
- if (file_path)
- {
- // copy the remote file to the local file
- Error err = platform_sp->GetFile(remote_file, file_spec);
- if (err.Fail())
+ else
{
- result.AppendError(err.AsCString());
- result.SetStatus (eReturnStatusFailed);
- return false;
+ // there is no local file and we need one
+ // in order to make the remote ---> local transfer we need a platform
+ // TODO: if the user has passed in a --platform argument, use it to fetch the right platform
+ if (!platform_sp)
+ {
+ result.AppendError("unable to perform remote debugging without a platform");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ if (file_path)
+ {
+ // copy the remote file to the local file
+ Error err = platform_sp->GetFile(remote_file, file_spec);
+ if (err.Fail())
+ {
+ result.AppendError(err.AsCString());
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
+ }
+ else
+ {
+ // make up a local file
+ result.AppendError("remote --> local transfer without local path is not implemented yet");
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
}
}
else
{
- // make up a local file
- result.AppendError("remote --> local transfer without local path is not implemented yet");
+ result.AppendError("no platform found for target");
result.SetStatus (eReturnStatusFailed);
return false;
}
}
- }
-
- TargetSP target_sp;
- const char *arch_cstr = m_arch_option.GetArchitectureName();
- const bool get_dependent_files = m_add_dependents.GetOptionValue().GetCurrentValue();
- Error error (debugger.GetTargetList().CreateTarget (debugger,
-// remote_file ? remote_file : file_spec,
- file_path,
- arch_cstr,
- get_dependent_files,
- &m_platform_options,
- target_sp));
- if (target_sp)
- {
if (symfile || remote_file)
{
ModuleSP module_sp (target_sp->GetExecutableModule());
@@ -376,6 +387,12 @@ protected:
core_file.GetPath(core_path, sizeof(core_path));
if (core_file.Exists())
{
+ if (!core_file.Readable())
+ {
+ result.AppendMessageWithFormat ("Core file '%s' is not readable.\n", core_path);
+ result.SetStatus (eReturnStatusFailed);
+ return false;
+ }
FileSpec core_file_dir;
core_file_dir.GetDirectory() = core_file.GetDirectory();
target_sp->GetExecutableSearchPaths ().Append (core_file_dir);
@@ -426,7 +443,7 @@ protected:
}
else
{
- result.AppendErrorWithFormat("'%s' takes exactly one executable path argument, or use the --core-file option.\n", m_cmd_name.c_str());
+ result.AppendErrorWithFormat("'%s' takes exactly one executable path argument, or use the --core option.\n", m_cmd_name.c_str());
result.SetStatus (eReturnStatusFailed);
}
return result.Succeeded();
@@ -435,7 +452,6 @@ protected:
private:
OptionGroupOptions m_option_group;
OptionGroupArchitecture m_arch_option;
- OptionGroupPlatform m_platform_options;
OptionGroupFile m_core_file;
OptionGroupFile m_platform_path;
OptionGroupFile m_symbol_file;
@@ -522,7 +538,7 @@ protected:
{
bool success = false;
const char *target_idx_arg = args.GetArgumentAtIndex(0);
- uint32_t target_idx = Args::StringToUInt32 (target_idx_arg, UINT32_MAX, 0, &success);
+ uint32_t target_idx = StringConvert::ToUInt32 (target_idx_arg, UINT32_MAX, 0, &success);
if (success)
{
TargetList &target_list = m_interpreter.GetDebugger().GetTargetList();
@@ -589,10 +605,20 @@ public:
"Delete one or more targets by target index.",
NULL,
0),
- m_option_group (interpreter),
- m_cleanup_option (LLDB_OPT_SET_1, false, "clean", 'c', "Perform extra cleanup to minimize memory consumption after deleting the target.", false, false)
- {
- m_option_group.Append (&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group(interpreter),
+ m_all_option(LLDB_OPT_SET_1, false, "all", 'a', "Delete all targets.", false, true),
+ m_cleanup_option(
+ LLDB_OPT_SET_1,
+ false,
+ "clean", 'c',
+ "Perform extra cleanup to minimize memory consumption after deleting the target. "
+ "By default, LLDB will keep in memory any modules previously loaded by the target as well "
+ "as all of its debug info. Specifying --clean will unload all of these shared modules and "
+ "cause them to be reparsed again the next time the target is run",
+ false, true)
+ {
+ m_option_group.Append(&m_all_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+ m_option_group.Append(&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Finalize();
}
@@ -614,90 +640,89 @@ protected:
const size_t argc = args.GetArgumentCount();
std::vector<TargetSP> delete_target_list;
TargetList &target_list = m_interpreter.GetDebugger().GetTargetList();
- bool success = true;
TargetSP target_sp;
- if (argc > 0)
+
+ if (m_all_option.GetOptionValue())
+ {
+ for (int i = 0; i < target_list.GetNumTargets(); ++i)
+ delete_target_list.push_back(target_list.GetTargetAtIndex(i));
+ }
+ else if (argc > 0)
{
const uint32_t num_targets = target_list.GetNumTargets();
// Bail out if don't have any targets.
if (num_targets == 0) {
result.AppendError("no targets to delete");
result.SetStatus(eReturnStatusFailed);
- success = false;
+ return false;
}
- for (uint32_t arg_idx = 0; success && arg_idx < argc; ++arg_idx)
+ for (uint32_t arg_idx = 0; arg_idx < argc; ++arg_idx)
{
const char *target_idx_arg = args.GetArgumentAtIndex(arg_idx);
- uint32_t target_idx = Args::StringToUInt32 (target_idx_arg, UINT32_MAX, 0, &success);
- if (success)
+ bool success = false;
+ uint32_t target_idx = StringConvert::ToUInt32 (target_idx_arg, UINT32_MAX, 0, &success);
+ if (!success)
{
- if (target_idx < num_targets)
- {
- target_sp = target_list.GetTargetAtIndex (target_idx);
- if (target_sp)
- {
- delete_target_list.push_back (target_sp);
- continue;
- }
- }
- if (num_targets > 1)
- result.AppendErrorWithFormat ("target index %u is out of range, valid target indexes are 0 - %u\n",
- target_idx,
- num_targets - 1);
- else
- result.AppendErrorWithFormat("target index %u is out of range, the only valid index is 0\n",
- target_idx);
-
+ result.AppendErrorWithFormat("invalid target index '%s'\n", target_idx_arg);
result.SetStatus (eReturnStatusFailed);
- success = false;
+ return false;
}
- else
+ if (target_idx < num_targets)
{
- result.AppendErrorWithFormat("invalid target index '%s'\n", target_idx_arg);
- result.SetStatus (eReturnStatusFailed);
- success = false;
+ target_sp = target_list.GetTargetAtIndex (target_idx);
+ if (target_sp)
+ {
+ delete_target_list.push_back (target_sp);
+ continue;
+ }
}
+ if (num_targets > 1)
+ result.AppendErrorWithFormat ("target index %u is out of range, valid target indexes are 0 - %u\n",
+ target_idx,
+ num_targets - 1);
+ else
+ result.AppendErrorWithFormat("target index %u is out of range, the only valid index is 0\n",
+ target_idx);
+
+ result.SetStatus (eReturnStatusFailed);
+ return false;
}
}
else
{
target_sp = target_list.GetSelectedTarget();
- if (target_sp)
- {
- delete_target_list.push_back (target_sp);
- }
- else
+ if (!target_sp)
{
result.AppendErrorWithFormat("no target is currently selected\n");
result.SetStatus (eReturnStatusFailed);
- success = false;
+ return false;
}
+ delete_target_list.push_back (target_sp);
}
- if (success)
+
+ const size_t num_targets_to_delete = delete_target_list.size();
+ for (size_t idx = 0; idx < num_targets_to_delete; ++idx)
{
- const size_t num_targets_to_delete = delete_target_list.size();
- for (size_t idx = 0; idx < num_targets_to_delete; ++idx)
- {
- target_sp = delete_target_list[idx];
- target_list.DeleteTarget(target_sp);
- target_sp->Destroy();
- }
- // If "--clean" was specified, prune any orphaned shared modules from
- // the global shared module list
- if (m_cleanup_option.GetOptionValue ())
- {
- const bool mandatory = true;
- ModuleList::RemoveOrphanSharedModules(mandatory);
- }
- result.GetOutputStream().Printf("%u targets deleted.\n", (uint32_t)num_targets_to_delete);
- result.SetStatus(eReturnStatusSuccessFinishResult);
+ target_sp = delete_target_list[idx];
+ target_list.DeleteTarget(target_sp);
+ target_sp->Destroy();
+ }
+ // If "--clean" was specified, prune any orphaned shared modules from
+ // the global shared module list
+ if (m_cleanup_option.GetOptionValue ())
+ {
+ const bool mandatory = true;
+ ModuleList::RemoveOrphanSharedModules(mandatory);
}
+ result.GetOutputStream().Printf("%u targets deleted.\n", (uint32_t)num_targets_to_delete);
+ result.SetStatus(eReturnStatusSuccessFinishResult);
- return result.Succeeded();
+ return true;
}
OptionGroupOptions m_option_group;
+ OptionGroupBoolean m_all_option;
OptionGroupBoolean m_cleanup_option;
};
@@ -719,7 +744,7 @@ public:
"target variable",
"Read global variable(s) prior to, or while running your binary.",
NULL,
- eFlagRequiresTarget),
+ eCommandRequiresTarget),
m_option_group (interpreter),
m_option_variable (false), // Don't include frame options
m_option_format (eFormatDefault),
@@ -762,6 +787,10 @@ public:
{
DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions());
+ if (false == valobj_sp->GetTargetSP()->GetDisplayRuntimeSupportValues() &&
+ true == valobj_sp->IsRuntimeSupportValue())
+ return;
+
switch (var_sp->GetScope())
{
case eValueTypeVariableGlobal:
@@ -1260,7 +1289,7 @@ protected:
{
bool success = false;
- uint32_t insert_idx = Args::StringToUInt32(command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success);
+ uint32_t insert_idx = StringConvert::ToUInt32(command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success);
if (!success)
{
@@ -1711,17 +1740,16 @@ LookupSymbolInModule (CommandInterpreter &interpreter, Stream &strm, Module *mod
DumpFullpath (strm, &module->GetFileSpec(), 0);
strm.PutCString(":\n");
strm.IndentMore ();
- //Symtab::DumpSymbolHeader (&strm);
for (i=0; i < num_matches; ++i)
{
Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]);
- DumpAddress (interpreter.GetExecutionContext().GetBestExecutionContextScope(),
- symbol->GetAddress(),
- verbose,
- strm);
-
-// strm.Indent ();
-// symbol->Dump (&strm, interpreter.GetExecutionContext().GetTargetPtr(), i);
+ if (symbol && symbol->ValueIsAddress())
+ {
+ DumpAddress (interpreter.GetExecutionContext().GetBestExecutionContextScope(),
+ symbol->GetAddressRef(),
+ verbose,
+ strm);
+ }
}
strm.IndentLess ();
return num_matches;
@@ -2536,7 +2564,7 @@ public:
"target modules dump line-table",
"Dump the line table for one or more compilation units.",
NULL,
- eFlagRequiresTarget)
+ eCommandRequiresTarget)
{
}
@@ -2963,7 +2991,7 @@ protected:
{
ConstString const_sect_name(sect_name);
bool success = false;
- addr_t load_addr = Args::StringToUInt64(load_addr_cstr, LLDB_INVALID_ADDRESS, 0, &success);
+ addr_t load_addr = StringConvert::ToUInt64(load_addr_cstr, LLDB_INVALID_ADDRESS, 0, &success);
if (success)
{
SectionSP section_sp (section_list->FindSectionByName(const_sect_name));
@@ -3451,28 +3479,24 @@ protected:
case 's':
case 'S':
{
- SymbolVendor *symbol_vendor = module->GetSymbolVendor();
+ const SymbolVendor *symbol_vendor = module->GetSymbolVendor();
if (symbol_vendor)
{
- SymbolFile *symbol_file = symbol_vendor->GetSymbolFile();
- if (symbol_file)
+ const FileSpec symfile_spec = symbol_vendor->GetMainFileSpec();
+ if (format_char == 'S')
{
- if (format_char == 'S')
+ // Dump symbol file only if different from module file
+ if (!symfile_spec || symfile_spec == module->GetFileSpec())
{
- FileSpec &symfile_spec = symbol_file->GetObjectFile()->GetFileSpec();
- // Dump symbol file only if different from module file
- if (!symfile_spec || symfile_spec == module->GetFileSpec())
- {
- print_space = false;
- break;
- }
- // Add a newline and indent past the index
- strm.Printf ("\n%*s", indent, "");
+ print_space = false;
+ break;
}
- DumpFullpath (strm, &symbol_file->GetObjectFile()->GetFileSpec(), width);
- dump_object_name = true;
- break;
+ // Add a newline and indent past the index
+ strm.Printf ("\n%*s", indent, "");
}
+ DumpFullpath (strm, &symfile_spec, width);
+ dump_object_name = true;
+ break;
}
strm.Printf("%.*s", width, "<NONE>");
}
@@ -3630,10 +3654,10 @@ public:
"target modules show-unwind",
"Show synthesized unwind instructions for a function.",
NULL,
- eFlagRequiresTarget |
- eFlagRequiresProcess |
- eFlagProcessMustBeLaunched |
- eFlagProcessMustBePaused ),
+ eCommandRequiresTarget |
+ eCommandRequiresProcess |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused ),
m_options (interpreter)
{
}
@@ -3756,7 +3780,7 @@ protected:
{
result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan is '%s'\n", callsite_unwind_plan->GetSourceName().AsCString());
}
- UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*thread.get());
+ UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread.get());
if (fast_unwind_plan.get())
{
result.GetOutputStream().Printf("Fast UnwindPlan is '%s'\n", fast_unwind_plan->GetSourceName().AsCString());
@@ -3890,7 +3914,7 @@ public:
break;
case 'o':
- m_offset = Args::StringToUInt64(option_arg, LLDB_INVALID_ADDRESS);
+ m_offset = StringConvert::ToUInt64(option_arg, LLDB_INVALID_ADDRESS);
if (m_offset == LLDB_INVALID_ADDRESS)
error.SetErrorStringWithFormat ("invalid offset string '%s'", option_arg);
break;
@@ -3910,7 +3934,7 @@ public:
break;
case 'l':
- m_line_number = Args::StringToUInt32(option_arg, UINT32_MAX);
+ m_line_number = StringConvert::ToUInt32(option_arg, UINT32_MAX);
if (m_line_number == UINT32_MAX)
error.SetErrorStringWithFormat ("invalid line number string '%s'", option_arg);
else if (m_line_number == 0)
@@ -3990,7 +4014,7 @@ public:
"target modules lookup",
"Look up information within executable and dependent shared library images.",
NULL,
- eFlagRequiresTarget),
+ eCommandRequiresTarget),
m_options (interpreter)
{
CommandArgumentEntry arg;
@@ -4078,7 +4102,7 @@ public:
if (LookupAddressInModule (m_interpreter,
result.GetOutputStream(),
module,
- eSymbolContextEverything,
+ eSymbolContextEverything | (m_options.m_verbose ? eSymbolContextVariable : 0),
m_options.m_addr,
m_options.m_offset,
m_options.m_verbose))
@@ -4370,7 +4394,7 @@ public:
CommandObjectParsed (interpreter,
"target symbols add",
"Add a debug symbol file to one of the target's current modules by specifying a path to a debug symbols file, or using the options to specify a module to download symbols for.",
- "target symbols add [<symfile>]", eFlagRequiresTarget),
+ "target symbols add [<symfile>]", eCommandRequiresTarget),
m_option_group (interpreter),
m_file_option (LLDB_OPT_SET_1, false, "shlib", 's', CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Fullpath or basename for module to find debug symbols for."),
m_current_frame_option (LLDB_OPT_SET_2, false, "frame", 'F', "Locate the debug symbols the currently selected frame.", false, true)
@@ -4873,7 +4897,7 @@ public:
break;
case 'e':
- m_line_end = Args::StringToUInt32 (option_arg, UINT_MAX, 0, &success);
+ m_line_end = StringConvert::ToUInt32 (option_arg, UINT_MAX, 0, &success);
if (!success)
{
error.SetErrorStringWithFormat ("invalid end line number: \"%s\"", option_arg);
@@ -4883,7 +4907,7 @@ public:
break;
case 'l':
- m_line_start = Args::StringToUInt32 (option_arg, 0, 0, &success);
+ m_line_start = StringConvert::ToUInt32 (option_arg, 0, 0, &success);
if (!success)
{
error.SetErrorStringWithFormat ("invalid start line number: \"%s\"", option_arg);
@@ -4912,7 +4936,7 @@ public:
break;
case 't' :
{
- m_thread_id = Args::StringToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0);
+ m_thread_id = StringConvert::ToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0);
if (m_thread_id == LLDB_INVALID_THREAD_ID)
error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg);
m_thread_specified = true;
@@ -4928,7 +4952,7 @@ public:
break;
case 'x':
{
- m_thread_index = Args::StringToUInt32(option_arg, UINT32_MAX, 0);
+ m_thread_index = StringConvert::ToUInt32(option_arg, UINT32_MAX, 0);
if (m_thread_id == UINT32_MAX)
error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg);
m_thread_specified = true;
@@ -5233,7 +5257,7 @@ protected:
bool success;
for (size_t i = 0; i < num_args; i++)
{
- lldb::user_id_t user_id = Args::StringToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success);
+ lldb::user_id_t user_id = StringConvert::ToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success);
if (!success)
{
result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
@@ -5302,7 +5326,7 @@ protected:
{
for (size_t i = 0; i < num_args; i++)
{
- lldb::user_id_t user_id = Args::StringToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success);
+ lldb::user_id_t user_id = StringConvert::ToUInt32 (command.GetArgumentAtIndex(i), 0, 0, &success);
if (!success)
{
result.AppendErrorWithFormat ("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i));
OpenPOWER on IntegriCloud