| Xiao Guangrong | b8f46c5 | 2010-02-03 11:53:14 +0800 | [diff] [blame] | 1 | #define _FILE_OFFSET_BITS 64 | 
|  | 2 |  | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 3 | #include <linux/kernel.h> | 
|  | 4 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 5 | #include <byteswap.h> | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 6 | #include <unistd.h> | 
|  | 7 | #include <sys/types.h> | 
| Arnaldo Carvalho de Melo | a41794c | 2010-05-18 18:29:23 -0300 | [diff] [blame] | 8 | #include <sys/mman.h> | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 9 |  | 
|  | 10 | #include "session.h" | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 11 | #include "sort.h" | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 12 | #include "util.h" | 
|  | 13 |  | 
|  | 14 | static int perf_session__open(struct perf_session *self, bool force) | 
|  | 15 | { | 
|  | 16 | struct stat input_stat; | 
|  | 17 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 18 | if (!strcmp(self->filename, "-")) { | 
|  | 19 | self->fd_pipe = true; | 
|  | 20 | self->fd = STDIN_FILENO; | 
|  | 21 |  | 
|  | 22 | if (perf_header__read(self, self->fd) < 0) | 
|  | 23 | pr_err("incompatible file format"); | 
|  | 24 |  | 
|  | 25 | return 0; | 
|  | 26 | } | 
|  | 27 |  | 
| Xiao Guangrong | f887f30 | 2010-02-04 16:46:42 +0800 | [diff] [blame] | 28 | self->fd = open(self->filename, O_RDONLY); | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 29 | if (self->fd < 0) { | 
| Andy Isaacson | 0f2c3de | 2010-06-11 20:36:15 -0700 | [diff] [blame] | 30 | int err = errno; | 
|  | 31 |  | 
|  | 32 | pr_err("failed to open %s: %s", self->filename, strerror(err)); | 
|  | 33 | if (err == ENOENT && !strcmp(self->filename, "perf.data")) | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 34 | pr_err("  (try 'perf record' first)"); | 
|  | 35 | pr_err("\n"); | 
|  | 36 | return -errno; | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | if (fstat(self->fd, &input_stat) < 0) | 
|  | 40 | goto out_close; | 
|  | 41 |  | 
|  | 42 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | 
|  | 43 | pr_err("file %s not owned by current user or root\n", | 
|  | 44 | self->filename); | 
|  | 45 | goto out_close; | 
|  | 46 | } | 
|  | 47 |  | 
|  | 48 | if (!input_stat.st_size) { | 
|  | 49 | pr_info("zero-sized file (%s), nothing to do!\n", | 
|  | 50 | self->filename); | 
|  | 51 | goto out_close; | 
|  | 52 | } | 
|  | 53 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 54 | if (perf_header__read(self, self->fd) < 0) { | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 55 | pr_err("incompatible file format"); | 
|  | 56 | goto out_close; | 
|  | 57 | } | 
|  | 58 |  | 
|  | 59 | self->size = input_stat.st_size; | 
|  | 60 | return 0; | 
|  | 61 |  | 
|  | 62 | out_close: | 
|  | 63 | close(self->fd); | 
|  | 64 | self->fd = -1; | 
|  | 65 | return -1; | 
|  | 66 | } | 
|  | 67 |  | 
| Arnaldo Carvalho de Melo | 9c90a61 | 2010-12-02 10:25:28 -0200 | [diff] [blame] | 68 | static void perf_session__id_header_size(struct perf_session *session) | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 69 | { | 
| Arnaldo Carvalho de Melo | 9c90a61 | 2010-12-02 10:25:28 -0200 | [diff] [blame] | 70 | struct sample_data *data; | 
|  | 71 | u64 sample_type = session->sample_type; | 
|  | 72 | u16 size = 0; | 
|  | 73 |  | 
|  | 74 | if (!session->sample_id_all) | 
|  | 75 | goto out; | 
|  | 76 |  | 
|  | 77 | if (sample_type & PERF_SAMPLE_TID) | 
|  | 78 | size += sizeof(data->tid) * 2; | 
|  | 79 |  | 
|  | 80 | if (sample_type & PERF_SAMPLE_TIME) | 
|  | 81 | size += sizeof(data->time); | 
|  | 82 |  | 
|  | 83 | if (sample_type & PERF_SAMPLE_ID) | 
|  | 84 | size += sizeof(data->id); | 
|  | 85 |  | 
|  | 86 | if (sample_type & PERF_SAMPLE_STREAM_ID) | 
|  | 87 | size += sizeof(data->stream_id); | 
|  | 88 |  | 
|  | 89 | if (sample_type & PERF_SAMPLE_CPU) | 
|  | 90 | size += sizeof(data->cpu) * 2; | 
|  | 91 | out: | 
|  | 92 | session->id_hdr_size = size; | 
|  | 93 | } | 
|  | 94 |  | 
|  | 95 | void perf_session__set_sample_id_all(struct perf_session *session, bool value) | 
|  | 96 | { | 
|  | 97 | session->sample_id_all = value; | 
|  | 98 | perf_session__id_header_size(session); | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 99 | } | 
|  | 100 |  | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 101 | void perf_session__set_sample_type(struct perf_session *session, u64 type) | 
|  | 102 | { | 
|  | 103 | session->sample_type = type; | 
|  | 104 | } | 
|  | 105 |  | 
| Arnaldo Carvalho de Melo | 9c90a61 | 2010-12-02 10:25:28 -0200 | [diff] [blame] | 106 | void perf_session__update_sample_type(struct perf_session *self) | 
|  | 107 | { | 
|  | 108 | self->sample_type = perf_header__sample_type(&self->header); | 
|  | 109 | self->sample_id_all = perf_header__sample_id_all(&self->header); | 
|  | 110 | perf_session__id_header_size(self); | 
|  | 111 | } | 
|  | 112 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 113 | int perf_session__create_kernel_maps(struct perf_session *self) | 
|  | 114 | { | 
| Arnaldo Carvalho de Melo | d118f8b | 2010-05-10 12:51:05 -0300 | [diff] [blame] | 115 | int ret = machine__create_kernel_maps(&self->host_machine); | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 116 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 117 | if (ret >= 0) | 
| Arnaldo Carvalho de Melo | d118f8b | 2010-05-10 12:51:05 -0300 | [diff] [blame] | 118 | ret = machines__create_guest_kernel_maps(&self->machines); | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 119 | return ret; | 
|  | 120 | } | 
|  | 121 |  | 
| Arnaldo Carvalho de Melo | 076c6e45 | 2010-08-02 18:18:28 -0300 | [diff] [blame] | 122 | static void perf_session__destroy_kernel_maps(struct perf_session *self) | 
|  | 123 | { | 
|  | 124 | machine__destroy_kernel_maps(&self->host_machine); | 
|  | 125 | machines__destroy_guest_kernel_maps(&self->machines); | 
|  | 126 | } | 
|  | 127 |  | 
| Ian Munsie | 21ef97f | 2010-12-10 14:09:16 +1100 | [diff] [blame] | 128 | struct perf_session *perf_session__new(const char *filename, int mode, | 
|  | 129 | bool force, bool repipe, | 
|  | 130 | struct perf_event_ops *ops) | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 131 | { | 
| Arnaldo Carvalho de Melo | b3165f4 | 2009-12-13 19:50:28 -0200 | [diff] [blame] | 132 | size_t len = filename ? strlen(filename) + 1 : 0; | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 133 | struct perf_session *self = zalloc(sizeof(*self) + len); | 
|  | 134 |  | 
|  | 135 | if (self == NULL) | 
|  | 136 | goto out; | 
|  | 137 |  | 
|  | 138 | if (perf_header__init(&self->header) < 0) | 
| Arnaldo Carvalho de Melo | 4aa6563 | 2009-12-13 19:50:29 -0200 | [diff] [blame] | 139 | goto out_free; | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 140 |  | 
|  | 141 | memcpy(self->filename, filename, len); | 
| Arnaldo Carvalho de Melo | b3165f4 | 2009-12-13 19:50:28 -0200 | [diff] [blame] | 142 | self->threads = RB_ROOT; | 
| Arnaldo Carvalho de Melo | 720a3ae | 2010-06-17 08:37:44 -0300 | [diff] [blame] | 143 | INIT_LIST_HEAD(&self->dead_threads); | 
| Arnaldo Carvalho de Melo | 1c02c4d | 2010-05-10 13:04:11 -0300 | [diff] [blame] | 144 | self->hists_tree = RB_ROOT; | 
| Arnaldo Carvalho de Melo | b3165f4 | 2009-12-13 19:50:28 -0200 | [diff] [blame] | 145 | self->last_match = NULL; | 
| Thomas Gleixner | 55b4462 | 2010-11-30 17:49:46 +0000 | [diff] [blame] | 146 | /* | 
|  | 147 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | 
|  | 148 | * slices. On 32bit we use 32MB. | 
|  | 149 | */ | 
|  | 150 | #if BITS_PER_LONG == 64 | 
|  | 151 | self->mmap_window = ULLONG_MAX; | 
|  | 152 | #else | 
|  | 153 | self->mmap_window = 32 * 1024 * 1024ULL; | 
|  | 154 | #endif | 
| Arnaldo Carvalho de Melo | 23346f2 | 2010-04-27 21:17:50 -0300 | [diff] [blame] | 155 | self->machines = RB_ROOT; | 
| Tom Zanussi | 454c407 | 2010-05-01 01:41:20 -0500 | [diff] [blame] | 156 | self->repipe = repipe; | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 157 | INIT_LIST_HEAD(&self->ordered_samples.samples); | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 158 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 
| Thomas Gleixner | 5c891f3 | 2010-11-30 17:49:55 +0000 | [diff] [blame] | 159 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 
| Arnaldo Carvalho de Melo | 1f626bc | 2010-05-09 19:57:08 -0300 | [diff] [blame] | 160 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 161 |  | 
| Arnaldo Carvalho de Melo | 64abebf | 2010-01-27 21:05:52 -0200 | [diff] [blame] | 162 | if (mode == O_RDONLY) { | 
|  | 163 | if (perf_session__open(self, force) < 0) | 
|  | 164 | goto out_delete; | 
|  | 165 | } else if (mode == O_WRONLY) { | 
|  | 166 | /* | 
|  | 167 | * In O_RDONLY mode this will be performed when reading the | 
|  | 168 | * kernel MMAP event, in event__process_mmap(). | 
|  | 169 | */ | 
|  | 170 | if (perf_session__create_kernel_maps(self) < 0) | 
|  | 171 | goto out_delete; | 
|  | 172 | } | 
| Arnaldo Carvalho de Melo | d549c76 | 2009-12-27 21:37:02 -0200 | [diff] [blame] | 173 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 174 | perf_session__update_sample_type(self); | 
| Ian Munsie | 21ef97f | 2010-12-10 14:09:16 +1100 | [diff] [blame] | 175 |  | 
|  | 176 | if (ops && ops->ordering_requires_timestamps && | 
|  | 177 | ops->ordered_samples && !self->sample_id_all) { | 
|  | 178 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | 
|  | 179 | ops->ordered_samples = false; | 
|  | 180 | } | 
|  | 181 |  | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 182 | out: | 
|  | 183 | return self; | 
| Arnaldo Carvalho de Melo | 4aa6563 | 2009-12-13 19:50:29 -0200 | [diff] [blame] | 184 | out_free: | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 185 | free(self); | 
|  | 186 | return NULL; | 
| Arnaldo Carvalho de Melo | 4aa6563 | 2009-12-13 19:50:29 -0200 | [diff] [blame] | 187 | out_delete: | 
|  | 188 | perf_session__delete(self); | 
|  | 189 | return NULL; | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 190 | } | 
|  | 191 |  | 
| Arnaldo Carvalho de Melo | d65a458 | 2010-07-30 18:31:28 -0300 | [diff] [blame] | 192 | static void perf_session__delete_dead_threads(struct perf_session *self) | 
|  | 193 | { | 
|  | 194 | struct thread *n, *t; | 
|  | 195 |  | 
|  | 196 | list_for_each_entry_safe(t, n, &self->dead_threads, node) { | 
|  | 197 | list_del(&t->node); | 
|  | 198 | thread__delete(t); | 
|  | 199 | } | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | static void perf_session__delete_threads(struct perf_session *self) | 
|  | 203 | { | 
|  | 204 | struct rb_node *nd = rb_first(&self->threads); | 
|  | 205 |  | 
|  | 206 | while (nd) { | 
|  | 207 | struct thread *t = rb_entry(nd, struct thread, rb_node); | 
|  | 208 |  | 
|  | 209 | rb_erase(&t->rb_node, &self->threads); | 
|  | 210 | nd = rb_next(nd); | 
|  | 211 | thread__delete(t); | 
|  | 212 | } | 
|  | 213 | } | 
|  | 214 |  | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 215 | void perf_session__delete(struct perf_session *self) | 
|  | 216 | { | 
|  | 217 | perf_header__exit(&self->header); | 
| Arnaldo Carvalho de Melo | 076c6e45 | 2010-08-02 18:18:28 -0300 | [diff] [blame] | 218 | perf_session__destroy_kernel_maps(self); | 
| Arnaldo Carvalho de Melo | d65a458 | 2010-07-30 18:31:28 -0300 | [diff] [blame] | 219 | perf_session__delete_dead_threads(self); | 
|  | 220 | perf_session__delete_threads(self); | 
|  | 221 | machine__exit(&self->host_machine); | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 222 | close(self->fd); | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 223 | free(self); | 
|  | 224 | } | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 225 |  | 
| Arnaldo Carvalho de Melo | 720a3ae | 2010-06-17 08:37:44 -0300 | [diff] [blame] | 226 | void perf_session__remove_thread(struct perf_session *self, struct thread *th) | 
|  | 227 | { | 
| Arnaldo Carvalho de Melo | 70597f2 | 2010-08-02 18:59:28 -0300 | [diff] [blame] | 228 | self->last_match = NULL; | 
| Arnaldo Carvalho de Melo | 720a3ae | 2010-06-17 08:37:44 -0300 | [diff] [blame] | 229 | rb_erase(&th->rb_node, &self->threads); | 
|  | 230 | /* | 
|  | 231 | * We may have references to this thread, for instance in some hist_entry | 
|  | 232 | * instances, so just move them to a separate list. | 
|  | 233 | */ | 
|  | 234 | list_add_tail(&th->node, &self->dead_threads); | 
|  | 235 | } | 
|  | 236 |  | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 237 | static bool symbol__match_parent_regex(struct symbol *sym) | 
|  | 238 | { | 
|  | 239 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | 
|  | 240 | return 1; | 
|  | 241 |  | 
|  | 242 | return 0; | 
|  | 243 | } | 
|  | 244 |  | 
| Arnaldo Carvalho de Melo | b3c9ac0 | 2010-03-24 16:40:18 -0300 | [diff] [blame] | 245 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 
|  | 246 | struct thread *thread, | 
|  | 247 | struct ip_callchain *chain, | 
|  | 248 | struct symbol **parent) | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 249 | { | 
|  | 250 | u8 cpumode = PERF_RECORD_MISC_USER; | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 251 | unsigned int i; | 
| Arnaldo Carvalho de Melo | ad5b217 | 2010-04-02 10:04:18 -0300 | [diff] [blame] | 252 | struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 253 |  | 
| Arnaldo Carvalho de Melo | ad5b217 | 2010-04-02 10:04:18 -0300 | [diff] [blame] | 254 | if (!syms) | 
|  | 255 | return NULL; | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 256 |  | 
|  | 257 | for (i = 0; i < chain->nr; i++) { | 
|  | 258 | u64 ip = chain->ips[i]; | 
|  | 259 | struct addr_location al; | 
|  | 260 |  | 
|  | 261 | if (ip >= PERF_CONTEXT_MAX) { | 
|  | 262 | switch (ip) { | 
|  | 263 | case PERF_CONTEXT_HV: | 
|  | 264 | cpumode = PERF_RECORD_MISC_HYPERVISOR;	break; | 
|  | 265 | case PERF_CONTEXT_KERNEL: | 
|  | 266 | cpumode = PERF_RECORD_MISC_KERNEL;	break; | 
|  | 267 | case PERF_CONTEXT_USER: | 
|  | 268 | cpumode = PERF_RECORD_MISC_USER;	break; | 
|  | 269 | default: | 
|  | 270 | break; | 
|  | 271 | } | 
|  | 272 | continue; | 
|  | 273 | } | 
|  | 274 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 275 | al.filtered = false; | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 276 | thread__find_addr_location(thread, self, cpumode, | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 277 | MAP__FUNCTION, thread->pid, ip, &al, NULL); | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 278 | if (al.sym != NULL) { | 
|  | 279 | if (sort__has_parent && !*parent && | 
|  | 280 | symbol__match_parent_regex(al.sym)) | 
|  | 281 | *parent = al.sym; | 
| Arnaldo Carvalho de Melo | d599db3 | 2009-12-15 20:04:42 -0200 | [diff] [blame] | 282 | if (!symbol_conf.use_callchain) | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 283 | break; | 
| Arnaldo Carvalho de Melo | b3c9ac0 | 2010-03-24 16:40:18 -0300 | [diff] [blame] | 284 | syms[i].map = al.map; | 
|  | 285 | syms[i].sym = al.sym; | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 286 | } | 
|  | 287 | } | 
|  | 288 |  | 
|  | 289 | return syms; | 
|  | 290 | } | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 291 |  | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 292 | static int process_event_synth_stub(event_t *event __used, | 
|  | 293 | struct perf_session *session __used) | 
|  | 294 | { | 
|  | 295 | dump_printf(": unhandled!\n"); | 
|  | 296 | return 0; | 
|  | 297 | } | 
|  | 298 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 299 | static int process_event_stub(event_t *event __used, | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 300 | struct sample_data *sample __used, | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 301 | struct perf_session *session __used) | 
|  | 302 | { | 
|  | 303 | dump_printf(": unhandled!\n"); | 
|  | 304 | return 0; | 
|  | 305 | } | 
|  | 306 |  | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 307 | static int process_finished_round_stub(event_t *event __used, | 
|  | 308 | struct perf_session *session __used, | 
|  | 309 | struct perf_event_ops *ops __used) | 
|  | 310 | { | 
|  | 311 | dump_printf(": unhandled!\n"); | 
|  | 312 | return 0; | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | static int process_finished_round(event_t *event, | 
|  | 316 | struct perf_session *session, | 
|  | 317 | struct perf_event_ops *ops); | 
|  | 318 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 319 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | 
|  | 320 | { | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 321 | if (handler->sample == NULL) | 
|  | 322 | handler->sample = process_event_stub; | 
|  | 323 | if (handler->mmap == NULL) | 
|  | 324 | handler->mmap = process_event_stub; | 
|  | 325 | if (handler->comm == NULL) | 
|  | 326 | handler->comm = process_event_stub; | 
|  | 327 | if (handler->fork == NULL) | 
|  | 328 | handler->fork = process_event_stub; | 
|  | 329 | if (handler->exit == NULL) | 
|  | 330 | handler->exit = process_event_stub; | 
|  | 331 | if (handler->lost == NULL) | 
| Arnaldo Carvalho de Melo | 37982ba | 2010-11-26 18:31:54 -0200 | [diff] [blame] | 332 | handler->lost = event__process_lost; | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 333 | if (handler->read == NULL) | 
|  | 334 | handler->read = process_event_stub; | 
|  | 335 | if (handler->throttle == NULL) | 
|  | 336 | handler->throttle = process_event_stub; | 
|  | 337 | if (handler->unthrottle == NULL) | 
|  | 338 | handler->unthrottle = process_event_stub; | 
| Tom Zanussi | 2c46dbb | 2010-04-01 23:59:19 -0500 | [diff] [blame] | 339 | if (handler->attr == NULL) | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 340 | handler->attr = process_event_synth_stub; | 
| Tom Zanussi | cd19a03 | 2010-04-01 23:59:20 -0500 | [diff] [blame] | 341 | if (handler->event_type == NULL) | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 342 | handler->event_type = process_event_synth_stub; | 
| Tom Zanussi | 9215545 | 2010-04-01 23:59:21 -0500 | [diff] [blame] | 343 | if (handler->tracing_data == NULL) | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 344 | handler->tracing_data = process_event_synth_stub; | 
| Tom Zanussi | c7929e4 | 2010-04-01 23:59:22 -0500 | [diff] [blame] | 345 | if (handler->build_id == NULL) | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 346 | handler->build_id = process_event_synth_stub; | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 347 | if (handler->finished_round == NULL) { | 
|  | 348 | if (handler->ordered_samples) | 
|  | 349 | handler->finished_round = process_finished_round; | 
|  | 350 | else | 
|  | 351 | handler->finished_round = process_finished_round_stub; | 
|  | 352 | } | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 353 | } | 
|  | 354 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 355 | void mem_bswap_64(void *src, int byte_size) | 
|  | 356 | { | 
|  | 357 | u64 *m = src; | 
|  | 358 |  | 
|  | 359 | while (byte_size > 0) { | 
|  | 360 | *m = bswap_64(*m); | 
|  | 361 | byte_size -= sizeof(u64); | 
|  | 362 | ++m; | 
|  | 363 | } | 
|  | 364 | } | 
|  | 365 |  | 
|  | 366 | static void event__all64_swap(event_t *self) | 
|  | 367 | { | 
|  | 368 | struct perf_event_header *hdr = &self->header; | 
|  | 369 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | 
|  | 370 | } | 
|  | 371 |  | 
|  | 372 | static void event__comm_swap(event_t *self) | 
|  | 373 | { | 
|  | 374 | self->comm.pid = bswap_32(self->comm.pid); | 
|  | 375 | self->comm.tid = bswap_32(self->comm.tid); | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | static void event__mmap_swap(event_t *self) | 
|  | 379 | { | 
|  | 380 | self->mmap.pid	 = bswap_32(self->mmap.pid); | 
|  | 381 | self->mmap.tid	 = bswap_32(self->mmap.tid); | 
|  | 382 | self->mmap.start = bswap_64(self->mmap.start); | 
|  | 383 | self->mmap.len	 = bswap_64(self->mmap.len); | 
|  | 384 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | 
|  | 385 | } | 
|  | 386 |  | 
|  | 387 | static void event__task_swap(event_t *self) | 
|  | 388 | { | 
|  | 389 | self->fork.pid	= bswap_32(self->fork.pid); | 
|  | 390 | self->fork.tid	= bswap_32(self->fork.tid); | 
|  | 391 | self->fork.ppid	= bswap_32(self->fork.ppid); | 
|  | 392 | self->fork.ptid	= bswap_32(self->fork.ptid); | 
|  | 393 | self->fork.time	= bswap_64(self->fork.time); | 
|  | 394 | } | 
|  | 395 |  | 
|  | 396 | static void event__read_swap(event_t *self) | 
|  | 397 | { | 
|  | 398 | self->read.pid		= bswap_32(self->read.pid); | 
|  | 399 | self->read.tid		= bswap_32(self->read.tid); | 
|  | 400 | self->read.value	= bswap_64(self->read.value); | 
|  | 401 | self->read.time_enabled	= bswap_64(self->read.time_enabled); | 
|  | 402 | self->read.time_running	= bswap_64(self->read.time_running); | 
|  | 403 | self->read.id		= bswap_64(self->read.id); | 
|  | 404 | } | 
|  | 405 |  | 
| Tom Zanussi | 2c46dbb | 2010-04-01 23:59:19 -0500 | [diff] [blame] | 406 | static void event__attr_swap(event_t *self) | 
|  | 407 | { | 
|  | 408 | size_t size; | 
|  | 409 |  | 
|  | 410 | self->attr.attr.type		= bswap_32(self->attr.attr.type); | 
|  | 411 | self->attr.attr.size		= bswap_32(self->attr.attr.size); | 
|  | 412 | self->attr.attr.config		= bswap_64(self->attr.attr.config); | 
|  | 413 | self->attr.attr.sample_period	= bswap_64(self->attr.attr.sample_period); | 
|  | 414 | self->attr.attr.sample_type	= bswap_64(self->attr.attr.sample_type); | 
|  | 415 | self->attr.attr.read_format	= bswap_64(self->attr.attr.read_format); | 
|  | 416 | self->attr.attr.wakeup_events	= bswap_32(self->attr.attr.wakeup_events); | 
|  | 417 | self->attr.attr.bp_type		= bswap_32(self->attr.attr.bp_type); | 
|  | 418 | self->attr.attr.bp_addr		= bswap_64(self->attr.attr.bp_addr); | 
|  | 419 | self->attr.attr.bp_len		= bswap_64(self->attr.attr.bp_len); | 
|  | 420 |  | 
|  | 421 | size = self->header.size; | 
|  | 422 | size -= (void *)&self->attr.id - (void *)self; | 
|  | 423 | mem_bswap_64(self->attr.id, size); | 
|  | 424 | } | 
|  | 425 |  | 
| Tom Zanussi | cd19a03 | 2010-04-01 23:59:20 -0500 | [diff] [blame] | 426 | static void event__event_type_swap(event_t *self) | 
|  | 427 | { | 
|  | 428 | self->event_type.event_type.event_id = | 
|  | 429 | bswap_64(self->event_type.event_type.event_id); | 
|  | 430 | } | 
|  | 431 |  | 
| Tom Zanussi | 9215545 | 2010-04-01 23:59:21 -0500 | [diff] [blame] | 432 | static void event__tracing_data_swap(event_t *self) | 
|  | 433 | { | 
|  | 434 | self->tracing_data.size = bswap_32(self->tracing_data.size); | 
|  | 435 | } | 
|  | 436 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 437 | typedef void (*event__swap_op)(event_t *self); | 
|  | 438 |  | 
|  | 439 | static event__swap_op event__swap_ops[] = { | 
|  | 440 | [PERF_RECORD_MMAP]   = event__mmap_swap, | 
|  | 441 | [PERF_RECORD_COMM]   = event__comm_swap, | 
|  | 442 | [PERF_RECORD_FORK]   = event__task_swap, | 
|  | 443 | [PERF_RECORD_EXIT]   = event__task_swap, | 
|  | 444 | [PERF_RECORD_LOST]   = event__all64_swap, | 
|  | 445 | [PERF_RECORD_READ]   = event__read_swap, | 
|  | 446 | [PERF_RECORD_SAMPLE] = event__all64_swap, | 
| Tom Zanussi | 2c46dbb | 2010-04-01 23:59:19 -0500 | [diff] [blame] | 447 | [PERF_RECORD_HEADER_ATTR]   = event__attr_swap, | 
| Tom Zanussi | cd19a03 | 2010-04-01 23:59:20 -0500 | [diff] [blame] | 448 | [PERF_RECORD_HEADER_EVENT_TYPE]   = event__event_type_swap, | 
| Tom Zanussi | 9215545 | 2010-04-01 23:59:21 -0500 | [diff] [blame] | 449 | [PERF_RECORD_HEADER_TRACING_DATA]   = event__tracing_data_swap, | 
| Tom Zanussi | c7929e4 | 2010-04-01 23:59:22 -0500 | [diff] [blame] | 450 | [PERF_RECORD_HEADER_BUILD_ID]   = NULL, | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 451 | [PERF_RECORD_HEADER_MAX]    = NULL, | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 452 | }; | 
|  | 453 |  | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 454 | struct sample_queue { | 
|  | 455 | u64			timestamp; | 
| Thomas Gleixner | e4c2df1 | 2010-12-07 12:48:50 +0000 | [diff] [blame] | 456 | u64			file_offset; | 
| Thomas Gleixner | 28990f7 | 2010-11-30 17:49:35 +0000 | [diff] [blame] | 457 | event_t			*event; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 458 | struct list_head	list; | 
|  | 459 | }; | 
|  | 460 |  | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 461 | static void perf_session_free_sample_buffers(struct perf_session *session) | 
|  | 462 | { | 
|  | 463 | struct ordered_samples *os = &session->ordered_samples; | 
|  | 464 |  | 
| Thomas Gleixner | 5c891f3 | 2010-11-30 17:49:55 +0000 | [diff] [blame] | 465 | while (!list_empty(&os->to_free)) { | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 466 | struct sample_queue *sq; | 
|  | 467 |  | 
| Thomas Gleixner | 5c891f3 | 2010-11-30 17:49:55 +0000 | [diff] [blame] | 468 | sq = list_entry(os->to_free.next, struct sample_queue, list); | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 469 | list_del(&sq->list); | 
|  | 470 | free(sq); | 
|  | 471 | } | 
|  | 472 | } | 
|  | 473 |  | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 474 | static int perf_session_deliver_event(struct perf_session *session, | 
|  | 475 | event_t *event, | 
|  | 476 | struct sample_data *sample, | 
| Thomas Gleixner | f74725d | 2010-12-07 12:48:53 +0000 | [diff] [blame] | 477 | struct perf_event_ops *ops, | 
|  | 478 | u64 file_offset); | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 479 |  | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 480 | static void flush_sample_queue(struct perf_session *s, | 
|  | 481 | struct perf_event_ops *ops) | 
|  | 482 | { | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 483 | struct ordered_samples *os = &s->ordered_samples; | 
|  | 484 | struct list_head *head = &os->samples; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 485 | struct sample_queue *tmp, *iter; | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 486 | struct sample_data sample; | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 487 | u64 limit = os->next_flush; | 
|  | 488 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 489 |  | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 490 | if (!ops->ordered_samples || !limit) | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 491 | return; | 
|  | 492 |  | 
|  | 493 | list_for_each_entry_safe(iter, tmp, head, list) { | 
|  | 494 | if (iter->timestamp > limit) | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 495 | break; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 496 |  | 
| Arnaldo Carvalho de Melo | 9c90a61 | 2010-12-02 10:25:28 -0200 | [diff] [blame] | 497 | event__parse_sample(iter->event, s, &sample); | 
| Thomas Gleixner | f74725d | 2010-12-07 12:48:53 +0000 | [diff] [blame] | 498 | perf_session_deliver_event(s, iter->event, &sample, ops, | 
|  | 499 | iter->file_offset); | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 500 |  | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 501 | os->last_flush = iter->timestamp; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 502 | list_del(&iter->list); | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 503 | list_add(&iter->list, &os->sample_cache); | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 504 | } | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 505 |  | 
|  | 506 | if (list_empty(head)) { | 
|  | 507 | os->last_sample = NULL; | 
|  | 508 | } else if (last_ts <= limit) { | 
|  | 509 | os->last_sample = | 
|  | 510 | list_entry(head->prev, struct sample_queue, list); | 
|  | 511 | } | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 512 | } | 
|  | 513 |  | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 514 | /* | 
|  | 515 | * When perf record finishes a pass on every buffers, it records this pseudo | 
|  | 516 | * event. | 
|  | 517 | * We record the max timestamp t found in the pass n. | 
|  | 518 | * Assuming these timestamps are monotonic across cpus, we know that if | 
|  | 519 | * a buffer still has events with timestamps below t, they will be all | 
|  | 520 | * available and then read in the pass n + 1. | 
|  | 521 | * Hence when we start to read the pass n + 2, we can safely flush every | 
|  | 522 | * events with timestamps below t. | 
|  | 523 | * | 
|  | 524 | *    ============ PASS n ================= | 
|  | 525 | *       CPU 0         |   CPU 1 | 
|  | 526 | *                     | | 
|  | 527 | *    cnt1 timestamps  |   cnt2 timestamps | 
|  | 528 | *          1          |         2 | 
|  | 529 | *          2          |         3 | 
|  | 530 | *          -          |         4  <--- max recorded | 
|  | 531 | * | 
|  | 532 | *    ============ PASS n + 1 ============== | 
|  | 533 | *       CPU 0         |   CPU 1 | 
|  | 534 | *                     | | 
|  | 535 | *    cnt1 timestamps  |   cnt2 timestamps | 
|  | 536 | *          3          |         5 | 
|  | 537 | *          4          |         6 | 
|  | 538 | *          5          |         7 <---- max recorded | 
|  | 539 | * | 
|  | 540 | *      Flush every events below timestamp 4 | 
|  | 541 | * | 
|  | 542 | *    ============ PASS n + 2 ============== | 
|  | 543 | *       CPU 0         |   CPU 1 | 
|  | 544 | *                     | | 
|  | 545 | *    cnt1 timestamps  |   cnt2 timestamps | 
|  | 546 | *          6          |         8 | 
|  | 547 | *          7          |         9 | 
|  | 548 | *          -          |         10 | 
|  | 549 | * | 
|  | 550 | *      Flush every events below timestamp 7 | 
|  | 551 | *      etc... | 
|  | 552 | */ | 
|  | 553 | static int process_finished_round(event_t *event __used, | 
|  | 554 | struct perf_session *session, | 
|  | 555 | struct perf_event_ops *ops) | 
|  | 556 | { | 
|  | 557 | flush_sample_queue(session, ops); | 
|  | 558 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | 
|  | 559 |  | 
|  | 560 | return 0; | 
|  | 561 | } | 
|  | 562 |  | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 563 | /* The queue is ordered by time */ | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 564 | static void __queue_event(struct sample_queue *new, struct perf_session *s) | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 565 | { | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 566 | struct ordered_samples *os = &s->ordered_samples; | 
|  | 567 | struct sample_queue *sample = os->last_sample; | 
|  | 568 | u64 timestamp = new->timestamp; | 
|  | 569 | struct list_head *p; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 570 |  | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 571 | os->last_sample = new; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 572 |  | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 573 | if (!sample) { | 
|  | 574 | list_add(&new->list, &os->samples); | 
|  | 575 | os->max_timestamp = timestamp; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 576 | return; | 
|  | 577 | } | 
|  | 578 |  | 
|  | 579 | /* | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 580 | * last_sample might point to some random place in the list as it's | 
|  | 581 | * the last queued event. We expect that the new event is close to | 
|  | 582 | * this. | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 583 | */ | 
| Thomas Gleixner | a1225de | 2010-11-30 17:49:33 +0000 | [diff] [blame] | 584 | if (sample->timestamp <= timestamp) { | 
|  | 585 | while (sample->timestamp <= timestamp) { | 
|  | 586 | p = sample->list.next; | 
|  | 587 | if (p == &os->samples) { | 
|  | 588 | list_add_tail(&new->list, &os->samples); | 
|  | 589 | os->max_timestamp = timestamp; | 
|  | 590 | return; | 
|  | 591 | } | 
|  | 592 | sample = list_entry(p, struct sample_queue, list); | 
|  | 593 | } | 
|  | 594 | list_add_tail(&new->list, &sample->list); | 
|  | 595 | } else { | 
|  | 596 | while (sample->timestamp > timestamp) { | 
|  | 597 | p = sample->list.prev; | 
|  | 598 | if (p == &os->samples) { | 
|  | 599 | list_add(&new->list, &os->samples); | 
|  | 600 | return; | 
|  | 601 | } | 
|  | 602 | sample = list_entry(p, struct sample_queue, list); | 
|  | 603 | } | 
|  | 604 | list_add(&new->list, &sample->list); | 
|  | 605 | } | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 606 | } | 
|  | 607 |  | 
| Thomas Gleixner | 5c891f3 | 2010-11-30 17:49:55 +0000 | [diff] [blame] | 608 | #define MAX_SAMPLE_BUFFER	(64 * 1024 / sizeof(struct sample_queue)) | 
|  | 609 |  | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 610 | static int perf_session_queue_event(struct perf_session *s, event_t *event, | 
| Thomas Gleixner | e4c2df1 | 2010-12-07 12:48:50 +0000 | [diff] [blame] | 611 | struct sample_data *data, u64 file_offset) | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 612 | { | 
| Thomas Gleixner | 5c891f3 | 2010-11-30 17:49:55 +0000 | [diff] [blame] | 613 | struct ordered_samples *os = &s->ordered_samples; | 
|  | 614 | struct list_head *sc = &os->sample_cache; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 615 | u64 timestamp = data->time; | 
|  | 616 | struct sample_queue *new; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 617 |  | 
| Thomas Gleixner | 79a14c1 | 2010-12-07 12:48:44 +0000 | [diff] [blame] | 618 | if (!timestamp || timestamp == ~0ULL) | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 619 | return -ETIME; | 
|  | 620 |  | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 621 | if (timestamp < s->ordered_samples.last_flush) { | 
|  | 622 | printf("Warning: Timestamp below last timeslice flush\n"); | 
|  | 623 | return -EINVAL; | 
|  | 624 | } | 
|  | 625 |  | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 626 | if (!list_empty(sc)) { | 
|  | 627 | new = list_entry(sc->next, struct sample_queue, list); | 
|  | 628 | list_del(&new->list); | 
| Thomas Gleixner | 5c891f3 | 2010-11-30 17:49:55 +0000 | [diff] [blame] | 629 | } else if (os->sample_buffer) { | 
|  | 630 | new = os->sample_buffer + os->sample_buffer_idx; | 
|  | 631 | if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER) | 
|  | 632 | os->sample_buffer = NULL; | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 633 | } else { | 
| Thomas Gleixner | 5c891f3 | 2010-11-30 17:49:55 +0000 | [diff] [blame] | 634 | os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new)); | 
|  | 635 | if (!os->sample_buffer) | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 636 | return -ENOMEM; | 
| Thomas Gleixner | 5c891f3 | 2010-11-30 17:49:55 +0000 | [diff] [blame] | 637 | list_add(&os->sample_buffer->list, &os->to_free); | 
|  | 638 | os->sample_buffer_idx = 2; | 
|  | 639 | new = os->sample_buffer + 1; | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 640 | } | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 641 |  | 
|  | 642 | new->timestamp = timestamp; | 
| Thomas Gleixner | e4c2df1 | 2010-12-07 12:48:50 +0000 | [diff] [blame] | 643 | new->file_offset = file_offset; | 
| Thomas Gleixner | fe17420 | 2010-11-30 17:49:49 +0000 | [diff] [blame] | 644 | new->event = event; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 645 |  | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 646 | __queue_event(new, s); | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 647 |  | 
|  | 648 | return 0; | 
|  | 649 | } | 
|  | 650 |  | 
| Arnaldo Carvalho de Melo | ddbc24b | 2010-12-09 12:20:20 -0200 | [diff] [blame] | 651 | static void callchain__printf(struct sample_data *sample) | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 652 | { | 
|  | 653 | unsigned int i; | 
|  | 654 |  | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 655 | printf("... chain: nr:%" PRIu64 "\n", sample->callchain->nr); | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 656 |  | 
|  | 657 | for (i = 0; i < sample->callchain->nr; i++) | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 658 | printf("..... %2d: %016" PRIx64 "\n", | 
|  | 659 | i, sample->callchain->ips[i]); | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 660 | } | 
|  | 661 |  | 
| Arnaldo Carvalho de Melo | 9c90a61 | 2010-12-02 10:25:28 -0200 | [diff] [blame] | 662 | static void perf_session__print_tstamp(struct perf_session *session, | 
|  | 663 | event_t *event, | 
|  | 664 | struct sample_data *sample) | 
|  | 665 | { | 
|  | 666 | if (event->header.type != PERF_RECORD_SAMPLE && | 
|  | 667 | !session->sample_id_all) { | 
|  | 668 | fputs("-1 -1 ", stdout); | 
|  | 669 | return; | 
|  | 670 | } | 
|  | 671 |  | 
|  | 672 | if ((session->sample_type & PERF_SAMPLE_CPU)) | 
|  | 673 | printf("%u ", sample->cpu); | 
|  | 674 |  | 
|  | 675 | if (session->sample_type & PERF_SAMPLE_TIME) | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 676 | printf("%" PRIu64 " ", sample->time); | 
| Arnaldo Carvalho de Melo | 9c90a61 | 2010-12-02 10:25:28 -0200 | [diff] [blame] | 677 | } | 
|  | 678 |  | 
| Thomas Gleixner | 9aefcab | 2010-12-07 12:48:47 +0000 | [diff] [blame] | 679 | static void dump_event(struct perf_session *session, event_t *event, | 
|  | 680 | u64 file_offset, struct sample_data *sample) | 
|  | 681 | { | 
|  | 682 | if (!dump_trace) | 
|  | 683 | return; | 
|  | 684 |  | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 685 | printf("\n%#" PRIx64 " [%#x]: event: %d\n", | 
|  | 686 | file_offset, event->header.size, event->header.type); | 
| Thomas Gleixner | 9aefcab | 2010-12-07 12:48:47 +0000 | [diff] [blame] | 687 |  | 
|  | 688 | trace_event(event); | 
|  | 689 |  | 
|  | 690 | if (sample) | 
|  | 691 | perf_session__print_tstamp(session, event, sample); | 
|  | 692 |  | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 693 | printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset, | 
|  | 694 | event->header.size, event__get_event_name(event->header.type)); | 
| Thomas Gleixner | 9aefcab | 2010-12-07 12:48:47 +0000 | [diff] [blame] | 695 | } | 
|  | 696 |  | 
|  | 697 | static void dump_sample(struct perf_session *session, event_t *event, | 
|  | 698 | struct sample_data *sample) | 
|  | 699 | { | 
| Arnaldo Carvalho de Melo | ddbc24b | 2010-12-09 12:20:20 -0200 | [diff] [blame] | 700 | if (!dump_trace) | 
|  | 701 | return; | 
|  | 702 |  | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 703 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 "\n", | 
|  | 704 | event->header.misc, sample->pid, sample->tid, sample->ip, | 
|  | 705 | sample->period); | 
| Thomas Gleixner | 9aefcab | 2010-12-07 12:48:47 +0000 | [diff] [blame] | 706 |  | 
|  | 707 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) | 
| Arnaldo Carvalho de Melo | ddbc24b | 2010-12-09 12:20:20 -0200 | [diff] [blame] | 708 | callchain__printf(sample); | 
| Thomas Gleixner | 9aefcab | 2010-12-07 12:48:47 +0000 | [diff] [blame] | 709 | } | 
|  | 710 |  | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 711 | static int perf_session_deliver_event(struct perf_session *session, | 
|  | 712 | event_t *event, | 
|  | 713 | struct sample_data *sample, | 
| Thomas Gleixner | f74725d | 2010-12-07 12:48:53 +0000 | [diff] [blame] | 714 | struct perf_event_ops *ops, | 
| Thomas Gleixner | 532e726 | 2010-12-07 12:48:55 +0000 | [diff] [blame] | 715 | u64 file_offset) | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 716 | { | 
| Thomas Gleixner | 532e726 | 2010-12-07 12:48:55 +0000 | [diff] [blame] | 717 | dump_event(session, event, file_offset, sample); | 
|  | 718 |  | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 719 | switch (event->header.type) { | 
|  | 720 | case PERF_RECORD_SAMPLE: | 
| Thomas Gleixner | 532e726 | 2010-12-07 12:48:55 +0000 | [diff] [blame] | 721 | dump_sample(session, event, sample); | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 722 | return ops->sample(event, sample, session); | 
|  | 723 | case PERF_RECORD_MMAP: | 
|  | 724 | return ops->mmap(event, sample, session); | 
|  | 725 | case PERF_RECORD_COMM: | 
|  | 726 | return ops->comm(event, sample, session); | 
|  | 727 | case PERF_RECORD_FORK: | 
|  | 728 | return ops->fork(event, sample, session); | 
|  | 729 | case PERF_RECORD_EXIT: | 
|  | 730 | return ops->exit(event, sample, session); | 
|  | 731 | case PERF_RECORD_LOST: | 
|  | 732 | return ops->lost(event, sample, session); | 
|  | 733 | case PERF_RECORD_READ: | 
|  | 734 | return ops->read(event, sample, session); | 
|  | 735 | case PERF_RECORD_THROTTLE: | 
|  | 736 | return ops->throttle(event, sample, session); | 
|  | 737 | case PERF_RECORD_UNTHROTTLE: | 
|  | 738 | return ops->unthrottle(event, sample, session); | 
|  | 739 | default: | 
|  | 740 | ++session->hists.stats.nr_unknown_events; | 
|  | 741 | return -1; | 
|  | 742 | } | 
|  | 743 | } | 
|  | 744 |  | 
| Thomas Gleixner | 3dfc2c0 | 2010-12-07 12:48:58 +0000 | [diff] [blame] | 745 | static int perf_session__preprocess_sample(struct perf_session *session, | 
|  | 746 | event_t *event, struct sample_data *sample) | 
|  | 747 | { | 
|  | 748 | if (event->header.type != PERF_RECORD_SAMPLE || | 
|  | 749 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) | 
|  | 750 | return 0; | 
|  | 751 |  | 
|  | 752 | if (!ip_callchain__valid(sample->callchain, event)) { | 
|  | 753 | pr_debug("call-chain problem with event, skipping it.\n"); | 
|  | 754 | ++session->hists.stats.nr_invalid_chains; | 
|  | 755 | session->hists.stats.total_invalid_chains += sample->period; | 
|  | 756 | return -EINVAL; | 
|  | 757 | } | 
|  | 758 | return 0; | 
|  | 759 | } | 
|  | 760 |  | 
| Thomas Gleixner | ba74f06 | 2010-12-07 12:49:01 +0000 | [diff] [blame] | 761 | static int perf_session__process_user_event(struct perf_session *session, event_t *event, | 
|  | 762 | struct perf_event_ops *ops, u64 file_offset) | 
|  | 763 | { | 
|  | 764 | dump_event(session, event, file_offset, NULL); | 
|  | 765 |  | 
|  | 766 | /* These events are processed right away */ | 
|  | 767 | switch (event->header.type) { | 
|  | 768 | case PERF_RECORD_HEADER_ATTR: | 
|  | 769 | return ops->attr(event, session); | 
|  | 770 | case PERF_RECORD_HEADER_EVENT_TYPE: | 
|  | 771 | return ops->event_type(event, session); | 
|  | 772 | case PERF_RECORD_HEADER_TRACING_DATA: | 
|  | 773 | /* setup for reading amidst mmap */ | 
|  | 774 | lseek(session->fd, file_offset, SEEK_SET); | 
|  | 775 | return ops->tracing_data(event, session); | 
|  | 776 | case PERF_RECORD_HEADER_BUILD_ID: | 
|  | 777 | return ops->build_id(event, session); | 
|  | 778 | case PERF_RECORD_FINISHED_ROUND: | 
|  | 779 | return ops->finished_round(event, session, ops); | 
|  | 780 | default: | 
|  | 781 | return -EINVAL; | 
|  | 782 | } | 
|  | 783 | } | 
|  | 784 |  | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 785 | static int perf_session__process_event(struct perf_session *session, | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 786 | event_t *event, | 
|  | 787 | struct perf_event_ops *ops, | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 788 | u64 file_offset) | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 789 | { | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 790 | struct sample_data sample; | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 791 | int ret; | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 792 |  | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 793 | if (session->header.needs_swap && event__swap_ops[event->header.type]) | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 794 | event__swap_ops[event->header.type](event); | 
|  | 795 |  | 
| Thomas Gleixner | 9aefcab | 2010-12-07 12:48:47 +0000 | [diff] [blame] | 796 | if (event->header.type >= PERF_RECORD_HEADER_MAX) | 
|  | 797 | return -EINVAL; | 
| Arnaldo Carvalho de Melo | 640c03c | 2010-12-02 14:10:21 -0200 | [diff] [blame] | 798 |  | 
| Thomas Gleixner | 9aefcab | 2010-12-07 12:48:47 +0000 | [diff] [blame] | 799 | hists__inc_nr_events(&session->hists, event->header.type); | 
|  | 800 |  | 
|  | 801 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) | 
| Thomas Gleixner | ba74f06 | 2010-12-07 12:49:01 +0000 | [diff] [blame] | 802 | return perf_session__process_user_event(session, event, ops, file_offset); | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 803 |  | 
| Thomas Gleixner | 3dfc2c0 | 2010-12-07 12:48:58 +0000 | [diff] [blame] | 804 | /* | 
|  | 805 | * For all kernel events we get the sample data | 
|  | 806 | */ | 
|  | 807 | event__parse_sample(event, session, &sample); | 
|  | 808 |  | 
|  | 809 | /* Preprocess sample records - precheck callchains */ | 
|  | 810 | if (perf_session__preprocess_sample(session, event, &sample)) | 
|  | 811 | return 0; | 
|  | 812 |  | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 813 | if (ops->ordered_samples) { | 
| Thomas Gleixner | e4c2df1 | 2010-12-07 12:48:50 +0000 | [diff] [blame] | 814 | ret = perf_session_queue_event(session, event, &sample, | 
|  | 815 | file_offset); | 
| Thomas Gleixner | cbf4164 | 2010-12-05 14:32:55 +0100 | [diff] [blame] | 816 | if (ret != -ETIME) | 
|  | 817 | return ret; | 
|  | 818 | } | 
|  | 819 |  | 
| Thomas Gleixner | f74725d | 2010-12-07 12:48:53 +0000 | [diff] [blame] | 820 | return perf_session_deliver_event(session, event, &sample, ops, | 
|  | 821 | file_offset); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 822 | } | 
|  | 823 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 824 | void perf_event_header__bswap(struct perf_event_header *self) | 
|  | 825 | { | 
|  | 826 | self->type = bswap_32(self->type); | 
|  | 827 | self->misc = bswap_16(self->misc); | 
|  | 828 | self->size = bswap_16(self->size); | 
|  | 829 | } | 
|  | 830 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 831 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | 
|  | 832 | { | 
|  | 833 | struct thread *thread = perf_session__findnew(self, 0); | 
|  | 834 |  | 
|  | 835 | if (thread == NULL || thread__set_comm(thread, "swapper")) { | 
|  | 836 | pr_err("problem inserting idle task.\n"); | 
|  | 837 | thread = NULL; | 
|  | 838 | } | 
|  | 839 |  | 
|  | 840 | return thread; | 
|  | 841 | } | 
|  | 842 |  | 
| Arnaldo Carvalho de Melo | 1109599 | 2011-01-04 16:25:15 -0200 | [diff] [blame] | 843 | static void perf_session__warn_about_errors(const struct perf_session *session, | 
|  | 844 | const struct perf_event_ops *ops) | 
|  | 845 | { | 
|  | 846 | if (ops->lost == event__process_lost && | 
|  | 847 | session->hists.stats.total_lost != 0) { | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 848 | ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 | 
|  | 849 | "!\n\nCheck IO/CPU overload!\n\n", | 
| Arnaldo Carvalho de Melo | 1109599 | 2011-01-04 16:25:15 -0200 | [diff] [blame] | 850 | session->hists.stats.total_period, | 
|  | 851 | session->hists.stats.total_lost); | 
|  | 852 | } | 
|  | 853 |  | 
|  | 854 | if (session->hists.stats.nr_unknown_events != 0) { | 
|  | 855 | ui__warning("Found %u unknown events!\n\n" | 
|  | 856 | "Is this an older tool processing a perf.data " | 
|  | 857 | "file generated by a more recent tool?\n\n" | 
|  | 858 | "If that is not the case, consider " | 
|  | 859 | "reporting to linux-kernel@vger.kernel.org.\n\n", | 
|  | 860 | session->hists.stats.nr_unknown_events); | 
|  | 861 | } | 
|  | 862 |  | 
|  | 863 | if (session->hists.stats.nr_invalid_chains != 0) { | 
|  | 864 | ui__warning("Found invalid callchains!\n\n" | 
|  | 865 | "%u out of %u events were discarded for this reason.\n\n" | 
|  | 866 | "Consider reporting to linux-kernel@vger.kernel.org.\n\n", | 
|  | 867 | session->hists.stats.nr_invalid_chains, | 
|  | 868 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); | 
|  | 869 | } | 
|  | 870 | } | 
|  | 871 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 872 | #define session_done()	(*(volatile int *)(&session_done)) | 
|  | 873 | volatile int session_done; | 
|  | 874 |  | 
|  | 875 | static int __perf_session__process_pipe_events(struct perf_session *self, | 
|  | 876 | struct perf_event_ops *ops) | 
|  | 877 | { | 
|  | 878 | event_t event; | 
|  | 879 | uint32_t size; | 
|  | 880 | int skip = 0; | 
|  | 881 | u64 head; | 
|  | 882 | int err; | 
|  | 883 | void *p; | 
|  | 884 |  | 
|  | 885 | perf_event_ops__fill_defaults(ops); | 
|  | 886 |  | 
|  | 887 | head = 0; | 
|  | 888 | more: | 
| Arnaldo Carvalho de Melo | 1e7972c | 2011-01-03 16:50:55 -0200 | [diff] [blame] | 889 | err = readn(self->fd, &event, sizeof(struct perf_event_header)); | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 890 | if (err <= 0) { | 
|  | 891 | if (err == 0) | 
|  | 892 | goto done; | 
|  | 893 |  | 
|  | 894 | pr_err("failed to read event header\n"); | 
|  | 895 | goto out_err; | 
|  | 896 | } | 
|  | 897 |  | 
|  | 898 | if (self->header.needs_swap) | 
|  | 899 | perf_event_header__bswap(&event.header); | 
|  | 900 |  | 
|  | 901 | size = event.header.size; | 
|  | 902 | if (size == 0) | 
|  | 903 | size = 8; | 
|  | 904 |  | 
|  | 905 | p = &event; | 
|  | 906 | p += sizeof(struct perf_event_header); | 
|  | 907 |  | 
| Tom Zanussi | 794e43b | 2010-05-05 00:27:40 -0500 | [diff] [blame] | 908 | if (size - sizeof(struct perf_event_header)) { | 
| Arnaldo Carvalho de Melo | 1e7972c | 2011-01-03 16:50:55 -0200 | [diff] [blame] | 909 | err = readn(self->fd, p, size - sizeof(struct perf_event_header)); | 
| Tom Zanussi | 794e43b | 2010-05-05 00:27:40 -0500 | [diff] [blame] | 910 | if (err <= 0) { | 
|  | 911 | if (err == 0) { | 
|  | 912 | pr_err("unexpected end of event stream\n"); | 
|  | 913 | goto done; | 
|  | 914 | } | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 915 |  | 
| Tom Zanussi | 794e43b | 2010-05-05 00:27:40 -0500 | [diff] [blame] | 916 | pr_err("failed to read event data\n"); | 
|  | 917 | goto out_err; | 
|  | 918 | } | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 919 | } | 
|  | 920 |  | 
|  | 921 | if (size == 0 || | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 922 | (skip = perf_session__process_event(self, &event, ops, head)) < 0) { | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 923 | dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 924 | head, event.header.size, event.header.type); | 
|  | 925 | /* | 
|  | 926 | * assume we lost track of the stream, check alignment, and | 
|  | 927 | * increment a single u64 in the hope to catch on again 'soon'. | 
|  | 928 | */ | 
|  | 929 | if (unlikely(head & 7)) | 
|  | 930 | head &= ~7ULL; | 
|  | 931 |  | 
|  | 932 | size = 8; | 
|  | 933 | } | 
|  | 934 |  | 
|  | 935 | head += size; | 
|  | 936 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 937 | if (skip > 0) | 
|  | 938 | head += skip; | 
|  | 939 |  | 
|  | 940 | if (!session_done()) | 
|  | 941 | goto more; | 
|  | 942 | done: | 
|  | 943 | err = 0; | 
|  | 944 | out_err: | 
| Arnaldo Carvalho de Melo | 1109599 | 2011-01-04 16:25:15 -0200 | [diff] [blame] | 945 | perf_session__warn_about_errors(self, ops); | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 946 | perf_session_free_sample_buffers(self); | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 947 | return err; | 
|  | 948 | } | 
|  | 949 |  | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 950 | int __perf_session__process_events(struct perf_session *session, | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 951 | u64 data_offset, u64 data_size, | 
|  | 952 | u64 file_size, struct perf_event_ops *ops) | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 953 | { | 
| Thomas Gleixner | 55b4462 | 2010-11-30 17:49:46 +0000 | [diff] [blame] | 954 | u64 head, page_offset, file_offset, file_pos, progress_next; | 
| Thomas Gleixner | fe17420 | 2010-11-30 17:49:49 +0000 | [diff] [blame] | 955 | int err, mmap_prot, mmap_flags, map_idx = 0; | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 956 | struct ui_progress *progress; | 
| Thomas Gleixner | 55b4462 | 2010-11-30 17:49:46 +0000 | [diff] [blame] | 957 | size_t	page_size, mmap_size; | 
| Thomas Gleixner | fe17420 | 2010-11-30 17:49:49 +0000 | [diff] [blame] | 958 | char *buf, *mmaps[8]; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 959 | event_t *event; | 
|  | 960 | uint32_t size; | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 961 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 962 | perf_event_ops__fill_defaults(ops); | 
|  | 963 |  | 
| Arnaldo Carvalho de Melo | 1b75962 | 2010-01-14 18:30:04 -0200 | [diff] [blame] | 964 | page_size = sysconf(_SC_PAGESIZE); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 965 |  | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 966 | page_offset = page_size * (data_offset / page_size); | 
|  | 967 | file_offset = page_offset; | 
|  | 968 | head = data_offset - page_offset; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 969 |  | 
| Thomas Gleixner | d651328 | 2010-11-30 17:49:44 +0000 | [diff] [blame] | 970 | if (data_offset + data_size < file_size) | 
|  | 971 | file_size = data_offset + data_size; | 
|  | 972 |  | 
| Thomas Gleixner | 55b4462 | 2010-11-30 17:49:46 +0000 | [diff] [blame] | 973 | progress_next = file_size / 16; | 
|  | 974 | progress = ui_progress__new("Processing events...", file_size); | 
|  | 975 | if (progress == NULL) | 
|  | 976 | return -1; | 
|  | 977 |  | 
|  | 978 | mmap_size = session->mmap_window; | 
|  | 979 | if (mmap_size > file_size) | 
|  | 980 | mmap_size = file_size; | 
|  | 981 |  | 
| Thomas Gleixner | fe17420 | 2010-11-30 17:49:49 +0000 | [diff] [blame] | 982 | memset(mmaps, 0, sizeof(mmaps)); | 
|  | 983 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 984 | mmap_prot  = PROT_READ; | 
|  | 985 | mmap_flags = MAP_SHARED; | 
|  | 986 |  | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 987 | if (session->header.needs_swap) { | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 988 | mmap_prot  |= PROT_WRITE; | 
|  | 989 | mmap_flags = MAP_PRIVATE; | 
|  | 990 | } | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 991 | remap: | 
| Thomas Gleixner | 55b4462 | 2010-11-30 17:49:46 +0000 | [diff] [blame] | 992 | buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, | 
|  | 993 | file_offset); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 994 | if (buf == MAP_FAILED) { | 
|  | 995 | pr_err("failed to mmap file\n"); | 
|  | 996 | err = -errno; | 
|  | 997 | goto out_err; | 
|  | 998 | } | 
| Thomas Gleixner | fe17420 | 2010-11-30 17:49:49 +0000 | [diff] [blame] | 999 | mmaps[map_idx] = buf; | 
|  | 1000 | map_idx = (map_idx + 1) & (ARRAY_SIZE(mmaps) - 1); | 
| Thomas Gleixner | d651328 | 2010-11-30 17:49:44 +0000 | [diff] [blame] | 1001 | file_pos = file_offset + head; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1002 |  | 
|  | 1003 | more: | 
|  | 1004 | event = (event_t *)(buf + head); | 
|  | 1005 |  | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 1006 | if (session->header.needs_swap) | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 1007 | perf_event_header__bswap(&event->header); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1008 | size = event->header.size; | 
|  | 1009 | if (size == 0) | 
|  | 1010 | size = 8; | 
|  | 1011 |  | 
| Arnaldo Carvalho de Melo | 3d03e2e | 2011-01-10 21:37:57 -0200 | [diff] [blame] | 1012 | if (head + event->header.size > mmap_size) { | 
| Thomas Gleixner | fe17420 | 2010-11-30 17:49:49 +0000 | [diff] [blame] | 1013 | if (mmaps[map_idx]) { | 
|  | 1014 | munmap(mmaps[map_idx], mmap_size); | 
|  | 1015 | mmaps[map_idx] = NULL; | 
|  | 1016 | } | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1017 |  | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 1018 | page_offset = page_size * (head / page_size); | 
|  | 1019 | file_offset += page_offset; | 
|  | 1020 | head -= page_offset; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1021 | goto remap; | 
|  | 1022 | } | 
|  | 1023 |  | 
|  | 1024 | size = event->header.size; | 
|  | 1025 |  | 
| Thomas Gleixner | d651328 | 2010-11-30 17:49:44 +0000 | [diff] [blame] | 1026 | if (size == 0 || | 
|  | 1027 | perf_session__process_event(session, event, ops, file_pos) < 0) { | 
| Arnaldo Carvalho de Melo | 9486aa3 | 2011-01-22 20:37:02 -0200 | [diff] [blame] | 1028 | dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 1029 | file_offset + head, event->header.size, | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1030 | event->header.type); | 
|  | 1031 | /* | 
|  | 1032 | * assume we lost track of the stream, check alignment, and | 
|  | 1033 | * increment a single u64 in the hope to catch on again 'soon'. | 
|  | 1034 | */ | 
|  | 1035 | if (unlikely(head & 7)) | 
|  | 1036 | head &= ~7ULL; | 
|  | 1037 |  | 
|  | 1038 | size = 8; | 
|  | 1039 | } | 
|  | 1040 |  | 
|  | 1041 | head += size; | 
| Thomas Gleixner | d651328 | 2010-11-30 17:49:44 +0000 | [diff] [blame] | 1042 | file_pos += size; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1043 |  | 
| Thomas Gleixner | 55b4462 | 2010-11-30 17:49:46 +0000 | [diff] [blame] | 1044 | if (file_pos >= progress_next) { | 
|  | 1045 | progress_next += file_size / 16; | 
|  | 1046 | ui_progress__update(progress, file_pos); | 
|  | 1047 | } | 
|  | 1048 |  | 
| Thomas Gleixner | d651328 | 2010-11-30 17:49:44 +0000 | [diff] [blame] | 1049 | if (file_pos < file_size) | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1050 | goto more; | 
| Thomas Gleixner | d651328 | 2010-11-30 17:49:44 +0000 | [diff] [blame] | 1051 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1052 | err = 0; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 1053 | /* do the final flush for ordered samples */ | 
| Thomas Gleixner | 0331ee0 | 2010-11-30 17:49:38 +0000 | [diff] [blame] | 1054 | session->ordered_samples.next_flush = ULLONG_MAX; | 
|  | 1055 | flush_sample_queue(session, ops); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1056 | out_err: | 
| Arnaldo Carvalho de Melo | 5f4d3f8 | 2010-03-26 21:16:22 -0300 | [diff] [blame] | 1057 | ui_progress__delete(progress); | 
| Arnaldo Carvalho de Melo | 1109599 | 2011-01-04 16:25:15 -0200 | [diff] [blame] | 1058 | perf_session__warn_about_errors(session, ops); | 
| Thomas Gleixner | 020bb75 | 2010-11-30 17:49:53 +0000 | [diff] [blame] | 1059 | perf_session_free_sample_buffers(session); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 1060 | return err; | 
|  | 1061 | } | 
| Arnaldo Carvalho de Melo | 2729559 | 2009-12-27 21:37:01 -0200 | [diff] [blame] | 1062 |  | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 1063 | int perf_session__process_events(struct perf_session *self, | 
|  | 1064 | struct perf_event_ops *ops) | 
|  | 1065 | { | 
|  | 1066 | int err; | 
|  | 1067 |  | 
|  | 1068 | if (perf_session__register_idle_thread(self) == NULL) | 
|  | 1069 | return -ENOMEM; | 
|  | 1070 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 1071 | if (!self->fd_pipe) | 
|  | 1072 | err = __perf_session__process_events(self, | 
|  | 1073 | self->header.data_offset, | 
|  | 1074 | self->header.data_size, | 
|  | 1075 | self->size, ops); | 
|  | 1076 | else | 
|  | 1077 | err = __perf_session__process_pipe_events(self, ops); | 
| Dave Martin | 88ca895 | 2010-07-27 11:46:12 -0300 | [diff] [blame] | 1078 |  | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 1079 | return err; | 
|  | 1080 | } | 
|  | 1081 |  | 
| Arnaldo Carvalho de Melo | d549c76 | 2009-12-27 21:37:02 -0200 | [diff] [blame] | 1082 | bool perf_session__has_traces(struct perf_session *self, const char *msg) | 
| Arnaldo Carvalho de Melo | 2729559 | 2009-12-27 21:37:01 -0200 | [diff] [blame] | 1083 | { | 
|  | 1084 | if (!(self->sample_type & PERF_SAMPLE_RAW)) { | 
| Arnaldo Carvalho de Melo | d549c76 | 2009-12-27 21:37:02 -0200 | [diff] [blame] | 1085 | pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); | 
|  | 1086 | return false; | 
| Arnaldo Carvalho de Melo | 2729559 | 2009-12-27 21:37:01 -0200 | [diff] [blame] | 1087 | } | 
|  | 1088 |  | 
| Arnaldo Carvalho de Melo | d549c76 | 2009-12-27 21:37:02 -0200 | [diff] [blame] | 1089 | return true; | 
| Arnaldo Carvalho de Melo | 2729559 | 2009-12-27 21:37:01 -0200 | [diff] [blame] | 1090 | } | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 1091 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 1092 | int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 1093 | const char *symbol_name, | 
|  | 1094 | u64 addr) | 
|  | 1095 | { | 
|  | 1096 | char *bracket; | 
| Arnaldo Carvalho de Melo | 9de89fe | 2010-02-03 16:52:00 -0200 | [diff] [blame] | 1097 | enum map_type i; | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 1098 | struct ref_reloc_sym *ref; | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 1099 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 1100 | ref = zalloc(sizeof(struct ref_reloc_sym)); | 
|  | 1101 | if (ref == NULL) | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 1102 | return -ENOMEM; | 
|  | 1103 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 1104 | ref->name = strdup(symbol_name); | 
|  | 1105 | if (ref->name == NULL) { | 
|  | 1106 | free(ref); | 
|  | 1107 | return -ENOMEM; | 
|  | 1108 | } | 
|  | 1109 |  | 
|  | 1110 | bracket = strchr(ref->name, ']'); | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 1111 | if (bracket) | 
|  | 1112 | *bracket = '\0'; | 
|  | 1113 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 1114 | ref->addr = addr; | 
| Arnaldo Carvalho de Melo | 9de89fe | 2010-02-03 16:52:00 -0200 | [diff] [blame] | 1115 |  | 
|  | 1116 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 1117 | struct kmap *kmap = map__kmap(maps[i]); | 
|  | 1118 | kmap->ref_reloc_sym = ref; | 
| Arnaldo Carvalho de Melo | 9de89fe | 2010-02-03 16:52:00 -0200 | [diff] [blame] | 1119 | } | 
|  | 1120 |  | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 1121 | return 0; | 
|  | 1122 | } | 
| Arnaldo Carvalho de Melo | 1f626bc | 2010-05-09 19:57:08 -0300 | [diff] [blame] | 1123 |  | 
|  | 1124 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) | 
|  | 1125 | { | 
|  | 1126 | return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) + | 
|  | 1127 | __dsos__fprintf(&self->host_machine.user_dsos, fp) + | 
|  | 1128 | machines__fprintf_dsos(&self->machines, fp); | 
|  | 1129 | } | 
| Arnaldo Carvalho de Melo | f869097 | 2010-05-19 13:41:23 -0300 | [diff] [blame] | 1130 |  | 
|  | 1131 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, | 
|  | 1132 | bool with_hits) | 
|  | 1133 | { | 
|  | 1134 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); | 
|  | 1135 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); | 
|  | 1136 | } |