Make "process attach" command to support attaching to remote process in case if selected platform allows this.
http://reviews.llvm.org/D7358 llvm-svn: 227899
This commit is contained in:
parent
346359daac
commit
926af0cdcb
|
@ -489,6 +489,8 @@ protected:
|
||||||
DoExecute (Args& command,
|
DoExecute (Args& command,
|
||||||
CommandReturnObject &result)
|
CommandReturnObject &result)
|
||||||
{
|
{
|
||||||
|
PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform());
|
||||||
|
|
||||||
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
|
||||||
// N.B. The attach should be synchronous. It doesn't help much to get the prompt back between initiating the attach
|
// N.B. The attach should be synchronous. It doesn't help much to get the prompt back between initiating the attach
|
||||||
// and the target actually stopping. So even if the interpreter is set to be asynchronous, we wait for the stop
|
// and the target actually stopping. So even if the interpreter is set to be asynchronous, we wait for the stop
|
||||||
|
@ -527,122 +529,130 @@ protected:
|
||||||
ModuleSP old_exec_module_sp = target->GetExecutableModule();
|
ModuleSP old_exec_module_sp = target->GetExecutableModule();
|
||||||
ArchSpec old_arch_spec = target->GetArchitecture();
|
ArchSpec old_arch_spec = target->GetArchitecture();
|
||||||
|
|
||||||
|
ProcessSP process_sp;
|
||||||
|
Error error;
|
||||||
if (command.GetArgumentCount())
|
if (command.GetArgumentCount())
|
||||||
{
|
{
|
||||||
result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str());
|
result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str());
|
||||||
result.SetStatus (eReturnStatusFailed);
|
result.SetStatus (eReturnStatusFailed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_interpreter.UpdateExecutionContext(nullptr);
|
||||||
|
ListenerSP listener_sp (new Listener("lldb.CommandObjectProcessAttach.DoExecute.attach.hijack"));
|
||||||
|
m_options.attach_info.SetHijackListener(listener_sp);
|
||||||
|
|
||||||
|
// If no process info was specified, then use the target executable
|
||||||
|
// name as the process to attach to by default
|
||||||
|
if (!m_options.attach_info.ProcessInfoSpecified ())
|
||||||
|
{
|
||||||
|
if (old_exec_module_sp)
|
||||||
|
m_options.attach_info.GetExecutableFile().GetFilename() = old_exec_module_sp->GetPlatformFileSpec().GetFilename();
|
||||||
|
|
||||||
|
if (!m_options.attach_info.ProcessInfoSpecified ())
|
||||||
|
{
|
||||||
|
error.SetErrorString ("no process specified, create a target with a file, or specify the --pid or --name command option");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.Success())
|
||||||
|
{
|
||||||
|
if (state != eStateConnected && platform_sp != nullptr && platform_sp->CanDebugProcess())
|
||||||
|
{
|
||||||
|
target->SetPlatform(platform_sp);
|
||||||
|
process = platform_sp->Attach(m_options.attach_info, m_interpreter.GetDebugger(), target, error).get();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (state != eStateConnected)
|
||||||
|
{
|
||||||
|
const char *plugin_name = m_options.attach_info.GetProcessPluginName();
|
||||||
|
process = target->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name, nullptr).get();
|
||||||
|
if (process == nullptr)
|
||||||
|
error.SetErrorStringWithFormat("failed to create process using plugin %s", plugin_name);
|
||||||
|
}
|
||||||
|
if (process)
|
||||||
|
{
|
||||||
|
process->HijackProcessEvents(listener_sp.get());
|
||||||
|
error = process->Attach(m_options.attach_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.Success() && process != nullptr)
|
||||||
|
{
|
||||||
|
result.SetStatus (eReturnStatusSuccessContinuingNoResult);
|
||||||
|
StreamString stream;
|
||||||
|
StateType state = process->WaitForProcessToStop (nullptr, nullptr, false, listener_sp.get(), &stream);
|
||||||
|
|
||||||
|
process->RestoreProcessEvents();
|
||||||
|
result.SetDidChangeProcessState (true);
|
||||||
|
|
||||||
|
if (stream.GetData())
|
||||||
|
result.AppendMessage(stream.GetData());
|
||||||
|
|
||||||
|
if (state == eStateStopped)
|
||||||
|
{
|
||||||
|
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *exit_desc = process->GetExitDescription();
|
||||||
|
if (exit_desc)
|
||||||
|
result.AppendErrorWithFormat ("attach failed: %s", exit_desc);
|
||||||
|
else
|
||||||
|
result.AppendError ("attach failed: process did not stop (no such process or permission problem?)");
|
||||||
|
process->Destroy();
|
||||||
|
result.SetStatus (eReturnStatusFailed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (state != eStateConnected)
|
result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString());
|
||||||
{
|
result.SetStatus (eReturnStatusFailed);
|
||||||
const char *plugin_name = m_options.attach_info.GetProcessPluginName();
|
|
||||||
process = target->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name, NULL).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process)
|
|
||||||
{
|
|
||||||
Error error;
|
|
||||||
// If no process info was specified, then use the target executable
|
|
||||||
// name as the process to attach to by default
|
|
||||||
if (!m_options.attach_info.ProcessInfoSpecified ())
|
|
||||||
{
|
|
||||||
if (old_exec_module_sp)
|
|
||||||
m_options.attach_info.GetExecutableFile().GetFilename() = old_exec_module_sp->GetPlatformFileSpec().GetFilename();
|
|
||||||
|
|
||||||
if (!m_options.attach_info.ProcessInfoSpecified ())
|
|
||||||
{
|
|
||||||
error.SetErrorString ("no process specified, create a target with a file, or specify the --pid or --name command option");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error.Success())
|
|
||||||
{
|
|
||||||
// Update the execution context so the current target and process are now selected
|
|
||||||
// in case we interrupt
|
|
||||||
m_interpreter.UpdateExecutionContext(NULL);
|
|
||||||
ListenerSP listener_sp (new Listener("lldb.CommandObjectProcessAttach.DoExecute.attach.hijack"));
|
|
||||||
m_options.attach_info.SetHijackListener(listener_sp);
|
|
||||||
process->HijackProcessEvents(listener_sp.get());
|
|
||||||
error = process->Attach (m_options.attach_info);
|
|
||||||
|
|
||||||
if (error.Success())
|
|
||||||
{
|
|
||||||
result.SetStatus (eReturnStatusSuccessContinuingNoResult);
|
|
||||||
StreamString stream;
|
|
||||||
StateType state = process->WaitForProcessToStop (NULL, NULL, false, listener_sp.get(), &stream);
|
|
||||||
|
|
||||||
process->RestoreProcessEvents();
|
|
||||||
|
|
||||||
result.SetDidChangeProcessState (true);
|
|
||||||
|
|
||||||
if (stream.GetData())
|
|
||||||
result.AppendMessage(stream.GetData());
|
|
||||||
|
|
||||||
if (state == eStateStopped)
|
|
||||||
{
|
|
||||||
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const char *exit_desc = process->GetExitDescription();
|
|
||||||
if (exit_desc)
|
|
||||||
result.AppendErrorWithFormat ("attach failed: %s", exit_desc);
|
|
||||||
else
|
|
||||||
result.AppendError ("attach failed: process did not stop (no such process or permission problem?)");
|
|
||||||
process->Destroy();
|
|
||||||
result.SetStatus (eReturnStatusFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString());
|
|
||||||
result.SetStatus (eReturnStatusFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.Succeeded())
|
if (!result.Succeeded())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Okay, we're done. Last step is to warn if the executable module has changed:
|
||||||
|
char new_path[PATH_MAX];
|
||||||
|
ModuleSP new_exec_module_sp (target->GetExecutableModule());
|
||||||
|
if (!old_exec_module_sp)
|
||||||
{
|
{
|
||||||
// Okay, we're done. Last step is to warn if the executable module has changed:
|
// We might not have a module if we attached to a raw pid...
|
||||||
char new_path[PATH_MAX];
|
if (new_exec_module_sp)
|
||||||
ModuleSP new_exec_module_sp (target->GetExecutableModule());
|
|
||||||
if (!old_exec_module_sp)
|
|
||||||
{
|
{
|
||||||
// We might not have a module if we attached to a raw pid...
|
new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX);
|
||||||
if (new_exec_module_sp)
|
result.AppendMessageWithFormat("Executable module set to \"%s\".\n", new_path);
|
||||||
{
|
|
||||||
new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX);
|
|
||||||
result.AppendMessageWithFormat("Executable module set to \"%s\".\n", new_path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (old_exec_module_sp->GetFileSpec() != new_exec_module_sp->GetFileSpec())
|
|
||||||
{
|
|
||||||
char old_path[PATH_MAX];
|
|
||||||
|
|
||||||
old_exec_module_sp->GetFileSpec().GetPath (old_path, PATH_MAX);
|
|
||||||
new_exec_module_sp->GetFileSpec().GetPath (new_path, PATH_MAX);
|
|
||||||
|
|
||||||
result.AppendWarningWithFormat("Executable module changed from \"%s\" to \"%s\".\n",
|
|
||||||
old_path, new_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!old_arch_spec.IsValid())
|
|
||||||
{
|
|
||||||
result.AppendMessageWithFormat ("Architecture set to: %s.\n", target->GetArchitecture().GetTriple().getTriple().c_str());
|
|
||||||
}
|
|
||||||
else if (!old_arch_spec.IsExactMatch(target->GetArchitecture()))
|
|
||||||
{
|
|
||||||
result.AppendWarningWithFormat("Architecture changed from %s to %s.\n",
|
|
||||||
old_arch_spec.GetTriple().getTriple().c_str(),
|
|
||||||
target->GetArchitecture().GetTriple().getTriple().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// This supports the use-case scenario of immediately continuing the process once attached.
|
|
||||||
if (m_options.attach_info.GetContinueOnceAttached())
|
|
||||||
m_interpreter.HandleCommand("process continue", eLazyBoolNo, result);
|
|
||||||
}
|
}
|
||||||
|
else if (old_exec_module_sp->GetFileSpec() != new_exec_module_sp->GetFileSpec())
|
||||||
|
{
|
||||||
|
char old_path[PATH_MAX];
|
||||||
|
|
||||||
|
old_exec_module_sp->GetFileSpec().GetPath (old_path, PATH_MAX);
|
||||||
|
new_exec_module_sp->GetFileSpec().GetPath (new_path, PATH_MAX);
|
||||||
|
|
||||||
|
result.AppendWarningWithFormat("Executable module changed from \"%s\" to \"%s\".\n",
|
||||||
|
old_path, new_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!old_arch_spec.IsValid())
|
||||||
|
{
|
||||||
|
result.AppendMessageWithFormat ("Architecture set to: %s.\n", target->GetArchitecture().GetTriple().getTriple().c_str());
|
||||||
|
}
|
||||||
|
else if (!old_arch_spec.IsExactMatch(target->GetArchitecture()))
|
||||||
|
{
|
||||||
|
result.AppendWarningWithFormat("Architecture changed from %s to %s.\n",
|
||||||
|
old_arch_spec.GetTriple().getTriple().c_str(),
|
||||||
|
target->GetArchitecture().GetTriple().getTriple().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This supports the use-case scenario of immediately continuing the process once attached.
|
||||||
|
if (m_options.attach_info.GetContinueOnceAttached())
|
||||||
|
m_interpreter.HandleCommand("process continue", eLazyBoolNo, result);
|
||||||
|
|
||||||
return result.Succeeded();
|
return result.Succeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -542,6 +542,9 @@ DynamicLoaderPOSIXDYLD::ComputeLoadOffset()
|
||||||
return LLDB_INVALID_ADDRESS;
|
return LLDB_INVALID_ADDRESS;
|
||||||
|
|
||||||
ObjectFile *exe = module->GetObjectFile();
|
ObjectFile *exe = module->GetObjectFile();
|
||||||
|
if (!exe)
|
||||||
|
return LLDB_INVALID_ADDRESS;
|
||||||
|
|
||||||
Address file_entry = exe->GetEntryPointAddress();
|
Address file_entry = exe->GetEntryPointAddress();
|
||||||
|
|
||||||
if (!file_entry.IsValid())
|
if (!file_entry.IsValid())
|
||||||
|
|
|
@ -765,8 +765,12 @@ PlatformPOSIX::Attach (ProcessAttachInfo &attach_info,
|
||||||
// Set UnixSignals appropriately.
|
// Set UnixSignals appropriately.
|
||||||
process_sp->SetUnixSignals (Host::GetUnixSignals ());
|
process_sp->SetUnixSignals (Host::GetUnixSignals ());
|
||||||
|
|
||||||
ListenerSP listener_sp (new Listener("lldb.PlatformPOSIX.attach.hijack"));
|
auto listener_sp = attach_info.GetHijackListener();
|
||||||
attach_info.SetHijackListener(listener_sp);
|
if (listener_sp == nullptr)
|
||||||
|
{
|
||||||
|
listener_sp.reset(new Listener("lldb.PlatformPOSIX.attach.hijack"));
|
||||||
|
attach_info.SetHijackListener(listener_sp);
|
||||||
|
}
|
||||||
process_sp->HijackProcessEvents(listener_sp.get());
|
process_sp->HijackProcessEvents(listener_sp.get());
|
||||||
error = process_sp->Attach (attach_info);
|
error = process_sp->Attach (attach_info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -665,10 +665,16 @@ PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info,
|
||||||
override_hostname ? override_hostname : m_platform_hostname.c_str(),
|
override_hostname ? override_hostname : m_platform_hostname.c_str(),
|
||||||
port + port_offset);
|
port + port_offset);
|
||||||
assert (connect_url_len < (int)sizeof(connect_url));
|
assert (connect_url_len < (int)sizeof(connect_url));
|
||||||
error = process_sp->ConnectRemote (NULL, connect_url);
|
error = process_sp->ConnectRemote(nullptr, connect_url);
|
||||||
if (error.Success())
|
if (error.Success())
|
||||||
|
{
|
||||||
|
auto listener = attach_info.GetHijackListener();
|
||||||
|
if (listener != nullptr)
|
||||||
|
process_sp->HijackProcessEvents(listener.get());
|
||||||
error = process_sp->Attach(attach_info);
|
error = process_sp->Attach(attach_info);
|
||||||
else if (debugserver_pid != LLDB_INVALID_PROCESS_ID)
|
}
|
||||||
|
|
||||||
|
if (error.Fail() && debugserver_pid != LLDB_INVALID_PROCESS_ID)
|
||||||
{
|
{
|
||||||
m_gdb_client.KillSpawnedProcess(debugserver_pid);
|
m_gdb_client.KillSpawnedProcess(debugserver_pid);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue