]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3826: Reap fix master
authorRon Dempster (rdempste) <rdempste@cisco.com>
Thu, 27 Apr 2023 14:08:35 +0000 (14:08 +0000)
committerRon Dempster (rdempste) <rdempste@cisco.com>
Thu, 27 Apr 2023 14:08:35 +0000 (14:08 +0000)
Merge in SNORT/snort3 from ~RDEMPSTE/snort3:reap_fix_master to master

Squashed commit of the following:

commit fcaaf4316971b0f38e170e3d92a98571c184e25a
Author: Ron Dempster (rdempste) <rdempste@cisco.com>
Date:   Thu Apr 20 14:49:11 2023 -0400

    flow, hash, stream: add a free list node count that is output as a peg count

commit 47a20ab19a85ac3a33787c5ab53a30d15c1208d9
Author: Ron Dempster (rdempste) <rdempste@cisco.com>
Date:   Wed Apr 19 13:05:52 2023 -0400

    memory: fix memory pruning race condition and bail on reap failure

14 files changed:
src/flow/flow_cache.cc
src/flow/flow_cache.h
src/flow/flow_control.cc
src/flow/flow_control.h
src/flow/test/flow_control_test.cc
src/hash/test/zhash_test.cc
src/hash/xhash.cc
src/hash/xhash.h
src/memory/memory_cap.cc
src/memory/memory_cap.h
src/memory/memory_module.cc
src/memory/test/memory_cap_test.cc
src/stream/base/stream_base.cc
src/stream/base/stream_module.h

index 22460ebb78627ce76e9aa892faef5db3a5ccf2d9..238cd49c3dc331093d718fd4d6b088d0c0ed734a 100644 (file)
@@ -560,3 +560,8 @@ size_t FlowCache::flows_size() const
 {
     return hash_table->get_num_nodes();
 }
+
+size_t FlowCache::free_flows_size() const
+{
+    return hash_table->get_num_free_nodes();
+}
index a3eb84c6df53b8c0bed6d174820cc4cef35746d1..0c725c42bc599857fdafb645338ec3926594bc1b 100644 (file)
@@ -104,6 +104,7 @@ public:
     size_t uni_flows_size() const;
     size_t uni_ip_flows_size() const;
     size_t flows_size() const;
+    size_t free_flows_size() const;
 
 private:
     void delete_uni();
index ede0570175761bcad9314f9d61e323dfcf2e29b1..77e898ae9f87beeb3dd3adc762d92f67e5cb0cba 100644 (file)
@@ -88,6 +88,9 @@ PegCount FlowControl::get_uni_ip_flows() const
 PegCount FlowControl::get_num_flows() const
 { return cache->flows_size(); }
 
+PegCount FlowControl::get_num_free_flows() const
+{ return cache->free_flows_size(); }
+
 
 //-------------------------------------------------------------------------
 // cache foo
index 500d9f5a8b54ab63e516e374750fe5caad4a27b6..fbe9a3f5c391f9c82cc5e5723a3237fe5c05d2f7 100644 (file)
@@ -98,6 +98,7 @@ public:
     PegCount get_uni_flows() const;
     PegCount get_uni_ip_flows() const;
     PegCount get_num_flows() const;
+    PegCount get_num_free_flows() const;
 
 private:
     void set_key(snort::FlowKey*, snort::Packet*);
index 8d4bcba5744cc900cdf98c69c2d5f4fcc530564f..e00c02de2a2236aee8dcae05f4bf0edf19fa1488 100644 (file)
@@ -85,6 +85,7 @@ unsigned FlowCache::timeout(unsigned, time_t) { return 1; }
 size_t FlowCache::uni_flows_size() const { return 0; }
 size_t FlowCache::uni_ip_flows_size() const { return 0; }
 size_t FlowCache::flows_size() const { return 0; }
+size_t FlowCache::free_flows_size() const { return 0; }
 void Flow::init(PktType) { }
 void DataBus::publish(unsigned, unsigned, DataEvent&, Flow*) { }
 void DataBus::publish(unsigned, unsigned, const uint8_t*, unsigned, Flow*) { }
