/* -*- c++ -*- */ /* * Copyright 2003,2013 Free Software Foundation, Inc. * * This file is part of GNU Radio * * SPDX-License-Identifier: GPL-3.0-or-later * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "local_sighandler.h" #include "vmcircbuf.h" #include "vmcircbuf_prefs.h" #include <assert.h> #include <stdio.h> #include <string.h> #include <boost/format.hpp> #include <stdexcept> #include <vector> // all the factories we know about #include "vmcircbuf_createfilemapping.h" #include "vmcircbuf_mmap_shm_open.h" #include "vmcircbuf_mmap_tmpfile.h" #include "vmcircbuf_sysv_shm.h" gr::thread::mutex s_vm_mutex; namespace gr { static const char* FACTORY_PREF_KEY = "vmcircbuf_default_factory"; vmcircbuf::~vmcircbuf() {} vmcircbuf_factory::~vmcircbuf_factory() {} // ---------------------------------------------------------------- static vmcircbuf_factory* s_default_factory = 0; vmcircbuf_factory* vmcircbuf_sysconfig::get_default_factory() { if (s_default_factory) return s_default_factory; bool verbose = false; std::vector<gr::vmcircbuf_factory*> all = all_factories(); logger_ptr logger, debug_logger; gr::configure_default_loggers(logger, debug_logger, "vmcircbuf_sysconfig"); char name[1024]; if (gr::vmcircbuf_prefs::get(FACTORY_PREF_KEY, name, sizeof(name)) >= 0) { for (unsigned int i = 0; i < all.size(); i++) { if (strncmp(name, all[i]->name(), strlen(all[i]->name())) == 0) { s_default_factory = all[i]; GR_LOG_INFO(debug_logger, boost::format("Using %s") % s_default_factory->name()); return s_default_factory; } } } // either we don't have a default, or the default named is not in our // list of factories. Find the first factory that works. GR_LOG_INFO(debug_logger, "finding a working factory..."); for (unsigned int i = 0; i < all.size(); i++) { if (test_factory(all[i], verbose)) { set_default_factory(all[i]); return s_default_factory; } } // We're screwed! GR_LOG_ERROR(logger, "unable to find a working factory!"); throw std::runtime_error("gr::vmcircbuf_sysconfig"); } std::vector<vmcircbuf_factory*> vmcircbuf_sysconfig::all_factories() { std::vector<vmcircbuf_factory*> result; result.push_back(gr::vmcircbuf_createfilemapping_factory::singleton()); #ifdef TRY_SHM_VMCIRCBUF result.push_back(gr::vmcircbuf_sysv_shm_factory::singleton()); result.push_back(gr::vmcircbuf_mmap_shm_open_factory::singleton()); #endif result.push_back(gr::vmcircbuf_mmap_tmpfile_factory::singleton()); return result; } void vmcircbuf_sysconfig::set_default_factory(vmcircbuf_factory* f) { gr::vmcircbuf_prefs::set(FACTORY_PREF_KEY, f->name()); s_default_factory = f; } // ------------------------------------------------------------------------ // test code for vmcircbuf factories // ------------------------------------------------------------------------ static void init_buffer(const vmcircbuf& c, int counter, int size) { unsigned int* p = (unsigned int*)c.pointer_to_first_copy(); for (unsigned int i = 0; i < size / sizeof(int); i++) p[i] = counter + i; } static bool check_mapping( const vmcircbuf& c, int counter, int size, const char* msg, logger_ptr debug_logger) { bool ok = true; GR_LOG_INFO(debug_logger, msg); unsigned int* p1 = (unsigned int*)c.pointer_to_first_copy(); unsigned int* p2 = (unsigned int*)c.pointer_to_second_copy(); for (unsigned int i = 0; i < size / sizeof(int); i++) { if (p1[i] != counter + i) { ok = false; GR_LOG_INFO(debug_logger, boost::format("p1[%d] == %u, expected %u") % i % p1[i] % (counter + i)); break; } if (p2[i] != counter + i) { GR_LOG_ERROR(debug_logger, boost::format("p1[%d] == %u, expected %u") % i % p2[i] % (counter + i)); ok = false; break; } } if (ok) { GR_LOG_INFO(debug_logger, "mapping OK"); } return ok; } static const char* memsize(int size) { static std::string buf; if (size >= (1 << 20)) { buf = str(boost::format("%dMB") % (size / (1 << 20))); } else if (size >= (1 << 10)) { buf = str(boost::format("%dKB") % (size / (1 << 10))); } else { buf = str(boost::format("%d") % size); } return buf.c_str(); } static bool test_a_bunch(vmcircbuf_factory* factory, int n, int size, int* start_ptr, bool verbose) { bool ok = true; std::vector<int> counter(n); std::vector<std::unique_ptr<vmcircbuf>> c(n); int cum_size = 0; logger_ptr logger, debug_logger; gr::configure_default_loggers(logger, debug_logger, "gr::test_a_bunch"); for (int i = 0; i < n; i++) { counter[i] = *start_ptr; *start_ptr += size; if ((c[i] = std::unique_ptr<vmcircbuf>(factory->make(size))) == 0) { GR_LOG_INFO(debug_logger, boost::format("Failed to allocate gr::vmcircbuf " "number %d of size %d (cum = %s)") % (i + 1) % size % memsize(cum_size)) return false; } init_buffer(*c[i], counter[i], size); cum_size += size; } for (int i = 0; i < n; i++) { std::string msg = str(boost::format("test_a_bunch_%dx%s[%d]") % n % memsize(size) % i); ok = check_mapping(*c[i], counter[i], size, msg.c_str(), debug_logger) && ok; } return ok; } static bool standard_tests(vmcircbuf_factory* f, int verbose) { logger_ptr logger, debug_logger; gr::configure_default_loggers(logger, debug_logger, "standard_tests"); GR_LOG_INFO(debug_logger, boost::format("Testing %s...") % f->name()); bool v = verbose >= 2; int granularity = f->granularity(); int start = 0; bool ok = true; ok = test_a_bunch(f, 1, 1 * granularity, &start, v) && ok; // 1 x 4KB = 4KB if (ok) { ok = test_a_bunch(f, 64, 4 * granularity, &start, v) && ok; // 64 x 16KB = 1MB ok = test_a_bunch(f, 4, 4 * (1L << 20), &start, v) && ok; // 4 x 4MB = 16MB // ok = test_a_bunch(f, 256, 256 * (1L << 10), &start, v) && ok; // 256 x 256KB // = 64MB } GR_LOG_INFO(debug_logger, boost::format("%s: %s") % f->name() % (ok ? "OK" : "Doesn't work")) return ok; } bool vmcircbuf_sysconfig::test_factory(vmcircbuf_factory* f, int verbose) { logger_ptr logger, debug_logger; gr::configure_default_loggers(logger, debug_logger, "gr::vmcircbuf_sysconfig"); // Install local signal handlers for SIGSEGV and SIGBUS. // If something goes wrong, these signals may be invoked. #ifdef SIGSEGV gr::local_sighandler sigsegv(SIGSEGV, gr::local_sighandler::throw_signal); #endif #ifdef SIGBUS gr::local_sighandler sigbus(SIGBUS, gr::local_sighandler::throw_signal); #endif #ifdef SIGSYS gr::local_sighandler sigsys(SIGSYS, gr::local_sighandler::throw_signal); #endif try { return standard_tests(f, verbose); } catch (gr::signal& sig) { GR_LOG_INFO(debug_logger, boost::format("vmcircbuf_factory::test_factory (%s): caught %s") % f->name() % sig.name().c_str()) return false; } catch (...) { GR_LOG_WARN(debug_logger, boost::format("vmcircbuf_factory::test_factory (%s) some " "kind of uncaught exception.") % f->name()) return false; } return false; // never gets here. shut compiler up. } bool vmcircbuf_sysconfig::test_all_factories(int verbose) { bool ok = false; std::vector<vmcircbuf_factory*> all = all_factories(); for (unsigned int i = 0; i < all.size(); i++) ok |= test_factory(all[i], verbose); return ok; } } /* namespace gr */