1 | /* Test pthread_setname_np and pthread_getname_np. |
2 | Copyright (C) 2013-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public License as |
7 | published by the Free Software Foundation; either version 2.1 of the |
8 | License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; see the file COPYING.LIB. If |
17 | not, see <https://www.gnu.org/licenses/>. */ |
18 | #include <stdio.h> |
19 | #include <stdlib.h> |
20 | #include <pthread.h> |
21 | #include <string.h> |
22 | #include <sys/syscall.h> |
23 | #include <unistd.h> |
24 | #include <fcntl.h> |
25 | #include <errno.h> |
26 | |
27 | /* New name of process. */ |
28 | #define NEW_NAME "setname" |
29 | |
30 | /* Name of process which is one byte too big |
31 | e.g. 17 bytes including null-terminator */ |
32 | #define BIG_NAME "....V....X....XV" |
33 | |
34 | /* Longest name of a process |
35 | e.g. 16 bytes including null-terminator. */ |
36 | #define LONGEST_NAME "....V....X....X" |
37 | |
38 | /* One less than longest name with unique |
39 | characters to detect modification. */ |
40 | #define CANARY_NAME "abcdefghijklmn" |
41 | |
42 | /* On Linux the maximum length of the name of a task *including* the null |
43 | terminator. */ |
44 | #define TASK_COMM_LEN 16 |
45 | |
46 | /* On Linux we can read this task's name from /proc. */ |
47 | int |
48 | get_self_comm (long tid, char *buf, size_t len) |
49 | { |
50 | int res = 0; |
51 | #define FMT "/proc/self/task/%lu/comm" |
52 | char fname[sizeof (FMT) + 32]; |
53 | sprintf (fname, FMT, (unsigned long) tid); |
54 | |
55 | int fd = open (file: fname, O_RDONLY); |
56 | if (fd == -1) |
57 | return errno; |
58 | |
59 | ssize_t n = read (fd, (void *) buf, len); |
60 | if (n < 0) |
61 | res = errno; |
62 | else |
63 | { |
64 | if (buf[n - 1] == '\n') |
65 | buf[n - 1] = '\0'; |
66 | else if (n == len) |
67 | res = ERANGE; |
68 | else |
69 | buf[n] = '\0'; |
70 | } |
71 | |
72 | close (fd: fd); |
73 | return res; |
74 | } |
75 | |
76 | int |
77 | do_test (int argc, char **argv) |
78 | { |
79 | pthread_t self; |
80 | int res; |
81 | int ret = 0; |
82 | char name[TASK_COMM_LEN]; |
83 | char name_check[TASK_COMM_LEN]; |
84 | |
85 | memset (name, '\0', TASK_COMM_LEN); |
86 | memset (name_check, '\0', TASK_COMM_LEN); |
87 | |
88 | /* Test 1: Get the name of the task via pthread_getname_np and /proc |
89 | and verify that they both match. */ |
90 | self = pthread_self (); |
91 | res = pthread_getname_np (target_thread: self, buf: name, TASK_COMM_LEN); |
92 | |
93 | if (res == 0) |
94 | { |
95 | res = get_self_comm (tid: gettid (), buf: name_check, TASK_COMM_LEN); |
96 | |
97 | if (res == 0) |
98 | { |
99 | if (strncmp (name, name_check, strlen (BIG_NAME)) == 0) |
100 | printf (format: "PASS: Test 1 - pthread_getname_np and /proc agree.\n" ); |
101 | else |
102 | { |
103 | printf (format: "FAIL: Test 1 - pthread_getname_np and /proc differ" |
104 | " i.e. %s != %s\n" , name, name_check); |
105 | ret++; |
106 | } |
107 | } |
108 | else |
109 | { |
110 | printf (format: "FAIL: Test 1 - unable read task name via proc.\n" ); |
111 | ret++; |
112 | } |
113 | } |
114 | else |
115 | { |
116 | printf (format: "FAIL: Test 1 - pthread_getname_np failed with error %d\n" , res); |
117 | ret++; |
118 | } |
119 | |
120 | /* Test 2: Test setting the name and then independently verify it |
121 | was set. */ |
122 | res = pthread_setname_np (target_thread: self, NEW_NAME); |
123 | |
124 | if (res == 0) |
125 | { |
126 | res = get_self_comm (tid: gettid (), buf: name_check, TASK_COMM_LEN); |
127 | if (res == 0) |
128 | { |
129 | if (strncmp (NEW_NAME, name_check, strlen (BIG_NAME)) == 0) |
130 | printf (format: "PASS: Test 2 - Value used in pthread_setname_np and" |
131 | " /proc agree.\n" ); |
132 | else |
133 | { |
134 | printf (format: "FAIL: Test 2 - Value used in pthread_setname_np" |
135 | " and /proc differ i.e. %s != %s\n" , |
136 | NEW_NAME, name_check); |
137 | ret++; |
138 | } |
139 | } |
140 | else |
141 | { |
142 | printf (format: "FAIL: Test 2 - unable to read task name via proc.\n" ); |
143 | ret++; |
144 | } |
145 | } |
146 | else |
147 | { |
148 | printf (format: "FAIL: Test 2 - pthread_setname_np failed with error %d\n" , res); |
149 | ret++; |
150 | } |
151 | |
152 | /* Test 3: Test setting a name that is one-byte too big. */ |
153 | res = pthread_getname_np (target_thread: self, buf: name, TASK_COMM_LEN); |
154 | |
155 | if (res == 0) |
156 | { |
157 | res = pthread_setname_np (target_thread: self, BIG_NAME); |
158 | if (res != 0) |
159 | { |
160 | if (res == ERANGE) |
161 | { |
162 | printf (format: "PASS: Test 3 - pthread_setname_np returned ERANGE" |
163 | " for a process name that was too long.\n" ); |
164 | |
165 | /* Verify the old name didn't change. */ |
166 | res = get_self_comm (tid: gettid (), buf: name_check, TASK_COMM_LEN); |
167 | if (res == 0) |
168 | { |
169 | if (strncmp (name, name_check, strlen (BIG_NAME)) == 0) |
170 | printf (format: "PASS: Test 3 - Original name unchanged after" |
171 | " pthread_setname_np returned ERANGE.\n" ); |
172 | else |
173 | { |
174 | printf (format: "FAIL: Test 3 - Original name changed after" |
175 | " pthread_setname_np returned ERANGE" |
176 | " i.e. %s != %s\n" , |
177 | name, name_check); |
178 | ret++; |
179 | } |
180 | } |
181 | else |
182 | { |
183 | printf (format: "FAIL: Test 3 - unable to read task name.\n" ); |
184 | ret++; |
185 | } |
186 | } |
187 | else |
188 | { |
189 | printf (format: "FAIL: Test 3 - Wrong error returned" |
190 | " i.e. ERANGE != %d\n" , res); |
191 | ret++; |
192 | } |
193 | } |
194 | else |
195 | { |
196 | printf (format: "FAIL: Test 3 - Too-long name accepted by" |
197 | " pthread_setname_np.\n" ); |
198 | ret++; |
199 | } |
200 | } |
201 | else |
202 | { |
203 | printf (format: "FAIL: Test 3 - Unable to get original name.\n" ); |
204 | ret++; |
205 | } |
206 | |
207 | /* Test 4: Verify that setting the longest name works. */ |
208 | res = pthread_setname_np (target_thread: self, LONGEST_NAME); |
209 | |
210 | if (res == 0) |
211 | { |
212 | res = get_self_comm (tid: gettid (), buf: name_check, TASK_COMM_LEN); |
213 | if (res == 0) |
214 | { |
215 | if (strncmp (LONGEST_NAME, name_check, strlen (BIG_NAME)) == 0) |
216 | printf (format: "PASS: Test 4 - Longest name set via pthread_setname_np" |
217 | " agrees with /proc.\n" ); |
218 | else |
219 | { |
220 | printf (format: "FAIL: Test 4 - Value used in pthread_setname_np and /proc" |
221 | " differ i.e. %s != %s\n" , LONGEST_NAME, name_check); |
222 | ret++; |
223 | } |
224 | } |
225 | else |
226 | { |
227 | printf (format: "FAIL: Test 4 - unable to read task name via proc.\n" ); |
228 | ret++; |
229 | } |
230 | } |
231 | else |
232 | { |
233 | printf (format: "FAIL: Test 4 - pthread_setname_np failed with error %d\n" , res); |
234 | ret++; |
235 | } |
236 | |
237 | /* Test 5: Verify that getting a long name into a small buffer fails. */ |
238 | strncpy (name, CANARY_NAME, strlen (CANARY_NAME) + 1); |
239 | |
240 | /* Claim the buffer length is strlen (LONGEST_NAME). This is one character |
241 | too small to hold LONGEST_NAME *and* the null terminator. We should get |
242 | back ERANGE and name should be unmodified. */ |
243 | res = pthread_getname_np (target_thread: self, buf: name, buflen: strlen (LONGEST_NAME)); |
244 | |
245 | if (res != 0) |
246 | { |
247 | if (res == ERANGE) |
248 | { |
249 | if (strncmp (CANARY_NAME, name, strlen (BIG_NAME)) == 0) |
250 | { |
251 | printf (format: "PASS: Test 5 - ERANGE and buffer unmodified.\n" ); |
252 | } |
253 | else |
254 | { |
255 | printf (format: "FAIL: Test 5 - Original buffer modified.\n" ); |
256 | ret++; |
257 | } |
258 | } |
259 | else |
260 | { |
261 | printf (format: "FAIL: Test 5 - Did not return ERANGE for small buffer.\n" ); |
262 | ret++; |
263 | } |
264 | } |
265 | else |
266 | { |
267 | printf (format: "FAIL: Test 5 - Returned name longer than buffer.\n" ); |
268 | ret++; |
269 | } |
270 | |
271 | /* Test 6: Lastly make sure we can read back the longest name. */ |
272 | res = pthread_getname_np (target_thread: self, buf: name, buflen: strlen (LONGEST_NAME) + 1); |
273 | |
274 | if (res == 0) |
275 | { |
276 | if (strncmp (LONGEST_NAME, name, strlen (BIG_NAME)) == 0) |
277 | { |
278 | printf (format: "PASS: Test 6 - Read back longest name correctly.\n" ); |
279 | } |
280 | else |
281 | { |
282 | printf (format: "FAIL: Test 6 - Read \"%s\" instead of longest name.\n" , |
283 | name); |
284 | ret++; |
285 | } |
286 | } |
287 | else |
288 | { |
289 | printf (format: "FAIL: Test 6 - pthread_getname_np failed with error %d\n" , res); |
290 | ret++; |
291 | } |
292 | |
293 | return ret; |
294 | } |
295 | |
296 | #include <test-skeleton.c> |
297 | |