index 6f590d7941f9654fdac71267f3fcc4bdc3a29c5d..25bad2d5f1d7f2c23589cc94b49ab98b827a82be 100644 (file)
@@ -108,16 +108,22 @@ TEST(zhash, create_zhash_test)
         zh->push(data);
     }
 
+    UNSIGNED_LONGS_EQUAL(0, zh->get_num_nodes());
+    UNSIGNED_LONGS_EQUAL(MAX_ZHASH_NODES, zh->get_num_free_nodes());
+
     std::string key_prefix = "foo";
     for (unsigned i = 0; i < MAX_ZHASH_NODES; i++ )
-     {
+    {
         std::string key;
         key = key_prefix + std::to_string(i + 1);
         memcpy(key_buf, key.c_str(), key.size());
         unsigned* data = (unsigned*)zh->get(key_buf);
         CHECK(*data == 0);
         *data = i + 1;
-     }
+    }
+
+    UNSIGNED_LONGS_EQUAL(MAX_ZHASH_NODES, zh->get_num_nodes());
+    UNSIGNED_LONGS_EQUAL(0, zh->get_num_free_nodes());
 
     unsigned nodes_walked = 0;
     unsigned* data = (unsigned*)zh->lru_first();
index 00373b2948d10d4e0b108de8407a3fd8d7ed9e62..654de28bead517d014bdc706a48c36b90365f1a9 100644 (file)
@@ -427,6 +427,7 @@ void XHash::save_free_node(HashNode* hnode)
         hnode->gnext = nullptr;
         fhead = hnode;
     }
+    ++num_free_nodes;
 }
 
 HashNode* XHash::get_free_node()
@@ -437,6 +438,7 @@ HashNode* XHash::get_free_node()
         fhead = fhead->gnext;
         if ( fhead )
             fhead->gprev = nullptr;
+        --num_free_nodes;
     }
 
     return node;
@@ -464,6 +466,7 @@ void XHash::purge_free_list()
     }
 
     fhead = nullptr;
+    num_free_nodes = 0;
 }
 
 void XHash::clear_hash()
index 94fa861337676bf09da16eb521cd94fa41228133..f823e31718a4286f01965c6ec4570908f38b539d 100644 (file)
@@ -74,6 +74,9 @@ public:
     unsigned get_num_nodes()
     { return num_nodes; }
 
+    unsigned get_num_free_nodes()
+    { return num_free_nodes; }
+
     void set_memcap(unsigned long memcap)
     { mem_allocator->set_mem_capacity(memcap); }
 
@@ -113,6 +116,7 @@ protected:
     unsigned nrows = 0;
     unsigned keysize = 0;
     unsigned num_nodes = 0;
+    unsigned num_free_nodes = 0;
     bool recycle_nodes = true;
     bool anr_enabled = true;
 
index bce6148505f2c2e07c2f793de465ff14c44546e8..ad3d8a6474f4b662891f978bc4a9a8dbe426a828 100644 (file)
@@ -56,10 +56,9 @@ static std::vector<MemoryCounts> pkt_mem_stats;
 static MemoryConfig config;
 static size_t limit = 0;
 
-static std::atomic<bool> over_limit { false };
+static bool over_limit = false;
 static std::atomic<uint64_t> current_epoch { 0 };
 
-static THREAD_LOCAL uint64_t last_dealloc = 0;
 static THREAD_LOCAL uint64_t start_dealloc = 0;
 static THREAD_LOCAL uint64_t start_alloc = 0;
 static THREAD_LOCAL uint64_t start_epoch = 0;
@@ -72,13 +71,15 @@ static void epoch_check(void*)
     uint64_t epoch, total;
     heap->get_process_total(epoch, total);
 
-    current_epoch = epoch;
-
     bool prior = over_limit;
     over_limit = limit and total > limit;
 
     if ( prior != over_limit )
         trace_logf(memory_trace, nullptr, "Epoch=%lu, memory=%lu (%s)\n", epoch, total, over_limit?"over":"under");
