Arnaldo Carvalho de Melo | 5c58104 | 2011-01-11 22:30:02 -0200 | [diff] [blame] | 1 | #include <poll.h> |
Arnaldo Carvalho de Melo | 361c99a | 2011-01-11 20:56:53 -0200 | [diff] [blame] | 2 | #include "evlist.h" |
| 3 | #include "evsel.h" |
| 4 | #include "util.h" |
| 5 | |
Arnaldo Carvalho de Melo | 70db753 | 2011-01-12 22:39:13 -0200 | [diff] [blame] | 6 | #include <linux/bitops.h> |
| 7 | #include <linux/hash.h> |
| 8 | |
Arnaldo Carvalho de Melo | ef1d1af | 2011-01-18 21:41:45 -0200 | [diff] [blame^] | 9 | void perf_evlist__init(struct perf_evlist *evlist) |
| 10 | { |
| 11 | int i; |
| 12 | |
| 13 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) |
| 14 | INIT_HLIST_HEAD(&evlist->heads[i]); |
| 15 | INIT_LIST_HEAD(&evlist->entries); |
| 16 | } |
| 17 | |
Arnaldo Carvalho de Melo | 361c99a | 2011-01-11 20:56:53 -0200 | [diff] [blame] | 18 | struct perf_evlist *perf_evlist__new(void) |
| 19 | { |
| 20 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); |
| 21 | |
Arnaldo Carvalho de Melo | ef1d1af | 2011-01-18 21:41:45 -0200 | [diff] [blame^] | 22 | if (evlist != NULL) |
| 23 | perf_evlist__init(evlist); |
Arnaldo Carvalho de Melo | 361c99a | 2011-01-11 20:56:53 -0200 | [diff] [blame] | 24 | |
| 25 | return evlist; |
| 26 | } |
| 27 | |
| 28 | static void perf_evlist__purge(struct perf_evlist *evlist) |
| 29 | { |
| 30 | struct perf_evsel *pos, *n; |
| 31 | |
| 32 | list_for_each_entry_safe(pos, n, &evlist->entries, node) { |
| 33 | list_del_init(&pos->node); |
| 34 | perf_evsel__delete(pos); |
| 35 | } |
| 36 | |
| 37 | evlist->nr_entries = 0; |
| 38 | } |
| 39 | |
Arnaldo Carvalho de Melo | ef1d1af | 2011-01-18 21:41:45 -0200 | [diff] [blame^] | 40 | void perf_evlist__exit(struct perf_evlist *evlist) |
| 41 | { |
| 42 | free(evlist->mmap); |
| 43 | free(evlist->pollfd); |
| 44 | evlist->mmap = NULL; |
| 45 | evlist->pollfd = NULL; |
| 46 | } |
| 47 | |
Arnaldo Carvalho de Melo | 361c99a | 2011-01-11 20:56:53 -0200 | [diff] [blame] | 48 | void perf_evlist__delete(struct perf_evlist *evlist) |
| 49 | { |
| 50 | perf_evlist__purge(evlist); |
Arnaldo Carvalho de Melo | ef1d1af | 2011-01-18 21:41:45 -0200 | [diff] [blame^] | 51 | perf_evlist__exit(evlist); |
Arnaldo Carvalho de Melo | 361c99a | 2011-01-11 20:56:53 -0200 | [diff] [blame] | 52 | free(evlist); |
| 53 | } |
| 54 | |
| 55 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) |
| 56 | { |
| 57 | list_add_tail(&entry->node, &evlist->entries); |
| 58 | ++evlist->nr_entries; |
| 59 | } |
| 60 | |
| 61 | int perf_evlist__add_default(struct perf_evlist *evlist) |
| 62 | { |
| 63 | struct perf_event_attr attr = { |
| 64 | .type = PERF_TYPE_HARDWARE, |
| 65 | .config = PERF_COUNT_HW_CPU_CYCLES, |
| 66 | }; |
| 67 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); |
| 68 | |
| 69 | if (evsel == NULL) |
| 70 | return -ENOMEM; |
| 71 | |
| 72 | perf_evlist__add(evlist, evsel); |
| 73 | return 0; |
| 74 | } |
Arnaldo Carvalho de Melo | 5c58104 | 2011-01-11 22:30:02 -0200 | [diff] [blame] | 75 | |
| 76 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads) |
| 77 | { |
| 78 | int nfds = ncpus * nthreads * evlist->nr_entries; |
| 79 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); |
| 80 | return evlist->pollfd != NULL ? 0 : -ENOMEM; |
| 81 | } |
Arnaldo Carvalho de Melo | 70082dd | 2011-01-12 17:03:24 -0200 | [diff] [blame] | 82 | |
| 83 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) |
| 84 | { |
| 85 | fcntl(fd, F_SETFL, O_NONBLOCK); |
| 86 | evlist->pollfd[evlist->nr_fds].fd = fd; |
| 87 | evlist->pollfd[evlist->nr_fds].events = POLLIN; |
| 88 | evlist->nr_fds++; |
| 89 | } |
Arnaldo Carvalho de Melo | 70db753 | 2011-01-12 22:39:13 -0200 | [diff] [blame] | 90 | |
| 91 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) |
| 92 | { |
| 93 | struct hlist_head *head; |
| 94 | struct hlist_node *pos; |
| 95 | struct perf_sample_id *sid; |
| 96 | int hash; |
| 97 | |
| 98 | if (evlist->nr_entries == 1) |
| 99 | return list_entry(evlist->entries.next, struct perf_evsel, node); |
| 100 | |
| 101 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
| 102 | head = &evlist->heads[hash]; |
| 103 | |
| 104 | hlist_for_each_entry(sid, pos, head, node) |
| 105 | if (sid->id == id) |
| 106 | return sid->evsel; |
| 107 | return NULL; |
| 108 | } |
Arnaldo Carvalho de Melo | 04391de | 2011-01-15 10:40:59 -0200 | [diff] [blame] | 109 | |
| 110 | event_t *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu) |
| 111 | { |
| 112 | /* XXX Move this to perf.c, making it generally available */ |
| 113 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); |
| 114 | struct perf_mmap *md = &evlist->mmap[cpu]; |
| 115 | unsigned int head = perf_mmap__read_head(md); |
| 116 | unsigned int old = md->prev; |
| 117 | unsigned char *data = md->base + page_size; |
| 118 | event_t *event = NULL; |
| 119 | int diff; |
| 120 | |
| 121 | /* |
| 122 | * If we're further behind than half the buffer, there's a chance |
| 123 | * the writer will bite our tail and mess up the samples under us. |
| 124 | * |
| 125 | * If we somehow ended up ahead of the head, we got messed up. |
| 126 | * |
| 127 | * In either case, truncate and restart at head. |
| 128 | */ |
| 129 | diff = head - old; |
| 130 | if (diff > md->mask / 2 || diff < 0) { |
| 131 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); |
| 132 | |
| 133 | /* |
| 134 | * head points to a known good entry, start there. |
| 135 | */ |
| 136 | old = head; |
| 137 | } |
| 138 | |
| 139 | if (old != head) { |
| 140 | size_t size; |
| 141 | |
| 142 | event = (event_t *)&data[old & md->mask]; |
| 143 | size = event->header.size; |
| 144 | |
| 145 | /* |
| 146 | * Event straddles the mmap boundary -- header should always |
| 147 | * be inside due to u64 alignment of output. |
| 148 | */ |
| 149 | if ((old & md->mask) + size != ((old + size) & md->mask)) { |
| 150 | unsigned int offset = old; |
| 151 | unsigned int len = min(sizeof(*event), size), cpy; |
| 152 | void *dst = &evlist->event_copy; |
| 153 | |
| 154 | do { |
| 155 | cpy = min(md->mask + 1 - (offset & md->mask), len); |
| 156 | memcpy(dst, &data[offset & md->mask], cpy); |
| 157 | offset += cpy; |
| 158 | dst += cpy; |
| 159 | len -= cpy; |
| 160 | } while (len); |
| 161 | |
| 162 | event = &evlist->event_copy; |
| 163 | } |
| 164 | |
| 165 | old += size; |
| 166 | } |
| 167 | |
| 168 | md->prev = old; |
| 169 | return event; |
| 170 | } |