xref: /freebsd/contrib/netbsd-tests/lib/libpthread_dbg/t_threads.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*	$NetBSD: t_threads.c,v 1.9 2017/01/13 05:18:22 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2016 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: t_threads.c,v 1.9 2017/01/13 05:18:22 christos Exp $");
32 
33 #include <dlfcn.h>
34 #include <pthread.h>
35 #include <pthread_dbg.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #include <atf-c.h>
41 
42 #include "h_common.h"
43 
44 #define MAX_THREADS (size_t)10
45 
46 ATF_TC(threads1);
47 ATF_TC_HEAD(threads1, tc)
48 {
49 
50 	atf_tc_set_md_var(tc, "descr",
51 	    "Asserts that td_thr_iter() call without extra logic works");
52 }
53 
54 static volatile int exiting1;
55 
56 static void *
57 busyFunction1(void *arg)
58 {
59 
60 	while (exiting1 == 0)
61 		usleep(50000);
62 
63 	return NULL;
64 }
65 
66 static int
67 iterateThreads1(td_thread_t *thread, void *arg)
68 {
69 
70 	return TD_ERR_OK;
71 }
72 
73 ATF_TC_BODY(threads1, tc)
74 {
75 	struct td_proc_callbacks_t dummy_callbacks;
76 	td_proc_t *main_ta;
77 	size_t i;
78 	pthread_t threads[MAX_THREADS];
79 
80 	dummy_callbacks.proc_read	= basic_proc_read;
81 	dummy_callbacks.proc_write	= basic_proc_write;
82 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
83 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
84 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
85 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
86 
87 	for (i = 0; i < MAX_THREADS; i++) {
88 		printf("Creating thread %zu\n", i);
89 		PTHREAD_REQUIRE
90 		    (pthread_create(&threads[i], NULL, busyFunction1, NULL));
91 	}
92 
93 	printf("Calling td_open(3)\n");
94 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
95 
96 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads1, NULL) == TD_ERR_OK);
97 
98 	exiting1 = 1;
99 
100 	printf("Calling td_close(3)\n");
101 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
102 }
103 
104 ATF_TC(threads2);
105 ATF_TC_HEAD(threads2, tc)
106 {
107 
108 	atf_tc_set_md_var(tc, "descr",
109 	    "Asserts that td_thr_iter() call is executed for each thread once");
110 }
111 
112 static volatile int exiting2;
113 
114 static void *
115 busyFunction2(void *arg)
116 {
117 
118 	while (exiting2 == 0)
119 		usleep(50000);
120 
121 	return NULL;
122 }
123 
124 static int
125 iterateThreads2(td_thread_t *thread, void *arg)
126 {
127 	int *counter = (int *)arg;
128 
129 	++(*counter);
130 
131 	return TD_ERR_OK;
132 }
133 
134 ATF_TC_BODY(threads2, tc)
135 {
136 	struct td_proc_callbacks_t dummy_callbacks;
137 	td_proc_t *main_ta;
138 	size_t i;
139 	pthread_t threads[MAX_THREADS];
140 	int count = 0;
141 
142 	dummy_callbacks.proc_read	= basic_proc_read;
143 	dummy_callbacks.proc_write	= basic_proc_write;
144 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
145 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
146 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
147 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
148 
149 
150 	for (i = 0; i < MAX_THREADS; i++) {
151 		printf("Creating thread %zu\n", i);
152 		PTHREAD_REQUIRE
153 		    (pthread_create(&threads[i], NULL, busyFunction2, NULL));
154 	}
155 
156 	printf("Calling td_open(3)\n");
157 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
158 
159 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads2, &count) == TD_ERR_OK);
160 
161 	exiting2 = 1;
162 
163 	printf("Calling td_close(3)\n");
164 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
165 
166 	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
167 	    "counted threads (%d) != expected threads (%zu)",
168 	    count, MAX_THREADS + 1);
169 }
170 
171 ATF_TC(threads3);
172 ATF_TC_HEAD(threads3, tc)
173 {
174 
175 	atf_tc_set_md_var(tc, "descr",
176 	    "Asserts that for each td_thr_iter() call td_thr_info() is valid");
177 }
178 
179 static volatile int exiting3;
180 
181 static void *
182 busyFunction3(void *arg)
183 {
184 
185 	while (exiting3 == 0)
186 		usleep(50000);
187 
188 	return NULL;
189 }
190 
191 static int
192 iterateThreads3(td_thread_t *thread, void *arg)
193 {
194 	int *counter = (int *)arg;
195 	td_thread_info_t info;
196 
197 	ATF_REQUIRE(td_thr_info(thread, &info) == TD_ERR_OK);
198 
199 	++(*counter);
200 
201 	return TD_ERR_OK;
202 }
203 
204 ATF_TC_BODY(threads3, tc)
205 {
206 	struct td_proc_callbacks_t dummy_callbacks;
207 	td_proc_t *main_ta;
208 	size_t i;
209 	pthread_t threads[MAX_THREADS];
210 	int count = 0;
211 
212 	dummy_callbacks.proc_read	= basic_proc_read;
213 	dummy_callbacks.proc_write	= basic_proc_write;
214 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
215 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
216 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
217 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
218 
219 
220 	for (i = 0; i < MAX_THREADS; i++) {
221 		printf("Creating thread %zu\n", i);
222 		PTHREAD_REQUIRE
223 		    (pthread_create(&threads[i], NULL, busyFunction3, NULL));
224 	}
225 
226 	printf("Calling td_open(3)\n");
227 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
228 
229 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads3, &count) == TD_ERR_OK);
230 
231 	exiting3 = 1;
232 
233 	printf("Calling td_close(3)\n");
234 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
235 
236 	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
237 	    "counted threads (%d) != expected threads (%zu)",
238 	    count, MAX_THREADS + 1);
239 }
240 
241 ATF_TC(threads4);
242 ATF_TC_HEAD(threads4, tc)
243 {
244 
245 	atf_tc_set_md_var(tc, "descr",
246 	    "Asserts that for each td_thr_iter() call td_thr_getname() is "
247 	    "valid");
248 }
249 
250 static volatile int exiting4;
251 
252 static void *
253 busyFunction4(void *arg)
254 {
255 
256 	while (exiting4 == 0)
257 		usleep(50000);
258 
259 	return NULL;
260 }
261 
262 static int
263 iterateThreads4(td_thread_t *thread, void *arg)
264 {
265 	int *counter = (int *)arg;
266 	char name[PTHREAD_MAX_NAMELEN_NP];
267 
268 	ATF_REQUIRE(td_thr_getname(thread, name, sizeof(name)) == TD_ERR_OK);
269 
270 	printf("Thread name: %s\n", name);
271 
272 	++(*counter);
273 
274 	return TD_ERR_OK;
275 }
276 
277 ATF_TC_BODY(threads4, tc)
278 {
279 	struct td_proc_callbacks_t dummy_callbacks;
280 	td_proc_t *main_ta;
281 	size_t i;
282 	pthread_t threads[MAX_THREADS];
283 	int count = 0;
284 
285 	dummy_callbacks.proc_read	= basic_proc_read;
286 	dummy_callbacks.proc_write	= basic_proc_write;
287 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
288 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
289 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
290 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
291 
292 	for (i = 0; i < MAX_THREADS; i++) {
293 		printf("Creating thread %zu\n", i);
294 		PTHREAD_REQUIRE
295 		    (pthread_create(&threads[i], NULL, busyFunction4, NULL));
296 	}
297 
298 	for (i = 0; i < MAX_THREADS; i++) {
299 		PTHREAD_REQUIRE
300 		    (pthread_setname_np(threads[i], "test_%d", (void*)i));
301 	}
302 
303 	printf("Calling td_open(3)\n");
304 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
305 
306 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads4, &count) == TD_ERR_OK);
307 
308 	exiting4 = 1;
309 
310 	printf("Calling td_close(3)\n");
311 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
312 
313 	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
314 	    "counted threads (%d) != expected threads (%zu)",
315 	    count, MAX_THREADS + 1);
316 }
317 
318 ATF_TC(threads5);
319 ATF_TC_HEAD(threads5, tc)
320 {
321 
322 	atf_tc_set_md_var(tc, "descr",
323 	    "Asserts that td_thr_getname() handles shorter buffer parameter "
324 	    "and the result is properly truncated");
325 }
326 
327 static volatile int exiting5;
328 
329 static void *
330 busyFunction5(void *arg)
331 {
332 
333 	while (exiting5 == 0)
334 		usleep(50000);
335 
336 	return NULL;
337 }
338 
339 static int
340 iterateThreads5(td_thread_t *thread, void *arg)
341 {
342 	int *counter = (int *)arg;
343 	/* Arbitrarily short string buffer */
344 	char name[3];
345 
346 	ATF_REQUIRE(td_thr_getname(thread, name, sizeof(name)) == TD_ERR_OK);
347 
348 	printf("Thread name: %s\n", name);
349 
350 	/* strlen(3) does not count including a '\0' character */
351 	ATF_REQUIRE(strlen(name) < sizeof(name));
352 
353 	++(*counter);
354 
355 	return TD_ERR_OK;
356 }
357 
358 ATF_TC_BODY(threads5, tc)
359 {
360 	struct td_proc_callbacks_t dummy_callbacks;
361 	td_proc_t *main_ta;
362 	size_t i;
363 	pthread_t threads[MAX_THREADS];
364 	int count = 0;
365 
366 	dummy_callbacks.proc_read	= basic_proc_read;
367 	dummy_callbacks.proc_write	= basic_proc_write;
368 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
369 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
370 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
371 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
372 
373 	for (i = 0; i < MAX_THREADS; i++) {
374 		printf("Creating thread %zu\n", i);
375 		PTHREAD_REQUIRE
376 		    (pthread_create(&threads[i], NULL, busyFunction5, NULL));
377 	}
378 
379 	for (i = 0; i < MAX_THREADS; i++) {
380 		PTHREAD_REQUIRE
381 		    (pthread_setname_np(threads[i], "test_%d", (void*)i));
382 	}
383 
384 	printf("Calling td_open(3)\n");
385 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
386 
387 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads5, &count) == TD_ERR_OK);
388 
389 	exiting5 = 1;
390 
391 	printf("Calling td_close(3)\n");
392 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
393 
394 	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
395 	    "counted threads (%d) != expected threads (%zu)",
396 	    count, MAX_THREADS + 1);
397 }
398 
399 ATF_TC(threads6);
400 ATF_TC_HEAD(threads6, tc)
401 {
402 
403 	atf_tc_set_md_var(tc, "descr",
404 	    "Asserts that pthread_t can be translated with td_map_pth2thr() "
405 	    "to td_thread_t -- and assert earlier that td_thr_iter() call is "
406 	    "valid");
407 }
408 
409 static volatile int exiting6;
410 
411 static void *
412 busyFunction6(void *arg)
413 {
414 
415 	while (exiting6 == 0)
416 		usleep(50000);
417 
418 	return NULL;
419 }
420 
421 static int
422 iterateThreads6(td_thread_t *thread, void *arg)
423 {
424 	int *counter = (int *)arg;
425 
426 	++(*counter);
427 
428 	return TD_ERR_OK;
429 }
430 
431 ATF_TC_BODY(threads6, tc)
432 {
433 	struct td_proc_callbacks_t dummy_callbacks;
434 	td_proc_t *main_ta;
435 	size_t i;
436 	pthread_t threads[MAX_THREADS];
437 	int count = 0;
438 
439 	dummy_callbacks.proc_read	= basic_proc_read;
440 	dummy_callbacks.proc_write	= basic_proc_write;
441 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
442 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
443 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
444 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
445 
446 	for (i = 0; i < MAX_THREADS; i++) {
447 		printf("Creating thread %zu\n", i);
448 		PTHREAD_REQUIRE
449 		    (pthread_create(&threads[i], NULL, busyFunction6, NULL));
450 	}
451 
452 	printf("Calling td_open(3)\n");
453 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
454 
455 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads6, &count) == TD_ERR_OK);
456 
457 	for (i = 0; i < MAX_THREADS; i++) {
458 		td_thread_t *td_thread;
459 		ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread)
460 		    == TD_ERR_OK);
461 	}
462 
463 	exiting6 = 1;
464 
465 	printf("Calling td_close(3)\n");
466 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
467 
468 	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
469 	    "counted threads (%d) != expected threads (%zu)",
470 	    count, MAX_THREADS + 1);
471 }
472 
473 ATF_TC(threads7);
474 ATF_TC_HEAD(threads7, tc)
475 {
476 
477 	atf_tc_set_md_var(tc, "descr",
478 	    "Asserts that pthread_t can be translated with td_map_pth2thr() "
479 	    "to td_thread_t -- and assert later that td_thr_iter() call is "
480 	    "valid");
481 }
482 
483 static volatile int exiting7;
484 
485 static void *
486 busyFunction7(void *arg)
487 {
488 
489 	while (exiting7 == 0)
490 		usleep(50000);
491 
492 	return NULL;
493 }
494 
495 static int
496 iterateThreads7(td_thread_t *thread, void *arg)
497 {
498 	int *counter = (int *)arg;
499 
500 	++(*counter);
501 
502 	return TD_ERR_OK;
503 }
504 
505 ATF_TC_BODY(threads7, tc)
506 {
507 	struct td_proc_callbacks_t dummy_callbacks;
508 	td_proc_t *main_ta;
509 	size_t i;
510 	pthread_t threads[MAX_THREADS];
511 	int count = 0;
512 
513 	dummy_callbacks.proc_read	= basic_proc_read;
514 	dummy_callbacks.proc_write	= basic_proc_write;
515 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
516 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
517 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
518 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
519 
520 	for (i = 0; i < MAX_THREADS; i++) {
521 		printf("Creating thread %zu\n", i);
522 		PTHREAD_REQUIRE
523 		    (pthread_create(&threads[i], NULL, busyFunction7, NULL));
524 	}
525 
526 	printf("Calling td_open(3)\n");
527 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
528 
529 	for (i = 0; i < MAX_THREADS; i++) {
530 		td_thread_t *td_thread;
531 		ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread)
532 		    == TD_ERR_OK);
533 	}
534 
535 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads7, &count) == TD_ERR_OK);
536 
537 	exiting7 = 1;
538 
539 	printf("Calling td_close(3)\n");
540 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
541 
542 	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
543 	    "counted threads (%d) != expected threads (%zu)",
544 	    count, MAX_THREADS + 1);
545 }
546 
547 ATF_TC(threads8);
548 ATF_TC_HEAD(threads8, tc)
549 {
550 
551 	atf_tc_set_md_var(tc, "descr",
552 	    "Asserts that pthread_t can be translated with td_map_pth2thr() "
553 	    "to td_thread_t -- compare thread's name of pthread_t and "
554 	    "td_thread_t");
555 }
556 
557 static volatile int exiting8;
558 
559 static void *
560 busyFunction8(void *arg)
561 {
562 
563 	while (exiting8 == 0)
564 		usleep(50000);
565 
566 	return NULL;
567 }
568 
569 static int
570 iterateThreads8(td_thread_t *thread, void *arg)
571 {
572 	int *counter = (int *)arg;
573 
574 	++(*counter);
575 
576 	return TD_ERR_OK;
577 }
578 
579 ATF_TC_BODY(threads8, tc)
580 {
581 	struct td_proc_callbacks_t dummy_callbacks;
582 	td_proc_t *main_ta;
583 	size_t i;
584 	pthread_t threads[MAX_THREADS];
585 	int count = 0;
586 
587 	dummy_callbacks.proc_read	= basic_proc_read;
588 	dummy_callbacks.proc_write	= basic_proc_write;
589 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
590 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
591 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
592 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
593 
594 	for (i = 0; i < MAX_THREADS; i++) {
595 		printf("Creating thread %zu\n", i);
596 		PTHREAD_REQUIRE
597 		    (pthread_create(&threads[i], NULL, busyFunction8, NULL));
598 	}
599 
600 	printf("Calling td_open(3)\n");
601 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
602 
603 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads8, &count) == TD_ERR_OK);
604 
605 	for (i = 0; i < MAX_THREADS; i++) {
606 		td_thread_t *td_thread;
607 		char td_threadname[PTHREAD_MAX_NAMELEN_NP];
608 		char pth_threadname[PTHREAD_MAX_NAMELEN_NP];
609 		ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread)
610 		    == TD_ERR_OK);
611 		ATF_REQUIRE(td_thr_getname(td_thread, td_threadname,
612 		    sizeof(td_threadname)) == TD_ERR_OK);
613 		PTHREAD_REQUIRE(pthread_getname_np(threads[i], pth_threadname,
614 		    sizeof(pth_threadname)));
615 		ATF_REQUIRE(strcmp(td_threadname, pth_threadname) == 0);
616 	}
617 
618 	exiting8 = 1;
619 
620 	printf("Calling td_close(3)\n");
621 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
622 
623 	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
624 	    "counted threads (%d) != expected threads (%zu)",
625 	    count, MAX_THREADS + 1);
626 }
627 
628 ATF_TC(threads9);
629 ATF_TC_HEAD(threads9, tc)
630 {
631 
632 	atf_tc_set_md_var(tc, "descr",
633 	    "Asserts that pthread_t can be translated with td_map_pth2thr() "
634 	    "to td_thread_t -- assert that thread is in the TD_STATE_RUNNING "
635             "state");
636 }
637 
638 static volatile int exiting9;
639 
640 static void *
641 busyFunction9(void *arg)
642 {
643 
644 	while (exiting9 == 0)
645 		usleep(50000);
646 
647 	return NULL;
648 }
649 
650 static int
651 iterateThreads9(td_thread_t *thread, void *arg)
652 {
653 	int *counter = (int *)arg;
654 
655 	++(*counter);
656 
657 	return TD_ERR_OK;
658 }
659 
660 ATF_TC_BODY(threads9, tc)
661 {
662 	struct td_proc_callbacks_t dummy_callbacks;
663 	td_proc_t *main_ta;
664 	size_t i;
665 	pthread_t threads[MAX_THREADS];
666 	int count = 0;
667 
668 	dummy_callbacks.proc_read	= basic_proc_read;
669 	dummy_callbacks.proc_write	= basic_proc_write;
670 	dummy_callbacks.proc_lookup	= basic_proc_lookup;
671 	dummy_callbacks.proc_regsize	= dummy_proc_regsize;
672 	dummy_callbacks.proc_getregs	= dummy_proc_getregs;
673 	dummy_callbacks.proc_setregs	= dummy_proc_setregs;
674 
675 	for (i = 0; i < MAX_THREADS; i++) {
676 		printf("Creating thread %zu\n", i);
677 		PTHREAD_REQUIRE
678 		    (pthread_create(&threads[i], NULL, busyFunction9, NULL));
679 	}
680 
681 	printf("Calling td_open(3)\n");
682 	ATF_REQUIRE(td_open(&dummy_callbacks, NULL, &main_ta) == TD_ERR_OK);
683 
684 	for (i = 0; i < MAX_THREADS; i++) {
685 		td_thread_t *td_thread;
686 		td_thread_info_t info;
687 		ATF_REQUIRE(td_map_pth2thr(main_ta, threads[i], &td_thread)
688 		    == TD_ERR_OK);
689 		ATF_REQUIRE(td_thr_info(td_thread, &info) == TD_ERR_OK);
690 		ATF_REQUIRE_EQ(info.thread_state, TD_STATE_RUNNING);
691 	}
692 
693 	ATF_REQUIRE(td_thr_iter(main_ta, iterateThreads9, &count) == TD_ERR_OK);
694 
695 	exiting9 = 1;
696 
697 	printf("Calling td_close(3)\n");
698 	ATF_REQUIRE(td_close(main_ta) == TD_ERR_OK);
699 
700 	ATF_REQUIRE_EQ_MSG(count, MAX_THREADS + 1,
701 	    "counted threads (%d) != expected threads (%zu)",
702 	    count, MAX_THREADS + 1);
703 }
704 
705 ATF_TP_ADD_TCS(tp)
706 {
707 
708 	ATF_TP_ADD_TC(tp, threads1);
709 	ATF_TP_ADD_TC(tp, threads2);
710 	ATF_TP_ADD_TC(tp, threads3);
711 	ATF_TP_ADD_TC(tp, threads4);
712 	ATF_TP_ADD_TC(tp, threads5);
713 	ATF_TP_ADD_TC(tp, threads6);
714 	ATF_TP_ADD_TC(tp, threads7);
715 	ATF_TP_ADD_TC(tp, threads8);
716 	ATF_TP_ADD_TC(tp, threads9);
717 
718 	return atf_no_error();
719 }
720