#include <cassert>
#include "main/snort_config.h"
+#include "main/snort_debug.h"
#include "main/thread.h"
#include "profiler/memory_profiler_active_context.h"
#include "memory_config.h"
#include "catch/catch.hpp"
#endif
-template<typename Tracker, typename Handler>
-static inline bool free_space(size_t requested, size_t cap, Tracker& trk, Handler& handler)
-{
- assert(requested <= cap);
- const auto required = cap - requested;
-
- if ( trk.used() > required )
- handler();
-
- return trk.used() <= required;
-}
-
namespace memory
{
-// -----------------------------------------------------------------------------
-// helpers
-// -----------------------------------------------------------------------------
+namespace
+{
struct Tracker
{
constexpr Tracker() = default;
};
+THREAD_LOCAL MemoryConfig s_config;
+THREAD_LOCAL Tracker s_tracker;
+const MemoryConfig* s_main_config = nullptr;
+
// -----------------------------------------------------------------------------
-// static variables
+// helpers
// -----------------------------------------------------------------------------
-static THREAD_LOCAL Tracker s_tracker;
+template<typename Tracker, typename Handler>
+inline bool free_space(size_t requested, size_t cap, Tracker& trk, Handler& handler)
+{
+ assert(requested <= cap);
+ const auto required = cap - requested;
+
+ if ( trk.used() > required )
+ handler();
+
+ return trk.used() <= required;
+}
+
+} // namespace
// -----------------------------------------------------------------------------
// public interface
// -----------------------------------------------------------------------------
-bool DefaultCap::free_space(size_t n)
+bool MemoryCap::free_space(size_t n)
{
if ( !is_packet_thread() )
return true;
- const auto& config = *snort_conf->memory;
+ const auto& config = s_config;
- if ( !config.enable || !config.cap )
+ if ( !config.enable )
return true;
- return ::free_space(n, config.cap, s_tracker, prune_handler);
+ return memory::free_space(n, config.cap, s_tracker, prune_handler);
}
-void DefaultCap::update_allocations(size_t n)
+void MemoryCap::update_allocations(size_t n)
{
- if ( is_packet_thread() )
- s_tracker.allocated += n;
-
+ s_tracker.allocated += n;
mp_active_context.update_allocs(n);
}
-void DefaultCap::update_deallocations(size_t n)
+void MemoryCap::update_deallocations(size_t n)
{
- if ( is_packet_thread() )
- s_tracker.deallocated += n;
-
+ s_tracker.deallocated += n;
mp_active_context.update_deallocs(n);
}
+// FIXIT-H J need to validate and print out warnings for configurations
+// that result in very low values for per-thread memory
+void MemoryCap::calculate(unsigned num_threads)
+{
+ if ( !snort_conf->memory->enable )
+ return;
+
+ s_config.enable = snort_conf->memory->enable;
+ assert(snort_conf->memory->cap >= s_tracker.used());
+
+ auto remaining = snort_conf->memory->cap - s_tracker.used();
+ auto per_thread_cap = remaining / num_threads;
+
+ assert(per_thread_cap > 0);
+
+ s_config.cap = per_thread_cap;
+
+ s_main_config = &s_config;
+
+ DebugFormat(DEBUG_MEMORY,
+ ("local memcap set: %zu startup cost, "
+ "%zu available (%zu per-thread)\n"),
+ s_tracker.used(), remaining, per_thread_cap, num_threads);
+}
+
+void MemoryCap::tinit()
+{
+ if ( s_main_config )
+ s_config = *s_main_config;
+}
+
} // namespace memory
#ifdef UNIT_TEST
MockTracker tracker { 0 };
HandlerSpy handler { 1, tracker };
- CHECK( ::free_space(1, 1024, tracker, handler) );
+ CHECK( memory::free_space(1, 1024, tracker, handler) );
CHECK_FALSE( handler.called );
}
MockTracker tracker { 1024 };
HandlerSpy handler { 1023, tracker };
- CHECK( ::free_space(1, 1024, tracker, handler) );
+ CHECK( memory::free_space(1, 1024, tracker, handler) );
CHECK( handler.called );
CHECK( tracker.result == handler.modify_tracker );
}
MockTracker tracker { 1024 };
HandlerSpy handler { 0, tracker };
- CHECK_FALSE( ::free_space(1, 1024, tracker, handler) );
+ CHECK_FALSE( memory::free_space(1, 1024, tracker, handler) );
CHECK( handler.called );
CHECK( tracker.result == 1024 );
}
// memory_manager.cc author Joel Cornett <jocornet@cisco.com>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include <new>
#include <cstdio>
#include <cassert>
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include "main/thread.h"
#include "memory_allocator.h"
#include "memory_cap.h"
Metadata(size_t = 0);
- static size_t SANITY_CHECK_VALUE;
-
static size_t calculate_total_size(size_t);
- template<typename Shim> static Metadata* create(size_t);
+
+ template<typename Allocator>
+ static Metadata* create(size_t);
+
static Metadata* extract(void*);
+
+ static size_t SANITY_CHECK_VALUE;
};
inline size_t Metadata::total_size() const
sanity(SANITY_CHECK_VALUE), payload_size(n)
{ }
-size_t Metadata::SANITY_CHECK_VALUE = 0xabcdef;
-
inline size_t Metadata::calculate_total_size(size_t n)
{ return sizeof(Metadata) + n; }
-template<typename Shim>
+template<typename Allocator>
Metadata* Metadata::create(size_t n)
{
auto meta =
- static_cast<Metadata*>(Shim::allocate(calculate_total_size(n)));
+ static_cast<Metadata*>(Allocator::allocate(calculate_total_size(n)));
if ( !meta )
return nullptr;
return meta;
}
+size_t Metadata::SANITY_CHECK_VALUE = 0xabcdef;
+
// -----------------------------------------------------------------------------
// the meat
// -----------------------------------------------------------------------------
-template<typename Allocator = DefaultAllocator, typename Cap = DefaultCap>
+class ReentryContext
+{
+public:
+ ReentryContext(bool& flag) :
+ already_entered(flag), flag(flag)
+ { flag = true; }
+
+ ~ReentryContext()
+ { flag = false; }
+
+ bool is_reentry() const
+ { return already_entered; }
+
+private:
+ const bool already_entered;
+ bool& flag;
+};
+
+template<typename Allocator = MemoryAllocator, typename Cap = MemoryCap>
struct Interface
{
static void* allocate(size_t);
static void deallocate(void*);
+
+ static THREAD_LOCAL bool in_allocation_call;
};
template<typename Allocator, typename Cap>
void* Interface<Allocator, Cap>::allocate(size_t n)
{
+ // prevent allocation reentry
+ ReentryContext reentry_context(in_allocation_call);
+ assert(!reentry_context.is_reentry());
+
if ( !Cap::free_space(Metadata::calculate_total_size(n)) )
return nullptr;
Allocator::deallocate(meta);
}
+template<typename Allocator, typename Cap>
+THREAD_LOCAL bool Interface<Allocator, Cap>::in_allocation_call = false;
+
} //namespace memory
// -----------------------------------------------------------------------------