Added a new class to Process.h: ProcessAttachInfo. This class contains enough

info for us to attach by pid, or by name and will also allow us to eventually
do a lot more powerful attaches. If you look at the options for the "platform
process list" command, there are many options which we should be able to
specify. This will allow us to do things like "attach to a process named 'tcsh'
that has a parent process ID of 123", or "attach to a process named 'x' which
has an effective user ID of 345". 

I finished up the --shell implementation so that it can be used without the
--tty option in "process launch". The "--shell" option now can take an 
optional argument which is the path to the shell to use (or a partial name
like "sh" which we will find using the current PATH environment variable).

Modified the Process::Attach to use the new ProcessAttachInfo as the sole
argument and centralized a lot of code that was in the "process attach"
Execute function so that everyone can take advantage of the powerful new
attach functionality.

llvm-svn: 144615
This commit is contained in:
Greg Clayton 2011-11-15 03:53:30 +00:00
parent 9c0de9bb6b
commit 144f3a9c90
20 changed files with 561 additions and 372 deletions

View File

@ -401,6 +401,9 @@ public:
static const char *
StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t &update);
static const char *
GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg);
// This one isn't really relevant to Arguments per se, but we're using the Args as a
// general strings container, so...
void

View File

@ -141,6 +141,10 @@ public:
void
AppendErrorWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3)));
void
SetError (const Error &error,
const char *fallback_error_cstr);
lldb::ReturnStatus
GetStatus();

View File

@ -305,7 +305,7 @@ namespace lldb_private {
/// appriopriate error fill into the \a error object.
//------------------------------------------------------------------
virtual lldb::ProcessSP
Attach (lldb::pid_t pid,
Attach (ProcessAttachInfo &attach_info,
Debugger &debugger,
Target *target, // Can be NULL, if NULL create a new target, else use existing one
Listener &listener,

View File

@ -148,12 +148,6 @@ public:
return m_executable.GetFilename().GetLength();
}
void
SetName (const char *name)
{
m_executable.GetFilename().SetCString (name);
}
FileSpec &
GetExecutableFile ()
{
@ -591,7 +585,7 @@ public:
}
void
FinalizeFileActions (Target *target, Process *process);
FinalizeFileActions (Target *target);
size_t
GetNumFileActions () const
@ -671,21 +665,158 @@ public:
m_plugin_name.clear();
}
const char *
GetShell () const
{
if (m_shell.empty())
return NULL;
return m_shell.c_str();
}
void
SetShell (const char * path)
{
if (path && path[0])
{
m_shell.assign (path);
m_flags.Set (lldb::eLaunchFlagLaunchInShell);
}
else
{
m_shell.clear();
m_flags.Clear (lldb::eLaunchFlagLaunchInShell);
}
}
uint32_t
GetResumeCount () const
{
return m_resume_count;
}
void
SetResumeCount (uint32_t c)
{
m_resume_count = c;
}
void
Clear ()
{
ProcessInfo::Clear();
m_working_dir.clear();
m_plugin_name.clear();
m_shell.clear();
m_flags.Clear();
m_file_actions.clear();
m_resume_count = 0;
}
bool
ConvertArgumentsForLaunchingInShell (Error &error, bool localhost);
protected:
std::string m_working_dir;
std::string m_plugin_name;
std::string m_shell;
Flags m_flags; // Bitwise OR of bits from lldb::LaunchFlags
std::vector<FileAction> m_file_actions; // File actions for any other files
uint32_t m_resume_count; // How many times do we resume after launching
};
//----------------------------------------------------------------------
// ProcessLaunchInfo
//
// Describes any information that is required to launch a process.
//----------------------------------------------------------------------
class ProcessAttachInfo : public ProcessInstanceInfo
{
public:
ProcessAttachInfo() :
ProcessInstanceInfo(),
m_plugin_name (),
m_resume_count (0),
m_wait_for_launch (false)
{
}
ProcessAttachInfo (const ProcessLaunchInfo &launch_info) :
ProcessInstanceInfo(),
m_plugin_name (),
m_resume_count (0),
m_wait_for_launch (false)
{
ProcessInfo::operator= (launch_info);
SetProcessPluginName (launch_info.GetProcessPluginName());
SetResumeCount (launch_info.GetResumeCount());
}
bool
GetWaitForLaunch () const
{
return m_wait_for_launch;
}
void
SetWaitForLaunch (bool b)
{
m_wait_for_launch = b;
}
uint32_t
GetResumeCount () const
{
return m_resume_count;
}
void
SetResumeCount (uint32_t c)
{
m_resume_count = c;
}
const char *
GetProcessPluginName () const
{
if (m_plugin_name.empty())
return NULL;
return m_plugin_name.c_str();
}
void
SetProcessPluginName (const char *plugin)
{
if (plugin && plugin[0])
m_plugin_name.assign (plugin);
else
m_plugin_name.clear();
}
void
Clear ()
{
ProcessInstanceInfo::Clear();
m_plugin_name.clear();
m_resume_count = 0;
m_wait_for_launch = false;
}
bool
ProcessInfoSpecified () const
{
if (GetExecutableFile())
return true;
if (GetProcessID() != LLDB_INVALID_PROCESS_ID)
return true;
if (GetParentProcessID() != LLDB_INVALID_PROCESS_ID)
return true;
return false;
}
protected:
std::string m_plugin_name;
uint32_t m_resume_count; // How many times do we resume after launching
bool m_wait_for_launch;
};
class ProcessLaunchCommandOptions : public Options
@ -744,12 +875,12 @@ public:
}
ProcessInstanceInfoMatch (const char *process_name,
lldb_private::NameMatchType process_name_match_type) :
lldb_private::NameMatchType process_name_match_type) :
m_match_info (),
m_name_match_type (process_name_match_type),
m_match_all_users (false)
{
m_match_info.SetName (process_name);
m_match_info.GetExecutableFile().SetFile(process_name, false);
}
ProcessInstanceInfo &
@ -1316,11 +1447,12 @@ public:
Launch (const ProcessLaunchInfo &launch_info);
//------------------------------------------------------------------
/// Attach to an existing process using a process ID.
/// Attach to an existing process using the process attach info.
///
/// This function is not meant to be overridden by Process
/// subclasses. It will first call Process::WillAttach (lldb::pid_t)
/// and if that returns \b true, Process::DoAttach (lldb::pid_t) will
/// subclasses. It will first call WillAttach (lldb::pid_t)
/// or WillAttach (const char *), and if that returns \b
/// true, DoAttach (lldb::pid_t) or DoAttach (const char *) will
/// be called to actually do the attach. If DoAttach returns \b
/// true, then Process::DidAttach() will be called.
///
@ -1332,28 +1464,8 @@ public:
/// LLDB_INVALID_PROCESS_ID if attaching fails.
//------------------------------------------------------------------
virtual Error
Attach (lldb::pid_t pid, uint32_t exec_count);
Attach (ProcessAttachInfo &attach_info);
//------------------------------------------------------------------
/// Attach to an existing process by process name.
///
/// This function is not meant to be overridden by Process
/// subclasses. It will first call
/// Process::WillAttach (const char *) and if that returns \b
/// true, Process::DoAttach (const char *) will be called to
/// actually do the attach. If DoAttach returns \b true, then
/// Process::DidAttach() will be called.
///
/// @param[in] process_name
/// A process name to match against the current process list.
///
/// @return
/// Returns \a pid if attaching was successful, or
/// LLDB_INVALID_PROCESS_ID if attaching fails.
//------------------------------------------------------------------
virtual Error
Attach (const char *process_name, bool wait_for_launch);
virtual Error
ConnectRemote (const char *remote_url);

