| 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 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 68 | void perf_session__update_sample_type(struct perf_session *self) | 
|  | 69 | { | 
|  | 70 | self->sample_type = perf_header__sample_type(&self->header); | 
|  | 71 | } | 
|  | 72 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 73 | int perf_session__create_kernel_maps(struct perf_session *self) | 
|  | 74 | { | 
| Arnaldo Carvalho de Melo | d118f8b | 2010-05-10 12:51:05 -0300 | [diff] [blame] | 75 | int ret = machine__create_kernel_maps(&self->host_machine); | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 76 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 77 | if (ret >= 0) | 
| Arnaldo Carvalho de Melo | d118f8b | 2010-05-10 12:51:05 -0300 | [diff] [blame] | 78 | ret = machines__create_guest_kernel_maps(&self->machines); | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 79 | return ret; | 
|  | 80 | } | 
|  | 81 |  | 
| Arnaldo Carvalho de Melo | 076c6e45 | 2010-08-02 18:18:28 -0300 | [diff] [blame] | 82 | static void perf_session__destroy_kernel_maps(struct perf_session *self) | 
|  | 83 | { | 
|  | 84 | machine__destroy_kernel_maps(&self->host_machine); | 
|  | 85 | machines__destroy_guest_kernel_maps(&self->machines); | 
|  | 86 | } | 
|  | 87 |  | 
| Tom Zanussi | 454c407 | 2010-05-01 01:41:20 -0500 | [diff] [blame] | 88 | struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 89 | { | 
| Arnaldo Carvalho de Melo | b3165f4 | 2009-12-13 19:50:28 -0200 | [diff] [blame] | 90 | size_t len = filename ? strlen(filename) + 1 : 0; | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 91 | struct perf_session *self = zalloc(sizeof(*self) + len); | 
|  | 92 |  | 
|  | 93 | if (self == NULL) | 
|  | 94 | goto out; | 
|  | 95 |  | 
|  | 96 | if (perf_header__init(&self->header) < 0) | 
| Arnaldo Carvalho de Melo | 4aa6563 | 2009-12-13 19:50:29 -0200 | [diff] [blame] | 97 | goto out_free; | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 98 |  | 
|  | 99 | memcpy(self->filename, filename, len); | 
| Arnaldo Carvalho de Melo | b3165f4 | 2009-12-13 19:50:28 -0200 | [diff] [blame] | 100 | self->threads = RB_ROOT; | 
| Arnaldo Carvalho de Melo | 720a3ae | 2010-06-17 08:37:44 -0300 | [diff] [blame] | 101 | INIT_LIST_HEAD(&self->dead_threads); | 
| Arnaldo Carvalho de Melo | 1c02c4d | 2010-05-10 13:04:11 -0300 | [diff] [blame] | 102 | self->hists_tree = RB_ROOT; | 
| Arnaldo Carvalho de Melo | b3165f4 | 2009-12-13 19:50:28 -0200 | [diff] [blame] | 103 | self->last_match = NULL; | 
| Arnaldo Carvalho de Melo | ec91336 | 2009-12-13 19:50:27 -0200 | [diff] [blame] | 104 | self->mmap_window = 32; | 
| Arnaldo Carvalho de Melo | 23346f2 | 2010-04-27 21:17:50 -0300 | [diff] [blame] | 105 | self->machines = RB_ROOT; | 
| Tom Zanussi | 454c407 | 2010-05-01 01:41:20 -0500 | [diff] [blame] | 106 | self->repipe = repipe; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 107 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); | 
| Arnaldo Carvalho de Melo | 1f626bc | 2010-05-09 19:57:08 -0300 | [diff] [blame] | 108 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 109 |  | 
| Arnaldo Carvalho de Melo | 64abebf | 2010-01-27 21:05:52 -0200 | [diff] [blame] | 110 | if (mode == O_RDONLY) { | 
|  | 111 | if (perf_session__open(self, force) < 0) | 
|  | 112 | goto out_delete; | 
|  | 113 | } else if (mode == O_WRONLY) { | 
|  | 114 | /* | 
|  | 115 | * In O_RDONLY mode this will be performed when reading the | 
|  | 116 | * kernel MMAP event, in event__process_mmap(). | 
|  | 117 | */ | 
|  | 118 | if (perf_session__create_kernel_maps(self) < 0) | 
|  | 119 | goto out_delete; | 
|  | 120 | } | 
| Arnaldo Carvalho de Melo | d549c76 | 2009-12-27 21:37:02 -0200 | [diff] [blame] | 121 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 122 | perf_session__update_sample_type(self); | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 123 | out: | 
|  | 124 | return self; | 
| Arnaldo Carvalho de Melo | 4aa6563 | 2009-12-13 19:50:29 -0200 | [diff] [blame] | 125 | out_free: | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 126 | free(self); | 
|  | 127 | return NULL; | 
| Arnaldo Carvalho de Melo | 4aa6563 | 2009-12-13 19:50:29 -0200 | [diff] [blame] | 128 | out_delete: | 
|  | 129 | perf_session__delete(self); | 
|  | 130 | return NULL; | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 131 | } | 
|  | 132 |  | 
| Arnaldo Carvalho de Melo | d65a458 | 2010-07-30 18:31:28 -0300 | [diff] [blame] | 133 | static void perf_session__delete_dead_threads(struct perf_session *self) | 
|  | 134 | { | 
|  | 135 | struct thread *n, *t; | 
|  | 136 |  | 
|  | 137 | list_for_each_entry_safe(t, n, &self->dead_threads, node) { | 
|  | 138 | list_del(&t->node); | 
|  | 139 | thread__delete(t); | 
|  | 140 | } | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | static void perf_session__delete_threads(struct perf_session *self) | 
|  | 144 | { | 
|  | 145 | struct rb_node *nd = rb_first(&self->threads); | 
|  | 146 |  | 
|  | 147 | while (nd) { | 
|  | 148 | struct thread *t = rb_entry(nd, struct thread, rb_node); | 
|  | 149 |  | 
|  | 150 | rb_erase(&t->rb_node, &self->threads); | 
|  | 151 | nd = rb_next(nd); | 
|  | 152 | thread__delete(t); | 
|  | 153 | } | 
|  | 154 | } | 
|  | 155 |  | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 156 | void perf_session__delete(struct perf_session *self) | 
|  | 157 | { | 
|  | 158 | perf_header__exit(&self->header); | 
| Arnaldo Carvalho de Melo | 076c6e45 | 2010-08-02 18:18:28 -0300 | [diff] [blame] | 159 | perf_session__destroy_kernel_maps(self); | 
| Arnaldo Carvalho de Melo | d65a458 | 2010-07-30 18:31:28 -0300 | [diff] [blame] | 160 | perf_session__delete_dead_threads(self); | 
|  | 161 | perf_session__delete_threads(self); | 
|  | 162 | machine__exit(&self->host_machine); | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 163 | close(self->fd); | 
| Arnaldo Carvalho de Melo | 94c744b | 2009-12-11 21:24:02 -0200 | [diff] [blame] | 164 | free(self); | 
|  | 165 | } | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 166 |  | 
| Arnaldo Carvalho de Melo | 720a3ae | 2010-06-17 08:37:44 -0300 | [diff] [blame] | 167 | void perf_session__remove_thread(struct perf_session *self, struct thread *th) | 
|  | 168 | { | 
| Arnaldo Carvalho de Melo | 70597f2 | 2010-08-02 18:59:28 -0300 | [diff] [blame] | 169 | self->last_match = NULL; | 
| Arnaldo Carvalho de Melo | 720a3ae | 2010-06-17 08:37:44 -0300 | [diff] [blame] | 170 | rb_erase(&th->rb_node, &self->threads); | 
|  | 171 | /* | 
|  | 172 | * We may have references to this thread, for instance in some hist_entry | 
|  | 173 | * instances, so just move them to a separate list. | 
|  | 174 | */ | 
|  | 175 | list_add_tail(&th->node, &self->dead_threads); | 
|  | 176 | } | 
|  | 177 |  | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 178 | static bool symbol__match_parent_regex(struct symbol *sym) | 
|  | 179 | { | 
|  | 180 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | 
|  | 181 | return 1; | 
|  | 182 |  | 
|  | 183 | return 0; | 
|  | 184 | } | 
|  | 185 |  | 
| Arnaldo Carvalho de Melo | b3c9ac0 | 2010-03-24 16:40:18 -0300 | [diff] [blame] | 186 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 
|  | 187 | struct thread *thread, | 
|  | 188 | struct ip_callchain *chain, | 
|  | 189 | struct symbol **parent) | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 190 | { | 
|  | 191 | u8 cpumode = PERF_RECORD_MISC_USER; | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 192 | unsigned int i; | 
| Arnaldo Carvalho de Melo | ad5b217 | 2010-04-02 10:04:18 -0300 | [diff] [blame] | 193 | struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 194 |  | 
| Arnaldo Carvalho de Melo | ad5b217 | 2010-04-02 10:04:18 -0300 | [diff] [blame] | 195 | if (!syms) | 
|  | 196 | return NULL; | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 197 |  | 
|  | 198 | for (i = 0; i < chain->nr; i++) { | 
|  | 199 | u64 ip = chain->ips[i]; | 
|  | 200 | struct addr_location al; | 
|  | 201 |  | 
|  | 202 | if (ip >= PERF_CONTEXT_MAX) { | 
|  | 203 | switch (ip) { | 
|  | 204 | case PERF_CONTEXT_HV: | 
|  | 205 | cpumode = PERF_RECORD_MISC_HYPERVISOR;	break; | 
|  | 206 | case PERF_CONTEXT_KERNEL: | 
|  | 207 | cpumode = PERF_RECORD_MISC_KERNEL;	break; | 
|  | 208 | case PERF_CONTEXT_USER: | 
|  | 209 | cpumode = PERF_RECORD_MISC_USER;	break; | 
|  | 210 | default: | 
|  | 211 | break; | 
|  | 212 | } | 
|  | 213 | continue; | 
|  | 214 | } | 
|  | 215 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 216 | al.filtered = false; | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 217 | thread__find_addr_location(thread, self, cpumode, | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 218 | MAP__FUNCTION, thread->pid, ip, &al, NULL); | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 219 | if (al.sym != NULL) { | 
|  | 220 | if (sort__has_parent && !*parent && | 
|  | 221 | symbol__match_parent_regex(al.sym)) | 
|  | 222 | *parent = al.sym; | 
| Arnaldo Carvalho de Melo | d599db3 | 2009-12-15 20:04:42 -0200 | [diff] [blame] | 223 | if (!symbol_conf.use_callchain) | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 224 | break; | 
| Arnaldo Carvalho de Melo | b3c9ac0 | 2010-03-24 16:40:18 -0300 | [diff] [blame] | 225 | syms[i].map = al.map; | 
|  | 226 | syms[i].sym = al.sym; | 
| Arnaldo Carvalho de Melo | a328626 | 2009-12-14 14:22:59 -0200 | [diff] [blame] | 227 | } | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 | return syms; | 
|  | 231 | } | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 232 |  | 
|  | 233 | static int process_event_stub(event_t *event __used, | 
|  | 234 | struct perf_session *session __used) | 
|  | 235 | { | 
|  | 236 | dump_printf(": unhandled!\n"); | 
|  | 237 | return 0; | 
|  | 238 | } | 
|  | 239 |  | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 240 | static int process_finished_round_stub(event_t *event __used, | 
|  | 241 | struct perf_session *session __used, | 
|  | 242 | struct perf_event_ops *ops __used) | 
|  | 243 | { | 
|  | 244 | dump_printf(": unhandled!\n"); | 
|  | 245 | return 0; | 
|  | 246 | } | 
|  | 247 |  | 
|  | 248 | static int process_finished_round(event_t *event, | 
|  | 249 | struct perf_session *session, | 
|  | 250 | struct perf_event_ops *ops); | 
|  | 251 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 252 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | 
|  | 253 | { | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 254 | if (handler->sample == NULL) | 
|  | 255 | handler->sample = process_event_stub; | 
|  | 256 | if (handler->mmap == NULL) | 
|  | 257 | handler->mmap = process_event_stub; | 
|  | 258 | if (handler->comm == NULL) | 
|  | 259 | handler->comm = process_event_stub; | 
|  | 260 | if (handler->fork == NULL) | 
|  | 261 | handler->fork = process_event_stub; | 
|  | 262 | if (handler->exit == NULL) | 
|  | 263 | handler->exit = process_event_stub; | 
|  | 264 | if (handler->lost == NULL) | 
|  | 265 | handler->lost = process_event_stub; | 
|  | 266 | if (handler->read == NULL) | 
|  | 267 | handler->read = process_event_stub; | 
|  | 268 | if (handler->throttle == NULL) | 
|  | 269 | handler->throttle = process_event_stub; | 
|  | 270 | if (handler->unthrottle == NULL) | 
|  | 271 | handler->unthrottle = process_event_stub; | 
| Tom Zanussi | 2c46dbb | 2010-04-01 23:59:19 -0500 | [diff] [blame] | 272 | if (handler->attr == NULL) | 
|  | 273 | handler->attr = process_event_stub; | 
| Tom Zanussi | cd19a03 | 2010-04-01 23:59:20 -0500 | [diff] [blame] | 274 | if (handler->event_type == NULL) | 
|  | 275 | handler->event_type = process_event_stub; | 
| Tom Zanussi | 9215545 | 2010-04-01 23:59:21 -0500 | [diff] [blame] | 276 | if (handler->tracing_data == NULL) | 
|  | 277 | handler->tracing_data = process_event_stub; | 
| Tom Zanussi | c7929e4 | 2010-04-01 23:59:22 -0500 | [diff] [blame] | 278 | if (handler->build_id == NULL) | 
|  | 279 | handler->build_id = process_event_stub; | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 280 | if (handler->finished_round == NULL) { | 
|  | 281 | if (handler->ordered_samples) | 
|  | 282 | handler->finished_round = process_finished_round; | 
|  | 283 | else | 
|  | 284 | handler->finished_round = process_finished_round_stub; | 
|  | 285 | } | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 286 | } | 
|  | 287 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 288 | void mem_bswap_64(void *src, int byte_size) | 
|  | 289 | { | 
|  | 290 | u64 *m = src; | 
|  | 291 |  | 
|  | 292 | while (byte_size > 0) { | 
|  | 293 | *m = bswap_64(*m); | 
|  | 294 | byte_size -= sizeof(u64); | 
|  | 295 | ++m; | 
|  | 296 | } | 
|  | 297 | } | 
|  | 298 |  | 
|  | 299 | static void event__all64_swap(event_t *self) | 
|  | 300 | { | 
|  | 301 | struct perf_event_header *hdr = &self->header; | 
|  | 302 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | 
|  | 303 | } | 
|  | 304 |  | 
|  | 305 | static void event__comm_swap(event_t *self) | 
|  | 306 | { | 
|  | 307 | self->comm.pid = bswap_32(self->comm.pid); | 
|  | 308 | self->comm.tid = bswap_32(self->comm.tid); | 
|  | 309 | } | 
|  | 310 |  | 
|  | 311 | static void event__mmap_swap(event_t *self) | 
|  | 312 | { | 
|  | 313 | self->mmap.pid	 = bswap_32(self->mmap.pid); | 
|  | 314 | self->mmap.tid	 = bswap_32(self->mmap.tid); | 
|  | 315 | self->mmap.start = bswap_64(self->mmap.start); | 
|  | 316 | self->mmap.len	 = bswap_64(self->mmap.len); | 
|  | 317 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | 
|  | 318 | } | 
|  | 319 |  | 
|  | 320 | static void event__task_swap(event_t *self) | 
|  | 321 | { | 
|  | 322 | self->fork.pid	= bswap_32(self->fork.pid); | 
|  | 323 | self->fork.tid	= bswap_32(self->fork.tid); | 
|  | 324 | self->fork.ppid	= bswap_32(self->fork.ppid); | 
|  | 325 | self->fork.ptid	= bswap_32(self->fork.ptid); | 
|  | 326 | self->fork.time	= bswap_64(self->fork.time); | 
|  | 327 | } | 
|  | 328 |  | 
|  | 329 | static void event__read_swap(event_t *self) | 
|  | 330 | { | 
|  | 331 | self->read.pid		= bswap_32(self->read.pid); | 
|  | 332 | self->read.tid		= bswap_32(self->read.tid); | 
|  | 333 | self->read.value	= bswap_64(self->read.value); | 
|  | 334 | self->read.time_enabled	= bswap_64(self->read.time_enabled); | 
|  | 335 | self->read.time_running	= bswap_64(self->read.time_running); | 
|  | 336 | self->read.id		= bswap_64(self->read.id); | 
|  | 337 | } | 
|  | 338 |  | 
| Tom Zanussi | 2c46dbb | 2010-04-01 23:59:19 -0500 | [diff] [blame] | 339 | static void event__attr_swap(event_t *self) | 
|  | 340 | { | 
|  | 341 | size_t size; | 
|  | 342 |  | 
|  | 343 | self->attr.attr.type		= bswap_32(self->attr.attr.type); | 
|  | 344 | self->attr.attr.size		= bswap_32(self->attr.attr.size); | 
|  | 345 | self->attr.attr.config		= bswap_64(self->attr.attr.config); | 
|  | 346 | self->attr.attr.sample_period	= bswap_64(self->attr.attr.sample_period); | 
|  | 347 | self->attr.attr.sample_type	= bswap_64(self->attr.attr.sample_type); | 
|  | 348 | self->attr.attr.read_format	= bswap_64(self->attr.attr.read_format); | 
|  | 349 | self->attr.attr.wakeup_events	= bswap_32(self->attr.attr.wakeup_events); | 
|  | 350 | self->attr.attr.bp_type		= bswap_32(self->attr.attr.bp_type); | 
|  | 351 | self->attr.attr.bp_addr		= bswap_64(self->attr.attr.bp_addr); | 
|  | 352 | self->attr.attr.bp_len		= bswap_64(self->attr.attr.bp_len); | 
|  | 353 |  | 
|  | 354 | size = self->header.size; | 
|  | 355 | size -= (void *)&self->attr.id - (void *)self; | 
|  | 356 | mem_bswap_64(self->attr.id, size); | 
|  | 357 | } | 
|  | 358 |  | 
| Tom Zanussi | cd19a03 | 2010-04-01 23:59:20 -0500 | [diff] [blame] | 359 | static void event__event_type_swap(event_t *self) | 
|  | 360 | { | 
|  | 361 | self->event_type.event_type.event_id = | 
|  | 362 | bswap_64(self->event_type.event_type.event_id); | 
|  | 363 | } | 
|  | 364 |  | 
| Tom Zanussi | 9215545 | 2010-04-01 23:59:21 -0500 | [diff] [blame] | 365 | static void event__tracing_data_swap(event_t *self) | 
|  | 366 | { | 
|  | 367 | self->tracing_data.size = bswap_32(self->tracing_data.size); | 
|  | 368 | } | 
|  | 369 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 370 | typedef void (*event__swap_op)(event_t *self); | 
|  | 371 |  | 
|  | 372 | static event__swap_op event__swap_ops[] = { | 
|  | 373 | [PERF_RECORD_MMAP]   = event__mmap_swap, | 
|  | 374 | [PERF_RECORD_COMM]   = event__comm_swap, | 
|  | 375 | [PERF_RECORD_FORK]   = event__task_swap, | 
|  | 376 | [PERF_RECORD_EXIT]   = event__task_swap, | 
|  | 377 | [PERF_RECORD_LOST]   = event__all64_swap, | 
|  | 378 | [PERF_RECORD_READ]   = event__read_swap, | 
|  | 379 | [PERF_RECORD_SAMPLE] = event__all64_swap, | 
| Tom Zanussi | 2c46dbb | 2010-04-01 23:59:19 -0500 | [diff] [blame] | 380 | [PERF_RECORD_HEADER_ATTR]   = event__attr_swap, | 
| Tom Zanussi | cd19a03 | 2010-04-01 23:59:20 -0500 | [diff] [blame] | 381 | [PERF_RECORD_HEADER_EVENT_TYPE]   = event__event_type_swap, | 
| Tom Zanussi | 9215545 | 2010-04-01 23:59:21 -0500 | [diff] [blame] | 382 | [PERF_RECORD_HEADER_TRACING_DATA]   = event__tracing_data_swap, | 
| Tom Zanussi | c7929e4 | 2010-04-01 23:59:22 -0500 | [diff] [blame] | 383 | [PERF_RECORD_HEADER_BUILD_ID]   = NULL, | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 384 | [PERF_RECORD_HEADER_MAX]    = NULL, | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 385 | }; | 
|  | 386 |  | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 387 | struct sample_queue { | 
|  | 388 | u64			timestamp; | 
|  | 389 | struct sample_event	*event; | 
|  | 390 | struct list_head	list; | 
|  | 391 | }; | 
|  | 392 |  | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 393 | static void flush_sample_queue(struct perf_session *s, | 
|  | 394 | struct perf_event_ops *ops) | 
|  | 395 | { | 
|  | 396 | struct list_head *head = &s->ordered_samples.samples_head; | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 397 | u64 limit = s->ordered_samples.next_flush; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 398 | struct sample_queue *tmp, *iter; | 
|  | 399 |  | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 400 | if (!ops->ordered_samples || !limit) | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 401 | return; | 
|  | 402 |  | 
|  | 403 | list_for_each_entry_safe(iter, tmp, head, list) { | 
|  | 404 | if (iter->timestamp > limit) | 
|  | 405 | return; | 
|  | 406 |  | 
|  | 407 | if (iter == s->ordered_samples.last_inserted) | 
|  | 408 | s->ordered_samples.last_inserted = NULL; | 
|  | 409 |  | 
|  | 410 | ops->sample((event_t *)iter->event, s); | 
|  | 411 |  | 
|  | 412 | s->ordered_samples.last_flush = iter->timestamp; | 
|  | 413 | list_del(&iter->list); | 
|  | 414 | free(iter->event); | 
|  | 415 | free(iter); | 
|  | 416 | } | 
|  | 417 | } | 
|  | 418 |  | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 419 | /* | 
|  | 420 | * When perf record finishes a pass on every buffers, it records this pseudo | 
|  | 421 | * event. | 
|  | 422 | * We record the max timestamp t found in the pass n. | 
|  | 423 | * Assuming these timestamps are monotonic across cpus, we know that if | 
|  | 424 | * a buffer still has events with timestamps below t, they will be all | 
|  | 425 | * available and then read in the pass n + 1. | 
|  | 426 | * Hence when we start to read the pass n + 2, we can safely flush every | 
|  | 427 | * events with timestamps below t. | 
|  | 428 | * | 
|  | 429 | *    ============ PASS n ================= | 
|  | 430 | *       CPU 0         |   CPU 1 | 
|  | 431 | *                     | | 
|  | 432 | *    cnt1 timestamps  |   cnt2 timestamps | 
|  | 433 | *          1          |         2 | 
|  | 434 | *          2          |         3 | 
|  | 435 | *          -          |         4  <--- max recorded | 
|  | 436 | * | 
|  | 437 | *    ============ PASS n + 1 ============== | 
|  | 438 | *       CPU 0         |   CPU 1 | 
|  | 439 | *                     | | 
|  | 440 | *    cnt1 timestamps  |   cnt2 timestamps | 
|  | 441 | *          3          |         5 | 
|  | 442 | *          4          |         6 | 
|  | 443 | *          5          |         7 <---- max recorded | 
|  | 444 | * | 
|  | 445 | *      Flush every events below timestamp 4 | 
|  | 446 | * | 
|  | 447 | *    ============ PASS n + 2 ============== | 
|  | 448 | *       CPU 0         |   CPU 1 | 
|  | 449 | *                     | | 
|  | 450 | *    cnt1 timestamps  |   cnt2 timestamps | 
|  | 451 | *          6          |         8 | 
|  | 452 | *          7          |         9 | 
|  | 453 | *          -          |         10 | 
|  | 454 | * | 
|  | 455 | *      Flush every events below timestamp 7 | 
|  | 456 | *      etc... | 
|  | 457 | */ | 
|  | 458 | static int process_finished_round(event_t *event __used, | 
|  | 459 | struct perf_session *session, | 
|  | 460 | struct perf_event_ops *ops) | 
|  | 461 | { | 
|  | 462 | flush_sample_queue(session, ops); | 
|  | 463 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | 
|  | 464 |  | 
|  | 465 | return 0; | 
|  | 466 | } | 
|  | 467 |  | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 468 | static void __queue_sample_end(struct sample_queue *new, struct list_head *head) | 
|  | 469 | { | 
|  | 470 | struct sample_queue *iter; | 
|  | 471 |  | 
|  | 472 | list_for_each_entry_reverse(iter, head, list) { | 
|  | 473 | if (iter->timestamp < new->timestamp) { | 
|  | 474 | list_add(&new->list, &iter->list); | 
|  | 475 | return; | 
|  | 476 | } | 
|  | 477 | } | 
|  | 478 |  | 
|  | 479 | list_add(&new->list, head); | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | static void __queue_sample_before(struct sample_queue *new, | 
|  | 483 | struct sample_queue *iter, | 
|  | 484 | struct list_head *head) | 
|  | 485 | { | 
|  | 486 | list_for_each_entry_continue_reverse(iter, head, list) { | 
|  | 487 | if (iter->timestamp < new->timestamp) { | 
|  | 488 | list_add(&new->list, &iter->list); | 
|  | 489 | return; | 
|  | 490 | } | 
|  | 491 | } | 
|  | 492 |  | 
|  | 493 | list_add(&new->list, head); | 
|  | 494 | } | 
|  | 495 |  | 
|  | 496 | static void __queue_sample_after(struct sample_queue *new, | 
|  | 497 | struct sample_queue *iter, | 
|  | 498 | struct list_head *head) | 
|  | 499 | { | 
|  | 500 | list_for_each_entry_continue(iter, head, list) { | 
|  | 501 | if (iter->timestamp > new->timestamp) { | 
|  | 502 | list_add_tail(&new->list, &iter->list); | 
|  | 503 | return; | 
|  | 504 | } | 
|  | 505 | } | 
|  | 506 | list_add_tail(&new->list, head); | 
|  | 507 | } | 
|  | 508 |  | 
|  | 509 | /* The queue is ordered by time */ | 
|  | 510 | static void __queue_sample_event(struct sample_queue *new, | 
|  | 511 | struct perf_session *s) | 
|  | 512 | { | 
|  | 513 | struct sample_queue *last_inserted = s->ordered_samples.last_inserted; | 
|  | 514 | struct list_head *head = &s->ordered_samples.samples_head; | 
|  | 515 |  | 
|  | 516 |  | 
|  | 517 | if (!last_inserted) { | 
|  | 518 | __queue_sample_end(new, head); | 
|  | 519 | return; | 
|  | 520 | } | 
|  | 521 |  | 
|  | 522 | /* | 
|  | 523 | * Most of the time the current event has a timestamp | 
|  | 524 | * very close to the last event inserted, unless we just switched | 
|  | 525 | * to another event buffer. Having a sorting based on a list and | 
|  | 526 | * on the last inserted event that is close to the current one is | 
|  | 527 | * probably more efficient than an rbtree based sorting. | 
|  | 528 | */ | 
|  | 529 | if (last_inserted->timestamp >= new->timestamp) | 
|  | 530 | __queue_sample_before(new, last_inserted, head); | 
|  | 531 | else | 
|  | 532 | __queue_sample_after(new, last_inserted, head); | 
|  | 533 | } | 
|  | 534 |  | 
|  | 535 | static int queue_sample_event(event_t *event, struct sample_data *data, | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 536 | struct perf_session *s) | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 537 | { | 
|  | 538 | u64 timestamp = data->time; | 
|  | 539 | struct sample_queue *new; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 540 |  | 
|  | 541 |  | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 542 | if (timestamp < s->ordered_samples.last_flush) { | 
|  | 543 | printf("Warning: Timestamp below last timeslice flush\n"); | 
|  | 544 | return -EINVAL; | 
|  | 545 | } | 
|  | 546 |  | 
|  | 547 | new = malloc(sizeof(*new)); | 
|  | 548 | if (!new) | 
|  | 549 | return -ENOMEM; | 
|  | 550 |  | 
|  | 551 | new->timestamp = timestamp; | 
|  | 552 |  | 
|  | 553 | new->event = malloc(event->header.size); | 
|  | 554 | if (!new->event) { | 
|  | 555 | free(new); | 
|  | 556 | return -ENOMEM; | 
|  | 557 | } | 
|  | 558 |  | 
|  | 559 | memcpy(new->event, event, event->header.size); | 
|  | 560 |  | 
|  | 561 | __queue_sample_event(new, s); | 
|  | 562 | s->ordered_samples.last_inserted = new; | 
|  | 563 |  | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 564 | if (new->timestamp > s->ordered_samples.max_timestamp) | 
|  | 565 | s->ordered_samples.max_timestamp = new->timestamp; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 566 |  | 
|  | 567 | return 0; | 
|  | 568 | } | 
|  | 569 |  | 
|  | 570 | static int perf_session__process_sample(event_t *event, struct perf_session *s, | 
|  | 571 | struct perf_event_ops *ops) | 
|  | 572 | { | 
|  | 573 | struct sample_data data; | 
|  | 574 |  | 
|  | 575 | if (!ops->ordered_samples) | 
|  | 576 | return ops->sample(event, s); | 
|  | 577 |  | 
|  | 578 | bzero(&data, sizeof(struct sample_data)); | 
|  | 579 | event__parse_sample(event, s->sample_type, &data); | 
|  | 580 |  | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 581 | queue_sample_event(event, &data, s); | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 582 |  | 
|  | 583 | return 0; | 
|  | 584 | } | 
|  | 585 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 586 | static int perf_session__process_event(struct perf_session *self, | 
|  | 587 | event_t *event, | 
|  | 588 | struct perf_event_ops *ops, | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 589 | u64 offset, u64 head) | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 590 | { | 
|  | 591 | trace_event(event); | 
|  | 592 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 593 | if (event->header.type < PERF_RECORD_HEADER_MAX) { | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 594 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | 
| Arnaldo Carvalho de Melo | 0d75503 | 2010-01-14 12:23:09 -0200 | [diff] [blame] | 595 | offset + head, event->header.size, | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 596 | event__name[event->header.type]); | 
| Arnaldo Carvalho de Melo | cee75ac | 2010-05-14 13:16:55 -0300 | [diff] [blame] | 597 | hists__inc_nr_events(&self->hists, event->header.type); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 598 | } | 
|  | 599 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 600 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | 
|  | 601 | event__swap_ops[event->header.type](event); | 
|  | 602 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 603 | switch (event->header.type) { | 
|  | 604 | case PERF_RECORD_SAMPLE: | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 605 | return perf_session__process_sample(event, self, ops); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 606 | case PERF_RECORD_MMAP: | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 607 | return ops->mmap(event, self); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 608 | case PERF_RECORD_COMM: | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 609 | return ops->comm(event, self); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 610 | case PERF_RECORD_FORK: | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 611 | return ops->fork(event, self); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 612 | case PERF_RECORD_EXIT: | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 613 | return ops->exit(event, self); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 614 | case PERF_RECORD_LOST: | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 615 | return ops->lost(event, self); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 616 | case PERF_RECORD_READ: | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 617 | return ops->read(event, self); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 618 | case PERF_RECORD_THROTTLE: | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 619 | return ops->throttle(event, self); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 620 | case PERF_RECORD_UNTHROTTLE: | 
| Arnaldo Carvalho de Melo | 55aa640 | 2009-12-27 21:37:05 -0200 | [diff] [blame] | 621 | return ops->unthrottle(event, self); | 
| Tom Zanussi | 2c46dbb | 2010-04-01 23:59:19 -0500 | [diff] [blame] | 622 | case PERF_RECORD_HEADER_ATTR: | 
|  | 623 | return ops->attr(event, self); | 
| Tom Zanussi | cd19a03 | 2010-04-01 23:59:20 -0500 | [diff] [blame] | 624 | case PERF_RECORD_HEADER_EVENT_TYPE: | 
|  | 625 | return ops->event_type(event, self); | 
| Tom Zanussi | 9215545 | 2010-04-01 23:59:21 -0500 | [diff] [blame] | 626 | case PERF_RECORD_HEADER_TRACING_DATA: | 
|  | 627 | /* setup for reading amidst mmap */ | 
|  | 628 | lseek(self->fd, offset + head, SEEK_SET); | 
|  | 629 | return ops->tracing_data(event, self); | 
| Tom Zanussi | c7929e4 | 2010-04-01 23:59:22 -0500 | [diff] [blame] | 630 | case PERF_RECORD_HEADER_BUILD_ID: | 
|  | 631 | return ops->build_id(event, self); | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 632 | case PERF_RECORD_FINISHED_ROUND: | 
|  | 633 | return ops->finished_round(event, self, ops); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 634 | default: | 
| Arnaldo Carvalho de Melo | c8446b9 | 2010-05-14 10:36:42 -0300 | [diff] [blame] | 635 | ++self->hists.stats.nr_unknown_events; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 636 | return -1; | 
|  | 637 | } | 
|  | 638 | } | 
|  | 639 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 640 | void perf_event_header__bswap(struct perf_event_header *self) | 
|  | 641 | { | 
|  | 642 | self->type = bswap_32(self->type); | 
|  | 643 | self->misc = bswap_16(self->misc); | 
|  | 644 | self->size = bswap_16(self->size); | 
|  | 645 | } | 
|  | 646 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 647 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | 
|  | 648 | { | 
|  | 649 | struct thread *thread = perf_session__findnew(self, 0); | 
|  | 650 |  | 
|  | 651 | if (thread == NULL || thread__set_comm(thread, "swapper")) { | 
|  | 652 | pr_err("problem inserting idle task.\n"); | 
|  | 653 | thread = NULL; | 
|  | 654 | } | 
|  | 655 |  | 
|  | 656 | return thread; | 
|  | 657 | } | 
|  | 658 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 659 | int do_read(int fd, void *buf, size_t size) | 
|  | 660 | { | 
|  | 661 | void *buf_start = buf; | 
|  | 662 |  | 
|  | 663 | while (size) { | 
|  | 664 | int ret = read(fd, buf, size); | 
|  | 665 |  | 
|  | 666 | if (ret <= 0) | 
|  | 667 | return ret; | 
|  | 668 |  | 
|  | 669 | size -= ret; | 
|  | 670 | buf += ret; | 
|  | 671 | } | 
|  | 672 |  | 
|  | 673 | return buf - buf_start; | 
|  | 674 | } | 
|  | 675 |  | 
|  | 676 | #define session_done()	(*(volatile int *)(&session_done)) | 
|  | 677 | volatile int session_done; | 
|  | 678 |  | 
|  | 679 | static int __perf_session__process_pipe_events(struct perf_session *self, | 
|  | 680 | struct perf_event_ops *ops) | 
|  | 681 | { | 
|  | 682 | event_t event; | 
|  | 683 | uint32_t size; | 
|  | 684 | int skip = 0; | 
|  | 685 | u64 head; | 
|  | 686 | int err; | 
|  | 687 | void *p; | 
|  | 688 |  | 
|  | 689 | perf_event_ops__fill_defaults(ops); | 
|  | 690 |  | 
|  | 691 | head = 0; | 
|  | 692 | more: | 
|  | 693 | err = do_read(self->fd, &event, sizeof(struct perf_event_header)); | 
|  | 694 | if (err <= 0) { | 
|  | 695 | if (err == 0) | 
|  | 696 | goto done; | 
|  | 697 |  | 
|  | 698 | pr_err("failed to read event header\n"); | 
|  | 699 | goto out_err; | 
|  | 700 | } | 
|  | 701 |  | 
|  | 702 | if (self->header.needs_swap) | 
|  | 703 | perf_event_header__bswap(&event.header); | 
|  | 704 |  | 
|  | 705 | size = event.header.size; | 
|  | 706 | if (size == 0) | 
|  | 707 | size = 8; | 
|  | 708 |  | 
|  | 709 | p = &event; | 
|  | 710 | p += sizeof(struct perf_event_header); | 
|  | 711 |  | 
| Tom Zanussi | 794e43b | 2010-05-05 00:27:40 -0500 | [diff] [blame] | 712 | if (size - sizeof(struct perf_event_header)) { | 
|  | 713 | err = do_read(self->fd, p, | 
|  | 714 | size - sizeof(struct perf_event_header)); | 
|  | 715 | if (err <= 0) { | 
|  | 716 | if (err == 0) { | 
|  | 717 | pr_err("unexpected end of event stream\n"); | 
|  | 718 | goto done; | 
|  | 719 | } | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 720 |  | 
| Tom Zanussi | 794e43b | 2010-05-05 00:27:40 -0500 | [diff] [blame] | 721 | pr_err("failed to read event data\n"); | 
|  | 722 | goto out_err; | 
|  | 723 | } | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 724 | } | 
|  | 725 |  | 
|  | 726 | if (size == 0 || | 
|  | 727 | (skip = perf_session__process_event(self, &event, ops, | 
|  | 728 | 0, head)) < 0) { | 
|  | 729 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | 
|  | 730 | head, event.header.size, event.header.type); | 
|  | 731 | /* | 
|  | 732 | * assume we lost track of the stream, check alignment, and | 
|  | 733 | * increment a single u64 in the hope to catch on again 'soon'. | 
|  | 734 | */ | 
|  | 735 | if (unlikely(head & 7)) | 
|  | 736 | head &= ~7ULL; | 
|  | 737 |  | 
|  | 738 | size = 8; | 
|  | 739 | } | 
|  | 740 |  | 
|  | 741 | head += size; | 
|  | 742 |  | 
|  | 743 | dump_printf("\n%#Lx [%#x]: event: %d\n", | 
|  | 744 | head, event.header.size, event.header.type); | 
|  | 745 |  | 
|  | 746 | if (skip > 0) | 
|  | 747 | head += skip; | 
|  | 748 |  | 
|  | 749 | if (!session_done()) | 
|  | 750 | goto more; | 
|  | 751 | done: | 
|  | 752 | err = 0; | 
|  | 753 | out_err: | 
|  | 754 | return err; | 
|  | 755 | } | 
|  | 756 |  | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 757 | int __perf_session__process_events(struct perf_session *self, | 
|  | 758 | u64 data_offset, u64 data_size, | 
|  | 759 | u64 file_size, struct perf_event_ops *ops) | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 760 | { | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 761 | int err, mmap_prot, mmap_flags; | 
|  | 762 | u64 head, shift; | 
|  | 763 | u64 offset = 0; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 764 | size_t	page_size; | 
|  | 765 | event_t *event; | 
|  | 766 | uint32_t size; | 
|  | 767 | char *buf; | 
| Arnaldo Carvalho de Melo | 5f4d3f8 | 2010-03-26 21:16:22 -0300 | [diff] [blame] | 768 | struct ui_progress *progress = ui_progress__new("Processing events...", | 
|  | 769 | self->size); | 
|  | 770 | if (progress == NULL) | 
|  | 771 | return -1; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 772 |  | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 773 | perf_event_ops__fill_defaults(ops); | 
|  | 774 |  | 
| Arnaldo Carvalho de Melo | 1b75962 | 2010-01-14 18:30:04 -0200 | [diff] [blame] | 775 | page_size = sysconf(_SC_PAGESIZE); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 776 |  | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 777 | head = data_offset; | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 778 | shift = page_size * (head / page_size); | 
|  | 779 | offset += shift; | 
|  | 780 | head -= shift; | 
|  | 781 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 782 | mmap_prot  = PROT_READ; | 
|  | 783 | mmap_flags = MAP_SHARED; | 
|  | 784 |  | 
|  | 785 | if (self->header.needs_swap) { | 
|  | 786 | mmap_prot  |= PROT_WRITE; | 
|  | 787 | mmap_flags = MAP_PRIVATE; | 
|  | 788 | } | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 789 | remap: | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 790 | buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, | 
|  | 791 | mmap_flags, self->fd, offset); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 792 | if (buf == MAP_FAILED) { | 
|  | 793 | pr_err("failed to mmap file\n"); | 
|  | 794 | err = -errno; | 
|  | 795 | goto out_err; | 
|  | 796 | } | 
|  | 797 |  | 
|  | 798 | more: | 
|  | 799 | event = (event_t *)(buf + head); | 
| Arnaldo Carvalho de Melo | 5f4d3f8 | 2010-03-26 21:16:22 -0300 | [diff] [blame] | 800 | ui_progress__update(progress, offset); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 801 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 802 | if (self->header.needs_swap) | 
|  | 803 | perf_event_header__bswap(&event->header); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 804 | size = event->header.size; | 
|  | 805 | if (size == 0) | 
|  | 806 | size = 8; | 
|  | 807 |  | 
|  | 808 | if (head + event->header.size >= page_size * self->mmap_window) { | 
|  | 809 | int munmap_ret; | 
|  | 810 |  | 
|  | 811 | shift = page_size * (head / page_size); | 
|  | 812 |  | 
|  | 813 | munmap_ret = munmap(buf, page_size * self->mmap_window); | 
|  | 814 | assert(munmap_ret == 0); | 
|  | 815 |  | 
|  | 816 | offset += shift; | 
|  | 817 | head -= shift; | 
|  | 818 | goto remap; | 
|  | 819 | } | 
|  | 820 |  | 
|  | 821 | size = event->header.size; | 
|  | 822 |  | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 823 | dump_printf("\n%#Lx [%#x]: event: %d\n", | 
| Arnaldo Carvalho de Melo | 0d75503 | 2010-01-14 12:23:09 -0200 | [diff] [blame] | 824 | offset + head, event->header.size, event->header.type); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 825 |  | 
|  | 826 | if (size == 0 || | 
|  | 827 | perf_session__process_event(self, event, ops, offset, head) < 0) { | 
| Arnaldo Carvalho de Melo | ba21594 | 2010-01-14 12:23:10 -0200 | [diff] [blame] | 828 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | 
| Arnaldo Carvalho de Melo | 0d75503 | 2010-01-14 12:23:09 -0200 | [diff] [blame] | 829 | offset + head, event->header.size, | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 830 | event->header.type); | 
|  | 831 | /* | 
|  | 832 | * assume we lost track of the stream, check alignment, and | 
|  | 833 | * increment a single u64 in the hope to catch on again 'soon'. | 
|  | 834 | */ | 
|  | 835 | if (unlikely(head & 7)) | 
|  | 836 | head &= ~7ULL; | 
|  | 837 |  | 
|  | 838 | size = 8; | 
|  | 839 | } | 
|  | 840 |  | 
|  | 841 | head += size; | 
|  | 842 |  | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 843 | if (offset + head >= data_offset + data_size) | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 844 | goto done; | 
|  | 845 |  | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 846 | if (offset + head < file_size) | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 847 | goto more; | 
|  | 848 | done: | 
|  | 849 | err = 0; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 850 | /* do the final flush for ordered samples */ | 
| Frederic Weisbecker | d6b17be | 2010-05-03 15:14:33 +0200 | [diff] [blame] | 851 | self->ordered_samples.next_flush = ULLONG_MAX; | 
| Frederic Weisbecker | c61e52e | 2010-04-24 00:04:12 +0200 | [diff] [blame] | 852 | flush_sample_queue(self, ops); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 853 | out_err: | 
| Arnaldo Carvalho de Melo | 5f4d3f8 | 2010-03-26 21:16:22 -0300 | [diff] [blame] | 854 | ui_progress__delete(progress); | 
| Arnaldo Carvalho de Melo | 06aae59 | 2009-12-27 21:36:59 -0200 | [diff] [blame] | 855 | return err; | 
|  | 856 | } | 
| Arnaldo Carvalho de Melo | 2729559 | 2009-12-27 21:37:01 -0200 | [diff] [blame] | 857 |  | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 858 | int perf_session__process_events(struct perf_session *self, | 
|  | 859 | struct perf_event_ops *ops) | 
|  | 860 | { | 
|  | 861 | int err; | 
|  | 862 |  | 
|  | 863 | if (perf_session__register_idle_thread(self) == NULL) | 
|  | 864 | return -ENOMEM; | 
|  | 865 |  | 
| Tom Zanussi | 8dc5810 | 2010-04-01 23:59:15 -0500 | [diff] [blame] | 866 | if (!self->fd_pipe) | 
|  | 867 | err = __perf_session__process_events(self, | 
|  | 868 | self->header.data_offset, | 
|  | 869 | self->header.data_size, | 
|  | 870 | self->size, ops); | 
|  | 871 | else | 
|  | 872 | err = __perf_session__process_pipe_events(self, ops); | 
| Dave Martin | 88ca895 | 2010-07-27 11:46:12 -0300 | [diff] [blame] | 873 |  | 
| Arnaldo Carvalho de Melo | 6122e4e | 2010-02-03 16:52:05 -0200 | [diff] [blame] | 874 | return err; | 
|  | 875 | } | 
|  | 876 |  | 
| Arnaldo Carvalho de Melo | d549c76 | 2009-12-27 21:37:02 -0200 | [diff] [blame] | 877 | 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] | 878 | { | 
|  | 879 | if (!(self->sample_type & PERF_SAMPLE_RAW)) { | 
| Arnaldo Carvalho de Melo | d549c76 | 2009-12-27 21:37:02 -0200 | [diff] [blame] | 880 | pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); | 
|  | 881 | return false; | 
| Arnaldo Carvalho de Melo | 2729559 | 2009-12-27 21:37:01 -0200 | [diff] [blame] | 882 | } | 
|  | 883 |  | 
| Arnaldo Carvalho de Melo | d549c76 | 2009-12-27 21:37:02 -0200 | [diff] [blame] | 884 | return true; | 
| Arnaldo Carvalho de Melo | 2729559 | 2009-12-27 21:37:01 -0200 | [diff] [blame] | 885 | } | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 886 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 887 | 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] | 888 | const char *symbol_name, | 
|  | 889 | u64 addr) | 
|  | 890 | { | 
|  | 891 | char *bracket; | 
| Arnaldo Carvalho de Melo | 9de89fe | 2010-02-03 16:52:00 -0200 | [diff] [blame] | 892 | enum map_type i; | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 893 | struct ref_reloc_sym *ref; | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 894 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 895 | ref = zalloc(sizeof(struct ref_reloc_sym)); | 
|  | 896 | if (ref == NULL) | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 897 | return -ENOMEM; | 
|  | 898 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 899 | ref->name = strdup(symbol_name); | 
|  | 900 | if (ref->name == NULL) { | 
|  | 901 | free(ref); | 
|  | 902 | return -ENOMEM; | 
|  | 903 | } | 
|  | 904 |  | 
|  | 905 | bracket = strchr(ref->name, ']'); | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 906 | if (bracket) | 
|  | 907 | *bracket = '\0'; | 
|  | 908 |  | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 909 | ref->addr = addr; | 
| Arnaldo Carvalho de Melo | 9de89fe | 2010-02-03 16:52:00 -0200 | [diff] [blame] | 910 |  | 
|  | 911 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 
| Zhang, Yanmin | a1645ce | 2010-04-19 13:32:50 +0800 | [diff] [blame] | 912 | struct kmap *kmap = map__kmap(maps[i]); | 
|  | 913 | kmap->ref_reloc_sym = ref; | 
| Arnaldo Carvalho de Melo | 9de89fe | 2010-02-03 16:52:00 -0200 | [diff] [blame] | 914 | } | 
|  | 915 |  | 
| Arnaldo Carvalho de Melo | 56b03f3 | 2010-01-05 16:50:31 -0200 | [diff] [blame] | 916 | return 0; | 
|  | 917 | } | 
| Arnaldo Carvalho de Melo | 1f626bc | 2010-05-09 19:57:08 -0300 | [diff] [blame] | 918 |  | 
|  | 919 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) | 
|  | 920 | { | 
|  | 921 | return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) + | 
|  | 922 | __dsos__fprintf(&self->host_machine.user_dsos, fp) + | 
|  | 923 | machines__fprintf_dsos(&self->machines, fp); | 
|  | 924 | } | 
| Arnaldo Carvalho de Melo | f869097 | 2010-05-19 13:41:23 -0300 | [diff] [blame] | 925 |  | 
|  | 926 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, | 
|  | 927 | bool with_hits) | 
|  | 928 | { | 
|  | 929 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); | 
|  | 930 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); | 
|  | 931 | } |