various: add LICENSE comments.
[ccan] / ccan / likely / likely.c
1 /* Licensed under LGPLv2.1+ - see LICENSE file for details */
2 #ifdef CCAN_LIKELY_DEBUG
3 #include <ccan/likely/likely.h>
4 #include <ccan/hash/hash.h>
5 #include <ccan/htable/htable.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 static struct htable *htable;
9
10 struct trace {
11         const char *condstr;
12         const char *file;
13         unsigned int line;
14         bool expect;
15         unsigned long count, right;
16 };
17
18 /* We hash the pointers, which will be identical for same call. */
19 static unsigned long hash_trace(const struct trace *trace)
20 {
21         return hash_pointer(trace->condstr,
22                             hash_pointer(trace->file,
23                                          trace->line + trace->expect));
24 }
25
26 static bool hash_cmp(const void *htelem, void *cmpdata)
27 {
28         const struct trace *t1 = htelem, *t2 = cmpdata;
29         return t1->condstr == t2->condstr
30                 && t1->file == t2->file
31                 && t1->line == t2->line
32                 && t1->expect == t2->expect;
33 }
34
35 static size_t rehash(const void *elem, void *priv)
36 {
37         return hash_trace(elem);
38 }
39
40 static void init_trace(struct trace *trace,
41                        const char *condstr, const char *file, unsigned int line,
42                        bool expect)
43 {
44         trace->condstr = condstr;
45         trace->file = file;
46         trace->line = line;
47         trace->expect = expect;
48         trace->count = trace->right = 0;
49 }
50
51 static struct trace *add_trace(const char *condstr,
52                                const char *file, unsigned int line, bool expect)
53 {
54         struct trace *trace = malloc(sizeof(*trace));
55         init_trace(trace, condstr, file, line, expect);
56         htable_add(htable, hash_trace(trace), trace);
57         return trace;
58 }
59
60 long _likely_trace(bool cond, bool expect,
61                    const char *condstr,
62                    const char *file, unsigned int line)
63 {
64         struct trace *p, trace;
65
66         if (!htable)
67                 htable = htable_new(rehash, NULL);
68
69         init_trace(&trace, condstr, file, line, expect);
70         p = htable_get(htable, hash_trace(&trace), hash_cmp, &trace);
71         if (!p)
72                 p = add_trace(condstr, file, line, expect);
73
74         p->count++;
75         if (cond == expect)
76                 p->right++;
77
78         return cond;
79 }
80
81 struct get_stats_info {
82         struct trace *worst;
83         unsigned int min_hits;
84         double worst_ratio;
85 };
86
87 static double right_ratio(const struct trace *t)
88 {
89         return (double)t->right / t->count;
90 }
91
92 static void get_stats(struct trace *trace, struct get_stats_info *info)
93 {
94         if (trace->count < info->min_hits)
95                 return;
96
97         if (right_ratio(trace) < info->worst_ratio) {
98                 info->worst = trace;
99                 info->worst_ratio = right_ratio(trace);
100         }
101 }
102
103 const char *likely_stats(unsigned int min_hits, unsigned int percent)
104 {
105         struct get_stats_info info;
106         struct htable_iter i;
107         char *ret;
108         struct trace *trace;
109
110         if (!htable)
111                 return NULL;
112
113         info.min_hits = min_hits;
114         info.worst = NULL;
115         info.worst_ratio = 2;
116
117         /* This is O(n), but it's not likely called that often. */
118         for (trace = htable_first(htable, &i);
119              trace;
120              trace = htable_next(htable,&i)) {
121                 get_stats(trace, &info);
122         }
123
124         if (info.worst_ratio * 100 > percent)
125                 return NULL;
126
127         ret = malloc(strlen(info.worst->condstr) +
128                      strlen(info.worst->file) +
129                      sizeof(long int) * 8 +
130                      sizeof("%s:%u:%slikely(%s) correct %u%% (%lu/%lu)"));
131         sprintf(ret, "%s:%u:%slikely(%s) correct %u%% (%lu/%lu)",
132                 info.worst->file, info.worst->line,
133                 info.worst->expect ? "" : "un", info.worst->condstr,
134                 (unsigned)(info.worst_ratio * 100),
135                 info.worst->right, info.worst->count);
136
137         htable_del(htable, hash_trace(info.worst), info.worst);
138         free(info.worst);
139
140         return ret;
141 }
142 #endif /*CCAN_LIKELY_DEBUG*/