1 /* -*- c++ -*- */
2 /*
3  * Copyright 2003,2013 Free Software Foundation, Inc.
4  *
5  * This file is part of GNU Radio
6  *
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  *
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "local_sighandler.h"
28 #include "vmcircbuf.h"
29 #include "vmcircbuf_prefs.h"
30 #include <assert.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <boost/format.hpp>
34 #include <stdexcept>
35 #include <vector>
36 
37 // all the factories we know about
38 #include "vmcircbuf_createfilemapping.h"
39 #include "vmcircbuf_mmap_shm_open.h"
40 #include "vmcircbuf_mmap_tmpfile.h"
41 #include "vmcircbuf_sysv_shm.h"
42 
43 gr::thread::mutex s_vm_mutex;
44 
45 namespace gr {
46 
47 static const char* FACTORY_PREF_KEY = "vmcircbuf_default_factory";
48 
~vmcircbuf()49 vmcircbuf::~vmcircbuf() {}
50 
~vmcircbuf_factory()51 vmcircbuf_factory::~vmcircbuf_factory() {}
52 
53 // ----------------------------------------------------------------
54 
55 static vmcircbuf_factory* s_default_factory = 0;
56 
get_default_factory()57 vmcircbuf_factory* vmcircbuf_sysconfig::get_default_factory()
58 {
59     if (s_default_factory)
60         return s_default_factory;
61 
62     bool verbose = false;
63 
64     std::vector<gr::vmcircbuf_factory*> all = all_factories();
65 
66     char name[1024];
67     if (gr::vmcircbuf_prefs::get(FACTORY_PREF_KEY, name, sizeof(name)) >= 0) {
68         for (unsigned int i = 0; i < all.size(); i++) {
69             if (strncmp(name, all[i]->name(), strlen(all[i]->name())) == 0) {
70                 s_default_factory = all[i];
71                 if (verbose)
72                     fprintf(stderr,
73                             "gr::vmcircbuf_sysconfig: using %s\n",
74                             s_default_factory->name());
75                 return s_default_factory;
76             }
77         }
78     }
79 
80     // either we don't have a default, or the default named is not in our
81     // list of factories.  Find the first factory that works.
82 
83     if (verbose)
84         fprintf(stderr, "gr::vmcircbuf_sysconfig: finding a working factory...\n");
85 
86     for (unsigned int i = 0; i < all.size(); i++) {
87         if (test_factory(all[i], verbose)) {
88             set_default_factory(all[i]);
89             return s_default_factory;
90         }
91     }
92 
93     // We're screwed!
94     fprintf(stderr, "gr::vmcircbuf_sysconfig: unable to find a working factory!\n");
95     throw std::runtime_error("gr::vmcircbuf_sysconfig");
96 }
97 
all_factories()98 std::vector<vmcircbuf_factory*> vmcircbuf_sysconfig::all_factories()
99 {
100     std::vector<vmcircbuf_factory*> result;
101 
102     result.push_back(gr::vmcircbuf_createfilemapping_factory::singleton());
103 #ifdef TRY_SHM_VMCIRCBUF
104     result.push_back(gr::vmcircbuf_sysv_shm_factory::singleton());
105     result.push_back(gr::vmcircbuf_mmap_shm_open_factory::singleton());
106 #endif
107     result.push_back(gr::vmcircbuf_mmap_tmpfile_factory::singleton());
108 
109     return result;
110 }
111 
set_default_factory(vmcircbuf_factory * f)112 void vmcircbuf_sysconfig::set_default_factory(vmcircbuf_factory* f)
113 {
114     gr::vmcircbuf_prefs::set(FACTORY_PREF_KEY, f->name());
115     s_default_factory = f;
116 }
117 
118 
119 // ------------------------------------------------------------------------
120 //		    test code for vmcircbuf factories
121 // ------------------------------------------------------------------------
122 
init_buffer(vmcircbuf * c,int counter,int size)123 static void init_buffer(vmcircbuf* c, int counter, int size)
124 {
125     unsigned int* p = (unsigned int*)c->pointer_to_first_copy();
126     for (unsigned int i = 0; i < size / sizeof(int); i++)
127         p[i] = counter + i;
128 }
129 
130 static bool
check_mapping(vmcircbuf * c,int counter,int size,const char * msg,bool verbose)131 check_mapping(vmcircbuf* c, int counter, int size, const char* msg, bool verbose)
132 {
133     bool ok = true;
134 
135     if (verbose)
136         fprintf(stderr, "... %s", msg);
137 
138     unsigned int* p1 = (unsigned int*)c->pointer_to_first_copy();
139     unsigned int* p2 = (unsigned int*)c->pointer_to_second_copy();
140 
141     // fprintf(stderr, "p1 = %p, p2 = %p\n", p1, p2);
142 
143     for (unsigned int i = 0; i < size / sizeof(int); i++) {
144         if (p1[i] != counter + i) {
145             ok = false;
146             if (verbose)
147                 fprintf(stderr, "  p1[%d] == %u, expected %u\n", i, p1[i], counter + i);
148             break;
149         }
150         if (p2[i] != counter + i) {
151             if (verbose)
152                 fprintf(stderr, "  p2[%d] == %u, expected %u\n", i, p2[i], counter + i);
153             ok = false;
154             break;
155         }
156     }
157 
158     if (ok && verbose) {
159         fprintf(stderr, "  OK\n");
160     }
161     return ok;
162 }
163 
memsize(int size)164 static const char* memsize(int size)
165 {
166     static std::string buf;
167     if (size >= (1 << 20)) {
168         buf = str(boost::format("%dMB") % (size / (1 << 20)));
169     } else if (size >= (1 << 10)) {
170         buf = str(boost::format("%dKB") % (size / (1 << 10)));
171     } else {
172         buf = str(boost::format("%d") % size);
173     }
174     return buf.c_str();
175 }
176 
177 static bool
test_a_bunch(vmcircbuf_factory * factory,int n,int size,int * start_ptr,bool verbose)178 test_a_bunch(vmcircbuf_factory* factory, int n, int size, int* start_ptr, bool verbose)
179 {
180     bool ok = true;
181     std::vector<int> counter(n);
182     std::vector<vmcircbuf*> c(n);
183     int cum_size = 0;
184 
185     for (int i = 0; i < n; i++) {
186         counter[i] = *start_ptr;
187         *start_ptr += size;
188         if ((c[i] = factory->make(size)) == 0) {
189             if (verbose)
190                 fprintf(
191                     stderr,
192                     "Failed to allocate gr::vmcircbuf number %d of size %d (cum = %s)\n",
193                     i + 1,
194                     size,
195                     memsize(cum_size));
196             return false;
197         }
198         init_buffer(c[i], counter[i], size);
199         cum_size += size;
200     }
201 
202     for (int i = 0; i < n; i++) {
203         std::string msg =
204             str(boost::format("test_a_bunch_%dx%s[%d]") % n % memsize(size) % i);
205         ok &= check_mapping(c[i], counter[i], size, msg.c_str(), verbose);
206     }
207 
208     for (int i = 0; i < n; i++) {
209         delete c[i];
210         c[i] = 0;
211     }
212 
213     return ok;
214 }
215 
standard_tests(vmcircbuf_factory * f,int verbose)216 static bool standard_tests(vmcircbuf_factory* f, int verbose)
217 {
218     if (verbose >= 1)
219         fprintf(stderr, "Testing %s...\n", f->name());
220 
221     bool v = verbose >= 2;
222     int granularity = f->granularity();
223     int start = 0;
224     bool ok = true;
225 
226     ok &= test_a_bunch(f, 1, 1 * granularity, &start, v); //   1 x   4KB =   4KB
227 
228     if (ok) {
229         ok &= test_a_bunch(f, 64, 4 * granularity, &start, v); //  64 x  16KB =   1MB
230         ok &= test_a_bunch(f, 4, 4 * (1L << 20), &start, v);   //   4 x   4MB =  16MB
231         //  ok &= test_a_bunch(f, 256, 256 * (1L << 10),  &start, v);  // 256 x 256KB =
232         //  64MB
233     }
234 
235     if (verbose >= 1)
236         fprintf(stderr, "....... %s: %s", f->name(), ok ? "OK\n" : "Doesn't work\n");
237 
238     return ok;
239 }
240 
test_factory(vmcircbuf_factory * f,int verbose)241 bool vmcircbuf_sysconfig::test_factory(vmcircbuf_factory* f, int verbose)
242 {
243     // Install local signal handlers for SIGSEGV and SIGBUS.
244     // If something goes wrong, these signals may be invoked.
245 
246 #ifdef SIGSEGV
247     gr::local_sighandler sigsegv(SIGSEGV, gr::local_sighandler::throw_signal);
248 #endif
249 #ifdef SIGBUS
250     gr::local_sighandler sigbus(SIGBUS, gr::local_sighandler::throw_signal);
251 #endif
252 #ifdef SIGSYS
253     gr::local_sighandler sigsys(SIGSYS, gr::local_sighandler::throw_signal);
254 #endif
255 
256     try {
257         return standard_tests(f, verbose);
258     } catch (gr::signal& sig) {
259         if (verbose) {
260             fprintf(stderr, "....... %s: %s", f->name(), "Doesn't work\n");
261             fprintf(stderr,
262                     "gr::vmcircbuf_factory::test_factory (%s): caught %s\n",
263                     f->name(),
264                     sig.name().c_str());
265             return false;
266         }
267     } catch (...) {
268         if (verbose) {
269             fprintf(stderr, "....... %s: %s", f->name(), "Doesn't work\n");
270             fprintf(stderr,
271                     "gr::vmcircbuf_factory::test_factory (%s): some kind of uncaught "
272                     "exception\n",
273                     f->name());
274         }
275         return false;
276     }
277     return false; // never gets here.  shut compiler up.
278 }
279 
test_all_factories(int verbose)280 bool vmcircbuf_sysconfig::test_all_factories(int verbose)
281 {
282     bool ok = false;
283 
284     std::vector<vmcircbuf_factory*> all = all_factories();
285 
286     for (unsigned int i = 0; i < all.size(); i++)
287         ok |= test_factory(all[i], verbose);
288 
289     return ok;
290 }
291 
292 } /* namespace gr */
293