View File

@ -112,6 +112,7 @@ class NamedOption;
class PathMappingList;
class Platform;
class Process;
class ProcessAttachInfo;
class ProcessModID;
class ProcessInfo;
class ProcessInstanceInfo;
@ -128,7 +129,7 @@ class ScriptInterpreter;
#ifndef LLDB_DISABLE_PYTHON
class ScriptInterpreterPython;
struct ScriptSummaryFormat;
#endif // #ifndef LLDB_DISABLE_PYTHON
#endif
class SearchFilter;
class Section;
class SectionImpl;
@ -161,7 +162,7 @@ class SyntheticChildren;
class SyntheticChildrenFrontEnd;
#ifndef LLDB_DISABLE_PYTHON
class SyntheticScriptProvider;
#endif // #ifndef LLDB_DISABLE_PYTHON
#endif
class Target;
class TargetList;
class Thread;

View File

@ -169,7 +169,9 @@ SBProcess::RemoteAttachToProcessWithID (lldb::pid_t pid, lldb::SBError& error)
Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
if (m_opaque_sp->GetState() == eStateConnected)
{
error.SetError (m_opaque_sp->Attach (pid, 0));
ProcessAttachInfo attach_info;
attach_info.SetProcessID (pid);
error.SetError (m_opaque_sp->Attach (attach_info));
}
else
{

View File

@ -337,7 +337,9 @@ SBTarget::AttachToProcessWithID
if (sb_process.IsValid())
{
error.SetError (sb_process->Attach (pid, 0));
ProcessAttachInfo attach_info;
attach_info.SetProcessID (pid);
error.SetError (sb_process->Attach (attach_info));
// If we are doing synchronous mode, then wait for the
// process to stop!
if (m_opaque_sp->GetDebugger().GetAsyncExecution () == false)
@ -409,7 +411,10 @@ SBTarget::AttachToProcessWithName
if (sb_process.IsValid())
{
error.SetError (sb_process->Attach (name, wait_for));
ProcessAttachInfo attach_info;
attach_info.GetExecutableFile().SetFile(name, false);
attach_info.SetWaitForLaunch(wait_for);
error.SetError (sb_process->Attach (attach_info));
// If we are doing synchronous mode, then wait for the
// process to stop!
if (m_opaque_sp->GetDebugger().GetAsyncExecution () == false)

View File

@ -653,27 +653,27 @@ protected:
break;
case 'n':
match_info.GetProcessInfo().SetName (option_arg);
match_info.GetProcessInfo().GetExecutableFile().SetFile (option_arg, false);
match_info.SetNameMatchType (eNameMatchEquals);
break;
case 'e':
match_info.GetProcessInfo().SetName (option_arg);
match_info.GetProcessInfo().GetExecutableFile().SetFile (option_arg, false);
match_info.SetNameMatchType (eNameMatchEndsWith);
break;
case 's':
match_info.GetProcessInfo().SetName (option_arg);
match_info.GetProcessInfo().GetExecutableFile().SetFile (option_arg, false);
match_info.SetNameMatchType (eNameMatchStartsWith);
break;
case 'c':
match_info.GetProcessInfo().SetName (option_arg);
match_info.GetProcessInfo().GetExecutableFile().SetFile (option_arg, false);
match_info.SetNameMatchType (eNameMatchContains);
break;
case 'r':
match_info.GetProcessInfo().SetName (option_arg);
match_info.GetProcessInfo().GetExecutableFile().SetFile (option_arg, false);
match_info.SetNameMatchType (eNameMatchRegularExpression);
break;

View File

@ -212,12 +212,32 @@ public:
}
}
if (launch_args.GetArgumentCount() > 0)
if (launch_args.GetArgumentCount() == 0)
{
const Args &process_args = target->GetRunArguments();
if (process_args.GetArgumentCount() > 0)
m_options.launch_info.GetArguments().AppendArguments (process_args);
}
else
{
m_options.launch_info.GetArguments().AppendArguments (launch_args);
}
if (target->GetDisableASLR())
m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR);
if (target->GetDisableSTDIO())
m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO);
m_options.launch_info.GetFlags().Set (eLaunchFlagDebug);
Args environment;
target->GetEnvironmentAsArgs (environment);
if (environment.GetArgumentCount() > 0)
m_options.launch_info.GetEnvironmentEntries ().AppendArguments (environment);
m_options.launch_info.FinalizeFileActions (target);
if (state == eStateConnected)
{
if (m_options.launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY))
@ -228,78 +248,18 @@ public:
}
else
{
const char *plugin_name = m_options.launch_info.GetProcessPluginName();
if (m_options.launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY))
{
if (!m_options.launch_info.GetArchitecture().IsValid())
m_options.launch_info.GetArchitecture() = target->GetArchitecture();
process = target->GetPlatform()->DebugProcess (m_options.launch_info,
debugger,
target,
debugger.GetListener(),
error).get();
}
else
{
process = target->CreateProcess (debugger.GetListener(), plugin_name).get();
if (launch_args.GetArgumentCount() == 0)
{
const Args &process_args = target->GetRunArguments();
if (process_args.GetArgumentCount() > 0)
m_options.launch_info.GetArguments().AppendArguments (process_args);
}
process = target->GetPlatform()->DebugProcess (m_options.launch_info,
debugger,
target,
debugger.GetListener(),
error).get();
Args environment;
target->GetEnvironmentAsArgs (environment);
m_options.launch_info.GetEnvironmentEntries ().AppendArguments (environment);
if (target->GetDisableASLR())
m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR);
if (m_options.launch_info.GetNumFileActions() == 0)
{
// Only use the settings value if the user hasn't specified any options that would override it.
if (target->GetDisableSTDIO())
m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO);
const char *path;
path = target->GetStandardErrorPath();
if (path)
{
ProcessLaunchInfo::FileAction file_action;
const bool read = true;
const bool write = true;
if (file_action.Open(STDERR_FILENO, path, read, write))
m_options.launch_info.AppendFileAction (file_action);
}
path = target->GetStandardInputPath();
if (path)
{
ProcessLaunchInfo::FileAction file_action;
const bool read = true;
const bool write = false;
if (file_action.Open(STDIN_FILENO, path, read, write))
m_options.launch_info.AppendFileAction (file_action);
}
path = target->GetStandardOutputPath();
if (path)
{
ProcessLaunchInfo::FileAction file_action;
const bool read = false;
const bool write = true;
if (file_action.Open(STDOUT_FILENO, path, read, write))
m_options.launch_info.AppendFileAction (file_action);
}
}
error = process->Launch (m_options.launch_info);
}
if (process == NULL)
{
result.AppendErrorWithFormat ("Failed to find a process plugin for executable.\n");
result.SetStatus (eReturnStatusFailed);
result.SetError (error, "failed to launch or debug process");
return false;
}
}
@ -326,7 +286,7 @@ public:
state = process->WaitForProcessToStop (NULL);
if (!StateIsStoppedState(state))
{
result.AppendErrorWithFormat ("Process isn't stopped: %s", StateAsCString(state));
result.AppendErrorWithFormat ("process isn't stopped: %s", StateAsCString(state));
}
result.SetDidChangeProcessState (true);
result.SetStatus (eReturnStatusSuccessFinishResult);
@ -338,13 +298,13 @@ public:
}
else
{
result.AppendErrorWithFormat ("Process resume at entry point failed: %s", error.AsCString());
result.AppendErrorWithFormat ("process resume at entry point failed: %s", error.AsCString());
result.SetStatus (eReturnStatusFailed);
}
}
else
{
result.AppendErrorWithFormat ("Initial process state wasn't stopped: %s", StateAsCString(state));
result.AppendErrorWithFormat ("initial process state wasn't stopped: %s", StateAsCString(state));
result.SetStatus (eReturnStatusFailed);
}
}
@ -423,23 +383,29 @@ public:
switch (short_option)
{
case 'p':
pid = Args::StringToUInt32 (option_arg, LLDB_INVALID_PROCESS_ID, 0, &success);
if (!success || pid == LLDB_INVALID_PROCESS_ID)
{
error.SetErrorStringWithFormat("invalid process ID '%s'", option_arg);
lldb::pid_t pid = Args::StringToUInt32 (option_arg, LLDB_INVALID_PROCESS_ID, 0, &success);
if (!success || pid == LLDB_INVALID_PROCESS_ID)
{
error.SetErrorStringWithFormat("invalid process ID '%s'", option_arg);
}
else
{
attach_info.SetProcessID (pid);
}
}
break;
case 'P':
plugin_name = option_arg;
attach_info.SetProcessPluginName (option_arg);
break;
case 'n':
name.assign(option_arg);
attach_info.GetExecutableFile().SetFile(option_arg, false);
break;
case 'w':
waitfor = true;
attach_info.SetWaitForLaunch(true);
break;
default:
@ -452,9 +418,7 @@ public:
void
OptionParsingStarting ()
{
pid = LLDB_INVALID_PROCESS_ID;
name.clear();
waitfor = false;
attach_info.Clear();
}
const OptionDefinition*
@ -497,7 +461,7 @@ public:
ProcessInstanceInfoMatch match_info;
if (partial_name)
{
match_info.GetProcessInfo().SetName(partial_name);
match_info.GetProcessInfo().GetExecutableFile().SetFile(partial_name, false);
match_info.SetNameMatchType(eNameMatchStartsWith);
}
platform_sp->FindProcesses (match_info, process_infos);
@ -522,10 +486,7 @@ public:
// Instance variables to hold the values for command options.
lldb::pid_t pid;
std::string plugin_name;
std::string name;
bool waitfor;
ProcessAttachInfo attach_info;
};
CommandObjectProcessAttach (CommandInterpreter &interpreter) :
@ -601,55 +562,37 @@ public:
{
if (state != eStateConnected)
{
const char *plugin_name = NULL;
if (!m_options.plugin_name.empty())
plugin_name = m_options.plugin_name.c_str();
const char *plugin_name = m_options.attach_info.GetProcessPluginName();
process = target->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name).get();
}
if (process)
{
Error error;
int attach_pid = m_options.pid;
const char *wait_name = NULL;
if (m_options.name.empty())
// 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)
{
wait_name = old_exec_module_sp->GetFileSpec().GetFilename().AsCString();
}
}
else
{
wait_name = m_options.name.c_str();
}
// If we are waiting for a process with this name to show up, do that first.
if (m_options.waitfor)
{
if (wait_name == NULL)
{
result.AppendError("Invalid arguments: must have a file loaded or supply a process name with the waitfor option.\n");
result.SetStatus (eReturnStatusFailed);
return false;
}
m_options.attach_info.GetExecutableFile().GetFilename() = old_exec_module_sp->GetFileSpec().GetFilename();
result.AppendMessageWithFormat("Waiting to attach to a process named \"%s\".\n", wait_name);
error = process->Attach (wait_name, m_options.waitfor);
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())
{
error = process->Attach (m_options.attach_info);
if (error.Success())
{
result.SetStatus (eReturnStatusSuccessContinuingNoResult);
}
else
{
result.AppendErrorWithFormat ("Waiting for a process to launch named '%s': %s\n",
wait_name,
error.AsCString());
result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString());
result.SetStatus (eReturnStatusFailed);
return false;
}
@ -658,70 +601,11 @@ public:
// FIXME: in the async case it will now be possible to get to the command
// interpreter with a state eStateAttaching. Make sure we handle that correctly.
StateType state = process->WaitForProcessToStop (NULL);
result.SetDidChangeProcessState (true);
result.AppendMessageWithFormat ("Process %llu %s\n", process->GetID(), StateAsCString (state));
result.SetStatus (eReturnStatusSuccessFinishNoResult);
}
else
{
// If the process was specified by name look it up, so we can warn if there are multiple
// processes with this pid.
if (attach_pid == LLDB_INVALID_PROCESS_ID && wait_name != NULL)
{
ProcessInstanceInfoList process_infos;
PlatformSP platform_sp (m_interpreter.GetPlatform (true));
if (platform_sp)
{
ProcessInstanceInfoMatch match_info (wait_name, eNameMatchEquals);
platform_sp->FindProcesses (match_info, process_infos);
}
if (process_infos.GetSize() > 1)
{
result.AppendErrorWithFormat("More than one process named %s\n", wait_name);
result.SetStatus (eReturnStatusFailed);
return false;
}
else if (process_infos.GetSize() == 0)
{
result.AppendErrorWithFormat("Could not find a process named %s\n", wait_name);
result.SetStatus (eReturnStatusFailed);
return false;
}
else
{
attach_pid = process_infos.GetProcessIDAtIndex (0);
}
}
if (attach_pid != LLDB_INVALID_PROCESS_ID)
{
error = process->Attach (attach_pid, 0);
if (error.Success())
{
result.SetStatus (eReturnStatusSuccessContinuingNoResult);
}
else
{
result.AppendErrorWithFormat ("Attaching to process %i failed: %s.\n",
attach_pid,
error.AsCString());
result.SetStatus (eReturnStatusFailed);
}
StateType state = process->WaitForProcessToStop (NULL);
result.SetDidChangeProcessState (true);
result.AppendMessageWithFormat ("Process %llu %s\n", process->GetID(), StateAsCString (state));
result.SetStatus (eReturnStatusSuccessFinishNoResult);
}
else
{
result.AppendErrorWithFormat ("No PID specified for attach\n");
result.SetStatus (eReturnStatusFailed);
}
}
}
}

