summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason King <jason.king@joyent.com>2017-10-02 15:24:43 +0000
committerJason King <jason.king@joyent.com>2017-10-05 14:42:41 +0000
commit3df01e00bc5ef3f882d18e8188e4165831f8b694 (patch)
treee26d5ba9406e2932cb252786f825fc4ce3e8a29f
parent6dda4c2c4460e45b80f917d3676f5ce39e479cac (diff)
downloadillumos-joyent-3df01e00bc5ef3f882d18e8188e4165831f8b694.tar.gz
OS-6366 lx: need support for PR_GET_NAME
Reviewed by: Dan McDonald <danmcd@joyent.com> Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Reviewed by: Robert Mustacchi <rm@joyent.com> Approved by: Jerry Jelinek <jerry.jelinek@joyent.com>
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c13
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/thread.c55
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/thread.h4
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_prctl.c73
-rw-r--r--usr/src/uts/common/disp/thread.c53
-rw-r--r--usr/src/uts/common/sys/thread.h6
6 files changed, 185 insertions, 19 deletions
diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index 3ffe53e336..9da3c5eba0 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -21,7 +21,7 @@
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2017 Joyent, Inc.
+ * Copyright (c) 2017, Joyent, Inc.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
@@ -160,8 +160,15 @@ ps_threadprint(uintptr_t addr, const void *data, void *private)
if (prt_flags & PS_PRTTHREADS)
mdb_printf("\tT %?a <%b>\n", addr, t->t_state, t_state_bits);
- if (prt_flags & PS_PRTLWPS)
- mdb_printf("\tL %?a ID: %u\n", t->t_lwp, t->t_tid);
+ if (prt_flags & PS_PRTLWPS) {
+ char name[THREAD_NAME_MAX];
+
+ mdb_printf("\tL %?a ID: %u", t->t_lwp, t->t_tid);
+ if (thread_getname(addr, name, sizeof (name))) {
+ mdb_printf(" NAME: %s", name);
+ }
+ mdb_printf("\n");
+ }
return (WALK_NEXT);
}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/thread.c b/usr/src/cmd/mdb/common/modules/genunix/thread.c
index 652e5af2d6..811f29ec66 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/thread.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/thread.c
@@ -24,11 +24,13 @@
*/
/*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_ks.h>
+#include <mdb/mdb_ctf.h>
#include <sys/types.h>
#include <sys/thread.h>
#include <sys/lwp.h>
@@ -38,6 +40,7 @@
#include <sys/disp.h>
#include <sys/taskq_impl.h>
#include <sys/stack.h>
+#include "thread.h"
#ifndef STACK_BIAS
#define STACK_BIAS 0
@@ -659,7 +662,12 @@ threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
else
mdb_printf(" %a()\n", t.t_startpc);
} else {
- mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid);
+ char name[THREAD_NAME_MAX];
+
+ mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
+ if (thread_getname(addr, name, sizeof (name)))
+ mdb_printf(" [%s]", name);
+ mdb_printf("\n");
}
}
@@ -1002,6 +1010,49 @@ stackinfo_help(void)
"-h shows history, dead kthreads that used their "
"kernel stack the most\n");
mdb_printf(
- "\nSee Solaris Modular Debugger Guide for detailed usage.\n");
+ "\nSee illumos Modular Debugger Guide for detailed usage.\n");
mdb_flush();
}
+
+/* If the field is not present in the target, return an empty (0 length) name */
+boolean_t
+thread_getname(uintptr_t addr, char *namep, size_t namelen)
+{
+ mdb_ctf_id_t id;
+ ulong_t offset;
+ uintptr_t nameaddr;
+
+ bzero(namep, namelen);
+
+ if (mdb_ctf_lookup_by_name("kthread_t", &id) == -1)
+ return (B_FALSE);
+
+ if (mdb_ctf_offsetof(id, "t_name", &offset) == -1)
+ return (B_FALSE);
+
+ if (offset % 8 != 0) {
+ mdb_warn("kthread_t.t_name is not on a byte boundary");
+ return (B_FALSE);
+ }
+ offset /= 8;
+
+ if (mdb_vread(&nameaddr, sizeof (nameaddr), addr + offset) !=
+ sizeof (nameaddr)) {
+ mdb_warn("could not read address of thread name buffer");
+ return (B_FALSE);
+ }
+
+ if (nameaddr != 0 && mdb_readstr(namep, namelen, addr + offset) == -1) {
+ mdb_warn("error reading thread name");
+ /*
+ * Just to be safe -- if mdb_readstr() succeeds, it always
+ * NUL terminates the output, but is unclear what it does
+ * on failure. In that case we attempt to show any partial
+ * content w/ the warning in case it's useful, but explicity
+ * NUL terminate to be safe.
+ */
+ namep[namelen - 1] = '\0';
+ }
+
+ return (strlen(namep) > 0 ? B_TRUE : B_FALSE);
+}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/thread.h b/usr/src/cmd/mdb/common/modules/genunix/thread.h
index 18952fdc0a..9763781008 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/thread.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/thread.h
@@ -21,6 +21,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright (c) 2017, Joyent, Inc.
*/
#ifndef _THREAD_H
@@ -58,6 +60,8 @@ void thread_state_to_text(uint_t, char *, size_t);
int thread_text_to_state(const char *, uint_t *);
void thread_walk_states(void (*)(uint_t, const char *, void *), void *);
+boolean_t thread_getname(uintptr_t, char *, size_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_prctl.c b/usr/src/uts/common/brand/lx/syscall/lx_prctl.c
index 23257245f0..3889f0f615 100644
--- a/usr/src/uts/common/brand/lx/syscall/lx_prctl.c
+++ b/usr/src/uts/common/brand/lx/syscall/lx_prctl.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2016 Joyent, Inc.
+ * Copyright (c) 2017, Joyent, Inc.
*/
#include <sys/systm.h>
@@ -133,29 +133,76 @@ lx_prctl(int opt, uintptr_t data)
return (0);
}
+ case LX_PR_GET_NAME: {
+ /*
+ * We allow longer thread names than Linux for compatibility
+ * with other OSes (Solaris, NetBSD) that also allow larger
+ * names. We just truncate (with NUL termination) if
+ * the name is longer.
+ */
+ char name[LX_PR_SET_NAME_NAMELEN] = { 0 };
+ kthread_t *t = curthread;
+
+ mutex_enter(&ttoproc(t)->p_lock);
+ if (t->t_name != NULL) {
+ (void) strlcpy(name, t->t_name, sizeof (name));
+ }
+ mutex_exit(&ttoproc(t)->p_lock);
+
+ /*
+ * FWIW, the prctl(2) manpage says that the user-supplied
+ * buffer should be at least 16 (LX_PR_SET_NAME_NAMELEN) bytes
+ * long.
+ */
+ if (copyout(name, (void *)data, LX_PR_SET_NAME_NAMELEN) != 0) {
+ return (set_errno(EFAULT));
+ }
+ return (0);
+ }
+
case LX_PR_SET_NAME: {
- char name[LX_PR_SET_NAME_NAMELEN + 1];
- proc_t *p = curproc;
+ char name[LX_PR_SET_NAME_NAMELEN] = { 0 };
+ kthread_t *t = curthread;
+ proc_t *p = ttoproc(t);
+ int ret;
+
+ ret = copyinstr((const char *)data, name, sizeof (name), NULL);
+ /*
+ * prctl(2) explicitly states that over length strings are
+ * silently truncated
+ */
+ if (ret != 0 && ret != ENAMETOOLONG) {
+ return (set_errno(EFAULT));
+ }
+ name[LX_PR_SET_NAME_NAMELEN - 1] = '\0';
+
+ thread_setname(t, name);
+
/*
* In Linux, PR_SET_NAME sets the name of the thread, not the
* process. Due to the historical quirks of Linux's asinine
* thread model, this name is effectively the name of the
* process (as visible via ps(1)) if the thread is the first of
* its task group. The first thread is therefore special, and
- * to best mimic Linux semantics (and absent a notion of
- * per-LWP names), we do nothing (but return success) on LWPs
- * other than LWP 1.
+ * to best mimic Linux semantics we set the thread name, and if
+ * we are setting LWP 1, we also update the name of the process.
*/
- if (curthread->t_tid != 1) {
+ if (t->t_tid != 1) {
return (0);
}
- if (copyin((void *)data, name, LX_PR_SET_NAME_NAMELEN) != 0) {
- return (set_errno(EFAULT));
- }
- name[LX_PR_SET_NAME_NAMELEN] = '\0';
+
+ /*
+ * We explicitly use t->t_name here instead of name in case
+ * a thread has come in between the above thread_setname()
+ * call and the setting of u_comm/u_psargs below. On Linux,
+ * one can also change the name of a thread (either itself or
+ * another thread in the same process) via writing to /proc, so
+ * while racy, this is no worse than what might happen on
+ * Linux.
+ */
mutex_enter(&p->p_lock);
- (void) strncpy(p->p_user.u_comm, name, MAXCOMLEN + 1);
- (void) strncpy(p->p_user.u_psargs, name, PSARGSZ);
+ (void) strncpy(p->p_user.u_comm, t->t_name, MAXCOMLEN + 1);
+ (void) strncpy(p->p_user.u_psargs, t->t_name, PSARGSZ);
mutex_exit(&p->p_lock);
return (0);
}
diff --git a/usr/src/uts/common/disp/thread.c b/usr/src/uts/common/disp/thread.c
index ae6c5eef16..0312cc0c8c 100644
--- a/usr/src/uts/common/disp/thread.c
+++ b/usr/src/uts/common/disp/thread.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -786,6 +786,11 @@ thread_free(kthread_t *t)
nthread--;
mutex_exit(&pidlock);
+ if (t->t_name != NULL) {
+ kmem_free(t->t_name, THREAD_NAME_MAX);
+ t->t_name = NULL;
+ }
+
/*
* Free thread, lwp and stack. This needs to be done carefully, since
* if T_TALLOCSTK is set, the thread is part of the stack.
@@ -2227,3 +2232,49 @@ stkinfo_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp)
}
return (percent);
}
+
+/*
+ * NOTE: This will silently truncate a name > THREAD_NAME_MAX - 1 characters
+ * long. It is expected that callers (acting on behalf of userland clients)
+ * will perform any required checks to return the correct error semantics.
+ * It is also expected callers on behalf of userland clients have done
+ * any necessary permission checks.
+ */
+void
+thread_setname(kthread_t *t, const char *name)
+{
+ char *buf = NULL;
+
+ /*
+ * We optimistically assume that a thread's name will only be set
+ * once and so allocate memory in preparation of setting t_name.
+ * If it turns out a name has already been set, we just discard (free)
+ * the buffer we just allocated and reuse the current buffer
+ * (as all should be THREAD_NAME_MAX large).
+ *
+ * Such an arrangement means over the lifetime of a kthread_t, t_name
+ * is either NULL or has one value (the address of the buffer holding
+ * the current thread name). The assumption is that most kthread_t
+ * instances will not have a name assigned, so dynamically allocating
+ * the memory should minimize the footprint of this feature, but by
+ * having the buffer persist for the life of the thread, it simplifies
+ * usage in highly constrained situations (e.g. dtrace).
+ */
+ if (name != NULL && name[0] != '\0') {
+ buf = kmem_zalloc(THREAD_NAME_MAX, KM_SLEEP);
+ (void) strlcpy(buf, name, THREAD_NAME_MAX);
+ }
+
+ mutex_enter(&ttoproc(t)->p_lock);
+ if (t->t_name == NULL) {
+ t->t_name = buf;
+ } else {
+ if (buf != NULL) {
+ (void) strlcpy(t->t_name, name, THREAD_NAME_MAX);
+ kmem_free(buf, THREAD_NAME_MAX);
+ } else {
+ bzero(t->t_name, THREAD_NAME_MAX);
+ }
+ }
+ mutex_exit(&ttoproc(t)->p_lock);
+}
diff --git a/usr/src/uts/common/sys/thread.h b/usr/src/uts/common/sys/thread.h
index 73aa768d39..63db0fc9e9 100644
--- a/usr/src/uts/common/sys/thread.h
+++ b/usr/src/uts/common/sys/thread.h
@@ -351,6 +351,8 @@ typedef struct _kthread {
kmutex_t t_ctx_lock; /* protects t_ctx in removectx() */
struct waitq *t_waitq; /* wait queue */
kmutex_t t_wait_mutex; /* used in CV wait functions */
+
+ char *t_name; /* thread name */
} kthread_t;
/*
@@ -601,11 +603,15 @@ extern disp_lock_t stop_lock; /* lock protecting stopped threads */
caddr_t thread_stk_init(caddr_t); /* init thread stack */
+void thread_setname(kthread_t *, const char *);
+
extern int default_binding_mode;
extern int default_stksize;
#endif /* _KERNEL */
+#define THREAD_NAME_MAX 32 /* includes terminating NUL */
+
/*
* Macros to indicate that the thread holds resources that could be critical
* to other kernel threads, so this thread needs to have kernel priority