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 SAFE_WAITPID(cleanup, pid, &status, WUNTRACED | WCONTINUED);
116 check_return(WEXITSTATUS(status), "initial.self ~= child.self",
117 "initial.self !~= child.self");
118 }
119
120 /* Testcase #02: fork inherit (cont.)
121 * expect: initial.children ~= 100MB, child.children = 0 */
inherit_fork2(void)122 static void inherit_fork2(void)
123 {
124 tst_resm(TINFO, "Testcase #02: fork inherit(cont.)");
125
126 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
127 tst_resm(TINFO, "initial.children = %ld", ru.ru_maxrss);
128 if (is_in_delta(ru.ru_maxrss - 102400))
129 tst_resm(TPASS, "initial.children ~= 100MB");
130 else
131 tst_resm(TFAIL, "initial.children !~= 100MB");
132
133 switch (pid = fork()) {
134 case -1:
135 tst_brkm(TBROK | TERRNO, cleanup, "fork #2");
136 case 0:
137 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
138 tst_resm(TINFO, "child.children = %ld", ru.ru_maxrss);
139 exit(ru.ru_maxrss == 0);
140 default:
141 break;
142 }
143
144 SAFE_WAITPID(cleanup, pid, &status, WUNTRACED | WCONTINUED);
145 check_return(WEXITSTATUS(status), "child.children == 0",
146 "child.children != 0");
147 }
148
149 /* Testcase #03: fork + malloc
150 * expect: initial.self + 50MB ~= child.self */
fork_malloc(void)151 static void fork_malloc(void)
152 {
153 tst_resm(TINFO, "Testcase #03: fork + malloc");
154
155 SAFE_GETRUSAGE(cleanup, RUSAGE_SELF, &ru);
156 tst_resm(TINFO, "initial.self = %ld", ru.ru_maxrss);
157
158 switch (pid = fork()) {
159 case -1:
160 tst_brkm(TBROK | TERRNO, cleanup, "fork #3");
161 case 0:
162 maxrss_init = ru.ru_maxrss;
163 tst_resm(TINFO, "child allocate +50MB");
164 consume(50);
165 SAFE_GETRUSAGE(cleanup, RUSAGE_SELF, &ru);
166 tst_resm(TINFO, "child.self = %ld", ru.ru_maxrss);
167 exit(is_in_delta(maxrss_init + 51200 - ru.ru_maxrss));
168 default:
169 break;
170 }
171
172 SAFE_WAITPID(cleanup, pid, &status, WUNTRACED | WCONTINUED);
173 check_return(WEXITSTATUS(status), "initial.self + 50MB ~= child.self",
174 "initial.self + 50MB !~= child.self");
175 }
176
177 /* Testcase #04: grandchild maxrss
178 * expect: post_wait.children ~= 300MB */
grandchild_maxrss(void)179 static void grandchild_maxrss(void)
180 {
181 tst_resm(TINFO, "Testcase #04: grandchild maxrss");
182
183 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
184 tst_resm(TINFO, "initial.children = %ld", ru.ru_maxrss);
185
186 switch (pid = fork()) {
187 case -1:
188 tst_brkm(TBROK | TERRNO, cleanup, "fork #4");
189 case 0:
190 retval = system("getrusage03_child -g 300");
191 if ((WIFEXITED(retval) && WEXITSTATUS(retval) != 0))
192 tst_brkm(TBROK | TERRNO, cleanup, "system");
193 exit(0);
194 default:
195 break;
196 }
197
198 SAFE_WAITPID(cleanup, pid, &status, WUNTRACED | WCONTINUED);
199 if (WEXITSTATUS(status) != 0)
200 tst_brkm(TBROK | TERRNO, cleanup, "child exit status is not 0");
201
202 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
203 tst_resm(TINFO, "post_wait.children = %ld", ru.ru_maxrss);
204 if (is_in_delta(ru.ru_maxrss - 307200))
205 tst_resm(TPASS, "child.children ~= 300MB");
206 else
207 tst_resm(TFAIL, "child.children !~= 300MB");
208 }
209
210 /* Testcase #05: zombie
211 * expect: initial ~= pre_wait, post_wait ~= 400MB */
zombie(void)212 static void zombie(void)
213 {
214 tst_resm(TINFO, "Testcase #05: zombie");
215
216 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
217 tst_resm(TINFO, "initial.children = %ld", ru.ru_maxrss);
218 maxrss_init = ru.ru_maxrss;
219
220 switch (pid = fork()) {
221 case -1:
222 tst_brkm(TBROK, cleanup, "fork #5");
223 case 0:
224 retval = system("getrusage03_child -n 400");
225 if ((WIFEXITED(retval) && WEXITSTATUS(retval) != 0))
226 tst_brkm(TBROK | TERRNO, cleanup, "system");
227 exit(0);
228 default:
229 break;
230 }
231
232 sleep(1); /* children become zombie */
233 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
234 tst_resm(TINFO, "pre_wait.children = %ld", ru.ru_maxrss);
235 if (is_in_delta(ru.ru_maxrss - maxrss_init))
236 tst_resm(TPASS, "initial.children ~= pre_wait.children");
237 else
238 tst_resm(TFAIL, "initial.children !~= pre_wait.children");
239
240 SAFE_WAITPID(cleanup, pid, &status, WUNTRACED | WCONTINUED);
241 if (WEXITSTATUS(status) != 0)
242 tst_brkm(TBROK | TERRNO, cleanup, "child exit status is not 0");
243
244 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
245 tst_resm(TINFO, "post_wait.children = %ld", ru.ru_maxrss);
246 if (is_in_delta(ru.ru_maxrss - 409600))
247 tst_resm(TPASS, "post_wait.children ~= 400MB");
248 else
249 tst_resm(TFAIL, "post_wait.children !~= 400MB");
250 }
251
252 /* Testcase #06: SIG_IGN
253 * expect: initial ~= after_zombie */
sig_ign(void)254 static void sig_ign(void)
255 {
256 tst_resm(TINFO, "Testcase #06: SIG_IGN");
257
258 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
259 tst_resm(TINFO, "initial.children = %ld", ru.ru_maxrss);
260 signal(SIGCHLD, SIG_IGN);
261 maxrss_init = ru.ru_maxrss;
262
263 switch (pid = fork()) {
264 case -1:
265 tst_brkm(TBROK, cleanup, "fork #6");
266 case 0:
267 retval = system("getrusage03_child -n 500");
268 if ((WIFEXITED(retval) && WEXITSTATUS(retval) != 0))
269 tst_brkm(TBROK | TERRNO, cleanup, "system");
270 exit(0);
271 default:
272 break;
273 }
274
275 sleep(1); /* children become zombie */
276 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
277 tst_resm(TINFO, "after_zombie.children = %ld", ru.ru_maxrss);
278 if (is_in_delta(ru.ru_maxrss - maxrss_init))
279 tst_resm(TPASS, "initial.children ~= after_zombie.children");
280 else
281 tst_resm(TFAIL, "initial.children !~= after_zombie.children");
282 signal(SIGCHLD, SIG_DFL);
283 }
284
285 /* Testcase #07: exec without fork
286 * expect: initial ~= fork */
exec_without_fork(void)287 static void exec_without_fork(void)
288 {
289 char str_maxrss_self[BUFSIZ], str_maxrss_child[BUFSIZ];
290 long maxrss_self, maxrss_child;
291
292 tst_resm(TINFO, "Testcase #07: exec without fork");
293
294 SAFE_GETRUSAGE(cleanup, RUSAGE_SELF, &ru);
295 maxrss_self = ru.ru_maxrss;
296 SAFE_GETRUSAGE(cleanup, RUSAGE_CHILDREN, &ru);
297 maxrss_child = ru.ru_maxrss;
298 tst_resm(TINFO, "initial.self = %ld, initial.children = %ld",
299 maxrss_self, maxrss_child);
300
301 sprintf(str_maxrss_self, "%ld", maxrss_self);
302 sprintf(str_maxrss_child, "%ld", maxrss_child);
303 if (execlp("getrusage03_child", "getrusage03_child", "-v",
304 "-s", str_maxrss_self, "-l", str_maxrss_child, NULL) == -1)
305 tst_brkm(TBROK | TERRNO, cleanup, "execlp");
306 }
307
is_in_delta(long value)308 static int is_in_delta(long value)
309 {
310 return (value >= -DELTA_MAX && value <= DELTA_MAX);
311 }
312
check_return(int status,char * pass_msg,char * fail_msg)313 static void check_return(int status, char *pass_msg, char *fail_msg)
314 {
315 switch (status) {
316 case 1:
317 tst_resm(TPASS, "%s", pass_msg);
318 break;
319 case 0:
320 tst_resm(TFAIL, "%s", fail_msg);
321 break;
322 default:
323 tst_resm(TFAIL, "child exit status is %d", status);
324 break;
325 }
326 }
327
consume(int mega)328 static void consume(int mega)
329 {
330 size_t sz;
331 void *ptr;
332
333 sz = mega * 1024 * 1024;
334 ptr = SAFE_MALLOC(cleanup, sz);
335 memset(ptr, 0, sz);
336 }
337
setup(void)338 static void setup(void)
339 {
340 /* Disable test if the version of the kernel is less than 2.6.32 */
341 if ((tst_kvercmp(2, 6, 32)) < 0) {
342 tst_resm(TCONF, "This ru_maxrss field is not supported");
343 tst_brkm(TCONF, NULL, "before kernel 2.6.32");
344 }
345
346 tst_sig(FORK, DEF_HANDLER, cleanup);
347
348 TEST_PAUSE;
349 }
350
cleanup(void)351 static void cleanup(void)
352 {
353 }
354