View File

@ -59,6 +59,12 @@
#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
#endif
extern "C"
{
int __pthread_chdir(const char *path);
int __pthread_fchdir (int fildes);
}
using namespace lldb;
using namespace lldb_private;
@ -431,25 +437,6 @@ tell application \"Terminal\"\n\
do script the_shell_script\n\
end tell\n";
static const char *
GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg)
{
safe_arg.assign (unsafe_arg);
size_t prev_pos = 0;
while (prev_pos < safe_arg.size())
{
// Escape spaces and quotes
size_t pos = safe_arg.find_first_of(" '\"", prev_pos);
if (pos != std::string::npos)
{
safe_arg.insert (pos, 1, '\\');
prev_pos = pos + 2;
}
else
break;
}
return safe_arg.c_str();
}
static Error
LaunchInNewTerminalWithAppleScript (const char *exe_path, ProcessLaunchInfo &launch_info)
@ -499,44 +486,22 @@ LaunchInNewTerminalWithAppleScript (const char *exe_path, ProcessLaunchInfo &lau
if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR))
command.PutCString(" --disable-aslr");
if (launch_info.GetFlags().Test (eLaunchFlagLaunchInShell))
command.PutCString(" -- ");
const char **argv = launch_info.GetArguments().GetConstArgumentVector ();
if (argv)
{
const char *shell_executable = "/bin/bash"; //getenv("SHELL");
std::string safe_arg;
if (launch_info.GetArchitecture().IsValid())
command.Printf(" -- %s -c 'exec /usr/bin/arch -arch %s", shell_executable, launch_info.GetArchitecture().GetArchitectureName());
else
command.Printf(" -- %s -c 'exec ", shell_executable);
const char **argv = launch_info.GetArguments().GetConstArgumentVector ();
if (argv)
for (size_t i=0; argv[i] != NULL; ++i)
{
for (size_t i=0; argv[i] != NULL; ++i)
{
const char *arg = GetShellSafeArgument (i == 0 ? exe_path : argv[i], safe_arg);
command.Printf(" %s", arg);
}
if (i==0)
command.Printf(" '%s'", exe_path);
else
command.Printf(" '%s'", argv[i]);
}
command.PutChar('\'');
}
else
{
command.PutCString(" -- ");
const char **argv = launch_info.GetArguments().GetConstArgumentVector ();
if (argv)
{
for (size_t i=0; argv[i] != NULL; ++i)
{
if (i==0)
command.Printf(" '%s'", exe_path);
else
command.Printf(" '%s'", argv[i]);
}
}
else
{
command.Printf(" '%s'", exe_path);
}
command.Printf(" '%s'", exe_path);
}
command.PutCString (" ; echo Process exited with status $?");
@ -1007,11 +972,11 @@ GetMacOSXProcessName (const ProcessInstanceInfoMatch *match_info_ptr,
match_info_ptr->GetNameMatchType(),
match_info_ptr->GetProcessInfo().GetName()))
{
process_info.SetName (process_name);
process_info.GetExecutableFile().SetFile (process_name, false);
return true;
}
}
process_info.SetName (NULL);
process_info.GetExecutableFile().Clear();
return false;
}
@ -1306,6 +1271,11 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info)
if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR))
flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag
//#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
// // Close all files exception those with file actions if this is supported.
// flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
//#endif
error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX);
if (error.Fail() || log)
error.PutToLog(log.get(), "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags);
@ -1347,6 +1317,13 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info)
}
const char *working_dir = launch_info.GetWorkingDirectory();
if (working_dir)
{
// No more thread specific current working directory
__pthread_chdir (working_dir);
}
const size_t num_file_actions = launch_info.GetNumFileActions ();
if (num_file_actions > 0)
{
@ -1410,6 +1387,12 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info)
envp);
}
if (working_dir)
{
// No more thread specific current working directory
__pthread_fchdir (-1);
}
if (pid != LLDB_INVALID_PROCESS_ID)
{
// If all went well, then set the process ID into the launch info

View File

@ -864,6 +864,26 @@ Args::StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t
return 0;
}
const char *
Args::GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg)
{
safe_arg.assign (unsafe_arg);
size_t prev_pos = 0;
while (prev_pos < safe_arg.size())
{
// Escape spaces and quotes
size_t pos = safe_arg.find_first_of(" '\"", prev_pos);
if (pos != std::string::npos)
{
safe_arg.insert (pos, 1, '\\');
prev_pos = pos + 2;
}
else
break;
}
return safe_arg.c_str();
}
int32_t
Args::StringToOptionEnum (const char *s, OptionEnumValueElement *enum_values, int32_t fail_value, Error &error)

