1 | // REQUIRES: memprof-64-bits |
2 | // Stress test dynamic TLS + dlopen + threads. |
3 | // |
4 | // RUN: %clang_memprof -x c -DSO_NAME=f0 %s -shared -o %t-f0.so -fPIC |
5 | // RUN: %clang_memprof -x c -DSO_NAME=f1 %s -shared -o %t-f1.so -fPIC |
6 | // RUN: %clang_memprof -x c -DSO_NAME=f2 %s -shared -o %t-f2.so -fPIC |
7 | // RUN: %clang_memprof %s -ldl -pthread -o %t |
8 | // RUN: %run %t 0 3 |
9 | // RUN: %run %t 2 3 |
10 | // RUN: %env_memprof_opts=print_text=true:log_path=stderr:verbosity=2 %run %t 10 2 2>&1 | FileCheck %s |
11 | // RUN: %env_memprof_opts=print_text=true:log_path=stderr:verbosity=2:intercept_tls_get_addr=1 %run %t 10 2 2>&1 | FileCheck %s |
12 | // RUN: %env_memprof_opts=print_text=true:log_path=stderr:verbosity=2:intercept_tls_get_addr=0 %run %t 10 2 2>&1 | FileCheck %s --check-prefix=CHECK0 |
13 | // CHECK: ==__tls_get_addr: |
14 | // CHECK: Creating thread 0 |
15 | // CHECK: ==__tls_get_addr: |
16 | // CHECK: Creating thread 1 |
17 | // CHECK: ==__tls_get_addr: |
18 | // CHECK: Creating thread 2 |
19 | // CHECK: ==__tls_get_addr: |
20 | // CHECK: Creating thread 3 |
21 | // CHECK: ==__tls_get_addr: |
22 | // Make sure that TLS slots don't leak |
23 | // CHECK-NOT: num_live_dtls 5 |
24 | // |
25 | // CHECK0-NOT: ==__tls_get_addr: |
26 | /* |
27 | cc=your-compiler |
28 | |
29 | $cc stress_dtls.c -pthread -ldl |
30 | for((i=0;i<100;i++)); do |
31 | $cc -fPIC -shared -DSO_NAME=f$i -o a.out-f$i.so stress_dtls.c; |
32 | done |
33 | ./a.out 2 4 # <<<<<< 2 threads, 4 libs |
34 | ./a.out 3 50 # <<<<<< 3 threads, 50 libs |
35 | */ |
36 | #ifndef SO_NAME |
37 | #define _GNU_SOURCE |
38 | #include <assert.h> |
39 | #include <dlfcn.h> |
40 | #include <pthread.h> |
41 | #include <stdint.h> |
42 | #include <stdio.h> |
43 | #include <stdlib.h> |
44 | |
45 | typedef void **(*f_t)(); |
46 | |
47 | __thread int my_tls; |
48 | |
49 | #define MAX_N_FUNCTIONS 1000 |
50 | f_t Functions[MAX_N_FUNCTIONS]; |
51 | |
52 | void *PrintStuff(void *unused) { |
53 | uintptr_t stack; |
54 | // fprintf(stderr, "STACK: %p TLS: %p SELF: %p\n", &stack, &my_tls, |
55 | // (void *)pthread_self()); |
56 | int i; |
57 | for (i = 0; i < MAX_N_FUNCTIONS; i++) { |
58 | if (!Functions[i]) |
59 | break; |
60 | uintptr_t dtls = (uintptr_t)Functions[i](); |
61 | fprintf(stderr, format: " dtls[%03d]: %lx\n" , i, dtls); |
62 | *(long *)dtls = 42; // check that this is writable. |
63 | } |
64 | return NULL; |
65 | } |
66 | |
67 | int main(int argc, char *argv[]) { |
68 | int num_threads = 1; |
69 | int num_libs = 1; |
70 | if (argc >= 2) |
71 | num_threads = atoi(nptr: argv[1]); |
72 | if (argc >= 3) |
73 | num_libs = atoi(nptr: argv[2]); |
74 | assert(num_libs <= MAX_N_FUNCTIONS); |
75 | |
76 | int lib; |
77 | for (lib = 0; lib < num_libs; lib++) { |
78 | char buf[4096]; |
79 | snprintf(s: buf, maxlen: sizeof(buf), format: "%s-f%d.so" , argv[0], lib); |
80 | void *handle = dlopen(file: buf, RTLD_LAZY); |
81 | if (!handle) { |
82 | fprintf(stderr, format: "%s\n" , dlerror()); |
83 | exit(status: 1); |
84 | } |
85 | snprintf(s: buf, maxlen: sizeof(buf), format: "f%d" , lib); |
86 | Functions[lib] = (f_t)dlsym(handle: handle, name: buf); |
87 | if (!Functions[lib]) { |
88 | fprintf(stderr, format: "%s\n" , dlerror()); |
89 | exit(status: 1); |
90 | } |
91 | fprintf(stderr, format: "LIB[%03d] %s: %p\n" , lib, buf, Functions[lib]); |
92 | PrintStuff(unused: 0); |
93 | |
94 | int i; |
95 | for (i = 0; i < num_threads; i++) { |
96 | pthread_t t; |
97 | fprintf(stderr, format: "Creating thread %d\n" , i); |
98 | pthread_create(newthread: &t, attr: 0, start_routine: PrintStuff, arg: 0); |
99 | pthread_join(th: t, thread_return: 0); |
100 | } |
101 | } |
102 | return 0; |
103 | } |
104 | #else // SO_NAME |
105 | #ifndef DTLS_SIZE |
106 | #define DTLS_SIZE (1 << 17) |
107 | #endif |
108 | __thread void *huge_thread_local_array[DTLS_SIZE]; |
109 | void **SO_NAME() { |
110 | return &huge_thread_local_array[0]; |
111 | } |
112 | #endif |
113 | |