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
45char *TCID = "getrusage03";
46int TST_TOTAL = 1;
47
48#define DELTA_MAX	10240
49
50static struct rusage ru;
51static long maxrss_init;
52static int retval, status;
53static pid_t pid;
54
55static void inherit_fork(void);
56static void inherit_fork2(void);
57static void fork_malloc(void);
58static void grandchild_maxrss(void);
59static void zombie(void);
60static void sig_ign(void);
61static void exec_without_fork(void);
62static void check_return(int status, char *pass_msg, char *fail_msg);
63static int is_in_delta(long value);
64static void consume(int mega);
65static void setup(void);
66static void cleanup(void);
67
68int 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 */
96static 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 */
123static 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 */
153static 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 */
182static 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 */
216static 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 */
259static 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 */
292static 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
313static int is_in_delta(long value)
314{
315	return (value >= -DELTA_MAX && value <= DELTA_MAX);
316}
317
318static 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
333static 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
343static 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
356static void cleanup(void)
357{
358}
359