<div dir="ltr"><div><div>This should fix bug #14541: <a href="http://llvm.org/bugs/show_bug.cgi?id=14541">http://llvm.org/bugs/show_bug.cgi?id=14541</a><br><br></div><div>I haven't been able to run the python test script mentioned in the bug report yet, but "platform process list" in lldb looks correct on my Ubuntu 12.04 64-bit machine.<br>
</div><div><br></div><div>Also should fix a leaked file descriptor, and null pointer dereference when ReadProcPseudoFile() fails.<br></div><div><br></div>Diff is down below and also here: <a href="https://gist.github.com/mikesartain/5580617">https://gist.github.com/mikesartain/5580617</a><br>
<br>Please yell at me with any feedback. Thanks!<br></div> -Mike<br><div><br>Index: source/Host/common/Host.cpp<br>===================================================================<br>--- source/Host/common/Host.cpp (revision 181847)<br>
+++ source/Host/common/Host.cpp (working copy)<br>@@ -1206,14 +1206,14 @@<br> return getegid();<br> }<br> <br>-#if !defined (__APPLE__)<br>+#if !defined (__APPLE__) && !defined(__linux__)<br> uint32_t<br> Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos)<br>
{<br> process_infos.Clear();<br> return process_infos.GetSize();<br> }<br>-#endif<br>+#endif // #if !defined (__APPLE__) && !defined(__linux__)<br> <br> #if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined(__linux__)<br>
bool<br>Index: source/Host/linux/Host.cpp<br>===================================================================<br>--- source/Host/linux/Host.cpp (revision 181847)<br>+++ source/Host/linux/Host.cpp (working copy)<br>
@@ -12,9 +12,9 @@<br> #include <sys/utsname.h><br> #include <sys/types.h><br> #include <sys/stat.h><br>+#include <dirent.h><br> #include <fcntl.h><br> <br>-<br> // C++ Includes<br> // Other libraries and framework includes<br>
// Project includes<br>@@ -28,57 +28,174 @@<br> using namespace lldb;<br> using namespace lldb_private;<br> <br>+typedef enum ProcessStateFlags<br>+{<br>+ eProcessStateRunning = (1u << 0), // Running<br>
+ eProcessStateSleeping = (1u << 1), // Sleeping in an interruptible wait<br>+ eProcessStateWaiting = (1u << 2), // Waiting in an uninterruptible disk sleep<br>+ eProcessStateZombie = (1u << 3), // Zombie<br>
+ eProcessStateTracedOrStopped = (1u << 4), // Traced or stopped (on a signal)<br>+ eProcessStatePaging = (1u << 5) // Paging<br>+} ProcessStateFlags;<br>+<br>+typedef struct ProcessStatInfo<br>
+{<br>+ lldb::pid_t ppid; // Parent Process ID<br>+ uint32_t fProcessState; // ProcessStateFlags<br>+} ProcessStatInfo;<br>+<br>+// Get the process info with additional information from /proc/$PID/stat (like process state, and tracer pid).<br>
+static bool GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid);<br>+<br> namespace<br> {<br> <br> lldb::DataBufferSP<br>-ReadProcPseudoFile(lldb::pid_t pid, const char *name)<br>
+ReadProcPseudoFile (lldb::pid_t pid, const char *name)<br> {<br>- static const size_t path_size = 128;<br>- static char path[path_size];<br>- lldb::DataBufferSP buf_sp;<br>-<br> int fd;<br>+ char path[PATH_MAX];<br>
<br>+ // Make sure we've got a nil terminated buffer for all the folks calling<br>+ // GetBytes() directly off our returned DataBufferSP if we hit an error.<br>+ lldb::DataBufferSP buf_sp (new DataBufferHeap(1, 0));<br>
+<br> // Ideally, we would simply create a FileSpec and call ReadFileContents.<br> // However, files in procfs have zero size (since they are, in general,<br> // dynamically generated by the kernel) which is incompatible with the<br>
- // current ReadFileContents implementation. Therefore we simply stream the<br>+ // current ReadFileContents implementation. Therefore we simply stream the<br> // data into a DataBuffer ourselves.<br>- if (snprintf(path, path_size, "/proc/%" PRIu64 "/%s", pid, name) < 0)<br>
- return buf_sp;<br>+ if (snprintf (path, PATH_MAX, "/proc/%" PRIu64 "/%s", pid, name) > 0)<br>+ {<br>+ if ((fd = open (path, O_RDONLY, 0)) >= 0)<br>+ {<br>+ size_t bytes_read = 0;<br>
+ std::unique_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(1024, 0));<br> <br>- if ((fd = open(path, O_RDONLY, 0)) < 0)<br>- return buf_sp;<br>+ for (;;) <br>+ {<br>+ size_t avail = buf_ap->GetByteSize() - bytes_read;<br>
+ ssize_t status = read (fd, buf_ap->GetBytes() + bytes_read, avail);<br> <br>- size_t bytes_read = 0;<br>- std::unique_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(1024, 0));<br>- for (;;) <br>
+ if (status < 0) <br>+ break;<br>+<br>+ if (status == 0) <br>+ {<br>+ buf_ap->SetByteSize (bytes_read);<br>+ buf_sp.reset (buf_ap.release());<br>
+ break;<br>+ }<br>+<br>+ bytes_read += status;<br>+<br>+ if (avail - status == 0)<br>+ buf_ap->SetByteSize (2 * buf_ap->GetByteSize());<br>
+ }<br>+<br>+ close (fd);<br>+ }<br>+ }<br>+<br>+ return buf_sp;<br>+}<br>+<br>+} // anonymous namespace<br>+<br>+static bool<br>+ReadProcPseudoFileStat (lldb::pid_t pid, ProcessStatInfo& stat_info)<br>
+{<br>+ // Read the /proc/$PID/stat file.<br>+ lldb::DataBufferSP buf_sp = ReadProcPseudoFile (pid, "stat");<br>+<br>+ // The filename of the executable is stored in parenthesis right after the pid. We look for the closing<br>
+ // parenthesis for the filename and work from there in case the name has something funky like ')' in it.<br>+ const char *filename_end = strrchr ((const char *)buf_sp->GetBytes(), ')');<br>+ if (filename_end)<br>
{<br>- size_t avail = buf_ap->GetByteSize() - bytes_read;<br>- ssize_t status = read(fd, buf_ap->GetBytes() + bytes_read, avail);<br>+ char state = '\0';<br>+ int ppid = LLDB_INVALID_PROCESS_ID;<br>
<br>- if (status < 0) <br>- break;<br>+ // Read state and ppid.<br>+ sscanf (filename_end + 1, " %c %d", &state, &ppid);<br> <br>- bytes_read += status;<br>+ stat_info.ppid = ppid;<br>
<br>- if (status == 0) <br>+ switch (state)<br> {<br>- buf_ap->SetByteSize(bytes_read);<br>- buf_sp.reset(buf_ap.release());<br>- break;<br>+ case 'R':<br>
+ stat_info.fProcessState |= eProcessStateRunning;<br>+ break;<br>+ case 'S':<br>+ stat_info.fProcessState |= eProcessStateSleeping;<br>+ break;<br>
+ case 'D':<br>+ stat_info.fProcessState |= eProcessStateWaiting;<br>+ break;<br>+ case 'Z':<br>+ stat_info.fProcessState |= eProcessStateZombie;<br>
+ break;<br>+ case 'T':<br>+ stat_info.fProcessState |= eProcessStateTracedOrStopped;<br>+ break;<br>+ case 'W':<br>+ stat_info.fProcessState |= eProcessStatePaging;<br>
+ break;<br> }<br> <br>- if (avail - status == 0)<br>- buf_ap->SetByteSize(2 * buf_ap->GetByteSize());<br>+ return true;<br> }<br> <br>- return buf_sp;<br>+ return false;<br>
}<br> <br>-} // anonymous namespace<br>+static void<br>+GetLinuxProcessUserAndGroup (lldb::pid_t pid, ProcessInstanceInfo &process_info, lldb::pid_t &tracerpid)<br>+{<br>+ tracerpid = 0;<br>+ uint32_t rUid = UINT32_MAX; // Real User ID<br>
+ uint32_t eUid = UINT32_MAX; // Effective User ID<br>+ uint32_t rGid = UINT32_MAX; // Real Group ID<br>+ uint32_t eGid = UINT32_MAX; // Effective Group ID<br> <br>+ // Read the /proc/$PID/status file and parse the Uid:, Gid:, and TracerPid: fields.<br>
+ lldb::DataBufferSP buf_sp = ReadProcPseudoFile (pid, "status");<br>+<br>+ static const char uid_token[] = "Uid:";<br>+ char *buf_uid = strstr ((char *)buf_sp->GetBytes(), uid_token);<br>
+ if (buf_uid)<br>
+ {<br>+ // Real, effective, saved set, and file system UIDs. Read the first two.<br>+ buf_uid += sizeof(uid_token);<br>+ rUid = strtol (buf_uid, &buf_uid, 10);<br>+ eUid = strtol (buf_uid, &buf_uid, 10);<br>
+ }<br>+<br>+ static const char gid_token[] = "Gid:";<br>+ char *buf_gid = strstr ((char *)buf_sp->GetBytes(), gid_token);<br>+ if (buf_gid)<br>+ {<br>+ // Real, effective, saved set, and file system GIDs. Read the first two.<br>
+ buf_gid += sizeof(gid_token);<br>+ rGid = strtol (buf_gid, &buf_gid, 10);<br>+ eGid = strtol (buf_gid, &buf_gid, 10);<br>+ }<br>+<br>+ static const char tracerpid_token[] = "TracerPid:";<br>
+ char *buf_tracerpid = strstr((char *)buf_sp->GetBytes(), tracerpid_token);<br>+ if (buf_tracerpid)<br>+ {<br>+ // Tracer PID. 0 if we're not being debugged.<br>+ buf_tracerpid += sizeof(tracerpid_token);<br>
+ tracerpid = strtol (buf_tracerpid, &buf_tracerpid, 10);<br>+ }<br>+<br>+ process_info.SetUserID (rUid);<br>+ process_info.SetEffectiveUserID (eUid);<br>+ process_info.SetGroupID (rGid);<br>+ process_info.SetEffectiveGroupID (eGid);<br>
+}<br>+<br> bool<br> Host::GetOSVersion(uint32_t &major,<br> uint32_t &minor,<br>@@ -108,12 +225,79 @@<br> return ReadProcPseudoFile(process->GetID(), "auxv");<br> }<br> <br>+static bool<br>
+IsDirNumeric(const char *dname)<br>+{<br>+ for (; *dname; dname++)<br>+ {<br>+ if (!isdigit (*dname))<br>+ return false;<br>+ }<br>+ return true;<br>+}<br> <br>-bool<br>-Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)<br>
+uint32_t<br>+Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos)<br> {<br>+ static const char procdir[] = "/proc/";<br>+<br>+ DIR *dirproc = opendir (procdir);<br>
+ if (dirproc)<br>+ {<br>+ struct dirent *direntry = NULL;<br>+ const uid_t our_uid = getuid();<br>+ const lldb::pid_t our_pid = getpid();<br>+ bool all_users = match_info.GetMatchAllUsers();<br>
+<br>+ while ((direntry = readdir (dirproc)) != NULL)<br>+ {<br>+ if (direntry->d_type != DT_DIR || !IsDirNumeric (direntry->d_name))<br>+ continue;<br>+<br>+ lldb::pid_t pid = atoi (direntry->d_name);<br>
+<br>+ // Skip this process.<br>+ if (pid == our_pid)<br>+ continue;<br>+<br>+ lldb::pid_t tracerpid;<br>+ ProcessStatInfo stat_info;<br>+ ProcessInstanceInfo process_info;<br>
+<br>+ if (!GetProcessAndStatInfo (pid, process_info, stat_info, tracerpid))<br>+ continue;<br>+<br>+ // Skip if process is being debugged.<br>+ if (tracerpid != 0)<br>+ continue;<br>
+<br>+ // Skip zombies.<br>+ if (stat_info.fProcessState & eProcessStateZombie)<br>+ continue;<br>+<br>+ // Check for user match if we're not matching all users and not running as root.<br>
+ if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))<br>+ continue;<br>+<br>+ if (match_info.Matches (process_info))<br>+ {<br>+ process_infos.Append (process_info);<br>
+ }<br>+ }<br>+<br>+ closedir (dirproc);<br>+ }<br>+<br>+ return process_infos.GetSize();<br>+}<br>+<br>+static bool<br>+GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid)<br>
+{<br>+ tracerpid = 0;<br> process_info.Clear();<br>- process_info.SetProcessID(pid);<br>+ ::memset (&stat_info, 0, sizeof(stat_info));<br>+ stat_info.ppid = LLDB_INVALID_PROCESS_ID;<br> <br> // Architecture is intentionally omitted because that's better resolved<br>
// in other places (see ProcessPOSIX::DoAttachWithID().<br>@@ -121,19 +305,26 @@<br> // Use special code here because proc/[pid]/exe is a symbolic link.<br> char link_path[PATH_MAX];<br> char exe_path[PATH_MAX] = "";<br>
- if (snprintf(link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", pid) > 0)<br>+ if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", pid) <= 0)<br>+ return false;<br>
+<br>+ ssize_t len = readlink (link_path, exe_path, sizeof(exe_path) - 1);<br>+ if (len <= 0)<br>+ return false;<br>+<br>+ // readlink does not append a null byte.<br>+ exe_path[len] = 0;<br>+<br>+ // If the binary has been deleted, the link name has " (deleted)" appended.<br>
+ // Remove if there.<br>+ static const ssize_t deleted_len = strlen(" (deleted)");<br>+ if (len > deleted_len &&<br>+ !strcmp(exe_path + len - deleted_len, " (deleted)"))<br>
{<br>- ssize_t len = readlink(link_path, exe_path, sizeof(exe_path) - 1);<br>- if (len > 0)<br>- exe_path[len] = 0;<br>+ exe_path[len - deleted_len] = 0;<br>+ }<br> <br>- static const ssize_t deleted_len = strlen(" (deleted)");<br>
- if (len > deleted_len &&<br>- !strcmp(exe_path + len - deleted_len, " (deleted)"))<br>- {<br>- exe_path[len - deleted_len] = 0;<br>- }<br>- }<br>+ process_info.SetProcessID(pid);<br>
process_info.GetExecutableFile().SetFile(exe_path, false);<br> <br> lldb::DataBufferSP buf_sp;<br>@@ -166,11 +357,27 @@<br> next_arg += strlen(next_arg) + 1;<br> }<br> <br>- // FIXME: Parse /proc/<pid>/status to get uid, gid, euid, egid and parent_pid<br>
+ // Read /proc/$PID/stat to get our parent pid.<br>+ if (ReadProcPseudoFileStat (pid, stat_info))<br>+ {<br>+ process_info.SetParentProcessID (stat_info.ppid);<br>+ }<br> <br>+ // Get User and Group IDs and get tracer pid.<br>
+ GetLinuxProcessUserAndGroup (pid, process_info, tracerpid);<br>+<br> return true;<br> }<br> <br>+bool<br>+Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)<br>+{<br>+ lldb::pid_t tracerpid;<br>
+ ProcessStatInfo stat_info;<br>+<br>+ return GetProcessAndStatInfo (pid, process_info, stat_info, tracerpid);<br>+}<br>+<br> void<br> Host::ThreadCreated (const char *thread_name)<br> {<br><br></div></div>