-
Notifications
You must be signed in to change notification settings - Fork 21
/
allocate-loop.c
178 lines (148 loc) · 4.25 KB
/
allocate-loop.c
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
// allocate-loop.c
//
// This program allocates memory up and down in a loop, based on the user-provided amounts.
//
// USAGE:
//
// ./allocate-loop <MIN SIZE> <MAX SIZE>
//
// Both <MIN SIZE> and <MAX SIZE> must be integers, in units of MiB. For example, running:
//
// ./allocate-loop 128 512
//
// ... will gradually allocate from 128 MiB to 512 MiB and back down, repeating back and forth.
//
// The rate of allocation is set by internal constants, and is currently 256 MiB/s.
#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
// Inferred stats:
// * Change by 256 MiB / second
// * Print every 0.1s (approx. every 25 MiB)
const size_t step_size = 256 * (1 << 10); // 256 KiB
const int print_every = 100;
const long step_wait_ns = 1000*1000; // Wait 1ms between steps
size_t step(size_t current, size_t previous, size_t min, size_t max);
size_t get_rss();
char* get_time();
int main(int argc, char* argv[]) {
if (argc != 3) {
fprintf(stderr, "bad arguments");
return 1;
}
size_t min_size_mb;
size_t max_size_mb;
if (sscanf(argv[1], "%zu", &min_size_mb) != 1 || sscanf(argv[2], "%zu", &max_size_mb) != 1) {
fprintf(stderr, "bad arguments: must be integers\n");
return 1;
}
printf("Selected: minimum = %zu MiB, maximum = %zu MiB\n", min_size_mb, max_size_mb);
size_t mib = 1 << 20;
size_t min_size = min_size_mb * mib;
size_t max_size = max_size_mb * mib;
int memfd = memfd_create("experiment", 0);
if (memfd == -1) {
err(errno, "memfd");
}
if (ftruncate(memfd, max_size) == -1) {
err(errno, "memfd set to min_size");
}
char *map = mmap(NULL, max_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, memfd, 0);
if (map == (void *)-1) {
err(errno, "map");
}
memset(map, 0xAB, min_size);
printf("mmap created at minimum size\n");
// Run in a loop
size_t current_size = min_size;
size_t previous_size = current_size;
for (int i = 0; ; i++) {
size_t new_size = step(current_size, previous_size, min_size, max_size);
if (new_size > current_size) {
// to allocate more, just write to the pages:
memset(&map[current_size], 0xAB, new_size-current_size);
} else if (new_size < current_size) {
// to deallocate, use madvise() to tell the kernel it can reclaim some pages
if (madvise(&map[new_size], current_size-new_size, MADV_REMOVE) == -1) {
err(errno, "madvise");
}
} else {
fprintf(stderr,
"internal error: previous = %ld, current = %ld, new = %ld\n",
previous_size, current_size, new_size);
return 1;
}
if (i % print_every == 0) {
char* time = get_time();
size_t rss = get_rss();
printf("%s :: %ld MiB + %ld KiB, rss = %ld\n",
time,
new_size / (1 << 20),
(new_size % (1 << 20)) / (1 << 10),
rss);
}
previous_size = current_size;
current_size = new_size;
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = step_wait_ns;
nanosleep(&ts, NULL);
}
return 0;
}
size_t step(size_t current, size_t previous, size_t min, size_t max) {
if (current == min) {
return current + step_size;
} else if (current == max) {
return current - step_size;
} else {
return current + (current - previous);
}
}
// note: the returned string points into a static variable. Do not use old return values across
// calls to this function.
char* get_time() {
static char time[64] = {0};
struct timeval now;
gettimeofday(&now, NULL);
int milli = now.tv_usec / 1000;
char buf[32];
strftime(buf, sizeof(buf), "%H:%M:%S", gmtime(&now.tv_sec));
sprintf(time, "%s.%03d", buf, milli);
return time;
}
size_t get_rss() {
char* path;
if (asprintf(&path, "/proc/%d/status", getpid()) == -1) {
err(errno, "asprintf");
}
FILE* file;
if ((file = fopen(path, "r")) == NULL) {
err(errno, "fopen");
}
char* vmrss_prefix = "VmRSS:";
int prefix_len = strlen(vmrss_prefix);
char line_buf[256];
while (fgets(line_buf, sizeof(line_buf), file)) {
if (strncmp(line_buf, vmrss_prefix, prefix_len) == 0) {
size_t rss;
if (sscanf(line_buf + prefix_len, " %ld", &rss) != 1) {
err(errno, "sscanf");
}
fclose(file);
free(path);
return rss;
}
}
fprintf(stderr, "couldn't find VmRSS in %s\n", path);
exit(1);
}