1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 static const stress_help_t help[] = {
28 	{ "B N","bigheap N",		"start N workers that grow the heap using calloc()" },
29 	{ NULL,	"bigheap-ops N",	"stop after N bogo bigheap operations" },
30 	{ NULL,	"bigheap-growth N",	"grow heap by N bytes per iteration" },
31 	{ NULL,	NULL,			NULL }
32 };
33 
34 /*
35  *  stress_set_bigheap_growth()
36  *  	Set bigheap growth from given opt arg string
37  */
stress_set_bigheap_growth(const char * opt)38 static int stress_set_bigheap_growth(const char *opt)
39 {
40 	uint64_t bigheap_growth;
41 
42 	bigheap_growth = stress_get_uint64_byte(opt);
43 	stress_check_range_bytes("bigheap-growth", bigheap_growth,
44 		MIN_BIGHEAP_GROWTH, MAX_BIGHEAP_GROWTH);
45 	return stress_set_setting("bigheap-growth", TYPE_ID_UINT64, &bigheap_growth);
46 }
47 
stress_bigheap_child(const stress_args_t * args,void * context)48 static int stress_bigheap_child(const stress_args_t *args, void *context)
49 {
50 	uint64_t bigheap_growth = DEFAULT_BIGHEAP_GROWTH;
51 	void *ptr = NULL, *last_ptr = NULL;
52 	const size_t page_size = args->page_size;
53 	const size_t stride = page_size;
54 	size_t size = 0;
55 	uint8_t *last_ptr_end = NULL;
56 
57 	(void)context;
58 
59 	if (!stress_get_setting("bigheap-growth", &bigheap_growth)) {
60 		if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
61 			bigheap_growth = MAX_BIGHEAP_GROWTH;
62 		if (g_opt_flags & OPT_FLAGS_MINIMIZE)
63 			bigheap_growth = MIN_BIGHEAP_GROWTH;
64 	}
65 	if (bigheap_growth < page_size)
66 		bigheap_growth = page_size;
67 
68 	/* Round growth size to nearest page size */
69 	bigheap_growth &= ~(page_size - 1);
70 
71 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
72 
73 	do {
74 		void *old_ptr = ptr;
75 		size += (size_t)bigheap_growth;
76 
77 		/*
78 		 * With many instances running it is wise to
79 		 * double check before the next realloc as
80 		 * sometimes process start up is delayed for
81 		 * some time and we should bail out before
82 		 * exerting any more memory pressure
83 		 */
84 		if (!keep_stressing(args))
85 			goto abort;
86 
87 		ptr = realloc(old_ptr, size);
88 		if (ptr == NULL) {
89 			pr_dbg("%s: out of memory at %" PRIu64
90 				" MB (instance %d)\n",
91 				args->name, (uint64_t)(4096ULL * size) >> 20,
92 				args->instance);
93 			free(old_ptr);
94 			size = 0;
95 		} else {
96 			size_t i, n;
97 			uint8_t *u8ptr, *tmp;
98 
99 			if (last_ptr == ptr) {
100 				tmp = u8ptr = last_ptr_end;
101 				n = (size_t)bigheap_growth;
102 			} else {
103 				tmp = u8ptr = ptr;
104 				n = size;
105 			}
106 			if (!keep_stressing(args))
107 				goto abort;
108 
109 			if (page_size > 0) {
110 				size_t sz = page_size - 1;
111 				uintptr_t pg_ptr = ((uintptr_t)ptr + sz) & ~sz;
112 				size_t len = size - (pg_ptr - (uintptr_t)ptr);
113 
114 				(void)stress_mincore_touch_pages_interruptible((void *)pg_ptr, len);
115 			}
116 
117 			for (i = 0; i < n; i+= stride, u8ptr += stride) {
118 				if (!keep_stressing(args))
119 					goto abort;
120 				*u8ptr = (uint8_t)i;
121 			}
122 
123 			if (g_opt_flags & OPT_FLAGS_VERIFY) {
124 				for (i = 0; i < n; i+= stride, tmp += stride) {
125 					if (!keep_stressing(args))
126 						goto abort;
127 					if (*tmp != (uint8_t)i)
128 						pr_fail("%s: byte at location %p was 0x%" PRIx8
129 							" instead of 0x%" PRIx8 "\n",
130 							args->name, (void *)u8ptr, *tmp, (uint8_t)i);
131 				}
132 			}
133 			last_ptr = ptr;
134 			last_ptr_end = u8ptr;
135 		}
136 		inc_counter(args);
137 	} while (keep_stressing(args));
138 abort:
139 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
140 
141 	free(ptr);
142 
143 	return EXIT_SUCCESS;
144 }
145 
146 /*
147  *  stress_bigheap()
148  *	stress heap allocation
149  */
stress_bigheap(const stress_args_t * args)150 static int stress_bigheap(const stress_args_t *args)
151 {
152 	return stress_oomable_child(args, NULL, stress_bigheap_child, STRESS_OOMABLE_NORMAL);
153 }
154 
155 static const stress_opt_set_func_t opt_set_funcs[] = {
156 	{ OPT_bigheap_growth,	stress_set_bigheap_growth },
157 	{ 0,			NULL },
158 };
159 
160 stressor_info_t stress_bigheap_info = {
161 	.stressor = stress_bigheap,
162 	.class = CLASS_OS | CLASS_VM,
163 	.opt_set_funcs = opt_set_funcs,
164 	.help = help
165 };
166