op_alloc_counter.c revision 2b16b5ffd52ea5c0289e5ce794298bce5d941b2b
1/**
2 * @file op_alloc_counter.c
3 * hardware counter allocation
4 *
5 * You can have silliness here.
6 *
7 * @remark Copyright 2002 OProfile authors
8 * @remark Read the file COPYING
9 *
10 * @author John Levon
11 * @author Philippe Elie
12 */
13
14#include <stdlib.h>
15#include <ctype.h>
16#include <dirent.h>
17
18#include "op_events.h"
19#include "op_libiberty.h"
20
21
22typedef struct counter_arc_head {
23	/** the head of allowed counter for this event */
24	struct list_head next;
25} counter_arc_head;
26
27
28typedef struct counter_arc {
29	/** counter nr */
30	int counter;
31	/** the next counter allowed for this event */
32	struct list_head next;
33} counter_arc;
34
35
36/**
37 * @param pev  an array of event
38 * @param nr_events  number of entry in pev
39 *
40 * build an array of counter list allowed for each events
41 *  counter_arc_head[i] is the list of allowed counter for pev[i] events
42 * The returned pointer is an array of nr_events entry
43 */
44static counter_arc_head *
45build_counter_arc(struct op_event const * pev[], int nr_events)
46{
47	counter_arc_head * ctr_arc;
48	int i;
49
50	ctr_arc = xmalloc(nr_events * sizeof(*ctr_arc));
51
52	for (i = 0; i < nr_events; ++i) {
53		int j;
54		u32 mask = pev[i]->counter_mask;
55
56		list_init(&ctr_arc[i].next);
57		for (j = 0; mask; ++j) {
58			if (mask & (1 << j)) {
59				counter_arc * arc =
60					xmalloc(sizeof(counter_arc));
61				arc->counter = j;
62				/* we are looping by increasing counter number,
63				 * allocation use a left to right tree walking
64				 * so we add at end to ensure counter will
65				 * be allocated by increasing number: it's not
66				 * required but a bit less surprising when
67				 * debugging code
68				 */
69				list_add_tail(&arc->next, &ctr_arc[i].next);
70				mask &= ~(1 << j);
71			}
72		}
73	}
74
75	return ctr_arc;
76}
77
78
79/**
80 * @param ctr_arc  the array to deallocate
81 * @param nr_events  number of entry in array
82 *
83 *  deallocate all previously allocated resource by build_counter_arc()
84 */
85static void delete_counter_arc(counter_arc_head * ctr_arc, int nr_events)
86{
87	int i;
88	for (i = 0; i < nr_events; ++i) {
89		struct list_head * pos, * pos2;
90		list_for_each_safe(pos, pos2, &ctr_arc[i].next) {
91			counter_arc * arc = list_entry(pos, counter_arc, next);
92			list_del(&arc->next);
93			free(arc);
94		}
95	}
96	free(ctr_arc);
97}
98
99
100/**
101 * @param ctr_arc  tree description, ctr_arc[i] is the i-th level of tree.
102 * @param max_depth  number of entry in array ctr_arc == depth of tree
103 * @param depth  current level we are exploring
104 * @param allocated_mask  current counter already allocated mask
105 * @param counter_map  array of counter number mapping, returned results go
106 *   here
107 *
108 * return non zero on succees, in this case counter_map is set to the counter
109 * mapping number.
110 *
111 * Solution is searched through a simple backtracking exploring recursively all
112 * possible solution until one is found, prunning is done in O(1) by tracking
113 * a bitmask of already allocated counter. Walking through node is done in
114 * preorder left to right.
115 *
116 * Possible improvment if neccessary: partition counters in class of counter,
117 * two counter belong to the same class if they allow exactly the same set of
118 * event. Now using a variant of the backtrack algo can works on class of
119 * counter rather on counter (this is not an improvment if each counter goes
120 * in it's own class)
121 */
122static int
123allocate_counter(counter_arc_head const * ctr_arc, int max_depth, int depth,
124		 u32 allocated_mask, size_t * counter_map)
125{
126	struct list_head * pos;
127
128	if (depth == max_depth)
129		return 1;
130
131	list_for_each(pos, &ctr_arc[depth].next) {
132		counter_arc const * arc = list_entry(pos, counter_arc, next);
133
134		if (allocated_mask & (1 << arc->counter))
135			continue;
136
137		counter_map[depth] = arc->counter;
138
139		if (allocate_counter(ctr_arc, max_depth, depth + 1,
140		                     allocated_mask | (1 << arc->counter),
141		                     counter_map))
142			return 1;
143	}
144
145	return 0;
146}
147
148/* determine which directories are counter directories
149 */
150static int perfcounterdir(const struct dirent * entry)
151{
152	return (isdigit(entry->d_name[0]));
153}
154
155
156/**
157 * @param mask pointer where to place bit mask of unavailable counters
158 *
159 * return >= 0 number of counters that are available
160 *        < 0  could not determine number of counters
161 *
162 */
163static int op_get_counter_mask(u32 * mask)
164{
165	struct dirent **counterlist;
166	int count, i;
167	/* assume nothing is available */
168	u32 available=0;
169
170	count = scandir("/dev/oprofile", &counterlist, perfcounterdir, alphasort);
171	if (count < 0)
172		/* unable to determine bit mask */
173		return -1;
174	/* convert to bit map (0 where counter exists) */
175	for (i=0; i<count; ++i) {
176		available |= 1 << atoi(counterlist[i]->d_name);
177		free(counterlist[i]);
178	}
179	*mask=~available;
180	free(counterlist);
181	return count;
182}
183
184size_t * map_event_to_counter(struct op_event const * pev[], int nr_events,
185                              op_cpu cpu_type)
186{
187	counter_arc_head * ctr_arc;
188	size_t * counter_map;
189	int nr_counters;
190	u32 unavailable_counters = 0;
191
192	nr_counters = op_get_counter_mask(&unavailable_counters);
193	/* no counters then probably perfmon managing perfmon hw */
194	if (nr_counters <= 0) {
195		nr_counters = op_get_nr_counters(cpu_type);
196		unavailable_counters = (~0) << nr_counters;
197	}
198	if (nr_counters < nr_events)
199		return 0;
200
201	ctr_arc = build_counter_arc(pev, nr_events);
202
203	counter_map = xmalloc(nr_counters * sizeof(size_t));
204
205	if (!allocate_counter(ctr_arc, nr_events, 0, unavailable_counters,
206			      counter_map)) {
207		free(counter_map);
208		counter_map = 0;
209	}
210
211	delete_counter_arc(ctr_arc, nr_events);
212	return counter_map;
213}
214