View File

@ -13,6 +13,7 @@
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/Error.h"
#include "lldb/Core/StreamString.h"
using namespace lldb;
@ -134,6 +135,15 @@ CommandReturnObject::AppendError (const char *in_string, int len)
GetErrorStream().Printf ("error: %*.*s\n", len, len, in_string);
}
void
CommandReturnObject::SetError (const Error &error, const char *fallback_error_cstr)
{
const char *error_cstr = error.AsCString();
if (error_cstr == NULL)
error_cstr = fallback_error_cstr;
AppendError (error_cstr);
SetStatus (eReturnStatusFailed);
}
// Similar to AppendError, but do not prepend 'Error: ' to message, and
// don't append "\n" to the end of it.

View File

@ -395,8 +395,15 @@ Error
PlatformDarwin::LaunchProcess (ProcessLaunchInfo &launch_info)
{
Error error;
if (IsHost())
{
if (launch_info.GetFlags().Test (eLaunchFlagLaunchInShell))
{
const bool is_localhost = true;
if (!launch_info.ConvertArgumentsForLaunchingInShell (error, is_localhost))
return error;
}
error = Platform::LaunchProcess (launch_info);
}
else
@ -410,13 +417,14 @@ PlatformDarwin::LaunchProcess (ProcessLaunchInfo &launch_info)
}
lldb::ProcessSP
PlatformDarwin::Attach (lldb::pid_t pid,
PlatformDarwin::Attach (ProcessAttachInfo &attach_info,
Debugger &debugger,
Target *target,
Listener &listener,
Error &error)
{
lldb::ProcessSP process_sp;
if (IsHost())
{
if (target == NULL)
@ -438,18 +446,17 @@ PlatformDarwin::Attach (lldb::pid_t pid,
if (target && error.Success())
{
debugger.GetTargetList().SetSelectedTarget(target);
// The darwin always currently uses the GDB remote debugger plug-in
// so even when debugging locally we are debugging remotely!
process_sp = target->CreateProcess (listener, "gdb-remote");
process_sp = target->CreateProcess (listener, attach_info.GetProcessPluginName());
if (process_sp)
error = process_sp->Attach (pid, 2);
error = process_sp->Attach (attach_info);
}
}
else
{
if (m_remote_platform_sp)
process_sp = m_remote_platform_sp->Attach (pid, debugger, target, listener, error);
process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error);
else
error.SetErrorString ("the platform is not currently connected");
}

View File

@ -79,7 +79,7 @@ public:
LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info);
virtual lldb::ProcessSP
Attach (lldb::pid_t pid,
Attach (lldb_private::ProcessAttachInfo &attach_info,
lldb_private::Debugger &debugger,
lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one
lldb_private::Listener &listener,

View File

@ -337,7 +337,7 @@ PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info)
}
lldb::ProcessSP
PlatformRemoteGDBServer::Attach (lldb::pid_t pid,
PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info,
Debugger &debugger,
Target *target, // Can be NULL, if NULL create a new target, else use existing one
Listener &listener,
@ -391,7 +391,7 @@ PlatformRemoteGDBServer::Attach (lldb::pid_t pid,
assert (connect_url_len < sizeof(connect_url));
error = process_sp->ConnectRemote (connect_url);
if (error.Success())
error = process_sp->Attach(pid, 0);
error = process_sp->Attach(attach_info);
}
}
}

