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