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 22 typedef struct counter_arc_head { 23 /** the head of allowed counter for this event */ 24 struct list_head next; 25 } counter_arc_head; 26 27 28 typedef 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 */ 44 static counter_arc_head * 45 build_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 */ 85 static 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 * In case of extended events (required no phisical counters), the associated 117 * counter_map entry will be -1. 118 * 119 * Possible improvment if neccessary: partition counters in class of counter, 120 * two counter belong to the same class if they allow exactly the same set of 121 * event. Now using a variant of the backtrack algo can works on class of 122 * counter rather on counter (this is not an improvment if each counter goes 123 * in it's own class) 124 */ 125 static int 126 allocate_counter(counter_arc_head const * ctr_arc, int max_depth, int depth, 127 u32 allocated_mask, size_t * counter_map) 128 { 129 struct list_head * pos; 130 131 if (depth == max_depth) 132 return 1; 133 134 /* If ctr_arc is not available, counter_map is -1 */ 135 if((&ctr_arc[depth].next)->next == &ctr_arc[depth].next) { 136 counter_map[depth] = -1; 137 if (allocate_counter(ctr_arc, max_depth, depth + 1, 138 allocated_mask, 139 counter_map)) 140 return 1; 141 } else { 142 list_for_each(pos, &ctr_arc[depth].next) { 143 counter_arc const * arc = list_entry(pos, counter_arc, next); 144 145 if (allocated_mask & (1 << arc->counter)) 146 continue; 147 148 counter_map[depth] = arc->counter; 149 150 if (allocate_counter(ctr_arc, max_depth, depth + 1, 151 allocated_mask | (1 << arc->counter), 152 counter_map)) 153 return 1; 154 } 155 } 156 157 return 0; 158 } 159 160 /* determine which directories are counter directories 161 */ 162 static int perfcounterdir(const struct dirent * entry) 163 { 164 return (isdigit(entry->d_name[0])); 165 } 166 167 168 /** 169 * @param mask pointer where to place bit mask of unavailable counters 170 * 171 * return >= 0 number of counters that are available 172 * < 0 could not determine number of counters 173 * 174 */ 175 static int op_get_counter_mask(u32 * mask) 176 { 177 struct dirent **counterlist; 178 int count, i; 179 /* assume nothing is available */ 180 u32 available=0; 181 182 count = scandir("/dev/oprofile", &counterlist, perfcounterdir, 183 alphasort); 184 if (count < 0) 185 /* unable to determine bit mask */ 186 return -1; 187 /* convert to bit map (0 where counter exists) */ 188 for (i=0; i<count; ++i) { 189 available |= 1 << atoi(counterlist[i]->d_name); 190 free(counterlist[i]); 191 } 192 *mask=~available; 193 free(counterlist); 194 return count; 195 } 196 197 size_t * map_event_to_counter(struct op_event const * pev[], int nr_events, 198 op_cpu cpu_type) 199 { 200 counter_arc_head * ctr_arc; 201 size_t * counter_map; 202 int i, nr_counters, nr_pmc_events; 203 op_cpu curr_cpu_type; 204 u32 unavailable_counters = 0; 205 206 /* Either ophelp or one of the libop tests may invoke this 207 * function with a non-native cpu_type. If so, we should not 208 * call op_get_counter_mask because that will look for real counter 209 * information in oprofilefs. 210 */ 211 curr_cpu_type = op_get_cpu_type(); 212 if (cpu_type != curr_cpu_type) 213 nr_counters = op_get_nr_counters(cpu_type); 214 else 215 nr_counters = op_get_counter_mask(&unavailable_counters); 216 217 /* no counters then probably perfmon managing perfmon hw */ 218 if (nr_counters <= 0) { 219 nr_counters = op_get_nr_counters(cpu_type); 220 unavailable_counters = (~0) << nr_counters; 221 } 222 223 /* Check to see if we have enough physical counters to map events*/ 224 for (i = 0, nr_pmc_events = 0; i < nr_events; i++) 225 if(pev[i]->ext == NULL) 226 if (++nr_pmc_events > nr_counters) 227 return 0; 228 229 ctr_arc = build_counter_arc(pev, nr_events); 230 231 counter_map = xmalloc(nr_events * sizeof(size_t)); 232 233 if (!allocate_counter(ctr_arc, nr_events, 0, unavailable_counters, 234 counter_map)) { 235 free(counter_map); 236 counter_map = 0; 237 } 238 239 delete_counter_arc(ctr_arc, nr_events); 240 return counter_map; 241 } 242