1 /*
2  * getrusage03 - test ru_maxrss behaviors in struct rusage
3  *
4  * This test program is backported from upstream commit:
5  * 1f10206cf8e945220f7220a809d8bfc15c21f9a5, which fills ru_maxrss
6  * value in struct rusage according to rss hiwater mark. To make sure
7  * this feature works correctly, a series of tests are executed in
8  * this program.
9  *
10  * Copyright (C) 2011  Red Hat, Inc.
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it would be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  * Further, this software is distributed without any warranty that it
20  * is free of the rightful claim of any third person regarding
21  * infringement or the like.  Any license provided herein, whether
22  * implied or otherwise, applies only to this software file.  Patent
23  * licenses, if any, provided herein do not apply to combinations of
24  * this program with other software, or any other product whatsoever.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29  * 02110-1301, USA.
30  */
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 #include <sys/resource.h>
34 #include <sys/time.h>
35 #include <sys/wait.h>
36 #include <unistd.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "test.h"
43 #include "safe_macros.h"
44 
45 char *TCID = "getrusage03";
46 int TST_TOTAL = 1;
47 
48 #define DELTA_MAX	10240
49 
50 static struct rusage ru;
51 static long maxrss_init;
52 static int retval, status;
53 static pid_t pid;
54 
55 static void inherit_fork(void);
56 static void inherit_fork2(void);
57 static void fork_malloc(void);
58 static void grandchild_maxrss(void);
59 static void zombie(void);
60 static void sig_ign(void);
61 static void exec_without_fork(void);
62 static void check_return(int status, char *pass_msg, char *fail_msg);
63 static int is_in_delta(long value);
64 static void consume(int mega);
65 static void setup(void);
66 static void cleanup(void);
67 
main(int argc,char * argv[])68 int main(int argc, char *argv[])
69 {
70 	int lc;
71 
72 	tst_parse_opts(argc, argv, NULL, NULL);
73 
74 	setup();
75 
76 	for (lc = 0; TEST_LOOPING(lc); lc++) {
77 		tst_count = 0;
78 
79 		tst_resm(TINFO, "allocate 100MB");
80 		consume(100);
81 
82 		inherit_fork();
83 		inherit_fork2();
84 		fork_malloc();
85 		grandchild_maxrss();
86 		zombie();
87 		sig_ign();
88 		exec_without_fork();
89 	}
90 	cleanup();
91 	tst_exit();
92 }
93 
94 /* Testcase #01: fork inherit
95  * expect: initial.self ~= child.self */
inherit_fork(void)96 static void inherit_fork(void)
97 {
98 	tst_resm(TINFO, "Testcase #01: fork inherit");
99 
100 	SAFE_GETRUSAGE(cleanup, RUSAGE_SELF, &ru);
101 	tst_resm(TINFO, "initial.self = %ld", ru.ru_maxrss);
102 
103 	switch (pid = fork()) {
104 	case -1:
105 		tst_brkm(TBROK | TERRNO, cleanup, "fork #1");
106 	case 0:
107 		maxrss_init = ru.ru_maxrss;
108 		SAFE_GETRUSAGE(cleanup, RUSAGE_SELF, &ru);
109 		tst_resm(TINFO, "child.self = %ld", ru.ru_maxrss);
110 		exit(is_in_delta(maxrss_init - ru.ru_maxrss));
111 	default:
112 		break;
113 	}
114 
115 	if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1)
116 		tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
117 	check_return(WEXITSTATUS(status), "initial.self ~= child.self",
118 		     "initial.self !~= child.self");
119 }
120 
121 /* Testcase #02: fork inherit (cont.)
122  * expect: initial.children ~= 100MB, child.children = 0 */
inherit_fork2(void)123 static void inherit_fork2(void)
124 {
125 	tst_resm(TINFO, "Testcase #02: fork inherit(cont.)");
126 
127 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
128 	tst_resm(TINFO, "initial.children = %ld", ru.ru_maxrss);
129 	if (is_in_delta(ru.ru_maxrss - 102400))
130 		tst_resm(TPASS, "initial.children ~= 100MB");
131 	else
132 		tst_resm(TFAIL, "initial.children !~= 100MB");
133 
134 	switch (pid = fork()) {
135 	case -1:
136 		tst_brkm(TBROK | TERRNO, cleanup, "fork #2");
137 	case 0:
138 		SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
139 		tst_resm(TINFO, "child.children = %ld", ru.ru_maxrss);
140 		exit(ru.ru_maxrss == 0);
141 	default:
142 		break;
143 	}
144 
145 	if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1)
146 		tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
147 	check_return(WEXITSTATUS(status), "child.children == 0",
148 		     "child.children != 0");
149 }
150 
151 /* Testcase #03: fork + malloc
152  * expect: initial.self + 50MB ~= child.self */
fork_malloc(void)153 static void fork_malloc(void)
154 {
155 	tst_resm(TINFO, "Testcase #03: fork + malloc");
156 
157 	SAFE_GETRUSAGE(cleanup, RUSAGE_SELF, &ru);
158 	tst_resm(TINFO, "initial.self = %ld", ru.ru_maxrss);
159 
160 	switch (pid = fork()) {
161 	case -1:
162 		tst_brkm(TBROK | TERRNO, cleanup, "fork #3");
163 	case 0:
164 		maxrss_init = ru.ru_maxrss;
165 		tst_resm(TINFO, "child allocate +50MB");
166 		consume(50);
167 		SAFE_GETRUSAGE(cleanup, RUSAGE_SELF, &ru);
168 		tst_resm(TINFO, "child.self = %ld", ru.ru_maxrss);
169 		exit(is_in_delta(maxrss_init + 51200 - ru.ru_maxrss));
170 	default:
171 		break;
172 	}
173 
174 	if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1)
175 		tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
176 	check_return(WEXITSTATUS(status), "initial.self + 50MB ~= child.self",
177 		     "initial.self + 50MB !~= child.self");
178 }
179 
180 /* Testcase #04: grandchild maxrss
181  * expect: post_wait.children ~= 300MB */
grandchild_maxrss(void)182 static void grandchild_maxrss(void)
183 {
184 	tst_resm(TINFO, "Testcase #04: grandchild maxrss");
185 
186 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
187 	tst_resm(TINFO, "initial.children = %ld", ru.ru_maxrss);
188 
189 	switch (pid = fork()) {
190 	case -1:
191 		tst_brkm(TBROK | TERRNO, cleanup, "fork #4");
192 	case 0:
193 		retval = system("getrusage03_child -g 300");
194 		if ((WIFEXITED(retval) && WEXITSTATUS(retval) != 0))
195 			tst_brkm(TBROK | TERRNO, cleanup, "system");
196 		exit(0);
197 	default:
198 		break;
199 	}
200 
201 	if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1)
202 		tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
203 	if (WEXITSTATUS(status) != 0)
204 		tst_brkm(TBROK | TERRNO, cleanup, "child exit status is not 0");
205 
206 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
207 	tst_resm(TINFO, "post_wait.children = %ld", ru.ru_maxrss);
208 	if (is_in_delta(ru.ru_maxrss - 307200))
209 		tst_resm(TPASS, "child.children ~= 300MB");
210 	else
211 		tst_resm(TFAIL, "child.children !~= 300MB");
212 }
213 
214 /* Testcase #05: zombie
215  * expect: initial ~= pre_wait, post_wait ~= 400MB */
zombie(void)216 static void zombie(void)
217 {
218 	tst_resm(TINFO, "Testcase #05: zombie");
219 
220 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
221 	tst_resm(TINFO, "initial.children = %ld", ru.ru_maxrss);
222 	maxrss_init = ru.ru_maxrss;
223 
224 	switch (pid = fork()) {
225 	case -1:
226 		tst_brkm(TBROK, cleanup, "fork #5");
227 	case 0:
228 		retval = system("getrusage03_child -n 400");
229 		if ((WIFEXITED(retval) && WEXITSTATUS(retval) != 0))
230 			tst_brkm(TBROK | TERRNO, cleanup, "system");
231 		exit(0);
232 	default:
233 		break;
234 	}
235 
236 	sleep(1);		/* children become zombie */
237 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
238 	tst_resm(TINFO, "pre_wait.children = %ld", ru.ru_maxrss);
239 	if (is_in_delta(ru.ru_maxrss - maxrss_init))
240 		tst_resm(TPASS, "initial.children ~= pre_wait.children");
241 	else
242 		tst_resm(TFAIL, "initial.children !~= pre_wait.children");
243 
244 	if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1)
245 		tst_brkm(TBROK | TERRNO, cleanup, "waitpid");
246 	if (WEXITSTATUS(status) != 0)
247 		tst_brkm(TBROK | TERRNO, cleanup, "child exit status is not 0");
248 
249 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
250 	tst_resm(TINFO, "post_wait.children = %ld", ru.ru_maxrss);
251 	if (is_in_delta(ru.ru_maxrss - 409600))
252 		tst_resm(TPASS, "post_wait.children ~= 400MB");
253 	else
254 		tst_resm(TFAIL, "post_wait.children !~= 400MB");
255 }
256 
257 /* Testcase #06: SIG_IGN
258  * expect: initial ~= after_zombie */
sig_ign(void)259 static void sig_ign(void)
260 {
261 	tst_resm(TINFO, "Testcase #06: SIG_IGN");
262 
263 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
264 	tst_resm(TINFO, "initial.children = %ld", ru.ru_maxrss);
265 	signal(SIGCHLD, SIG_IGN);
266 	maxrss_init = ru.ru_maxrss;
267 
268 	switch (pid = fork()) {
269 	case -1:
270 		tst_brkm(TBROK, cleanup, "fork #6");
271 	case 0:
272 		retval = system("getrusage03_child -n 500");
273 		if ((WIFEXITED(retval) && WEXITSTATUS(retval) != 0))
274 			tst_brkm(TBROK | TERRNO, cleanup, "system");
275 		exit(0);
276 	default:
277 		break;
278 	}
279 
280 	sleep(1);		/* children become zombie */
281 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
282 	tst_resm(TINFO, "after_zombie.children = %ld", ru.ru_maxrss);
283 	if (is_in_delta(ru.ru_maxrss - maxrss_init))
284 		tst_resm(TPASS, "initial.children ~= after_zombie.children");
285 	else
286 		tst_resm(TFAIL, "initial.children !~= after_zombie.children");
287 	signal(SIGCHLD, SIG_DFL);
288 }
289 
290 /* Testcase #07: exec without fork
291  * expect: initial ~= fork */
exec_without_fork(void)292 static void exec_without_fork(void)
293 {
294 	char str_maxrss_self[BUFSIZ], str_maxrss_child[BUFSIZ];
295 	long maxrss_self, maxrss_child;
296 
297 	tst_resm(TINFO, "Testcase #07: exec without fork");
298 
299 	SAFE_GETRUSAGE(cleanup, RUSAGE_SELF, &ru);
300 	maxrss_self = ru.ru_maxrss;
301 	SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
302 	maxrss_child = ru.ru_maxrss;
303 	tst_resm(TINFO, "initial.self = %ld, initial.children = %ld",
304 		 maxrss_self, maxrss_child);
305 
306 	sprintf(str_maxrss_self, "%ld", maxrss_self);
307 	sprintf(str_maxrss_child, "%ld", maxrss_child);
308 	if (execlp("getrusage03_child", "getrusage03_child", "-v",
309 		   "-s", str_maxrss_self, "-l", str_maxrss_child, NULL) == -1)
310 		tst_brkm(TBROK | TERRNO, cleanup, "execlp");
311 }
312 
is_in_delta(long value)313 static int is_in_delta(long value)
314 {
315 	return (value >= -DELTA_MAX && value <= DELTA_MAX);
316 }
317 
check_return(int status,char * pass_msg,char * fail_msg)318 static void check_return(int status, char *pass_msg, char *fail_msg)
319 {
320 	switch (status) {
321 	case 1:
322 		tst_resm(TPASS, "%s", pass_msg);
323 		break;
324 	case 0:
325 		tst_resm(TFAIL, "%s", fail_msg);
326 		break;
327 	default:
328 		tst_resm(TFAIL, "child exit status is %d", status);
329 		break;
330 	}
331 }
332 
consume(int mega)333 static void consume(int mega)
334 {
335 	size_t sz;
336 	void *ptr;
337 
338 	sz = mega * 1024 * 1024;
339 	ptr = SAFE_MALLOC(cleanup, sz);
340 	memset(ptr, 0, sz);
341 }
342 
setup(void)343 static void setup(void)
344 {
345 	/* Disable test if the version of the kernel is less than 2.6.32 */
346 	if ((tst_kvercmp(2, 6, 32)) < 0) {
347 		tst_resm(TCONF, "This ru_maxrss field is not supported");
348 		tst_brkm(TCONF, NULL, "before kernel 2.6.32");
349 	}
350 
351 	tst_sig(FORK, DEF_HANDLER, cleanup);
352 
353 	TEST_PAUSE;
354 }
355 
cleanup(void)356 static void cleanup(void)
357 {
358 }
359