aboutsummaryrefslogtreecommitdiff
path: root/testcases/kernel/syscalls/getrusage/getrusage04.c
blob: 06b576d79898f1ba2336d2702441bce491761413 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/*
 * getrusage04 - accuracy of getrusage() with RUSAGE_THREAD
 *
 * This program is used for testing the following upstream commit:
 * 761b1d26df542fd5eb348837351e4d2f3bc7bffe.
 *
 * getrusage() returns cpu resource usage with accuracy of 10ms
 * when RUSAGE_THREAD is specified to the argument who. Meanwhile,
 * accuracy is 1ms when RUSAGE_SELF is specified. This bad accuracy
 * of getrusage() caused a big impact on some application which is
 * critical to accuracy of cpu usage. The upstream fix removed
 * casts to clock_t in task_u/stime(), to keep granularity of
 * cputime_t over the calculation.
 *
 * Note that the accuracy on powerpc and s390x systems is always
 * 10ms when either RUSAGE_THREAD or RUSAGE_SELF specified, so
 * this test won't be executed on those platforms.
 *
 * Copyright (C) 2011  Red Hat, Inc.
 * Copyright (C) 2013  Cyril Hrubis <chrubis@suse.cz>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Further, this software is distributed without any warranty that it
 * is free of the rightful claim of any third person regarding
 * infringement or the like.  Any license provided herein, whether
 * implied or otherwise, applies only to this software file.  Patent
 * licenses, if any, provided herein do not apply to combinations of
 * this program with other software, or any other product whatsoever.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "test.h"
#include "safe_macros.h"
#include "lapi/posix_clocks.h"

char *TCID = "getrusage04";
int TST_TOTAL = 1;

#define RECORD_MAX    20
#define FACTOR_MAX    10

#ifndef RUSAGE_THREAD
#define RUSAGE_THREAD 1
#endif

static long BIAS_MAX;

static int opt_factor;
static char *factor_str;
static long factor_nr = 1;

option_t child_options[] = {
	{"m:", &opt_factor, &factor_str},
	{NULL, NULL, NULL}
};

static void fusage(void);
static void busyloop(long wait);
static void setup(void);
static void cleanup(void);

int main(int argc, char *argv[])
{
	struct rusage usage;
	unsigned long ulast, udelta, slast, sdelta;
	int i, lc;
	char msg_string[BUFSIZ];

	tst_parse_opts(argc, argv, child_options, fusage);

#if (__powerpc__) || (__powerpc64__) || (__s390__) || (__s390x__)
	tst_brkm(TCONF, NULL, "This test is not designed for current system");
#endif

	setup();

	if (opt_factor)
		factor_nr = SAFE_STRTOL(cleanup, factor_str, 0, FACTOR_MAX);

	tst_resm(TINFO, "Using %ld as multiply factor for max [us]time "
		 "increment (1000+%ldus)!", factor_nr, BIAS_MAX * factor_nr);

	for (lc = 0; TEST_LOOPING(lc); lc++) {
		tst_count = 0;
		i = 0;
		SAFE_GETRUSAGE(cleanup, RUSAGE_THREAD, &usage);
		tst_resm(TINFO, "utime:%12luus; stime:%12luus",
			 usage.ru_utime.tv_usec, usage.ru_stime.tv_usec);
		ulast = usage.ru_utime.tv_usec;
		slast = usage.ru_stime.tv_usec;

		while (i < RECORD_MAX) {
			SAFE_GETRUSAGE(cleanup, RUSAGE_THREAD, &usage);
			udelta = usage.ru_utime.tv_usec - ulast;
			sdelta = usage.ru_stime.tv_usec - slast;
			if (udelta > 0 || sdelta > 0) {
				i++;
				tst_resm(TINFO, "utime:%12luus; stime:%12luus",
					 usage.ru_utime.tv_usec,
					 usage.ru_stime.tv_usec);
				if ((long)udelta > 1000 + (BIAS_MAX * factor_nr)) {
					sprintf(msg_string,
						"utime increased > %ldus:",
						1000 + BIAS_MAX * factor_nr);
					tst_brkm(TFAIL, cleanup, msg_string,
						 " delta = %luus", udelta);
				}
				if ((long)sdelta > 1000 + (BIAS_MAX * factor_nr)) {
					sprintf(msg_string,
						"stime increased > %ldus:",
						1000 + BIAS_MAX * factor_nr);
					tst_brkm(TFAIL, cleanup, msg_string,
						 " delta = %luus", sdelta);
				}
			}
			ulast = usage.ru_utime.tv_usec;
			slast = usage.ru_stime.tv_usec;
			busyloop(100000);
		}
	}

	tst_resm(TPASS, "Test Passed");

	cleanup();
	tst_exit();
}

static void fusage(void)
{
	printf("  -m n    use n as multiply factor for max [us]time "
	       "increment (1000+(1000*n)us),\n          default value is 1\n");
}

static void busyloop(long wait)
{
	while (wait--) ;
}

/*
 * The resolution of getrusage timers currently depends on CONFIG_HZ settings,
 * as they are measured in jiffies.
 *
 * The problem is that there is no reasonable API to get either getrusage
 * timers resolution or duration of jiffie.
 *
 * Here we use clock_getres() with linux specific CLOCK_REALTIME_COARSE (added
 * in 2.6.32) which is also based on jiffies. This timer has the same
 * granularity as getrusage but it's not guaranteed and it may change in the
 * future.
 *
 * The default value for resolution was choosen to be 4ms as it corresponds to
 * CONFIG_HZ=250 which seems to be default value.
 */
static unsigned long guess_timer_resolution(void)
{
	struct timespec res;

	if (clock_getres(CLOCK_REALTIME_COARSE, &res)) {
		tst_resm(TINFO,
		        "CLOCK_REALTIME_COARSE not supported, using 4000 us");
		return 4000;
	}

	if (res.tv_nsec < 1000000 || res.tv_nsec > 10000000) {
		tst_resm(TINFO, "Unexpected CLOCK_REALTIME_COARSE resolution,"
		        " using 4000 us");
		return 4000;
	}

	tst_resm(TINFO, "Expected timers granularity is %li us",
	         res.tv_nsec / 1000);

	return res.tv_nsec / 1000;
}

static void setup(void)
{
	tst_sig(NOFORK, DEF_HANDLER, cleanup);

	if (tst_is_virt(VIRT_XEN) || tst_is_virt(VIRT_KVM) || tst_is_virt(VIRT_HYPERV))
		tst_brkm(TCONF, NULL, "This testcase is not supported on this"
		        " virtual machine.");

	BIAS_MAX = guess_timer_resolution();

	TEST_PAUSE;
}

static void cleanup(void)
{
}