]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
hashtable: Optionally collect and report profiling data
authorTobias Brunner <tobias@strongswan.org>
Fri, 24 Apr 2020 08:56:50 +0000 (10:56 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 20 Jul 2020 11:50:11 +0000 (13:50 +0200)
src/libstrongswan/collections/hashtable.c

index 28f2bef8d6a476fba5b9ceb9e7bd6bf4952cc3c3..c89bf65e1bc1c61c27ed42015520a93abfdab1f4 100644 (file)
 #include "hashtable.h"
 
 #include <utils/chunk.h>
+#include <utils/debug.h>
+#ifdef HASHTABLE_PROFILER
+#include <utils/backtrace.h>
+#endif
 
 /** The minimum size of the hash table (MUST be a power of 2) */
 #define MIN_SIZE 8
@@ -114,8 +118,64 @@ struct private_hashtable_t {
         * Alternative comparison function.
         */
        hashtable_cmp_t cmp;
+
+#ifdef HASHTABLE_PROFILER
+       /**
+        * Some stats to profile lookups in the table
+        */
+       struct {
+               size_t count;
+               size_t probes;
+               size_t longest;
+       } success, failure;
+
+       /**
+        * Stats on the memory usage of the table
+        */
+       struct {
+               size_t count;
+               size_t size;
+       } max;
+
+       /**
+        * Keep track of where the hash table was created
+        */
+       backtrace_t *backtrace;
+#endif
 };
 
+#ifdef HASHTABLE_PROFILER
+
+#define lookup_start() \
+       u_int _lookup_probes = 0;
+
+#define lookup_probing() \
+       _lookup_probes++;
+
+#define _lookup_done(table, result) \
+       table->result.count++; \
+       table->result.probes += _lookup_probes; \
+       table->result.longest = max(table->result.longest, _lookup_probes);
+
+#define lookup_success(table) _lookup_done(table, success);
+#define lookup_failure(table) _lookup_done(table, failure);
+
+#define profile_size(table) \
+       table->max.size = max(table->max.size, table->size);
+#define profile_count(table) \
+       table->max.count = max(table->max.count, table->count);
+
+#else
+
+#define lookup_start(...) {}
+#define lookup_probing(...) {}
+#define lookup_success(...) {}
+#define lookup_failure(...) {}
+#define profile_size(...) {}
+#define profile_count(...) {}
+
+#endif
+
 typedef struct private_enumerator_t private_enumerator_t;
 
 /**
@@ -212,6 +272,7 @@ static void init_hashtable(private_hashtable_t *this, u_int size)
        size = max(MIN_SIZE, min(size, MAX_SIZE));
        this->size = get_nearest_powerof2(size);
        this->mask = this->size - 1;
+       profile_size(this);
 
        this->table = calloc(this->size, sizeof(pair_t*));
 }
@@ -294,9 +355,12 @@ static inline pair_t *find_key(private_hashtable_t *this, const void *key,
                *out_hash = hash;
        }
 
+       lookup_start();
+
        pair = this->table[hash & this->mask];
        while (pair)
        {
+               lookup_probing();
                /* when keys are ordered, we compare all items so we can abort earlier
                 * even if the hash does not match, but only as long as we don't
                 * have a callback */
@@ -324,6 +388,14 @@ static inline pair_t *find_key(private_hashtable_t *this, const void *key,
        {
                *out_prev = prev;
        }
+       if (pair)
+       {
+               lookup_success(this);
+       }
+       else
+       {
+               lookup_failure(this);
+       }
        return pair;
 }
 
@@ -360,6 +432,7 @@ METHOD(hashtable_t, put, void*,
                        this->table[hash & this->mask] = pair;
                }
                this->count++;
+               profile_count(this);
        }
        return old_value;
 }
@@ -490,6 +563,23 @@ static void destroy_internal(private_hashtable_t *this,
        pair_t *pair, *next;
        u_int row;
 
+#ifdef HASHTABLE_PROFILER
+       if (this->success.count || this->failure.count)
+       {
+               fprintf(stderr, "%zu elements [max. %zu], %zu buckets [%zu], %zu "
+                               "successful / %zu failed lookups, %.4f [%zu] / %.4f "
+                               "[%zu] avg. probes in table created at:",
+                               this->count, this->max.count, this->size, this->max.size,
+                               this->success.count, this->failure.count,
+                               (double)this->success.probes/this->success.count,
+                               this->success.longest,
+                               (double)this->failure.probes/this->failure.count,
+                               this->failure.longest);
+               this->backtrace->log(this->backtrace, stderr, TRUE);
+       }
+       this->backtrace->destroy(this->backtrace);
+#endif
+
        for (row = 0; row < this->size; row++)
        {
                pair = this->table[row];
@@ -545,6 +635,10 @@ static private_hashtable_t *hashtable_create_internal(hashtable_hash_t hash,
 
        init_hashtable(this, size);
 
+#ifdef HASHTABLE_PROFILER
+       this->backtrace = backtrace_create(3);
+#endif
+
        return this;
 }