+    if ( over_limit )
+        current_epoch = epoch;
+    else
+        current_epoch = 0;
 
     MemoryCounts& mc = MemoryCap::get_mem_stats();
 
@@ -221,37 +222,73 @@ void MemoryCap::free_space()
     MemoryCounts& mc = get_mem_stats();
     heap->get_thread_allocs(mc.allocated, mc.deallocated);
 
-    if ( !over_limit and !start_dealloc )
-        return;
-
+    // Not already pruning
     if ( !start_dealloc )
     {
+        // Not over the limit
+        if ( !current_epoch )
+            return;
+        // Already completed pruning in the epoch
         if ( current_epoch == start_epoch )
             return;
 
-        start_dealloc = last_dealloc = mc.deallocated;
+        // Start pruning for this epoch
+        start_dealloc = mc.deallocated;
         start_alloc = mc.allocated;
         start_epoch = current_epoch;
         mc.reap_cycles++;
     }
 
-    mc.pruned += (mc.deallocated - last_dealloc);
-    last_dealloc = mc.deallocated;
-
-    uint64_t alloc = mc.allocated - start_alloc;
     uint64_t dealloc = mc.deallocated - start_dealloc;
-    if ( dealloc > alloc and ( ( dealloc - alloc ) >= config.prune_target ) )
+    uint64_t alloc = mc.allocated - start_alloc;
+
+    // Has the process gone under the limit
+    if ( !current_epoch )
+    {
+        if ( dealloc > alloc)
+            mc.reap_decrease += dealloc - alloc;
+        else
+            mc.reap_increase += alloc - dealloc;
+        start_dealloc = 0;
+        mc.reap_aborts++;
+        return;
+    }
+    // Has this thread freed enough outside of pruning
+    else if ( dealloc > alloc and ( ( dealloc - alloc ) >= config.prune_target ) )
     {
+        mc.reap_decrease += dealloc - alloc;
         start_dealloc = 0;
         return;
     }
 
     ++mc.reap_attempts;
 
-    if ( pruner() )
-        return;
+    bool prune_success = pruner();
 
-    ++mc.reap_failures;
+    // Updates values after pruning
+    heap->get_thread_allocs(mc.allocated, mc.deallocated);
+    alloc = mc.allocated - start_alloc;
+    dealloc = mc.deallocated - start_dealloc;
+
+    if ( prune_success )
+    {
+        // Pruned the target amount, so stop pruning for this epoch
+        if ( dealloc > alloc and ( ( dealloc - alloc ) >= config.prune_target ) )
+        {
+            mc.reap_decrease += dealloc - alloc;
+            start_dealloc = 0;
+        }
+    }
+    else
+    {
+        // Failed to prune, so stop pruning
+        if ( dealloc > alloc)
+            mc.reap_decrease += dealloc - alloc;
+        else
+            mc.reap_increase += alloc - dealloc;
+        start_dealloc = 0;
+        ++mc.reap_failures;
+    }
 }
 
 // required to capture any update in final epoch
index cef9963ce6fed530b131c71bad19abe3aaf17b29..1d03dee128c56cc4c1e80049f70b35b1ea0b5c85 100644 (file)
@@ -45,7 +45,9 @@ struct MemoryCounts
     PegCount reap_cycles;
     PegCount reap_attempts;
     PegCount reap_failures;
-    PegCount pruned;
+    PegCount reap_aborts;
+    PegCount reap_decrease;
+    PegCount reap_increase;
 };
 
 typedef bool (*PruneHandler)();
index d3c55890eac80829bb1f00c0d425ac171bdd6aab..b364a846f059eb678897a3b04a1fc244f0ea06c7 100644 (file)
@@ -70,7 +70,9 @@ const PegInfo mem_pegs[] =
     { 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::NOW, "pruned", "total amount of memory pruned" },
