Merge tag 'upstream/4.0.5+dfsg1' into samba_4.0_ivo
[abartlet/samba-debian.git] / lib / ccan / likely / likely.c
diff --git a/lib/ccan/likely/likely.c b/lib/ccan/likely/likely.c
new file mode 100644 (file)
index 0000000..1114efc
--- /dev/null
@@ -0,0 +1,136 @@
+/* Licensed under LGPLv2.1+ - see LICENSE file for details */
+#ifdef CCAN_LIKELY_DEBUG
+#include <ccan/likely/likely.h>
+#include <ccan/hash/hash.h>
+#include <ccan/htable/htable_type.h>
+#include <stdlib.h>
+#include <stdio.h>
+struct trace {
+       const char *condstr;
+       const char *file;
+       unsigned int line;
+       bool expect;
+       unsigned long count, right;
+};
+
+static size_t hash_trace(const struct trace *trace)
+{
+       return hash(trace->condstr, strlen(trace->condstr),
+                   hash(trace->file, strlen(trace->file),
+                        trace->line + trace->expect));
+}
+
+static bool trace_eq(const struct trace *t1, const struct trace *t2)
+{
+       return t1->condstr == t2->condstr
+               && t1->file == t2->file
+               && t1->line == t2->line
+               && t1->expect == t2->expect;
+}
+
+/* struct thash */
+HTABLE_DEFINE_TYPE(struct trace, (const struct trace *), hash_trace, trace_eq,
+                  thash);
+
+static struct thash htable
+= { HTABLE_INITIALIZER(htable.raw, thash_hash, NULL) };
+
+static void init_trace(struct trace *trace,
+                      const char *condstr, const char *file, unsigned int line,
+                      bool expect)
+{
+       trace->condstr = condstr;
+       trace->file = file;
+       trace->line = line;
+       trace->expect = expect;
+       trace->count = trace->right = 0;
+}
+
+static struct trace *add_trace(const struct trace *t)
+{
+       struct trace *trace = malloc(sizeof(*trace));
+       *trace = *t;
+       thash_add(&htable, trace);
+       return trace;
+}
+
+long _likely_trace(bool cond, bool expect,
+                  const char *condstr,
+                  const char *file, unsigned int line)
+{
+       struct trace *p, trace;
+
+       init_trace(&trace, condstr, file, line, expect);
+       p = thash_get(&htable, &trace);
+       if (!p)
+               p = add_trace(&trace);
+
+       p->count++;
+       if (cond == expect)
+               p->right++;
+
+       return cond;
+}
+
+static double right_ratio(const struct trace *t)
+{
+       return (double)t->right / t->count;
+}
+
+char *likely_stats(unsigned int min_hits, unsigned int percent)
+{
+       struct trace *worst;
+       double worst_ratio;
+       struct thash_iter i;
+       char *ret;
+       struct trace *t;
+
+       worst = NULL;
+       worst_ratio = 2;
+
+       /* This is O(n), but it's not likely called that often. */
+       for (t = thash_first(&htable, &i); t; t = thash_next(&htable, &i)) {
+               if (t->count >= min_hits) {
+                       if (right_ratio(t) < worst_ratio) {
+                               worst = t;
+                               worst_ratio = right_ratio(t);
+                       }
+               }
+       }
+
+       if (worst_ratio * 100 > percent)
+               return NULL;
+
+       ret = malloc(strlen(worst->condstr) +
+                    strlen(worst->file) +
+                    sizeof(long int) * 8 +
+                    sizeof("%s:%u:%slikely(%s) correct %u%% (%lu/%lu)"));
+       sprintf(ret, "%s:%u:%slikely(%s) correct %u%% (%lu/%lu)",
+               worst->file, worst->line,
+               worst->expect ? "" : "un", worst->condstr,
+               (unsigned)(worst_ratio * 100),
+               worst->right, worst->count);
+
+       thash_del(&htable, worst);
+       free(worst);
+
+       return ret;
+}
+
+void likely_stats_reset(void)
+{
+       struct thash_iter i;
+       struct trace *t;
+
+       /* This is a bit better than O(n^2), but we have to loop since
+        * first/next during delete is unreliable. */
+       while ((t = thash_first(&htable, &i)) != NULL) {
+               for (; t; t = thash_next(&htable, &i)) {
+                       thash_del(&htable, t);
+                       free(t);
+               }
+       }
+
+       thash_clear(&htable);
+}
+#endif /*CCAN_LIKELY_DEBUG*/