-
Enhancement
-
Resolution: Fixed
-
P4
-
26
-
b18
-
linux
ASAN only shows some internal thread designation on reports, e.g.:
```
WRITE of size 8 at 0x7b749d2d9190 thread T49
```
which is useless. We should add this capability to Linux.
JDK-7102541 added support for thread names in gdb, which uses `_pthread_setname_np`, but these names do not show up in ASAN.
Investigation:
We call pthread_setname_np assuming that it causes libpthread to call prctl(PR_SET_NAME,...), which would set the thread name in the kernel task structure. We further assume that name would percolate through to all interested parties, like gdb and ASAN.
But it reproducibly does not work for ASAN, tested on Fedora with glibc 2.42 and on Debian 12 stable. I also often see JVM ASAN reports from unknown Linux versions that never show a Thread name.
glibc does everything correctly:
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=nptl/pthread_setname.c;hb=42aba9189557280ad367c35908cbdfe26f5aeeb1
Specifically, in the Linux version of pthread_setname_np, it calls eventually:
```
int
__pthread_setname_np (pthread_t th, const char *name)
{
...
if (pd == THREAD_SELF)
return __prctl (PR_SET_NAME, name) ? errno : 0;
...
```
ASAN, in GCC, intercepts prctl(SET_NAME): it does the real prctl first, then calls COMMON_INTERCEPTOR_SET_THREAD_NAME:
https://gcc.gnu.org/git/?p=gcc.git;a=blob_plain;f=libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc;hb=HEAD
```
INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5) {
...
int res = REAL(prctl)(option, arg2, arg3, arg4, arg5);
if (option == PR_SET_NAME) {
...
COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff);
} else if (res == 0 && option == PR_GET_NAME) {
...
```
COMMON_INTERCEPTOR_SET_THREAD_NAME is defined as SetThreadName in ASAN here:
https://gcc.gnu.org/git/?p=gcc.git;a=blob_plain;f=libsanitizer/asan/asan_interceptors.h;hb=HEAD
```
#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name)
```
Which will register the thread name with the central ASAN thread registry:
```
void SetThreadName(const char *name) {
...
asanThreadRegistry().SetThreadName(t->tid(), name);
...
}
```
And now the thread name is known to ASAN.
ASAN also intercepts pthread_setname_np. It calls the interceptor first, then goes on calling the real pthread_setname_np:
```
INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) {
COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name);
return REAL(pthread_setname_np)(thread, name);
}
```
COMMON_INTERCEPTOR_SET_PTHREAD_NAME, however, is a noop:
```
#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
do { \
} while (false)
```
Looking at the thread registry code in ASAN: the internal thread structure (class ThreadContextBase) carries the user-defined thread name. When we call pthread_setname_np, the real glibc version is called correctly, which does prctl(PR_SET_NAME), correctly. However, the name in the ASAN thread registry does not obtain its string from prctl(PR_GET_NAME), the proc file system, or any other source. It only gets the name from intercepting prctl(PR_SET_NAME).
And intercepting prctl(PR_SET_NAME) only works for **an ASAN-instrumented binary**.
So, if we call prctl(PR_SET_NAME) from within the hotspot, the ASAN interception kicks in and modifies the ASAN thread registry name alongside the kernel task structure name.
If we just call glibc pthread_setname_np, the glibc does call prctl(PR_SET_NAME) correctly, but since glibc is not ASAN-instrumented, the ASAN interception does not kick in and the ASAN thread registry won't be modified.
It seems that the only way to set the ASAN thread name is to call prctl directly from the hotspot.
```
WRITE of size 8 at 0x7b749d2d9190 thread T49
```
which is useless. We should add this capability to Linux.
Investigation:
We call pthread_setname_np assuming that it causes libpthread to call prctl(PR_SET_NAME,...), which would set the thread name in the kernel task structure. We further assume that name would percolate through to all interested parties, like gdb and ASAN.
But it reproducibly does not work for ASAN, tested on Fedora with glibc 2.42 and on Debian 12 stable. I also often see JVM ASAN reports from unknown Linux versions that never show a Thread name.
glibc does everything correctly:
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=nptl/pthread_setname.c;hb=42aba9189557280ad367c35908cbdfe26f5aeeb1
Specifically, in the Linux version of pthread_setname_np, it calls eventually:
```
int
__pthread_setname_np (pthread_t th, const char *name)
{
...
if (pd == THREAD_SELF)
return __prctl (PR_SET_NAME, name) ? errno : 0;
...
```
ASAN, in GCC, intercepts prctl(SET_NAME): it does the real prctl first, then calls COMMON_INTERCEPTOR_SET_THREAD_NAME:
https://gcc.gnu.org/git/?p=gcc.git;a=blob_plain;f=libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc;hb=HEAD
```
INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5) {
...
int res = REAL(prctl)(option, arg2, arg3, arg4, arg5);
if (option == PR_SET_NAME) {
...
COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff);
} else if (res == 0 && option == PR_GET_NAME) {
...
```
COMMON_INTERCEPTOR_SET_THREAD_NAME is defined as SetThreadName in ASAN here:
https://gcc.gnu.org/git/?p=gcc.git;a=blob_plain;f=libsanitizer/asan/asan_interceptors.h;hb=HEAD
```
#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name)
```
Which will register the thread name with the central ASAN thread registry:
```
void SetThreadName(const char *name) {
...
asanThreadRegistry().SetThreadName(t->tid(), name);
...
}
```
And now the thread name is known to ASAN.
ASAN also intercepts pthread_setname_np. It calls the interceptor first, then goes on calling the real pthread_setname_np:
```
INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) {
COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name);
return REAL(pthread_setname_np)(thread, name);
}
```
COMMON_INTERCEPTOR_SET_PTHREAD_NAME, however, is a noop:
```
#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
do { \
} while (false)
```
Looking at the thread registry code in ASAN: the internal thread structure (class ThreadContextBase) carries the user-defined thread name. When we call pthread_setname_np, the real glibc version is called correctly, which does prctl(PR_SET_NAME), correctly. However, the name in the ASAN thread registry does not obtain its string from prctl(PR_GET_NAME), the proc file system, or any other source. It only gets the name from intercepting prctl(PR_SET_NAME).
And intercepting prctl(PR_SET_NAME) only works for **an ASAN-instrumented binary**.
So, if we call prctl(PR_SET_NAME) from within the hotspot, the ASAN interception kicks in and modifies the ASAN thread registry name alongside the kernel task structure name.
If we just call glibc pthread_setname_np, the glibc does call prctl(PR_SET_NAME) correctly, but since glibc is not ASAN-instrumented, the ASAN interception does not kick in and the ASAN thread registry won't be modified.
It seems that the only way to set the ASAN thread name is to call prctl directly from the hotspot.
- relates to
-
JDK-8368621 In ASAN builds, attached non-main threads should have meaningful native names
-
- Open
-
-
JDK-7102541 RFE: os::set_native_thread_name() cleanups
-
- Closed
-
-
JDK-8368365 ASAN errors should produce hs-err files and core dumps
-
- Open
-
-
JDK-8368621 In ASAN builds, attached non-main threads should have meaningful native names
-
- Open
-
- links to
-
Commit(master) openjdk/jdk/a6638121
-
Review(master) openjdk/jdk/27395
(1 links to)