+    { CountType::NOW, "reap_aborts", "abort pruning before target due to process under limit" },
+    { CountType::NOW, "reap_decrease", "total amount of the decrease in thread memory while process over limit" },
+    { CountType::NOW, "reap_increase", "total amount of the increase in thread memory while process over limit" },
     { CountType::END, nullptr, nullptr }
 };
 
index 35f5ad8f841344fd2faf864e3f0e5226974dad06..83972678145bfb98652d5bf2dbfcd27aae2bdb3a 100644 (file)
@@ -36,6 +36,7 @@ using namespace snort;
 
 #include <CppUTest/CommandLineTestRunner.h>
 #include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
 
 using namespace memory;
 
@@ -92,10 +93,21 @@ public:
 static void periodic_check()
 { MemoryCap::test_main_check(); }
 
-static int flows;
+struct TestFlowData
+{
+    int flows = 0;
+    unsigned flow_to_alloc_factor = 1;
+};
 
 static bool pruner()
-{ return --flows >= 0; }
+{
+    MockHeap* heap = (MockHeap*)mock().getData("heap").getObjectPointer();
+    TestFlowData* fd = (TestFlowData*)mock().getData("flows").getObjectPointer();
+    if ( heap && 0 < fd->flows)
+        heap->dealloc += fd->flow_to_alloc_factor;
+    fd->flows--;
+    return fd->flows >= 0;
+}
 
 static bool pkt_thread = false;
 
@@ -120,8 +132,13 @@ static void free_space()
 
 TEST_GROUP(memory_off)
 {
+    TestFlowData fd;
+
     void setup() override
     {
+        fd = {};
+        mock().setDataObject("flows", "TestFlowData", &fd);
+        mock().setDataObject("heap", "MockHeap", nullptr);
         MemoryCap::init(1);
         MemoryCap::set_pruner(pruner);
     }
@@ -129,7 +146,6 @@ TEST_GROUP(memory_off)
     void teardown() override
     {
         MemoryCap::term();
-        flows = 0;
     }
 };
 
@@ -140,7 +156,7 @@ TEST(memory_off, disabled)
 
     free_space();
 
-    CHECK(flows == 0);
+    CHECK(fd.flows == 0);
 
     const MemoryCounts& mc = MemoryCap::get_mem_stats();
     CHECK(mc.start_up_use == 0);
@@ -157,7 +173,7 @@ TEST(memory_off, nerfed)
 
     free_space();
 
-    CHECK(flows == 0);
+    CHECK(fd.flows == 0);
 
     const MemoryCounts& mc = MemoryCap::get_mem_stats();
     CHECK(mc.start_up_use == 0);
@@ -173,20 +189,22 @@ TEST(memory_off, nerfed)
 
 TEST_GROUP(memory)
 {
+    TestFlowData fd;
     MockHeap* heap = nullptr;
 
     void setup() override
     {
+        fd = {0, 1};
+        mock().setDataObject("flows", "TestFlowData", &fd);
         MemoryCap::init(1);
         heap = new MockHeap;
         MemoryCap::set_heap_interface(heap);
+        mock().setDataObject("heap", "MockHeap", heap);
     }
 
     void teardown() override
     {
         MemoryCap::term();
-        heap = nullptr;
-        flows = 0;
     }
 };
 
@@ -205,7 +223,7 @@ TEST(memory, default_enabled)
 
     periodic_check();
     CHECK(heap->epoch == 2);
-    CHECK(flows == 0);
+    CHECK(fd.flows == 0);
 
     MemoryCap::stop();
     CHECK(heap->epoch == 3);
@@ -217,51 +235,97 @@ TEST(memory, prune1)
     const uint64_t start = 50;
     heap->total = start;
 
-    MemoryConfig config { cap, 100, 0, 1, true };
+    MemoryConfig config { cap, 100, 0, 2, true };
     MemoryCap::start(config, pruner);
     MemoryCap::thread_init();
 
     const MemoryCounts& mc = MemoryCap::get_mem_stats();
     CHECK(mc.start_up_use == start);
 