View File

@ -94,7 +94,7 @@ public:
LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info);
virtual lldb::ProcessSP
Attach (lldb::pid_t pid,
Attach (lldb_private::ProcessAttachInfo &attach_info,
lldb_private::Debugger &debugger,
lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one
lldb_private::Listener &listener,

View File

@ -1243,7 +1243,7 @@ GDBRemoteCommunicationClient::DecodeProcessInfoResponse (StringExtractorGDBRemot
extractor.GetStringRef().swap(value);
extractor.SetFilePos(0);
extractor.GetHexByteString (value);
process_info.SetName (value.c_str());
process_info.GetExecutableFile().SetFile (value.c_str(), false);
}
}

View File

@ -335,7 +335,7 @@ GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &pa
StringExtractor extractor;
extractor.GetStringRef().swap(value);
extractor.GetHexByteString (value);
match_info.GetProcessInfo().SetName (value.c_str());
match_info.GetProcessInfo().GetExecutableFile().SetFile(value.c_str(), false);
}
else if (key.compare("name_match") == 0)
{

View File

@ -577,16 +577,10 @@ Platform::DebugProcess (ProcessLaunchInfo &launch_info,
error = LaunchProcess (launch_info);
if (error.Success())
{
lldb::pid_t pid = launch_info.GetProcessID();
if (pid != LLDB_INVALID_PROCESS_ID)
if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
{
process_sp = Attach (pid, debugger, target, listener, error);
// if (process_sp)
// {
// if (launch_info.GetFlags().IsClear (eLaunchFlagStopAtEntry))
// process_sp->Resume();
// }
ProcessAttachInfo attach_info (launch_info);
process_sp = Attach (attach_info, debugger, target, listener, error);
}
}
return process_sp;

View File

@ -251,7 +251,7 @@ ProcessInfo::SetArguments (const Args& args,
}
void
ProcessLaunchInfo::FinalizeFileActions (Target *target, Process *process)
ProcessLaunchInfo::FinalizeFileActions (Target *target)
{
// If notthing was specified, then check the process for any default
// settings that were set with "settings set"
@ -304,6 +304,87 @@ ProcessLaunchInfo::FinalizeFileActions (Target *target, Process *process)
}
}
bool
ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, bool localhost)
{
error.Clear();
if (GetFlags().Test (eLaunchFlagLaunchInShell))
{
const char *shell_executable = GetShell();
if (shell_executable)
{
char shell_resolved_path[PATH_MAX];
if (localhost)
{
FileSpec shell_filespec (shell_executable, true);
if (!shell_filespec.Exists())
{
// Resolve the path in case we just got "bash", "sh" or "tcsh"
if (!shell_filespec.ResolveExecutableLocation ())
{
error.SetErrorStringWithFormat("invalid shell path '%s'", shell_executable);
return false;
}
}
shell_filespec.GetPath (shell_resolved_path, sizeof(shell_resolved_path));
shell_executable = shell_resolved_path;
}
Args shell_arguments;
std::string safe_arg;
shell_arguments.AppendArgument (shell_executable);
StreamString shell_command;
shell_arguments.AppendArgument ("-c");
shell_command.PutCString ("exec");
if (GetArchitecture().IsValid())
{
shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName());
// Set the resume count to 2:
// 1 - stop in shell
// 2 - stop in /usr/bin/arch
// 3 - then we will stop in our program
SetResumeCount(2);
}
else
{
// Set the resume count to 1:
// 1 - stop in shell
// 2 - then we will stop in our program
SetResumeCount(1);
}
const char **argv = GetArguments().GetConstArgumentVector ();
if (argv)
{
for (size_t i=0; argv[i] != NULL; ++i)
{
const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg);
shell_command.Printf(" %s", arg);
}
}
shell_arguments.AppendArgument (shell_command.GetString().c_str());
m_executable.SetFile(shell_executable, false);
m_arguments = shell_arguments;
return true;
}
else
{
error.SetErrorString ("invalid shell path");
}
}
else
{
error.SetErrorString ("not launching in shell");
}
return false;
}
bool
ProcessLaunchInfo::FileAction::Open (int fd, const char *path, bool read, bool write)
{
@ -312,11 +393,11 @@ ProcessLaunchInfo::FileAction::Open (int fd, const char *path, bool read, bool w
m_action = eFileActionOpen;
m_fd = fd;
if (read && write)
m_arg = O_RDWR;
m_arg = O_NOCTTY | O_CREAT | O_RDWR;
else if (read)
m_arg = O_RDONLY;
m_arg = O_NOCTTY | O_RDONLY;
else
m_arg = O_WRONLY;
m_arg = O_NOCTTY | O_CREAT | O_WRONLY;
m_path.assign (path);
return true;
}
@ -404,8 +485,12 @@ ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (posix_spawn_file_actions
else
{
int oflag = info->m_arg;
mode_t mode = 0;
if (oflag & O_CREAT)
mode = 0640;
error.SetError (::posix_spawn_file_actions_addopen (file_actions,
info->m_fd,
info->m_path.c_str(),
@ -496,7 +581,10 @@ ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *op
break;
case 'c':
launch_info.GetFlags().Set (eLaunchFlagLaunchInShell);
if (option_arg && option_arg[0])
launch_info.SetShell (option_arg);
else
launch_info.SetShell ("/bin/bash");
break;
case 'v':
@ -520,7 +608,7 @@ ProcessLaunchCommandOptions::g_option_table[] =
{ LLDB_OPT_SET_ALL, false, "working-dir", 'w', required_argument, NULL, 0, eArgTypePath, "Set the current working directory to <path> when running the inferior."},
{ LLDB_OPT_SET_ALL, false, "arch", 'a', required_argument, NULL, 0, eArgTypeArchitecture, "Set the architecture for the process to launch when ambiguous."},
{ LLDB_OPT_SET_ALL, false, "environment", 'v', required_argument, NULL, 0, eArgTypeNone, "Specify an environment variable name/value stirng (--environement NAME=VALUE). Can be specified multiple times for subsequent environment entries."},
{ LLDB_OPT_SET_ALL, false, "shell", 'c', no_argument, NULL, 0, eArgTypeNone, "Run the process in a shell (not supported on all platforms)."},
{ LLDB_OPT_SET_ALL, false, "shell", 'c', optional_argument, NULL, 0, eArgTypePath, "Run the process in a shell (not supported on all platforms)."},
{ LLDB_OPT_SET_1 , false, "stdin", 'i', required_argument, NULL, 0, eArgTypePath, "Redirect stdin for the process to <path>."},
{ LLDB_OPT_SET_1 , false, "stdout", 'o', required_argument, NULL, 0, eArgTypePath, "Redirect stdout for the process to <path>."},
@ -2229,91 +2317,103 @@ Process::AttachCompletionHandler::GetExitString ()
}
Error
Process::Attach (lldb::pid_t attach_pid, uint32_t exec_count)
Process::Attach (ProcessAttachInfo &attach_info)
{
m_abi_sp.reset();
m_process_input_reader.reset();
m_dyld_ap.reset();
m_os_ap.reset();
Error error (WillAttachToProcessWithID(attach_pid));
if (error.Success())
{
SetPublicState (eStateAttaching);
error = DoAttachToProcessWithID (attach_pid);
if (error.Success())
{
SetNextEventAction(new Process::AttachCompletionHandler(this, exec_count));
StartPrivateStateThread();
}
else
{
if (GetID() != LLDB_INVALID_PROCESS_ID)
{
SetID (LLDB_INVALID_PROCESS_ID);
const char *error_string = error.AsCString();
if (error_string == NULL)
error_string = "attach failed";
SetExitStatus(-1, error_string);
}
}
}
return error;
}
Error
Process::Attach (const char *process_name, bool wait_for_launch)
{
m_abi_sp.reset();
m_process_input_reader.reset();
// Find the process and its architecture. Make sure it matches the architecture
// of the current Target, and if not adjust it.
lldb::pid_t attach_pid = attach_info.GetProcessID();
Error error;
if (!wait_for_launch)
if (attach_pid == LLDB_INVALID_PROCESS_ID)
{
ProcessInstanceInfoList process_infos;
PlatformSP platform_sp (m_target.GetPlatform ());
assert (platform_sp.get());
char process_name[PATH_MAX];
if (platform_sp)
if (attach_info.GetExecutableFile().GetPath (process_name, sizeof(process_name)))
{
ProcessInstanceInfoMatch match_info;
match_info.GetProcessInfo().SetName(process_name);
match_info.SetNameMatchType (eNameMatchEquals);
platform_sp->FindProcesses (match_info, process_infos);
if (process_infos.GetSize() > 1)
const bool wait_for_launch = attach_info.GetWaitForLaunch();
if (wait_for_launch)
{
error.SetErrorStringWithFormat ("more than one process named %s", process_name);
error = WillAttachToProcessWithName(process_name, wait_for_launch);
if (error.Success())
{
SetPublicState (eStateAttaching);
error = DoAttachToProcessWithName (process_name, wait_for_launch);
if (error.Fail())
{
if (GetID() != LLDB_INVALID_PROCESS_ID)
{
SetID (LLDB_INVALID_PROCESS_ID);
if (error.AsCString() == NULL)
error.SetErrorString("attach failed");
SetExitStatus(-1, error.AsCString());
}
}
else
{
SetNextEventAction(new Process::AttachCompletionHandler(this, attach_info.GetResumeCount()));
StartPrivateStateThread();
}
return error;
}
}
else if (process_infos.GetSize() == 0)
else
{
error.SetErrorStringWithFormat ("could not find a process named %s", process_name);
ProcessInstanceInfoList process_infos;
PlatformSP platform_sp (m_target.GetPlatform ());
if (platform_sp)
{
ProcessInstanceInfoMatch match_info;
match_info.GetProcessInfo() = attach_info;
match_info.SetNameMatchType (eNameMatchEquals);
platform_sp->FindProcesses (match_info, process_infos);
const uint32_t num_matches = process_infos.GetSize();
if (num_matches == 1)
{
attach_pid = process_infos.GetProcessIDAtIndex(0);
// Fall through and attach using the above process ID
}
else
{
match_info.GetProcessInfo().GetExecutableFile().GetPath (process_name, sizeof(process_name));
if (num_matches > 1)
error.SetErrorStringWithFormat ("more than one process named %s", process_name);
else
error.SetErrorStringWithFormat ("could not find a process named %s", process_name);
}
}
else
{
error.SetErrorString ("invalid platform, can't find processes by name");
return error;
}
}
}
else
{
error.SetErrorString ("invalid platform");
{
error.SetErrorString ("invalid process name");
}
}
if (error.Success())
if (attach_pid != LLDB_INVALID_PROCESS_ID)
{
m_dyld_ap.reset();
m_os_ap.reset();
error = WillAttachToProcessWithName(process_name, wait_for_launch);
error = WillAttachToProcessWithID(attach_pid);
if (error.Success())
{
SetPublicState (eStateAttaching);
error = DoAttachToProcessWithName (process_name, wait_for_launch);
if (error.Fail())
error = DoAttachToProcessWithID (attach_pid);
if (error.Success())
{
SetNextEventAction(new Process::AttachCompletionHandler(this, attach_info.GetResumeCount()));
StartPrivateStateThread();
}
else
{
if (GetID() != LLDB_INVALID_PROCESS_ID)
{
@ -2325,16 +2425,80 @@ Process::Attach (const char *process_name, bool wait_for_launch)
SetExitStatus(-1, error_string);
}
}
else
{
SetNextEventAction(new Process::AttachCompletionHandler(this, 0));
StartPrivateStateThread();
}
}
}
return error;
}
//Error
//Process::Attach (const char *process_name, bool wait_for_launch)
//{
// m_abi_sp.reset();
// m_process_input_reader.reset();
//
// // Find the process and its architecture. Make sure it matches the architecture
// // of the current Target, and if not adjust it.
// Error error;
//
// if (!wait_for_launch)
// {
// ProcessInstanceInfoList process_infos;
// PlatformSP platform_sp (m_target.GetPlatform ());
// assert (platform_sp.get());
//
// if (platform_sp)
// {
// ProcessInstanceInfoMatch match_info;
// match_info.GetProcessInfo().SetName(process_name);
// match_info.SetNameMatchType (eNameMatchEquals);
// platform_sp->FindProcesses (match_info, process_infos);
// if (process_infos.GetSize() > 1)
// {
// error.SetErrorStringWithFormat ("more than one process named %s", process_name);
// }
// else if (process_infos.GetSize() == 0)
// {
// error.SetErrorStringWithFormat ("could not find a process named %s", process_name);
// }
// }
// else
// {
// error.SetErrorString ("invalid platform");
// }
// }
//
// if (error.Success())
// {
// m_dyld_ap.reset();
// m_os_ap.reset();
//
// error = WillAttachToProcessWithName(process_name, wait_for_launch);
// if (error.Success())
// {
// SetPublicState (eStateAttaching);
// error = DoAttachToProcessWithName (process_name, wait_for_launch);
// if (error.Fail())
// {
// if (GetID() != LLDB_INVALID_PROCESS_ID)
// {
// SetID (LLDB_INVALID_PROCESS_ID);
// const char *error_string = error.AsCString();
// if (error_string == NULL)
// error_string = "attach failed";
//
// SetExitStatus(-1, error_string);
// }
// }
// else
// {
// SetNextEventAction(new Process::AttachCompletionHandler(this, 0));
// StartPrivateStateThread();
// }
// }
// }
// return error;
//}
void
Process::CompleteAttach ()
{