1 #include "cado.h" // IWYU pragma: keep
2 // IWYU pragma: no_include <ext/alloc_traits.h>
3 #include <cstdio>
4 #include <cstring>
5 #include <vector>
6 #include <memory>
7 #include <string>
8 #include <sstream>
9 // #include <sys/time.h>
10 #include "mmap_allocator.hpp"
11 #include "mmappable_vector.hpp"
12 #include "portability.h" // strdup // IWYU pragma: keep
13 #include "macros.h"
14
15 /* This is inspired from https://github.com/johannesthoma/mmap_allocator
16 * License is LGPL.
17 * The version here has been trimmed down significantly, look for uptream
18 * version for more features */
19
20 using namespace std;
21 using namespace mmap_allocator_details;
22
23
24 const char * tmpdir = "/tmp";
25
26 const char * TESTFILE;
27 const char * TESTFILE2;
28
generate_test_file(int count,const char * fname)29 void generate_test_file(int count, const char *fname)
30 {
31 FILE * f = fopen(fname, "w+");
32 for (int i=0;i<count;i++) {
33 fwrite(&i, 1, sizeof(i), f);
34 }
35 fclose(f);
36 }
37
test_test_file(int count,const char * fname,bool expect_zeros)38 void test_test_file(int count, const char * fname, bool expect_zeros)
39 {
40 FILE * f = fopen(fname, "r");
41 for (int i=0;i<count;i++) {
42 int j;
43 int n = fread(&j, 1, sizeof(j), f);
44 ASSERT_ALWAYS(n == (int) sizeof(j));
45 ASSERT_ALWAYS(j == (expect_zeros ? 0 : i));
46 }
47 fclose(f);
48 }
49
test_mmap(void)50 void test_mmap(void)
51 {
52 generate_test_file(1024, TESTFILE);
53 generate_test_file(1024*1024, TESTFILE2);
54
55 {
56 fprintf(stderr, "Testing R/O mapping\n");
57 mmapped_file M(TESTFILE, READ_ONLY);
58 mmappable_vector<int> int_vec_default(mmap_allocator<int>(M, 0, 1024));
59 int_vec_default.mmap(1024);
60 ASSERT_ALWAYS(int_vec_default.size() == 1024);
61 for (int i=0;i<1024;i++) {
62 // this will segfault because the mapping is read-only.
63 // int_vec_default[i] = i;
64 ASSERT_ALWAYS(int_vec_default[i] == i); /* just to be sure */
65 }
66 test_test_file(1024, TESTFILE, false);
67 }
68
69 /* Now do the same test read-write */
70 {
71 fprintf(stderr, "Testing private R/W mapping\n");
72 mmapped_file M(TESTFILE, READ_WRITE_PRIVATE);
73 mmappable_vector<int> int_vec_default(mmap_allocator<int>(M, 0, 1024));
74 int_vec_default.mmap(1024);
75 ASSERT_ALWAYS(int_vec_default.size() == 1024);
76 for (int i=0;i<1024;i++) {
77 int_vec_default[i] = 0; /* should not segfault */
78 }
79 /* because the mapping is private, we should still have the
80 * normal stuff */
81 test_test_file(1024, TESTFILE, false);
82 }
83
84 /* If we test that with a shared read-write, this will be
85 * different */
86 {
87 fprintf(stderr, "Testing shared R/W mapping\n");
88 mmapped_file M(TESTFILE, READ_WRITE_SHARED);
89 mmappable_vector<int> int_vec_default(mmap_allocator<int>(M, 0, 1024));
90 int_vec_default.mmap(1024);
91 ASSERT_ALWAYS(int_vec_default.size() == 1024);
92 for (int i=0;i<1024;i++) {
93 int_vec_default[i] = 0; /* should not segfault */
94 }
95 /* because the mapping is shared, we should see zeroes now. */
96 test_test_file(1024, TESTFILE, true);
97
98 /* clean up our mess */
99 generate_test_file(1024, TESTFILE);
100 }
101
102 /* Now how does it go if we map only part of a file */
103 {
104 fprintf(stderr, "Testing fragment mapping\n");
105 mmapped_file M(TESTFILE2, READ_WRITE_SHARED, 8000, 1040576);
106 mmappable_vector<int> int_vec_default(mmap_allocator<int>(M, 8000, 1024));
107 int_vec_default.mmap(1024);
108 ASSERT_ALWAYS(int_vec_default.size() == 1024);
109 for (int i=0;i<1024;i++) {
110 ASSERT_ALWAYS(int_vec_default[i] == i + 2000); /* just to be sure */
111 }
112 }
113
114 /* explicitly zero-initialize elements, but on a private mapping,
115 * so that we don't see the result in the file.
116 */
117 {
118 fprintf(stderr, "Testing value-initialized + private mapping\n");
119 mmapped_file M(TESTFILE, READ_WRITE_PRIVATE);
120 mmappable_vector<int> int_vec_default(1024, 0, mmap_allocator<int>(M, 0, 1024));
121 ASSERT_ALWAYS(int_vec_default.size() == 1024);
122 for (int i=0;i<1024;i++) {
123 ASSERT_ALWAYS(int_vec_default[i] == 0); /* just to be sure */
124 }
125 test_test_file(1024, TESTFILE, false);
126 }
127
128 /* on a shared mapping, we're supposed to get the zeroes */
129 {
130 fprintf(stderr, "Testing value-initialized + shared mapping\n");
131 mmapped_file M(TESTFILE, READ_WRITE_SHARED);
132 mmappable_vector<int> int_vec_default(1024, 0, mmap_allocator<int>(M, 0, 1024));
133 ASSERT_ALWAYS(int_vec_default.size() == 1024);
134 for (int i=0;i<1024;i++) {
135 ASSERT_ALWAYS(int_vec_default[i] == 0); /* just to be sure */
136 }
137 test_test_file(1024, TESTFILE, true);
138
139 /* clean up our mess */
140 generate_test_file(1024, TESTFILE);
141 }
142 }
143
test_conversion(void)144 void test_conversion(void)
145 {
146 fprintf(stderr, "Testing conversion between STL vector and mmap vector.\n");
147 generate_test_file(1024, TESTFILE);
148
149 mmappable_vector<int> mmap_vector;
150 mmap_vector.mmap_file(TESTFILE, READ_ONLY, 0, 1024);
151 for (int i=0;i<1024;i++) {
152 ASSERT_ALWAYS(mmap_vector[i] == i);
153 }
154
155 vector<int> std_vector(mmap_vector.begin(), mmap_vector.end());
156 for (int i=0;i<1024;i++) {
157 ASSERT_ALWAYS(std_vector[i] == i);
158 }
159 for (int i=0;i<1024;i++) {
160 std_vector[i] *= 2;
161 }
162 mmappable_vector<int> mmap_vector2(std_vector.begin(), std_vector.end());
163 for (int i=0;i<1024;i++) {
164 ASSERT_ALWAYS(mmap_vector2[i] == i*2);
165 }
166 }
167
test_shortcut_interface(void)168 void test_shortcut_interface(void)
169 {
170 fprintf(stderr, "Testing shortcut interface\n");
171
172 generate_test_file(1024, TESTFILE);
173
174 mmappable_vector<int> vec;
175 vec.mmap_file(TESTFILE, READ_ONLY, 0, 1024);
176
177 for (int i=0;i<1024;i++) {
178 ASSERT_ALWAYS(vec[i] == i);
179 }
180 try {
181 /* This is expected to fail, because the vector is already mapped */
182 vec.mmap_file(TESTFILE, READ_ONLY, 0, 1024);
183 ASSERT_ALWAYS(0);
184 } catch (mmap_allocator_exception const & e) {
185 fprintf(stderr, "Exception message (expected): %s\n", e.what());
186 }
187 vec.munmap_file();
188
189 generate_test_file(2048, TESTFILE);
190 vec.mmap_file(TESTFILE, READ_ONLY, 4096, 1024);
191 for (int i=0;i<1024;i++) {
192 ASSERT_ALWAYS(vec[i] == i+1024);
193 }
194 }
195
test_cache_bug(void)196 void test_cache_bug(void)
197 {
198 mmappable_vector<int> vec;
199
200 fprintf(stderr, "Testing if wrong offset bug in pool is fixed.\n");
201 generate_test_file(2048, TESTFILE);
202 vec.mmap_file(TESTFILE, READ_ONLY, 4096, 1024);
203
204 for (int i=0;i<1024;i++) {
205 ASSERT_ALWAYS(vec[i] == i+1024);
206 }
207 }
208
209 #define FILESIZE (1024*1024*16)
210
read_large_file(enum access_mode mode)211 void read_large_file(enum access_mode mode)
212 {
213 // struct timeval t, t2;
214 mmappable_vector<int> vec;
215
216 // gettimeofday(&t, NULL);
217
218 vec.mmap_file(TESTFILE, mode, 0, FILESIZE);
219 for (int i=0;i<FILESIZE;i++) {
220 ASSERT_ALWAYS(vec[i] == i);
221 }
222 // gettimeofday(&t2, NULL);
223 // fprintf(stderr, "Mode: %d Time: %lu.%06lu\n", mode, (t2.tv_sec - t.tv_sec)-(t2.tv_usec < t.tv_usec), (t2.tv_usec < t.tv_usec)*1000000 + (t2.tv_usec - t.tv_usec));
224 }
225
test_large_file(void)226 void test_large_file(void)
227 {
228 fprintf(stderr, "Testing large file.\n");
229 generate_test_file(FILESIZE, TESTFILE); /* 1G */
230
231 read_large_file(READ_ONLY);
232 read_large_file(READ_WRITE_PRIVATE);
233 read_large_file(READ_WRITE_SHARED);
234 }
235
test_multiple_open(void)236 void test_multiple_open(void)
237 {
238 generate_test_file(1024, TESTFILE);
239 generate_test_file(1024, TESTFILE2);
240
241 /* first we create a file mapping for each vector, which causes many
242 * different mmap() calls to be issued */
243 {
244 fprintf(stderr, "Testing multiple open (you need to strace this).\n");
245 mmappable_vector<int> vec1, vec2, vec3, vec4;
246 vec1.mmap_file(TESTFILE, READ_ONLY, 0, 1024);
247 vec2.mmap_file(TESTFILE, READ_ONLY, 0, 1024);
248 vec3.mmap_file(TESTFILE2, READ_ONLY, 0, 1024);
249 vec4.mmap_file(TESTFILE2, READ_ONLY, 0, 1024);
250 }
251
252 /* It is better to specifically create a mapping for the file, and
253 * use it. That way, we have only one mmap() per file */
254 {
255 mmapped_file M(TESTFILE, READ_ONLY);
256 mmapped_file M2(TESTFILE2, READ_ONLY);
257 fprintf(stderr, "Testing multiple open (you need to strace this).\n");
258 mmappable_vector<int> vec1(mmap_allocator<int>(M, 0, 1024));
259 mmappable_vector<int> vec2(mmap_allocator<int>(M, 0, 1024));
260 mmappable_vector<int> vec3(mmap_allocator<int>(M, 0, 1024));
261 mmappable_vector<int> vec4(mmap_allocator<int>(M, 0, 1024));
262 vec1.mmap(1024);
263 vec2.mmap(1024);
264 vec3.mmap(1024);
265 vec4.mmap(1024);
266 }
267 }
268
test_allocate_0_bytes(void)269 void test_allocate_0_bytes(void) /* shouldn't segfault */
270 {
271 fprintf(stderr, "Testing vectors of mmappable_vectors.\n");
272
273 vector<mmappable_vector<int> > vecs;
274 vecs.resize(2);
275 for (int i=0; i<2; i++) {
276 vecs[i].mmap_file(TESTFILE, READ_ONLY, 0, 1024);
277 for (int j=0;j<1024;j++) {
278 ASSERT_ALWAYS(vecs[i][j] == j);
279 }
280 }
281 }
282
283 // coverity[root_function]
main(int argc,char * argv[])284 int main(int argc, char * argv[])
285 {
286 if (argc == 3 && std::string(argv[1]) == "--tmpdir") {
287 tmpdir = argv[2];
288 }
289 TESTFILE = strdup((std::string(tmpdir) + "/testfile").c_str());
290 TESTFILE2 = strdup((std::string(tmpdir) + "/testfile2").c_str());
291
292 test_mmap();
293 test_conversion();
294 test_cache_bug();
295
296 test_shortcut_interface();
297 test_large_file();
298 test_multiple_open();
299 test_allocate_0_bytes();
300 }
301