-    flows = 2;
+    fd.flows = 3;
     free_space();
-    CHECK(flows == 2);
+    UNSIGNED_LONGS_EQUAL(3, fd.flows);
 
-    heap->total = cap + 1;
+    heap->total = cap + 1; // over the limit
     periodic_check();
     CHECK(heap->epoch == 2);
 
-    CHECK(flows == 2);
+    UNSIGNED_LONGS_EQUAL(3, fd.flows);
+    free_space(); // this prunes 1
+    UNSIGNED_LONGS_EQUAL(2, fd.flows);
+
+    free_space(); // finish pruning
+    UNSIGNED_LONGS_EQUAL(1, fd.flows);
+    UNSIGNED_LONGS_EQUAL(2, mc.reap_decrease);
+
     free_space();
-    CHECK(flows == 1);
+    UNSIGNED_LONGS_EQUAL(1, fd.flows);
+    UNSIGNED_LONGS_EQUAL(2, mc.reap_decrease);
 
-    heap->total = cap;
-    periodic_check();
+    periodic_check(); // still over the limit
     CHECK(heap->epoch == 3);
 
-    heap->alloc++;
-    heap->dealloc++;
+    free_space();
+    UNSIGNED_LONGS_EQUAL(0, fd.flows);
+
+    heap->total = cap; // no longer over the limit
+    periodic_check();
+    CHECK(heap->epoch == 4);
+
+    free_space(); // abort
+    UNSIGNED_LONGS_EQUAL(0, fd.flows);
+    UNSIGNED_LONGS_EQUAL(3, mc.reap_decrease);
 
+    heap->total = cap + 1; // over the limit
+    periodic_check();
+    CHECK(heap->epoch == 5);
+
+    fd.flows = 1;
     free_space();
-    CHECK(flows == 0);
+    UNSIGNED_LONGS_EQUAL(0, fd.flows);
 
-    heap->dealloc++;
+    heap->total = cap; // no longer over the limit
+    periodic_check();
+    CHECK(heap->epoch == 6);
+
+    heap->alloc += 5;
+
+    free_space(); // abort, reap_increase update
+    UNSIGNED_LONGS_EQUAL(0, fd.flows);
+    UNSIGNED_LONGS_EQUAL(3, mc.reap_decrease);
+    UNSIGNED_LONGS_EQUAL(4, mc.reap_increase);
+
+    heap->total = cap + 1; // over the limit
+    periodic_check();
+    CHECK(heap->epoch == 7);
+
+    fd.flows = 1;
     free_space();
-    CHECK(flows == 0);
+    UNSIGNED_LONGS_EQUAL(0, fd.flows);
 
-    CHECK(mc.start_up_use == start);
-    CHECK(mc.max_in_use == cap + 1);
-    CHECK(mc.cur_in_use == cap);
+    heap->total = cap; // no longer over the limit
+    periodic_check();
+    CHECK(heap->epoch == 8);
 
-    CHECK(mc.epochs == heap->epoch);
-    CHECK(mc.allocated == heap->alloc);
-    CHECK(mc.deallocated == heap->dealloc);
+    heap->alloc += 3;
+
+    free_space(); // abort, reap_increase update
+    UNSIGNED_LONGS_EQUAL(0, fd.flows);
+    UNSIGNED_LONGS_EQUAL(3, mc.reap_decrease);
+    UNSIGNED_LONGS_EQUAL(6, mc.reap_increase);
 
-    CHECK(mc.reap_cycles == 1);
-    CHECK(mc.reap_attempts == 2);
-    CHECK(mc.reap_failures == 0);
-    CHECK(mc.pruned == 2);
+    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, mc.allocated);
+    UNSIGNED_LONGS_EQUAL(heap->dealloc, mc.deallocated);
+
+    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();
@@ -276,35 +340,37 @@ TEST(memory, prune3)
     MemoryCap::start(config, pruner);
     MemoryCap::thread_init();
 
