clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name sel_spd.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nspr/Linux4.19_x86_64_gcc_glibc_PTH_64_DBG.OBJ/pr/tests -fcoverage-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nspr/Linux4.19_x86_64_gcc_glibc_PTH_64_DBG.OBJ/pr/tests -resource-dir /usr/lib/llvm-18/lib/clang/18 -U NDEBUG -D DEBUG_jenkins -D PACKAGE_NAME="" -D PACKAGE_TARNAME="" -D PACKAGE_VERSION="" -D PACKAGE_STRING="" -D PACKAGE_BUGREPORT="" -D PACKAGE_URL="" -D DEBUG=1 -D HAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1 -D HAVE_VISIBILITY_PRAGMA=1 -D XP_UNIX=1 -D _GNU_SOURCE=1 -D HAVE_FCNTL_FILE_LOCKING=1 -D HAVE_POINTER_LOCALTIME_R=1 -D LINUX=1 -D HAVE_DLADDR=1 -D HAVE_GETTID=1 -D HAVE_LCHOWN=1 -D HAVE_SETPRIORITY=1 -D HAVE_STRERROR=1 -D HAVE_SYSCALL=1 -D HAVE_SECURE_GETENV=1 -D _REENTRANT=1 -D FORCE_PR_LOG -D _PR_PTHREADS -U HAVE_CVAR_BUILT_ON_SEM -I /var/lib/jenkins/workspace/nss-scan-build/nss/../dist/Linux4.19_x86_64_gcc_glibc_PTH_64_DBG.OBJ/include -I ../../../pr/include -I ../../../pr/include/private -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -fno-inline -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-18-082241-28900-1 -x c ../../../pr/tests/sel_spd.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | #include "nspr.h" |
12 | #include "prpriv.h" |
13 | |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <errno.h> |
17 | #include <string.h> |
18 | |
19 | #if defined(XP_UNIX) |
20 | #include <unistd.h> |
21 | #endif |
22 | |
23 | #ifdef DEBUG |
24 | #define PORT_INC_DO +100 |
25 | #else |
26 | #define PORT_INC_DO |
27 | #endif |
28 | #ifdef IS_64 |
29 | #define PORT_INC_3264 +200 |
30 | #else |
31 | #define PORT_INC_3264 |
32 | #endif |
33 | |
34 | #define PORT_BASE 19000 PORT_INC_DO PORT_INC_3264 |
35 | |
36 | typedef struct timer_slot_t { |
37 | unsigned long d_connect; |
38 | unsigned long d_cl_data; |
39 | unsigned long d_sv_data; |
40 | unsigned long d_close; |
41 | unsigned long d_total; |
42 | unsigned long requests; |
43 | } timer_slot_t; |
44 | |
45 | static long _iterations = 5; |
46 | static long _client_data = 8192; |
47 | |
48 | static long _server_data = (128*1024); |
49 | static long _threads_max = 10, _threads = 10; |
50 | |
51 | static int verbose=0; |
52 | static PRMonitor *exit_cv; |
53 | static long _thread_exit_count; |
54 | static timer_slot_t *timer_data; |
55 | static PRThreadScope scope1, scope2; |
56 | |
57 | void tally_results(int); |
58 | |
59 | |
60 | unsigned long _delta(PRIntervalTime *start, PRIntervalTime *stop) |
61 | { |
62 | |
63 | |
64 | |
65 | return PR_IntervalToMicroseconds(*stop - *start); |
66 | } |
67 | |
68 | int _readn(PRFileDesc *sock, char *buf, int len) |
69 | { |
70 | int rem; |
71 | int bytes; |
72 | |
73 | for (rem=len; rem; rem -= bytes) { |
74 | bytes = PR_Recv(sock, buf+len-rem, rem, 0, PR_INTERVAL_NO_TIMEOUT); |
75 | if (bytes <= 0) { |
76 | return -1; |
77 | } |
78 | } |
79 | return len; |
80 | } |
81 | |
82 | void |
83 | _thread_exit(int id) |
84 | { |
85 | PR_EnterMonitor(exit_cv); |
86 | #ifdef DEBUG |
87 | fprintf(stdout, "Thread %d EXIT\n", id); |
88 | #endif |
89 | |
90 | _thread_exit_count--; |
91 | if (_thread_exit_count == 0) { |
92 | #ifdef DEBUG |
93 | fprintf(stdout, "Thread %d EXIT triggered notify\n", id); |
94 | #endif |
95 | PR_Notify(exit_cv); |
96 | } |
97 | PR_ExitMonitor(exit_cv); |
98 | } |
99 | |
100 | void |
101 | _server_thread(void *arg_id) |
102 | { |
103 | void _client_thread(void *); |
104 | int *id = (int *)arg_id; |
105 | PRFileDesc *sock; |
| 1 | 'sock' declared without an initial value | |
|
106 | PRSocketOptionData sockopt; |
107 | PRNetAddr sa; |
108 | PRFileDesc * newsock; |
109 | char *data_buffer = NULL; |
110 | int data_buffer_size; |
111 | int index; |
112 | PRIntervalTime start, |
113 | connect_done, |
114 | read_done, |
115 | write_done, |
116 | close_done; |
117 | |
118 | |
119 | #ifdef DEBUG |
120 | fprintf(stdout, "server thread %d alive\n", *id); |
121 | #endif |
122 | |
123 | data_buffer_size = (_client_data>_server_data?_client_data:_server_data); |
| 2 | | Assuming '_client_data' is <= '_server_data' | |
|
| |
124 | |
125 | if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL ) { |
| 4 | | Assuming the condition is true | |
|
| |
126 | fprintf(stderr, "Error creating buffer in server thread %d\n", *id); |
127 | goto done; |
| 6 | | Control jumps to line 233 | |
|
128 | } |
129 | |
130 | |
131 | if ( (sock = PR_NewTCPSocket()) == NULL) { |
132 | fprintf(stderr, "Error creating socket in server thread %d\n", *id); |
133 | goto done; |
134 | } |
135 | |
136 | sockopt.option = PR_SockOpt_Reuseaddr; |
137 | sockopt.value.reuse_addr = PR_TRUE; |
138 | if ( PR_SetSocketOption(sock, &sockopt) == PR_FAILURE) { |
139 | fprintf(stderr, "Error setting socket option in server thread %d\n", *id); |
140 | goto done; |
141 | } |
142 | |
143 | memset(&sa, 0, sizeof(sa)); |
144 | sa.inet.family = PR_AF_INET; |
145 | sa.inet.port = PR_htons(PORT_BASE + *id); |
146 | sa.inet.ip = PR_htonl(PR_INADDR_ANY); |
147 | |
148 | if ( PR_Bind(sock, &sa) < 0) { |
149 | fprintf(stderr, "Error binding socket in server thread %d errno = %d\n", *id, errno); |
150 | goto done; |
151 | } |
152 | |
153 | if ( PR_Listen(sock, 32) < 0 ) { |
154 | fprintf(stderr, "Error listening to socket in server thread %d\n", *id); |
155 | goto done; |
156 | } |
157 | |
158 | |
159 | if ( PR_CreateThread(PR_USER_THREAD, |
160 | _client_thread, |
161 | id, |
162 | PR_PRIORITY_NORMAL, |
163 | scope2, |
164 | PR_UNJOINABLE_THREAD, |
165 | 0) == NULL) { |
166 | fprintf(stderr, "Error creating client thread %d\n", *id); |
167 | } |
168 | |
169 | for (index = 0; index< _iterations; index++) { |
170 | |
171 | #ifdef DEBUG |
172 | fprintf(stdout, "server thread %d loop %d\n", *id, index); |
173 | #endif |
174 | |
175 | start = PR_IntervalNow(); |
176 | |
177 | if ( (newsock = PR_Accept(sock, &sa, |
178 | PR_INTERVAL_NO_TIMEOUT)) == NULL) { |
179 | fprintf(stderr, "Error accepting connection %d in server thread %d\n", |
180 | index, *id); |
181 | goto done; |
182 | } |
183 | #ifdef DEBUG |
184 | fprintf(stdout, "server thread %d got connection %d\n", *id, newsock); |
185 | #endif |
186 | |
187 | |
188 | connect_done = PR_IntervalNow(); |
189 | |
190 | if ( _readn(newsock, data_buffer, _client_data) < _client_data) { |
191 | fprintf(stderr, "Error reading client data for iteration %d in server thread %d\n", index, *id ); |
192 | goto done; |
193 | } |
194 | |
195 | #ifdef DEBUG |
196 | fprintf(stdout, "server thread %d read %d bytes\n", *id, _client_data); |
197 | #endif |
198 | read_done = PR_IntervalNow(); |
199 | |
200 | if ( PR_Send(newsock, data_buffer, _server_data, 0, |
201 | PR_INTERVAL_NO_TIMEOUT) < _server_data) { |
202 | fprintf(stderr, "Error sending client data for iteration %d in server thread %d\n", index, *id ); |
203 | goto done; |
204 | } |
205 | |
206 | #ifdef DEBUG |
207 | fprintf(stdout, "server thread %d write %d bytes\n", *id, _server_data); |
208 | #endif |
209 | |
210 | write_done = PR_IntervalNow(); |
211 | |
212 | PR_Close(newsock); |
213 | |
214 | close_done = PR_IntervalNow(); |
215 | |
216 | timer_data[2*(*id)].d_connect += _delta(&start, &connect_done); |
217 | timer_data[2*(*id)].d_cl_data += _delta(&connect_done, &read_done); |
218 | timer_data[2*(*id)].d_sv_data += _delta(&read_done, &write_done); |
219 | timer_data[2*(*id)].d_close += _delta(&write_done, &close_done); |
220 | timer_data[2*(*id)].d_total += _delta(&start, &close_done); |
221 | timer_data[2*(*id)].requests++; |
222 | |
223 | |
224 | #ifdef DEBUG |
225 | fprintf(stdout, "server: %d %d %d %d %d\n", |
226 | _delta(&start, &connect_done), _delta(&connect_done, &read_done), |
227 | _delta(&read_done, &write_done), _delta(&write_done, &close_done), |
228 | _delta(&start, &close_done)); |
229 | #endif |
230 | } |
231 | |
232 | done: |
233 | if (data_buffer != NULL) { |
| |
234 | PR_Free (data_buffer); |
235 | } |
236 | if (sock) { |
| 8 | | Branch condition evaluates to a garbage value |
|
237 | PR_Close(sock); |
238 | } |
239 | _thread_exit(*id); |
240 | return; |
241 | } |
242 | |
243 | void |
244 | _client_thread(void *arg_id) |
245 | { |
246 | int *id = (int *)arg_id; |
247 | int index; |
248 | PRNetAddr sa; |
249 | PRFileDesc *sock_h; |
250 | char *data_buffer = NULL; |
251 | int data_buffer_size; |
252 | int bytes; |
253 | PRIntervalTime start, |
254 | connect_done, |
255 | read_done, |
256 | write_done, |
257 | close_done; |
258 | PRStatus rv; |
259 | |
260 | #ifdef DEBUG |
261 | fprintf(stdout, "client thread %d alive\n", *id); |
262 | #endif |
263 | |
264 | data_buffer_size = (_client_data>_server_data?_client_data:_server_data); |
265 | |
266 | if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL) { |
267 | fprintf(stderr, "Error creating buffer in server thread %d\n", *id); |
268 | goto done; |
269 | } |
270 | |
271 | memset(&sa, 0, sizeof(sa)); |
272 | rv = PR_InitializeNetAddr(PR_IpAddrLoopback, PORT_BASE + *id, &sa); |
273 | PR_ASSERT(PR_SUCCESS == rv); |
274 | |
275 | for (index = 0; index< _iterations; index++) { |
276 | |
277 | #ifdef DEBUG |
278 | fprintf(stdout, "client thread %d loop %d\n", *id, index); |
279 | #endif |
280 | |
281 | start = PR_IntervalNow(); |
282 | if ( (sock_h = PR_NewTCPSocket()) == NULL) { |
283 | fprintf(stderr, "Error creating socket %d in client thread %d\n", |
284 | index, *id); |
285 | goto done; |
286 | } |
287 | |
288 | #ifdef DEBUG |
289 | fprintf(stdout, "client thread %d socket created %d\n", *id, sock_h); |
290 | #endif |
291 | |
292 | if ( PR_Connect(sock_h, &sa, |
293 | PR_INTERVAL_NO_TIMEOUT) < 0) { |
294 | fprintf(stderr, "Error accepting connection %d in client thread %d\n", |
295 | index, *id); |
296 | goto done; |
297 | } |
298 | |
299 | #ifdef DEBUG |
300 | fprintf(stdout, "client thread %d socket connected %d\n", *id, sock_h); |
301 | #endif |
302 | |
303 | connect_done = PR_IntervalNow(); |
304 | if ( PR_Send(sock_h, data_buffer, _client_data, 0, |
305 | PR_INTERVAL_NO_TIMEOUT) < _client_data) { |
306 | fprintf(stderr, "Error sending client data for iteration %d in client thread %d\n", index, *id ); |
307 | goto done; |
308 | } |
309 | |
310 | #ifdef DEBUG |
311 | fprintf(stdout, "client thread %d socket wrote %d\n", *id, _client_data); |
312 | #endif |
313 | |
314 | write_done = PR_IntervalNow(); |
315 | if ( (bytes = _readn(sock_h, data_buffer, _server_data)) < _server_data) { |
316 | fprintf(stderr, "Error reading server data for iteration %d in client thread %d (read %d bytes)\n", index, *id, bytes ); |
317 | goto done; |
318 | } |
319 | |
320 | #ifdef DEBUG |
321 | fprintf(stdout, "client thread %d socket read %d\n", *id, _server_data); |
322 | #endif |
323 | |
324 | read_done = PR_IntervalNow(); |
325 | PR_Close(sock_h); |
326 | close_done = PR_IntervalNow(); |
327 | |
328 | timer_data[2*(*id)+1].d_connect += _delta(&start, &connect_done); |
329 | timer_data[2*(*id)+1].d_cl_data += _delta(&connect_done, &write_done); |
330 | timer_data[2*(*id)+1].d_sv_data += _delta(&write_done, &read_done); |
331 | timer_data[2*(*id)+1].d_close += _delta(&read_done, &close_done); |
332 | timer_data[2*(*id)+1].d_total += _delta(&start, &close_done); |
333 | timer_data[2*(*id)+1].requests++; |
334 | } |
335 | done: |
336 | if (data_buffer != NULL) { |
337 | PR_Free (data_buffer); |
338 | } |
339 | _thread_exit(*id); |
340 | |
341 | return; |
342 | } |
343 | |
344 | static |
345 | void do_work(void) |
346 | { |
347 | int index; |
348 | |
349 | _thread_exit_count = _threads * 2; |
350 | for (index=0; index<_threads; index++) { |
351 | int *id = (int *)PR_Malloc(sizeof(int)); |
352 | |
353 | *id = index; |
354 | |
355 | if ( PR_CreateThread(PR_USER_THREAD, |
356 | _server_thread, |
357 | id, |
358 | PR_PRIORITY_NORMAL, |
359 | scope1, |
360 | PR_UNJOINABLE_THREAD, |
361 | 0) == NULL) { |
362 | fprintf(stderr, "Error creating server thread %d\n", index); |
363 | } |
364 | } |
365 | |
366 | PR_EnterMonitor(exit_cv); |
367 | while (_thread_exit_count > 0) { |
368 | PR_Wait(exit_cv, PR_INTERVAL_NO_TIMEOUT); |
369 | } |
370 | PR_ExitMonitor(exit_cv); |
371 | |
372 | fprintf(stdout, "TEST COMPLETE!\n"); |
373 | |
374 | tally_results(verbose); |
375 | |
376 | } |
377 | |
378 | static void do_workUU(void) |
379 | { |
380 | scope1 = PR_LOCAL_THREAD; |
381 | scope2 = PR_LOCAL_THREAD; |
382 | do_work(); |
383 | } |
384 | |
385 | static void do_workUK(void) |
386 | { |
387 | scope1 = PR_LOCAL_THREAD; |
388 | scope2 = PR_GLOBAL_THREAD; |
389 | do_work(); |
390 | } |
391 | |
392 | static void do_workKU(void) |
393 | { |
394 | scope1 = PR_GLOBAL_THREAD; |
395 | scope2 = PR_LOCAL_THREAD; |
396 | do_work(); |
397 | } |
398 | |
399 | static void do_workKK(void) |
400 | { |
401 | scope1 = PR_GLOBAL_THREAD; |
402 | scope2 = PR_GLOBAL_THREAD; |
403 | do_work(); |
404 | } |
405 | |
406 | |
407 | |
408 | static void Measure(void (*func)(void), const char *msg) |
409 | { |
410 | PRIntervalTime start, stop; |
411 | double d; |
412 | |
413 | start = PR_IntervalNow(); |
414 | (*func)(); |
415 | stop = PR_IntervalNow(); |
416 | |
417 | d = (double)PR_IntervalToMicroseconds(stop - start); |
418 | |
419 | printf("%40s: %6.2f usec\n", msg, d / _iterations); |
420 | } |
421 | |
422 | |
423 | int main(int argc, char **argv) |
424 | { |
425 | #if defined(XP_UNIX) || defined(XP_OS2) |
426 | int opt; |
427 | PR_IMPORT_DATA(char *) optarg; |
428 | #endif |
429 | |
430 | #if defined(XP_UNIX) || defined(XP_OS2) |
431 | while ( (opt = getopt(argc, argv, "c:s:i:t:v")) != EOF) { |
432 | switch(opt) { |
433 | case 'i': |
434 | _iterations = atoi(optarg); |
435 | break; |
436 | case 't': |
437 | _threads_max = _threads = atoi(optarg); |
438 | break; |
439 | case 'c': |
440 | _client_data = atoi(optarg); |
441 | break; |
442 | case 's': |
443 | _server_data = atoi(optarg); |
444 | break; |
445 | case 'v': |
446 | verbose = 1; |
447 | break; |
448 | default: |
449 | break; |
450 | } |
451 | } |
452 | #endif |
453 | |
454 | PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); |
455 | PR_STDIO_INIT(); |
456 | |
457 | fprintf(stdout, "Running test for %d iterations with %d simultaneous threads.\n", |
458 | _iterations, _threads); |
459 | fprintf(stdout, "\tWill send %d bytes of client data and %d bytes of server data\n", |
460 | _client_data, _server_data); |
461 | |
462 | if ( (exit_cv = PR_NewMonitor()) == NULL) { |
463 | fprintf(stderr, "Error creating monitor for exit cv\n"); |
464 | } |
465 | if ( (timer_data = (timer_slot_t *)PR_Malloc(2*_threads * sizeof(timer_slot_t))) == NULL) { |
466 | fprintf(stderr, "error allocating thread time results array\n"); |
467 | } |
468 | memset(timer_data, 0, 2*_threads*sizeof(timer_slot_t)); |
469 | |
470 | Measure(do_workUU, "select loop user/user"); |
471 | Measure(do_workUK, "select loop user/kernel"); |
472 | Measure(do_workKU, "select loop kernel/user"); |
473 | Measure(do_workKK, "select loop kernel/kernel"); |
474 | |
475 | |
476 | return 0; |
477 | } |
478 | |
479 | void |
480 | tally_results(int verbose) |
481 | { |
482 | int index; |
483 | unsigned long tot_connect = 0; |
484 | unsigned long tot_cl_data = 0; |
485 | unsigned long tot_sv_data = 0; |
486 | unsigned long tot_close = 0; |
487 | unsigned long tot_all = 0; |
488 | unsigned long tot_requests = 0; |
489 | |
490 | fprintf(stdout, "Server results:\n\n"); |
491 | for (index=0; index<_threads_max*2; index+=2) { |
492 | |
493 | if (verbose) |
494 | fprintf(stdout, "server thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", |
495 | index, timer_data[index].requests, timer_data[index].d_connect, |
496 | timer_data[index].d_cl_data, timer_data[index].d_sv_data, |
497 | timer_data[index].d_close, timer_data[index].d_total); |
498 | |
499 | tot_connect += timer_data[index].d_connect / _threads; |
500 | tot_cl_data += timer_data[index].d_cl_data / _threads; |
501 | tot_sv_data += timer_data[index].d_sv_data / _threads; |
502 | tot_close += timer_data[index].d_close / _threads; |
503 | tot_all += timer_data[index].d_total / _threads; |
504 | tot_requests += timer_data[index].requests / _threads; |
505 | } |
506 | fprintf(stdout, "----------\n"); |
507 | fprintf(stdout, "server per thread totals %u\t%u\t%u\t%u\t%u\n", |
508 | tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close); |
509 | fprintf(stdout, "server per thread elapsed time %u\n", tot_all); |
510 | fprintf(stdout, "----------\n"); |
511 | |
512 | tot_connect = tot_cl_data = tot_sv_data = tot_close = tot_all = tot_requests = 0; |
513 | fprintf(stdout, "Client results:\n\n"); |
514 | for (index=1; index<_threads_max*2; index+=2) { |
515 | |
516 | if (verbose) |
517 | fprintf(stdout, "client thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", |
518 | index, timer_data[index].requests, timer_data[index].d_connect, |
519 | timer_data[index].d_cl_data, timer_data[index].d_sv_data, |
520 | timer_data[index].d_close, timer_data[index].d_total); |
521 | |
522 | tot_connect += timer_data[index].d_connect / _threads; |
523 | tot_cl_data += timer_data[index].d_cl_data / _threads; |
524 | tot_sv_data += timer_data[index].d_sv_data / _threads; |
525 | tot_close += timer_data[index].d_close / _threads; |
526 | tot_all += timer_data[index].d_total / _threads; |
527 | tot_requests += timer_data[index].requests / _threads; |
528 | } |
529 | fprintf(stdout, "----------\n"); |
530 | fprintf(stdout, "client per thread totals %u\t%u\t%u\t%u\t%u\n", |
531 | tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close); |
532 | fprintf(stdout, "client per thread elapsed time %u\n", tot_all); |
533 | } |
534 | |