From 0ca59c3431dcb07cc5de00e279c574459aa3bc34 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Thu, 19 Nov 2015 11:37:28 +0800 Subject: [PATCH] pthread: Add lwp_setname(2) and implement pthread_set_name_np(3) - Return thread name through kinfo. - Show thread name in ps(1) for 'Hc'. - Show thread name in top(1), if it is different from process name. --- bin/ps/extern.h | 2 +- bin/ps/print.c | 63 +++++++++++++++++--- bin/ps/ps.c | 3 +- include/unistd.h | 1 + lib/libc/sys/Makefile.inc | 2 +- lib/libc/sys/Symbol.map | 3 + lib/libc/sys/lwp_setname.2 | 82 ++++++++++++++++++++++++++ lib/libpthread/pthread_set_name_np.3 | 8 +-- lib/libthread_xu/thread/thr_info.c | 23 ++------ sys/kern/init_sysent.c | 1 + sys/kern/kern_kinfo.c | 2 + sys/kern/kern_prot.c | 38 ++++++++++++ sys/kern/syscalls.c | 1 + sys/kern/syscalls.master | 1 + sys/sys/kinfo.h | 2 + sys/sys/param.h | 3 +- sys/sys/syscall.h | 3 +- sys/sys/syscall.mk | 3 +- sys/sys/sysproto.h | 8 +++ sys/sys/sysunion.h | 1 + test/pthread/setname/Makefile | 8 +++ test/pthread/setname/pthread_setname.c | 59 ++++++++++++++++++ usr.bin/top/m_dragonfly.c | 13 +++- 23 files changed, 290 insertions(+), 40 deletions(-) create mode 100644 lib/libc/sys/lwp_setname.2 create mode 100644 test/pthread/setname/Makefile create mode 100644 test/pthread/setname/pthread_setname.c diff --git a/bin/ps/extern.h b/bin/ps/extern.h index b3190b31c5..565b03d976 100644 --- a/bin/ps/extern.h +++ b/bin/ps/extern.h @@ -40,7 +40,7 @@ struct nlist; struct var; struct varent; -extern int eval, fscale, mempages, nlistread, rawcpu, cflag; +extern int eval, fscale, mempages, nlistread, rawcpu, cflag, showtid; extern int sumrusage, termwidth, totwidth; extern int numcpus; extern STAILQ_HEAD(varent_head, varent) var_head; diff --git a/bin/ps/print.c b/bin/ps/print.c index 2bb98d05ef..035c4b7823 100644 --- a/bin/ps/print.c +++ b/bin/ps/print.c @@ -58,8 +58,12 @@ #include "ps.h" static const char *make_printable(const char *str); +static const char *make_printable2(const char *str); static void put64(u_int64_t n, int w, int type); +#define SHOW_THRNAME(k) \ + (showtid && KI_PROC(k, pid) != -1 && KI_PROC(k, nthreads) > 1) + void printheader(void) { @@ -100,11 +104,24 @@ command(const KINFO *k, const struct varent *vent) if (cflag) { /* Don't pad the last field. */ - if (STAILQ_NEXT(vent, link) == NULL) - printf("%s", make_printable(KI_PROC(k, comm))); - else - printf("%-*s", vent->width, - make_printable(KI_PROC(k, comm))); + if (STAILQ_NEXT(vent, link) == NULL) { + if (SHOW_THRNAME(k)) { + printf("%s/%s", + make_printable(KI_PROC(k, comm)), + make_printable2(KI_LWP(k, comm))); + } else { + printf("%s", make_printable(KI_PROC(k, comm))); + } + } else { + if (SHOW_THRNAME(k)) { + printf("%-*s/%s", vent->width, + make_printable(KI_PROC(k, comm)), + make_printable2(KI_LWP(k, comm))); + } else { + printf("%-*s", vent->width, + make_printable(KI_PROC(k, comm))); + } + } return; } @@ -163,10 +180,23 @@ void ucomm(const KINFO *k, const struct varent *vent) { /* Do not pad the last field */ - if (STAILQ_NEXT(vent, link) == NULL) - printf("%s", make_printable(KI_PROC(k, comm))); - else - printf("%-*s", vent->width, make_printable(KI_PROC(k, comm))); + if (STAILQ_NEXT(vent, link) == NULL) { + if (SHOW_THRNAME(k)) { + printf("%s/%s", make_printable(KI_PROC(k, comm)), + make_printable2(KI_LWP(k, comm))); + } else { + printf("%s", make_printable(KI_PROC(k, comm))); + } + } else { + if (SHOW_THRNAME(k)) { + printf("%-*s/%s", vent->width, + make_printable(KI_PROC(k, comm)), + make_printable2(KI_LWP(k, comm))); + } else { + printf("%-*s", vent->width, + make_printable(KI_PROC(k, comm))); + } + } } void @@ -711,6 +741,21 @@ make_printable(const char *str) return(cpy); } +static const char * +make_printable2(const char *str) +{ + static char *cpy2; + int len; + + if (cpy2) + free(cpy2); + len = strlen(str); + if ((cpy2 = malloc(len * 4 + 1)) == NULL) + err(1, NULL); + strvis(cpy2, str, VIS_TAB | VIS_NL | VIS_NOSLASH); + return(cpy2); +} + /* * Output a number, divide down as needed to fit within the * field. This function differs from the code used by systat diff --git a/bin/ps/ps.c b/bin/ps/ps.c index a98a399b33..ea6520531e 100644 --- a/bin/ps/ps.c +++ b/bin/ps/ps.c @@ -74,6 +74,7 @@ int sumrusage; /* -S */ int termwidth; /* width of screen (0 == infinity) */ int totwidth; /* calculated width of requested variables */ int numcpus; /* hw.ncpu */ +int showtid; /* -H */ static int needuser, needcomm, needenv; #if defined(LAZY_PS) @@ -121,7 +122,7 @@ main(int argc, char **argv) pid_t pid; uid_t *uids; int all, ch, flag, i, fmt, ofmt, lineno, nentries, nocludge, dropgid; - int prtheader, wflag, what, xflg, uid, nuids, showtid; + int prtheader, wflag, what, xflg, uid, nuids; int chainflg; char errbuf[_POSIX2_LINE_MAX]; const char *cp, *nlistf, *memf; diff --git a/include/unistd.h b/include/unistd.h index 0df93ec050..2f0aad9aba 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -557,6 +557,7 @@ int issetugid(void); long lpathconf(const char *, int); int lwp_create(struct lwp_params *); lwpid_t lwp_gettid(void); +int lwp_setname(lwpid_t, const char *); #ifndef _MKNOD_DECLARED int mknod(const char *, mode_t, dev_t); #define _MKNOD_DECLARED diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 2f5f8e184e..d9df58bea3 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -75,7 +75,7 @@ MAN+= _exit.2 accept.2 access.2 acct.2 adjtime.2 \ intro.2 ioctl.2 ioprio_get.2 issetugid.2 jail.2 jail_attach.2 kill.2 \ kldfind.2 kldfirstmod.2 kldload.2 kldnext.2 kldstat.2 kldsym.2 \ kldunload.2 ktrace.2 kqueue.2 link.2 listen.2 lseek.2 \ - lwp_create.2 lwp_gettid.2 lwp_kill.2 \ + lwp_create.2 lwp_gettid.2 lwp_kill.2 lwp_setname.2 \ madvise.2 mincore.2 minherit.2 mkdir.2 mkfifo.2 mknod.2 mlock.2 \ mlockall.2 mmap.2 \ modfind.2 modnext.2 modstat.2 \ diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index a8bf615538..2d2e54ed10 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -146,6 +146,7 @@ DF404.0 { lwp_gettid; lwp_kill; lwp_rtprio; + lwp_setname; madvise; mcontrol; mincore; @@ -442,6 +443,7 @@ DFprivate_1.0 { __sys_lwp_gettid; __sys_lwp_kill; __sys_lwp_rtprio; + __sys_lwp_setname; __sys_madvise; __sys_mcontrol; __sys_mincore; @@ -734,6 +736,7 @@ DFprivate_1.0 { _lwp_gettid; _lwp_kill; _lwp_rtprio; + _lwp_setname; _madvise; _mcontrol; _mincore; diff --git a/lib/libc/sys/lwp_setname.2 b/lib/libc/sys/lwp_setname.2 new file mode 100644 index 0000000000..07bc2580d8 --- /dev/null +++ b/lib/libc/sys/lwp_setname.2 @@ -0,0 +1,82 @@ +.\" Copyright (c) 2015 The DragonFly Project. All rights reserved. +.\" +.\" This code is derived from software contributed to The DragonFly Project +.\" by Sepherosa Ziehau . +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" 3. Neither the name of The DragonFly Project nor the names of its +.\" contributors may be used to endorse or promote products derived +.\" from this software without specific, prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, +.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd November 19, 2015 +.Dt LWP_SETNAME 2 +.Os +.Sh NAME +.Nm lwp_setname +.Nd Change lwp name +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn lwp_setname "lwpid_t tid" "const char *name" +.Sh DESCRIPTION +The +.Fn lwp_setname +system call sets the lwp name. +The lwp is in the process calling this system call, +and the lwp is specified by +.Fa tid . +If the +.Fa name +is +.Dv NULL , +the lwp name will be set to the process name, +which is the default name of the lwp. +The +.Fa name +will be truncated, +if it is longer than +.Dv MAXCOMLEN . +.Sh RETURN VALUES +This system call returns \-1 on error and +0 upon successful completion. +.Sh ERRORS +The +.Fn lwp_setname +system call will fail if: +.Bl -tag -width Er +.It Bq Er EFAULT +The +.Fa name +parameter is outside the process's allocated address space. +.Sh SEE ALSO +.Xr lwp_create 2 +.Xr setproctitle 3 +.Sh HISTORY +The +.Fn lwp_setname +function first appeared in +.Dx 4.3 . diff --git a/lib/libpthread/pthread_set_name_np.3 b/lib/libpthread/pthread_set_name_np.3 index da251191e4..123fa08623 100644 --- a/lib/libpthread/pthread_set_name_np.3 +++ b/lib/libpthread/pthread_set_name_np.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD: src/share/man/man3/pthread_set_name_np.3,v 1.6 2007/10/22 10:08:01 ru Exp $ .\" -.Dd July 10, 2009 +.Dd November 19, 2015 .Dt PTHREAD_SET_NAME_NP 3 .Os .Sh NAME @@ -45,12 +45,8 @@ function sets internal name for thread specified by argument to string value specified by .Fa name argument. -.Pp -This is a debugging interface and using it on a day-by-day basis makes -no sense. .Sh ERRORS -Because of the debugging nature of this function, all errors that may -appear inside are silently ignored. +All errors that may appear inside are silently ignored. .Sh AUTHORS This manual page was written by .An Alexey Zelkin Aq Mt phantom@FreeBSD.org . diff --git a/lib/libthread_xu/thread/thr_info.c b/lib/libthread_xu/thread/thr_info.c index 01c7e8d767..112ad751bf 100644 --- a/lib/libthread_xu/thread/thr_info.c +++ b/lib/libthread_xu/thread/thr_info.c @@ -33,6 +33,7 @@ #include "namespace.h" #include #include +#include #include #include #include "un-namespace.h" @@ -43,31 +44,19 @@ __weak_reference(_pthread_set_name_np, pthread_set_name_np); /* Set the thread name for debug. */ void -_pthread_set_name_np(pthread_t thread __unused, const char *name __unused) +_pthread_set_name_np(pthread_t thread, const char *name) { -#if 0 - struct pthread *curthread = _get_curthread(); - int ret = 0; + struct pthread *curthread = tls_get_curthread(); if (curthread == thread) { - if (thr_set_name(thread->tid, name)) - ret = errno; + lwp_setname(thread->tid, name); } else { if (_thr_ref_add(curthread, thread, 0) == 0) { THR_THREAD_LOCK(curthread, thread); - if (thread->state != PS_DEAD) { - if (thr_set_name(thread->tid, name)) - ret = errno; - } + if (thread->state != PS_DEAD) + lwp_setname(thread->tid, name); THR_THREAD_UNLOCK(curthread, thread); _thr_ref_delete(curthread, thread); - } else { - ret = ESRCH; } } -#if 0 - /* XXX should return error code. */ - return (ret); -#endif -#endif } diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index 5868782898..bc784d05b3 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -567,4 +567,5 @@ struct sysent sysent[] = { { AS(utimensat_args), (sy_call_t *)sys_utimensat }, /* 539 = utimensat */ { AS(futimens_args), (sy_call_t *)sys_futimens }, /* 540 = futimens */ { AS(accept4_args), (sy_call_t *)sys_accept4 }, /* 541 = accept4 */ + { AS(lwp_setname_args), (sy_call_t *)sys_lwp_setname }, /* 542 = lwp_setname */ }; diff --git a/sys/kern/kern_kinfo.c b/sys/kern/kern_kinfo.c index 4ee9dff837..26ad76e1a8 100644 --- a/sys/kern/kern_kinfo.c +++ b/sys/kern/kern_kinfo.c @@ -227,6 +227,7 @@ fill_kinfo_lwp(struct lwp *lwp, struct kinfo_lwp *kl) strncpy(kl->kl_wmesg, lwp->lwp_thread->td_wmesg, WMESGLEN); kl->kl_wmesg[WMESGLEN] = 0; } + strlcpy(kl->kl_comm, lwp->lwp_thread->td_comm, sizeof(kl->kl_comm)); } /* @@ -279,4 +280,5 @@ fill_kinfo_proc_kthread(struct thread *td, struct kinfo_proc *kp) strncpy(kp->kp_lwp.kl_wmesg, td->td_wmesg, WMESGLEN); kp->kp_lwp.kl_wmesg[WMESGLEN] = 0; } + strlcpy(kp->kp_lwp.kl_comm, td->td_comm, sizeof(kp->kp_lwp.kl_comm)); } diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 48ecd328b6..ba5b3ced6c 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -246,6 +246,44 @@ sys_getgroups(struct getgroups_args *uap) return (error); } +int +sys_lwp_setname(struct lwp_setname_args *uap) +{ + struct proc *p = curproc; + char comm0[MAXCOMLEN + 1]; + const char *comm = NULL; + struct lwp *lp; + int error; + + if (uap->name != NULL) { + error = copyinstr(uap->name, comm0, sizeof(comm0), NULL); + if (error) { + if (error != ENAMETOOLONG) + return error; + /* Truncate */ + comm0[MAXCOMLEN] = '\0'; + } + comm = comm0; + } else { + /* Restore to the default name, i.e. process name. */ + comm = p->p_comm; + } + + lwkt_gettoken(&p->p_token); + + lp = lwp_rb_tree_RB_LOOKUP(&p->p_lwp_tree, uap->tid); + if (lp != NULL) { + strlcpy(lp->lwp_thread->td_comm, comm, + sizeof(lp->lwp_thread->td_comm)); + error = 0; + } else { + error = ESRCH; + } + + lwkt_reltoken(&p->p_token); + return error; +} + int sys_setsid(struct setsid_args *uap) { diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 77d5033b2a..f9170b8470 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -550,4 +550,5 @@ const char *syscallnames[] = { "utimensat", /* 539 = utimensat */ "futimens", /* 540 = futimens */ "accept4", /* 541 = accept4 */ + "lwp_setname", /* 542 = lwp_setname */ }; diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 5baaaf103c..11ff4d5a72 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -745,3 +745,4 @@ 539 STD { int utimensat(int fd, const char *path, const struct timespec *ts, int flags); } 540 STD { int futimens(int fd, const struct timespec *ts); } 541 STD { int accept4(int s, caddr_t name, int *anamelen, int flags); } +542 STD { int lwp_setname(lwpid_t tid, const char *name); } diff --git a/sys/sys/kinfo.h b/sys/sys/kinfo.h index ddf87312e3..5bb82e5b4c 100644 --- a/sys/sys/kinfo.h +++ b/sys/sys/kinfo.h @@ -141,6 +141,8 @@ struct kinfo_lwp { #define WMESGLEN 8 uintptr_t kl_wchan; /* waiting channel */ char kl_wmesg[WMESGLEN+1]; /* waiting message */ + + char kl_comm[MAXCOMLEN+1]; /* lwp name */ }; /* diff --git a/sys/sys/param.h b/sys/sys/param.h index a1eb427778..ca05088932 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -131,9 +131,10 @@ * 400306 - Add libexecinfo to base * 400307 - drm/i915 kernel module renamed to i915.ko * 400308 - removal + * 400309 - Add lwp_setname() system call */ #undef __DragonFly_version -#define __DragonFly_version 400308 /* propagated to newvers */ +#define __DragonFly_version 400309 /* propagated to newvers */ #include diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index a2563ee401..9137581b18 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -375,4 +375,5 @@ #define SYS_utimensat 539 #define SYS_futimens 540 #define SYS_accept4 541 -#define SYS_MAXSYSCALL 542 +#define SYS_lwp_setname 542 +#define SYS_MAXSYSCALL 543 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index e4ebfb33d7..7bc9d2dcb1 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -304,4 +304,5 @@ MIASM = \ pipe2.o \ utimensat.o \ futimens.o \ - accept4.o + accept4.o \ + lwp_setname.o diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index eacd36f9d3..cc2da299fa 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -2308,6 +2308,13 @@ struct accept4_args { int * anamelen; char anamelen_[PAD_(int *)]; int flags; char flags_[PAD_(int)]; }; +struct lwp_setname_args { +#ifdef _KERNEL + struct sysmsg sysmsg; +#endif + lwpid_t tid; char tid_[PAD_(lwpid_t)]; + const char * name; char name_[PAD_(const char *)]; +}; #ifdef COMPAT_43 @@ -2855,6 +2862,7 @@ int sys_pipe2 (struct pipe2_args *); int sys_utimensat (struct utimensat_args *); int sys_futimens (struct futimens_args *); int sys_accept4 (struct accept4_args *); +int sys_lwp_setname (struct lwp_setname_args *); #endif /* !_SYS_SYSPROTO_H_ */ #undef PAD_ diff --git a/sys/sys/sysunion.h b/sys/sys/sysunion.h index 4692d6c7ab..dfdecf5977 100644 --- a/sys/sys/sysunion.h +++ b/sys/sys/sysunion.h @@ -394,4 +394,5 @@ union sysunion { struct utimensat_args utimensat; struct futimens_args futimens; struct accept4_args accept4; + struct lwp_setname_args lwp_setname; }; diff --git a/test/pthread/setname/Makefile b/test/pthread/setname/Makefile new file mode 100644 index 0000000000..f7120353eb --- /dev/null +++ b/test/pthread/setname/Makefile @@ -0,0 +1,8 @@ +PROG = pthread_setname +WARNS = 6 +NOMAN = YES +BINDIR= /usr/local/bin + +LDADD = -lpthread + +.include diff --git a/test/pthread/setname/pthread_setname.c b/test/pthread/setname/pthread_setname.c new file mode 100644 index 0000000000..baef5115c1 --- /dev/null +++ b/test/pthread/setname/pthread_setname.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include + +static void * +setselfname(void *arg __unused) +{ + pthread_set_name_np(pthread_self(), __func__); + pause(); + return NULL; +} + +static void * +waitname(void *arg __unused) +{ + pause(); + return NULL; +} + +static void * +resetname(void *arg __unused) +{ + pthread_set_name_np(pthread_self(), __func__); + sleep(10); + pthread_set_name_np(pthread_self(), NULL); + pause(); + return NULL; +} + +int +main(void) +{ + pthread_t tid1, tid2, tid3; + char longname[256]; + int error; + + error = pthread_create(&tid1, NULL, setselfname, NULL); + if (error) + errc(1, error, "pthread_create(setselfname) failed"); + + error = pthread_create(&tid2, NULL, waitname, NULL); + if (error) + errc(1, error, "pthread_create(waitname) failed"); + pthread_set_name_np(tid2, "waitname"); + + error = pthread_create(&tid3, NULL, resetname, NULL); + if (error) + errc(1, error, "pthread_create(resetname) failed"); + + memset(longname, 'x', sizeof(longname)); + longname[sizeof(longname) - 1] = '\0'; + pthread_set_name_np(pthread_self(), longname); + + pause(); + exit(0); +} diff --git a/usr.bin/top/m_dragonfly.c b/usr.bin/top/m_dragonfly.c index 37ac314453..7c0ffed24a 100644 --- a/usr.bin/top/m_dragonfly.c +++ b/usr.bin/top/m_dragonfly.c @@ -69,6 +69,7 @@ int n_cpus = 0; struct handle { struct kinfo_proc **next_proc; /* points to next valid proc pointer */ int remaining; /* number of pointers remaining */ + int show_threads; }; /* declarations for load_avg */ @@ -624,6 +625,7 @@ get_process_info(struct system_info *si, struct process_select *sel, /* pass back a handle */ handle.next_proc = pref; handle.remaining = active_procs; + handle.show_threads = show_threads; return ((caddr_t) & handle); } @@ -666,9 +668,16 @@ format_next_process(caddr_t xhandle, char *(*get_userid) (int)) if (PP(pp, flags) & P_SYSTEM) { /* system process */ snprintf(cmdfield, sizeof cmdfield, "[%s]", comm); - } else if (LP(pp, tid) > 0) { + } else if (hp->show_threads && PP(pp, nthreads) > 1) { /* display it as a thread */ - snprintf(cmdfield, sizeof cmdfield, "%s{%d}", comm, LP(pp, tid)); + if (strcmp(PP(pp, comm), LP(pp, comm)) == 0) { + snprintf(cmdfield, sizeof cmdfield, "%s{%d}", comm, + LP(pp, tid)); + } else { + /* show thread name in addition to tid */ + snprintf(cmdfield, sizeof cmdfield, "%s{%d/%s}", comm, + LP(pp, tid), LP(pp, comm)); + } } else { snprintf(cmdfield, sizeof cmdfield, "%s", comm); } -- 2.41.0