summaryrefslogtreecommitdiff
path: root/lib/ccan/likely/likely.c
blob: 8893d0b6d244b5af79831a66900375fcce48b324 (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
137
138
139
140
141
#ifdef CCAN_LIKELY_DEBUG
#include <ccan/likely/likely.h>
#include <ccan/hash/hash.h>
#include <ccan/htable/htable.h>
#include <stdlib.h>
#include <stdio.h>
static struct htable *htable;

struct trace {
	const char *condstr;
	const char *file;
	unsigned int line;
	bool expect;
	unsigned long count, right;
};

/* We hash the pointers, which will be identical for same call. */
static unsigned long hash_trace(const struct trace *trace)
{
	return hash_pointer(trace->condstr,
			    hash_pointer(trace->file,
					 trace->line + trace->expect));
}

static bool hash_cmp(const void *htelem, void *cmpdata)
{
	const struct trace *t1 = htelem, *t2 = cmpdata;
	return t1->condstr == t2->condstr
		&& t1->file == t2->file
		&& t1->line == t2->line
		&& t1->expect == t2->expect;
}

static size_t rehash(const void *elem, void *priv)
{
	return hash_trace(elem);
}

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 char *condstr,
			       const char *file, unsigned int line, bool expect)
{
	struct trace *trace = malloc(sizeof(*trace));
	init_trace(trace, condstr, file, line, expect);
	htable_add(htable, hash_trace(trace), trace);
	return trace;
}

long _likely_trace(bool cond, bool expect,
		   const char *condstr,
		   const char *file, unsigned int line)
{
	struct trace *p, trace;

	if (!htable)
		htable = htable_new(rehash, NULL);

	init_trace(&trace, condstr, file, line, expect);
	p = htable_get(htable, hash_trace(&trace), hash_cmp, &trace);
	if (!p)
		p = add_trace(condstr, file, line, expect);

	p->count++;
	if (cond == expect)
		p->right++;

	return cond;
}

struct get_stats_info {
	struct trace *worst;
	unsigned int min_hits;
	double worst_ratio;
};

static double right_ratio(const struct trace *t)
{
	return (double)t->right / t->count;
}

static void get_stats(struct trace *trace, struct get_stats_info *info)
{
	if (trace->count < info->min_hits)
		return;

	if (right_ratio(trace) < info->worst_ratio) {
		info->worst = trace;
		info->worst_ratio = right_ratio(trace);
	}
}

const char *likely_stats(unsigned int min_hits, unsigned int percent)
{
	struct get_stats_info info;
	struct htable_iter i;
	char *ret;
	struct trace *trace;

	if (!htable)
		return NULL;

	info.min_hits = min_hits;
	info.worst = NULL;
	info.worst_ratio = 2;

	/* This is O(n), but it's not likely called that often. */
	for (trace = htable_first(htable, &i);
	     trace;
	     trace = htable_next(htable,&i)) {
		get_stats(trace, &info);
	}

	if (info.worst_ratio * 100 > percent)
		return NULL;

	ret = malloc(strlen(info.worst->condstr) +
		     strlen(info.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)",
		info.worst->file, info.worst->line,
		info.worst->expect ? "" : "un", info.worst->condstr,
		(unsigned)(info.worst_ratio * 100),
		info.worst->right, info.worst->count);

	htable_del(htable, hash_trace(info.worst), info.worst);
	free(info.worst);

	return ret;
}
#endif /*CCAN_LIKELY_DEBUG*/