summaryrefslogtreecommitdiff
path: root/lib/ccan/likely/likely.c
blob: 1114efc2077f81895b1cf535cc828fc2aac4c33f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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*/