<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>