-    flows = 3;
+    fd.flows = 3;
+    fd.flow_to_alloc_factor = 0;
     heap->total = cap + 1;
 
     periodic_check();
     CHECK(heap->epoch == 2);
 
-    CHECK(flows == 3);
+    CHECK(fd.flows == 3);
     free_space();
-    CHECK(flows == 2);
+    CHECK(fd.flows == 2);
 
     free_space();
-    CHECK(flows == 1);
+    CHECK(fd.flows == 1);
 
+    fd.flow_to_alloc_factor = 1;
     free_space();
-    CHECK(flows == 0);
+    CHECK(fd.flows == 0);
 
     heap->total = cap;
     periodic_check();
     CHECK(heap->epoch == 3);
 
-    heap->dealloc++;
     free_space();
-    CHECK(flows == 0);
+    CHECK(fd.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);
+    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();
 }
@@ -316,43 +382,37 @@ TEST(memory, two_cycles)
     MemoryCap::start(config, pruner);
     MemoryCap::thread_init();
 
-    flows = 3;
+    fd.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);
+    CHECK(fd.flows == 3);
+    free_space();  // prune 1 flow and stop pruning from target
+    CHECK(fd.flows == 2);
 
-    free_space();  // at most 1 reap cycle per epoch
-    CHECK(flows == 2);
+    free_space();
+    CHECK(fd.flows == 2);
 
     heap->total = cap;
     periodic_check();
     free_space();
-    CHECK(flows == 2);
+    CHECK(fd.flows == 2);
 
-    heap->total = cap + 10;
+    fd.flow_to_alloc_factor = 10;
+    heap->total = cap + 1;
     periodic_check();
     free_space();
-    CHECK(flows == 1);
-
-    heap->total = cap;
-    heap->dealloc += 10;
-    periodic_check();
+    CHECK(fd.flows == 1);
 
     free_space();
-    CHECK(flows == 1);
+    CHECK(fd.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);
+    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();
 }
@@ -367,24 +427,89 @@ TEST(memory, reap_failure)
     MemoryCap::start(config, pruner);
     MemoryCap::thread_init();
 
-    flows = 1;
+    const MemoryCounts& mc = MemoryCap::get_mem_stats();
+
+    fd.flows = 1;
+    heap->total = cap + 1;
+    periodic_check();
+
+    free_space();
+    CHECK(fd.flows == 0);
+
+    free_space(); // reap failure
+    CHECK(fd.flows == -1);
+    UNSIGNED_LONGS_EQUAL(1, mc.reap_decrease);
+
+    fd.flows = 1;
+    heap->total = cap + 1;
+    periodic_check();
+
+    free_space();
+    CHECK(fd.flows == 0);
+
+    heap->alloc += 3;
+
+    free_space(); // reap failure, reap_increase update
+    CHECK(fd.flows == -1);
+    UNSIGNED_LONGS_EQUAL(1, mc.reap_decrease);
+    UNSIGNED_LONGS_EQUAL(2, mc.reap_increase);
+
+    fd.flows = 1;
+    heap->total = cap + 1;
+    periodic_check();
+
+    free_space();
+    CHECK(fd.flows == 0);
+
+    heap->alloc += 2;
+
+    free_space(); // reap failure, reap_increase update
+    CHECK(fd.flows == -1);
+    UNSIGNED_LONGS_EQUAL(1, mc.reap_decrease);
+    UNSIGNED_LONGS_EQUAL(3, mc.reap_increase);
+
+    UNSIGNED_LONGS_EQUAL(3, mc.reap_cycles);
+    UNSIGNED_LONGS_EQUAL(6, mc.reap_attempts);
+    UNSIGNED_LONGS_EQUAL(3, mc.reap_failures);
+
+    MemoryCap::stop();
+}
+
+TEST(memory, reap_freed_outside_of_pruning)
+{
+    const uint64_t cap = 100;
+    const uint64_t start = 50;
+    heap->total = start;
+
+    MemoryConfig config { cap, 100, 0, 2, true };
+    MemoryCap::start(config, pruner);
+    MemoryCap::thread_init();
+
+    fd.flows = 2;
     heap->total = cap + 1;
 
     periodic_check();
 
-    CHECK(flows == 1);
+    UNSIGNED_LONGS_EQUAL(2, fd.flows);
     free_space();
-    CHECK(flows == 0);
+    UNSIGNED_LONGS_EQUAL(1, fd.flows);
+
+    heap->alloc++;
+
+    free_space();
+    UNSIGNED_LONGS_EQUAL(0, fd.flows);
 
     heap->dealloc++;
+
     free_space();
-    CHECK(flows == -1);
+    UNSIGNED_LONGS_EQUAL(0, fd.flows);
 
     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);
