From: Michael Matirko (mmatirko) Date: Mon, 22 Sep 2025 14:15:02 +0000 (+0000) Subject: Pull request #4912: memory: sum global MemoryModule stats during sum_stats to avoid... X-Git-Tag: 3.9.6.0~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c87edc59c225b1bea3ffb95070032f8050576c5f;p=thirdparty%2Fsnort3.git Pull request #4912: memory: sum global MemoryModule stats during sum_stats to avoid data race and fix perf_mon issues Merge in SNORT/snort3 from ~MMATIRKO/snort3:mem_stats3 to master Squashed commit of the following: commit 7e73ec3e4a1d1b4e87839d748e86d014b4cccc3d Author: Michael Matirko Date: Tue Sep 16 10:33:59 2025 -0400 memory: combine main and first pkt thread memory stats; resolve race condition commit b846e6c2d758b2905e848c8004539e8b2a9af2a0 Author: Michael Matirko Date: Tue Sep 16 09:54:46 2025 -0400 Revert "Pull request #4194: memory: prevent data race between main and packet threads" This reverts commit 37bcc63e957bff0ef7103363126a4df8e3259626. --- diff --git a/src/main/analyzer_command.cc b/src/main/analyzer_command.cc index 4e8c7acf5..d756a574a 100644 --- a/src/main/analyzer_command.cc +++ b/src/main/analyzer_command.cc @@ -146,7 +146,7 @@ bool ACGetStats::execute(Analyzer&, void**) ACGetStats::~ACGetStats() { - ModuleManager::accumulate_module("memory"); + // FIXIT-L This should track the owner so it can dump stats to the // shell instead of the logs when initiated by a shell command DropStats(ctrlcon); @@ -164,18 +164,6 @@ bool ACResetStats::execute(Analyzer&, void**) ACResetStats::ACResetStats(clear_counter_type_t requested_type_l) : requested_type( requested_type_l) { } -ACResetStats::~ACResetStats() -{ - // Destructor is called only from main thread, - // main-thread stats are reset here. - - if (requested_type == TYPE_MODULE or requested_type == TYPE_ALL) - ModuleManager::reset_module_stats("memory"); - - if (requested_type == TYPE_SNORT or requested_type == TYPE_ALL) - ModuleManager::reset_module_stats("snort"); -} - bool ACSwap::execute(Analyzer& analyzer, void** ac_state) { if (analyzer.get_state() != Analyzer::State::PAUSED and diff --git a/src/main/analyzer_command.h b/src/main/analyzer_command.h index 03e670e8f..64998f500 100644 --- a/src/main/analyzer_command.h +++ b/src/main/analyzer_command.h @@ -109,7 +109,6 @@ public: explicit ACResetStats(clear_counter_type_t requested_type); bool execute(Analyzer&, void**) override; const char* stringify() override { return "RESET_STATS"; } - ~ACResetStats() override; private: clear_counter_type_t requested_type; }; diff --git a/src/managers/module_manager.cc b/src/managers/module_manager.cc index b700c543e..155aa5d1a 100644 --- a/src/managers/module_manager.cc +++ b/src/managers/module_manager.cc @@ -1550,16 +1550,6 @@ void ModuleManager::reset_stats(SnortConfig*) } } -void ModuleManager::reset_module_stats(const char* name) -{ - ModHook* mh = get_hook(name); - if ( mh ) - { - lock_guard lock(stats_mutex); - mh->mod->reset_stats(); - } -} - void ModuleManager::clear_global_active_counters() { auto mod_hooks = get_all_modhooks(); diff --git a/src/managers/module_manager.h b/src/managers/module_manager.h index 670ce2d98..a656fdb57 100644 --- a/src/managers/module_manager.h +++ b/src/managers/module_manager.h @@ -96,7 +96,6 @@ public: static void reset_stats(SnortConfig*); static void reset_stats(clear_counter_type_t); - static void reset_module_stats(const char* name); static void clear_global_active_counters(); static bool is_parallel_cmd(std::string control_cmd); diff --git a/src/memory/heap_interface.cc b/src/memory/heap_interface.cc index 892cf9da9..b9e6a3ec0 100644 --- a/src/memory/heap_interface.cc +++ b/src/memory/heap_interface.cc @@ -46,7 +46,7 @@ class JemallocInterface : public HeapInterface void main_init() override; void thread_init() override; - void get_process_total(uint64_t&, uint64_t&) override; + void get_process_total(uint64_t&, uint64_t&, bool bump_epoch) override; void get_thread_allocs(uint64_t&, uint64_t&) override; void print_stats(ControlConn*) override; @@ -98,11 +98,22 @@ void JemallocInterface::thread_init() // __STRDUMP_ENABLE__ } -void JemallocInterface::get_process_total(uint64_t& epoch, uint64_t& utotal) +void JemallocInterface::get_process_total(uint64_t& epoch, uint64_t& utotal, bool bump_epoch) { uint64_t cycle = 13; size_t sz = sizeof(epoch); - mallctl("epoch", (void*)&epoch, &sz, (void*)&cycle, sizeof(cycle)); + + if (!bump_epoch) + { + // Don't perform the mallctl as it'll bump the epoch, return 0 for epoch and the value for the total + epoch = 0; + mallctl("epoch", nullptr, nullptr, nullptr, 0); + + } + else + { + mallctl("epoch", (void*)&epoch, &sz, (void*)&cycle, sizeof(cycle)); + } size_t total; sz = sizeof(total); @@ -195,8 +206,8 @@ 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_process_total(uint64_t& e, uint64_t& t, bool bump_epoch) override + { e = t = 0; UNUSED(bump_epoch); } void get_thread_allocs(uint64_t& a, uint64_t& d) override { a = d = 0; } diff --git a/src/memory/heap_interface.h b/src/memory/heap_interface.h index 0ac3862a1..dfb8979ab 100644 --- a/src/memory/heap_interface.h +++ b/src/memory/heap_interface.h @@ -35,7 +35,7 @@ public: 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_process_total(uint64_t& epoch, uint64_t& total, bool bump_epoch = true) = 0; virtual void get_thread_allocs(uint64_t& alloc, uint64_t& dealloc) = 0; virtual void print_stats(ControlConn*) { } diff --git a/src/memory/memory_cap.cc b/src/memory/memory_cap.cc index e67be122f..4fa58fbad 100644 --- a/src/memory/memory_cap.cc +++ b/src/memory/memory_cap.cc @@ -35,7 +35,6 @@ #include "main/snort_config.h" #include "main/snort_types.h" #include "main/thread.h" -#include "managers/module_manager.h" #include "time/periodic.h" #include "trace/trace_api.h" #include "utils/stats.h" @@ -60,6 +59,11 @@ static size_t limit = 0; static bool over_limit = false; static std::atomic current_epoch { 0 }; +static std::atomic start_up_use { 0 }; +static std::atomic max_in_use { 0 }; + +// The most recent epoch reported by jemalloc +static std::atomic latest_epoch { 0 }; static THREAD_LOCAL uint64_t start_dealloc = 0; static THREAD_LOCAL uint64_t start_alloc = 0; @@ -71,7 +75,7 @@ static PruneHandler pruner; static void epoch_check(void*) { uint64_t epoch, total; - heap->get_process_total(epoch, total); + heap->get_process_total(epoch, total, true); bool prior = over_limit; over_limit = limit and total > limit; @@ -83,33 +87,13 @@ static void epoch_check(void*) else current_epoch = 0; -#ifndef REG_TEST - MemoryCounts& mc = MemoryCap::get_mem_stats(); -#else - auto orig_type = get_thread_type(); - - // Due to call of epoch_check in first pthread - // for build with REG_TEST, we need make that - // pthread think that we're main thread - set_thread_type(STHREAD_TYPE_MAIN); - MemoryCounts& mc = MemoryCap::get_mem_stats(); - set_thread_type(orig_type); -#endif - - if ( total > mc.max_in_use ) - mc.max_in_use = total; + if ( !start_up_use ) + start_up_use = total; - mc.cur_in_use = total; - mc.epochs++; + if ( total > max_in_use ) + max_in_use = total; - // for reporting / tracking only - uint64_t all, act, res, ret; - heap->get_aux_counts(all, act, res, ret); - - mc.app_all = all; - mc.active = act; - mc.resident = res; - mc.retained = ret; + latest_epoch = epoch; } // ----------------------------------------------------------------------------- @@ -156,7 +140,7 @@ void MemoryCap::test_main_check() void MemoryCap::init(unsigned n) { assert(in_main_thread()); - pkt_mem_stats.resize(n + 1); + pkt_mem_stats.resize(n); #ifdef UNIT_TEST pkt_mem_stats[0] = { }; @@ -202,13 +186,13 @@ void MemoryCap::start(const MemoryConfig& c, PruneHandler ph) #endif epoch_check(nullptr); - - MemoryCounts& mc = get_mem_stats(); - mc.start_up_use = mc.cur_in_use; } void MemoryCap::stop() -{ epoch_check(nullptr); } +{ + epoch_check(nullptr); + update_global_stats(); +} void MemoryCap::thread_init() { @@ -225,11 +209,11 @@ void MemoryCap::thread_term() MemoryCounts& MemoryCap::get_mem_stats() { - // main thread stats do not overlap with packet threads + // main thread stats overlap with packet thread 1 if ( in_main_thread() ) return pkt_mem_stats[0]; - auto id = get_instance_id() + 1; + auto id = get_instance_id(); return pkt_mem_stats[id]; } @@ -315,10 +299,51 @@ void MemoryCap::free_space() // required to capture any update in final epoch // which happens after packet threads have stopped -void MemoryCap::update_pegs() +void MemoryCap::update_pegs(PegCount* pc) { + MemoryCounts* mp = (MemoryCounts*)pc; + const MemoryCounts& mc = get_mem_stats(); + if ( config.enabled ) - ModuleManager::accumulate_module("memory"); + { + mp->epochs = mc.epochs; + mp->cur_in_use = mc.cur_in_use; + } + else + { + mp->allocated = 0; + mp->deallocated = 0; + } +} + +void MemoryCap::update_global_stats() +{ + if ( !pkt_mem_stats.size() ) + return; + + MemoryCounts& mc = pkt_mem_stats[0]; + + uint64_t epoch, total; + heap->get_process_total(epoch, total, false); + + mc.max_in_use = max_in_use; + + mc.cur_in_use = total; + + if ( epoch > latest_epoch ) + latest_epoch = epoch; + + mc.epochs = latest_epoch; + mc.start_up_use = start_up_use; + + // for reporting / tracking only + uint64_t all, act, res, ret; + heap->get_aux_counts(all, act, res, ret); + + mc.app_all = all; + mc.active = act; + mc.resident = res; + mc.retained = ret; } // called at startup and shutdown diff --git a/src/memory/memory_cap.h b/src/memory/memory_cap.h index 5b9e550df..edd990c8c 100644 --- a/src/memory/memory_cap.h +++ b/src/memory/memory_cap.h @@ -80,9 +80,10 @@ public: // main and packet threads static MemoryCounts& get_mem_stats(); + static void update_global_stats(); // main thread - shutdown - static void update_pegs(); + static void update_pegs(PegCount*); static void dump_mem_stats(ControlConn*); static void heap_profile_config(bool enable, uint64_t sample_rate); diff --git a/src/memory/memory_module.cc b/src/memory/memory_module.cc index 3c3e3391f..286eb90c2 100644 --- a/src/memory/memory_module.cc +++ b/src/memory/memory_module.cc @@ -118,6 +118,12 @@ const PegInfo* MemoryModule::get_pegs() const PegCount* MemoryModule::get_counts() const { return (PegCount*)&memory::MemoryCap::get_mem_stats(); } +void MemoryModule::sum_stats(bool dump_stats) +{ + memory::MemoryCap::update_global_stats(); + Module::sum_stats(dump_stats); +} + void MemoryModule::set_trace(const Trace* trace) const { memory_trace = trace; } diff --git a/src/memory/memory_module.h b/src/memory/memory_module.h index cb5721de4..5aa05eaaa 100644 --- a/src/memory/memory_module.h +++ b/src/memory/memory_module.h @@ -30,6 +30,7 @@ public: const PegInfo* get_pegs() const override; PegCount* get_counts() const override; + void sum_stats(bool dump_stats) override; bool set(const char*, snort::Value&, snort::SnortConfig*) override; bool end(const char*, int, snort::SnortConfig*) override; diff --git a/src/memory/test/CMakeLists.txt b/src/memory/test/CMakeLists.txt index b1b2baf19..0d470e3c1 100644 --- a/src/memory/test/CMakeLists.txt +++ b/src/memory/test/CMakeLists.txt @@ -3,4 +3,5 @@ add_cpputest( memory_cap_test SOURCES ../heap_interface.cc ../memory_cap.cc + ../../framework/module.cc ) diff --git a/src/memory/test/memory_cap_test.cc b/src/memory/test/memory_cap_test.cc index b110b4094..f2bc3c9d2 100644 --- a/src/memory/test/memory_cap_test.cc +++ b/src/memory/test/memory_cap_test.cc @@ -24,9 +24,11 @@ #include "memory/memory_cap.h" +#include + #include "log/messages.h" #include "main/thread.h" -#include "managers/module_manager.h" +#include "main/thread_config.h" #include "profiler/profiler.h" #include "time/periodic.h" #include "trace/trace_api.h" @@ -41,6 +43,7 @@ using namespace snort; #include using namespace memory; +using IndexVec = std::vector; //-------------------------------------------------------------------------- // stubs @@ -60,16 +63,21 @@ uint8_t snort::TraceApi::get_constraints_generation() { return 0; } unsigned get_instance_id() { return 0; } + +unsigned ThreadConfig::get_instance_max() +{ return 0; } + } +void show_stats(PegCount*, const PegInfo*, unsigned, const char*) { } +void show_stats(PegCount*, const PegInfo*, const IndexVec&, const char*, FILE*) { } + THREAD_LOCAL const Trace* memory_trace = nullptr; #ifndef REG_TEST void Periodic::register_handler(PeriodicHook, void*, uint16_t, uint32_t) { } #endif -void ModuleManager::accumulate_module(const char*) { } - //-------------------------------------------------------------------------- // mocks //-------------------------------------------------------------------------- @@ -83,8 +91,14 @@ public: void thread_init() override { thread_init_calls++; } - void get_process_total(uint64_t& e, uint64_t& t) override - { e = ++epoch; t = total; } + void get_process_total(uint64_t& e, uint64_t& t, bool bump_epoch) override + { + t = total; + if ( bump_epoch ) + e = ++epoch; + else + e = epoch; + } void get_thread_allocs(uint64_t& a, uint64_t& d) override { a = alloc; d = dealloc; } @@ -125,9 +139,6 @@ SThreadType get_thread_type() } -void set_thread_type(SThreadType) -{ pkt_thread = false; } - static void free_space() { pkt_thread = true; @@ -135,19 +146,6 @@ static void free_space() pkt_thread = false; } -static MemoryCounts& get_main_stats() -{ - return MemoryCap::get_mem_stats(); -} - -static MemoryCounts& get_pkt_stats() -{ - pkt_thread = true; - MemoryCounts& res = MemoryCap::get_mem_stats(); - pkt_thread = false; - return res; -} - //-------------------------------------------------------------------------- // tests //-------------------------------------------------------------------------- @@ -261,8 +259,8 @@ TEST(memory, prune1) MemoryCap::start(config, pruner); MemoryCap::thread_init(); - const MemoryCounts& mc = get_main_stats(); - const MemoryCounts& pc = get_pkt_stats(); + const MemoryCounts& mc = MemoryCap::get_mem_stats(); + MemoryCap::update_global_stats(); CHECK(mc.start_up_use == start); fd.flows = 3; @@ -279,11 +277,11 @@ TEST(memory, prune1) free_space(); // finish pruning UNSIGNED_LONGS_EQUAL(1, fd.flows); - UNSIGNED_LONGS_EQUAL(2, pc.reap_decrease); + UNSIGNED_LONGS_EQUAL(2, mc.reap_decrease); free_space(); UNSIGNED_LONGS_EQUAL(1, fd.flows); - UNSIGNED_LONGS_EQUAL(2, pc.reap_decrease); + UNSIGNED_LONGS_EQUAL(2, mc.reap_decrease); periodic_check(); // still over the limit CHECK(heap->epoch == 3); @@ -297,7 +295,7 @@ TEST(memory, prune1) free_space(); // abort UNSIGNED_LONGS_EQUAL(0, fd.flows); - UNSIGNED_LONGS_EQUAL(3, pc.reap_decrease); + UNSIGNED_LONGS_EQUAL(3, mc.reap_decrease); heap->total = cap + 1; // over the limit periodic_check(); @@ -315,8 +313,8 @@ TEST(memory, prune1) free_space(); // abort, reap_increase update UNSIGNED_LONGS_EQUAL(0, fd.flows); - UNSIGNED_LONGS_EQUAL(3, pc.reap_decrease); - UNSIGNED_LONGS_EQUAL(4, pc.reap_increase); + UNSIGNED_LONGS_EQUAL(3, mc.reap_decrease); + UNSIGNED_LONGS_EQUAL(4, mc.reap_increase); heap->total = cap + 1; // over the limit periodic_check(); @@ -333,25 +331,27 @@ TEST(memory, prune1) heap->alloc += 3; free_space(); // abort, reap_increase update + memory::MemoryCap::update_global_stats(); UNSIGNED_LONGS_EQUAL(0, fd.flows); - UNSIGNED_LONGS_EQUAL(3, pc.reap_decrease); - UNSIGNED_LONGS_EQUAL(6, pc.reap_increase); + UNSIGNED_LONGS_EQUAL(3, mc.reap_decrease); + UNSIGNED_LONGS_EQUAL(6, mc.reap_increase); UNSIGNED_LONGS_EQUAL(start, mc.start_up_use); UNSIGNED_LONGS_EQUAL(cap + 1, mc.max_in_use); UNSIGNED_LONGS_EQUAL(cap, mc.cur_in_use); UNSIGNED_LONGS_EQUAL(heap->epoch, mc.epochs); - UNSIGNED_LONGS_EQUAL(heap->alloc, pc.allocated); - UNSIGNED_LONGS_EQUAL(heap->dealloc, pc.deallocated); + UNSIGNED_LONGS_EQUAL(heap->alloc, mc.allocated); + UNSIGNED_LONGS_EQUAL(heap->dealloc, mc.deallocated); - UNSIGNED_LONGS_EQUAL(4, pc.reap_cycles); - UNSIGNED_LONGS_EQUAL(5, pc.reap_attempts); - UNSIGNED_LONGS_EQUAL(0, pc.reap_failures); - UNSIGNED_LONGS_EQUAL(3, pc.reap_aborts); + UNSIGNED_LONGS_EQUAL(4, mc.reap_cycles); + UNSIGNED_LONGS_EQUAL(5, mc.reap_attempts); + UNSIGNED_LONGS_EQUAL(0, mc.reap_failures); + UNSIGNED_LONGS_EQUAL(3, mc.reap_aborts); heap->total = start; MemoryCap::stop(); + memory::MemoryCap::update_global_stats(); CHECK(mc.epochs == heap->epoch); CHECK(mc.cur_in_use == start); } @@ -388,12 +388,12 @@ TEST(memory, prune3) free_space(); CHECK(fd.flows == 0); - const MemoryCounts& pc = get_pkt_stats(); - UNSIGNED_LONGS_EQUAL(1, pc.reap_cycles); - UNSIGNED_LONGS_EQUAL(3, pc.reap_attempts); - UNSIGNED_LONGS_EQUAL(0, pc.reap_failures); - UNSIGNED_LONGS_EQUAL(1, pc.reap_decrease); - UNSIGNED_LONGS_EQUAL(0, pc.reap_increase); + const MemoryCounts& mc = MemoryCap::get_mem_stats(); + UNSIGNED_LONGS_EQUAL(1, mc.reap_cycles); + UNSIGNED_LONGS_EQUAL(3, mc.reap_attempts); + UNSIGNED_LONGS_EQUAL(0, mc.reap_failures); + UNSIGNED_LONGS_EQUAL(1, mc.reap_decrease); + UNSIGNED_LONGS_EQUAL(0, mc.reap_increase); MemoryCap::stop(); } @@ -430,12 +430,12 @@ TEST(memory, two_cycles) free_space(); CHECK(fd.flows == 1); - const MemoryCounts& pc = get_pkt_stats(); - UNSIGNED_LONGS_EQUAL(2, pc.reap_cycles); - UNSIGNED_LONGS_EQUAL(2, pc.reap_attempts); - UNSIGNED_LONGS_EQUAL(0, pc.reap_failures); - UNSIGNED_LONGS_EQUAL(11, pc.reap_decrease); - UNSIGNED_LONGS_EQUAL(0, pc.reap_increase); + const MemoryCounts& mc = MemoryCap::get_mem_stats(); + UNSIGNED_LONGS_EQUAL(2, mc.reap_cycles); + UNSIGNED_LONGS_EQUAL(2, mc.reap_attempts); + UNSIGNED_LONGS_EQUAL(0, mc.reap_failures); + UNSIGNED_LONGS_EQUAL(11, mc.reap_decrease); + UNSIGNED_LONGS_EQUAL(0, mc.reap_increase); MemoryCap::stop(); } @@ -450,7 +450,7 @@ TEST(memory, reap_failure) MemoryCap::start(config, pruner); MemoryCap::thread_init(); - const MemoryCounts& pc = get_pkt_stats(); + const MemoryCounts& mc = MemoryCap::get_mem_stats(); fd.flows = 1; heap->total = cap + 1; @@ -461,7 +461,7 @@ TEST(memory, reap_failure) free_space(); // reap failure CHECK(fd.flows == -1); - UNSIGNED_LONGS_EQUAL(1, pc.reap_decrease); + UNSIGNED_LONGS_EQUAL(1, mc.reap_decrease); fd.flows = 1; heap->total = cap + 1; @@ -474,8 +474,8 @@ TEST(memory, reap_failure) free_space(); // reap failure, reap_increase update CHECK(fd.flows == -1); - UNSIGNED_LONGS_EQUAL(1, pc.reap_decrease); - UNSIGNED_LONGS_EQUAL(2, pc.reap_increase); + UNSIGNED_LONGS_EQUAL(1, mc.reap_decrease); + UNSIGNED_LONGS_EQUAL(2, mc.reap_increase); fd.flows = 1; heap->total = cap + 1; @@ -488,12 +488,12 @@ TEST(memory, reap_failure) free_space(); // reap failure, reap_increase update CHECK(fd.flows == -1); - UNSIGNED_LONGS_EQUAL(1, pc.reap_decrease); - UNSIGNED_LONGS_EQUAL(3, pc.reap_increase); + UNSIGNED_LONGS_EQUAL(1, mc.reap_decrease); + UNSIGNED_LONGS_EQUAL(3, mc.reap_increase); - UNSIGNED_LONGS_EQUAL(3, pc.reap_cycles); - UNSIGNED_LONGS_EQUAL(6, pc.reap_attempts); - UNSIGNED_LONGS_EQUAL(3, pc.reap_failures); + UNSIGNED_LONGS_EQUAL(3, mc.reap_cycles); + UNSIGNED_LONGS_EQUAL(6, mc.reap_attempts); + UNSIGNED_LONGS_EQUAL(3, mc.reap_failures); MemoryCap::stop(); } @@ -527,12 +527,12 @@ TEST(memory, reap_freed_outside_of_pruning) free_space(); UNSIGNED_LONGS_EQUAL(0, fd.flows); - const MemoryCounts& pc = get_pkt_stats(); - UNSIGNED_LONGS_EQUAL(1, pc.reap_cycles); - UNSIGNED_LONGS_EQUAL(2, pc.reap_attempts); - UNSIGNED_LONGS_EQUAL(0, pc.reap_failures); - UNSIGNED_LONGS_EQUAL(2, pc.reap_decrease); - UNSIGNED_LONGS_EQUAL(0, pc.reap_increase); + const MemoryCounts& mc = MemoryCap::get_mem_stats(); + UNSIGNED_LONGS_EQUAL(1, mc.reap_cycles); + UNSIGNED_LONGS_EQUAL(2, mc.reap_attempts); + UNSIGNED_LONGS_EQUAL(0, mc.reap_failures); + UNSIGNED_LONGS_EQUAL(2, mc.reap_decrease); + UNSIGNED_LONGS_EQUAL(0, mc.reap_increase); MemoryCap::stop(); } diff --git a/src/utils/stats.cc b/src/utils/stats.cc index 5fe5cc50b..5d384791c 100644 --- a/src/utils/stats.cc +++ b/src/utils/stats.cc @@ -205,8 +205,8 @@ void DropStats(ControlConn* ctrlcon) void PrintStatistics() { - if ( ModuleManager::get_stats("memory") ) - memory::MemoryCap::update_pegs(); + if ( PegCount* pc = ModuleManager::get_stats("memory") ) + memory::MemoryCap::update_pegs(pc); DropStats(); timing_stats();