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;
+}
diff --git a/libc/include/pthread.h b/libc/include/pthread.h
index 6603b3f..c0aa4b9 100644
--- a/libc/include/pthread.h
+++ b/libc/include/pthread.h
@@ -226,6 +226,8 @@
 
 int pthread_once(pthread_once_t  *once_control, void (*init_routine)(void));
 
+int pthread_setname_np(pthread_t thid, const char *thname);
+
 typedef void  (*__pthread_cleanup_func_t)(void*);
 
 typedef struct __pthread_cleanup_t {
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index 1ada37e..73b7f41 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -185,6 +185,12 @@
 extern pid_t tcgetpgrp(int fd);
 extern int   tcsetpgrp(int fd, pid_t _pid);
 
+#define TEMP_FAILURE_RETRY(expr) \
+    ({ long int __ret;                            \
+       do __ret = (long int)(expr);               \
+       while (__ret == -1L && errno == EINTR);    \
+       __ret; })
+
 __END_DECLS
 
 #endif /* _UNISTD_H_ */