set ( USE_TSC_CLOCK ${ENABLE_TSC_CLOCK} )
set ( NO_PROFILER ${DISABLE_SNORT_PROFILER} )
set ( DEEP_PROFILING ${ENABLE_DEEP_PROFILING} )
-set ( ENABLE_MEMORY_OVERLOADS ${ENABLE_MEMORY_OVERLOADS} )
set ( ENABLE_MEMORY_PROFILER ${ENABLE_MEMORY_PROFILER} )
set ( ENABLE_RULE_PROFILER ${ENABLE_RULE_PROFILER} )
option ( ENABLE_PROFILE "Enable profiling options (developers only)" OFF )
option ( DISABLE_SNORT_PROFILER "Disable snort Profiler (developers only)" OFF )
option ( ENABLE_DEEP_PROFILING "Enable deep profiling of snort functions (developers only)" OFF )
-option ( ENABLE_MEMORY_OVERLOADS "Use new / delete overloads for profiling (developers only)" OFF )
option ( ENABLE_MEMORY_PROFILER "Enable memory profiler (developers only)" OFF )
option ( ENABLE_RULE_PROFILER "Enable rule keyword profiler (developers only)" OFF )
option ( ENABLE_ADDRESS_SANITIZER "enable address sanitizer support" OFF )
set(DAQ_CPPFLAGS "-I${DAQ_INCLUDE_DIR}")
endif()
-if(ENABLE_MEMORY_OVERLOADS)
- set(MEMORY_OVERLOADS_CPPFLAGS "-DENABLE_MEMORY_OVERLOADS")
-endif()
-
if(ENABLE_MEMORY_PROFILER)
set(MEMORY_PROFILER_CPPFLAGS "-DENABLE_MEMORY_PROFILER")
endif()
--enable-gprof-profile enable gprof profiling options (developers only)
--disable-snort-profiler
disable snort performance profiling (cpu and memory) (developers only)
- --enable-memory-overloads
- overload new and delete
--enable-memory-profiler
enable memory profiler
--enable-rule-profiler enable rule keyword profiler (developers only)
--disable-snort-profiler)
append_cache_entry DISABLE_SNORT_PROFILER BOOL false
;;
- --enable-memory-overloads)
- append_cache_entry ENABLE_MEMORY_OVERLOADS BOOL true
- ;;
--enable-memory-profiler)
append_cache_entry ENABLE_MEMORY_PROFILER BOOL true
;;
;;
--enable-tcmalloc)
append_cache_entry ENABLE_TCMALLOC BOOL true
+ tcm=1
;;
--disable-tcmalloc)
append_cache_entry ENABLE_TCMALLOC BOOL false
--enable-jemalloc)
append_cache_entry ENABLE_JEMALLOC BOOL true
append_cache_entry STATIC_JEMALLOC BOOL false
+ jem=1
;;
--disable-jemalloc)
append_cache_entry ENABLE_JEMALLOC BOOL false
shift
done
+if [ $tcm -eq 1 -a $jem -eq 1 ] ; then
+ echo "--enable-tcmalloc and --enable-tcmalloc are mutually exclusive; enable at most one"
+ exit 2
+fi
+
if [ -d $builddir ]; then
# If build directory exists, check if it has a CMake cache
if [ -f $builddir/CMakeCache.txt ]; then
#include "framework/data_bus.h"
#include "helpers/bitop.h"
#include "main/analyzer.h"
-#include "memory/memory_cap.h"
#include "protocols/packet.h"
#include "protocols/tcp.h"
#include "pub_sub/intrinsic_event_ids.h"
#include "hash/hash_defs.h"
#include "hash/zhash.h"
#include "helpers/flag_context.h"
-#include "memory/memory_cap.h"
#include "packet_io/active.h"
#include "packet_tracer/packet_tracer.h"
#include "stream/base/stream_module.h"
#include "framework/inspector.h"
#include "main/snort_config.h"
#include "managers/so_manager.h"
-#include "memory/memory_cap.h"
using namespace snort;
#include <cstdint>
#include <string>
-#include "memory/memory_cap.h"
-
#define STASH_APPID_DATA "appid_data"
#define STASH_GENERIC_OBJECT_APPID 1
InitTag();
EventTrace_Init();
+ memory::MemoryCap::thread_init();
EventManager::open_outputs();
IpsManager::setup_options(sc);
ActionManager::thread_init(sc);
#include "service_inspectors/service_inspectors.h"
#include "side_channel/side_channel.h"
#include "stream/stream_inspectors.h"
+#include "stream/stream.h"
#include "target_based/host_attributes.h"
#include "time/periodic.h"
#include "trace/trace_api.h"
set_quick_exit(false);
- memory::MemoryCap::setup(*sc->memory, sc->thread_config->get_instance_max());
- memory::MemoryCap::print(SnortConfig::log_verbose());
+ memory::MemoryCap::setup(*sc->memory, sc->thread_config->get_instance_max(), Stream::prune_flows);
+ memory::MemoryCap::print(SnortConfig::log_verbose(), true);
host_cache.print_config();
#include "main/snort_config.h"
#include "main/swapper.h"
#include "main/thread_config.h"
+#include "memory/memory_cap.h"
#include "network_inspectors/packet_tracer/packet_tracer.h"
#include "packet_io/active.h"
#include "packet_io/sfdaq.h"
void ThreadConfig::set_instance_tid(const int, const int) { }
}
-namespace memory
-{
-void MemoryCap::free_space() { }
-}
+void memory::MemoryCap::thread_init() { }
+void memory::MemoryCap::free_space() { }
+
set (MEMCAP_INCLUDES
+ heap_interface.h
memory_cap.h
)
set ( MEMORY_SOURCES
${MEMCAP_INCLUDES}
- memory_allocator.cc
- memory_allocator.h
+ heap_interface.cc
memory_cap.cc
memory_config.h
- memory_manager.cc
memory_module.cc
memory_module.h
- prune_handler.cc
- prune_handler.h
+ memory_overloads.cc
)
+set ( ALLOC_SOURCES
+ memory_allocator.cc
+ memory_allocator.h
+)
+
+if ( ENABLE_MEMORY_PROFILER )
+ list ( APPEND MEMORY_SOURCES ${ALLOC_SOURCES} )
+endif ()
+
add_library ( memory OBJECT
${MEMORY_SOURCES}
)
install(FILES ${MEMCAP_INCLUDES}
DESTINATION "${INCLUDE_INSTALL_PATH}/memory/"
)
+
+add_subdirectory(test)
+
-Snort memory management monitors memory usage and prunes flows as needed to keep the total process
-usage below the configured limit, if any. There are two ways to build memory management: build with
-jemalloc (--enable-jemalloc) or enable the new / delete overloads (--enable-memory-overloads). The
-latter option is required to support memory profiling (--enable-memory-profiler). Profiling is not
-enabled by default due to performance concerns and is viewed as a developer tool: apart from cache
-memcaps, users should not have to care about how Snort allocates memory, only that it doesn't
-exceed the configured limit if any.
+Snort provides two memory related features: management and profiling. Memory management ensures that
+Snort uses no more than a configured cap and is built with --enable-jemalloc (jemalloc required).
+Profiling displays memory usage by component and is enabled with --enable-memory-profiling. These
+features are independent and may be used together. jemalloc is required for management because it
+supports the necessary introspection in an efficient manner. Profiling is not enabled by default
+because of the runtime overhead incurred by overloading new and delete.
+
+Memory management is implemented by periodically comparing the total process usage reported by
+jemalloc ("stats.allocated") against the configured cap. If over limit, packet threads commence a
+reap cycle and prune LRU flows up to the configured prune target. This is achieved by capturing the
+deallocated total (via "thread.deallocatedp") at the start of the reap cycle and checking the
+current difference and pruning a single flow if the target has not been reached. At most one flow is
+pruned per packet. You can learn more about the jemalloc api at http://jemalloc.net/jemalloc.3.html.
+
+Snort will go over the configured cap by a (relatively) small amount so the configured cap should be
+below the hard limit, eg the limit enforced by cgroups. The default values for memory.interval and
+memory.prune_target generally work well so you should only have to set memory.cap and
+memory.threshold. If cap is set to the hard limit, set threshold to 99% or so (or just set cap lower
+and threshold to 100%). memory.threshold is provided for convenience; the actual effective limit is
+the product of cap * threshold..
+
+Some things to keep in mind:
+
+* jemalloc updates summary stats like process total once per epoch. Snort will bump the epoch once
+ per memory.interval ms. Performance suffers if interval is too low.
+
+* Snort generally frees memory in the same thread that allocated it, however this is not always
+ true. Shared caches may delete LRU entries when updated leading to freeing by different packet
+ threads. File capture will free memory allocated by a packet thread in a capture thread. This means
+ that the memory.allocated and memory.deallocated pegs can be confusing and should be considered
+ independently.
+
+* Since only packet threads take action to enforce the cap, packet threads will be forced to
+ compensate for non-packet threads. This could lead to an apparent out of memory condition if
+ non-packet threads allocate and hold significant memory. Therefore, non-packet threads should be
+ configured with some cap and that amount should be factored into the fixed startup cost.
tcmalloc builds (--enable-tcmalloc) do not support memory management. A process total is available
-from the tcmalloc extensions but it is too expensive to call per packet. Checking every N packets
-would also mean potentially freeing K > 1 flows after each exceeded event. Also, a process total
-does not allow pruning only the threads that are over limit. tcmalloc does provide a performance
-advantage over glibc so that may be preferred for deployments that don't need memory management.
+from the tcmalloc extensions but it lacks a thread deallocated number. A scheme could be implemented
+that released prune_target flows but that is not planned. tcmalloc does provide a performance
+advantage over glibc so that may be preferred for deployments that don't need memory management,
+however internal tests show jemalloc performs better than tcmalloc for more than about 8 threads /
+cores.
-jemalloc is preferred because it is quicker and uses less memory since the implementation does not
-require memory tracking. jemalloc provides access to the current thread allocated total (which is
-between the number that Snort requests and what the system footprint is).
+Files pertaining to management (all under src/memory/):
-memory_module.* - provides parameters and peg counts. The key parameters are the process_cap,
-thread_cap, and threshold. The caps are in bytes, and the threshold is a percentage of the caps
-specified.
+* heap_interface: implements a jemalloc interface if enabled or a nerfed interface if disabled. A
+ custom HeapInterface (and pruner) can be provided by a plugin for testing. MemoryCap provides
+ methods to install them.
-memory_manager.cc - when enabled with --enable-memory-overloads, overloads new and delete operators
-to provide support memory tracking. Metadata is allocated in front of the requested memory to store
-the sized allocated so the deallocation can be tracked. Due to the drag on performance, this is
-disabled by default.
+* memory_cap: implementation of reap cycles using heap interface.
-memory_allocator.* - implements the malloc and free calls used by the operator new and delete
-overloads.
+* memory_config: defines config struct.
-memory_config.h - provides MemoryConfig used by MemoryCap and stored in SnortConfig.
+* memory_module: glue between Snort and memory features.
-memory_cap.* - provides the logic to enforce the thread cap by calling the prune handler. Tracks
-thread usage in pegs and updates the memory profiler if built. To avoid confusion, thread_cap
-refers to the configured maximums, while thread_limit refer to the configured percentage of the
-caps (memory.cap * memory.threshold / 100). The jemalloc specific code is here.
+Files pertaining to profiling (all under src/memory/):
-prune_handler.* - implements the call to stream to prune.
+* memory_allocator: overloads call the allocator to actually malloc and free memory.
+
+* memory_overloads: implements new and delete overloads using allocator.
The current iteration of the memory manager is exclusively preemptive. MemoryCap::free_space is
called by the analyzer before each DAQ message is processed. If thread_usage > thread_limit, a
This implementation has the following limitations:
-* If the overload manager is built, it only tracks memory allocated with C++ new. Specifically,
+* If the profiler is built, it only tracks memory allocated with C++ new. Specifically,
direct calls to malloc or calloc which may be made by libraries are not tracked.
-* Packet thread tracking does not include heap overhead, which can be substantial.
-
* Non-packet threads are assumed to have bounded memory usage, eg via a cache.
* Heap managers tend to acquire memory from the system quickly and release back much more slowly,
processing of a single packet will not push us over. It must also allow for additional heap
overhead.
-Future work:
-
-* Support simplified configuration of a process cap instead of a thread cap. Implement a MemoryCap
- method that can be called to inform the memory module of various cache related memcaps. Deduct
- the startup ru_maxrss and the sum of memcaps from the configured process cap and then divide by
- --max-packet-threads to get the effective thread cap.
-
-* Compensate for heap fragmentation and other overhead by using the current process footprint
- (process_total below) as feedback to adjust the current packet thread limits:
-
- thread_limit = [(cap - (process_total - sum_thread_usage)) / num_threads] * threshold
-
-* Recognize when a memory leak drives excessive pruning.
-
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// heap_interface.cc author Russ Combs <rucombs@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "heap_interface.h"
+
+#include <cassert>
+
+#ifdef HAVE_JEMALLOC
+#include <jemalloc/jemalloc.h>
+#endif
+
+#include "main/thread.h"
+
+namespace memory
+{
+
+// -----------------------------------------------------------------------------
+#ifdef HAVE_JEMALLOC
+// -----------------------------------------------------------------------------
+
+class JemallocInterface : public HeapInterface
+{
+ void main_init() override;
+ void thread_init() override;
+
+ void get_process_total(uint64_t&, uint64_t&) override;
+ void get_thread_allocs(uint64_t&, uint64_t&) override;
+};
+
+static size_t stats_mib[2], mib_len = 2;
+
+static THREAD_LOCAL uint64_t* alloc_ptr = nullptr;
+static THREAD_LOCAL uint64_t* dealloc_ptr = nullptr;
+
+void JemallocInterface::main_init()
+{
+ mallctlnametomib("stats.allocated", stats_mib, &mib_len);
+}
+
+void JemallocInterface::thread_init()
+{
+ size_t sz = sizeof(alloc_ptr);
+
+ // __STRDUMP_DISABLE__
+ mallctl("thread.allocatedp", (void*)&alloc_ptr, &sz, nullptr, 0);
+ mallctl("thread.deallocatedp", (void*)&dealloc_ptr, &sz, nullptr, 0);
+ // __STRDUMP_ENABLE__
+}
+
+void JemallocInterface::get_process_total(uint64_t& epoch, uint64_t& utotal)
+{
+ uint64_t cycle = 13;
+ size_t sz = sizeof(epoch);
+ mallctl("epoch", (void*)&epoch, &sz, (void*)&cycle, sizeof(cycle));
+
+ size_t total;
+ sz = sizeof(total);
+ mallctlbymib(stats_mib, mib_len, (void*)&total, &sz, NULL, 0);
+
+ utotal = total;
+}
+
+void JemallocInterface::get_thread_allocs(uint64_t& alloc, uint64_t& dealloc)
+{
+ assert(alloc_ptr);
+ assert(dealloc_ptr);
+
+ alloc = *alloc_ptr;
+ dealloc = *dealloc_ptr;
+}
+
+//--------------------------------------------------------------------------
+#else // disabled interface
+//--------------------------------------------------------------------------
+
+class NerfedInterface : public HeapInterface
+{
+public:
+ void main_init() override { }
+ void thread_init() override { }
+
+ void get_process_total(uint64_t& e, uint64_t& t) override
+ { e = t = 0; }
+
+ void get_thread_allocs(uint64_t& a, uint64_t& d) override
+ { a = d = 0; }
+};
+
+#endif
+
+HeapInterface* HeapInterface::get_instance()
+{
+#ifdef HAVE_JEMALLOC
+ return new JemallocInterface;
+#else
+ return new NerfedInterface;
+#endif
+}
+
+} // memory
+
//--------------------------------------------------------------------------
-// Copyright (C) 2016-2022 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2023-2023 Cisco and/or its affiliates. All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License Version 2 as published
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//--------------------------------------------------------------------------
-// prune_handler.cc author Joel Cornett <jocornet@cisco.com>
+// heap_interface.cc author Russ Combs <rucombs@cisco.com>
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "prune_handler.h"
-
-#include "stream/stream.h"
+#ifndef HEAP_INTERFACE_H
+#define HEAP_INTERFACE_H
-using namespace snort;
+#include <cstdint>
namespace memory
{
-bool prune_handler()
+class HeapInterface
{
- return Stream::prune_flows();
+public:
+ virtual ~HeapInterface() { }
+
+ virtual void main_init() = 0;
+ virtual void thread_init() = 0;
+
+ virtual void get_process_total(uint64_t& epoch, uint64_t& total) = 0;
+ virtual void get_thread_allocs(uint64_t& alloc, uint64_t& dealloc) = 0;
+
+ static HeapInterface* get_instance();
+
+protected:
+ HeapInterface() { }
+};
+
}
-} // namespace memory
+#endif
+
#include "config.h"
#endif
+#include "memory_cap.h"
+
#include <malloc.h>
#include <sys/resource.h>
+#include <atomic>
#include <cassert>
#include <vector>
-#ifdef HAVE_JEMALLOC
-#include <jemalloc/jemalloc.h>
-#endif
-
-#include "memory_cap.h"
-
#include "log/messages.h"
#include "main/snort_config.h"
#include "main/snort_types.h"
#include "main/thread.h"
-#include "profiler/memory_profiler_active_context.h"
+#include "time/periodic.h"
+#include "trace/trace_api.h"
#include "utils/stats.h"
+#include "heap_interface.h"
#include "memory_config.h"
#include "memory_module.h"
-#include "prune_handler.h"
using namespace snort;
namespace memory
{
-static MemoryCounts ctl_mem_stats;
-static std::vector<MemoryCounts> pkt_mem_stats;
-
-namespace
-{
-
// -----------------------------------------------------------------------------
-// helpers
+// private
// -----------------------------------------------------------------------------
-#ifdef HAVE_JEMALLOC
-static size_t get_usage(MemoryCounts& mc)
-{
- static THREAD_LOCAL uint64_t* alloc_ptr = nullptr, * dealloc_ptr = nullptr;
-
- if ( !alloc_ptr )
- {
- size_t sz = sizeof(alloc_ptr);
- // __STRDUMP_DISABLE__
- mallctl("thread.allocatedp", (void*)&alloc_ptr, &sz, nullptr, 0);
- mallctl("thread.deallocatedp", (void*)&dealloc_ptr, &sz, nullptr, 0);
- // __STRDUMP_ENABLE__
- }
- mc.allocated = *alloc_ptr;
- mc.deallocated = *dealloc_ptr;
+static std::vector<MemoryCounts> pkt_mem_stats;
- if ( mc.allocated > mc.deallocated )
- {
- size_t usage = mc.allocated - mc.deallocated;
+static MemoryConfig config;
+static size_t limit = 0;
- if ( usage > mc.max_in_use )
- mc.max_in_use = usage;
+static std::atomic<bool> over_limit { false };
+static std::atomic<uint64_t> current_epoch { 0 };
- return usage;
- }
- return 0;
-}
-#else
-static size_t get_usage(const MemoryCounts& mc)
-{
-#ifdef ENABLE_MEMORY_OVERLOADS
- assert(mc.allocated >= mc.deallocated);
- return mc.allocated - mc.deallocated;
+static THREAD_LOCAL uint64_t last_dealloc = 0;
+static THREAD_LOCAL uint64_t start_dealloc = 0;
+static THREAD_LOCAL uint64_t start_epoch = 0;
-#else
- UNUSED(mc);
- return 0;
-#endif
-}
-#endif
+static HeapInterface* heap = nullptr;
+static PruneHandler pruner;
-template<typename Handler>
-inline void free_space(size_t cap, Handler& handler)
+static void epoch_check(void*)
{
- MemoryCounts& mc = memory::MemoryCap::get_mem_stats();
- size_t usage = get_usage(mc);
+ uint64_t epoch, total;
+ heap->get_process_total(epoch, total);
- if ( usage < cap )
- return;
+ current_epoch = epoch;
- ++mc.reap_attempts;
+ bool prior = over_limit;
+ over_limit = limit and total > limit;
- if ( handler() )
- return;
+ if ( prior != over_limit )
+ trace_logf(memory_trace, nullptr, "Epoch=%lu, memory=%lu (%s)\n", epoch, total, over_limit?"over":"under");
- ++mc.reap_failures;
-}
+ MemoryCounts& mc = memory::MemoryCap::get_mem_stats();
-inline size_t calculate_threshold(size_t cap, size_t threshold)
-{ return cap * threshold / 100; }
+ if ( total > mc.max_in_use )
+ mc.max_in_use = total;
-} // namespace
+ mc.cur_in_use = total;
+ mc.epochs++;
+}
// -----------------------------------------------------------------------------
-// per-thread configuration
+// public
// -----------------------------------------------------------------------------
-size_t MemoryCap::limit = 0;
+void MemoryCap::set_heap_interface(HeapInterface* h)
+{ heap = h; }
-// -----------------------------------------------------------------------------
-// public interface
-// -----------------------------------------------------------------------------
+void MemoryCap::set_pruner(PruneHandler p)
+{ pruner = p; }
-void MemoryCap::setup(const MemoryConfig& config, unsigned n)
+void MemoryCap::setup(const MemoryConfig& c, unsigned n, PruneHandler ph)
{
assert(!is_packet_thread());
- limit = memory::calculate_threshold(config.cap, config.threshold);
+
pkt_mem_stats.resize(n);
+ config = c;
+
+ if ( !heap )
+ heap = HeapInterface::get_instance();
+
+ if ( !config.enabled )
+ return;
+
+ if ( !pruner )
+ pruner = ph;
+
+ limit = config.cap * config.threshold / 100;
+ over_limit = false;
+ current_epoch = 0;
+
+ Periodic::register_handler(epoch_check, nullptr, 0, config.interval);
+ heap->main_init();
+
+ MemoryCounts& mc = memory::MemoryCap::get_mem_stats();
+#ifdef UNIT_TEST
+ mc = { };
+#endif
+
+ epoch_check(nullptr);
+ mc.start_up_use = mc.cur_in_use;
}
void MemoryCap::cleanup()
{
pkt_mem_stats.resize(0);
+ delete heap;
+ heap = nullptr;
+}
+
+void MemoryCap::thread_init()
+{
+ if ( config.enabled )
+ heap->thread_init();
+
+ start_dealloc = 0;
+ start_epoch = 0;
}
MemoryCounts& MemoryCap::get_mem_stats()
{
+ // main thread stats do not overlap with packet threads
if ( !is_packet_thread() )
- return ctl_mem_stats;
+ return pkt_mem_stats[0];
auto id = get_instance_id();
return pkt_mem_stats[id];
{
assert(is_packet_thread());
- if ( !limit )
- return;
-
- memory::free_space(limit, prune_handler);
-}
-
-#ifdef ENABLE_MEMORY_OVERLOADS
-void MemoryCap::allocate(size_t n)
-{
MemoryCounts& mc = memory::MemoryCap::get_mem_stats();
+ heap->get_thread_allocs(mc.allocated, mc.deallocated);
- mc.allocated += n;
- ++mc.allocations;
-
- assert(mc.allocated >= mc.deallocated);
- auto in_use = mc.allocated - mc.deallocated;
+ if ( !over_limit and !start_dealloc )
+ return;
- if ( in_use > mc.max_in_use )
- mc.max_in_use = in_use;
+ if ( !start_dealloc )
+ {
+ if ( current_epoch == start_epoch )
+ return;
-#ifdef ENABLE_MEMORY_PROFILER
- mp_active_context.update_allocs(n);
-#endif
-}
+ start_dealloc = last_dealloc = mc.deallocated;
+ start_epoch = current_epoch;
+ mc.reap_cycles++;
+ }
-void MemoryCap::deallocate(size_t n)
-{
- MemoryCounts& mc = memory::MemoryCap::get_mem_stats();
+ mc.pruned += (mc.deallocated - last_dealloc);
+ last_dealloc = mc.deallocated;
- // std::thread causes an extra deallocation in packet
- // threads so the below asserts don't hold
- if ( mc.allocated >= mc.deallocated + n )
+ if ( mc.deallocated - start_dealloc >= config.prune_target )
{
- mc.deallocated += n;
- ++mc.deallocations;
+ start_dealloc = 0;
+ return;
}
-#if 0
- assert(mc.deallocated <= mc.allocated);
- assert(mc.deallocations <= mc.allocations);
- assert(mc.allocated or !mc.allocations);
-#endif
+ ++mc.reap_attempts;
-#ifdef ENABLE_MEMORY_PROFILER
- mp_active_context.update_deallocs(n);
-#endif
+ if ( pruner() )
+ return;
+
+ ++mc.reap_failures;
}
-#endif
-void MemoryCap::print(bool verbose, bool print_all)
+// called at startup and shutdown
+void MemoryCap::print(bool verbose, bool init)
{
- if ( !MemoryModule::is_active() )
+ if ( !config.enabled )
return;
MemoryCounts& mc = get_mem_stats();
- uint64_t usage = get_usage(mc);
-
- if ( verbose or usage )
- LogLabel("memory (heap)");
- if ( verbose and print_all )
+ if ( init and (verbose or mc.start_up_use) )
+ {
+ LogLabel("memory");
LogCount("pruning threshold", limit);
+ LogCount("start up use", mc.start_up_use);
+ }
- LogCount("main thread usage", usage);
- LogCount("allocations", mc.allocations);
- LogCount("deallocations", mc.deallocations);
+ if ( limit and (mc.max_in_use > limit) )
+ LogCount("process over limit", mc.max_in_use - limit);
if ( verbose )
{
struct rusage ru;
getrusage(RUSAGE_SELF, &ru);
- LogCount("max_rss", ru.ru_maxrss * 1024);
+ LogCount("max rss", ru.ru_maxrss * 1024);
}
}
#ifndef MEMORY_CAP_H
#define MEMORY_CAP_H
+#include "memory/heap_interface.h"
+
#include <cstddef>
#include "framework/counts.h"
struct MemoryCounts
{
- PegCount allocations;
- PegCount deallocations;
+ PegCount start_up_use;
+ PegCount cur_in_use;
+ PegCount max_in_use;
+ PegCount epochs;
PegCount allocated;
PegCount deallocated;
+ PegCount reap_cycles;
PegCount reap_attempts;
PegCount reap_failures;
- PegCount max_in_use;
+ PegCount pruned;
};
+typedef bool (*PruneHandler)();
+
class SO_PUBLIC MemoryCap
{
public:
- static void setup(const MemoryConfig&, unsigned);
+ // main thread - in configure
+ static void set_heap_interface(HeapInterface*);
+ static void set_pruner(PruneHandler);
+
+ // main thread - after configure
+ static void setup(const MemoryConfig&, unsigned num_threads, PruneHandler);
static void cleanup();
+ static void print(bool verbose, bool init = false);
+ // packet threads
+ static void thread_init();
static void free_space();
- // call from main thread
- static void print(bool verbose, bool print_all = true);
-
static MemoryCounts& get_mem_stats();
-
-#ifdef ENABLE_MEMORY_OVERLOADS
- static void allocate(size_t);
- static void deallocate(size_t);
-#endif
-
-private:
- static size_t limit;
};
-} // namespace memory
+}
#endif
{
size_t cap = 0;
unsigned threshold = 0;
+ unsigned interval = 1000;
+ unsigned prune_target = 1048576;
+ bool enabled = false;
constexpr MemoryConfig() = default;
};
#include "memory_module.h"
#include "main/snort_config.h"
+#include "trace/trace.h"
#include "memory_cap.h"
#include "memory_config.h"
using namespace snort;
+THREAD_LOCAL const Trace* memory_trace = nullptr;
+
// -----------------------------------------------------------------------------
// memory attributes
// -----------------------------------------------------------------------------
static const Parameter s_params[] =
{
{ "cap", Parameter::PT_INT, "0:maxSZ", "0",
- "set the per-packet-thread cap on memory (bytes, 0 to disable)" },
+ "set the process cap on memory in bytes (0 to disable)" },
+
+ { "interval", Parameter::PT_INT, "1:max32", "50",
+ "approximate ms between memory epochs" },
+
+ { "prune_target", Parameter::PT_INT, "1:max32", "1048576",
+ "bytes to prune per packet thread prune cycle" },
{ "threshold", Parameter::PT_INT, "1:100", "100",
"scale cap to account for heap overhead" },
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
-static memory::MemoryCounts zero_stats = { };
-
const PegInfo mem_pegs[] =
{
- { CountType::NOW, "allocations", "total number of allocations" },
- { CountType::NOW, "deallocations", "total number of deallocations" },
- { CountType::NOW, "allocated", "total amount of memory allocated" },
- { CountType::NOW, "deallocated", "total amount of memory deallocated" },
+ { CountType::NOW, "start_up_use", "memory used before packet processing" },
+ { CountType::NOW, "cur_in_use", "current memory used" },
+ { CountType::MAX, "max_in_use", "maximum memory used" },
+ { CountType::NOW, "epochs", "number of memory updates" },
+ { CountType::NOW, "allocated", "total amount of memory allocated by packet threads" },
+ { CountType::NOW, "deallocated", "total amount of memory deallocated by packet threads" },
+ { CountType::NOW, "reap_cycles", "number of actionable over-limit conditions" },
{ CountType::NOW, "reap_attempts", "attempts to reclaim memory" },
{ CountType::NOW, "reap_failures", "failures to reclaim memory" },
- { CountType::MAX, "max_in_use", "maximum memory used" },
+ { CountType::NOW, "pruned", "total amount of memory pruned" },
{ CountType::END, nullptr, nullptr }
};
// memory module
// -----------------------------------------------------------------------------
-bool MemoryModule::configured = false;
-
MemoryModule::MemoryModule() :
Module(s_name, s_help, s_params)
{ }
if ( v.is("cap") )
sc->memory->cap = v.get_size();
+ else if ( v.is("interval") )
+ sc->memory->interval = v.get_uint32();
+
+ else if ( v.is("prune_target") )
+ sc->memory->prune_target = v.get_uint32();
+
else if ( v.is("threshold") )
sc->memory->threshold = v.get_uint8();
return true;
}
-bool MemoryModule::end(const char*, int, SnortConfig*)
+bool MemoryModule::end(const char*, int, SnortConfig* sc)
{
- configured = true;
+ sc->memory->enabled = true;
return true;
}
-bool MemoryModule::is_active()
-{ return configured; }
-
const PegInfo* MemoryModule::get_pegs() const
{ return mem_pegs; }
PegCount* MemoryModule::get_counts() const
+{ return (PegCount*)&memory::MemoryCap::get_mem_stats(); }
+
+void MemoryModule::set_trace(const Trace* trace) const
+{ memory_trace = trace; }
+
+const TraceOption* MemoryModule::get_trace_options() const
{
- if ( !is_active() )
- return (PegCount*)&zero_stats;
+ static const TraceOption memory_trace_options(nullptr, 0, nullptr);
- return (PegCount*)&memory::MemoryCap::get_mem_stats();
+ return &memory_trace_options;
}
Usage get_usage() const override
{ return GLOBAL; }
- static bool is_active();
-
-private:
- static bool configured;
+ void set_trace(const snort::Trace*) const override;
+ const snort::TraceOption* get_trace_options() const override;
};
+extern THREAD_LOCAL const snort::Trace* memory_trace;
+
#endif
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//--------------------------------------------------------------------------
-// memory_manager.cc author Joel Cornett <jocornet@cisco.com>
+// memory_overloads.cc author Joel Cornett <jocornet@cisco.com>
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <new>
#include "main/thread.h"
+#include "profiler/memory_profiler_active_context.h"
#include "memory_allocator.h"
-#include "memory_cap.h"
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
bool& flag;
};
-template<typename Allocator = MemoryAllocator, typename Cap = MemoryCap>
+template<typename Allocator = MemoryAllocator>
struct Interface
{
static void* allocate(size_t);
static THREAD_LOCAL bool in_allocation_call;
};
-template<typename Allocator, typename Cap>
-void* Interface<Allocator, Cap>::allocate(size_t n)
+template<typename Allocator>
+void* Interface<Allocator>::allocate(size_t n)
{
// prevent allocation reentry
ReentryContext reentry_context(in_allocation_call);
if ( !meta )
return nullptr;
- Cap::allocate(meta->total_size());
+#ifdef ENABLE_MEMORY_PROFILER
+ mp_active_context.update_allocs(meta->total_size());
+#endif
+
return meta->payload_offset();
}
-template<typename Allocator, typename Cap>
-void Interface<Allocator, Cap>::deallocate(void* p)
+template<typename Allocator>
+void Interface<Allocator>::deallocate(void* p)
{
if ( !p )
return;
auto meta = Metadata::extract(p);
assert(meta);
- Cap::deallocate(meta->total_size());
+#ifdef ENABLE_MEMORY_PROFILER
+ mp_active_context.update_deallocs(meta->total_size());
+#endif
+
Allocator::deallocate(meta);
}
-template<typename Allocator, typename Cap>
-THREAD_LOCAL bool Interface<Allocator, Cap>::in_allocation_call = false;
+template<typename Allocator>
+THREAD_LOCAL bool Interface<Allocator>::in_allocation_call = false;
} //namespace memory
// these don't have to be visible to operate as replacements
-#ifdef ENABLE_MEMORY_OVERLOADS
+#ifdef ENABLE_MEMORY_PROFILER
void* operator new(size_t n)
{
auto p = memory::Interface<>::allocate(n);
bool AllocatorSpy::deallocate_called = false;
void* AllocatorSpy::deallocate_arg = nullptr;
-struct CapSpy
-{
- static void allocate(size_t n)
- {
- update_allocations_called = true;
- update_allocations_arg = n;
- }
-
- static void deallocate(size_t n)
- {
- update_deallocations_called = true;
- update_deallocations_arg = n;
- }
-
- static void reset()
- {
- update_allocations_called = false;
- update_allocations_arg = 0;
-
- update_deallocations_called = false;
- update_deallocations_arg = 0;
- }
-
- static bool update_allocations_called;
- static size_t update_allocations_arg;
-
- static bool update_deallocations_called;
- static size_t update_deallocations_arg;
-};
-
-bool CapSpy::update_allocations_called = false;
-size_t CapSpy::update_allocations_arg = 0;
-
-bool CapSpy::update_deallocations_called = false;
-size_t CapSpy::update_deallocations_arg = 0;
-
} // namespace t_memory
TEST_CASE( "memory metadata", "[memory]" )
}
}
-TEST_CASE( "memory manager interface", "[memory]" )
+TEST_CASE( "memory overloads", "[memory]" )
{
using namespace t_memory;
AllocatorSpy::reset();
- CapSpy::reset();
constexpr size_t n = 1;
char pool[sizeof(memory::Metadata) + n];
- using Interface = memory::Interface<AllocatorSpy, CapSpy>;
+ using Interface = memory::Interface<AllocatorSpy>;
SECTION( "allocation" )
{
CHECK( AllocatorSpy::allocate_called );
CHECK( AllocatorSpy::allocate_arg == memory::Metadata::calculate_total_size(n) );
-
- CHECK_FALSE( CapSpy::update_allocations_called );
}
SECTION( "success" )
CHECK( AllocatorSpy::allocate_called );
CHECK( AllocatorSpy::allocate_arg == memory::Metadata::calculate_total_size(n) );
-
- CHECK( CapSpy::update_allocations_called );
- CHECK( CapSpy::update_allocations_arg == memory::Metadata::calculate_total_size(n) );
}
}
SECTION( "nullptr" )
{
Interface::deallocate(nullptr);
-
CHECK_FALSE( AllocatorSpy::deallocate_called );
- CHECK_FALSE( CapSpy::update_deallocations_called );
}
SECTION( "success" )
CHECK( AllocatorSpy::deallocate_called );
CHECK( AllocatorSpy::deallocate_arg == (void*)pool );
- CHECK( CapSpy::update_deallocations_called );
- CHECK( CapSpy::update_deallocations_arg == memory::Metadata::calculate_total_size(n) );
}
}
AllocatorSpy::pool = nullptr;
+++ /dev/null
-//--------------------------------------------------------------------------
-// Copyright (C) 2016-2022 Cisco and/or its affiliates. All rights reserved.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License Version 2 as published
-// by the Free Software Foundation. You may not use, modify or distribute
-// this program under any other version of the GNU General Public License.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//--------------------------------------------------------------------------
-
-// prune_handler.h author Joel Cornett <jocornet@cisco.com>
-
-#ifndef PRUNE_HANDLER_H
-#define PRUNE_HANDLER_H
-
-namespace memory
-{
-
-bool prune_handler();
-
-}
-
-
-#endif
-
--- /dev/null
+
+add_cpputest( memory_cap_test
+ SOURCES
+ ../heap_interface.cc
+ ../memory_cap.cc
+)
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// memory_cap_test.cc author Russ Combs <rucombs@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "memory/memory_cap.h"
+
+#include "log/messages.h"
+#include "main/thread.h"
+#include "time/periodic.h"
+#include "trace/trace_api.h"
+
+#include "memory/heap_interface.h"
+#include "memory/memory_config.h"
+
+using namespace snort;
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+
+using namespace memory;
+
+//--------------------------------------------------------------------------
+// stubs
+//--------------------------------------------------------------------------
+
+namespace snort
+{
+// LCOV_EXCL_START
+void LogCount(char const*, uint64_t, FILE*) { }
+void LogLabel(const char*, FILE*) { }
+
+void TraceApi::filter(snort::Packet const&) { }
+void trace_vprintf(const char*, TraceLevel, const char*, const Packet*, const char*, va_list) { }
+
+uint8_t snort::TraceApi::get_constraints_generation() { return 0; }
+// LCOV_EXCL_STOP
+
+unsigned get_instance_id()
+{ return 0; }
+}
+
+THREAD_LOCAL const Trace* memory_trace = nullptr;
+
+//--------------------------------------------------------------------------
+// mocks
+//--------------------------------------------------------------------------
+
+class MockHeap : public HeapInterface
+{
+public:
+ void main_init() override
+ { main_init_calls++; }
+
+ void thread_init() override
+ { thread_init_calls++; }
+
+ void get_process_total(uint64_t& e, uint64_t& t) override
+ { e = ++epoch; t = total; }
+
+ void get_thread_allocs(uint64_t& a, uint64_t& d) override
+ { a = alloc; d = dealloc; }
+
+ uint64_t alloc = 2, dealloc = 1;
+ uint64_t epoch = 0, total = 0;
+
+ unsigned main_init_calls = 0;
+ unsigned thread_init_calls = 0;
+};
+
+static PeriodicHook phook = nullptr;
+static void* parg = nullptr;
+
+void Periodic::register_handler(PeriodicHook f, void* v, uint16_t, uint32_t)
+{ phook = f; parg = v; }
+
+static void periodic_check()
+{
+ if ( phook )
+ phook(parg);
+}
+
+static int flows;
+
+static bool pruner()
+{ return --flows >= 0; }
+
+static bool pkt_thread = false;
+
+namespace snort
+{
+
+SThreadType get_thread_type()
+{ return pkt_thread ? STHREAD_TYPE_PACKET : STHREAD_TYPE_MAIN; }
+
+}
+
+static void free_space()
+{
+ pkt_thread = true;
+ MemoryCap::free_space();
+ pkt_thread = false;
+}
+
+//--------------------------------------------------------------------------
+// tests
+//--------------------------------------------------------------------------
+
+TEST_GROUP(memory_off)
+{
+ void setup() override
+ { MemoryCap::set_pruner(pruner); }
+
+ void teardown() override
+ { flows = 0; }
+};
+
+TEST(memory_off, disabled)
+{
+ MemoryConfig config { 0, 100, 0, 1, false };
+ MemoryCap::setup(config, 1, pruner);
+
+ free_space();
+
+ CHECK(phook == nullptr);
+ CHECK(flows == 0);
+
+ const MemoryCounts& mc = MemoryCap::get_mem_stats();
+ CHECK(mc.start_up_use == 0);
+ CHECK(mc.epochs == 0);
+ CHECK(mc.reap_cycles == 0);
+
+ MemoryCap::cleanup();
+}
+
+TEST(memory_off, nerfed)
+{
+ MemoryConfig config { 100, 100, 0, 1, false };
+ MemoryCap::setup(config, 1, pruner);
+
+ free_space();
+
+ CHECK(phook == nullptr);
+ CHECK(flows == 0);
+
+ const MemoryCounts& mc = MemoryCap::get_mem_stats();
+ CHECK(mc.start_up_use == 0);
+ CHECK(mc.epochs == 0);
+ CHECK(mc.reap_cycles == 0);
+
+ MemoryCap::cleanup();
+}
+
+//--------------------------------------------------------------------------
+// tests
+//--------------------------------------------------------------------------
+
+TEST_GROUP(memory)
+{
+ MockHeap* heap = nullptr;
+
+ void setup() override
+ {
+ heap = new MockHeap;
+ MemoryCap::set_heap_interface(heap);
+ }
+
+ void teardown() override
+ {
+ heap = nullptr;
+ phook = nullptr;
+ parg = nullptr;
+ flows = 0;
+ }
+};
+
+TEST(memory, default_enabled)
+{
+ MemoryConfig config { 0, 100, 0, 1, true };
+ MemoryCap::setup(config, 1, pruner);
+
+ CHECK(phook != nullptr);
+ CHECK(heap->epoch == 1);
+ CHECK(heap->main_init_calls == 1);
+
+ CHECK(heap->thread_init_calls == 0);
+ MemoryCap::thread_init();
+ CHECK(heap->thread_init_calls == 1);
+ CHECK(heap->main_init_calls == 1);
+
+ periodic_check();
+ CHECK(heap->epoch == 2);
+ CHECK(flows == 0);
+
+ MemoryCap::cleanup();
+}
+
+TEST(memory, prune1)
+{
+ const uint64_t cap = 100;
+ const uint64_t start = 50;
+ heap->total = start;
+
+ MemoryConfig config { cap, 100, 0, 1, true };
+ MemoryCap::setup(config, 1, pruner);
+ MemoryCap::thread_init();
+
+ const MemoryCounts& mc = MemoryCap::get_mem_stats();
+ CHECK(mc.start_up_use == start);
+
+ flows = 1;
+ free_space();
+ CHECK(flows == 1);
+
+ heap->total = cap + 1;
+ periodic_check();
+ CHECK(heap->epoch == 2);
+
+ CHECK(flows == 1);
+ free_space();
+ CHECK(flows == 0);
+
+ heap->total = cap;
+ periodic_check();
+ CHECK(heap->epoch == 3);
+
+ heap->alloc++;
+ heap->dealloc++;
+
+ free_space();
+ CHECK(flows == 0);
+
+ CHECK(mc.start_up_use == start);
+ CHECK(mc.max_in_use == cap + 1);
+ CHECK(mc.cur_in_use == cap);
+
+ CHECK(mc.epochs == heap->epoch);
+ CHECK(mc.allocated == heap->alloc);
+ CHECK(mc.deallocated == heap->dealloc);
+
+ CHECK(mc.reap_cycles == 1);
+ CHECK(mc.reap_attempts == 1);
+ CHECK(mc.reap_failures == 0);
+ CHECK(mc.pruned == 1);
+
+ MemoryCap::cleanup();
+}
+
+TEST(memory, prune3)
+{
+ uint64_t cap = 100;
+ MemoryConfig config { cap, 100, 0, 1, true };
+ MemoryCap::setup(config, 1, pruner);
+ MemoryCap::thread_init();
+
+ flows = 3;
+ heap->total = cap + 1;
+
+ periodic_check();
+ CHECK(heap->epoch == 2);
+
+ CHECK(flows == 3);
+ free_space();
+ CHECK(flows == 2);
+
+ free_space();
+ CHECK(flows == 1);
+
+ free_space();
+ CHECK(flows == 0);
+
+ heap->total = cap;
+ periodic_check();
+ CHECK(heap->epoch == 3);
+
+ heap->dealloc++;
+ free_space();
+ CHECK(flows == 0);
+
+ const MemoryCounts& mc = MemoryCap::get_mem_stats();
+ CHECK(mc.reap_cycles == 1);
+ CHECK(mc.reap_attempts == 3);
+ CHECK(mc.reap_failures == 0);
+ CHECK(mc.pruned == 1);
+
+ MemoryCap::cleanup();
+}
+
+TEST(memory, two_cycles)
+{
+ uint64_t cap = 100;
+ MemoryConfig config { cap, 100, 0, 1, true };
+ MemoryCap::setup(config, 1, pruner);
+ MemoryCap::thread_init();
+
+ flows = 3;
+ heap->total = cap + 1;
+ periodic_check();
+
+ CHECK(flows == 3);
+ free_space(); // prune 1 flow
+ CHECK(flows == 2);
+
+ heap->dealloc++;
+ free_space(); // reset state
+ CHECK(flows == 2);
+
+ free_space(); // at most 1 reap cycle per epoch
+ CHECK(flows == 2);
+
+ heap->total = cap;
+ periodic_check();
+ free_space();
+ CHECK(flows == 2);
+
+ heap->total = cap + 10;
+ periodic_check();
+ free_space();
+ CHECK(flows == 1);
+
+ heap->total = cap;
+ heap->dealloc += 10;
+ periodic_check();
+
+ free_space();
+ CHECK(flows == 1);
+
+ const MemoryCounts& mc = MemoryCap::get_mem_stats();
+ CHECK(mc.reap_cycles == 2);
+ CHECK(mc.reap_attempts == 2);
+ CHECK(mc.reap_failures == 0);
+ CHECK(mc.pruned == 11);
+
+ MemoryCap::cleanup();
+}
+
+TEST(memory, reap_failure)
+{
+ const uint64_t cap = 100;
+ const uint64_t start = 50;
+ heap->total = start;
+
+ MemoryConfig config { cap, 100, 0, 2, true };
+ MemoryCap::setup(config, 1, pruner);
+ MemoryCap::thread_init();
+
+ flows = 1;
+ heap->total = cap + 1;
+
+ periodic_check();
+
+ CHECK(flows == 1);
+ free_space();
+ CHECK(flows == 0);
+
+ heap->dealloc++;
+ free_space();
+ CHECK(flows == -1);
+
+ const MemoryCounts& mc = MemoryCap::get_mem_stats();
+ CHECK(mc.reap_cycles == 1);
+ CHECK(mc.reap_attempts == 2);
+ CHECK(mc.reap_failures == 1);
+ CHECK(mc.pruned == 1);
+
+ MemoryCap::cleanup();
+}
+
+//-------------------------------------------------------------------------
+// main
+//-------------------------------------------------------------------------
+
+int main(int argc, char** argv)
+{
+ MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
#include "file_api/file_flows.h"
#include "hash/hash_key_operations.h"
#include "log/messages.h"
-#include "memory/memory_cap.h"
#include "search_engines/search_tool.h"
#include "utils/util_cstring.h"
#include "appid_http_session.h"
#include "flow/ha.h"
-#include "memory/memory_cap.h"
#include "profiler/profiler.h"
#include "app_info_table.h"
#include <string>
#include "framework/data_bus.h"
-#include "memory/memory_cap.h"
#include "protocols/protocol_ids.h"
#include "service_inspectors/http_inspect/http_msg_header.h"
#include "tp_appid_module_api.h"
#include "dce_smb1.h"
-#include "memory/memory_cap.h"
-
#include "dce_smb_utils.h"
using namespace snort;
// This implements smb session data for SMBv2
// Also provides SMBv2 related header structures
+#include <mutex>
+
#include "main/thread_config.h"
-#include "memory/memory_cap.h"
#include "utils/util.h"
-#include <mutex>
#include "dce_smb_common.h"
#include "file_api/file_flows.h"
#include "file_api/file_service.h"
-#include "memory/memory_cap.h"
#include "dce_smb1.h"
#include "dce_smb2.h"
#include "dce_smb_transaction.h"
#include "detection/detect.h"
#include "file_api/file_service.h"
-#include "memory/memory_cap.h"
#include "packet_io/active.h"
#include "protocols/packet.h"
#include "trace/trace_api.h"
#include "events/event_queue.h"
#include "log/messages.h"
#include "managers/inspector_manager.h"
-#include "memory/memory_cap.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
#include "pub_sub/sip_events.h"
#include "detection/detection_engine.h"
#include "events/event_queue.h"
-#include "memory/memory_cap.h"
#include "utils/util.h"
#include "utils/util_cstring.h"
#include "detection/detection_engine.h"
#include "file_api/file_flows.h"
-#include "memory/memory_cap.h"
#include "packet_io/sfdaq.h"
#include "profiler/profiler_defs.h"
#include "protocols/packet.h"
#include "detection/ips_context.h"
#include "flow/flow_key.h"
-#include "memory/memory_cap.h"
#include "profiler/profiler_defs.h"
#include "protocols/icmp4.h"
#include "protocols/packet.h"
#include "log/messages.h"
#include "main/analyzer.h"
#include "main/snort_config.h"
-#include "memory/memory_cap.h"
#include "packet_io/active.h"
#include "packet_io/sfdaq_config.h"
#include "profiler/profiler_defs.h"
#include "ip_session.h"
#include "framework/data_bus.h"
-#include "memory/memory_cap.h"
#include "profiler/profiler_defs.h"
#include "protocols/packet.h"
#include "pub_sub/stream_event_ids.h"
#include "detection/detection_engine.h"
#include "log/log.h"
#include "main/analyzer.h"
-#include "memory/memory_cap.h"
#include "packet_io/active.h"
#include "profiler/profiler.h"
#include "protocols/packet_manager.h"
#include "tcp_segment_node.h"
#include "main/thread.h"
-#include "memory/memory_cap.h"
#include "utils/util.h"
#include "segment_overlap_editor.h"
#include "detection/detection_engine.h"
#include "detection/rules.h"
#include "log/log.h"
-#include "memory/memory_cap.h"
#include "profiler/profiler.h"
#include "protocols/eth.h"
#include "pub_sub/intrinsic_event_ids.h"
#include "log/messages.h"
#include "main/analyzer.h"
#include "main/snort.h"
-#include "memory/memory_cap.h"
#include "packet_io/active.h"
#include "profiler/profiler_defs.h"
#include "protocols/eth.h"
#include "flow/session.h"
#include "framework/data_bus.h"
#include "hash/xhash.h"
-#include "memory/memory_cap.h"
#include "profiler/profiler_defs.h"
#include "protocols/packet.h"
#include "pub_sub/intrinsic_event_ids.h"
#include "detection/detection_engine.h"
#include "detection/rules.h"
#include "main/analyzer.h"
-#include "memory/memory_cap.h"
#include "profiler/profiler_defs.h"
#include "protocols/packet.h"
#include "trace/trace_api.h"
s_ctrlcon = ctrlcon;
LogLabel("Packet Statistics");
ModuleManager::get_module("daq")->show_stats();
-
PacketManager::dump_stats();
LogLabel("Module Statistics");
- const char* exclude = "daq snort";
+ const char* exclude = "daq snort memory";
ModuleManager::dump_stats(exclude, false);
ModuleManager::dump_stats(exclude, true);
LogLabel("Summary Statistics");
show_stats((PegCount*)&proc_stats, proc_names, array_size(proc_names)-1, "process");
+ ModuleManager::get_module("memory")->show_stats();
+ memory::MemoryCap::print(SnortConfig::log_verbose());
+
s_ctrlcon = nullptr;
}
void PrintStatistics()
{
DropStats();
- memory::MemoryCap::print(SnortConfig::log_verbose(), false);
timing_stats();
// FIXIT-L can do flag saving with RAII (much cleaner)