I reverted TestProcessAttach changes and merged with
http://reviews.llvm.org/D7358 - no regressions noticed for local test runs on
Ubuntu and OSX, remote Linux->Linux execution of TestProcessAttach works fine.
Please take another look.
Thank you!
http://reviews.llvm.org/D7263
Files:
include/lldb/API/SBPlatform.h
include/lldb/Target/Platform.h
scripts/Python/interface/SBPlatform.i
source/API/SBPlatform.cpp
source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
source/Target/Platform.cpp
test/lldbtest.py
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
Index: include/lldb/API/SBPlatform.h
===================================================================
--- include/lldb/API/SBPlatform.h
+++ include/lldb/API/SBPlatform.h
@@ -12,6 +12,9 @@
#include "lldb/API/SBDefines.h"
+#include <functional>
+#include <string>
+
struct PlatformConnectOptions;
struct PlatformShellCommand;
@@ -100,6 +103,35 @@
PlatformShellCommand *m_opaque_ptr;
};
+ class SBPlatformLaunchCommand
+ {
+ public:
+ SBPlatformLaunchCommand (const char* command,
+ const char* working_dir,
+ const char* arch);
+
+ const char*
+ GetCommand () const;
+
+ const char*
+ GetWorkingDir () const;
+
+ const char*
+ GetArch () const;
+
+ const lldb::pid_t&
+ GetPID () const;
+
+ void
+ SetPID (const lldb::pid_t& pid);
+
+ private:
+ const std::string m_command;
+ const std::string m_working_dir;
+ const std::string m_arch;
+ lldb::pid_t m_pid;
+ };
+
class SBPlatform
{
public:
@@ -171,6 +203,12 @@
Run (SBPlatformShellCommand &shell_command);
SBError
+ Launch (SBPlatformLaunchCommand &launch_command);
+
+ SBError
+ Kill (const lldb::pid_t pid);
+
+ SBError
MakeDirectory (const char *path, uint32_t file_permissions = eFilePermissionsDirectoryDefault);
uint32_t
@@ -190,6 +228,9 @@
void
SetSP (const lldb::PlatformSP& platform_sp);
+ SBError
+ ExecuteConnected (const std::function<lldb_private::Error(const lldb::PlatformSP&)>& func);
+
lldb::PlatformSP m_opaque_sp;
};
Index: include/lldb/Target/Platform.h
===================================================================
--- include/lldb/Target/Platform.h
+++ include/lldb/Target/Platform.h
@@ -380,6 +380,12 @@
LaunchProcess (ProcessLaunchInfo &launch_info);
//------------------------------------------------------------------
+ /// Kill process on a platform.
+ //------------------------------------------------------------------
+ virtual Error
+ KillProcess (const lldb::pid_t pid);
+
+ //------------------------------------------------------------------
/// Lets a platform answer if it is compatible with a given
/// architecture and the target triple contained within.
//------------------------------------------------------------------
Index: scripts/Python/interface/SBPlatform.i
===================================================================
--- scripts/Python/interface/SBPlatform.i
+++ scripts/Python/interface/SBPlatform.i
@@ -83,6 +83,29 @@
GetOutput ();
};
+class SBPlatformLaunchCommand
+{
+public:
+ SBPlatformLaunchCommand (const char* command,
+ const char* working_dir,
+ const char* arch);
+
+ const char*
+ GetCommand () const;
+
+ const char*
+ GetWorkingDir () const;
+
+ const char*
+ GetArch () const;
+
+ const lldb::pid_t&
+ GetPID () const;
+
+ void
+ SetPID (const lldb::pid_t& pid);
+};
+
%feature("docstring",
"A class that represents the a platform that can represent the current host or a remote host debug platform.
@@ -174,6 +197,12 @@
Run (lldb::SBPlatformShellCommand &shell_command);
lldb::SBError
+ Launch (lldb::SBPlatformLaunchCommand &launch_command);
+
+ lldb::SBError
+ Kill (const lldb::pid_t pid);
+
+ lldb::SBError
MakeDirectory (const char *path, uint32_t file_permissions = lldb::eFilePermissionsDirectoryDefault);
uint32_t
Index: source/API/SBPlatform.cpp
===================================================================
--- source/API/SBPlatform.cpp
+++ source/API/SBPlatform.cpp
@@ -14,6 +14,7 @@
#include "lldb/Core/Error.h"
#include "lldb/Host/File.h"
#include "lldb/Interpreter/Args.h"
+#include "lldb/Target/ProcessLaunchInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Platform.h"
@@ -215,6 +216,7 @@
return m_opaque_ptr->m_working_dir.c_str();
}
+
void
SBPlatformShellCommand::SetWorkingDirectory (const char *path)
{
@@ -257,6 +259,50 @@
}
//----------------------------------------------------------------------
+// SBPlatformLaunchCommand
+//----------------------------------------------------------------------
+
+SBPlatformLaunchCommand::SBPlatformLaunchCommand (const char* command,
+ const char* working_dir,
+ const char* arch) :
+ m_command (command ? command : ""),
+ m_working_dir (working_dir ? working_dir: ""),
+ m_arch (arch ? arch: ""),
+ m_pid(LLDB_INVALID_PROCESS_ID)
+{
+}
+
+const char*
+SBPlatformLaunchCommand::GetCommand () const
+{
+ return m_command.c_str ();
+}
+
+const char*
+SBPlatformLaunchCommand::GetWorkingDir () const
+{
+ return m_working_dir.c_str ();
+}
+
+const char*
+SBPlatformLaunchCommand::GetArch () const
+{
+ return m_arch.c_str ();
+}
+
+const lldb::pid_t&
+SBPlatformLaunchCommand::GetPID () const
+{
+ return m_pid;
+}
+
+void
+SBPlatformLaunchCommand::SetPID (const lldb::pid_t& pid)
+{
+ m_pid = pid;
+}
+
+//----------------------------------------------------------------------
// SBPlatform
//----------------------------------------------------------------------
SBPlatform::SBPlatform () :
@@ -484,104 +530,129 @@
SBPlatform::Put (SBFileSpec &src,
SBFileSpec &dst)
{
- SBError sb_error;
-
- PlatformSP platform_sp(GetSP());
- if (platform_sp)
- {
- if (src.Exists())
+ return ExecuteConnected(
+ [&](const lldb::PlatformSP& platform_sp)
+ {
+ if (src.Exists())
+ {
+ uint32_t permissions = src.ref().GetPermissions();
+ if (permissions == 0)
+ {
+ if (src.ref().GetFileType() == FileSpec::eFileTypeDirectory)
+ permissions = eFilePermissionsDirectoryDefault;
+ else
+ permissions = eFilePermissionsFileDefault;
+ }
+
+ return platform_sp->PutFile(src.ref(), dst.ref(), permissions);
+ }
+
+ Error error;
+ error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'", src.ref().GetPath().c_str());
+ return error;
+ });
+}
+
+SBError
+SBPlatform::Install (SBFileSpec &src,
+ SBFileSpec &dst)
+{
+ return ExecuteConnected(
+ [&](const lldb::PlatformSP& platform_sp)
+ {
+ if (src.Exists())
+ return platform_sp->Install(src.ref(), dst.ref());
+
+ Error error;
+ error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'", src.ref().GetPath().c_str());
+ return error;
+ });
+}
+
+
+SBError
+SBPlatform::Run (SBPlatformShellCommand &shell_command)
+{
+ return ExecuteConnected(
+ [&](const lldb::PlatformSP& platform_sp)
{
- uint32_t permissions = src.ref().GetPermissions();
- if (permissions == 0)
+ const char *command = shell_command.GetCommand();
+ if (!command)
+ return Error("invalid shell command (empty)");
+
+ const char *working_dir = shell_command.GetWorkingDirectory();
+ if (working_dir == NULL)
{
- if (src.ref().GetFileType() == FileSpec::eFileTypeDirectory)
- permissions = eFilePermissionsDirectoryDefault;
- else
- permissions = eFilePermissionsFileDefault;
+ working_dir = platform_sp->GetWorkingDirectory().GetCString();
+ if (working_dir)
+ shell_command.SetWorkingDirectory(working_dir);
}
+ return platform_sp->RunShellCommand(command,
+ working_dir,
+ &shell_command.m_opaque_ptr->m_status,
+ &shell_command.m_opaque_ptr->m_signo,
+ &shell_command.m_opaque_ptr->m_output,
+ shell_command.m_opaque_ptr->m_timeout_sec);
+ });
+}
- sb_error.ref() = platform_sp->PutFile(src.ref(),
- dst.ref(),
- permissions);
- }
- else
+SBError
+SBPlatform::Launch (SBPlatformLaunchCommand &launch_command)
+{
+ return ExecuteConnected(
+ [&](const lldb::PlatformSP& platform_sp)
{
- sb_error.ref().SetErrorStringWithFormat("'src' argument doesn't exist: '%s'", src.ref().GetPath().c_str());
- }
- }
- else
- {
- sb_error.SetErrorString("invalid platform");
- }
- return sb_error;
+ auto command = launch_command.GetCommand();
+ if (!command)
+ return Error("invalid launch command (empty)");
+
+ ProcessLaunchInfo launch_info;
+ launch_info.SetArguments(Args(command), true);
+
+ auto working_dir = launch_command.GetWorkingDir();
+ if (working_dir == nullptr)
+ working_dir = platform_sp->GetWorkingDirectory().GetCString();
+ launch_info.SetWorkingDirectory(working_dir);
+ auto arch = launch_command.GetArch();
+ if (arch)
+ launch_info.SetArchitecture(ArchSpec(arch));
+ else
+ launch_info.SetArchitecture(platform_sp->GetRemoteSystemArchitecture());
+
+ auto error = platform_sp->LaunchProcess(launch_info);
+ if (error.Success())
+ launch_command.SetPID(launch_info.GetProcessID());
+
+ return error;
+ });
}
SBError
-SBPlatform::Install (SBFileSpec &src,
- SBFileSpec &dst)
+SBPlatform::Kill (const lldb::pid_t pid)
{
- SBError sb_error;
- PlatformSP platform_sp(GetSP());
- if (platform_sp)
- {
- if (src.Exists())
+ return ExecuteConnected(
+ [&](const lldb::PlatformSP& platform_sp)
{
- sb_error.ref() = platform_sp->Install(src.ref(), dst.ref());
- }
- else
- {
- sb_error.ref().SetErrorStringWithFormat("'src' argument doesn't exist: '%s'", src.ref().GetPath().c_str());
- }
- }
- else
- {
- sb_error.SetErrorString("invalid platform");
- }
- return sb_error;
+ return platform_sp->KillProcess(pid);
+ });
}
-
SBError
-SBPlatform::Run (SBPlatformShellCommand &shell_command)
+SBPlatform::ExecuteConnected (const std::function<Error(const lldb::PlatformSP&)>& func)
{
SBError sb_error;
- PlatformSP platform_sp(GetSP());
+ const auto platform_sp(GetSP());
if (platform_sp)
{
if (platform_sp->IsConnected())
- {
- const char *command = shell_command.GetCommand();
- if (command)
- {
- const char *working_dir = shell_command.GetWorkingDirectory();
- if (working_dir == NULL)
- {
- working_dir = platform_sp->GetWorkingDirectory().GetCString();
- if (working_dir)
- shell_command.SetWorkingDirectory(working_dir);
- }
- sb_error.ref() = platform_sp->RunShellCommand(command,
- working_dir,
- &shell_command.m_opaque_ptr->m_status,
- &shell_command.m_opaque_ptr->m_signo,
- &shell_command.m_opaque_ptr->m_output,
- shell_command.m_opaque_ptr->m_timeout_sec);
- }
- else
- {
- sb_error.SetErrorString("invalid shell command (empty)");
- }
- }
+ sb_error.ref() = func(platform_sp);
else
- {
sb_error.SetErrorString("not connected");
- }
}
else
- {
sb_error.SetErrorString("invalid platform");
- }
- return sb_error;
+
+ return sb_error;
}
SBError
Index: source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
===================================================================
--- source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
+++ source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -430,7 +430,6 @@
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
Error error;
- lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
if (log)
log->Printf ("PlatformRemoteGDBServer::%s() called", __FUNCTION__);
@@ -475,7 +474,7 @@
std::string error_str;
if (m_gdb_client.GetLaunchSuccess (error_str))
{
- pid = m_gdb_client.GetCurrentProcessID ();
+ const auto pid = m_gdb_client.GetCurrentProcessID (false);
if (pid != LLDB_INVALID_PROCESS_ID)
{
launch_info.SetProcessID (pid);
@@ -486,7 +485,7 @@
{
if (log)
log->Printf ("PlatformRemoteGDBServer::%s() launch succeeded but we didn't get a valid process id back!", __FUNCTION__);
- // FIXME isn't this an error condition? Do we need to set an error here? Check with Greg.
+ error.SetErrorString ("failed to get PID");
}
}
else
@@ -503,6 +502,14 @@
return error;
}
+Error
+PlatformRemoteGDBServer::KillProcess (const lldb::pid_t pid)
+{
+ if (!m_gdb_client.KillSpawnedProcess(pid))
+ return Error("failed to kill remote spawned process");
+ return Error();
+}
+
lldb::ProcessSP
PlatformRemoteGDBServer::DebugProcess (lldb_private::ProcessLaunchInfo &launch_info,
lldb_private::Debugger &debugger,
Index: source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
===================================================================
--- source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
+++ source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h
@@ -86,7 +86,10 @@
virtual lldb_private::Error
LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info);
-
+
+ virtual lldb_private::Error
+ KillProcess (const lldb::pid_t pid);
+
virtual lldb::ProcessSP
DebugProcess (lldb_private::ProcessLaunchInfo &launch_info,
lldb_private::Debugger &debugger,
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -1211,13 +1211,13 @@
}
lldb::pid_t
-GDBRemoteCommunicationClient::GetCurrentProcessID ()
+GDBRemoteCommunicationClient::GetCurrentProcessID (bool allow_lazy)
{
- if (m_curr_pid_is_valid == eLazyBoolYes)
+ if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes)
return m_curr_pid;
// First try to retrieve the pid via the qProcessInfo request.
- GetCurrentProcessInfo ();
+ GetCurrentProcessInfo (allow_lazy);
if (m_curr_pid_is_valid == eLazyBoolYes)
{
// We really got it.
@@ -2409,14 +2409,17 @@
}
bool
-GDBRemoteCommunicationClient::GetCurrentProcessInfo ()
+GDBRemoteCommunicationClient::GetCurrentProcessInfo (bool allow_lazy)
{
Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS));
- if (m_qProcessInfo_is_valid == eLazyBoolYes)
- return true;
- if (m_qProcessInfo_is_valid == eLazyBoolNo)
- return false;
+ if (allow_lazy)
+ {
+ if (m_qProcessInfo_is_valid == eLazyBoolYes)
+ return true;
+ if (m_qProcessInfo_is_valid == eLazyBoolNo)
+ return false;
+ }
GetHostInfo ();
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -109,7 +109,7 @@
bool &timed_out);
lldb::pid_t
- GetCurrentProcessID ();
+ GetCurrentProcessID (bool allow_lazy = true);
bool
GetLaunchSuccess (std::string &error_str);
@@ -534,7 +534,7 @@
StringExtractorGDBRemote &response);
bool
- GetCurrentProcessInfo ();
+ GetCurrentProcessInfo (bool allow_lazy_pid = true);
bool
GetGDBServerVersion();
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
@@ -603,14 +603,12 @@
// add to list of spawned processes. On an lldb-gdbserver, we
// would expect there to be only one.
- lldb::pid_t pid;
- if ( (pid = m_process_launch_info.GetProcessID()) != LLDB_INVALID_PROCESS_ID )
+ const auto pid = m_process_launch_info.GetProcessID();
+ if (pid != LLDB_INVALID_PROCESS_ID)
{
// add to spawned pids
- {
- Mutex::Locker locker (m_spawned_pids_mutex);
- m_spawned_pids.insert(pid);
- }
+ Mutex::Locker locker (m_spawned_pids_mutex);
+ m_spawned_pids.insert(pid);
}
return error;
@@ -1424,23 +1422,33 @@
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServer::Handle_qProcessInfo (StringExtractorGDBRemote &packet)
{
- // Only the gdb server handles this.
- if (!IsGdbServer ())
- return SendUnimplementedResponse (packet.GetStringRef ().c_str ());
-
- // Fail if we don't have a current process.
- if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID))
- return SendErrorResponse (68);
-
- ProcessInstanceInfo proc_info;
- if (Host::GetProcessInfo (m_debugged_process_sp->GetID (), proc_info))
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ if (IsGdbServer ())
{
- StreamString response;
- CreateProcessInfoResponse_DebugServerStyle(proc_info, response);
- return SendPacketNoLock (response.GetData (), response.GetSize ());
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse (68);
+
+ pid = m_debugged_process_sp->GetID ();
}
-
- return SendErrorResponse (1);
+ else if (m_is_platform)
+ {
+ pid = m_process_launch_info.GetProcessID ();
+ }
+ else
+ return SendUnimplementedResponse (packet.GetStringRef ().c_str ());
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return SendErrorResponse (1);
+
+ ProcessInstanceInfo proc_info;
+ if (!Host::GetProcessInfo (pid, proc_info))
+ return SendErrorResponse (1);
+
+ StreamString response;
+ CreateProcessInfoResponse_DebugServerStyle(proc_info, response);
+ return SendPacketNoLock (response.GetData (), response.GetSize ());
}
GDBRemoteCommunication::PacketResult
Index: source/Target/Platform.cpp
===================================================================
--- source/Target/Platform.cpp
+++ source/Target/Platform.cpp
@@ -1103,6 +1103,20 @@
return error;
}
+Error
+Platform::KillProcess (const lldb::pid_t pid)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM));
+ if (log)
+ log->Printf ("Platform::%s, pid %" PRIu64, __FUNCTION__, pid);
+
+ if (!IsHost ())
+ return Error ("base lldb_private::Platform class can't launch remote processes");
+
+ Host::Kill (pid, SIGTERM);
+ return Error();
+}
+
lldb::ProcessSP
Platform::DebugProcess (ProcessLaunchInfo &launch_info,
Debugger &debugger,
Index: test/lldbtest.py
===================================================================
--- test/lldbtest.py
+++ test/lldbtest.py
@@ -31,6 +31,7 @@
$
"""
+import abc
import os, sys, traceback
import os.path
import re
@@ -41,6 +42,7 @@
import types
import unittest2
import lldb
+from _pyio import __metaclass__
# See also dotest.parseOptionsAndInitTestdirs(), where the environment variables
# LLDB_COMMAND_TRACE and LLDB_DO_CLEANUP are set from '-t' and '-r dir' options.
@@ -233,6 +235,72 @@
print >> self.session, self.getvalue()
self.close()
+class _BaseProcess(object):
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractproperty
+ def pid(self):
+ """Returns process PID if has been launched already."""
+
+ @abc.abstractmethod
+ def launch(self, executable, args):
+ """Launches new process with given executable and args."""
+
+ @abc.abstractmethod
+ def terminate(self):
+ """Terminates previously launched process.."""
+
+class _LocalProcess(_BaseProcess):
+
+ def __init__(self, trace_on):
+ self._proc = None
+ self._trace_on = trace_on
+
+ @property
+ def pid(self):
+ return self._proc.pid
+
+ def launch(self, executable, args):
+ self._proc = Popen([executable] + args,
+ stdout = open(os.devnull) if not self._trace_on else None,
+ stdin = PIPE)
+
+ def terminate(self):
+ if self._proc.poll() == None:
+ self._proc.terminate()
+
+class _RemoteProcess(_BaseProcess):
+
+ def __init__(self):
+ self._pid = None
+
+ @property
+ def pid(self):
+ return self._pid
+
+ def launch(self, executable, args):
+ remote_work_dir = lldb.remote_platform.GetWorkingDirectory()
+ src_path = executable
+ dst_path = os.path.join(remote_work_dir, os.path.basename(executable))
+
+ err = lldb.remote_platform.Install(lldb.SBFileSpec(src_path, True),
+ lldb.SBFileSpec(dst_path, False))
+ if err.Fail():
+ raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (src_path, dst_path, err))
+
+ cmd = lldb.SBPlatformLaunchCommand(' '.join([dst_path] + args),
+ remote_work_dir,
+ None)
+ err = lldb.remote_platform.Launch(cmd)
+ if err.Fail():
+ raise Exception("remote_platform.Launch('%s', '%s') failed: %s" % (dst_path, args, err))
+ self._pid = cmd.GetPID()
+
+ def terminate(self):
+ err = lldb.remote_platform.Kill(self._pid)
+ if err.Fail():
+ raise Exception("remote_platform.Kill(%d) failed: %s" % (self._pid, err))
+
# From 2.7's subprocess.check_output() convenience function.
# Return a tuple (stdoutdata, stderrdata).
def system(commands, **kwargs):
@@ -956,8 +1024,7 @@
def cleanupSubprocesses(self):
# Ensure any subprocesses are cleaned up
for p in self.subprocesses:
- if p.poll() == None:
- p.terminate()
+ p.terminate()
del p
del self.subprocesses[:]
# Ensure any forked processes are cleaned up
@@ -974,11 +1041,8 @@
otherwise the test suite will leak processes.
"""
-
- # Don't display the stdout if not in TraceOn() mode.
- proc = Popen([executable] + args,
- stdout = open(os.devnull) if not self.TraceOn() else None,
- stdin = PIPE)
+ proc = _RemoteProcess() if lldb.remote_platform else _LocalProcess(self.TraceOn())
+ proc.launch(executable, args)
self.subprocesses.append(proc)
return proc
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-commits