+    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();
 }
index 15abe460f4c82a954767f239ce0a0eb104f569c7..0f12ff7e363d935ec29efdd107366489e4db01ad 100644 (file)
@@ -58,6 +58,7 @@ static std::mutex crash_dump_flow_control_mutex;
 static BaseStats g_stats;
 THREAD_LOCAL BaseStats stream_base_stats;
 THREAD_LOCAL PegCount current_flows_prev;
+THREAD_LOCAL PegCount current_free_flows_prev;
 THREAD_LOCAL PegCount uni_flows_prev;
 THREAD_LOCAL PegCount uni_ip_flows_prev;
 
@@ -87,12 +88,13 @@ const PegInfo base_pegs[] =
 
     // Keep the NOW stats at the bottom as it requires special sum_stats logic
     { CountType::NOW, "current_flows", "current number of flows in cache" },
+    { CountType::NOW, "current_free_flows", "current number of free flows in cache" },
     { CountType::NOW, "uni_flows", "number of uni flows in cache" },
     { CountType::NOW, "uni_ip_flows", "number of uni ip flows in cache" },
     { CountType::END, nullptr, nullptr }
 };
 
-#define NOW_PEGS_NUM 3
+#define NOW_PEGS_NUM 4
 
 // FIXIT-L dependency on stats define in another file
 void base_prep()
@@ -114,6 +116,7 @@ void base_prep()
     stream_base_stats.reload_blocked_flow_deletes= flow_con->get_deletes(FlowDeleteState::BLOCKED);
 
     stream_base_stats.current_flows = flow_con->get_num_flows();
+    stream_base_stats.current_free_flows = flow_con->get_num_free_flows();
     stream_base_stats.uni_flows = flow_con->get_uni_flows();
     stream_base_stats.uni_ip_flows = flow_con->get_uni_ip_flows();
 
@@ -134,6 +137,7 @@ void base_sum()
         array_size(base_pegs) - 1 - NOW_PEGS_NUM);
 
     g_stats.current_flows += (int64_t)stream_base_stats.current_flows - (int64_t)current_flows_prev;
+    g_stats.current_free_flows += (int64_t)stream_base_stats.current_free_flows - (int64_t)current_free_flows_prev;
     g_stats.uni_flows += (int64_t)stream_base_stats.uni_flows - (int64_t)uni_flows_prev;
     g_stats.uni_ip_flows += (int64_t)stream_base_stats.uni_ip_flows - (int64_t)uni_ip_flows_prev;
 
@@ -148,6 +152,7 @@ void base_stats()
 void base_reset(bool reset_all)
 {
     current_flows_prev = stream_base_stats.current_flows;
+    current_free_flows_prev = stream_base_stats.current_free_flows;
     uni_flows_prev = stream_base_stats.uni_flows;
     uni_ip_flows_prev = stream_base_stats.uni_ip_flows;
 
index 96a8f6e19c9113267da68d4345d2b69d522d98d8..58e73c5cc1d433b4b0f9c406d48d73dce066aa35 100644 (file)
@@ -77,6 +77,7 @@ struct BaseStats
 
      // Keep the NOW stats at the bottom as it requires special sum_stats logic
      PegCount current_flows;
+     PegCount current_free_flows;
      PegCount uni_flows;
      PegCount uni_ip_flows;