How do I change the name of one single thread in Linux?
Asked Answered
E

1

0

I've found this post about changing the name of a thread.

I tried the prctl() and pthread_setname_np() functions. Both change the name of ALL my threads. In other words, it doesn't seem to work as expected.

I used:

pthread_setname_np(pthread_self(), "thread ONE");

and

pthread_setname_np(pthread_self(), "thread TWO");

Depending on which runs first, both threads say "thread ONE" or "thread TWO". I was expecting one of them to be "thread ONE" and the other to be "thread TWO".

Am I doing something wrong?


As proposed by Tzig in a comment, I tested the example as shown in the pthread_setname_np() documentation. However, I needed to test with at least two threads so I changed the code as follow to have a thread1 and thread2.

By default, I can start htop and use F4 to only show threads/processes with names including THREAD (I can also use the command line to use a different name: ./a.out MULTIFOO MULTIBAR and then use the word MULTI as the filter).

#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

#define NAMELEN 16

#define errExitEN(en, msg) \
                       do { errno = en; perror(msg); \
                            exit(EXIT_FAILURE); } while (0)

static void *
threadfunc(void *parm)
{
   sleep(15);          // allow main program to set the thread name
   return NULL;
}

int
main(int argc, char **argv)
{
   pthread_t thread1, thread2;
   int rc;
   char thread_name[NAMELEN];

   rc = pthread_create(&thread1, NULL, threadfunc, NULL);
   if (rc != 0)
       errExitEN(rc, "pthread_create");

   rc = pthread_create(&thread2, NULL, threadfunc, NULL);
   if (rc != 0)
       errExitEN(rc, "pthread_create");


   // test name of thread1
   //
   rc = pthread_getname_np(thread1, thread_name, NAMELEN);
   if (rc != 0)
       errExitEN(rc, "pthread_getname_np");
   printf("Created thread1. Default name is: %s\n", thread_name);

   rc = pthread_setname_np(thread1, (argc > 1) ? argv[1] : "THREADFOO");
   if (rc != 0)
       errExitEN(rc, "pthread_setname_np");

   sleep(2);

   rc = pthread_getname_np(thread1, thread_name, NAMELEN);
   if (rc != 0)
       errExitEN(rc, "pthread_getname_np");
   printf("The thread1 name after setting it is %s.\n", thread_name);


   // test name of thread2
   //
   rc = pthread_getname_np(thread2, thread_name, NAMELEN);
   if (rc != 0)
       errExitEN(rc, "pthread_getname_np");
   printf("Created thread2. Default name is: %s\n", thread_name);

   rc = pthread_setname_np(thread2, (argc > 2) ? argv[2] : "THREADBAR");
   if (rc != 0)
       errExitEN(rc, "pthread_setname_np");

   sleep(2);

   rc = pthread_getname_np(thread2, thread_name, NAMELEN);
   if (rc != 0)
       errExitEN(rc, "pthread_getname_np");
   printf("The thread2 name after setting it is %s.\n", thread_name);


   // thread1 name changed too?
   //
   rc = pthread_getname_np(thread1, thread_name, NAMELEN);
   if (rc != 0)
       errExitEN(rc, "pthread_getname_np");
   printf("The thread1 name after setting thread2 name is %s.\n", thread_name);


   rc = pthread_join(thread1, NULL);
   if (rc != 0)
       errExitEN(rc, "pthread_join");

   rc = pthread_join(thread2, NULL);
   if (rc != 0)
       errExitEN(rc, "pthread_join");

   printf("Done\n");
   exit(EXIT_SUCCESS);
}
P.S. There is a bug in the original:
rc = pthread_getname_np(thread, thread_name,
                               (argc > 2) ? atoi(argv[1]) : NAMELEN);

Notice that the atoi() uses argv[1] instead of argv[2]. (I have reported the bug to the man-pages maintainers.)

In my example, I use the second argument as the name of the second thread and always use NAMELEN as the length of my buffer. I have no reason for reducing that amount.

RESULTS:

As expected, the pthread_getname_np() works. Great!

However, the htop or cat /proc/self/task/<tid>/comm all return the last name that was set. I guess that's a bug in the Linux kernel... Yet, my process has other threads created by the NVidia driver and those have different names.


Just in case, I tried the functions found in Linux - how to change info of forked processes in C which did seem wrong since it says "fork()'ed". But since each task has its own entry under /proc... but the issue, I suppose, is that the threads share the same memory as their main process and there is only one location for the argv[0] data. In other words, they implemented a pthread_setname_np() which works internally, but does not reflect that name in tools such as ps and htop.

Extracurricular answered 6/8, 2021 at 5:8 Comment(3)
Does the example at pthread_setname_np(3) work on your machine?Vociferance
@Vociferance I added a code sample and tweaked it so it would have two threads (it works with just one, my issue is setting the name of one thread and not have names of the other threads changed at the same time).Extracurricular
Maybe you'd like to strace the program to see what the NVidia threads are doing.Clabo
E
1

Okay, I found out how to make it work. You want to write the name in the proc file directly. Than it works as expected. Each thread gets its own name.

First, we need to know the thread identifier (it's number, not the pthread_id). Under Linux, you can get that information with the following function:

pid_t gettid()
{
    return static_cast<pid_t>(syscall(SYS_gettid));
}

Now to setup the thread name open the comm file in write mode and write the name there:

void set_thread_name(std::string const & name)
{
    if(name.length() > 15)
        throw std::range_error("thread name is limited to 15 chars");

    pid_t const tid(gettid());
    std::ofstream comm("/proc/" + std::to_string(tid) + "/comm");
    comm << name;
}

I use C++ which simplifies things. You can, of course, do the same thing in C:

void set_thread_name(const char * name)
{
    pid_t tid;
    char filename[6 + 5 + 5 + 1];

    if(strlen(name) > 15)
    {
        errno = EINVAL;
        return -1;
    }

    tid = gettid();
    snprintf(filename, sizeof(filename), "/proc/%d/comm", tid);

    FILE * comm(fopen(filename, "w"));
    fprintf(comm, "%s", name);
    fclose(comm);
}

Now each one of my thread has a different name. Thanks to Nate who gave me the idea of trying this (even if his comment does not quite read this way).

You may also want to use the pthread_setname_np(). Somehow that function will not update the name you directly wrote to the comm file.

Extracurricular answered 6/8, 2021 at 20:22 Comment(2)
The man page says that with sufficiently recent versions of glibc, you should be able to call gettid directly as a library function.Clabo
@NateEldredge That's good news. It should have been there for a while, I think. In Ubuntu 2018, though, I still don't see it.Extracurricular

© 2022 - 2024 — McMap. All rights reserved.