ccan: Correct some poor conventions in _info includes
[ccan] / ccan / likely / likely.c
1 /* CC0 (Public domain) - 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_type.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 struct trace {
9         const char *condstr;
10         const char *file;
11         unsigned int line;
12         bool expect;
13         unsigned long count, right;
14 };
15
16 static size_t hash_trace(const struct trace *trace)
17 {
18         return hash(trace->condstr, strlen(trace->condstr),
19                     hash(trace->file, strlen(trace->file),
20                          trace->line + trace->expect));
21 }
22
23 static bool trace_eq(const struct trace *t1, const struct trace *t2)
24 {
25         return t1->condstr == t2->condstr
26                 && t1->file == t2->file
27                 && t1->line == t2->line
28                 && t1->expect == t2->expect;
29 }
30
31 /* struct thash */
32 HTABLE_DEFINE_TYPE(struct trace, (const struct trace *), hash_trace, trace_eq,
33                    thash);
34
35 static struct thash htable
36 = { HTABLE_INITIALIZER(htable.raw, thash_hash, NULL) };
37
38 static void init_trace(struct trace *trace,
39                        const char *condstr, const char *file, unsigned int line,
40                        bool expect)
41 {
42         trace->condstr = condstr;
43         trace->file = file;
44         trace->line = line;
45         trace->expect = expect;
46         trace->count = trace->right = 0;
47 }
48
49 static struct trace *add_trace(const struct trace *t)
50 {
51         struct trace *trace = malloc(sizeof(*trace));
52         *trace = *t;
53         thash_add(&htable, trace);
54         return trace;
55 }
56
57 long _likely_trace(bool cond, bool expect,
58                    const char *condstr,
59                    const char *file, unsigned int line)
60 {
61         struct trace *p, trace;
62
63         init_trace(&trace, condstr, file, line, expect);
64         p = thash_get(&htable, &trace);
65         if (!p)
66                 p = add_trace(&trace);
67
68         p->count++;
69         if (cond == expect)
70                 p->right++;
71
72         return cond;
73 }
74
75 static double right_ratio(const struct trace *t)
76 {
77         return (double)t->right / t->count;
78 }
79
80 char *likely_stats(unsigned int min_hits, unsigned int percent)
81 {
82         struct trace *worst;
83         double worst_ratio;
84         struct thash_iter i;
85         char *ret;
86         struct trace *t;
87
88         worst = NULL;
89         worst_ratio = 2;
90
91         /* This is O(n), but it's not likely called that often. */
92         for (t = thash_first(&htable, &i); t; t = thash_next(&htable, &i)) {
93                 if (t->count >= min_hits) {
94                         if (right_ratio(t) < worst_ratio) {
95                                 worst = t;
96                                 worst_ratio = right_ratio(t);
97                         }
98                 }
99         }
100
101         if (worst_ratio * 100 > percent)
102                 return NULL;
103
104         ret = malloc(strlen(worst->condstr) +
105                      strlen(worst->file) +
106                      sizeof(long int) * 8 +
107                      sizeof("%s:%u:%slikely(%s) correct %u%% (%lu/%lu)"));
108         sprintf(ret, "%s:%u:%slikely(%s) correct %u%% (%lu/%lu)",
109                 worst->file, worst->line,
110                 worst->expect ? "" : "un", worst->condstr,
111                 (unsigned)(worst_ratio * 100),
112                 worst->right, worst->count);
113
114         thash_del(&htable, worst);
115         free(worst);
116
117         return ret;
118 }
119
120 void likely_stats_reset(void)
121 {
122         struct thash_iter i;
123         struct trace *t;
124
125         /* This is a bit better than O(n^2), but we have to loop since
126          * first/next during delete is unreliable. */
127         while ((t = thash_first(&htable, &i)) != NULL) {
128                 for (; t; t = thash_next(&htable, &i)) {
129                         thash_del(&htable, t);
130                         free(t);
131                 }
132         }
133
134         thash_clear(&htable);
135 }
136 #endif /*CCAN_LIKELY_DEBUG*/