pthread: introduce pthread_setname_np() as a mean to give names to threads

... so that each cloned process at the kernel level can be named
independently. Tools like 'top' can display the CPU/memory statistics
for each process's thread if "Show Threads" mode is on.

With this function in place, we can convert dalvik/Thread.c setThreadName()
function over this function. This feature ought to be provided by the
underlying C library and not coded directly in Dalvik.

Change-Id: Ifa997665dbaa114e0b126f8c667708be9a4137fd
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c
index 7d4056d..666a6b8 100644
--- a/libc/bionic/pthread.c
+++ b/libc/bionic/pthread.c
@@ -43,6 +43,9 @@
 #include <memory.h>
 #include <assert.h>
 #include <malloc.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 extern int  __pthread_clone(int (*fn)(void*), void *child_stack, int flags, void *arg);
 extern void _exit_with_stack_teardown(void * stackBase, int stackSize, int retCode);
@@ -1721,3 +1724,54 @@
     }
     return 0;
 }
+
+/* This value is not exported by kernel headers, so hardcode it here */
+#define MAX_TASK_COMM_LEN	16
+#define TASK_COMM_FMT 		"/proc/self/task/%u/comm"
+
+int pthread_setname_np(pthread_t thid, const char *thname)
+{
+    size_t thname_len;
+    int saved_errno, ret;
+
+    if (thid == 0 || thname == NULL)
+        return EINVAL;
+
+    thname_len = strlen(thname);
+    if (thname_len >= MAX_TASK_COMM_LEN)
+        return ERANGE;
+
+    saved_errno = errno;
+    if (thid == pthread_self())
+    {
+        ret = prctl(PR_SET_NAME, (unsigned long)thname, 0, 0, 0) ? errno : 0;
+    }
+    else
+    {
+        /* Have to change another thread's name */
+        pthread_internal_t *thread = (pthread_internal_t *)thid;
+        char comm_name[sizeof(TASK_COMM_FMT) + 8];
+        ssize_t n;
+        int fd;
+
+        snprintf(comm_name, sizeof(comm_name), TASK_COMM_FMT, (unsigned int)thread->kernel_id);
+        fd = open(comm_name, O_RDWR);
+        if (fd == -1)
+        {
+            ret = errno;
+            goto exit;
+        }
+        n = TEMP_FAILURE_RETRY(write(fd, thname, thname_len));
+        close(fd);
+
+        if (n < 0)
+            ret = errno;
+        else if ((size_t)n != thname_len)
+            ret = EIO;
+        else
+            ret = 0;
+    }
+exit:
+    errno = saved_errno;
+    return ret;
+}