diff options
| author | Jason King <jason.king@joyent.com> | 2017-10-02 15:24:43 +0000 |
|---|---|---|
| committer | Jason King <jason.king@joyent.com> | 2017-10-05 14:42:41 +0000 |
| commit | 3df01e00bc5ef3f882d18e8188e4165831f8b694 (patch) | |
| tree | e26d5ba9406e2932cb252786f825fc4ce3e8a29f | |
| parent | 6dda4c2c4460e45b80f917d3676f5ce39e479cac (diff) | |
| download | illumos-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.c | 13 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/thread.c | 55 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/thread.h | 4 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/syscall/lx_prctl.c | 73 | ||||
| -rw-r--r-- | usr/src/uts/common/disp/thread.c | 53 | ||||
| -rw-r--r-- | usr/src/uts/common/sys/thread.h | 6 |
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 |
