1/******************************************************************************
2 *
3 *   Copyright © International Business Machines  Corp., 2007, 2008
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program 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
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * NAME
20 *    tc-2.c
21 *
22 * DESCRIPTION
23 *    Check if clock_gettime is working properly.
24 *    This test creates NUMSLEEP threads that just sleep and NUMWORK threads
25 *    that spend time on the CPU. It then reads the thread cpu clocks of all
26 *    these threads and compares the sum of thread cpu clocks with the process
27 *    cpu clock value. The test expects that:
28 *    the cpu clock of every sleeping thread shows close to zero value.
29 *    sum of cpu clocks of all threads is comparable with the process cpu clock.
30 *
31 *
32 * USAGE:
33 *    Use run_auto.sh script in current directory to build and run test.
34 *
35 * AUTHOR
36 *    Sripathi Kodi <sripathik@in.ibm.com>
37 *
38 * HISTORY
39 *    2007-Apr-04:  Initial version by Sripathi Kodi <sripathik@in.ibm.com>
40 *
41 *****************************************************************************/
42
43#include <stdio.h>
44#include <pthread.h>
45#include <time.h>
46#include <errno.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <librttest.h>
50
51#define NS_PER_SEC 1000000000
52#define THRESHOLD 0.5		/* 500 milliseconds */
53#define NUMSLEEP 5
54#define NUMWORK 2
55
56struct timespec sleepts[NUMSLEEP];
57struct timespec workts[NUMWORK];
58
59void usage(void)
60{
61	rt_help();
62	printf("thread_clock specific options:\n");
63}
64
65int parse_args(int c, char *v)
66{
67	int handled = 1;
68	switch (c) {
69	case 'h':
70		usage();
71		exit(0);
72	default:
73		handled = 0;
74		break;
75	}
76	return handled;
77}
78
79/* Just spend some time on the CPU */
80void work(void)
81{
82	unsigned int i = 0;
83	for (i = 0; i < 2147483600; i++) {
84		if ((i == i + 1) || (i == i - 1))
85			printf("Hey!\n");
86	}
87}
88
89void *workerthread(void *arg)
90{
91	struct thread *pthr = (struct thread *)arg;
92	int tid = (int)(long)pthr->arg;
93	struct timespec *ts = &workts[tid];
94
95#ifdef DEBUG
96	printf("Worker thread %d working\n", tid);
97#endif
98	work();
99
100	if ((clock_gettime(CLOCK_THREAD_CPUTIME_ID, ts)) < 0) {
101		perror("clock_gettime: CLOCK_THREAD_CPUTIME_ID: ");
102		exit(1);
103	}
104#ifdef DEBUG
105	printf("workerthread %d: AFTER WORK: tv_sec = %ld, tv_nsec = %ld\n",
106	       tid, ts->tv_sec, ts->tv_nsec);
107#endif
108	return NULL;
109}
110
111void *sleeperthread(void *arg)
112{
113	struct thread *pthr = (struct thread *)arg;
114	int tid = (int)(long)pthr->arg;
115	struct timespec *ts = &sleepts[tid];
116
117#ifdef DEBUG
118	printf("Sleeper thread %d sleeping\n", tid);
119#endif
120
121	sleep(5);
122
123	if ((clock_gettime(CLOCK_THREAD_CPUTIME_ID, ts)) < 0) {
124		perror("clock_gettime: CLOCK_THREAD_CPUTIME_ID: ");
125		exit(1);
126	}
127#ifdef DEBUG
128	printf("sleeperthread %d: AFTER SLEEP: tv_sec = %ld, tv_nsec = %ld\n",
129	       tid, ts->tv_sec, ts->tv_nsec);
130#endif
131	return NULL;
132}
133
134int checkresult(float proctime)
135{
136	int i, retval = 0;
137	float diff, threadstime = 0;
138	for (i = 0; i < NUMSLEEP; i++) {
139		/* Sleeping thread should not accumulate more than 1 second of CPU time */
140		if (sleepts[i].tv_sec > 0) {
141			printf
142			    ("Sleeper thread %d time is %f, should have been close to zero. FAIL\n",
143			     i,
144			     sleepts[i].tv_sec +
145			     ((float)sleepts[i].tv_nsec / NS_PER_SEC));
146			retval = 1;
147		}
148		threadstime +=
149		    sleepts[i].tv_sec +
150		    ((float)sleepts[i].tv_nsec / NS_PER_SEC);
151	}
152	if (retval)
153		return retval;
154
155	for (i = 0; i < NUMWORK; i++) {
156		threadstime +=
157		    workts[i].tv_sec + ((float)workts[i].tv_nsec / NS_PER_SEC);
158	}
159	diff = proctime - threadstime;
160	if (diff < 0)
161		diff = -diff;
162	printf("Process: %.4f s\n", proctime);
163	printf("Threads: %.4f s\n", threadstime);
164	printf("Delta:   %.4f s\n", diff);
165	/* Difference between the sum of thread times and process time
166	 * should not be more than pass_criteria */
167	printf("\nCriteria: Delta < %.4f s\n", pass_criteria);
168	printf("Result: ");
169	if (diff > pass_criteria) {
170		printf("FAIL\n");
171		retval = 1;
172	} else {
173		printf("PASS\n");
174	}
175	return retval;
176}
177
178int main(int argc, char *argv[])
179{
180	int i, retval = 0;
181	struct timespec myts;
182	setup();
183
184	pass_criteria = THRESHOLD;
185	rt_init("ht:", parse_args, argc, argv);
186
187	/* Start sleeper threads */
188	for (i = 0; i < NUMSLEEP; i++) {
189		if ((create_other_thread(sleeperthread, (void *)(intptr_t) i)) <
190		    0) {
191			exit(1);
192		}
193	}
194	printf("\n%d sleeper threads created\n", NUMSLEEP);
195
196	/* Start worker threads */
197	for (i = 0; i < NUMWORK; i++) {
198		if ((create_other_thread(workerthread, (void *)(intptr_t) i)) <
199		    0) {
200			exit(1);
201		}
202	}
203	printf("\n%d worker threads created\n", NUMWORK);
204
205	printf("\nPlease wait...\n\n");
206
207	join_threads();
208	/* Get the process cpu clock value */
209	if ((clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &myts)) < 0) {
210		perror("clock_gettime: CLOCK_PROCESS_CPUTIME_ID: ");
211		exit(1);
212	}
213	retval = checkresult(myts.tv_sec + ((float)myts.tv_nsec / NS_PER_SEC));
214	return retval;
215}
216