diff options
36 files changed, 1647 insertions, 1022 deletions
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 86b797a35aa6..fcc51fe0195c 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
| @@ -73,6 +73,10 @@ OPTIONS | |||
| 73 | (Only for --vars) Show external defined variables in addition to local | 73 | (Only for --vars) Show external defined variables in addition to local |
| 74 | variables. | 74 | variables. |
| 75 | 75 | ||
| 76 | -F:: | ||
| 77 | --funcs:: | ||
| 78 | Show available functions in given module or kernel. | ||
| 79 | |||
| 76 | -f:: | 80 | -f:: |
| 77 | --force:: | 81 | --force:: |
| 78 | Forcibly add events with existing name. | 82 | Forcibly add events with existing name. |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7141c42e1469..638e8e146bb9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -402,6 +402,7 @@ LIB_H += util/debug.h | |||
| 402 | LIB_H += util/debugfs.h | 402 | LIB_H += util/debugfs.h |
| 403 | LIB_H += util/event.h | 403 | LIB_H += util/event.h |
| 404 | LIB_H += util/evsel.h | 404 | LIB_H += util/evsel.h |
| 405 | LIB_H += util/evlist.h | ||
| 405 | LIB_H += util/exec_cmd.h | 406 | LIB_H += util/exec_cmd.h |
| 406 | LIB_H += util/types.h | 407 | LIB_H += util/types.h |
| 407 | LIB_H += util/levenshtein.h | 408 | LIB_H += util/levenshtein.h |
| @@ -425,6 +426,7 @@ LIB_H += util/values.h | |||
| 425 | LIB_H += util/sort.h | 426 | LIB_H += util/sort.h |
| 426 | LIB_H += util/hist.h | 427 | LIB_H += util/hist.h |
| 427 | LIB_H += util/thread.h | 428 | LIB_H += util/thread.h |
| 429 | LIB_H += util/thread_map.h | ||
| 428 | LIB_H += util/trace-event.h | 430 | LIB_H += util/trace-event.h |
| 429 | LIB_H += util/probe-finder.h | 431 | LIB_H += util/probe-finder.h |
| 430 | LIB_H += util/probe-event.h | 432 | LIB_H += util/probe-event.h |
| @@ -440,6 +442,7 @@ LIB_OBJS += $(OUTPUT)util/ctype.o | |||
| 440 | LIB_OBJS += $(OUTPUT)util/debugfs.o | 442 | LIB_OBJS += $(OUTPUT)util/debugfs.o |
| 441 | LIB_OBJS += $(OUTPUT)util/environment.o | 443 | LIB_OBJS += $(OUTPUT)util/environment.o |
| 442 | LIB_OBJS += $(OUTPUT)util/event.o | 444 | LIB_OBJS += $(OUTPUT)util/event.o |
| 445 | LIB_OBJS += $(OUTPUT)util/evlist.o | ||
| 443 | LIB_OBJS += $(OUTPUT)util/evsel.o | 446 | LIB_OBJS += $(OUTPUT)util/evsel.o |
| 444 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o | 447 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o |
| 445 | LIB_OBJS += $(OUTPUT)util/help.o | 448 | LIB_OBJS += $(OUTPUT)util/help.o |
| @@ -469,6 +472,7 @@ LIB_OBJS += $(OUTPUT)util/map.o | |||
| 469 | LIB_OBJS += $(OUTPUT)util/pstack.o | 472 | LIB_OBJS += $(OUTPUT)util/pstack.o |
| 470 | LIB_OBJS += $(OUTPUT)util/session.o | 473 | LIB_OBJS += $(OUTPUT)util/session.o |
| 471 | LIB_OBJS += $(OUTPUT)util/thread.o | 474 | LIB_OBJS += $(OUTPUT)util/thread.o |
| 475 | LIB_OBJS += $(OUTPUT)util/thread_map.o | ||
| 472 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o | 476 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o |
| 473 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o | 477 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o |
| 474 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o | 478 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index add163c9f0e7..6cf708aba7c9 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
| @@ -52,6 +52,7 @@ static struct { | |||
| 52 | bool show_lines; | 52 | bool show_lines; |
| 53 | bool show_vars; | 53 | bool show_vars; |
| 54 | bool show_ext_vars; | 54 | bool show_ext_vars; |
| 55 | bool show_funcs; | ||
| 55 | bool mod_events; | 56 | bool mod_events; |
| 56 | int nevents; | 57 | int nevents; |
| 57 | struct perf_probe_event events[MAX_PROBES]; | 58 | struct perf_probe_event events[MAX_PROBES]; |
| @@ -221,6 +222,8 @@ static const struct option options[] = { | |||
| 221 | OPT__DRY_RUN(&probe_event_dry_run), | 222 | OPT__DRY_RUN(&probe_event_dry_run), |
| 222 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, | 223 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
| 223 | "Set how many probe points can be found for a probe."), | 224 | "Set how many probe points can be found for a probe."), |
| 225 | OPT_BOOLEAN('F', "funcs", ¶ms.show_funcs, | ||
| 226 | "Show potential probe-able functions."), | ||
| 224 | OPT_END() | 227 | OPT_END() |
| 225 | }; | 228 | }; |
| 226 | 229 | ||
| @@ -246,7 +249,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
| 246 | params.max_probe_points = MAX_PROBES; | 249 | params.max_probe_points = MAX_PROBES; |
| 247 | 250 | ||
| 248 | if ((!params.nevents && !params.dellist && !params.list_events && | 251 | if ((!params.nevents && !params.dellist && !params.list_events && |
| 249 | !params.show_lines)) | 252 | !params.show_lines && !params.show_funcs)) |
| 250 | usage_with_options(probe_usage, options); | 253 | usage_with_options(probe_usage, options); |
| 251 | 254 | ||
| 252 | /* | 255 | /* |
| @@ -267,12 +270,36 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
| 267 | pr_err(" Error: Don't use --list with --vars.\n"); | 270 | pr_err(" Error: Don't use --list with --vars.\n"); |
| 268 | usage_with_options(probe_usage, options); | 271 | usage_with_options(probe_usage, options); |
| 269 | } | 272 | } |
| 273 | if (params.show_funcs) { | ||
| 274 | pr_err(" Error: Don't use --list with --funcs.\n"); | ||
| 275 | usage_with_options(probe_usage, options); | ||
| 276 | } | ||
| 270 | ret = show_perf_probe_events(); | 277 | ret = show_perf_probe_events(); |
| 271 | if (ret < 0) | 278 | if (ret < 0) |
| 272 | pr_err(" Error: Failed to show event list. (%d)\n", | 279 | pr_err(" Error: Failed to show event list. (%d)\n", |
| 273 | ret); | 280 | ret); |
| 274 | return ret; | 281 | return ret; |
| 275 | } | 282 | } |
| 283 | if (params.show_funcs) { | ||
| 284 | if (params.nevents != 0 || params.dellist) { | ||
| 285 | pr_err(" Error: Don't use --funcs with" | ||
| 286 | " --add/--del.\n"); | ||
| 287 | usage_with_options(probe_usage, options); | ||
| 288 | } | ||
| 289 | if (params.show_lines) { | ||
| 290 | pr_err(" Error: Don't use --funcs with --line.\n"); | ||
| 291 | usage_with_options(probe_usage, options); | ||
| 292 | } | ||
| 293 | if (params.show_vars) { | ||
| 294 | pr_err(" Error: Don't use --funcs with --vars.\n"); | ||
| 295 | usage_with_options(probe_usage, options); | ||
| 296 | } | ||
| 297 | ret = show_available_funcs(params.target_module); | ||
| 298 | if (ret < 0) | ||
| 299 | pr_err(" Error: Failed to show functions." | ||
| 300 | " (%d)\n", ret); | ||
| 301 | return ret; | ||
| 302 | } | ||
| 276 | 303 | ||
| 277 | #ifdef DWARF_SUPPORT | 304 | #ifdef DWARF_SUPPORT |
| 278 | if (params.show_lines) { | 305 | if (params.show_lines) { |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b2f729fdb317..d7886307f6f4 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -18,17 +18,20 @@ | |||
| 18 | 18 | ||
| 19 | #include "util/header.h" | 19 | #include "util/header.h" |
| 20 | #include "util/event.h" | 20 | #include "util/event.h" |
| 21 | #include "util/evlist.h" | ||
| 21 | #include "util/evsel.h" | 22 | #include "util/evsel.h" |
| 22 | #include "util/debug.h" | 23 | #include "util/debug.h" |
| 23 | #include "util/session.h" | 24 | #include "util/session.h" |
| 24 | #include "util/symbol.h" | 25 | #include "util/symbol.h" |
| 25 | #include "util/cpumap.h" | 26 | #include "util/cpumap.h" |
| 27 | #include "util/thread_map.h" | ||
| 26 | 28 | ||
| 27 | #include <unistd.h> | 29 | #include <unistd.h> |
| 28 | #include <sched.h> | 30 | #include <sched.h> |
| 29 | #include <sys/mman.h> | 31 | #include <sys/mman.h> |
| 30 | 32 | ||
| 31 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 33 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
| 34 | #define SID(e, x, y) xyarray__entry(e->id, x, y) | ||
| 32 | 35 | ||
| 33 | enum write_mode_t { | 36 | enum write_mode_t { |
| 34 | WRITE_FORCE, | 37 | WRITE_FORCE, |
| @@ -46,7 +49,7 @@ static unsigned int user_freq = UINT_MAX; | |||
| 46 | static int freq = 1000; | 49 | static int freq = 1000; |
| 47 | static int output; | 50 | static int output; |
| 48 | static int pipe_output = 0; | 51 | static int pipe_output = 0; |
| 49 | static const char *output_name = "perf.data"; | 52 | static const char *output_name = NULL; |
| 50 | static int group = 0; | 53 | static int group = 0; |
| 51 | static int realtime_prio = 0; | 54 | static int realtime_prio = 0; |
| 52 | static bool nodelay = false; | 55 | static bool nodelay = false; |
| @@ -66,51 +69,17 @@ static bool sample_address = false; | |||
| 66 | static bool sample_time = false; | 69 | static bool sample_time = false; |
| 67 | static bool no_buildid = false; | 70 | static bool no_buildid = false; |
| 68 | static bool no_buildid_cache = false; | 71 | static bool no_buildid_cache = false; |
| 72 | static struct perf_evlist *evsel_list; | ||
| 69 | 73 | ||
| 70 | static long samples = 0; | 74 | static long samples = 0; |
| 71 | static u64 bytes_written = 0; | 75 | static u64 bytes_written = 0; |
| 72 | 76 | ||
| 73 | static struct pollfd *event_array; | ||
| 74 | |||
| 75 | static int nr_poll = 0; | ||
| 76 | static int nr_cpu = 0; | ||
| 77 | |||
| 78 | static int file_new = 1; | 77 | static int file_new = 1; |
| 79 | static off_t post_processing_offset; | 78 | static off_t post_processing_offset; |
| 80 | 79 | ||
| 81 | static struct perf_session *session; | 80 | static struct perf_session *session; |
| 82 | static const char *cpu_list; | 81 | static const char *cpu_list; |
| 83 | 82 | ||
| 84 | struct mmap_data { | ||
| 85 | void *base; | ||
| 86 | unsigned int mask; | ||
| 87 | unsigned int prev; | ||
| 88 | }; | ||
| 89 | |||
| 90 | static struct mmap_data mmap_array[MAX_NR_CPUS]; | ||
| 91 | |||
| 92 | static unsigned long mmap_read_head(struct mmap_data *md) | ||
| 93 | { | ||
| 94 | struct perf_event_mmap_page *pc = md->base; | ||
| 95 | long head; | ||
| 96 | |||
| 97 | head = pc->data_head; | ||
| 98 | rmb(); | ||
| 99 | |||
| 100 | return head; | ||
| 101 | } | ||
| 102 | |||
| 103 | static void mmap_write_tail(struct mmap_data *md, unsigned long tail) | ||
| 104 | { | ||
| 105 | struct perf_event_mmap_page *pc = md->base; | ||
| 106 | |||
| 107 | /* | ||
| 108 | * ensure all reads are done before we write the tail out. | ||
| 109 | */ | ||
| 110 | /* mb(); */ | ||
| 111 | pc->data_tail = tail; | ||
| 112 | } | ||
| 113 | |||
| 114 | static void advance_output(size_t size) | 83 | static void advance_output(size_t size) |
| 115 | { | 84 | { |
| 116 | bytes_written += size; | 85 | bytes_written += size; |
| @@ -139,9 +108,9 @@ static int process_synthesized_event(event_t *event, | |||
| 139 | return 0; | 108 | return 0; |
| 140 | } | 109 | } |
| 141 | 110 | ||
| 142 | static void mmap_read(struct mmap_data *md) | 111 | static void mmap_read(struct perf_mmap *md) |
| 143 | { | 112 | { |
| 144 | unsigned int head = mmap_read_head(md); | 113 | unsigned int head = perf_mmap__read_head(md); |
| 145 | unsigned int old = md->prev; | 114 | unsigned int old = md->prev; |
| 146 | unsigned char *data = md->base + page_size; | 115 | unsigned char *data = md->base + page_size; |
| 147 | unsigned long size; | 116 | unsigned long size; |
| @@ -185,7 +154,7 @@ static void mmap_read(struct mmap_data *md) | |||
| 185 | write_output(buf, size); | 154 | write_output(buf, size); |
| 186 | 155 | ||
| 187 | md->prev = old; | 156 | md->prev = old; |
| 188 | mmap_write_tail(md, old); | 157 | perf_mmap__write_tail(md, old); |
| 189 | } | 158 | } |
| 190 | 159 | ||
| 191 | static volatile int done = 0; | 160 | static volatile int done = 0; |
| @@ -209,8 +178,6 @@ static void sig_atexit(void) | |||
| 209 | kill(getpid(), signr); | 178 | kill(getpid(), signr); |
| 210 | } | 179 | } |
| 211 | 180 | ||
| 212 | static int group_fd; | ||
| 213 | |||
| 214 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) | 181 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) |
| 215 | { | 182 | { |
| 216 | struct perf_header_attr *h_attr; | 183 | struct perf_header_attr *h_attr; |
| @@ -234,28 +201,47 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
| 234 | char *filter = evsel->filter; | 201 | char *filter = evsel->filter; |
| 235 | struct perf_event_attr *attr = &evsel->attr; | 202 | struct perf_event_attr *attr = &evsel->attr; |
| 236 | struct perf_header_attr *h_attr; | 203 | struct perf_header_attr *h_attr; |
| 237 | int track = !evsel->idx; /* only the first counter needs these */ | 204 | struct perf_sample_id *sid; |
| 238 | int thread_index; | 205 | int thread_index; |
| 239 | int ret; | 206 | int ret; |
| 240 | struct { | 207 | |
| 241 | u64 count; | 208 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { |
| 242 | u64 time_enabled; | 209 | h_attr = get_header_attr(attr, evsel->idx); |
| 243 | u64 time_running; | 210 | if (h_attr == NULL) |
| 244 | u64 id; | 211 | die("nomem\n"); |
| 245 | } read_data; | 212 | |
| 246 | /* | 213 | if (!file_new) { |
| 247 | * Check if parse_single_tracepoint_event has already asked for | 214 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { |
| 248 | * PERF_SAMPLE_TIME. | 215 | fprintf(stderr, "incompatible append\n"); |
| 249 | * | 216 | exit(-1); |
| 250 | * XXX this is kludgy but short term fix for problems introduced by | 217 | } |
| 251 | * eac23d1c that broke 'perf script' by having different sample_types | 218 | } |
| 252 | * when using multiple tracepoint events when we use a perf binary | 219 | |
| 253 | * that tries to use sample_id_all on an older kernel. | 220 | sid = SID(evsel, cpu, thread_index); |
| 254 | * | 221 | if (perf_header_attr__add_id(h_attr, sid->id) < 0) { |
| 255 | * We need to move counter creation to perf_session, support | 222 | pr_warning("Not enough memory to add id\n"); |
| 256 | * different sample_types, etc. | 223 | exit(-1); |
| 257 | */ | 224 | } |
| 258 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | 225 | |
| 226 | if (filter != NULL) { | ||
| 227 | ret = ioctl(FD(evsel, cpu, thread_index), | ||
| 228 | PERF_EVENT_IOC_SET_FILTER, filter); | ||
| 229 | if (ret) { | ||
| 230 | error("failed to set filter with %d (%s)\n", errno, | ||
| 231 | strerror(errno)); | ||
| 232 | exit(-1); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | if (!sample_type) | ||
| 238 | sample_type = attr->sample_type; | ||
| 239 | } | ||
| 240 | |||
| 241 | static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) | ||
| 242 | { | ||
| 243 | struct perf_event_attr *attr = &evsel->attr; | ||
| 244 | int track = !evsel->idx; /* only the first counter needs these */ | ||
| 259 | 245 | ||
| 260 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 246 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
| 261 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 247 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
| @@ -263,7 +249,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
| 263 | 249 | ||
| 264 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 250 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 265 | 251 | ||
| 266 | if (nr_counters > 1) | 252 | if (evlist->nr_entries > 1) |
| 267 | attr->sample_type |= PERF_SAMPLE_ID; | 253 | attr->sample_type |= PERF_SAMPLE_ID; |
| 268 | 254 | ||
| 269 | /* | 255 | /* |
| @@ -315,19 +301,39 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
| 315 | 301 | ||
| 316 | attr->mmap = track; | 302 | attr->mmap = track; |
| 317 | attr->comm = track; | 303 | attr->comm = track; |
| 318 | attr->inherit = !no_inherit; | 304 | |
| 319 | if (target_pid == -1 && target_tid == -1 && !system_wide) { | 305 | if (target_pid == -1 && target_tid == -1 && !system_wide) { |
| 320 | attr->disabled = 1; | 306 | attr->disabled = 1; |
| 321 | attr->enable_on_exec = 1; | 307 | attr->enable_on_exec = 1; |
| 322 | } | 308 | } |
| 323 | retry_sample_id: | 309 | } |
| 324 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
| 325 | 310 | ||
| 326 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { | 311 | static void open_counters(struct perf_evlist *evlist) |
| 327 | try_again: | 312 | { |
| 328 | FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); | 313 | struct perf_evsel *pos; |
| 314 | int cpu; | ||
| 329 | 315 | ||
| 330 | if (FD(evsel, nr_cpu, thread_index) < 0) { | 316 | list_for_each_entry(pos, &evlist->entries, node) { |
| 317 | struct perf_event_attr *attr = &pos->attr; | ||
| 318 | /* | ||
| 319 | * Check if parse_single_tracepoint_event has already asked for | ||
| 320 | * PERF_SAMPLE_TIME. | ||
| 321 | * | ||
| 322 | * XXX this is kludgy but short term fix for problems introduced by | ||
| 323 | * eac23d1c that broke 'perf script' by having different sample_types | ||
| 324 | * when using multiple tracepoint events when we use a perf binary | ||
| 325 | * that tries to use sample_id_all on an older kernel. | ||
| 326 | * | ||
| 327 | * We need to move counter creation to perf_session, support | ||
| 328 | * different sample_types, etc. | ||
| 329 | */ | ||
| 330 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
| 331 | |||
| 332 | config_attr(pos, evlist); | ||
| 333 | retry_sample_id: | ||
| 334 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
| 335 | try_again: | ||
| 336 | if (perf_evsel__open(pos, cpus, threads, group, !no_inherit) < 0) { | ||
| 331 | int err = errno; | 337 | int err = errno; |
| 332 | 338 | ||
| 333 | if (err == EPERM || err == EACCES) | 339 | if (err == EPERM || err == EACCES) |
| @@ -364,7 +370,7 @@ try_again: | |||
| 364 | } | 370 | } |
| 365 | printf("\n"); | 371 | printf("\n"); |
| 366 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 372 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", |
| 367 | FD(evsel, nr_cpu, thread_index), strerror(err)); | 373 | err, strerror(err)); |
| 368 | 374 | ||
| 369 | #if defined(__i386__) || defined(__x86_64__) | 375 | #if defined(__i386__) || defined(__x86_64__) |
| 370 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 376 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
| @@ -375,90 +381,16 @@ try_again: | |||
| 375 | #endif | 381 | #endif |
| 376 | 382 | ||
| 377 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 383 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
| 378 | exit(-1); | ||
| 379 | } | ||
| 380 | |||
| 381 | h_attr = get_header_attr(attr, evsel->idx); | ||
| 382 | if (h_attr == NULL) | ||
| 383 | die("nomem\n"); | ||
| 384 | |||
| 385 | if (!file_new) { | ||
| 386 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | ||
| 387 | fprintf(stderr, "incompatible append\n"); | ||
| 388 | exit(-1); | ||
| 389 | } | ||
| 390 | } | ||
| 391 | |||
| 392 | if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { | ||
| 393 | perror("Unable to read perf file descriptor"); | ||
| 394 | exit(-1); | ||
| 395 | } | ||
| 396 | |||
| 397 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { | ||
| 398 | pr_warning("Not enough memory to add id\n"); | ||
| 399 | exit(-1); | ||
| 400 | } | ||
| 401 | |||
| 402 | assert(FD(evsel, nr_cpu, thread_index) >= 0); | ||
| 403 | fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK); | ||
| 404 | |||
| 405 | /* | ||
| 406 | * First counter acts as the group leader: | ||
| 407 | */ | ||
| 408 | if (group && group_fd == -1) | ||
| 409 | group_fd = FD(evsel, nr_cpu, thread_index); | ||
| 410 | |||
| 411 | if (evsel->idx || thread_index) { | ||
| 412 | struct perf_evsel *first; | ||
| 413 | first = list_entry(evsel_list.next, struct perf_evsel, node); | ||
| 414 | ret = ioctl(FD(evsel, nr_cpu, thread_index), | ||
| 415 | PERF_EVENT_IOC_SET_OUTPUT, | ||
| 416 | FD(first, nr_cpu, 0)); | ||
| 417 | if (ret) { | ||
| 418 | error("failed to set output: %d (%s)\n", errno, | ||
| 419 | strerror(errno)); | ||
| 420 | exit(-1); | ||
| 421 | } | ||
| 422 | } else { | ||
| 423 | mmap_array[nr_cpu].prev = 0; | ||
| 424 | mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; | ||
| 425 | mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
| 426 | PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0); | ||
| 427 | if (mmap_array[nr_cpu].base == MAP_FAILED) { | ||
| 428 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
| 429 | exit(-1); | ||
| 430 | } | ||
| 431 | |||
| 432 | event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index); | ||
| 433 | event_array[nr_poll].events = POLLIN; | ||
| 434 | nr_poll++; | ||
| 435 | } | ||
| 436 | |||
| 437 | if (filter != NULL) { | ||
| 438 | ret = ioctl(FD(evsel, nr_cpu, thread_index), | ||
| 439 | PERF_EVENT_IOC_SET_FILTER, filter); | ||
| 440 | if (ret) { | ||
| 441 | error("failed to set filter with %d (%s)\n", errno, | ||
| 442 | strerror(errno)); | ||
| 443 | exit(-1); | ||
| 444 | } | ||
| 445 | } | 384 | } |
| 446 | } | 385 | } |
| 447 | 386 | ||
| 448 | if (!sample_type) | 387 | if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, false) < 0) |
| 449 | sample_type = attr->sample_type; | 388 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
| 450 | } | ||
| 451 | |||
| 452 | static void open_counters(int cpu) | ||
| 453 | { | ||
| 454 | struct perf_evsel *pos; | ||
| 455 | |||
| 456 | group_fd = -1; | ||
| 457 | |||
| 458 | list_for_each_entry(pos, &evsel_list, node) | ||
| 459 | create_counter(pos, cpu); | ||
| 460 | 389 | ||
| 461 | nr_cpu++; | 390 | for (cpu = 0; cpu < cpus->nr; ++cpu) { |
| 391 | list_for_each_entry(pos, &evlist->entries, node) | ||
| 392 | create_counter(pos, cpu); | ||
| 393 | } | ||
| 462 | } | 394 | } |
| 463 | 395 | ||
| 464 | static int process_buildids(void) | 396 | static int process_buildids(void) |
| @@ -481,9 +413,9 @@ static void atexit_header(void) | |||
| 481 | 413 | ||
| 482 | if (!no_buildid) | 414 | if (!no_buildid) |
| 483 | process_buildids(); | 415 | process_buildids(); |
| 484 | perf_header__write(&session->header, output, true); | 416 | perf_header__write(&session->header, evsel_list, output, true); |
| 485 | perf_session__delete(session); | 417 | perf_session__delete(session); |
| 486 | perf_evsel_list__delete(); | 418 | perf_evlist__delete(evsel_list); |
| 487 | symbol__exit(); | 419 | symbol__exit(); |
| 488 | } | 420 | } |
| 489 | } | 421 | } |
| @@ -533,9 +465,9 @@ static void mmap_read_all(void) | |||
| 533 | { | 465 | { |
| 534 | int i; | 466 | int i; |
| 535 | 467 | ||
| 536 | for (i = 0; i < nr_cpu; i++) { | 468 | for (i = 0; i < cpus->nr; i++) { |
| 537 | if (mmap_array[i].base) | 469 | if (evsel_list->mmap[i].base) |
| 538 | mmap_read(&mmap_array[i]); | 470 | mmap_read(&evsel_list->mmap[i]); |
| 539 | } | 471 | } |
| 540 | 472 | ||
| 541 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) | 473 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) |
| @@ -566,18 +498,26 @@ static int __cmd_record(int argc, const char **argv) | |||
| 566 | exit(-1); | 498 | exit(-1); |
| 567 | } | 499 | } |
| 568 | 500 | ||
| 569 | if (!strcmp(output_name, "-")) | 501 | if (!output_name) { |
| 570 | pipe_output = 1; | 502 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) |
| 571 | else if (!stat(output_name, &st) && st.st_size) { | 503 | pipe_output = 1; |
| 572 | if (write_mode == WRITE_FORCE) { | 504 | else |
| 573 | char oldname[PATH_MAX]; | 505 | output_name = "perf.data"; |
| 574 | snprintf(oldname, sizeof(oldname), "%s.old", | 506 | } |
| 575 | output_name); | 507 | if (output_name) { |
| 576 | unlink(oldname); | 508 | if (!strcmp(output_name, "-")) |
| 577 | rename(output_name, oldname); | 509 | pipe_output = 1; |
| 510 | else if (!stat(output_name, &st) && st.st_size) { | ||
| 511 | if (write_mode == WRITE_FORCE) { | ||
| 512 | char oldname[PATH_MAX]; | ||
| 513 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
| 514 | output_name); | ||
| 515 | unlink(oldname); | ||
| 516 | rename(output_name, oldname); | ||
| 517 | } | ||
| 518 | } else if (write_mode == WRITE_APPEND) { | ||
| 519 | write_mode = WRITE_FORCE; | ||
| 578 | } | 520 | } |
| 579 | } else if (write_mode == WRITE_APPEND) { | ||
| 580 | write_mode = WRITE_FORCE; | ||
| 581 | } | 521 | } |
| 582 | 522 | ||
| 583 | flags = O_CREAT|O_RDWR; | 523 | flags = O_CREAT|O_RDWR; |
| @@ -611,7 +551,7 @@ static int __cmd_record(int argc, const char **argv) | |||
| 611 | goto out_delete_session; | 551 | goto out_delete_session; |
| 612 | } | 552 | } |
| 613 | 553 | ||
| 614 | if (have_tracepoints(&evsel_list)) | 554 | if (have_tracepoints(&evsel_list->entries)) |
| 615 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 555 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
| 616 | 556 | ||
| 617 | /* | 557 | /* |
| @@ -673,12 +613,7 @@ static int __cmd_record(int argc, const char **argv) | |||
| 673 | close(child_ready_pipe[0]); | 613 | close(child_ready_pipe[0]); |
| 674 | } | 614 | } |
| 675 | 615 | ||
| 676 | if (!system_wide && no_inherit && !cpu_list) { | 616 | open_counters(evsel_list); |
| 677 | open_counters(-1); | ||
| 678 | } else { | ||
| 679 | for (i = 0; i < cpus->nr; i++) | ||
| 680 | open_counters(cpus->map[i]); | ||
| 681 | } | ||
| 682 | 617 | ||
| 683 | perf_session__set_sample_type(session, sample_type); | 618 | perf_session__set_sample_type(session, sample_type); |
| 684 | 619 | ||
| @@ -687,7 +622,8 @@ static int __cmd_record(int argc, const char **argv) | |||
| 687 | if (err < 0) | 622 | if (err < 0) |
| 688 | return err; | 623 | return err; |
| 689 | } else if (file_new) { | 624 | } else if (file_new) { |
| 690 | err = perf_header__write(&session->header, output, false); | 625 | err = perf_header__write(&session->header, evsel_list, |
| 626 | output, false); | ||
| 691 | if (err < 0) | 627 | if (err < 0) |
| 692 | return err; | 628 | return err; |
| 693 | } | 629 | } |
| @@ -712,7 +648,7 @@ static int __cmd_record(int argc, const char **argv) | |||
| 712 | return err; | 648 | return err; |
| 713 | } | 649 | } |
| 714 | 650 | ||
| 715 | if (have_tracepoints(&evsel_list)) { | 651 | if (have_tracepoints(&evsel_list->entries)) { |
| 716 | /* | 652 | /* |
| 717 | * FIXME err <= 0 here actually means that | 653 | * FIXME err <= 0 here actually means that |
| 718 | * there were no tracepoints so its not really | 654 | * there were no tracepoints so its not really |
| @@ -721,7 +657,7 @@ static int __cmd_record(int argc, const char **argv) | |||
| 721 | * return this more properly and also | 657 | * return this more properly and also |
| 722 | * propagate errors that now are calling die() | 658 | * propagate errors that now are calling die() |
| 723 | */ | 659 | */ |
| 724 | err = event__synthesize_tracing_data(output, &evsel_list, | 660 | err = event__synthesize_tracing_data(output, evsel_list, |
| 725 | process_synthesized_event, | 661 | process_synthesized_event, |
| 726 | session); | 662 | session); |
| 727 | if (err <= 0) { | 663 | if (err <= 0) { |
| @@ -789,15 +725,15 @@ static int __cmd_record(int argc, const char **argv) | |||
| 789 | if (hits == samples) { | 725 | if (hits == samples) { |
| 790 | if (done) | 726 | if (done) |
| 791 | break; | 727 | break; |
| 792 | err = poll(event_array, nr_poll, -1); | 728 | err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); |
| 793 | waking++; | 729 | waking++; |
| 794 | } | 730 | } |
| 795 | 731 | ||
| 796 | if (done) { | 732 | if (done) { |
| 797 | for (i = 0; i < nr_cpu; i++) { | 733 | for (i = 0; i < cpus->nr; i++) { |
| 798 | struct perf_evsel *pos; | 734 | struct perf_evsel *pos; |
| 799 | 735 | ||
| 800 | list_for_each_entry(pos, &evsel_list, node) { | 736 | list_for_each_entry(pos, &evsel_list->entries, node) { |
| 801 | for (thread = 0; | 737 | for (thread = 0; |
| 802 | thread < threads->nr; | 738 | thread < threads->nr; |
| 803 | thread++) | 739 | thread++) |
| @@ -838,10 +774,10 @@ static const char * const record_usage[] = { | |||
| 838 | static bool force, append_file; | 774 | static bool force, append_file; |
| 839 | 775 | ||
| 840 | const struct option record_options[] = { | 776 | const struct option record_options[] = { |
| 841 | OPT_CALLBACK('e', "event", NULL, "event", | 777 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
| 842 | "event selector. use 'perf list' to list available events", | 778 | "event selector. use 'perf list' to list available events", |
| 843 | parse_events), | 779 | parse_events), |
| 844 | OPT_CALLBACK(0, "filter", NULL, "filter", | 780 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
| 845 | "event filter", parse_filter), | 781 | "event filter", parse_filter), |
| 846 | OPT_INTEGER('p', "pid", &target_pid, | 782 | OPT_INTEGER('p', "pid", &target_pid, |
| 847 | "record events on existing process id"), | 783 | "record events on existing process id"), |
| @@ -892,6 +828,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
| 892 | int err = -ENOMEM; | 828 | int err = -ENOMEM; |
| 893 | struct perf_evsel *pos; | 829 | struct perf_evsel *pos; |
| 894 | 830 | ||
| 831 | evsel_list = perf_evlist__new(); | ||
| 832 | if (evsel_list == NULL) | ||
| 833 | return -ENOMEM; | ||
| 834 | |||
| 895 | argc = parse_options(argc, argv, record_options, record_usage, | 835 | argc = parse_options(argc, argv, record_options, record_usage, |
| 896 | PARSE_OPT_STOP_AT_NON_OPTION); | 836 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 897 | if (!argc && target_pid == -1 && target_tid == -1 && | 837 | if (!argc && target_pid == -1 && target_tid == -1 && |
| @@ -913,7 +853,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
| 913 | if (no_buildid_cache || no_buildid) | 853 | if (no_buildid_cache || no_buildid) |
| 914 | disable_buildid_cache(); | 854 | disable_buildid_cache(); |
| 915 | 855 | ||
| 916 | if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) { | 856 | if (evsel_list->nr_entries == 0 && |
| 857 | perf_evlist__add_default(evsel_list) < 0) { | ||
| 917 | pr_err("Not enough memory for event selector list\n"); | 858 | pr_err("Not enough memory for event selector list\n"); |
| 918 | goto out_symbol_exit; | 859 | goto out_symbol_exit; |
| 919 | } | 860 | } |
| @@ -927,21 +868,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
| 927 | usage_with_options(record_usage, record_options); | 868 | usage_with_options(record_usage, record_options); |
| 928 | } | 869 | } |
| 929 | 870 | ||
| 930 | cpus = cpu_map__new(cpu_list); | 871 | if (target_tid != -1) |
| 931 | if (cpus == NULL) { | 872 | cpus = cpu_map__dummy_new(); |
| 932 | perror("failed to parse CPUs map"); | 873 | else |
| 933 | return -1; | 874 | cpus = cpu_map__new(cpu_list); |
| 934 | } | ||
| 935 | 875 | ||
| 936 | list_for_each_entry(pos, &evsel_list, node) { | 876 | if (cpus == NULL) |
| 877 | usage_with_options(record_usage, record_options); | ||
| 878 | |||
| 879 | list_for_each_entry(pos, &evsel_list->entries, node) { | ||
| 937 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | 880 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) |
| 938 | goto out_free_fd; | 881 | goto out_free_fd; |
| 939 | if (perf_header__push_event(pos->attr.config, event_name(pos))) | 882 | if (perf_header__push_event(pos->attr.config, event_name(pos))) |
| 940 | goto out_free_fd; | 883 | goto out_free_fd; |
| 941 | } | 884 | } |
| 942 | event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS * | 885 | |
| 943 | MAX_COUNTERS * threads->nr)); | 886 | if (perf_evlist__alloc_pollfd(evsel_list, cpus->nr, threads->nr) < 0) |
| 944 | if (!event_array) | ||
| 945 | goto out_free_fd; | 887 | goto out_free_fd; |
| 946 | 888 | ||
| 947 | if (user_interval != ULLONG_MAX) | 889 | if (user_interval != ULLONG_MAX) |
| @@ -959,13 +901,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
| 959 | } else { | 901 | } else { |
| 960 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 902 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
| 961 | err = -EINVAL; | 903 | err = -EINVAL; |
| 962 | goto out_free_event_array; | 904 | goto out_free_fd; |
| 963 | } | 905 | } |
| 964 | 906 | ||
| 965 | err = __cmd_record(argc, argv); | 907 | err = __cmd_record(argc, argv); |
| 966 | 908 | ||
| 967 | out_free_event_array: | ||
| 968 | free(event_array); | ||
| 969 | out_free_fd: | 909 | out_free_fd: |
| 970 | thread_map__delete(threads); | 910 | thread_map__delete(threads); |
| 971 | threads = NULL; | 911 | threads = NULL; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c27e31f289e6..f6a43493d1d0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -81,18 +81,17 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
| 81 | struct addr_location *al, | 81 | struct addr_location *al, |
| 82 | struct sample_data *data) | 82 | struct sample_data *data) |
| 83 | { | 83 | { |
| 84 | struct map_symbol *syms = NULL; | ||
| 85 | struct symbol *parent = NULL; | 84 | struct symbol *parent = NULL; |
| 86 | int err = -ENOMEM; | 85 | int err = 0; |
| 87 | struct hist_entry *he; | 86 | struct hist_entry *he; |
| 88 | struct hists *hists; | 87 | struct hists *hists; |
| 89 | struct perf_event_attr *attr; | 88 | struct perf_event_attr *attr; |
| 90 | 89 | ||
| 91 | if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { | 90 | if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { |
| 92 | syms = perf_session__resolve_callchain(self, al->thread, | 91 | err = perf_session__resolve_callchain(self, al->thread, |
| 93 | data->callchain, &parent); | 92 | data->callchain, &parent); |
| 94 | if (syms == NULL) | 93 | if (err) |
| 95 | return -ENOMEM; | 94 | return err; |
| 96 | } | 95 | } |
| 97 | 96 | ||
| 98 | attr = perf_header__find_attr(data->id, &self->header); | 97 | attr = perf_header__find_attr(data->id, &self->header); |
| @@ -101,16 +100,17 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
| 101 | else | 100 | else |
| 102 | hists = perf_session__hists_findnew(self, data->id, 0, 0); | 101 | hists = perf_session__hists_findnew(self, data->id, 0, 0); |
| 103 | if (hists == NULL) | 102 | if (hists == NULL) |
| 104 | goto out_free_syms; | 103 | return -ENOMEM; |
| 104 | |||
| 105 | he = __hists__add_entry(hists, al, parent, data->period); | 105 | he = __hists__add_entry(hists, al, parent, data->period); |
| 106 | if (he == NULL) | 106 | if (he == NULL) |
| 107 | goto out_free_syms; | 107 | return -ENOMEM; |
| 108 | err = 0; | 108 | |
| 109 | if (symbol_conf.use_callchain) { | 109 | if (symbol_conf.use_callchain) { |
| 110 | err = callchain_append(he->callchain, data->callchain, syms, | 110 | err = callchain_append(he->callchain, &self->callchain_cursor, |
| 111 | data->period); | 111 | data->period); |
| 112 | if (err) | 112 | if (err) |
| 113 | goto out_free_syms; | 113 | return err; |
| 114 | } | 114 | } |
| 115 | /* | 115 | /* |
| 116 | * Only in the newt browser we are doing integrated annotation, | 116 | * Only in the newt browser we are doing integrated annotation, |
| @@ -119,8 +119,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
| 119 | */ | 119 | */ |
| 120 | if (use_browser > 0) | 120 | if (use_browser > 0) |
| 121 | err = hist_entry__inc_addr_samples(he, al->addr); | 121 | err = hist_entry__inc_addr_samples(he, al->addr); |
| 122 | out_free_syms: | 122 | |
| 123 | free(syms); | ||
| 124 | return err; | 123 | return err; |
| 125 | } | 124 | } |
| 126 | 125 | ||
| @@ -222,7 +221,7 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
| 222 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && | 221 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
| 223 | !symbol_conf.use_callchain) { | 222 | !symbol_conf.use_callchain) { |
| 224 | symbol_conf.use_callchain = true; | 223 | symbol_conf.use_callchain = true; |
| 225 | if (register_callchain_param(&callchain_param) < 0) { | 224 | if (callchain_register_param(&callchain_param) < 0) { |
| 226 | fprintf(stderr, "Can't register callchain" | 225 | fprintf(stderr, "Can't register callchain" |
| 227 | " params\n"); | 226 | " params\n"); |
| 228 | return -EINVAL; | 227 | return -EINVAL; |
| @@ -424,7 +423,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
| 424 | if (tok2) | 423 | if (tok2) |
| 425 | callchain_param.print_limit = strtod(tok2, &endptr); | 424 | callchain_param.print_limit = strtod(tok2, &endptr); |
| 426 | setup: | 425 | setup: |
| 427 | if (register_callchain_param(&callchain_param) < 0) { | 426 | if (callchain_register_param(&callchain_param) < 0) { |
| 428 | fprintf(stderr, "Can't register callchain params\n"); | 427 | fprintf(stderr, "Can't register callchain params\n"); |
| 429 | return -1; | 428 | return -1; |
| 430 | } | 429 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index a482a191a0ca..8906adfdbd8e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -43,11 +43,13 @@ | |||
| 43 | #include "util/parse-options.h" | 43 | #include "util/parse-options.h" |
| 44 | #include "util/parse-events.h" | 44 | #include "util/parse-events.h" |
| 45 | #include "util/event.h" | 45 | #include "util/event.h" |
| 46 | #include "util/evlist.h" | ||
| 46 | #include "util/evsel.h" | 47 | #include "util/evsel.h" |
| 47 | #include "util/debug.h" | 48 | #include "util/debug.h" |
| 48 | #include "util/header.h" | 49 | #include "util/header.h" |
| 49 | #include "util/cpumap.h" | 50 | #include "util/cpumap.h" |
| 50 | #include "util/thread.h" | 51 | #include "util/thread.h" |
| 52 | #include "util/thread_map.h" | ||
| 51 | 53 | ||
| 52 | #include <sys/prctl.h> | 54 | #include <sys/prctl.h> |
| 53 | #include <math.h> | 55 | #include <math.h> |
| @@ -71,6 +73,8 @@ static struct perf_event_attr default_attrs[] = { | |||
| 71 | 73 | ||
| 72 | }; | 74 | }; |
| 73 | 75 | ||
| 76 | struct perf_evlist *evsel_list; | ||
| 77 | |||
| 74 | static bool system_wide = false; | 78 | static bool system_wide = false; |
| 75 | static struct cpu_map *cpus; | 79 | static struct cpu_map *cpus; |
| 76 | static int run_idx = 0; | 80 | static int run_idx = 0; |
| @@ -166,7 +170,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
| 166 | PERF_FORMAT_TOTAL_TIME_RUNNING; | 170 | PERF_FORMAT_TOTAL_TIME_RUNNING; |
| 167 | 171 | ||
| 168 | if (system_wide) | 172 | if (system_wide) |
| 169 | return perf_evsel__open_per_cpu(evsel, cpus); | 173 | return perf_evsel__open_per_cpu(evsel, cpus, false, false); |
| 170 | 174 | ||
| 171 | attr->inherit = !no_inherit; | 175 | attr->inherit = !no_inherit; |
| 172 | if (target_pid == -1 && target_tid == -1) { | 176 | if (target_pid == -1 && target_tid == -1) { |
| @@ -174,7 +178,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
| 174 | attr->enable_on_exec = 1; | 178 | attr->enable_on_exec = 1; |
| 175 | } | 179 | } |
| 176 | 180 | ||
| 177 | return perf_evsel__open_per_thread(evsel, threads); | 181 | return perf_evsel__open_per_thread(evsel, threads, false, false); |
| 178 | } | 182 | } |
| 179 | 183 | ||
| 180 | /* | 184 | /* |
| @@ -309,7 +313,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
| 309 | close(child_ready_pipe[0]); | 313 | close(child_ready_pipe[0]); |
| 310 | } | 314 | } |
| 311 | 315 | ||
| 312 | list_for_each_entry(counter, &evsel_list, node) { | 316 | list_for_each_entry(counter, &evsel_list->entries, node) { |
| 313 | if (create_perf_stat_counter(counter) < 0) { | 317 | if (create_perf_stat_counter(counter) < 0) { |
| 314 | if (errno == -EPERM || errno == -EACCES) { | 318 | if (errno == -EPERM || errno == -EACCES) { |
| 315 | error("You may not have permission to collect %sstats.\n" | 319 | error("You may not have permission to collect %sstats.\n" |
| @@ -347,12 +351,12 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
| 347 | update_stats(&walltime_nsecs_stats, t1 - t0); | 351 | update_stats(&walltime_nsecs_stats, t1 - t0); |
| 348 | 352 | ||
| 349 | if (no_aggr) { | 353 | if (no_aggr) { |
| 350 | list_for_each_entry(counter, &evsel_list, node) { | 354 | list_for_each_entry(counter, &evsel_list->entries, node) { |
| 351 | read_counter(counter); | 355 | read_counter(counter); |
| 352 | perf_evsel__close_fd(counter, cpus->nr, 1); | 356 | perf_evsel__close_fd(counter, cpus->nr, 1); |
| 353 | } | 357 | } |
| 354 | } else { | 358 | } else { |
| 355 | list_for_each_entry(counter, &evsel_list, node) { | 359 | list_for_each_entry(counter, &evsel_list->entries, node) { |
| 356 | read_counter_aggr(counter); | 360 | read_counter_aggr(counter); |
| 357 | perf_evsel__close_fd(counter, cpus->nr, threads->nr); | 361 | perf_evsel__close_fd(counter, cpus->nr, threads->nr); |
| 358 | } | 362 | } |
| @@ -555,10 +559,10 @@ static void print_stat(int argc, const char **argv) | |||
| 555 | } | 559 | } |
| 556 | 560 | ||
| 557 | if (no_aggr) { | 561 | if (no_aggr) { |
| 558 | list_for_each_entry(counter, &evsel_list, node) | 562 | list_for_each_entry(counter, &evsel_list->entries, node) |
| 559 | print_counter(counter); | 563 | print_counter(counter); |
| 560 | } else { | 564 | } else { |
| 561 | list_for_each_entry(counter, &evsel_list, node) | 565 | list_for_each_entry(counter, &evsel_list->entries, node) |
| 562 | print_counter_aggr(counter); | 566 | print_counter_aggr(counter); |
| 563 | } | 567 | } |
| 564 | 568 | ||
| @@ -610,7 +614,7 @@ static int stat__set_big_num(const struct option *opt __used, | |||
| 610 | } | 614 | } |
| 611 | 615 | ||
| 612 | static const struct option options[] = { | 616 | static const struct option options[] = { |
| 613 | OPT_CALLBACK('e', "event", NULL, "event", | 617 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
| 614 | "event selector. use 'perf list' to list available events", | 618 | "event selector. use 'perf list' to list available events", |
| 615 | parse_events), | 619 | parse_events), |
| 616 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 620 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, |
| @@ -648,6 +652,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 648 | 652 | ||
| 649 | setlocale(LC_ALL, ""); | 653 | setlocale(LC_ALL, ""); |
| 650 | 654 | ||
| 655 | evsel_list = perf_evlist__new(); | ||
| 656 | if (evsel_list == NULL) | ||
| 657 | return -ENOMEM; | ||
| 658 | |||
| 651 | argc = parse_options(argc, argv, options, stat_usage, | 659 | argc = parse_options(argc, argv, options, stat_usage, |
| 652 | PARSE_OPT_STOP_AT_NON_OPTION); | 660 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 653 | 661 | ||
| @@ -679,17 +687,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 679 | usage_with_options(stat_usage, options); | 687 | usage_with_options(stat_usage, options); |
| 680 | 688 | ||
| 681 | /* Set attrs and nr_counters if no event is selected and !null_run */ | 689 | /* Set attrs and nr_counters if no event is selected and !null_run */ |
| 682 | if (!null_run && !nr_counters) { | 690 | if (!null_run && !evsel_list->nr_entries) { |
| 683 | size_t c; | 691 | size_t c; |
| 684 | 692 | ||
| 685 | nr_counters = ARRAY_SIZE(default_attrs); | ||
| 686 | |||
| 687 | for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { | 693 | for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { |
| 688 | pos = perf_evsel__new(&default_attrs[c], | 694 | pos = perf_evsel__new(&default_attrs[c], c); |
| 689 | nr_counters); | ||
| 690 | if (pos == NULL) | 695 | if (pos == NULL) |
| 691 | goto out; | 696 | goto out; |
| 692 | list_add(&pos->node, &evsel_list); | 697 | perf_evlist__add(evsel_list, pos); |
| 693 | } | 698 | } |
| 694 | } | 699 | } |
| 695 | 700 | ||
| @@ -713,7 +718,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 713 | return -1; | 718 | return -1; |
| 714 | } | 719 | } |
| 715 | 720 | ||
| 716 | list_for_each_entry(pos, &evsel_list, node) { | 721 | list_for_each_entry(pos, &evsel_list->entries, node) { |
| 717 | if (perf_evsel__alloc_stat_priv(pos) < 0 || | 722 | if (perf_evsel__alloc_stat_priv(pos) < 0 || |
| 718 | perf_evsel__alloc_counts(pos, cpus->nr) < 0 || | 723 | perf_evsel__alloc_counts(pos, cpus->nr) < 0 || |
| 719 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | 724 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) |
| @@ -741,9 +746,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 741 | if (status != -1) | 746 | if (status != -1) |
| 742 | print_stat(argc, argv); | 747 | print_stat(argc, argv); |
| 743 | out_free_fd: | 748 | out_free_fd: |
| 744 | list_for_each_entry(pos, &evsel_list, node) | 749 | list_for_each_entry(pos, &evsel_list->entries, node) |
| 745 | perf_evsel__free_stat_priv(pos); | 750 | perf_evsel__free_stat_priv(pos); |
| 746 | perf_evsel_list__delete(); | 751 | perf_evlist__delete(evsel_list); |
| 747 | out: | 752 | out: |
| 748 | thread_map__delete(threads); | 753 | thread_map__delete(threads); |
| 749 | threads = NULL; | 754 | threads = NULL; |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 5dcdba653d70..231e3e21810c 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
| @@ -7,10 +7,11 @@ | |||
| 7 | 7 | ||
| 8 | #include "util/cache.h" | 8 | #include "util/cache.h" |
| 9 | #include "util/debug.h" | 9 | #include "util/debug.h" |
| 10 | #include "util/evlist.h" | ||
| 10 | #include "util/parse-options.h" | 11 | #include "util/parse-options.h" |
| 11 | #include "util/session.h" | 12 | #include "util/parse-events.h" |
| 12 | #include "util/symbol.h" | 13 | #include "util/symbol.h" |
| 13 | #include "util/thread.h" | 14 | #include "util/thread_map.h" |
| 14 | 15 | ||
| 15 | static long page_size; | 16 | static long page_size; |
| 16 | 17 | ||
| @@ -238,14 +239,14 @@ out: | |||
| 238 | #include "util/evsel.h" | 239 | #include "util/evsel.h" |
| 239 | #include <sys/types.h> | 240 | #include <sys/types.h> |
| 240 | 241 | ||
| 241 | static int trace_event__id(const char *event_name) | 242 | static int trace_event__id(const char *evname) |
| 242 | { | 243 | { |
| 243 | char *filename; | 244 | char *filename; |
| 244 | int err = -1, fd; | 245 | int err = -1, fd; |
| 245 | 246 | ||
| 246 | if (asprintf(&filename, | 247 | if (asprintf(&filename, |
| 247 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", | 248 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", |
| 248 | event_name) < 0) | 249 | evname) < 0) |
| 249 | return -1; | 250 | return -1; |
| 250 | 251 | ||
| 251 | fd = open(filename, O_RDONLY); | 252 | fd = open(filename, O_RDONLY); |
| @@ -289,7 +290,7 @@ static int test__open_syscall_event(void) | |||
| 289 | goto out_thread_map_delete; | 290 | goto out_thread_map_delete; |
| 290 | } | 291 | } |
| 291 | 292 | ||
| 292 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | 293 | if (perf_evsel__open_per_thread(evsel, threads, false, false) < 0) { |
| 293 | pr_debug("failed to open counter: %s, " | 294 | pr_debug("failed to open counter: %s, " |
| 294 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 295 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
| 295 | strerror(errno)); | 296 | strerror(errno)); |
| @@ -347,9 +348,9 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
| 347 | } | 348 | } |
| 348 | 349 | ||
| 349 | cpus = cpu_map__new(NULL); | 350 | cpus = cpu_map__new(NULL); |
| 350 | if (threads == NULL) { | 351 | if (cpus == NULL) { |
| 351 | pr_debug("thread_map__new\n"); | 352 | pr_debug("cpu_map__new\n"); |
| 352 | return -1; | 353 | goto out_thread_map_delete; |
| 353 | } | 354 | } |
| 354 | 355 | ||
| 355 | 356 | ||
| @@ -364,7 +365,7 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
| 364 | goto out_thread_map_delete; | 365 | goto out_thread_map_delete; |
| 365 | } | 366 | } |
| 366 | 367 | ||
| 367 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | 368 | if (perf_evsel__open(evsel, cpus, threads, false, false) < 0) { |
| 368 | pr_debug("failed to open counter: %s, " | 369 | pr_debug("failed to open counter: %s, " |
| 369 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 370 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
| 370 | strerror(errno)); | 371 | strerror(errno)); |
| @@ -408,6 +409,8 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
| 408 | goto out_close_fd; | 409 | goto out_close_fd; |
| 409 | } | 410 | } |
| 410 | 411 | ||
| 412 | err = 0; | ||
| 413 | |||
| 411 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | 414 | for (cpu = 0; cpu < cpus->nr; ++cpu) { |
| 412 | unsigned int expected; | 415 | unsigned int expected; |
| 413 | 416 | ||
| @@ -416,18 +419,18 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
| 416 | 419 | ||
| 417 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { | 420 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { |
| 418 | pr_debug("perf_evsel__open_read_on_cpu\n"); | 421 | pr_debug("perf_evsel__open_read_on_cpu\n"); |
| 419 | goto out_close_fd; | 422 | err = -1; |
| 423 | break; | ||
| 420 | } | 424 | } |
| 421 | 425 | ||
| 422 | expected = nr_open_calls + cpu; | 426 | expected = nr_open_calls + cpu; |
| 423 | if (evsel->counts->cpu[cpu].val != expected) { | 427 | if (evsel->counts->cpu[cpu].val != expected) { |
| 424 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", | 428 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", |
| 425 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); | 429 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); |
| 426 | goto out_close_fd; | 430 | err = -1; |
| 427 | } | 431 | } |
| 428 | } | 432 | } |
| 429 | 433 | ||
| 430 | err = 0; | ||
| 431 | out_close_fd: | 434 | out_close_fd: |
| 432 | perf_evsel__close_fd(evsel, 1, threads->nr); | 435 | perf_evsel__close_fd(evsel, 1, threads->nr); |
| 433 | out_evsel_delete: | 436 | out_evsel_delete: |
| @@ -437,6 +440,159 @@ out_thread_map_delete: | |||
| 437 | return err; | 440 | return err; |
| 438 | } | 441 | } |
| 439 | 442 | ||
| 443 | /* | ||
| 444 | * This test will generate random numbers of calls to some getpid syscalls, | ||
| 445 | * then establish an mmap for a group of events that are created to monitor | ||
| 446 | * the syscalls. | ||
| 447 | * | ||
| 448 | * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated | ||
| 449 | * sample.id field to map back to its respective perf_evsel instance. | ||
| 450 | * | ||
| 451 | * Then it checks if the number of syscalls reported as perf events by | ||
| 452 | * the kernel corresponds to the number of syscalls made. | ||
| 453 | */ | ||
| 454 | static int test__basic_mmap(void) | ||
| 455 | { | ||
| 456 | int err = -1; | ||
| 457 | event_t *event; | ||
| 458 | struct thread_map *threads; | ||
| 459 | struct cpu_map *cpus; | ||
| 460 | struct perf_evlist *evlist; | ||
| 461 | struct perf_event_attr attr = { | ||
| 462 | .type = PERF_TYPE_TRACEPOINT, | ||
| 463 | .read_format = PERF_FORMAT_ID, | ||
| 464 | .sample_type = PERF_SAMPLE_ID, | ||
| 465 | .watermark = 0, | ||
| 466 | }; | ||
| 467 | cpu_set_t cpu_set; | ||
| 468 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", | ||
| 469 | "getpgid", }; | ||
| 470 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, | ||
| 471 | (void*)getpgid }; | ||
| 472 | #define nsyscalls ARRAY_SIZE(syscall_names) | ||
| 473 | int ids[nsyscalls]; | ||
| 474 | unsigned int nr_events[nsyscalls], | ||
| 475 | expected_nr_events[nsyscalls], i, j; | ||
| 476 | struct perf_evsel *evsels[nsyscalls], *evsel; | ||
| 477 | |||
| 478 | for (i = 0; i < nsyscalls; ++i) { | ||
| 479 | char name[64]; | ||
| 480 | |||
| 481 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
| 482 | ids[i] = trace_event__id(name); | ||
| 483 | if (ids[i] < 0) { | ||
| 484 | pr_debug("Is debugfs mounted on /sys/kernel/debug?\n"); | ||
| 485 | return -1; | ||
| 486 | } | ||
| 487 | nr_events[i] = 0; | ||
| 488 | expected_nr_events[i] = random() % 257; | ||
| 489 | } | ||
| 490 | |||
| 491 | threads = thread_map__new(-1, getpid()); | ||
| 492 | if (threads == NULL) { | ||
| 493 | pr_debug("thread_map__new\n"); | ||
| 494 | return -1; | ||
| 495 | } | ||
| 496 | |||
| 497 | cpus = cpu_map__new(NULL); | ||
| 498 | if (threads == NULL) { | ||
| 499 | pr_debug("thread_map__new\n"); | ||
| 500 | goto out_free_threads; | ||
| 501 | } | ||
| 502 | |||
| 503 | CPU_ZERO(&cpu_set); | ||
| 504 | CPU_SET(cpus->map[0], &cpu_set); | ||
| 505 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | ||
| 506 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
| 507 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
| 508 | cpus->map[0], strerror(errno)); | ||
| 509 | goto out_free_cpus; | ||
| 510 | } | ||
| 511 | |||
| 512 | evlist = perf_evlist__new(); | ||
| 513 | if (threads == NULL) { | ||
| 514 | pr_debug("perf_evlist__new\n"); | ||
| 515 | goto out_free_cpus; | ||
| 516 | } | ||
| 517 | |||
| 518 | /* anonymous union fields, can't be initialized above */ | ||
| 519 | attr.wakeup_events = 1; | ||
| 520 | attr.sample_period = 1; | ||
| 521 | |||
| 522 | for (i = 0; i < nsyscalls; ++i) { | ||
| 523 | attr.config = ids[i]; | ||
| 524 | evsels[i] = perf_evsel__new(&attr, i); | ||
| 525 | if (evsels[i] == NULL) { | ||
| 526 | pr_debug("perf_evsel__new\n"); | ||
| 527 | goto out_free_evlist; | ||
| 528 | } | ||
| 529 | |||
| 530 | perf_evlist__add(evlist, evsels[i]); | ||
| 531 | |||
| 532 | if (perf_evsel__open(evsels[i], cpus, threads, false, false) < 0) { | ||
| 533 | pr_debug("failed to open counter: %s, " | ||
| 534 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
| 535 | strerror(errno)); | ||
| 536 | goto out_close_fd; | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | if (perf_evlist__mmap(evlist, cpus, threads, 128, true) < 0) { | ||
| 541 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
| 542 | strerror(errno)); | ||
| 543 | goto out_close_fd; | ||
| 544 | } | ||
| 545 | |||
| 546 | for (i = 0; i < nsyscalls; ++i) | ||
| 547 | for (j = 0; j < expected_nr_events[i]; ++j) { | ||
| 548 | int foo = syscalls[i](); | ||
| 549 | ++foo; | ||
| 550 | } | ||
| 551 | |||
| 552 | while ((event = perf_evlist__read_on_cpu(evlist, 0)) != NULL) { | ||
| 553 | struct sample_data sample; | ||
| 554 | |||
| 555 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
| 556 | pr_debug("unexpected %s event\n", | ||
| 557 | event__get_event_name(event->header.type)); | ||
| 558 | goto out_munmap; | ||
| 559 | } | ||
| 560 | |||
| 561 | event__parse_sample(event, attr.sample_type, false, &sample); | ||
| 562 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
| 563 | if (evsel == NULL) { | ||
| 564 | pr_debug("event with id %" PRIu64 | ||
| 565 | " doesn't map to an evsel\n", sample.id); | ||
| 566 | goto out_munmap; | ||
| 567 | } | ||
| 568 | nr_events[evsel->idx]++; | ||
| 569 | } | ||
| 570 | |||
| 571 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
| 572 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { | ||
| 573 | pr_debug("expected %d %s events, got %d\n", | ||
| 574 | expected_nr_events[evsel->idx], | ||
| 575 | event_name(evsel), nr_events[evsel->idx]); | ||
| 576 | goto out_munmap; | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | err = 0; | ||
| 581 | out_munmap: | ||
| 582 | perf_evlist__munmap(evlist, 1); | ||
| 583 | out_close_fd: | ||
| 584 | for (i = 0; i < nsyscalls; ++i) | ||
| 585 | perf_evsel__close_fd(evsels[i], 1, threads->nr); | ||
| 586 | out_free_evlist: | ||
| 587 | perf_evlist__delete(evlist); | ||
| 588 | out_free_cpus: | ||
| 589 | cpu_map__delete(cpus); | ||
| 590 | out_free_threads: | ||
| 591 | thread_map__delete(threads); | ||
| 592 | return err; | ||
| 593 | #undef nsyscalls | ||
| 594 | } | ||
| 595 | |||
| 440 | static struct test { | 596 | static struct test { |
| 441 | const char *desc; | 597 | const char *desc; |
| 442 | int (*func)(void); | 598 | int (*func)(void); |
| @@ -454,6 +610,10 @@ static struct test { | |||
| 454 | .func = test__open_syscall_event_on_all_cpus, | 610 | .func = test__open_syscall_event_on_all_cpus, |
| 455 | }, | 611 | }, |
| 456 | { | 612 | { |
| 613 | .desc = "read samples using the mmap interface", | ||
| 614 | .func = test__basic_mmap, | ||
| 615 | }, | ||
| 616 | { | ||
| 457 | .func = NULL, | 617 | .func = NULL, |
| 458 | }, | 618 | }, |
| 459 | }; | 619 | }; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b6998e055767..ce2e50c891c7 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -21,10 +21,12 @@ | |||
| 21 | #include "perf.h" | 21 | #include "perf.h" |
| 22 | 22 | ||
| 23 | #include "util/color.h" | 23 | #include "util/color.h" |
| 24 | #include "util/evlist.h" | ||
| 24 | #include "util/evsel.h" | 25 | #include "util/evsel.h" |
| 25 | #include "util/session.h" | 26 | #include "util/session.h" |
| 26 | #include "util/symbol.h" | 27 | #include "util/symbol.h" |
| 27 | #include "util/thread.h" | 28 | #include "util/thread.h" |
| 29 | #include "util/thread_map.h" | ||
| 28 | #include "util/util.h" | 30 | #include "util/util.h" |
| 29 | #include <linux/rbtree.h> | 31 | #include <linux/rbtree.h> |
| 30 | #include "util/parse-options.h" | 32 | #include "util/parse-options.h" |
| @@ -60,6 +62,8 @@ | |||
| 60 | 62 | ||
| 61 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 63 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
| 62 | 64 | ||
| 65 | struct perf_evlist *evsel_list; | ||
| 66 | |||
| 63 | static bool system_wide = false; | 67 | static bool system_wide = false; |
| 64 | 68 | ||
| 65 | static int default_interval = 0; | 69 | static int default_interval = 0; |
| @@ -75,7 +79,7 @@ static struct cpu_map *cpus; | |||
| 75 | static int realtime_prio = 0; | 79 | static int realtime_prio = 0; |
| 76 | static bool group = false; | 80 | static bool group = false; |
| 77 | static unsigned int page_size; | 81 | static unsigned int page_size; |
| 78 | static unsigned int mmap_pages = 16; | 82 | static unsigned int mmap_pages = 128; |
| 79 | static int freq = 1000; /* 1 KHz */ | 83 | static int freq = 1000; /* 1 KHz */ |
| 80 | 84 | ||
| 81 | static int delay_secs = 2; | 85 | static int delay_secs = 2; |
| @@ -267,7 +271,7 @@ static void __zero_source_counters(struct sym_entry *syme) | |||
| 267 | 271 | ||
| 268 | line = syme->src->lines; | 272 | line = syme->src->lines; |
| 269 | while (line) { | 273 | while (line) { |
| 270 | for (i = 0; i < nr_counters; i++) | 274 | for (i = 0; i < evsel_list->nr_entries; i++) |
| 271 | line->count[i] = 0; | 275 | line->count[i] = 0; |
| 272 | line = line->next; | 276 | line = line->next; |
| 273 | } | 277 | } |
| @@ -414,7 +418,7 @@ static double sym_weight(const struct sym_entry *sym) | |||
| 414 | if (!display_weighted) | 418 | if (!display_weighted) |
| 415 | return weight; | 419 | return weight; |
| 416 | 420 | ||
| 417 | for (counter = 1; counter < nr_counters-1; counter++) | 421 | for (counter = 1; counter < evsel_list->nr_entries - 1; counter++) |
| 418 | weight *= sym->count[counter]; | 422 | weight *= sym->count[counter]; |
| 419 | 423 | ||
| 420 | weight /= (sym->count[counter] + 1); | 424 | weight /= (sym->count[counter] + 1); |
| @@ -501,7 +505,7 @@ static void print_sym_table(void) | |||
| 501 | rb_insert_active_sym(&tmp, syme); | 505 | rb_insert_active_sym(&tmp, syme); |
| 502 | sum_ksamples += syme->snap_count; | 506 | sum_ksamples += syme->snap_count; |
| 503 | 507 | ||
| 504 | for (j = 0; j < nr_counters; j++) | 508 | for (j = 0; j < evsel_list->nr_entries; j++) |
| 505 | syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; | 509 | syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; |
| 506 | } else | 510 | } else |
| 507 | list_remove_active_sym(syme); | 511 | list_remove_active_sym(syme); |
| @@ -535,9 +539,9 @@ static void print_sym_table(void) | |||
| 535 | esamples_percent); | 539 | esamples_percent); |
| 536 | } | 540 | } |
| 537 | 541 | ||
| 538 | if (nr_counters == 1 || !display_weighted) { | 542 | if (evsel_list->nr_entries == 1 || !display_weighted) { |
| 539 | struct perf_evsel *first; | 543 | struct perf_evsel *first; |
| 540 | first = list_entry(evsel_list.next, struct perf_evsel, node); | 544 | first = list_entry(evsel_list->entries.next, struct perf_evsel, node); |
| 541 | printf("%" PRIu64, (uint64_t)first->attr.sample_period); | 545 | printf("%" PRIu64, (uint64_t)first->attr.sample_period); |
| 542 | if (freq) | 546 | if (freq) |
| 543 | printf("Hz "); | 547 | printf("Hz "); |
| @@ -547,7 +551,7 @@ static void print_sym_table(void) | |||
| 547 | 551 | ||
| 548 | if (!display_weighted) | 552 | if (!display_weighted) |
| 549 | printf("%s", event_name(sym_evsel)); | 553 | printf("%s", event_name(sym_evsel)); |
| 550 | else list_for_each_entry(counter, &evsel_list, node) { | 554 | else list_for_each_entry(counter, &evsel_list->entries, node) { |
| 551 | if (counter->idx) | 555 | if (counter->idx) |
| 552 | printf("/"); | 556 | printf("/"); |
| 553 | 557 | ||
| @@ -606,7 +610,7 @@ static void print_sym_table(void) | |||
| 606 | sym_width = winsize.ws_col - dso_width - 29; | 610 | sym_width = winsize.ws_col - dso_width - 29; |
| 607 | } | 611 | } |
| 608 | putchar('\n'); | 612 | putchar('\n'); |
| 609 | if (nr_counters == 1) | 613 | if (evsel_list->nr_entries == 1) |
| 610 | printf(" samples pcnt"); | 614 | printf(" samples pcnt"); |
| 611 | else | 615 | else |
| 612 | printf(" weight samples pcnt"); | 616 | printf(" weight samples pcnt"); |
| @@ -615,7 +619,7 @@ static void print_sym_table(void) | |||
| 615 | printf(" RIP "); | 619 | printf(" RIP "); |
| 616 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); | 620 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); |
| 617 | printf(" %s _______ _____", | 621 | printf(" %s _______ _____", |
| 618 | nr_counters == 1 ? " " : "______"); | 622 | evsel_list->nr_entries == 1 ? " " : "______"); |
| 619 | if (verbose) | 623 | if (verbose) |
| 620 | printf(" ________________"); | 624 | printf(" ________________"); |
| 621 | printf(" %-*.*s", sym_width, sym_width, graph_line); | 625 | printf(" %-*.*s", sym_width, sym_width, graph_line); |
| @@ -634,7 +638,7 @@ static void print_sym_table(void) | |||
| 634 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | 638 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / |
| 635 | sum_ksamples)); | 639 | sum_ksamples)); |
| 636 | 640 | ||
| 637 | if (nr_counters == 1 || !display_weighted) | 641 | if (evsel_list->nr_entries == 1 || !display_weighted) |
| 638 | printf("%20.2f ", syme->weight); | 642 | printf("%20.2f ", syme->weight); |
| 639 | else | 643 | else |
| 640 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); | 644 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
| @@ -744,7 +748,7 @@ static void print_mapped_keys(void) | |||
| 744 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); | 748 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); |
| 745 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); | 749 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); |
| 746 | 750 | ||
| 747 | if (nr_counters > 1) | 751 | if (evsel_list->nr_entries > 1) |
| 748 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); | 752 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); |
| 749 | 753 | ||
| 750 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 754 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); |
| @@ -753,7 +757,7 @@ static void print_mapped_keys(void) | |||
| 753 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 757 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
| 754 | fprintf(stdout, "\t[S] stop annotation.\n"); | 758 | fprintf(stdout, "\t[S] stop annotation.\n"); |
| 755 | 759 | ||
| 756 | if (nr_counters > 1) | 760 | if (evsel_list->nr_entries > 1) |
| 757 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 761 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
| 758 | 762 | ||
| 759 | fprintf(stdout, | 763 | fprintf(stdout, |
| @@ -783,7 +787,7 @@ static int key_mapped(int c) | |||
| 783 | return 1; | 787 | return 1; |
| 784 | case 'E': | 788 | case 'E': |
| 785 | case 'w': | 789 | case 'w': |
| 786 | return nr_counters > 1 ? 1 : 0; | 790 | return evsel_list->nr_entries > 1 ? 1 : 0; |
| 787 | default: | 791 | default: |
| 788 | break; | 792 | break; |
| 789 | } | 793 | } |
| @@ -831,22 +835,22 @@ static void handle_keypress(struct perf_session *session, int c) | |||
| 831 | signal(SIGWINCH, SIG_DFL); | 835 | signal(SIGWINCH, SIG_DFL); |
| 832 | break; | 836 | break; |
| 833 | case 'E': | 837 | case 'E': |
| 834 | if (nr_counters > 1) { | 838 | if (evsel_list->nr_entries > 1) { |
| 835 | fprintf(stderr, "\nAvailable events:"); | 839 | fprintf(stderr, "\nAvailable events:"); |
| 836 | 840 | ||
| 837 | list_for_each_entry(sym_evsel, &evsel_list, node) | 841 | list_for_each_entry(sym_evsel, &evsel_list->entries, node) |
| 838 | fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); | 842 | fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); |
| 839 | 843 | ||
| 840 | prompt_integer(&sym_counter, "Enter details event counter"); | 844 | prompt_integer(&sym_counter, "Enter details event counter"); |
| 841 | 845 | ||
| 842 | if (sym_counter >= nr_counters) { | 846 | if (sym_counter >= evsel_list->nr_entries) { |
| 843 | sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); | 847 | sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node); |
| 844 | sym_counter = 0; | 848 | sym_counter = 0; |
| 845 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); | 849 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); |
| 846 | sleep(1); | 850 | sleep(1); |
| 847 | break; | 851 | break; |
| 848 | } | 852 | } |
| 849 | list_for_each_entry(sym_evsel, &evsel_list, node) | 853 | list_for_each_entry(sym_evsel, &evsel_list->entries, node) |
| 850 | if (sym_evsel->idx == sym_counter) | 854 | if (sym_evsel->idx == sym_counter) |
| 851 | break; | 855 | break; |
| 852 | } else sym_counter = 0; | 856 | } else sym_counter = 0; |
| @@ -930,6 +934,7 @@ repeat: | |||
| 930 | /* Tag samples to be skipped. */ | 934 | /* Tag samples to be skipped. */ |
| 931 | static const char *skip_symbols[] = { | 935 | static const char *skip_symbols[] = { |
| 932 | "default_idle", | 936 | "default_idle", |
| 937 | "native_safe_halt", | ||
| 933 | "cpu_idle", | 938 | "cpu_idle", |
| 934 | "enter_idle", | 939 | "enter_idle", |
| 935 | "exit_idle", | 940 | "exit_idle", |
| @@ -988,8 +993,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
| 988 | 993 | ||
| 989 | static void event__process_sample(const event_t *self, | 994 | static void event__process_sample(const event_t *self, |
| 990 | struct sample_data *sample, | 995 | struct sample_data *sample, |
| 991 | struct perf_session *session, | 996 | struct perf_session *session) |
| 992 | struct perf_evsel *evsel) | ||
| 993 | { | 997 | { |
| 994 | u64 ip = self->ip.ip; | 998 | u64 ip = self->ip.ip; |
| 995 | struct sym_entry *syme; | 999 | struct sym_entry *syme; |
| @@ -1082,8 +1086,12 @@ static void event__process_sample(const event_t *self, | |||
| 1082 | 1086 | ||
| 1083 | syme = symbol__priv(al.sym); | 1087 | syme = symbol__priv(al.sym); |
| 1084 | if (!syme->skip) { | 1088 | if (!syme->skip) { |
| 1085 | syme->count[evsel->idx]++; | 1089 | struct perf_evsel *evsel; |
| 1090 | |||
| 1086 | syme->origin = origin; | 1091 | syme->origin = origin; |
| 1092 | evsel = perf_evlist__id2evsel(evsel_list, sample->id); | ||
| 1093 | assert(evsel != NULL); | ||
| 1094 | syme->count[evsel->idx]++; | ||
| 1087 | record_precise_ip(syme, evsel->idx, ip); | 1095 | record_precise_ip(syme, evsel->idx, ip); |
| 1088 | pthread_mutex_lock(&active_symbols_lock); | 1096 | pthread_mutex_lock(&active_symbols_lock); |
| 1089 | if (list_empty(&syme->node) || !syme->node.next) | 1097 | if (list_empty(&syme->node) || !syme->node.next) |
| @@ -1092,156 +1100,52 @@ static void event__process_sample(const event_t *self, | |||
| 1092 | } | 1100 | } |
| 1093 | } | 1101 | } |
| 1094 | 1102 | ||
| 1095 | struct mmap_data { | 1103 | static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu) |
| 1096 | void *base; | ||
| 1097 | int mask; | ||
| 1098 | unsigned int prev; | ||
| 1099 | }; | ||
| 1100 | |||
| 1101 | static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel, | ||
| 1102 | int ncpus, int nthreads) | ||
| 1103 | { | ||
| 1104 | evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data)); | ||
| 1105 | return evsel->priv != NULL ? 0 : -ENOMEM; | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | static void perf_evsel__free_mmap(struct perf_evsel *evsel) | ||
| 1109 | { | ||
| 1110 | xyarray__delete(evsel->priv); | ||
| 1111 | evsel->priv = NULL; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | static unsigned int mmap_read_head(struct mmap_data *md) | ||
| 1115 | { | ||
| 1116 | struct perf_event_mmap_page *pc = md->base; | ||
| 1117 | int head; | ||
| 1118 | |||
| 1119 | head = pc->data_head; | ||
| 1120 | rmb(); | ||
| 1121 | |||
| 1122 | return head; | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | static void perf_session__mmap_read_counter(struct perf_session *self, | ||
| 1126 | struct perf_evsel *evsel, | ||
| 1127 | int cpu, int thread_idx) | ||
| 1128 | { | 1104 | { |
| 1129 | struct xyarray *mmap_array = evsel->priv; | ||
| 1130 | struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx); | ||
| 1131 | unsigned int head = mmap_read_head(md); | ||
| 1132 | unsigned int old = md->prev; | ||
| 1133 | unsigned char *data = md->base + page_size; | ||
| 1134 | struct sample_data sample; | 1105 | struct sample_data sample; |
| 1135 | int diff; | 1106 | event_t *event; |
| 1136 | |||
| 1137 | /* | ||
| 1138 | * If we're further behind than half the buffer, there's a chance | ||
| 1139 | * the writer will bite our tail and mess up the samples under us. | ||
| 1140 | * | ||
| 1141 | * If we somehow ended up ahead of the head, we got messed up. | ||
| 1142 | * | ||
| 1143 | * In either case, truncate and restart at head. | ||
| 1144 | */ | ||
| 1145 | diff = head - old; | ||
| 1146 | if (diff > md->mask / 2 || diff < 0) { | ||
| 1147 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | ||
| 1148 | |||
| 1149 | /* | ||
| 1150 | * head points to a known good entry, start there. | ||
| 1151 | */ | ||
| 1152 | old = head; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | for (; old != head;) { | ||
| 1156 | event_t *event = (event_t *)&data[old & md->mask]; | ||
| 1157 | 1107 | ||
| 1158 | event_t event_copy; | 1108 | while ((event = perf_evlist__read_on_cpu(evsel_list, cpu)) != NULL) { |
| 1159 | 1109 | perf_session__parse_sample(self, event, &sample); | |
| 1160 | size_t size = event->header.size; | ||
| 1161 | |||
| 1162 | /* | ||
| 1163 | * Event straddles the mmap boundary -- header should always | ||
| 1164 | * be inside due to u64 alignment of output. | ||
| 1165 | */ | ||
| 1166 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | ||
| 1167 | unsigned int offset = old; | ||
| 1168 | unsigned int len = min(sizeof(*event), size), cpy; | ||
| 1169 | void *dst = &event_copy; | ||
| 1170 | |||
| 1171 | do { | ||
| 1172 | cpy = min(md->mask + 1 - (offset & md->mask), len); | ||
| 1173 | memcpy(dst, &data[offset & md->mask], cpy); | ||
| 1174 | offset += cpy; | ||
| 1175 | dst += cpy; | ||
| 1176 | len -= cpy; | ||
| 1177 | } while (len); | ||
| 1178 | |||
| 1179 | event = &event_copy; | ||
| 1180 | } | ||
| 1181 | 1110 | ||
| 1182 | event__parse_sample(event, self, &sample); | ||
| 1183 | if (event->header.type == PERF_RECORD_SAMPLE) | 1111 | if (event->header.type == PERF_RECORD_SAMPLE) |
| 1184 | event__process_sample(event, &sample, self, evsel); | 1112 | event__process_sample(event, &sample, self); |
| 1185 | else | 1113 | else |
| 1186 | event__process(event, &sample, self); | 1114 | event__process(event, &sample, self); |
| 1187 | old += size; | ||
| 1188 | } | 1115 | } |
| 1189 | |||
| 1190 | md->prev = old; | ||
| 1191 | } | 1116 | } |
| 1192 | 1117 | ||
| 1193 | static struct pollfd *event_array; | ||
| 1194 | |||
| 1195 | static void perf_session__mmap_read(struct perf_session *self) | 1118 | static void perf_session__mmap_read(struct perf_session *self) |
| 1196 | { | 1119 | { |
| 1197 | struct perf_evsel *counter; | 1120 | int i; |
| 1198 | int i, thread_index; | ||
| 1199 | |||
| 1200 | for (i = 0; i < cpus->nr; i++) { | ||
| 1201 | list_for_each_entry(counter, &evsel_list, node) { | ||
| 1202 | for (thread_index = 0; | ||
| 1203 | thread_index < threads->nr; | ||
| 1204 | thread_index++) { | ||
| 1205 | perf_session__mmap_read_counter(self, | ||
| 1206 | counter, i, thread_index); | ||
| 1207 | } | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | } | ||
| 1211 | 1121 | ||
| 1212 | int nr_poll; | 1122 | for (i = 0; i < cpus->nr; i++) |
| 1213 | int group_fd; | 1123 | perf_session__mmap_read_cpu(self, i); |
| 1124 | } | ||
| 1214 | 1125 | ||
| 1215 | static void start_counter(int i, struct perf_evsel *evsel) | 1126 | static void start_counters(struct perf_evlist *evlist) |
| 1216 | { | 1127 | { |
| 1217 | struct xyarray *mmap_array = evsel->priv; | 1128 | struct perf_evsel *counter; |
| 1218 | struct mmap_data *mm; | ||
| 1219 | struct perf_event_attr *attr; | ||
| 1220 | int cpu = -1; | ||
| 1221 | int thread_index; | ||
| 1222 | |||
| 1223 | if (target_tid == -1) | ||
| 1224 | cpu = cpus->map[i]; | ||
| 1225 | 1129 | ||
| 1226 | attr = &evsel->attr; | 1130 | list_for_each_entry(counter, &evlist->entries, node) { |
| 1131 | struct perf_event_attr *attr = &counter->attr; | ||
| 1227 | 1132 | ||
| 1228 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 1133 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 1229 | 1134 | ||
| 1230 | if (freq) { | 1135 | if (freq) { |
| 1231 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 1136 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
| 1232 | attr->freq = 1; | 1137 | attr->freq = 1; |
| 1233 | attr->sample_freq = freq; | 1138 | attr->sample_freq = freq; |
| 1234 | } | 1139 | } |
| 1235 | 1140 | ||
| 1236 | attr->inherit = (cpu < 0) && inherit; | 1141 | if (evlist->nr_entries > 1) { |
| 1237 | attr->mmap = 1; | 1142 | attr->sample_type |= PERF_SAMPLE_ID; |
| 1143 | attr->read_format |= PERF_FORMAT_ID; | ||
| 1144 | } | ||
| 1238 | 1145 | ||
| 1239 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { | 1146 | attr->mmap = 1; |
| 1240 | try_again: | 1147 | try_again: |
| 1241 | FD(evsel, i, thread_index) = sys_perf_event_open(attr, | 1148 | if (perf_evsel__open(counter, cpus, threads, group, inherit) < 0) { |
| 1242 | threads->map[thread_index], cpu, group_fd, 0); | ||
| 1243 | |||
| 1244 | if (FD(evsel, i, thread_index) < 0) { | ||
| 1245 | int err = errno; | 1149 | int err = errno; |
| 1246 | 1150 | ||
| 1247 | if (err == EPERM || err == EACCES) | 1151 | if (err == EPERM || err == EACCES) |
| @@ -1253,8 +1157,8 @@ try_again: | |||
| 1253 | * based cpu-clock-tick sw counter, which | 1157 | * based cpu-clock-tick sw counter, which |
| 1254 | * is always available even if no PMU support: | 1158 | * is always available even if no PMU support: |
| 1255 | */ | 1159 | */ |
| 1256 | if (attr->type == PERF_TYPE_HARDWARE | 1160 | if (attr->type == PERF_TYPE_HARDWARE && |
| 1257 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | 1161 | attr->config == PERF_COUNT_HW_CPU_CYCLES) { |
| 1258 | 1162 | ||
| 1259 | if (verbose) | 1163 | if (verbose) |
| 1260 | warning(" ... trying to fall back to cpu-clock-ticks\n"); | 1164 | warning(" ... trying to fall back to cpu-clock-ticks\n"); |
| @@ -1264,39 +1168,23 @@ try_again: | |||
| 1264 | goto try_again; | 1168 | goto try_again; |
| 1265 | } | 1169 | } |
| 1266 | printf("\n"); | 1170 | printf("\n"); |
| 1267 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 1171 | error("sys_perf_event_open() syscall returned with %d " |
| 1268 | FD(evsel, i, thread_index), strerror(err)); | 1172 | "(%s). /bin/dmesg may provide additional information.\n", |
| 1173 | err, strerror(err)); | ||
| 1269 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 1174 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
| 1270 | exit(-1); | 1175 | exit(-1); |
| 1271 | } | 1176 | } |
| 1272 | assert(FD(evsel, i, thread_index) >= 0); | ||
| 1273 | fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK); | ||
| 1274 | |||
| 1275 | /* | ||
| 1276 | * First counter acts as the group leader: | ||
| 1277 | */ | ||
| 1278 | if (group && group_fd == -1) | ||
| 1279 | group_fd = FD(evsel, i, thread_index); | ||
| 1280 | |||
| 1281 | event_array[nr_poll].fd = FD(evsel, i, thread_index); | ||
| 1282 | event_array[nr_poll].events = POLLIN; | ||
| 1283 | nr_poll++; | ||
| 1284 | |||
| 1285 | mm = xyarray__entry(mmap_array, i, thread_index); | ||
| 1286 | mm->prev = 0; | ||
| 1287 | mm->mask = mmap_pages*page_size - 1; | ||
| 1288 | mm->base = mmap(NULL, (mmap_pages+1)*page_size, | ||
| 1289 | PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0); | ||
| 1290 | if (mm->base == MAP_FAILED) | ||
| 1291 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
| 1292 | } | 1177 | } |
| 1178 | |||
| 1179 | if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, true) < 0) | ||
| 1180 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
| 1293 | } | 1181 | } |
| 1294 | 1182 | ||
| 1295 | static int __cmd_top(void) | 1183 | static int __cmd_top(void) |
| 1296 | { | 1184 | { |
| 1297 | pthread_t thread; | 1185 | pthread_t thread; |
| 1298 | struct perf_evsel *counter; | 1186 | struct perf_evsel *first; |
| 1299 | int i, ret; | 1187 | int ret; |
| 1300 | /* | 1188 | /* |
| 1301 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 1189 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
| 1302 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 1190 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
| @@ -1310,14 +1198,12 @@ static int __cmd_top(void) | |||
| 1310 | else | 1198 | else |
| 1311 | event__synthesize_threads(event__process, session); | 1199 | event__synthesize_threads(event__process, session); |
| 1312 | 1200 | ||
| 1313 | for (i = 0; i < cpus->nr; i++) { | 1201 | start_counters(evsel_list); |
| 1314 | group_fd = -1; | 1202 | first = list_entry(evsel_list->entries.next, struct perf_evsel, node); |
| 1315 | list_for_each_entry(counter, &evsel_list, node) | 1203 | perf_session__set_sample_type(session, first->attr.sample_type); |
| 1316 | start_counter(i, counter); | ||
| 1317 | } | ||
| 1318 | 1204 | ||
| 1319 | /* Wait for a minimal set of events before starting the snapshot */ | 1205 | /* Wait for a minimal set of events before starting the snapshot */ |
| 1320 | poll(&event_array[0], nr_poll, 100); | 1206 | poll(evsel_list->pollfd, evsel_list->nr_fds, 100); |
| 1321 | 1207 | ||
| 1322 | perf_session__mmap_read(session); | 1208 | perf_session__mmap_read(session); |
| 1323 | 1209 | ||
| @@ -1342,7 +1228,7 @@ static int __cmd_top(void) | |||
| 1342 | perf_session__mmap_read(session); | 1228 | perf_session__mmap_read(session); |
| 1343 | 1229 | ||
| 1344 | if (hits == samples) | 1230 | if (hits == samples) |
| 1345 | ret = poll(event_array, nr_poll, 100); | 1231 | ret = poll(evsel_list->pollfd, evsel_list->nr_fds, 100); |
| 1346 | } | 1232 | } |
| 1347 | 1233 | ||
| 1348 | return 0; | 1234 | return 0; |
| @@ -1354,7 +1240,7 @@ static const char * const top_usage[] = { | |||
| 1354 | }; | 1240 | }; |
| 1355 | 1241 | ||
| 1356 | static const struct option options[] = { | 1242 | static const struct option options[] = { |
| 1357 | OPT_CALLBACK('e', "event", NULL, "event", | 1243 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
| 1358 | "event selector. use 'perf list' to list available events", | 1244 | "event selector. use 'perf list' to list available events", |
| 1359 | parse_events), | 1245 | parse_events), |
| 1360 | OPT_INTEGER('c', "count", &default_interval, | 1246 | OPT_INTEGER('c', "count", &default_interval, |
| @@ -1404,6 +1290,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1404 | struct perf_evsel *pos; | 1290 | struct perf_evsel *pos; |
| 1405 | int status = -ENOMEM; | 1291 | int status = -ENOMEM; |
| 1406 | 1292 | ||
| 1293 | evsel_list = perf_evlist__new(); | ||
| 1294 | if (evsel_list == NULL) | ||
| 1295 | return -ENOMEM; | ||
| 1296 | |||
| 1407 | page_size = sysconf(_SC_PAGE_SIZE); | 1297 | page_size = sysconf(_SC_PAGE_SIZE); |
| 1408 | 1298 | ||
| 1409 | argc = parse_options(argc, argv, options, top_usage, 0); | 1299 | argc = parse_options(argc, argv, options, top_usage, 0); |
| @@ -1419,11 +1309,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1419 | usage_with_options(top_usage, options); | 1309 | usage_with_options(top_usage, options); |
| 1420 | } | 1310 | } |
| 1421 | 1311 | ||
| 1422 | event_array = malloc((sizeof(struct pollfd) * | ||
| 1423 | MAX_NR_CPUS * MAX_COUNTERS * threads->nr)); | ||
| 1424 | if (!event_array) | ||
| 1425 | return -ENOMEM; | ||
| 1426 | |||
| 1427 | /* CPU and PID are mutually exclusive */ | 1312 | /* CPU and PID are mutually exclusive */ |
| 1428 | if (target_tid > 0 && cpu_list) { | 1313 | if (target_tid > 0 && cpu_list) { |
| 1429 | printf("WARNING: PID switch overriding CPU\n"); | 1314 | printf("WARNING: PID switch overriding CPU\n"); |
| @@ -1431,7 +1316,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1431 | cpu_list = NULL; | 1316 | cpu_list = NULL; |
| 1432 | } | 1317 | } |
| 1433 | 1318 | ||
| 1434 | if (!nr_counters && perf_evsel_list__create_default() < 0) { | 1319 | if (!evsel_list->nr_entries && |
| 1320 | perf_evlist__add_default(evsel_list) < 0) { | ||
| 1435 | pr_err("Not enough memory for event selector list\n"); | 1321 | pr_err("Not enough memory for event selector list\n"); |
| 1436 | return -ENOMEM; | 1322 | return -ENOMEM; |
| 1437 | } | 1323 | } |
| @@ -1459,9 +1345,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1459 | if (cpus == NULL) | 1345 | if (cpus == NULL) |
| 1460 | usage_with_options(top_usage, options); | 1346 | usage_with_options(top_usage, options); |
| 1461 | 1347 | ||
| 1462 | list_for_each_entry(pos, &evsel_list, node) { | 1348 | list_for_each_entry(pos, &evsel_list->entries, node) { |
| 1463 | if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 || | 1349 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) |
| 1464 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | ||
| 1465 | goto out_free_fd; | 1350 | goto out_free_fd; |
| 1466 | /* | 1351 | /* |
| 1467 | * Fill in the ones not specifically initialized via -c: | 1352 | * Fill in the ones not specifically initialized via -c: |
| @@ -1472,10 +1357,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1472 | pos->attr.sample_period = default_interval; | 1357 | pos->attr.sample_period = default_interval; |
| 1473 | } | 1358 | } |
| 1474 | 1359 | ||
| 1475 | sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); | 1360 | if (perf_evlist__alloc_pollfd(evsel_list, cpus->nr, threads->nr) < 0 || |
| 1361 | perf_evlist__alloc_mmap(evsel_list, cpus->nr) < 0) | ||
| 1362 | goto out_free_fd; | ||
| 1363 | |||
| 1364 | sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node); | ||
| 1476 | 1365 | ||
| 1477 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | 1366 | symbol_conf.priv_size = (sizeof(struct sym_entry) + |
| 1478 | (nr_counters + 1) * sizeof(unsigned long)); | 1367 | (evsel_list->nr_entries + 1) * sizeof(unsigned long)); |
| 1479 | 1368 | ||
| 1480 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1369 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
| 1481 | if (symbol__init() < 0) | 1370 | if (symbol__init() < 0) |
| @@ -1489,9 +1378,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1489 | 1378 | ||
| 1490 | status = __cmd_top(); | 1379 | status = __cmd_top(); |
| 1491 | out_free_fd: | 1380 | out_free_fd: |
| 1492 | list_for_each_entry(pos, &evsel_list, node) | 1381 | perf_evlist__delete(evsel_list); |
| 1493 | perf_evsel__free_mmap(pos); | ||
| 1494 | perf_evsel_list__delete(); | ||
| 1495 | 1382 | ||
| 1496 | return status; | 1383 | return status; |
| 1497 | } | 1384 | } |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 95aaf565c704..a5fc660c1f12 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -94,6 +94,32 @@ void get_term_dimensions(struct winsize *ws); | |||
| 94 | #include "util/types.h" | 94 | #include "util/types.h" |
| 95 | #include <stdbool.h> | 95 | #include <stdbool.h> |
| 96 | 96 | ||
| 97 | struct perf_mmap { | ||
| 98 | void *base; | ||
| 99 | int mask; | ||
| 100 | unsigned int prev; | ||
| 101 | }; | ||
| 102 | |||
| 103 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | ||
| 104 | { | ||
| 105 | struct perf_event_mmap_page *pc = mm->base; | ||
| 106 | int head = pc->data_head; | ||
| 107 | rmb(); | ||
| 108 | return head; | ||
| 109 | } | ||
| 110 | |||
| 111 | static inline void perf_mmap__write_tail(struct perf_mmap *md, | ||
| 112 | unsigned long tail) | ||
| 113 | { | ||
| 114 | struct perf_event_mmap_page *pc = md->base; | ||
| 115 | |||
| 116 | /* | ||
| 117 | * ensure all reads are done before we write the tail out. | ||
| 118 | */ | ||
| 119 | /* mb(); */ | ||
| 120 | pc->data_tail = tail; | ||
| 121 | } | ||
| 122 | |||
| 97 | /* | 123 | /* |
| 98 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all | 124 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all |
| 99 | * counters in the current task. | 125 | * counters in the current task. |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e12d539417b2..f8c66d1435e0 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com> | 2 | * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com> |
| 3 | * | 3 | * |
| 4 | * Handle the callchains from the stream in an ad-hoc radix tree and then | 4 | * Handle the callchains from the stream in an ad-hoc radix tree and then |
| 5 | * sort them in an rbtree. | 5 | * sort them in an rbtree. |
| @@ -26,10 +26,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) | |||
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | #define chain_for_each_child(child, parent) \ | 28 | #define chain_for_each_child(child, parent) \ |
| 29 | list_for_each_entry(child, &parent->children, brothers) | 29 | list_for_each_entry(child, &parent->children, siblings) |
| 30 | 30 | ||
| 31 | #define chain_for_each_child_safe(child, next, parent) \ | 31 | #define chain_for_each_child_safe(child, next, parent) \ |
| 32 | list_for_each_entry_safe(child, next, &parent->children, brothers) | 32 | list_for_each_entry_safe(child, next, &parent->children, siblings) |
| 33 | 33 | ||
| 34 | static void | 34 | static void |
| 35 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 35 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
| @@ -38,14 +38,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
| 38 | struct rb_node **p = &root->rb_node; | 38 | struct rb_node **p = &root->rb_node; |
| 39 | struct rb_node *parent = NULL; | 39 | struct rb_node *parent = NULL; |
| 40 | struct callchain_node *rnode; | 40 | struct callchain_node *rnode; |
| 41 | u64 chain_cumul = cumul_hits(chain); | 41 | u64 chain_cumul = callchain_cumul_hits(chain); |
| 42 | 42 | ||
| 43 | while (*p) { | 43 | while (*p) { |
| 44 | u64 rnode_cumul; | 44 | u64 rnode_cumul; |
| 45 | 45 | ||
| 46 | parent = *p; | 46 | parent = *p; |
| 47 | rnode = rb_entry(parent, struct callchain_node, rb_node); | 47 | rnode = rb_entry(parent, struct callchain_node, rb_node); |
| 48 | rnode_cumul = cumul_hits(rnode); | 48 | rnode_cumul = callchain_cumul_hits(rnode); |
| 49 | 49 | ||
| 50 | switch (mode) { | 50 | switch (mode) { |
| 51 | case CHAIN_FLAT: | 51 | case CHAIN_FLAT: |
| @@ -104,7 +104,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, | |||
| 104 | 104 | ||
| 105 | chain_for_each_child(child, node) { | 105 | chain_for_each_child(child, node) { |
| 106 | __sort_chain_graph_abs(child, min_hit); | 106 | __sort_chain_graph_abs(child, min_hit); |
| 107 | if (cumul_hits(child) >= min_hit) | 107 | if (callchain_cumul_hits(child) >= min_hit) |
| 108 | rb_insert_callchain(&node->rb_root, child, | 108 | rb_insert_callchain(&node->rb_root, child, |
| 109 | CHAIN_GRAPH_ABS); | 109 | CHAIN_GRAPH_ABS); |
| 110 | } | 110 | } |
| @@ -129,7 +129,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node, | |||
| 129 | 129 | ||
| 130 | chain_for_each_child(child, node) { | 130 | chain_for_each_child(child, node) { |
| 131 | __sort_chain_graph_rel(child, min_percent); | 131 | __sort_chain_graph_rel(child, min_percent); |
| 132 | if (cumul_hits(child) >= min_hit) | 132 | if (callchain_cumul_hits(child) >= min_hit) |
| 133 | rb_insert_callchain(&node->rb_root, child, | 133 | rb_insert_callchain(&node->rb_root, child, |
| 134 | CHAIN_GRAPH_REL); | 134 | CHAIN_GRAPH_REL); |
| 135 | } | 135 | } |
| @@ -143,7 +143,7 @@ sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, | |||
| 143 | rb_root->rb_node = chain_root->node.rb_root.rb_node; | 143 | rb_root->rb_node = chain_root->node.rb_root.rb_node; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | int register_callchain_param(struct callchain_param *param) | 146 | int callchain_register_param(struct callchain_param *param) |
| 147 | { | 147 | { |
| 148 | switch (param->mode) { | 148 | switch (param->mode) { |
| 149 | case CHAIN_GRAPH_ABS: | 149 | case CHAIN_GRAPH_ABS: |
| @@ -189,32 +189,27 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
| 189 | chain_for_each_child(next, new) | 189 | chain_for_each_child(next, new) |
| 190 | next->parent = new; | 190 | next->parent = new; |
| 191 | } | 191 | } |
| 192 | list_add_tail(&new->brothers, &parent->children); | 192 | list_add_tail(&new->siblings, &parent->children); |
| 193 | 193 | ||
| 194 | return new; | 194 | return new; |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | 197 | ||
| 198 | struct resolved_ip { | ||
| 199 | u64 ip; | ||
| 200 | struct map_symbol ms; | ||
| 201 | }; | ||
| 202 | |||
| 203 | struct resolved_chain { | ||
| 204 | u64 nr; | ||
| 205 | struct resolved_ip ips[0]; | ||
| 206 | }; | ||
| 207 | |||
| 208 | |||
| 209 | /* | 198 | /* |
| 210 | * Fill the node with callchain values | 199 | * Fill the node with callchain values |
| 211 | */ | 200 | */ |
| 212 | static void | 201 | static void |
| 213 | fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | 202 | fill_node(struct callchain_node *node, struct callchain_cursor *cursor) |
| 214 | { | 203 | { |
| 215 | unsigned int i; | 204 | struct callchain_cursor_node *cursor_node; |
| 205 | |||
| 206 | node->val_nr = cursor->nr - cursor->pos; | ||
| 207 | if (!node->val_nr) | ||
| 208 | pr_warning("Warning: empty node in callchain tree\n"); | ||
| 216 | 209 | ||
| 217 | for (i = start; i < chain->nr; i++) { | 210 | cursor_node = callchain_cursor_current(cursor); |
| 211 | |||
| 212 | while (cursor_node) { | ||
| 218 | struct callchain_list *call; | 213 | struct callchain_list *call; |
| 219 | 214 | ||
| 220 | call = zalloc(sizeof(*call)); | 215 | call = zalloc(sizeof(*call)); |
| @@ -222,23 +217,25 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | |||
| 222 | perror("not enough memory for the code path tree"); | 217 | perror("not enough memory for the code path tree"); |
| 223 | return; | 218 | return; |
| 224 | } | 219 | } |
| 225 | call->ip = chain->ips[i].ip; | 220 | call->ip = cursor_node->ip; |
| 226 | call->ms = chain->ips[i].ms; | 221 | call->ms.sym = cursor_node->sym; |
| 222 | call->ms.map = cursor_node->map; | ||
| 227 | list_add_tail(&call->list, &node->val); | 223 | list_add_tail(&call->list, &node->val); |
| 224 | |||
| 225 | callchain_cursor_advance(cursor); | ||
| 226 | cursor_node = callchain_cursor_current(cursor); | ||
| 228 | } | 227 | } |
| 229 | node->val_nr = chain->nr - start; | ||
| 230 | if (!node->val_nr) | ||
| 231 | pr_warning("Warning: empty node in callchain tree\n"); | ||
| 232 | } | 228 | } |
| 233 | 229 | ||
| 234 | static void | 230 | static void |
| 235 | add_child(struct callchain_node *parent, struct resolved_chain *chain, | 231 | add_child(struct callchain_node *parent, |
| 236 | int start, u64 period) | 232 | struct callchain_cursor *cursor, |
| 233 | u64 period) | ||
| 237 | { | 234 | { |
| 238 | struct callchain_node *new; | 235 | struct callchain_node *new; |
| 239 | 236 | ||
| 240 | new = create_child(parent, false); | 237 | new = create_child(parent, false); |
| 241 | fill_node(new, chain, start); | 238 | fill_node(new, cursor); |
| 242 | 239 | ||
| 243 | new->children_hit = 0; | 240 | new->children_hit = 0; |
| 244 | new->hit = period; | 241 | new->hit = period; |
| @@ -250,9 +247,10 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
| 250 | * Then create another child to host the given callchain of new branch | 247 | * Then create another child to host the given callchain of new branch |
| 251 | */ | 248 | */ |
| 252 | static void | 249 | static void |
| 253 | split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | 250 | split_add_child(struct callchain_node *parent, |
| 254 | struct callchain_list *to_split, int idx_parents, int idx_local, | 251 | struct callchain_cursor *cursor, |
| 255 | u64 period) | 252 | struct callchain_list *to_split, |
| 253 | u64 idx_parents, u64 idx_local, u64 period) | ||
| 256 | { | 254 | { |
| 257 | struct callchain_node *new; | 255 | struct callchain_node *new; |
| 258 | struct list_head *old_tail; | 256 | struct list_head *old_tail; |
| @@ -272,14 +270,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
| 272 | /* split the hits */ | 270 | /* split the hits */ |
| 273 | new->hit = parent->hit; | 271 | new->hit = parent->hit; |
| 274 | new->children_hit = parent->children_hit; | 272 | new->children_hit = parent->children_hit; |
| 275 | parent->children_hit = cumul_hits(new); | 273 | parent->children_hit = callchain_cumul_hits(new); |
| 276 | new->val_nr = parent->val_nr - idx_local; | 274 | new->val_nr = parent->val_nr - idx_local; |
| 277 | parent->val_nr = idx_local; | 275 | parent->val_nr = idx_local; |
| 278 | 276 | ||
| 279 | /* create a new child for the new branch if any */ | 277 | /* create a new child for the new branch if any */ |
| 280 | if (idx_total < chain->nr) { | 278 | if (idx_total < cursor->nr) { |
| 281 | parent->hit = 0; | 279 | parent->hit = 0; |
| 282 | add_child(parent, chain, idx_total, period); | 280 | add_child(parent, cursor, period); |
| 283 | parent->children_hit += period; | 281 | parent->children_hit += period; |
| 284 | } else { | 282 | } else { |
| 285 | parent->hit = period; | 283 | parent->hit = period; |
| @@ -287,36 +285,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
| 287 | } | 285 | } |
| 288 | 286 | ||
| 289 | static int | 287 | static int |
| 290 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 288 | append_chain(struct callchain_node *root, |
| 291 | unsigned int start, u64 period); | 289 | struct callchain_cursor *cursor, |
| 290 | u64 period); | ||
| 292 | 291 | ||
| 293 | static void | 292 | static void |
| 294 | append_chain_children(struct callchain_node *root, struct resolved_chain *chain, | 293 | append_chain_children(struct callchain_node *root, |
| 295 | unsigned int start, u64 period) | 294 | struct callchain_cursor *cursor, |
| 295 | u64 period) | ||
| 296 | { | 296 | { |
| 297 | struct callchain_node *rnode; | 297 | struct callchain_node *rnode; |
| 298 | 298 | ||
| 299 | /* lookup in childrens */ | 299 | /* lookup in childrens */ |
| 300 | chain_for_each_child(rnode, root) { | 300 | chain_for_each_child(rnode, root) { |
| 301 | unsigned int ret = append_chain(rnode, chain, start, period); | 301 | unsigned int ret = append_chain(rnode, cursor, period); |
| 302 | 302 | ||
| 303 | if (!ret) | 303 | if (!ret) |
| 304 | goto inc_children_hit; | 304 | goto inc_children_hit; |
| 305 | } | 305 | } |
| 306 | /* nothing in children, add to the current node */ | 306 | /* nothing in children, add to the current node */ |
| 307 | add_child(root, chain, start, period); | 307 | add_child(root, cursor, period); |
| 308 | 308 | ||
| 309 | inc_children_hit: | 309 | inc_children_hit: |
| 310 | root->children_hit += period; | 310 | root->children_hit += period; |
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | static int | 313 | static int |
| 314 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 314 | append_chain(struct callchain_node *root, |
| 315 | unsigned int start, u64 period) | 315 | struct callchain_cursor *cursor, |
| 316 | u64 period) | ||
| 316 | { | 317 | { |
| 318 | struct callchain_cursor_node *curr_snap = cursor->curr; | ||
| 317 | struct callchain_list *cnode; | 319 | struct callchain_list *cnode; |
| 318 | unsigned int i = start; | 320 | u64 start = cursor->pos; |
| 319 | bool found = false; | 321 | bool found = false; |
| 322 | u64 matches; | ||
| 320 | 323 | ||
| 321 | /* | 324 | /* |
| 322 | * Lookup in the current node | 325 | * Lookup in the current node |
| @@ -324,141 +327,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain, | |||
| 324 | * anywhere inside a function. | 327 | * anywhere inside a function. |
| 325 | */ | 328 | */ |
| 326 | list_for_each_entry(cnode, &root->val, list) { | 329 | list_for_each_entry(cnode, &root->val, list) { |
| 330 | struct callchain_cursor_node *node; | ||
| 327 | struct symbol *sym; | 331 | struct symbol *sym; |
| 328 | 332 | ||
| 329 | if (i == chain->nr) | 333 | node = callchain_cursor_current(cursor); |
| 334 | if (!node) | ||
| 330 | break; | 335 | break; |
| 331 | 336 | ||
| 332 | sym = chain->ips[i].ms.sym; | 337 | sym = node->sym; |
| 333 | 338 | ||
| 334 | if (cnode->ms.sym && sym) { | 339 | if (cnode->ms.sym && sym) { |
| 335 | if (cnode->ms.sym->start != sym->start) | 340 | if (cnode->ms.sym->start != sym->start) |
| 336 | break; | 341 | break; |
| 337 | } else if (cnode->ip != chain->ips[i].ip) | 342 | } else if (cnode->ip != node->ip) |
| 338 | break; | 343 | break; |
| 339 | 344 | ||
| 340 | if (!found) | 345 | if (!found) |
| 341 | found = true; | 346 | found = true; |
| 342 | i++; | 347 | |
| 348 | callchain_cursor_advance(cursor); | ||
| 343 | } | 349 | } |
| 344 | 350 | ||
| 345 | /* matches not, relay on the parent */ | 351 | /* matches not, relay on the parent */ |
| 346 | if (!found) | 352 | if (!found) { |
| 353 | cursor->curr = curr_snap; | ||
| 354 | cursor->pos = start; | ||
| 347 | return -1; | 355 | return -1; |
| 356 | } | ||
| 357 | |||
| 358 | matches = cursor->pos - start; | ||
| 348 | 359 | ||
| 349 | /* we match only a part of the node. Split it and add the new chain */ | 360 | /* we match only a part of the node. Split it and add the new chain */ |
| 350 | if (i - start < root->val_nr) { | 361 | if (matches < root->val_nr) { |
| 351 | split_add_child(root, chain, cnode, start, i - start, period); | 362 | split_add_child(root, cursor, cnode, start, matches, period); |
| 352 | return 0; | 363 | return 0; |
| 353 | } | 364 | } |
| 354 | 365 | ||
| 355 | /* we match 100% of the path, increment the hit */ | 366 | /* we match 100% of the path, increment the hit */ |
| 356 | if (i - start == root->val_nr && i == chain->nr) { | 367 | if (matches == root->val_nr && cursor->pos == cursor->nr) { |
| 357 | root->hit += period; | 368 | root->hit += period; |
| 358 | return 0; | 369 | return 0; |
| 359 | } | 370 | } |
| 360 | 371 | ||
| 361 | /* We match the node and still have a part remaining */ | 372 | /* We match the node and still have a part remaining */ |
| 362 | append_chain_children(root, chain, i, period); | 373 | append_chain_children(root, cursor, period); |
| 363 | 374 | ||
| 364 | return 0; | 375 | return 0; |
| 365 | } | 376 | } |
| 366 | 377 | ||
| 367 | static void filter_context(struct ip_callchain *old, struct resolved_chain *new, | 378 | int callchain_append(struct callchain_root *root, |
| 368 | struct map_symbol *syms) | 379 | struct callchain_cursor *cursor, |
| 369 | { | 380 | u64 period) |
| 370 | int i, j = 0; | ||
| 371 | |||
| 372 | for (i = 0; i < (int)old->nr; i++) { | ||
| 373 | if (old->ips[i] >= PERF_CONTEXT_MAX) | ||
| 374 | continue; | ||
| 375 | |||
| 376 | new->ips[j].ip = old->ips[i]; | ||
| 377 | new->ips[j].ms = syms[i]; | ||
| 378 | j++; | ||
| 379 | } | ||
| 380 | |||
| 381 | new->nr = j; | ||
| 382 | } | ||
| 383 | |||
| 384 | |||
| 385 | int callchain_append(struct callchain_root *root, struct ip_callchain *chain, | ||
| 386 | struct map_symbol *syms, u64 period) | ||
| 387 | { | 381 | { |
| 388 | struct resolved_chain *filtered; | 382 | if (!cursor->nr) |
| 389 | |||
| 390 | if (!chain->nr) | ||
| 391 | return 0; | 383 | return 0; |
| 392 | 384 | ||
| 393 | filtered = zalloc(sizeof(*filtered) + | 385 | callchain_cursor_commit(cursor); |
| 394 | chain->nr * sizeof(struct resolved_ip)); | ||
| 395 | if (!filtered) | ||
| 396 | return -ENOMEM; | ||
| 397 | |||
| 398 | filter_context(chain, filtered, syms); | ||
| 399 | |||
| 400 | if (!filtered->nr) | ||
| 401 | goto end; | ||
| 402 | 386 | ||
| 403 | append_chain_children(&root->node, filtered, 0, period); | 387 | append_chain_children(&root->node, cursor, period); |
| 404 | 388 | ||
| 405 | if (filtered->nr > root->max_depth) | 389 | if (cursor->nr > root->max_depth) |
| 406 | root->max_depth = filtered->nr; | 390 | root->max_depth = cursor->nr; |
| 407 | end: | ||
| 408 | free(filtered); | ||
| 409 | 391 | ||
| 410 | return 0; | 392 | return 0; |
| 411 | } | 393 | } |
| 412 | 394 | ||
| 413 | static int | 395 | static int |
| 414 | merge_chain_branch(struct callchain_node *dst, struct callchain_node *src, | 396 | merge_chain_branch(struct callchain_cursor *cursor, |
| 415 | struct resolved_chain *chain) | 397 | struct callchain_node *dst, struct callchain_node *src) |
| 416 | { | 398 | { |
| 399 | struct callchain_cursor_node **old_last = cursor->last; | ||
| 417 | struct callchain_node *child, *next_child; | 400 | struct callchain_node *child, *next_child; |
| 418 | struct callchain_list *list, *next_list; | 401 | struct callchain_list *list, *next_list; |
| 419 | int old_pos = chain->nr; | 402 | int old_pos = cursor->nr; |
| 420 | int err = 0; | 403 | int err = 0; |
| 421 | 404 | ||
| 422 | list_for_each_entry_safe(list, next_list, &src->val, list) { | 405 | list_for_each_entry_safe(list, next_list, &src->val, list) { |
| 423 | chain->ips[chain->nr].ip = list->ip; | 406 | callchain_cursor_append(cursor, list->ip, |
| 424 | chain->ips[chain->nr].ms = list->ms; | 407 | list->ms.map, list->ms.sym); |
| 425 | chain->nr++; | ||
| 426 | list_del(&list->list); | 408 | list_del(&list->list); |
| 427 | free(list); | 409 | free(list); |
| 428 | } | 410 | } |
| 429 | 411 | ||
| 430 | if (src->hit) | 412 | if (src->hit) { |
| 431 | append_chain_children(dst, chain, 0, src->hit); | 413 | callchain_cursor_commit(cursor); |
| 414 | append_chain_children(dst, cursor, src->hit); | ||
| 415 | } | ||
| 432 | 416 | ||
| 433 | chain_for_each_child_safe(child, next_child, src) { | 417 | chain_for_each_child_safe(child, next_child, src) { |
| 434 | err = merge_chain_branch(dst, child, chain); | 418 | err = merge_chain_branch(cursor, dst, child); |
| 435 | if (err) | 419 | if (err) |
| 436 | break; | 420 | break; |
| 437 | 421 | ||
| 438 | list_del(&child->brothers); | 422 | list_del(&child->siblings); |
| 439 | free(child); | 423 | free(child); |
| 440 | } | 424 | } |
| 441 | 425 | ||
| 442 | chain->nr = old_pos; | 426 | cursor->nr = old_pos; |
| 427 | cursor->last = old_last; | ||
| 443 | 428 | ||
| 444 | return err; | 429 | return err; |
| 445 | } | 430 | } |
| 446 | 431 | ||
| 447 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src) | 432 | int callchain_merge(struct callchain_cursor *cursor, |
| 433 | struct callchain_root *dst, struct callchain_root *src) | ||
| 434 | { | ||
| 435 | return merge_chain_branch(cursor, &dst->node, &src->node); | ||
| 436 | } | ||
| 437 | |||
| 438 | int callchain_cursor_append(struct callchain_cursor *cursor, | ||
| 439 | u64 ip, struct map *map, struct symbol *sym) | ||
| 448 | { | 440 | { |
| 449 | struct resolved_chain *chain; | 441 | struct callchain_cursor_node *node = *cursor->last; |
| 450 | int err; | ||
| 451 | 442 | ||
| 452 | chain = malloc(sizeof(*chain) + | 443 | if (!node) { |
| 453 | src->max_depth * sizeof(struct resolved_ip)); | 444 | node = calloc(sizeof(*node), 1); |
| 454 | if (!chain) | 445 | if (!node) |
| 455 | return -ENOMEM; | 446 | return -ENOMEM; |
| 456 | 447 | ||
| 457 | chain->nr = 0; | 448 | *cursor->last = node; |
| 449 | } | ||
| 458 | 450 | ||
| 459 | err = merge_chain_branch(&dst->node, &src->node, chain); | 451 | node->ip = ip; |
| 452 | node->map = map; | ||
| 453 | node->sym = sym; | ||
| 460 | 454 | ||
| 461 | free(chain); | 455 | cursor->nr++; |
| 462 | 456 | ||
| 463 | return err; | 457 | cursor->last = &node->next; |
| 458 | |||
| 459 | return 0; | ||
| 464 | } | 460 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index c15fb8c24ad2..67137256a1cd 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -16,7 +16,7 @@ enum chain_mode { | |||
| 16 | 16 | ||
| 17 | struct callchain_node { | 17 | struct callchain_node { |
| 18 | struct callchain_node *parent; | 18 | struct callchain_node *parent; |
| 19 | struct list_head brothers; | 19 | struct list_head siblings; |
| 20 | struct list_head children; | 20 | struct list_head children; |
| 21 | struct list_head val; | 21 | struct list_head val; |
| 22 | struct rb_node rb_node; /* to sort nodes in an rbtree */ | 22 | struct rb_node rb_node; /* to sort nodes in an rbtree */ |
| @@ -49,9 +49,30 @@ struct callchain_list { | |||
| 49 | struct list_head list; | 49 | struct list_head list; |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | /* | ||
| 53 | * A callchain cursor is a single linked list that | ||
| 54 | * let one feed a callchain progressively. | ||
| 55 | * It keeps persitent allocated entries to minimize | ||
| 56 | * allocations. | ||
| 57 | */ | ||
| 58 | struct callchain_cursor_node { | ||
| 59 | u64 ip; | ||
| 60 | struct map *map; | ||
| 61 | struct symbol *sym; | ||
| 62 | struct callchain_cursor_node *next; | ||
| 63 | }; | ||
| 64 | |||
| 65 | struct callchain_cursor { | ||
| 66 | u64 nr; | ||
| 67 | struct callchain_cursor_node *first; | ||
| 68 | struct callchain_cursor_node **last; | ||
| 69 | u64 pos; | ||
| 70 | struct callchain_cursor_node *curr; | ||
| 71 | }; | ||
| 72 | |||
| 52 | static inline void callchain_init(struct callchain_root *root) | 73 | static inline void callchain_init(struct callchain_root *root) |
| 53 | { | 74 | { |
| 54 | INIT_LIST_HEAD(&root->node.brothers); | 75 | INIT_LIST_HEAD(&root->node.siblings); |
| 55 | INIT_LIST_HEAD(&root->node.children); | 76 | INIT_LIST_HEAD(&root->node.children); |
| 56 | INIT_LIST_HEAD(&root->node.val); | 77 | INIT_LIST_HEAD(&root->node.val); |
| 57 | 78 | ||
| @@ -61,15 +82,54 @@ static inline void callchain_init(struct callchain_root *root) | |||
| 61 | root->max_depth = 0; | 82 | root->max_depth = 0; |
| 62 | } | 83 | } |
| 63 | 84 | ||
| 64 | static inline u64 cumul_hits(struct callchain_node *node) | 85 | static inline u64 callchain_cumul_hits(struct callchain_node *node) |
| 65 | { | 86 | { |
| 66 | return node->hit + node->children_hit; | 87 | return node->hit + node->children_hit; |
| 67 | } | 88 | } |
| 68 | 89 | ||
| 69 | int register_callchain_param(struct callchain_param *param); | 90 | int callchain_register_param(struct callchain_param *param); |
| 70 | int callchain_append(struct callchain_root *root, struct ip_callchain *chain, | 91 | int callchain_append(struct callchain_root *root, |
| 71 | struct map_symbol *syms, u64 period); | 92 | struct callchain_cursor *cursor, |
| 72 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src); | 93 | u64 period); |
| 94 | |||
| 95 | int callchain_merge(struct callchain_cursor *cursor, | ||
| 96 | struct callchain_root *dst, struct callchain_root *src); | ||
| 73 | 97 | ||
| 74 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); | 98 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); |
| 99 | |||
| 100 | /* | ||
| 101 | * Initialize a cursor before adding entries inside, but keep | ||
| 102 | * the previously allocated entries as a cache. | ||
| 103 | */ | ||
| 104 | static inline void callchain_cursor_reset(struct callchain_cursor *cursor) | ||
| 105 | { | ||
| 106 | cursor->nr = 0; | ||
| 107 | cursor->last = &cursor->first; | ||
| 108 | } | ||
| 109 | |||
| 110 | int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, | ||
| 111 | struct map *map, struct symbol *sym); | ||
| 112 | |||
| 113 | /* Close a cursor writing session. Initialize for the reader */ | ||
| 114 | static inline void callchain_cursor_commit(struct callchain_cursor *cursor) | ||
| 115 | { | ||
| 116 | cursor->curr = cursor->first; | ||
| 117 | cursor->pos = 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | /* Cursor reading iteration helpers */ | ||
| 121 | static inline struct callchain_cursor_node * | ||
| 122 | callchain_cursor_current(struct callchain_cursor *cursor) | ||
| 123 | { | ||
| 124 | if (cursor->pos == cursor->nr) | ||
| 125 | return NULL; | ||
| 126 | |||
| 127 | return cursor->curr; | ||
| 128 | } | ||
| 129 | |||
| 130 | static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | ||
| 131 | { | ||
| 132 | cursor->curr = cursor->curr->next; | ||
| 133 | cursor->pos++; | ||
| 134 | } | ||
| 75 | #endif /* __PERF_CALLCHAIN_H */ | 135 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 3ccaa1043383..6893eec693ab 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
| @@ -177,3 +177,8 @@ struct cpu_map *cpu_map__dummy_new(void) | |||
| 177 | 177 | ||
| 178 | return cpus; | 178 | return cpus; |
| 179 | } | 179 | } |
| 180 | |||
| 181 | void cpu_map__delete(struct cpu_map *map) | ||
| 182 | { | ||
| 183 | free(map); | ||
| 184 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index f7a4f42f6307..072c0a374794 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
| @@ -8,6 +8,6 @@ struct cpu_map { | |||
| 8 | 8 | ||
| 9 | struct cpu_map *cpu_map__new(const char *cpu_list); | 9 | struct cpu_map *cpu_map__new(const char *cpu_list); |
| 10 | struct cpu_map *cpu_map__dummy_new(void); | 10 | struct cpu_map *cpu_map__dummy_new(void); |
| 11 | void *cpu_map__delete(struct cpu_map *map); | 11 | void cpu_map__delete(struct cpu_map *map); |
| 12 | 12 | ||
| 13 | #endif /* __PERF_CPUMAP_H */ | 13 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1478ab4ee222..e4db8b888546 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -826,128 +826,3 @@ out_filtered: | |||
| 826 | al->filtered = true; | 826 | al->filtered = true; |
| 827 | return 0; | 827 | return 0; |
| 828 | } | 828 | } |
| 829 | |||
| 830 | static int event__parse_id_sample(const event_t *event, | ||
| 831 | struct perf_session *session, | ||
| 832 | struct sample_data *sample) | ||
| 833 | { | ||
| 834 | const u64 *array; | ||
| 835 | u64 type; | ||
| 836 | |||
| 837 | sample->cpu = sample->pid = sample->tid = -1; | ||
| 838 | sample->stream_id = sample->id = sample->time = -1ULL; | ||
| 839 | |||
| 840 | if (!session->sample_id_all) | ||
| 841 | return 0; | ||
| 842 | |||
| 843 | array = event->sample.array; | ||
| 844 | array += ((event->header.size - | ||
| 845 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
| 846 | type = session->sample_type; | ||
| 847 | |||
| 848 | if (type & PERF_SAMPLE_CPU) { | ||
| 849 | u32 *p = (u32 *)array; | ||
| 850 | sample->cpu = *p; | ||
| 851 | array--; | ||
| 852 | } | ||
| 853 | |||
| 854 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
| 855 | sample->stream_id = *array; | ||
| 856 | array--; | ||
| 857 | } | ||
| 858 | |||
| 859 | if (type & PERF_SAMPLE_ID) { | ||
| 860 | sample->id = *array; | ||
| 861 | array--; | ||
| 862 | } | ||
| 863 | |||
| 864 | if (type & PERF_SAMPLE_TIME) { | ||
| 865 | sample->time = *array; | ||
| 866 | array--; | ||
| 867 | } | ||
| 868 | |||
| 869 | if (type & PERF_SAMPLE_TID) { | ||
| 870 | u32 *p = (u32 *)array; | ||
| 871 | sample->pid = p[0]; | ||
| 872 | sample->tid = p[1]; | ||
| 873 | } | ||
| 874 | |||
| 875 | return 0; | ||
| 876 | } | ||
| 877 | |||
| 878 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
| 879 | struct sample_data *data) | ||
| 880 | { | ||
| 881 | const u64 *array; | ||
| 882 | u64 type; | ||
| 883 | |||
| 884 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
| 885 | return event__parse_id_sample(event, session, data); | ||
| 886 | |||
| 887 | array = event->sample.array; | ||
| 888 | type = session->sample_type; | ||
| 889 | |||
| 890 | if (type & PERF_SAMPLE_IP) { | ||
| 891 | data->ip = event->ip.ip; | ||
| 892 | array++; | ||
| 893 | } | ||
| 894 | |||
| 895 | if (type & PERF_SAMPLE_TID) { | ||
| 896 | u32 *p = (u32 *)array; | ||
| 897 | data->pid = p[0]; | ||
| 898 | data->tid = p[1]; | ||
| 899 | array++; | ||
| 900 | } | ||
| 901 | |||
| 902 | if (type & PERF_SAMPLE_TIME) { | ||
| 903 | data->time = *array; | ||
| 904 | array++; | ||
| 905 | } | ||
| 906 | |||
| 907 | if (type & PERF_SAMPLE_ADDR) { | ||
| 908 | data->addr = *array; | ||
| 909 | array++; | ||
| 910 | } | ||
| 911 | |||
| 912 | data->id = -1ULL; | ||
| 913 | if (type & PERF_SAMPLE_ID) { | ||
| 914 | data->id = *array; | ||
| 915 | array++; | ||
| 916 | } | ||
| 917 | |||
| 918 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
| 919 | data->stream_id = *array; | ||
| 920 | array++; | ||
| 921 | } | ||
| 922 | |||
| 923 | if (type & PERF_SAMPLE_CPU) { | ||
| 924 | u32 *p = (u32 *)array; | ||
| 925 | data->cpu = *p; | ||
| 926 | array++; | ||
| 927 | } else | ||
| 928 | data->cpu = -1; | ||
| 929 | |||
| 930 | if (type & PERF_SAMPLE_PERIOD) { | ||
| 931 | data->period = *array; | ||
| 932 | array++; | ||
| 933 | } | ||
| 934 | |||
| 935 | if (type & PERF_SAMPLE_READ) { | ||
| 936 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
| 937 | return -1; | ||
| 938 | } | ||
| 939 | |||
| 940 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
| 941 | data->callchain = (struct ip_callchain *)array; | ||
| 942 | array += 1 + data->callchain->nr; | ||
| 943 | } | ||
| 944 | |||
| 945 | if (type & PERF_SAMPLE_RAW) { | ||
| 946 | u32 *p = (u32 *)array; | ||
| 947 | data->raw_size = *p; | ||
| 948 | p++; | ||
| 949 | data->raw_data = p; | ||
| 950 | } | ||
| 951 | |||
| 952 | return 0; | ||
| 953 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 2b7e91902f10..d79e4edd82f9 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -169,9 +169,10 @@ struct addr_location; | |||
| 169 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 169 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
| 170 | struct addr_location *al, struct sample_data *data, | 170 | struct addr_location *al, struct sample_data *data, |
| 171 | symbol_filter_t filter); | 171 | symbol_filter_t filter); |
| 172 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
| 173 | struct sample_data *sample); | ||
| 174 | 172 | ||
| 175 | const char *event__get_event_name(unsigned int id); | 173 | const char *event__get_event_name(unsigned int id); |
| 176 | 174 | ||
| 175 | int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, | ||
| 176 | struct sample_data *sample); | ||
| 177 | |||
| 177 | #endif /* __PERF_RECORD_H */ | 178 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c new file mode 100644 index 000000000000..df0610e9c61b --- /dev/null +++ b/tools/perf/util/evlist.c | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | #include <poll.h> | ||
| 2 | #include "evlist.h" | ||
| 3 | #include "evsel.h" | ||
| 4 | #include "util.h" | ||
| 5 | |||
| 6 | #include <linux/bitops.h> | ||
| 7 | #include <linux/hash.h> | ||
| 8 | |||
| 9 | void perf_evlist__init(struct perf_evlist *evlist) | ||
| 10 | { | ||
| 11 | int i; | ||
| 12 | |||
| 13 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) | ||
| 14 | INIT_HLIST_HEAD(&evlist->heads[i]); | ||
| 15 | INIT_LIST_HEAD(&evlist->entries); | ||
| 16 | } | ||
| 17 | |||
| 18 | struct perf_evlist *perf_evlist__new(void) | ||
| 19 | { | ||
| 20 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | ||
| 21 | |||
| 22 | if (evlist != NULL) | ||
| 23 | perf_evlist__init(evlist); | ||
| 24 | |||
| 25 | return evlist; | ||
| 26 | } | ||
| 27 | |||
| 28 | static void perf_evlist__purge(struct perf_evlist *evlist) | ||
| 29 | { | ||
| 30 | struct perf_evsel *pos, *n; | ||
| 31 | |||
| 32 | list_for_each_entry_safe(pos, n, &evlist->entries, node) { | ||
| 33 | list_del_init(&pos->node); | ||
| 34 | perf_evsel__delete(pos); | ||
| 35 | } | ||
| 36 | |||
| 37 | evlist->nr_entries = 0; | ||
| 38 | } | ||
| 39 | |||
| 40 | void perf_evlist__exit(struct perf_evlist *evlist) | ||
| 41 | { | ||
| 42 | free(evlist->mmap); | ||
| 43 | free(evlist->pollfd); | ||
| 44 | evlist->mmap = NULL; | ||
| 45 | evlist->pollfd = NULL; | ||
| 46 | } | ||
| 47 | |||
| 48 | void perf_evlist__delete(struct perf_evlist *evlist) | ||
| 49 | { | ||
| 50 | perf_evlist__purge(evlist); | ||
| 51 | perf_evlist__exit(evlist); | ||
| 52 | free(evlist); | ||
| 53 | } | ||
| 54 | |||
| 55 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | ||
| 56 | { | ||
| 57 | list_add_tail(&entry->node, &evlist->entries); | ||
| 58 | ++evlist->nr_entries; | ||
| 59 | } | ||
| 60 | |||
| 61 | int perf_evlist__add_default(struct perf_evlist *evlist) | ||
| 62 | { | ||
| 63 | struct perf_event_attr attr = { | ||
| 64 | .type = PERF_TYPE_HARDWARE, | ||
| 65 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
| 66 | }; | ||
| 67 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | ||
| 68 | |||
| 69 | if (evsel == NULL) | ||
| 70 | return -ENOMEM; | ||
| 71 | |||
| 72 | perf_evlist__add(evlist, evsel); | ||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads) | ||
| 77 | { | ||
| 78 | int nfds = ncpus * nthreads * evlist->nr_entries; | ||
| 79 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | ||
| 80 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | ||
| 81 | } | ||
| 82 | |||
| 83 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | ||
| 84 | { | ||
| 85 | fcntl(fd, F_SETFL, O_NONBLOCK); | ||
| 86 | evlist->pollfd[evlist->nr_fds].fd = fd; | ||
| 87 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
| 88 | evlist->nr_fds++; | ||
| 89 | } | ||
| 90 | |||
| 91 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | ||
| 92 | { | ||
| 93 | struct hlist_head *head; | ||
| 94 | struct hlist_node *pos; | ||
| 95 | struct perf_sample_id *sid; | ||
| 96 | int hash; | ||
| 97 | |||
| 98 | if (evlist->nr_entries == 1) | ||
| 99 | return list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 100 | |||
| 101 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | ||
| 102 | head = &evlist->heads[hash]; | ||
| 103 | |||
| 104 | hlist_for_each_entry(sid, pos, head, node) | ||
| 105 | if (sid->id == id) | ||
| 106 | return sid->evsel; | ||
| 107 | return NULL; | ||
| 108 | } | ||
| 109 | |||
| 110 | event_t *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu) | ||
| 111 | { | ||
| 112 | /* XXX Move this to perf.c, making it generally available */ | ||
| 113 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
| 114 | struct perf_mmap *md = &evlist->mmap[cpu]; | ||
| 115 | unsigned int head = perf_mmap__read_head(md); | ||
| 116 | unsigned int old = md->prev; | ||
| 117 | unsigned char *data = md->base + page_size; | ||
| 118 | event_t *event = NULL; | ||
| 119 | int diff; | ||
| 120 | |||
| 121 | /* | ||
| 122 | * If we're further behind than half the buffer, there's a chance | ||
| 123 | * the writer will bite our tail and mess up the samples under us. | ||
| 124 | * | ||
| 125 | * If we somehow ended up ahead of the head, we got messed up. | ||
| 126 | * | ||
| 127 | * In either case, truncate and restart at head. | ||
| 128 | */ | ||
| 129 | diff = head - old; | ||
| 130 | if (diff > md->mask / 2 || diff < 0) { | ||
| 131 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | ||
| 132 | |||
| 133 | /* | ||
| 134 | * head points to a known good entry, start there. | ||
| 135 | */ | ||
| 136 | old = head; | ||
| 137 | } | ||
| 138 | |||
| 139 | if (old != head) { | ||
| 140 | size_t size; | ||
| 141 | |||
| 142 | event = (event_t *)&data[old & md->mask]; | ||
| 143 | size = event->header.size; | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Event straddles the mmap boundary -- header should always | ||
| 147 | * be inside due to u64 alignment of output. | ||
| 148 | */ | ||
| 149 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | ||
| 150 | unsigned int offset = old; | ||
| 151 | unsigned int len = min(sizeof(*event), size), cpy; | ||
| 152 | void *dst = &evlist->event_copy; | ||
| 153 | |||
| 154 | do { | ||
| 155 | cpy = min(md->mask + 1 - (offset & md->mask), len); | ||
| 156 | memcpy(dst, &data[offset & md->mask], cpy); | ||
| 157 | offset += cpy; | ||
| 158 | dst += cpy; | ||
| 159 | len -= cpy; | ||
| 160 | } while (len); | ||
| 161 | |||
| 162 | event = &evlist->event_copy; | ||
| 163 | } | ||
| 164 | |||
| 165 | old += size; | ||
| 166 | } | ||
| 167 | |||
| 168 | md->prev = old; | ||
| 169 | return event; | ||
| 170 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h new file mode 100644 index 000000000000..acbe48eac608 --- /dev/null +++ b/tools/perf/util/evlist.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #ifndef __PERF_EVLIST_H | ||
| 2 | #define __PERF_EVLIST_H 1 | ||
| 3 | |||
| 4 | #include <linux/list.h> | ||
| 5 | #include "../perf.h" | ||
| 6 | #include "event.h" | ||
| 7 | |||
| 8 | struct pollfd; | ||
| 9 | |||
| 10 | #define PERF_EVLIST__HLIST_BITS 8 | ||
| 11 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | ||
| 12 | |||
| 13 | struct perf_evlist { | ||
| 14 | struct list_head entries; | ||
| 15 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | ||
| 16 | int nr_entries; | ||
| 17 | int nr_fds; | ||
| 18 | int mmap_len; | ||
| 19 | event_t event_copy; | ||
| 20 | struct perf_mmap *mmap; | ||
| 21 | struct pollfd *pollfd; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct perf_evsel; | ||
| 25 | |||
| 26 | struct perf_evlist *perf_evlist__new(void); | ||
| 27 | void perf_evlist__init(struct perf_evlist *evlist); | ||
| 28 | void perf_evlist__exit(struct perf_evlist *evlist); | ||
| 29 | void perf_evlist__delete(struct perf_evlist *evlist); | ||
| 30 | |||
| 31 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); | ||
| 32 | int perf_evlist__add_default(struct perf_evlist *evlist); | ||
| 33 | |||
| 34 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads); | ||
| 35 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | ||
| 36 | |||
| 37 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | ||
| 38 | |||
| 39 | event_t *perf_evlist__read_on_cpu(struct perf_evlist *self, int cpu); | ||
| 40 | |||
| 41 | #endif /* __PERF_EVLIST_H */ | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f5cfed60af98..76ab553637d6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -1,20 +1,33 @@ | |||
| 1 | #include "evsel.h" | 1 | #include "evsel.h" |
| 2 | #include "evlist.h" | ||
| 2 | #include "../perf.h" | 3 | #include "../perf.h" |
| 3 | #include "util.h" | 4 | #include "util.h" |
| 4 | #include "cpumap.h" | 5 | #include "cpumap.h" |
| 5 | #include "thread.h" | 6 | #include "thread_map.h" |
| 7 | |||
| 8 | #include <unistd.h> | ||
| 9 | #include <sys/mman.h> | ||
| 10 | |||
| 11 | #include <linux/bitops.h> | ||
| 12 | #include <linux/hash.h> | ||
| 6 | 13 | ||
| 7 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 14 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
| 15 | #define SID(e, x, y) xyarray__entry(e->id, x, y) | ||
| 16 | |||
| 17 | void perf_evsel__init(struct perf_evsel *evsel, | ||
| 18 | struct perf_event_attr *attr, int idx) | ||
| 19 | { | ||
| 20 | evsel->idx = idx; | ||
| 21 | evsel->attr = *attr; | ||
| 22 | INIT_LIST_HEAD(&evsel->node); | ||
| 23 | } | ||
| 8 | 24 | ||
| 9 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 25 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
| 10 | { | 26 | { |
| 11 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 27 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
| 12 | 28 | ||
| 13 | if (evsel != NULL) { | 29 | if (evsel != NULL) |
| 14 | evsel->idx = idx; | 30 | perf_evsel__init(evsel, attr, idx); |
| 15 | evsel->attr = *attr; | ||
| 16 | INIT_LIST_HEAD(&evsel->node); | ||
| 17 | } | ||
| 18 | 31 | ||
| 19 | return evsel; | 32 | return evsel; |
| 20 | } | 33 | } |
| @@ -25,6 +38,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
| 25 | return evsel->fd != NULL ? 0 : -ENOMEM; | 38 | return evsel->fd != NULL ? 0 : -ENOMEM; |
| 26 | } | 39 | } |
| 27 | 40 | ||
| 41 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
| 42 | { | ||
| 43 | evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | ||
| 44 | return evsel->id != NULL ? 0 : -ENOMEM; | ||
| 45 | } | ||
| 46 | |||
| 28 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 47 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
| 29 | { | 48 | { |
| 30 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 49 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
| @@ -38,6 +57,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) | |||
| 38 | evsel->fd = NULL; | 57 | evsel->fd = NULL; |
| 39 | } | 58 | } |
| 40 | 59 | ||
| 60 | void perf_evsel__free_id(struct perf_evsel *evsel) | ||
| 61 | { | ||
| 62 | xyarray__delete(evsel->id); | ||
| 63 | evsel->id = NULL; | ||
| 64 | } | ||
| 65 | |||
| 41 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 66 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
| 42 | { | 67 | { |
| 43 | int cpu, thread; | 68 | int cpu, thread; |
| @@ -49,10 +74,34 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
| 49 | } | 74 | } |
| 50 | } | 75 | } |
| 51 | 76 | ||
| 52 | void perf_evsel__delete(struct perf_evsel *evsel) | 77 | void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus) |
| 78 | { | ||
| 79 | int cpu; | ||
| 80 | |||
| 81 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
| 82 | if (evlist->mmap[cpu].base != NULL) { | ||
| 83 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
| 84 | evlist->mmap[cpu].base = NULL; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus) | ||
| 90 | { | ||
| 91 | evlist->mmap = zalloc(ncpus * sizeof(struct perf_mmap)); | ||
| 92 | return evlist->mmap != NULL ? 0 : -ENOMEM; | ||
| 93 | } | ||
| 94 | |||
| 95 | void perf_evsel__exit(struct perf_evsel *evsel) | ||
| 53 | { | 96 | { |
| 54 | assert(list_empty(&evsel->node)); | 97 | assert(list_empty(&evsel->node)); |
| 55 | xyarray__delete(evsel->fd); | 98 | xyarray__delete(evsel->fd); |
| 99 | xyarray__delete(evsel->id); | ||
| 100 | } | ||
| 101 | |||
| 102 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
| 103 | { | ||
| 104 | perf_evsel__exit(evsel); | ||
| 56 | free(evsel); | 105 | free(evsel); |
| 57 | } | 106 | } |
| 58 | 107 | ||
| @@ -128,7 +177,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
| 128 | } | 177 | } |
| 129 | 178 | ||
| 130 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 179 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 131 | struct thread_map *threads) | 180 | struct thread_map *threads, bool group, bool inherit) |
| 132 | { | 181 | { |
| 133 | int cpu, thread; | 182 | int cpu, thread; |
| 134 | 183 | ||
| @@ -137,12 +186,20 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
| 137 | return -1; | 186 | return -1; |
| 138 | 187 | ||
| 139 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 188 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
| 189 | int group_fd = -1; | ||
| 190 | |||
| 191 | evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit; | ||
| 192 | |||
| 140 | for (thread = 0; thread < threads->nr; thread++) { | 193 | for (thread = 0; thread < threads->nr; thread++) { |
| 141 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 194 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
| 142 | threads->map[thread], | 195 | threads->map[thread], |
| 143 | cpus->map[cpu], -1, 0); | 196 | cpus->map[cpu], |
| 197 | group_fd, 0); | ||
| 144 | if (FD(evsel, cpu, thread) < 0) | 198 | if (FD(evsel, cpu, thread) < 0) |
| 145 | goto out_close; | 199 | goto out_close; |
| 200 | |||
| 201 | if (group && group_fd == -1) | ||
| 202 | group_fd = FD(evsel, cpu, thread); | ||
| 146 | } | 203 | } |
| 147 | } | 204 | } |
| 148 | 205 | ||
| @@ -175,10 +232,9 @@ static struct { | |||
| 175 | .threads = { -1, }, | 232 | .threads = { -1, }, |
| 176 | }; | 233 | }; |
| 177 | 234 | ||
| 178 | int perf_evsel__open(struct perf_evsel *evsel, | 235 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 179 | struct cpu_map *cpus, struct thread_map *threads) | 236 | struct thread_map *threads, bool group, bool inherit) |
| 180 | { | 237 | { |
| 181 | |||
| 182 | if (cpus == NULL) { | 238 | if (cpus == NULL) { |
| 183 | /* Work around old compiler warnings about strict aliasing */ | 239 | /* Work around old compiler warnings about strict aliasing */ |
| 184 | cpus = &empty_cpu_map.map; | 240 | cpus = &empty_cpu_map.map; |
| @@ -187,15 +243,243 @@ int perf_evsel__open(struct perf_evsel *evsel, | |||
| 187 | if (threads == NULL) | 243 | if (threads == NULL) |
| 188 | threads = &empty_thread_map.map; | 244 | threads = &empty_thread_map.map; |
| 189 | 245 | ||
| 190 | return __perf_evsel__open(evsel, cpus, threads); | 246 | return __perf_evsel__open(evsel, cpus, threads, group, inherit); |
| 247 | } | ||
| 248 | |||
| 249 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | ||
| 250 | struct cpu_map *cpus, bool group, bool inherit) | ||
| 251 | { | ||
| 252 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); | ||
| 253 | } | ||
| 254 | |||
| 255 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | ||
| 256 | struct thread_map *threads, bool group, bool inherit) | ||
| 257 | { | ||
| 258 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); | ||
| 259 | } | ||
| 260 | |||
| 261 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot, | ||
| 262 | int mask, int fd) | ||
| 263 | { | ||
| 264 | evlist->mmap[cpu].prev = 0; | ||
| 265 | evlist->mmap[cpu].mask = mask; | ||
| 266 | evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, | ||
| 267 | MAP_SHARED, fd, 0); | ||
| 268 | if (evlist->mmap[cpu].base == MAP_FAILED) | ||
| 269 | return -1; | ||
| 270 | |||
| 271 | perf_evlist__add_pollfd(evlist, fd); | ||
| 272 | return 0; | ||
| 273 | } | ||
| 274 | |||
| 275 | static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
| 276 | int cpu, int thread, int fd) | ||
| 277 | { | ||
| 278 | struct perf_sample_id *sid; | ||
| 279 | u64 read_data[4] = { 0, }; | ||
| 280 | int hash, id_idx = 1; /* The first entry is the counter value */ | ||
| 281 | |||
| 282 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | ||
| 283 | read(fd, &read_data, sizeof(read_data)) == -1) | ||
| 284 | return -1; | ||
| 285 | |||
| 286 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
| 287 | ++id_idx; | ||
| 288 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
| 289 | ++id_idx; | ||
| 290 | |||
| 291 | sid = SID(evsel, cpu, thread); | ||
| 292 | sid->id = read_data[id_idx]; | ||
| 293 | sid->evsel = evsel; | ||
| 294 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); | ||
| 295 | hlist_add_head(&sid->node, &evlist->heads[hash]); | ||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | /** perf_evlist__mmap - Create per cpu maps to receive events | ||
| 300 | * | ||
| 301 | * @evlist - list of events | ||
| 302 | * @cpus - cpu map being monitored | ||
| 303 | * @threads - threads map being monitored | ||
| 304 | * @pages - map length in pages | ||
| 305 | * @overwrite - overwrite older events? | ||
| 306 | * | ||
| 307 | * If overwrite is false the user needs to signal event consuption using: | ||
| 308 | * | ||
| 309 | * struct perf_mmap *m = &evlist->mmap[cpu]; | ||
| 310 | * unsigned int head = perf_mmap__read_head(m); | ||
| 311 | * | ||
| 312 | * perf_mmap__write_tail(m, head) | ||
| 313 | */ | ||
| 314 | int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
| 315 | struct thread_map *threads, int pages, bool overwrite) | ||
| 316 | { | ||
| 317 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
| 318 | int mask = pages * page_size - 1, cpu; | ||
| 319 | struct perf_evsel *first_evsel, *evsel; | ||
| 320 | int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); | ||
| 321 | |||
| 322 | if (evlist->mmap == NULL && | ||
| 323 | perf_evlist__alloc_mmap(evlist, cpus->nr) < 0) | ||
| 324 | return -ENOMEM; | ||
| 325 | |||
| 326 | if (evlist->pollfd == NULL && | ||
| 327 | perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0) | ||
| 328 | return -ENOMEM; | ||
| 329 | |||
| 330 | evlist->mmap_len = (pages + 1) * page_size; | ||
| 331 | first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
| 332 | |||
| 333 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
| 334 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
| 335 | evsel->id == NULL && | ||
| 336 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | ||
| 337 | return -ENOMEM; | ||
| 338 | |||
| 339 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
| 340 | for (thread = 0; thread < threads->nr; thread++) { | ||
| 341 | int fd = FD(evsel, cpu, thread); | ||
| 342 | |||
| 343 | if (evsel->idx || thread) { | ||
| 344 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, | ||
| 345 | FD(first_evsel, cpu, 0)) != 0) | ||
| 346 | goto out_unmap; | ||
| 347 | } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0) | ||
| 348 | goto out_unmap; | ||
| 349 | |||
| 350 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
| 351 | perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0) | ||
| 352 | goto out_unmap; | ||
| 353 | } | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | return 0; | ||
| 358 | |||
| 359 | out_unmap: | ||
| 360 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
| 361 | if (evlist->mmap[cpu].base != NULL) { | ||
| 362 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
| 363 | evlist->mmap[cpu].base = NULL; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | return -1; | ||
| 191 | } | 367 | } |
| 192 | 368 | ||
| 193 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) | 369 | static int event__parse_id_sample(const event_t *event, u64 type, |
| 370 | struct sample_data *sample) | ||
| 194 | { | 371 | { |
| 195 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); | 372 | const u64 *array = event->sample.array; |
| 373 | |||
| 374 | array += ((event->header.size - | ||
| 375 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
| 376 | |||
| 377 | if (type & PERF_SAMPLE_CPU) { | ||
| 378 | u32 *p = (u32 *)array; | ||
| 379 | sample->cpu = *p; | ||
| 380 | array--; | ||
| 381 | } | ||
| 382 | |||
| 383 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
| 384 | sample->stream_id = *array; | ||
| 385 | array--; | ||
| 386 | } | ||
| 387 | |||
| 388 | if (type & PERF_SAMPLE_ID) { | ||
| 389 | sample->id = *array; | ||
| 390 | array--; | ||
| 391 | } | ||
| 392 | |||
| 393 | if (type & PERF_SAMPLE_TIME) { | ||
| 394 | sample->time = *array; | ||
| 395 | array--; | ||
| 396 | } | ||
| 397 | |||
| 398 | if (type & PERF_SAMPLE_TID) { | ||
| 399 | u32 *p = (u32 *)array; | ||
| 400 | sample->pid = p[0]; | ||
| 401 | sample->tid = p[1]; | ||
| 402 | } | ||
| 403 | |||
| 404 | return 0; | ||
| 196 | } | 405 | } |
| 197 | 406 | ||
| 198 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) | 407 | int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, |
| 408 | struct sample_data *data) | ||
| 199 | { | 409 | { |
| 200 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); | 410 | const u64 *array; |
| 411 | |||
| 412 | data->cpu = data->pid = data->tid = -1; | ||
| 413 | data->stream_id = data->id = data->time = -1ULL; | ||
| 414 | |||
| 415 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
| 416 | if (!sample_id_all) | ||
| 417 | return 0; | ||
| 418 | return event__parse_id_sample(event, type, data); | ||
| 419 | } | ||
| 420 | |||
| 421 | array = event->sample.array; | ||
| 422 | |||
| 423 | if (type & PERF_SAMPLE_IP) { | ||
| 424 | data->ip = event->ip.ip; | ||
| 425 | array++; | ||
| 426 | } | ||
| 427 | |||
| 428 | if (type & PERF_SAMPLE_TID) { | ||
| 429 | u32 *p = (u32 *)array; | ||
| 430 | data->pid = p[0]; | ||
| 431 | data->tid = p[1]; | ||
| 432 | array++; | ||
| 433 | } | ||
| 434 | |||
| 435 | if (type & PERF_SAMPLE_TIME) { | ||
| 436 | data->time = *array; | ||
| 437 | array++; | ||
| 438 | } | ||
| 439 | |||
| 440 | if (type & PERF_SAMPLE_ADDR) { | ||
| 441 | data->addr = *array; | ||
| 442 | array++; | ||
| 443 | } | ||
| 444 | |||
| 445 | data->id = -1ULL; | ||
| 446 | if (type & PERF_SAMPLE_ID) { | ||
| 447 | data->id = *array; | ||
| 448 | array++; | ||
| 449 | } | ||
| 450 | |||
| 451 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
| 452 | data->stream_id = *array; | ||
| 453 | array++; | ||
| 454 | } | ||
| 455 | |||
| 456 | if (type & PERF_SAMPLE_CPU) { | ||
| 457 | u32 *p = (u32 *)array; | ||
| 458 | data->cpu = *p; | ||
| 459 | array++; | ||
| 460 | } | ||
| 461 | |||
| 462 | if (type & PERF_SAMPLE_PERIOD) { | ||
| 463 | data->period = *array; | ||
| 464 | array++; | ||
| 465 | } | ||
| 466 | |||
| 467 | if (type & PERF_SAMPLE_READ) { | ||
| 468 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | ||
| 469 | return -1; | ||
| 470 | } | ||
| 471 | |||
| 472 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
| 473 | data->callchain = (struct ip_callchain *)array; | ||
| 474 | array += 1 + data->callchain->nr; | ||
| 475 | } | ||
| 476 | |||
| 477 | if (type & PERF_SAMPLE_RAW) { | ||
| 478 | u32 *p = (u32 *)array; | ||
| 479 | data->raw_size = *p; | ||
| 480 | p++; | ||
| 481 | data->raw_data = p; | ||
| 482 | } | ||
| 483 | |||
| 484 | return 0; | ||
| 201 | } | 485 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b2d755fe88a5..7962e7587dea 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -24,11 +24,24 @@ struct perf_counts { | |||
| 24 | struct perf_counts_values cpu[]; | 24 | struct perf_counts_values cpu[]; |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | struct perf_evsel; | ||
| 28 | |||
| 29 | /* | ||
| 30 | * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are | ||
| 31 | * more than one entry in the evlist. | ||
| 32 | */ | ||
| 33 | struct perf_sample_id { | ||
| 34 | struct hlist_node node; | ||
| 35 | u64 id; | ||
| 36 | struct perf_evsel *evsel; | ||
| 37 | }; | ||
| 38 | |||
| 27 | struct perf_evsel { | 39 | struct perf_evsel { |
| 28 | struct list_head node; | 40 | struct list_head node; |
| 29 | struct perf_event_attr attr; | 41 | struct perf_event_attr attr; |
| 30 | char *filter; | 42 | char *filter; |
| 31 | struct xyarray *fd; | 43 | struct xyarray *fd; |
| 44 | struct xyarray *id; | ||
| 32 | struct perf_counts *counts; | 45 | struct perf_counts *counts; |
| 33 | int idx; | 46 | int idx; |
| 34 | void *priv; | 47 | void *priv; |
| @@ -36,19 +49,31 @@ struct perf_evsel { | |||
| 36 | 49 | ||
| 37 | struct cpu_map; | 50 | struct cpu_map; |
| 38 | struct thread_map; | 51 | struct thread_map; |
| 52 | struct perf_evlist; | ||
| 39 | 53 | ||
| 40 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | 54 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); |
| 55 | void perf_evsel__init(struct perf_evsel *evsel, | ||
| 56 | struct perf_event_attr *attr, int idx); | ||
| 57 | void perf_evsel__exit(struct perf_evsel *evsel); | ||
| 41 | void perf_evsel__delete(struct perf_evsel *evsel); | 58 | void perf_evsel__delete(struct perf_evsel *evsel); |
| 42 | 59 | ||
| 43 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 60 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
| 61 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
| 44 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 62 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
| 63 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus); | ||
| 45 | void perf_evsel__free_fd(struct perf_evsel *evsel); | 64 | void perf_evsel__free_fd(struct perf_evsel *evsel); |
| 65 | void perf_evsel__free_id(struct perf_evsel *evsel); | ||
| 46 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 66 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
| 47 | 67 | ||
| 48 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); | 68 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
| 49 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads); | 69 | struct cpu_map *cpus, bool group, bool inherit); |
| 50 | int perf_evsel__open(struct perf_evsel *evsel, | 70 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
| 51 | struct cpu_map *cpus, struct thread_map *threads); | 71 | struct thread_map *threads, bool group, bool inherit); |
| 72 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||
| 73 | struct thread_map *threads, bool group, bool inherit); | ||
| 74 | int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
| 75 | struct thread_map *threads, int pages, bool overwrite); | ||
| 76 | void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus); | ||
| 52 | 77 | ||
| 53 | #define perf_evsel__match(evsel, t, c) \ | 78 | #define perf_evsel__match(evsel, t, c) \ |
| 54 | (evsel->attr.type == PERF_TYPE_##t && \ | 79 | (evsel->attr.type == PERF_TYPE_##t && \ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f6a929e74981..f0138d472339 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
| 9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
| 10 | 10 | ||
| 11 | #include "evlist.h" | ||
| 11 | #include "util.h" | 12 | #include "util.h" |
| 12 | #include "header.h" | 13 | #include "header.h" |
| 13 | #include "../perf.h" | 14 | #include "../perf.h" |
| @@ -428,7 +429,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi | |||
| 428 | return ret; | 429 | return ret; |
| 429 | } | 430 | } |
| 430 | 431 | ||
| 431 | static int perf_header__adds_write(struct perf_header *self, int fd) | 432 | static int perf_header__adds_write(struct perf_header *self, |
| 433 | struct perf_evlist *evlist, int fd) | ||
| 432 | { | 434 | { |
| 433 | int nr_sections; | 435 | int nr_sections; |
| 434 | struct perf_session *session; | 436 | struct perf_session *session; |
| @@ -463,7 +465,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
| 463 | 465 | ||
| 464 | /* Write trace info */ | 466 | /* Write trace info */ |
| 465 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 467 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); |
| 466 | read_tracing_data(fd, &evsel_list); | 468 | read_tracing_data(fd, &evlist->entries); |
| 467 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 469 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; |
| 468 | } | 470 | } |
| 469 | 471 | ||
| @@ -513,7 +515,8 @@ int perf_header__write_pipe(int fd) | |||
| 513 | return 0; | 515 | return 0; |
| 514 | } | 516 | } |
| 515 | 517 | ||
| 516 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) | 518 | int perf_header__write(struct perf_header *self, struct perf_evlist *evlist, |
| 519 | int fd, bool at_exit) | ||
| 517 | { | 520 | { |
| 518 | struct perf_file_header f_header; | 521 | struct perf_file_header f_header; |
| 519 | struct perf_file_attr f_attr; | 522 | struct perf_file_attr f_attr; |
| @@ -566,7 +569,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
| 566 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 569 | self->data_offset = lseek(fd, 0, SEEK_CUR); |
| 567 | 570 | ||
| 568 | if (at_exit) { | 571 | if (at_exit) { |
| 569 | err = perf_header__adds_write(self, fd); | 572 | err = perf_header__adds_write(self, evlist, fd); |
| 570 | if (err < 0) | 573 | if (err < 0) |
| 571 | return err; | 574 | return err; |
| 572 | } | 575 | } |
| @@ -1133,7 +1136,7 @@ int event__process_event_type(event_t *self, | |||
| 1133 | return 0; | 1136 | return 0; |
| 1134 | } | 1137 | } |
| 1135 | 1138 | ||
| 1136 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | 1139 | int event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
| 1137 | event__handler_t process, | 1140 | event__handler_t process, |
| 1138 | struct perf_session *session __unused) | 1141 | struct perf_session *session __unused) |
| 1139 | { | 1142 | { |
| @@ -1144,7 +1147,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | |||
| 1144 | memset(&ev, 0, sizeof(ev)); | 1147 | memset(&ev, 0, sizeof(ev)); |
| 1145 | 1148 | ||
| 1146 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 1149 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
| 1147 | size = read_tracing_data_size(fd, pattrs); | 1150 | size = read_tracing_data_size(fd, &evlist->entries); |
| 1148 | if (size <= 0) | 1151 | if (size <= 0) |
| 1149 | return size; | 1152 | return size; |
| 1150 | aligned_size = ALIGN(size, sizeof(u64)); | 1153 | aligned_size = ALIGN(size, sizeof(u64)); |
| @@ -1154,7 +1157,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | |||
| 1154 | 1157 | ||
| 1155 | process(&ev, NULL, session); | 1158 | process(&ev, NULL, session); |
| 1156 | 1159 | ||
| 1157 | err = read_tracing_data(fd, pattrs); | 1160 | err = read_tracing_data(fd, &evlist->entries); |
| 1158 | write_padded(fd, NULL, 0, padding); | 1161 | write_padded(fd, NULL, 0, padding); |
| 1159 | 1162 | ||
| 1160 | return aligned_size; | 1163 | return aligned_size; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 33f16be7b72f..65afd7f74e0d 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -65,8 +65,11 @@ struct perf_header { | |||
| 65 | int perf_header__init(struct perf_header *self); | 65 | int perf_header__init(struct perf_header *self); |
| 66 | void perf_header__exit(struct perf_header *self); | 66 | void perf_header__exit(struct perf_header *self); |
| 67 | 67 | ||
| 68 | struct perf_evlist; | ||
| 69 | |||
| 68 | int perf_header__read(struct perf_session *session, int fd); | 70 | int perf_header__read(struct perf_session *session, int fd); |
| 69 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | 71 | int perf_header__write(struct perf_header *self, struct perf_evlist *evlist, |
| 72 | int fd, bool at_exit); | ||
| 70 | int perf_header__write_pipe(int fd); | 73 | int perf_header__write_pipe(int fd); |
| 71 | 74 | ||
| 72 | int perf_header__add_attr(struct perf_header *self, | 75 | int perf_header__add_attr(struct perf_header *self, |
| @@ -113,7 +116,7 @@ int event__synthesize_event_types(event__handler_t process, | |||
| 113 | int event__process_event_type(event_t *self, | 116 | int event__process_event_type(event_t *self, |
| 114 | struct perf_session *session); | 117 | struct perf_session *session); |
| 115 | 118 | ||
| 116 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | 119 | int event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
| 117 | event__handler_t process, | 120 | event__handler_t process, |
| 118 | struct perf_session *session); | 121 | struct perf_session *session); |
| 119 | int event__process_tracing_data(event_t *self, | 122 | int event__process_tracing_data(event_t *self, |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 32f4f1f2f6e4..02ed318d7312 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -211,7 +211,9 @@ void hist_entry__free(struct hist_entry *he) | |||
| 211 | * collapse the histogram | 211 | * collapse the histogram |
| 212 | */ | 212 | */ |
| 213 | 213 | ||
| 214 | static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | 214 | static bool hists__collapse_insert_entry(struct hists *self, |
| 215 | struct rb_root *root, | ||
| 216 | struct hist_entry *he) | ||
| 215 | { | 217 | { |
| 216 | struct rb_node **p = &root->rb_node; | 218 | struct rb_node **p = &root->rb_node; |
| 217 | struct rb_node *parent = NULL; | 219 | struct rb_node *parent = NULL; |
| @@ -226,8 +228,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
| 226 | 228 | ||
| 227 | if (!cmp) { | 229 | if (!cmp) { |
| 228 | iter->period += he->period; | 230 | iter->period += he->period; |
| 229 | if (symbol_conf.use_callchain) | 231 | if (symbol_conf.use_callchain) { |
| 230 | callchain_merge(iter->callchain, he->callchain); | 232 | callchain_cursor_reset(&self->callchain_cursor); |
| 233 | callchain_merge(&self->callchain_cursor, iter->callchain, | ||
| 234 | he->callchain); | ||
| 235 | } | ||
| 231 | hist_entry__free(he); | 236 | hist_entry__free(he); |
| 232 | return false; | 237 | return false; |
| 233 | } | 238 | } |
| @@ -262,7 +267,7 @@ void hists__collapse_resort(struct hists *self) | |||
| 262 | next = rb_next(&n->rb_node); | 267 | next = rb_next(&n->rb_node); |
| 263 | 268 | ||
| 264 | rb_erase(&n->rb_node, &self->entries); | 269 | rb_erase(&n->rb_node, &self->entries); |
| 265 | if (collapse__insert_entry(&tmp, n)) | 270 | if (hists__collapse_insert_entry(self, &tmp, n)) |
| 266 | hists__inc_nr_entries(self, n); | 271 | hists__inc_nr_entries(self, n); |
| 267 | } | 272 | } |
| 268 | 273 | ||
| @@ -425,7 +430,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 425 | u64 cumul; | 430 | u64 cumul; |
| 426 | 431 | ||
| 427 | child = rb_entry(node, struct callchain_node, rb_node); | 432 | child = rb_entry(node, struct callchain_node, rb_node); |
| 428 | cumul = cumul_hits(child); | 433 | cumul = callchain_cumul_hits(child); |
| 429 | remaining -= cumul; | 434 | remaining -= cumul; |
| 430 | 435 | ||
| 431 | /* | 436 | /* |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ee789856a8c9..889559b86492 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -77,6 +77,8 @@ struct hists { | |||
| 77 | u64 event_stream; | 77 | u64 event_stream; |
| 78 | u32 type; | 78 | u32 type; |
| 79 | u16 col_len[HISTC_NR_COLS]; | 79 | u16 col_len[HISTC_NR_COLS]; |
| 80 | /* Best would be to reuse the session callchain cursor */ | ||
| 81 | struct callchain_cursor callchain_cursor; | ||
| 80 | }; | 82 | }; |
| 81 | 83 | ||
| 82 | struct hist_entry *__hists__add_entry(struct hists *self, | 84 | struct hist_entry *__hists__add_entry(struct hists *self, |
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index f5ca26e53fbb..356c7e467b83 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #include <linux/kernel.h> | ||
| 1 | #include "../../../../include/linux/list.h" | 2 | #include "../../../../include/linux/list.h" |
| 2 | 3 | ||
| 3 | #ifndef PERF_LIST_H | 4 | #ifndef PERF_LIST_H |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 135f69baf966..cf082daa43e3 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #include "../../../include/linux/hw_breakpoint.h" | 1 | #include "../../../include/linux/hw_breakpoint.h" |
| 2 | #include "util.h" | 2 | #include "util.h" |
| 3 | #include "../perf.h" | 3 | #include "../perf.h" |
| 4 | #include "evlist.h" | ||
| 4 | #include "evsel.h" | 5 | #include "evsel.h" |
| 5 | #include "parse-options.h" | 6 | #include "parse-options.h" |
| 6 | #include "parse-events.h" | 7 | #include "parse-events.h" |
| @@ -11,10 +12,6 @@ | |||
| 11 | #include "header.h" | 12 | #include "header.h" |
| 12 | #include "debugfs.h" | 13 | #include "debugfs.h" |
| 13 | 14 | ||
| 14 | int nr_counters; | ||
| 15 | |||
| 16 | LIST_HEAD(evsel_list); | ||
| 17 | |||
| 18 | struct event_symbol { | 15 | struct event_symbol { |
| 19 | u8 type; | 16 | u8 type; |
| 20 | u64 config; | 17 | u64 config; |
| @@ -449,8 +446,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
| 449 | /* sys + ':' + event + ':' + flags*/ | 446 | /* sys + ':' + event + ':' + flags*/ |
| 450 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 447 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
| 451 | static enum event_result | 448 | static enum event_result |
| 452 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | 449 | parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, |
| 453 | char *flags) | 450 | const char *evt_exp, char *flags) |
| 454 | { | 451 | { |
| 455 | char evt_path[MAXPATHLEN]; | 452 | char evt_path[MAXPATHLEN]; |
| 456 | struct dirent *evt_ent; | 453 | struct dirent *evt_ent; |
| @@ -483,15 +480,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | |||
| 483 | if (len < 0) | 480 | if (len < 0) |
| 484 | return EVT_FAILED; | 481 | return EVT_FAILED; |
| 485 | 482 | ||
| 486 | if (parse_events(NULL, event_opt, 0)) | 483 | if (parse_events(opt, event_opt, 0)) |
| 487 | return EVT_FAILED; | 484 | return EVT_FAILED; |
| 488 | } | 485 | } |
| 489 | 486 | ||
| 490 | return EVT_HANDLED_ALL; | 487 | return EVT_HANDLED_ALL; |
| 491 | } | 488 | } |
| 492 | 489 | ||
| 493 | static enum event_result parse_tracepoint_event(const char **strp, | 490 | static enum event_result |
| 494 | struct perf_event_attr *attr) | 491 | parse_tracepoint_event(const struct option *opt, const char **strp, |
| 492 | struct perf_event_attr *attr) | ||
| 495 | { | 493 | { |
| 496 | const char *evt_name; | 494 | const char *evt_name; |
| 497 | char *flags = NULL, *comma_loc; | 495 | char *flags = NULL, *comma_loc; |
| @@ -530,7 +528,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
| 530 | return EVT_FAILED; | 528 | return EVT_FAILED; |
| 531 | if (strpbrk(evt_name, "*?")) { | 529 | if (strpbrk(evt_name, "*?")) { |
| 532 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | 530 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ |
| 533 | return parse_multiple_tracepoint_event(sys_name, evt_name, | 531 | return parse_multiple_tracepoint_event(opt, sys_name, evt_name, |
| 534 | flags); | 532 | flags); |
| 535 | } else { | 533 | } else { |
| 536 | return parse_single_tracepoint_event(sys_name, evt_name, | 534 | return parse_single_tracepoint_event(sys_name, evt_name, |
| @@ -740,11 +738,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
| 740 | * Symbolic names are (almost) exactly matched. | 738 | * Symbolic names are (almost) exactly matched. |
| 741 | */ | 739 | */ |
| 742 | static enum event_result | 740 | static enum event_result |
| 743 | parse_event_symbols(const char **str, struct perf_event_attr *attr) | 741 | parse_event_symbols(const struct option *opt, const char **str, |
| 742 | struct perf_event_attr *attr) | ||
| 744 | { | 743 | { |
| 745 | enum event_result ret; | 744 | enum event_result ret; |
| 746 | 745 | ||
| 747 | ret = parse_tracepoint_event(str, attr); | 746 | ret = parse_tracepoint_event(opt, str, attr); |
| 748 | if (ret != EVT_FAILED) | 747 | if (ret != EVT_FAILED) |
| 749 | goto modifier; | 748 | goto modifier; |
| 750 | 749 | ||
| @@ -778,14 +777,15 @@ modifier: | |||
| 778 | return ret; | 777 | return ret; |
| 779 | } | 778 | } |
| 780 | 779 | ||
| 781 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 780 | int parse_events(const struct option *opt, const char *str, int unset __used) |
| 782 | { | 781 | { |
| 782 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
| 783 | struct perf_event_attr attr; | 783 | struct perf_event_attr attr; |
| 784 | enum event_result ret; | 784 | enum event_result ret; |
| 785 | 785 | ||
| 786 | for (;;) { | 786 | for (;;) { |
| 787 | memset(&attr, 0, sizeof(attr)); | 787 | memset(&attr, 0, sizeof(attr)); |
| 788 | ret = parse_event_symbols(&str, &attr); | 788 | ret = parse_event_symbols(opt, &str, &attr); |
| 789 | if (ret == EVT_FAILED) | 789 | if (ret == EVT_FAILED) |
| 790 | return -1; | 790 | return -1; |
| 791 | 791 | ||
| @@ -794,12 +794,10 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
| 794 | 794 | ||
| 795 | if (ret != EVT_HANDLED_ALL) { | 795 | if (ret != EVT_HANDLED_ALL) { |
| 796 | struct perf_evsel *evsel; | 796 | struct perf_evsel *evsel; |
| 797 | evsel = perf_evsel__new(&attr, | 797 | evsel = perf_evsel__new(&attr, evlist->nr_entries); |
| 798 | nr_counters); | ||
| 799 | if (evsel == NULL) | 798 | if (evsel == NULL) |
| 800 | return -1; | 799 | return -1; |
| 801 | list_add_tail(&evsel->node, &evsel_list); | 800 | perf_evlist__add(evlist, evsel); |
| 802 | ++nr_counters; | ||
| 803 | } | 801 | } |
| 804 | 802 | ||
| 805 | if (*str == 0) | 803 | if (*str == 0) |
| @@ -813,13 +811,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
| 813 | return 0; | 811 | return 0; |
| 814 | } | 812 | } |
| 815 | 813 | ||
| 816 | int parse_filter(const struct option *opt __used, const char *str, | 814 | int parse_filter(const struct option *opt, const char *str, |
| 817 | int unset __used) | 815 | int unset __used) |
| 818 | { | 816 | { |
| 817 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
| 819 | struct perf_evsel *last = NULL; | 818 | struct perf_evsel *last = NULL; |
| 820 | 819 | ||
| 821 | if (!list_empty(&evsel_list)) | 820 | if (evlist->nr_entries > 0) |
| 822 | last = list_entry(evsel_list.prev, struct perf_evsel, node); | 821 | last = list_entry(evlist->entries.prev, struct perf_evsel, node); |
| 823 | 822 | ||
| 824 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 823 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
| 825 | fprintf(stderr, | 824 | fprintf(stderr, |
| @@ -981,33 +980,3 @@ void print_events(void) | |||
| 981 | 980 | ||
| 982 | exit(129); | 981 | exit(129); |
| 983 | } | 982 | } |
| 984 | |||
| 985 | int perf_evsel_list__create_default(void) | ||
| 986 | { | ||
| 987 | struct perf_evsel *evsel; | ||
| 988 | struct perf_event_attr attr; | ||
| 989 | |||
| 990 | memset(&attr, 0, sizeof(attr)); | ||
| 991 | attr.type = PERF_TYPE_HARDWARE; | ||
| 992 | attr.config = PERF_COUNT_HW_CPU_CYCLES; | ||
| 993 | |||
| 994 | evsel = perf_evsel__new(&attr, 0); | ||
| 995 | |||
| 996 | if (evsel == NULL) | ||
| 997 | return -ENOMEM; | ||
| 998 | |||
| 999 | list_add(&evsel->node, &evsel_list); | ||
| 1000 | ++nr_counters; | ||
| 1001 | return 0; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | void perf_evsel_list__delete(void) | ||
| 1005 | { | ||
| 1006 | struct perf_evsel *pos, *n; | ||
| 1007 | |||
| 1008 | list_for_each_entry_safe(pos, n, &evsel_list, node) { | ||
| 1009 | list_del_init(&pos->node); | ||
| 1010 | perf_evsel__delete(pos); | ||
| 1011 | } | ||
| 1012 | nr_counters = 0; | ||
| 1013 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 458e3ecf17af..cf7e94abb676 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -9,11 +9,6 @@ | |||
| 9 | struct list_head; | 9 | struct list_head; |
| 10 | struct perf_evsel; | 10 | struct perf_evsel; |
| 11 | 11 | ||
| 12 | extern struct list_head evsel_list; | ||
| 13 | |||
| 14 | int perf_evsel_list__create_default(void); | ||
| 15 | void perf_evsel_list__delete(void); | ||
| 16 | |||
| 17 | struct option; | 12 | struct option; |
| 18 | 13 | ||
| 19 | struct tracepoint_path { | 14 | struct tracepoint_path { |
| @@ -25,8 +20,6 @@ struct tracepoint_path { | |||
| 25 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | 20 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); |
| 26 | extern bool have_tracepoints(struct list_head *evlist); | 21 | extern bool have_tracepoints(struct list_head *evlist); |
| 27 | 22 | ||
| 28 | extern int nr_counters; | ||
| 29 | |||
| 30 | const char *event_name(struct perf_evsel *event); | 23 | const char *event_name(struct perf_evsel *event); |
| 31 | extern const char *__event_name(int type, u64 config); | 24 | extern const char *__event_name(int type, u64 config); |
| 32 | 25 | ||
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6e29d9c9dccc..859d377a3df3 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <string.h> | 31 | #include <string.h> |
| 32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
| 33 | #include <limits.h> | 33 | #include <limits.h> |
| 34 | #include <elf.h> | ||
| 34 | 35 | ||
| 35 | #undef _GNU_SOURCE | 36 | #undef _GNU_SOURCE |
| 36 | #include "util.h" | 37 | #include "util.h" |
| @@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name, | |||
| 111 | NULL); | 112 | NULL); |
| 112 | } | 113 | } |
| 113 | 114 | ||
| 114 | const char *kernel_get_module_path(const char *module) | 115 | static struct map *kernel_get_module_map(const char *module) |
| 116 | { | ||
| 117 | struct rb_node *nd; | ||
| 118 | struct map_groups *grp = &machine.kmaps; | ||
| 119 | |||
| 120 | if (!module) | ||
| 121 | module = "kernel"; | ||
| 122 | |||
| 123 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | ||
| 124 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
| 125 | if (strncmp(pos->dso->short_name + 1, module, | ||
| 126 | pos->dso->short_name_len - 2) == 0) { | ||
| 127 | return pos; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | return NULL; | ||
| 131 | } | ||
| 132 | |||
| 133 | static struct dso *kernel_get_module_dso(const char *module) | ||
| 115 | { | 134 | { |
| 116 | struct dso *dso; | 135 | struct dso *dso; |
| 117 | struct map *map; | 136 | struct map *map; |
| @@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module) | |||
| 141 | } | 160 | } |
| 142 | } | 161 | } |
| 143 | found: | 162 | found: |
| 144 | return dso->long_name; | 163 | return dso; |
| 164 | } | ||
| 165 | |||
| 166 | const char *kernel_get_module_path(const char *module) | ||
| 167 | { | ||
| 168 | struct dso *dso = kernel_get_module_dso(module); | ||
| 169 | return (dso) ? dso->long_name : NULL; | ||
| 145 | } | 170 | } |
| 146 | 171 | ||
| 147 | #ifdef DWARF_SUPPORT | 172 | #ifdef DWARF_SUPPORT |
| @@ -1913,3 +1938,42 @@ int del_perf_probe_events(struct strlist *dellist) | |||
| 1913 | return ret; | 1938 | return ret; |
| 1914 | } | 1939 | } |
| 1915 | 1940 | ||
| 1941 | /* | ||
| 1942 | * If a symbol corresponds to a function with global binding return 0. | ||
| 1943 | * For all others return 1. | ||
| 1944 | */ | ||
| 1945 | static int filter_non_global_functions(struct map *map __unused, | ||
| 1946 | struct symbol *sym) | ||
| 1947 | { | ||
| 1948 | if (sym->binding != STB_GLOBAL) | ||
| 1949 | return 1; | ||
| 1950 | |||
| 1951 | return 0; | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | int show_available_funcs(const char *module) | ||
| 1955 | { | ||
| 1956 | struct map *map; | ||
| 1957 | int ret; | ||
| 1958 | |||
| 1959 | setup_pager(); | ||
| 1960 | |||
| 1961 | ret = init_vmlinux(); | ||
| 1962 | if (ret < 0) | ||
| 1963 | return ret; | ||
| 1964 | |||
| 1965 | map = kernel_get_module_map(module); | ||
| 1966 | if (!map) { | ||
| 1967 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | ||
| 1968 | return -EINVAL; | ||
| 1969 | } | ||
| 1970 | if (map__load(map, filter_non_global_functions)) { | ||
| 1971 | pr_err("Failed to load map.\n"); | ||
| 1972 | return -EINVAL; | ||
| 1973 | } | ||
| 1974 | if (!dso__sorted_by_name(map->dso, map->type)) | ||
| 1975 | dso__sort_by_name(map->dso, map->type); | ||
| 1976 | |||
| 1977 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | ||
| 1978 | return 0; | ||
| 1979 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 5accbedfea37..1fb4f18337d3 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
| @@ -127,6 +127,7 @@ extern int show_line_range(struct line_range *lr, const char *module); | |||
| 127 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 127 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
| 128 | int max_probe_points, const char *module, | 128 | int max_probe_points, const char *module, |
| 129 | bool externs); | 129 | bool externs); |
| 130 | extern int show_available_funcs(const char *module); | ||
| 130 | 131 | ||
| 131 | 132 | ||
| 132 | /* Maximum index number of event-name postfix */ | 133 | /* Maximum index number of event-name postfix */ |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ab83b6ac5d65..69215bff17e9 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -280,6 +280,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | |||
| 280 | return name ? (strcmp(tname, name) == 0) : false; | 280 | return name ? (strcmp(tname, name) == 0) : false; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | /* Get callsite line number of inline-function instance */ | ||
| 284 | static int die_get_call_lineno(Dwarf_Die *in_die) | ||
| 285 | { | ||
| 286 | Dwarf_Attribute attr; | ||
| 287 | Dwarf_Word ret; | ||
| 288 | |||
| 289 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
| 290 | return -ENOENT; | ||
| 291 | |||
| 292 | dwarf_formudata(&attr, &ret); | ||
| 293 | return (int)ret; | ||
| 294 | } | ||
| 295 | |||
| 283 | /* Get type die */ | 296 | /* Get type die */ |
| 284 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | 297 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) |
| 285 | { | 298 | { |
| @@ -458,6 +471,151 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | |||
| 458 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); | 471 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); |
| 459 | } | 472 | } |
| 460 | 473 | ||
| 474 | /* Walker on lines (Note: line number will not be sorted) */ | ||
| 475 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
| 476 | Dwarf_Addr addr, void *data); | ||
| 477 | |||
| 478 | struct __line_walk_param { | ||
| 479 | const char *fname; | ||
| 480 | line_walk_handler_t handler; | ||
| 481 | void *data; | ||
| 482 | int retval; | ||
| 483 | }; | ||
| 484 | |||
| 485 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
| 486 | { | ||
| 487 | struct __line_walk_param *lw = data; | ||
| 488 | Dwarf_Addr addr; | ||
| 489 | int lineno; | ||
| 490 | |||
| 491 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
| 492 | lineno = die_get_call_lineno(in_die); | ||
| 493 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
| 494 | lw->retval = lw->handler(lw->fname, lineno, addr, | ||
| 495 | lw->data); | ||
| 496 | if (lw->retval != 0) | ||
| 497 | return DIE_FIND_CB_FOUND; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | return DIE_FIND_CB_SIBLING; | ||
| 501 | } | ||
| 502 | |||
| 503 | /* Walk on lines of blocks included in given DIE */ | ||
| 504 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
| 505 | line_walk_handler_t handler, void *data) | ||
| 506 | { | ||
| 507 | struct __line_walk_param lw = { | ||
| 508 | .handler = handler, | ||
| 509 | .data = data, | ||
| 510 | .retval = 0, | ||
| 511 | }; | ||
| 512 | Dwarf_Die die_mem; | ||
| 513 | Dwarf_Addr addr; | ||
| 514 | int lineno; | ||
| 515 | |||
| 516 | /* Handle function declaration line */ | ||
| 517 | lw.fname = dwarf_decl_file(sp_die); | ||
| 518 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
| 519 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
| 520 | lw.retval = handler(lw.fname, lineno, addr, data); | ||
| 521 | if (lw.retval != 0) | ||
| 522 | goto done; | ||
| 523 | } | ||
| 524 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
| 525 | done: | ||
| 526 | return lw.retval; | ||
| 527 | } | ||
| 528 | |||
| 529 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
| 530 | { | ||
| 531 | struct __line_walk_param *lw = data; | ||
| 532 | |||
| 533 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
| 534 | if (lw->retval != 0) | ||
| 535 | return DWARF_CB_ABORT; | ||
| 536 | |||
| 537 | return DWARF_CB_OK; | ||
| 538 | } | ||
| 539 | |||
| 540 | /* | ||
| 541 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
| 542 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
| 543 | */ | ||
| 544 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
| 545 | void *data) | ||
| 546 | { | ||
| 547 | Dwarf_Lines *lines; | ||
| 548 | Dwarf_Line *line; | ||
| 549 | Dwarf_Addr addr; | ||
| 550 | const char *fname; | ||
| 551 | int lineno, ret = 0; | ||
| 552 | Dwarf_Die die_mem, *cu_die; | ||
| 553 | size_t nlines, i; | ||
| 554 | |||
| 555 | /* Get the CU die */ | ||
| 556 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
| 557 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
| 558 | else | ||
| 559 | cu_die = pdie; | ||
| 560 | if (!cu_die) { | ||
| 561 | pr_debug2("Failed to get CU from subprogram\n"); | ||
| 562 | return -EINVAL; | ||
| 563 | } | ||
| 564 | |||
| 565 | /* Get lines list in the CU */ | ||
| 566 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
| 567 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
| 568 | return -ENOENT; | ||
| 569 | } | ||
| 570 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
| 571 | |||
| 572 | /* Walk on the lines on lines list */ | ||
| 573 | for (i = 0; i < nlines; i++) { | ||
| 574 | line = dwarf_onesrcline(lines, i); | ||
| 575 | if (line == NULL || | ||
| 576 | dwarf_lineno(line, &lineno) != 0 || | ||
| 577 | dwarf_lineaddr(line, &addr) != 0) { | ||
| 578 | pr_debug2("Failed to get line info. " | ||
| 579 | "Possible error in debuginfo.\n"); | ||
| 580 | continue; | ||
| 581 | } | ||
| 582 | /* Filter lines based on address */ | ||
| 583 | if (pdie != cu_die) | ||
| 584 | /* | ||
| 585 | * Address filtering | ||
| 586 | * The line is included in given function, and | ||
| 587 | * no inline block includes it. | ||
| 588 | */ | ||
| 589 | if (!dwarf_haspc(pdie, addr) || | ||
| 590 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
| 591 | continue; | ||
| 592 | /* Get source line */ | ||
| 593 | fname = dwarf_linesrc(line, NULL, NULL); | ||
| 594 | |||
| 595 | ret = handler(fname, lineno, addr, data); | ||
| 596 | if (ret != 0) | ||
| 597 | return ret; | ||
| 598 | } | ||
| 599 | |||
| 600 | /* | ||
| 601 | * Dwarf lines doesn't include function declarations and inlined | ||
| 602 | * subroutines. We have to check functions list or given function. | ||
| 603 | */ | ||
| 604 | if (pdie != cu_die) | ||
| 605 | ret = __die_walk_funclines(pdie, handler, data); | ||
| 606 | else { | ||
| 607 | struct __line_walk_param param = { | ||
| 608 | .handler = handler, | ||
| 609 | .data = data, | ||
| 610 | .retval = 0, | ||
| 611 | }; | ||
| 612 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
| 613 | ret = param.retval; | ||
| 614 | } | ||
| 615 | |||
| 616 | return ret; | ||
| 617 | } | ||
| 618 | |||
| 461 | struct __find_variable_param { | 619 | struct __find_variable_param { |
| 462 | const char *name; | 620 | const char *name; |
| 463 | Dwarf_Addr addr; | 621 | Dwarf_Addr addr; |
| @@ -1050,43 +1208,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1050 | return ret; | 1208 | return ret; |
| 1051 | } | 1209 | } |
| 1052 | 1210 | ||
| 1053 | /* Find probe point from its line number */ | 1211 | static int probe_point_line_walker(const char *fname, int lineno, |
| 1054 | static int find_probe_point_by_line(struct probe_finder *pf) | 1212 | Dwarf_Addr addr, void *data) |
| 1055 | { | 1213 | { |
| 1056 | Dwarf_Lines *lines; | 1214 | struct probe_finder *pf = data; |
| 1057 | Dwarf_Line *line; | 1215 | int ret; |
| 1058 | size_t nlines, i; | ||
| 1059 | Dwarf_Addr addr; | ||
| 1060 | int lineno; | ||
| 1061 | int ret = 0; | ||
| 1062 | |||
| 1063 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | ||
| 1064 | pr_warning("No source lines found.\n"); | ||
| 1065 | return -ENOENT; | ||
| 1066 | } | ||
| 1067 | 1216 | ||
| 1068 | for (i = 0; i < nlines && ret == 0; i++) { | 1217 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
| 1069 | line = dwarf_onesrcline(lines, i); | 1218 | return 0; |
| 1070 | if (dwarf_lineno(line, &lineno) != 0 || | ||
| 1071 | lineno != pf->lno) | ||
| 1072 | continue; | ||
| 1073 | 1219 | ||
| 1074 | /* TODO: Get fileno from line, but how? */ | 1220 | pf->addr = addr; |
| 1075 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | 1221 | ret = call_probe_finder(NULL, pf); |
| 1076 | continue; | ||
| 1077 | 1222 | ||
| 1078 | if (dwarf_lineaddr(line, &addr) != 0) { | 1223 | /* Continue if no error, because the line will be in inline function */ |
| 1079 | pr_warning("Failed to get the address of the line.\n"); | 1224 | return ret < 0 ?: 0; |
| 1080 | return -ENOENT; | 1225 | } |
| 1081 | } | ||
| 1082 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
| 1083 | (int)i, lineno, (uintmax_t)addr); | ||
| 1084 | pf->addr = addr; | ||
| 1085 | 1226 | ||
| 1086 | ret = call_probe_finder(NULL, pf); | 1227 | /* Find probe point from its line number */ |
| 1087 | /* Continuing, because target line might be inlined. */ | 1228 | static int find_probe_point_by_line(struct probe_finder *pf) |
| 1088 | } | 1229 | { |
| 1089 | return ret; | 1230 | return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); |
| 1090 | } | 1231 | } |
| 1091 | 1232 | ||
| 1092 | /* Find lines which match lazy pattern */ | 1233 | /* Find lines which match lazy pattern */ |
| @@ -1140,15 +1281,31 @@ out_close: | |||
| 1140 | return nlines; | 1281 | return nlines; |
| 1141 | } | 1282 | } |
| 1142 | 1283 | ||
| 1284 | static int probe_point_lazy_walker(const char *fname, int lineno, | ||
| 1285 | Dwarf_Addr addr, void *data) | ||
| 1286 | { | ||
| 1287 | struct probe_finder *pf = data; | ||
| 1288 | int ret; | ||
| 1289 | |||
| 1290 | if (!line_list__has_line(&pf->lcache, lineno) || | ||
| 1291 | strtailcmp(fname, pf->fname) != 0) | ||
| 1292 | return 0; | ||
| 1293 | |||
| 1294 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | ||
| 1295 | lineno, (unsigned long long)addr); | ||
| 1296 | pf->addr = addr; | ||
| 1297 | ret = call_probe_finder(NULL, pf); | ||
| 1298 | |||
| 1299 | /* | ||
| 1300 | * Continue if no error, because the lazy pattern will match | ||
| 1301 | * to other lines | ||
| 1302 | */ | ||
| 1303 | return ret < 0 ?: 0; | ||
| 1304 | } | ||
| 1305 | |||
| 1143 | /* Find probe points from lazy pattern */ | 1306 | /* Find probe points from lazy pattern */ |
| 1144 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | 1307 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) |
| 1145 | { | 1308 | { |
| 1146 | Dwarf_Lines *lines; | ||
| 1147 | Dwarf_Line *line; | ||
| 1148 | size_t nlines, i; | ||
| 1149 | Dwarf_Addr addr; | ||
| 1150 | Dwarf_Die die_mem; | ||
| 1151 | int lineno; | ||
| 1152 | int ret = 0; | 1309 | int ret = 0; |
| 1153 | 1310 | ||
| 1154 | if (list_empty(&pf->lcache)) { | 1311 | if (list_empty(&pf->lcache)) { |
| @@ -1162,45 +1319,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
| 1162 | return ret; | 1319 | return ret; |
| 1163 | } | 1320 | } |
| 1164 | 1321 | ||
| 1165 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1322 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); |
| 1166 | pr_warning("No source lines found.\n"); | ||
| 1167 | return -ENOENT; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | for (i = 0; i < nlines && ret >= 0; i++) { | ||
| 1171 | line = dwarf_onesrcline(lines, i); | ||
| 1172 | |||
| 1173 | if (dwarf_lineno(line, &lineno) != 0 || | ||
| 1174 | !line_list__has_line(&pf->lcache, lineno)) | ||
| 1175 | continue; | ||
| 1176 | |||
| 1177 | /* TODO: Get fileno from line, but how? */ | ||
| 1178 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
| 1179 | continue; | ||
| 1180 | |||
| 1181 | if (dwarf_lineaddr(line, &addr) != 0) { | ||
| 1182 | pr_debug("Failed to get the address of line %d.\n", | ||
| 1183 | lineno); | ||
| 1184 | continue; | ||
| 1185 | } | ||
| 1186 | if (sp_die) { | ||
| 1187 | /* Address filtering 1: does sp_die include addr? */ | ||
| 1188 | if (!dwarf_haspc(sp_die, addr)) | ||
| 1189 | continue; | ||
| 1190 | /* Address filtering 2: No child include addr? */ | ||
| 1191 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
| 1192 | continue; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", | ||
| 1196 | (int)i, lineno, (unsigned long long)addr); | ||
| 1197 | pf->addr = addr; | ||
| 1198 | |||
| 1199 | ret = call_probe_finder(sp_die, pf); | ||
| 1200 | /* Continuing, because target line might be inlined. */ | ||
| 1201 | } | ||
| 1202 | /* TODO: deallocate lines, but how? */ | ||
| 1203 | return ret; | ||
| 1204 | } | 1323 | } |
| 1205 | 1324 | ||
| 1206 | /* Callback parameter with return value */ | 1325 | /* Callback parameter with return value */ |
| @@ -1644,91 +1763,28 @@ static int line_range_add_line(const char *src, unsigned int lineno, | |||
| 1644 | return line_list__add_line(&lr->line_list, lineno); | 1763 | return line_list__add_line(&lr->line_list, lineno); |
| 1645 | } | 1764 | } |
| 1646 | 1765 | ||
| 1647 | /* Search function declaration lines */ | 1766 | static int line_range_walk_cb(const char *fname, int lineno, |
| 1648 | static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) | 1767 | Dwarf_Addr addr __used, |
| 1768 | void *data) | ||
| 1649 | { | 1769 | { |
| 1650 | struct dwarf_callback_param *param = data; | 1770 | struct line_finder *lf = data; |
| 1651 | struct line_finder *lf = param->data; | ||
| 1652 | const char *src; | ||
| 1653 | int lineno; | ||
| 1654 | 1771 | ||
| 1655 | src = dwarf_decl_file(sp_die); | 1772 | if ((strtailcmp(fname, lf->fname) != 0) || |
| 1656 | if (src && strtailcmp(src, lf->fname) != 0) | ||
| 1657 | return DWARF_CB_OK; | ||
| 1658 | |||
| 1659 | if (dwarf_decl_line(sp_die, &lineno) != 0 || | ||
| 1660 | (lf->lno_s > lineno || lf->lno_e < lineno)) | 1773 | (lf->lno_s > lineno || lf->lno_e < lineno)) |
| 1661 | return DWARF_CB_OK; | 1774 | return 0; |
| 1662 | 1775 | ||
| 1663 | param->retval = line_range_add_line(src, lineno, lf->lr); | 1776 | if (line_range_add_line(fname, lineno, lf->lr) < 0) |
| 1664 | if (param->retval < 0) | 1777 | return -EINVAL; |
| 1665 | return DWARF_CB_ABORT; | ||
| 1666 | return DWARF_CB_OK; | ||
| 1667 | } | ||
| 1668 | 1778 | ||
| 1669 | static int find_line_range_func_decl_lines(struct line_finder *lf) | 1779 | return 0; |
| 1670 | { | ||
| 1671 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; | ||
| 1672 | dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); | ||
| 1673 | return param.retval; | ||
| 1674 | } | 1780 | } |
| 1675 | 1781 | ||
| 1676 | /* Find line range from its line number */ | 1782 | /* Find line range from its line number */ |
| 1677 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 1783 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
| 1678 | { | 1784 | { |
| 1679 | Dwarf_Lines *lines; | 1785 | int ret; |
| 1680 | Dwarf_Line *line; | ||
| 1681 | size_t nlines, i; | ||
| 1682 | Dwarf_Addr addr; | ||
| 1683 | int lineno, ret = 0; | ||
| 1684 | const char *src; | ||
| 1685 | Dwarf_Die die_mem; | ||
| 1686 | |||
| 1687 | line_list__init(&lf->lr->line_list); | ||
| 1688 | if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { | ||
| 1689 | pr_warning("No source lines found.\n"); | ||
| 1690 | return -ENOENT; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | /* Search probable lines on lines list */ | ||
| 1694 | for (i = 0; i < nlines; i++) { | ||
| 1695 | line = dwarf_onesrcline(lines, i); | ||
| 1696 | if (dwarf_lineno(line, &lineno) != 0 || | ||
| 1697 | (lf->lno_s > lineno || lf->lno_e < lineno)) | ||
| 1698 | continue; | ||
| 1699 | |||
| 1700 | if (sp_die) { | ||
| 1701 | /* Address filtering 1: does sp_die include addr? */ | ||
| 1702 | if (dwarf_lineaddr(line, &addr) != 0 || | ||
| 1703 | !dwarf_haspc(sp_die, addr)) | ||
| 1704 | continue; | ||
| 1705 | |||
| 1706 | /* Address filtering 2: No child include addr? */ | ||
| 1707 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
| 1708 | continue; | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | /* TODO: Get fileno from line, but how? */ | ||
| 1712 | src = dwarf_linesrc(line, NULL, NULL); | ||
| 1713 | if (strtailcmp(src, lf->fname) != 0) | ||
| 1714 | continue; | ||
| 1715 | |||
| 1716 | ret = line_range_add_line(src, lineno, lf->lr); | ||
| 1717 | if (ret < 0) | ||
| 1718 | return ret; | ||
| 1719 | } | ||
| 1720 | 1786 | ||
| 1721 | /* | 1787 | ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); |
| 1722 | * Dwarf lines doesn't include function declarations. We have to | ||
| 1723 | * check functions list or given function. | ||
| 1724 | */ | ||
| 1725 | if (sp_die) { | ||
| 1726 | src = dwarf_decl_file(sp_die); | ||
| 1727 | if (src && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
| 1728 | (lf->lno_s <= lineno && lf->lno_e >= lineno)) | ||
| 1729 | ret = line_range_add_line(src, lineno, lf->lr); | ||
| 1730 | } else | ||
| 1731 | ret = find_line_range_func_decl_lines(lf); | ||
| 1732 | 1788 | ||
| 1733 | /* Update status */ | 1789 | /* Update status */ |
| 1734 | if (ret >= 0) | 1790 | if (ret >= 0) |
| @@ -1758,9 +1814,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
| 1758 | struct line_finder *lf = param->data; | 1814 | struct line_finder *lf = param->data; |
| 1759 | struct line_range *lr = lf->lr; | 1815 | struct line_range *lr = lf->lr; |
| 1760 | 1816 | ||
| 1761 | pr_debug("find (%llx) %s\n", | ||
| 1762 | (unsigned long long)dwarf_dieoffset(sp_die), | ||
| 1763 | dwarf_diename(sp_die)); | ||
| 1764 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1817 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
| 1765 | die_compare_name(sp_die, lr->function)) { | 1818 | die_compare_name(sp_die, lr->function)) { |
| 1766 | lf->fname = dwarf_decl_file(sp_die); | 1819 | lf->fname = dwarf_decl_file(sp_die); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 105f00bfd555..e6a07408669e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -242,17 +242,16 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
| 242 | return 0; | 242 | return 0; |
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 245 | int perf_session__resolve_callchain(struct perf_session *self, |
| 246 | struct thread *thread, | 246 | struct thread *thread, |
| 247 | struct ip_callchain *chain, | 247 | struct ip_callchain *chain, |
| 248 | struct symbol **parent) | 248 | struct symbol **parent) |
| 249 | { | 249 | { |
| 250 | u8 cpumode = PERF_RECORD_MISC_USER; | 250 | u8 cpumode = PERF_RECORD_MISC_USER; |
| 251 | unsigned int i; | 251 | unsigned int i; |
| 252 | struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); | 252 | int err; |
| 253 | 253 | ||
| 254 | if (!syms) | 254 | callchain_cursor_reset(&self->callchain_cursor); |
| 255 | return NULL; | ||
| 256 | 255 | ||
| 257 | for (i = 0; i < chain->nr; i++) { | 256 | for (i = 0; i < chain->nr; i++) { |
| 258 | u64 ip = chain->ips[i]; | 257 | u64 ip = chain->ips[i]; |
| @@ -281,12 +280,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
| 281 | *parent = al.sym; | 280 | *parent = al.sym; |
| 282 | if (!symbol_conf.use_callchain) | 281 | if (!symbol_conf.use_callchain) |
| 283 | break; | 282 | break; |
| 284 | syms[i].map = al.map; | ||
| 285 | syms[i].sym = al.sym; | ||
| 286 | } | 283 | } |
| 284 | |||
| 285 | err = callchain_cursor_append(&self->callchain_cursor, | ||
| 286 | ip, al.map, al.sym); | ||
| 287 | if (err) | ||
| 288 | return err; | ||
| 287 | } | 289 | } |
| 288 | 290 | ||
| 289 | return syms; | 291 | return 0; |
| 290 | } | 292 | } |
| 291 | 293 | ||
| 292 | static int process_event_synth_stub(event_t *event __used, | 294 | static int process_event_synth_stub(event_t *event __used, |
| @@ -494,7 +496,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
| 494 | if (iter->timestamp > limit) | 496 | if (iter->timestamp > limit) |
| 495 | break; | 497 | break; |
| 496 | 498 | ||
| 497 | event__parse_sample(iter->event, s, &sample); | 499 | perf_session__parse_sample(s, iter->event, &sample); |
| 498 | perf_session_deliver_event(s, iter->event, &sample, ops, | 500 | perf_session_deliver_event(s, iter->event, &sample, ops, |
| 499 | iter->file_offset); | 501 | iter->file_offset); |
| 500 | 502 | ||
| @@ -804,7 +806,7 @@ static int perf_session__process_event(struct perf_session *session, | |||
| 804 | /* | 806 | /* |
| 805 | * For all kernel events we get the sample data | 807 | * For all kernel events we get the sample data |
| 806 | */ | 808 | */ |
| 807 | event__parse_sample(event, session, &sample); | 809 | perf_session__parse_sample(session, event, &sample); |
| 808 | 810 | ||
| 809 | /* Preprocess sample records - precheck callchains */ | 811 | /* Preprocess sample records - precheck callchains */ |
| 810 | if (perf_session__preprocess_sample(session, event, &sample)) | 812 | if (perf_session__preprocess_sample(session, event, &sample)) |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index decd83f274fd..78239767011e 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -51,7 +51,8 @@ struct perf_session { | |||
| 51 | int cwdlen; | 51 | int cwdlen; |
| 52 | char *cwd; | 52 | char *cwd; |
| 53 | struct ordered_samples ordered_samples; | 53 | struct ordered_samples ordered_samples; |
| 54 | char filename[0]; | 54 | struct callchain_cursor callchain_cursor; |
| 55 | char filename[0]; | ||
| 55 | }; | 56 | }; |
| 56 | 57 | ||
| 57 | struct perf_event_ops; | 58 | struct perf_event_ops; |
| @@ -94,10 +95,10 @@ int __perf_session__process_events(struct perf_session *self, | |||
| 94 | int perf_session__process_events(struct perf_session *self, | 95 | int perf_session__process_events(struct perf_session *self, |
| 95 | struct perf_event_ops *event_ops); | 96 | struct perf_event_ops *event_ops); |
| 96 | 97 | ||
| 97 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 98 | int perf_session__resolve_callchain(struct perf_session *self, |
| 98 | struct thread *thread, | 99 | struct thread *thread, |
| 99 | struct ip_callchain *chain, | 100 | struct ip_callchain *chain, |
| 100 | struct symbol **parent); | 101 | struct symbol **parent); |
| 101 | 102 | ||
| 102 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 103 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
| 103 | 104 | ||
| @@ -154,4 +155,13 @@ size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) | |||
| 154 | { | 155 | { |
| 155 | return hists__fprintf_nr_events(&self->hists, fp); | 156 | return hists__fprintf_nr_events(&self->hists, fp); |
| 156 | } | 157 | } |
| 158 | |||
| 159 | static inline int perf_session__parse_sample(struct perf_session *session, | ||
| 160 | const event_t *event, | ||
| 161 | struct sample_data *sample) | ||
| 162 | { | ||
| 163 | return event__parse_sample(event, session->sample_type, | ||
| 164 | session->sample_id_all, sample); | ||
| 165 | } | ||
| 166 | |||
| 157 | #endif /* __PERF_SESSION_H */ | 167 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 00f4eade2e3e..d5d3b22250f3 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -7,61 +7,6 @@ | |||
| 7 | #include "util.h" | 7 | #include "util.h" |
| 8 | #include "debug.h" | 8 | #include "debug.h" |
| 9 | 9 | ||
| 10 | /* Skip "." and ".." directories */ | ||
| 11 | static int filter(const struct dirent *dir) | ||
| 12 | { | ||
| 13 | if (dir->d_name[0] == '.') | ||
| 14 | return 0; | ||
| 15 | else | ||
| 16 | return 1; | ||
| 17 | } | ||
| 18 | |||
| 19 | struct thread_map *thread_map__new_by_pid(pid_t pid) | ||
| 20 | { | ||
| 21 | struct thread_map *threads; | ||
| 22 | char name[256]; | ||
| 23 | int items; | ||
| 24 | struct dirent **namelist = NULL; | ||
| 25 | int i; | ||
| 26 | |||
| 27 | sprintf(name, "/proc/%d/task", pid); | ||
| 28 | items = scandir(name, &namelist, filter, NULL); | ||
| 29 | if (items <= 0) | ||
| 30 | return NULL; | ||
| 31 | |||
| 32 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | ||
| 33 | if (threads != NULL) { | ||
| 34 | for (i = 0; i < items; i++) | ||
| 35 | threads->map[i] = atoi(namelist[i]->d_name); | ||
| 36 | threads->nr = items; | ||
| 37 | } | ||
| 38 | |||
| 39 | for (i=0; i<items; i++) | ||
| 40 | free(namelist[i]); | ||
| 41 | free(namelist); | ||
| 42 | |||
| 43 | return threads; | ||
| 44 | } | ||
| 45 | |||
| 46 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
| 47 | { | ||
| 48 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
| 49 | |||
| 50 | if (threads != NULL) { | ||
| 51 | threads->map[0] = tid; | ||
| 52 | threads->nr = 1; | ||
| 53 | } | ||
| 54 | |||
| 55 | return threads; | ||
| 56 | } | ||
| 57 | |||
| 58 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
| 59 | { | ||
| 60 | if (pid != -1) | ||
| 61 | return thread_map__new_by_pid(pid); | ||
| 62 | return thread_map__new_by_tid(tid); | ||
| 63 | } | ||
| 64 | |||
| 65 | static struct thread *thread__new(pid_t pid) | 10 | static struct thread *thread__new(pid_t pid) |
| 66 | { | 11 | { |
| 67 | struct thread *self = zalloc(sizeof(*self)); | 12 | struct thread *self = zalloc(sizeof(*self)); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index d7574101054a..e5f2401c1b5e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -18,24 +18,10 @@ struct thread { | |||
| 18 | int comm_len; | 18 | int comm_len; |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | struct thread_map { | ||
| 22 | int nr; | ||
| 23 | int map[]; | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct perf_session; | 21 | struct perf_session; |
| 27 | 22 | ||
| 28 | void thread__delete(struct thread *self); | 23 | void thread__delete(struct thread *self); |
| 29 | 24 | ||
| 30 | struct thread_map *thread_map__new_by_pid(pid_t pid); | ||
| 31 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
| 32 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
| 33 | |||
| 34 | static inline void thread_map__delete(struct thread_map *threads) | ||
| 35 | { | ||
| 36 | free(threads); | ||
| 37 | } | ||
| 38 | |||
| 39 | int thread__set_comm(struct thread *self, const char *comm); | 25 | int thread__set_comm(struct thread *self, const char *comm); |
| 40 | int thread__comm_len(struct thread *self); | 26 | int thread__comm_len(struct thread *self); |
| 41 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 27 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c new file mode 100644 index 000000000000..a5df131b77c3 --- /dev/null +++ b/tools/perf/util/thread_map.c | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | #include <dirent.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include "thread_map.h" | ||
| 5 | |||
| 6 | /* Skip "." and ".." directories */ | ||
| 7 | static int filter(const struct dirent *dir) | ||
| 8 | { | ||
| 9 | if (dir->d_name[0] == '.') | ||
| 10 | return 0; | ||
| 11 | else | ||
| 12 | return 1; | ||
| 13 | } | ||
| 14 | |||
| 15 | struct thread_map *thread_map__new_by_pid(pid_t pid) | ||
| 16 | { | ||
| 17 | struct thread_map *threads; | ||
| 18 | char name[256]; | ||
| 19 | int items; | ||
| 20 | struct dirent **namelist = NULL; | ||
| 21 | int i; | ||
| 22 | |||
| 23 | sprintf(name, "/proc/%d/task", pid); | ||
| 24 | items = scandir(name, &namelist, filter, NULL); | ||
| 25 | if (items <= 0) | ||
| 26 | return NULL; | ||
| 27 | |||
| 28 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | ||
| 29 | if (threads != NULL) { | ||
| 30 | for (i = 0; i < items; i++) | ||
| 31 | threads->map[i] = atoi(namelist[i]->d_name); | ||
| 32 | threads->nr = items; | ||
| 33 | } | ||
| 34 | |||
| 35 | for (i=0; i<items; i++) | ||
| 36 | free(namelist[i]); | ||
| 37 | free(namelist); | ||
| 38 | |||
| 39 | return threads; | ||
| 40 | } | ||
| 41 | |||
| 42 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
| 43 | { | ||
| 44 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
| 45 | |||
| 46 | if (threads != NULL) { | ||
| 47 | threads->map[0] = tid; | ||
| 48 | threads->nr = 1; | ||
| 49 | } | ||
| 50 | |||
| 51 | return threads; | ||
| 52 | } | ||
| 53 | |||
| 54 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
| 55 | { | ||
| 56 | if (pid != -1) | ||
| 57 | return thread_map__new_by_pid(pid); | ||
| 58 | return thread_map__new_by_tid(tid); | ||
| 59 | } | ||
| 60 | |||
| 61 | void thread_map__delete(struct thread_map *threads) | ||
| 62 | { | ||
| 63 | free(threads); | ||
| 64 | } | ||
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h new file mode 100644 index 000000000000..3cb907311409 --- /dev/null +++ b/tools/perf/util/thread_map.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef __PERF_THREAD_MAP_H | ||
| 2 | #define __PERF_THREAD_MAP_H | ||
| 3 | |||
| 4 | #include <sys/types.h> | ||
| 5 | |||
| 6 | struct thread_map { | ||
| 7 | int nr; | ||
| 8 | int map[]; | ||
| 9 | }; | ||
| 10 | |||
| 11 | struct thread_map *thread_map__new_by_pid(pid_t pid); | ||
| 12 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
| 13 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
| 14 | void thread_map__delete(struct thread_map *threads); | ||
| 15 | #endif /* __PERF_THREAD_MAP_H */ | ||
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 60c463c16028..86428239fa65 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
| @@ -377,7 +377,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | |||
| 377 | while (node) { | 377 | while (node) { |
| 378 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 378 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
| 379 | struct rb_node *next = rb_next(node); | 379 | struct rb_node *next = rb_next(node); |
| 380 | u64 cumul = cumul_hits(child); | 380 | u64 cumul = callchain_cumul_hits(child); |
| 381 | struct callchain_list *chain; | 381 | struct callchain_list *chain; |
| 382 | char folded_sign = ' '; | 382 | char folded_sign = ' '; |
| 383 | int first = true; | 383 | int first = true; |
