diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 450 |
1 files changed, 169 insertions, 281 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 60cac6f92e8b..6febcc168a8c 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -18,11 +18,13 @@ | |||
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> |
@@ -37,16 +39,14 @@ enum write_mode_t { | |||
37 | 39 | ||
38 | static u64 user_interval = ULLONG_MAX; | 40 | static u64 user_interval = ULLONG_MAX; |
39 | static u64 default_interval = 0; | 41 | static u64 default_interval = 0; |
40 | static u64 sample_type; | ||
41 | 42 | ||
42 | static struct cpu_map *cpus; | ||
43 | static unsigned int page_size; | 43 | static unsigned int page_size; |
44 | static unsigned int mmap_pages = 128; | 44 | static unsigned int mmap_pages = 128; |
45 | static unsigned int user_freq = UINT_MAX; | 45 | static unsigned int user_freq = UINT_MAX; |
46 | static int freq = 1000; | 46 | static int freq = 1000; |
47 | static int output; | 47 | static int output; |
48 | static int pipe_output = 0; | 48 | static int pipe_output = 0; |
49 | static const char *output_name = "perf.data"; | 49 | static const char *output_name = NULL; |
50 | static int group = 0; | 50 | static int group = 0; |
51 | static int realtime_prio = 0; | 51 | static int realtime_prio = 0; |
52 | static bool nodelay = false; | 52 | static bool nodelay = false; |
@@ -55,7 +55,6 @@ static bool sample_id_all_avail = true; | |||
55 | static bool system_wide = false; | 55 | static bool system_wide = false; |
56 | static pid_t target_pid = -1; | 56 | static pid_t target_pid = -1; |
57 | static pid_t target_tid = -1; | 57 | static pid_t target_tid = -1; |
58 | static struct thread_map *threads; | ||
59 | static pid_t child_pid = -1; | 58 | static pid_t child_pid = -1; |
60 | static bool no_inherit = false; | 59 | static bool no_inherit = false; |
61 | static enum write_mode_t write_mode = WRITE_FORCE; | 60 | static enum write_mode_t write_mode = WRITE_FORCE; |
@@ -66,51 +65,17 @@ static bool sample_address = false; | |||
66 | static bool sample_time = false; | 65 | static bool sample_time = false; |
67 | static bool no_buildid = false; | 66 | static bool no_buildid = false; |
68 | static bool no_buildid_cache = false; | 67 | static bool no_buildid_cache = false; |
68 | static struct perf_evlist *evsel_list; | ||
69 | 69 | ||
70 | static long samples = 0; | 70 | static long samples = 0; |
71 | static u64 bytes_written = 0; | 71 | static u64 bytes_written = 0; |
72 | 72 | ||
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; | 73 | static int file_new = 1; |
79 | static off_t post_processing_offset; | 74 | static off_t post_processing_offset; |
80 | 75 | ||
81 | static struct perf_session *session; | 76 | static struct perf_session *session; |
82 | static const char *cpu_list; | 77 | static const char *cpu_list; |
83 | 78 | ||
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) | 79 | static void advance_output(size_t size) |
115 | { | 80 | { |
116 | bytes_written += size; | 81 | bytes_written += size; |
@@ -131,42 +96,26 @@ static void write_output(void *buf, size_t size) | |||
131 | } | 96 | } |
132 | } | 97 | } |
133 | 98 | ||
134 | static int process_synthesized_event(event_t *event, | 99 | static int process_synthesized_event(union perf_event *event, |
135 | struct sample_data *sample __used, | 100 | struct perf_sample *sample __used, |
136 | struct perf_session *self __used) | 101 | struct perf_session *self __used) |
137 | { | 102 | { |
138 | write_output(event, event->header.size); | 103 | write_output(event, event->header.size); |
139 | return 0; | 104 | return 0; |
140 | } | 105 | } |
141 | 106 | ||
142 | static void mmap_read(struct mmap_data *md) | 107 | static void mmap_read(struct perf_mmap *md) |
143 | { | 108 | { |
144 | unsigned int head = mmap_read_head(md); | 109 | unsigned int head = perf_mmap__read_head(md); |
145 | unsigned int old = md->prev; | 110 | unsigned int old = md->prev; |
146 | unsigned char *data = md->base + page_size; | 111 | unsigned char *data = md->base + page_size; |
147 | unsigned long size; | 112 | unsigned long size; |
148 | void *buf; | 113 | void *buf; |
149 | int diff; | ||
150 | 114 | ||
151 | /* | 115 | if (old == head) |
152 | * If we're further behind than half the buffer, there's a chance | 116 | return; |
153 | * the writer will bite our tail and mess up the samples under us. | ||
154 | * | ||
155 | * If we somehow ended up ahead of the head, we got messed up. | ||
156 | * | ||
157 | * In either case, truncate and restart at head. | ||
158 | */ | ||
159 | diff = head - old; | ||
160 | if (diff < 0) { | ||
161 | fprintf(stderr, "WARNING: failed to keep up with mmap data\n"); | ||
162 | /* | ||
163 | * head points to a known good entry, start there. | ||
164 | */ | ||
165 | old = head; | ||
166 | } | ||
167 | 117 | ||
168 | if (old != head) | 118 | samples++; |
169 | samples++; | ||
170 | 119 | ||
171 | size = head - old; | 120 | size = head - old; |
172 | 121 | ||
@@ -185,7 +134,7 @@ static void mmap_read(struct mmap_data *md) | |||
185 | write_output(buf, size); | 134 | write_output(buf, size); |
186 | 135 | ||
187 | md->prev = old; | 136 | md->prev = old; |
188 | mmap_write_tail(md, old); | 137 | perf_mmap__write_tail(md, old); |
189 | } | 138 | } |
190 | 139 | ||
191 | static volatile int done = 0; | 140 | static volatile int done = 0; |
@@ -209,53 +158,10 @@ static void sig_atexit(void) | |||
209 | kill(getpid(), signr); | 158 | kill(getpid(), signr); |
210 | } | 159 | } |
211 | 160 | ||
212 | static int group_fd; | 161 | static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) |
213 | |||
214 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) | ||
215 | { | ||
216 | struct perf_header_attr *h_attr; | ||
217 | |||
218 | if (nr < session->header.attrs) { | ||
219 | h_attr = session->header.attr[nr]; | ||
220 | } else { | ||
221 | h_attr = perf_header_attr__new(a); | ||
222 | if (h_attr != NULL) | ||
223 | if (perf_header__add_attr(&session->header, h_attr) < 0) { | ||
224 | perf_header_attr__delete(h_attr); | ||
225 | h_attr = NULL; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | return h_attr; | ||
230 | } | ||
231 | |||
232 | static void create_counter(struct perf_evsel *evsel, int cpu) | ||
233 | { | 162 | { |
234 | char *filter = evsel->filter; | ||
235 | struct perf_event_attr *attr = &evsel->attr; | 163 | struct perf_event_attr *attr = &evsel->attr; |
236 | struct perf_header_attr *h_attr; | ||
237 | int track = !evsel->idx; /* only the first counter needs these */ | 164 | int track = !evsel->idx; /* only the first counter needs these */ |
238 | int thread_index; | ||
239 | int ret; | ||
240 | struct { | ||
241 | u64 count; | ||
242 | u64 time_enabled; | ||
243 | u64 time_running; | ||
244 | u64 id; | ||
245 | } read_data; | ||
246 | /* | ||
247 | * Check if parse_single_tracepoint_event has already asked for | ||
248 | * PERF_SAMPLE_TIME. | ||
249 | * | ||
250 | * XXX this is kludgy but short term fix for problems introduced by | ||
251 | * eac23d1c that broke 'perf script' by having different sample_types | ||
252 | * when using multiple tracepoint events when we use a perf binary | ||
253 | * that tries to use sample_id_all on an older kernel. | ||
254 | * | ||
255 | * We need to move counter creation to perf_session, support | ||
256 | * different sample_types, etc. | ||
257 | */ | ||
258 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
259 | 165 | ||
260 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 166 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
261 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 167 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
@@ -263,7 +169,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
263 | 169 | ||
264 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 170 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
265 | 171 | ||
266 | if (nr_counters > 1) | 172 | if (evlist->nr_entries > 1) |
267 | attr->sample_type |= PERF_SAMPLE_ID; | 173 | attr->sample_type |= PERF_SAMPLE_ID; |
268 | 174 | ||
269 | /* | 175 | /* |
@@ -315,19 +221,58 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
315 | 221 | ||
316 | attr->mmap = track; | 222 | attr->mmap = track; |
317 | attr->comm = track; | 223 | attr->comm = track; |
318 | attr->inherit = !no_inherit; | 224 | |
319 | if (target_pid == -1 && target_tid == -1 && !system_wide) { | 225 | if (target_pid == -1 && target_tid == -1 && !system_wide) { |
320 | attr->disabled = 1; | 226 | attr->disabled = 1; |
321 | attr->enable_on_exec = 1; | 227 | attr->enable_on_exec = 1; |
322 | } | 228 | } |
323 | retry_sample_id: | 229 | } |
324 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
325 | 230 | ||
326 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { | 231 | static bool perf_evlist__equal(struct perf_evlist *evlist, |
327 | try_again: | 232 | struct perf_evlist *other) |
328 | FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); | 233 | { |
234 | struct perf_evsel *pos, *pair; | ||
235 | |||
236 | if (evlist->nr_entries != other->nr_entries) | ||
237 | return false; | ||
238 | |||
239 | pair = list_entry(other->entries.next, struct perf_evsel, node); | ||
329 | 240 | ||
330 | if (FD(evsel, nr_cpu, thread_index) < 0) { | 241 | list_for_each_entry(pos, &evlist->entries, node) { |
242 | if (memcmp(&pos->attr, &pair->attr, sizeof(pos->attr) != 0)) | ||
243 | return false; | ||
244 | pair = list_entry(pair->node.next, struct perf_evsel, node); | ||
245 | } | ||
246 | |||
247 | return true; | ||
248 | } | ||
249 | |||
250 | static void open_counters(struct perf_evlist *evlist) | ||
251 | { | ||
252 | struct perf_evsel *pos; | ||
253 | |||
254 | list_for_each_entry(pos, &evlist->entries, node) { | ||
255 | struct perf_event_attr *attr = &pos->attr; | ||
256 | /* | ||
257 | * Check if parse_single_tracepoint_event has already asked for | ||
258 | * PERF_SAMPLE_TIME. | ||
259 | * | ||
260 | * XXX this is kludgy but short term fix for problems introduced by | ||
261 | * eac23d1c that broke 'perf script' by having different sample_types | ||
262 | * when using multiple tracepoint events when we use a perf binary | ||
263 | * that tries to use sample_id_all on an older kernel. | ||
264 | * | ||
265 | * We need to move counter creation to perf_session, support | ||
266 | * different sample_types, etc. | ||
267 | */ | ||
268 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
269 | |||
270 | config_attr(pos, evlist); | ||
271 | retry_sample_id: | ||
272 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
273 | try_again: | ||
274 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group, | ||
275 | !no_inherit) < 0) { | ||
331 | int err = errno; | 276 | int err = errno; |
332 | 277 | ||
333 | if (err == EPERM || err == EACCES) | 278 | if (err == EPERM || err == EACCES) |
@@ -364,7 +309,7 @@ try_again: | |||
364 | } | 309 | } |
365 | printf("\n"); | 310 | printf("\n"); |
366 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 311 | 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)); | 312 | err, strerror(err)); |
368 | 313 | ||
369 | #if defined(__i386__) || defined(__x86_64__) | 314 | #if defined(__i386__) || defined(__x86_64__) |
370 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 315 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
@@ -375,90 +320,28 @@ try_again: | |||
375 | #endif | 320 | #endif |
376 | 321 | ||
377 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 322 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
378 | exit(-1); | ||
379 | } | 323 | } |
324 | } | ||
380 | 325 | ||
381 | h_attr = get_header_attr(attr, evsel->idx); | 326 | if (perf_evlist__set_filters(evlist)) { |
382 | if (h_attr == NULL) | 327 | error("failed to set filter with %d (%s)\n", errno, |
383 | die("nomem\n"); | 328 | strerror(errno)); |
329 | exit(-1); | ||
330 | } | ||
384 | 331 | ||
385 | if (!file_new) { | 332 | if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) |
386 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | 333 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
387 | fprintf(stderr, "incompatible append\n"); | ||
388 | exit(-1); | ||
389 | } | ||
390 | } | ||
391 | 334 | ||
392 | if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { | 335 | if (file_new) |
393 | perror("Unable to read perf file descriptor"); | 336 | session->evlist = evlist; |
337 | else { | ||
338 | if (!perf_evlist__equal(session->evlist, evlist)) { | ||
339 | fprintf(stderr, "incompatible append\n"); | ||
394 | exit(-1); | 340 | exit(-1); |
395 | } | 341 | } |
342 | } | ||
396 | 343 | ||
397 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { | 344 | perf_session__update_sample_type(session); |
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 | } | ||
446 | } | ||
447 | |||
448 | if (!sample_type) | ||
449 | sample_type = attr->sample_type; | ||
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 | |||
461 | nr_cpu++; | ||
462 | } | 345 | } |
463 | 346 | ||
464 | static int process_buildids(void) | 347 | static int process_buildids(void) |
@@ -481,14 +364,14 @@ static void atexit_header(void) | |||
481 | 364 | ||
482 | if (!no_buildid) | 365 | if (!no_buildid) |
483 | process_buildids(); | 366 | process_buildids(); |
484 | perf_header__write(&session->header, output, true); | 367 | perf_session__write_header(session, evsel_list, output, true); |
485 | perf_session__delete(session); | 368 | perf_session__delete(session); |
486 | perf_evsel_list__delete(); | 369 | perf_evlist__delete(evsel_list); |
487 | symbol__exit(); | 370 | symbol__exit(); |
488 | } | 371 | } |
489 | } | 372 | } |
490 | 373 | ||
491 | static void event__synthesize_guest_os(struct machine *machine, void *data) | 374 | static void perf_event__synthesize_guest_os(struct machine *machine, void *data) |
492 | { | 375 | { |
493 | int err; | 376 | int err; |
494 | struct perf_session *psession = data; | 377 | struct perf_session *psession = data; |
@@ -504,8 +387,8 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) | |||
504 | *method is used to avoid symbol missing when the first addr is | 387 | *method is used to avoid symbol missing when the first addr is |
505 | *in module instead of in guest kernel. | 388 | *in module instead of in guest kernel. |
506 | */ | 389 | */ |
507 | err = event__synthesize_modules(process_synthesized_event, | 390 | err = perf_event__synthesize_modules(process_synthesized_event, |
508 | psession, machine); | 391 | psession, machine); |
509 | if (err < 0) | 392 | if (err < 0) |
510 | pr_err("Couldn't record guest kernel [%d]'s reference" | 393 | pr_err("Couldn't record guest kernel [%d]'s reference" |
511 | " relocation symbol.\n", machine->pid); | 394 | " relocation symbol.\n", machine->pid); |
@@ -514,11 +397,12 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) | |||
514 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms | 397 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms |
515 | * have no _text sometimes. | 398 | * have no _text sometimes. |
516 | */ | 399 | */ |
517 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 400 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
518 | psession, machine, "_text"); | 401 | psession, machine, "_text"); |
519 | if (err < 0) | 402 | if (err < 0) |
520 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 403 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
521 | psession, machine, "_stext"); | 404 | psession, machine, |
405 | "_stext"); | ||
522 | if (err < 0) | 406 | if (err < 0) |
523 | pr_err("Couldn't record guest kernel [%d]'s reference" | 407 | pr_err("Couldn't record guest kernel [%d]'s reference" |
524 | " relocation symbol.\n", machine->pid); | 408 | " relocation symbol.\n", machine->pid); |
@@ -533,9 +417,9 @@ static void mmap_read_all(void) | |||
533 | { | 417 | { |
534 | int i; | 418 | int i; |
535 | 419 | ||
536 | for (i = 0; i < nr_cpu; i++) { | 420 | for (i = 0; i < evsel_list->cpus->nr; i++) { |
537 | if (mmap_array[i].base) | 421 | if (evsel_list->mmap[i].base) |
538 | mmap_read(&mmap_array[i]); | 422 | mmap_read(&evsel_list->mmap[i]); |
539 | } | 423 | } |
540 | 424 | ||
541 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) | 425 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) |
@@ -566,18 +450,26 @@ static int __cmd_record(int argc, const char **argv) | |||
566 | exit(-1); | 450 | exit(-1); |
567 | } | 451 | } |
568 | 452 | ||
569 | if (!strcmp(output_name, "-")) | 453 | if (!output_name) { |
570 | pipe_output = 1; | 454 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) |
571 | else if (!stat(output_name, &st) && st.st_size) { | 455 | pipe_output = 1; |
572 | if (write_mode == WRITE_FORCE) { | 456 | else |
573 | char oldname[PATH_MAX]; | 457 | output_name = "perf.data"; |
574 | snprintf(oldname, sizeof(oldname), "%s.old", | 458 | } |
575 | output_name); | 459 | if (output_name) { |
576 | unlink(oldname); | 460 | if (!strcmp(output_name, "-")) |
577 | rename(output_name, oldname); | 461 | pipe_output = 1; |
462 | else if (!stat(output_name, &st) && st.st_size) { | ||
463 | if (write_mode == WRITE_FORCE) { | ||
464 | char oldname[PATH_MAX]; | ||
465 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
466 | output_name); | ||
467 | unlink(oldname); | ||
468 | rename(output_name, oldname); | ||
469 | } | ||
470 | } else if (write_mode == WRITE_APPEND) { | ||
471 | write_mode = WRITE_FORCE; | ||
578 | } | 472 | } |
579 | } else if (write_mode == WRITE_APPEND) { | ||
580 | write_mode = WRITE_FORCE; | ||
581 | } | 473 | } |
582 | 474 | ||
583 | flags = O_CREAT|O_RDWR; | 475 | flags = O_CREAT|O_RDWR; |
@@ -606,19 +498,14 @@ static int __cmd_record(int argc, const char **argv) | |||
606 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); | 498 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); |
607 | 499 | ||
608 | if (!file_new) { | 500 | if (!file_new) { |
609 | err = perf_header__read(session, output); | 501 | err = perf_session__read_header(session, output); |
610 | if (err < 0) | 502 | if (err < 0) |
611 | goto out_delete_session; | 503 | goto out_delete_session; |
612 | } | 504 | } |
613 | 505 | ||
614 | if (have_tracepoints(&evsel_list)) | 506 | if (have_tracepoints(&evsel_list->entries)) |
615 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 507 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
616 | 508 | ||
617 | /* | ||
618 | * perf_session__delete(session) will be called at atexit_header() | ||
619 | */ | ||
620 | atexit(atexit_header); | ||
621 | |||
622 | if (forks) { | 509 | if (forks) { |
623 | child_pid = fork(); | 510 | child_pid = fork(); |
624 | if (child_pid < 0) { | 511 | if (child_pid < 0) { |
@@ -659,7 +546,7 @@ static int __cmd_record(int argc, const char **argv) | |||
659 | } | 546 | } |
660 | 547 | ||
661 | if (!system_wide && target_tid == -1 && target_pid == -1) | 548 | if (!system_wide && target_tid == -1 && target_pid == -1) |
662 | threads->map[0] = child_pid; | 549 | evsel_list->threads->map[0] = child_pid; |
663 | 550 | ||
664 | close(child_ready_pipe[1]); | 551 | close(child_ready_pipe[1]); |
665 | close(go_pipe[0]); | 552 | close(go_pipe[0]); |
@@ -673,46 +560,42 @@ static int __cmd_record(int argc, const char **argv) | |||
673 | close(child_ready_pipe[0]); | 560 | close(child_ready_pipe[0]); |
674 | } | 561 | } |
675 | 562 | ||
676 | if (!system_wide && no_inherit && !cpu_list) { | 563 | 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 | 564 | ||
683 | perf_session__set_sample_type(session, sample_type); | 565 | /* |
566 | * perf_session__delete(session) will be called at atexit_header() | ||
567 | */ | ||
568 | atexit(atexit_header); | ||
684 | 569 | ||
685 | if (pipe_output) { | 570 | if (pipe_output) { |
686 | err = perf_header__write_pipe(output); | 571 | err = perf_header__write_pipe(output); |
687 | if (err < 0) | 572 | if (err < 0) |
688 | return err; | 573 | return err; |
689 | } else if (file_new) { | 574 | } else if (file_new) { |
690 | err = perf_header__write(&session->header, output, false); | 575 | err = perf_session__write_header(session, evsel_list, |
576 | output, false); | ||
691 | if (err < 0) | 577 | if (err < 0) |
692 | return err; | 578 | return err; |
693 | } | 579 | } |
694 | 580 | ||
695 | post_processing_offset = lseek(output, 0, SEEK_CUR); | 581 | post_processing_offset = lseek(output, 0, SEEK_CUR); |
696 | 582 | ||
697 | perf_session__set_sample_id_all(session, sample_id_all_avail); | ||
698 | |||
699 | if (pipe_output) { | 583 | if (pipe_output) { |
700 | err = event__synthesize_attrs(&session->header, | 584 | err = perf_session__synthesize_attrs(session, |
701 | process_synthesized_event, | 585 | process_synthesized_event); |
702 | session); | ||
703 | if (err < 0) { | 586 | if (err < 0) { |
704 | pr_err("Couldn't synthesize attrs.\n"); | 587 | pr_err("Couldn't synthesize attrs.\n"); |
705 | return err; | 588 | return err; |
706 | } | 589 | } |
707 | 590 | ||
708 | err = event__synthesize_event_types(process_synthesized_event, | 591 | err = perf_event__synthesize_event_types(process_synthesized_event, |
709 | session); | 592 | session); |
710 | if (err < 0) { | 593 | if (err < 0) { |
711 | pr_err("Couldn't synthesize event_types.\n"); | 594 | pr_err("Couldn't synthesize event_types.\n"); |
712 | return err; | 595 | return err; |
713 | } | 596 | } |
714 | 597 | ||
715 | if (have_tracepoints(&evsel_list)) { | 598 | if (have_tracepoints(&evsel_list->entries)) { |
716 | /* | 599 | /* |
717 | * FIXME err <= 0 here actually means that | 600 | * FIXME err <= 0 here actually means that |
718 | * there were no tracepoints so its not really | 601 | * there were no tracepoints so its not really |
@@ -721,9 +604,9 @@ static int __cmd_record(int argc, const char **argv) | |||
721 | * return this more properly and also | 604 | * return this more properly and also |
722 | * propagate errors that now are calling die() | 605 | * propagate errors that now are calling die() |
723 | */ | 606 | */ |
724 | err = event__synthesize_tracing_data(output, &evsel_list, | 607 | err = perf_event__synthesize_tracing_data(output, evsel_list, |
725 | process_synthesized_event, | 608 | process_synthesized_event, |
726 | session); | 609 | session); |
727 | if (err <= 0) { | 610 | if (err <= 0) { |
728 | pr_err("Couldn't record tracing data.\n"); | 611 | pr_err("Couldn't record tracing data.\n"); |
729 | return err; | 612 | return err; |
@@ -738,31 +621,34 @@ static int __cmd_record(int argc, const char **argv) | |||
738 | return -1; | 621 | return -1; |
739 | } | 622 | } |
740 | 623 | ||
741 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 624 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
742 | session, machine, "_text"); | 625 | session, machine, "_text"); |
743 | if (err < 0) | 626 | if (err < 0) |
744 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 627 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
745 | session, machine, "_stext"); | 628 | session, machine, "_stext"); |
746 | if (err < 0) | 629 | if (err < 0) |
747 | pr_err("Couldn't record kernel reference relocation symbol\n" | 630 | pr_err("Couldn't record kernel reference relocation symbol\n" |
748 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | 631 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" |
749 | "Check /proc/kallsyms permission or run as root.\n"); | 632 | "Check /proc/kallsyms permission or run as root.\n"); |
750 | 633 | ||
751 | err = event__synthesize_modules(process_synthesized_event, | 634 | err = perf_event__synthesize_modules(process_synthesized_event, |
752 | session, machine); | 635 | session, machine); |
753 | if (err < 0) | 636 | if (err < 0) |
754 | pr_err("Couldn't record kernel module information.\n" | 637 | pr_err("Couldn't record kernel module information.\n" |
755 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | 638 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" |
756 | "Check /proc/modules permission or run as root.\n"); | 639 | "Check /proc/modules permission or run as root.\n"); |
757 | 640 | ||
758 | if (perf_guest) | 641 | if (perf_guest) |
759 | perf_session__process_machines(session, event__synthesize_guest_os); | 642 | perf_session__process_machines(session, |
643 | perf_event__synthesize_guest_os); | ||
760 | 644 | ||
761 | if (!system_wide) | 645 | if (!system_wide) |
762 | event__synthesize_thread_map(threads, process_synthesized_event, | 646 | perf_event__synthesize_thread_map(evsel_list->threads, |
763 | session); | 647 | process_synthesized_event, |
648 | session); | ||
764 | else | 649 | else |
765 | event__synthesize_threads(process_synthesized_event, session); | 650 | perf_event__synthesize_threads(process_synthesized_event, |
651 | session); | ||
766 | 652 | ||
767 | if (realtime_prio) { | 653 | if (realtime_prio) { |
768 | struct sched_param param; | 654 | struct sched_param param; |
@@ -789,17 +675,17 @@ static int __cmd_record(int argc, const char **argv) | |||
789 | if (hits == samples) { | 675 | if (hits == samples) { |
790 | if (done) | 676 | if (done) |
791 | break; | 677 | break; |
792 | err = poll(event_array, nr_poll, -1); | 678 | err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); |
793 | waking++; | 679 | waking++; |
794 | } | 680 | } |
795 | 681 | ||
796 | if (done) { | 682 | if (done) { |
797 | for (i = 0; i < nr_cpu; i++) { | 683 | for (i = 0; i < evsel_list->cpus->nr; i++) { |
798 | struct perf_evsel *pos; | 684 | struct perf_evsel *pos; |
799 | 685 | ||
800 | list_for_each_entry(pos, &evsel_list, node) { | 686 | list_for_each_entry(pos, &evsel_list->entries, node) { |
801 | for (thread = 0; | 687 | for (thread = 0; |
802 | thread < threads->nr; | 688 | thread < evsel_list->threads->nr; |
803 | thread++) | 689 | thread++) |
804 | ioctl(FD(pos, i, thread), | 690 | ioctl(FD(pos, i, thread), |
805 | PERF_EVENT_IOC_DISABLE); | 691 | PERF_EVENT_IOC_DISABLE); |
@@ -838,10 +724,10 @@ static const char * const record_usage[] = { | |||
838 | static bool force, append_file; | 724 | static bool force, append_file; |
839 | 725 | ||
840 | const struct option record_options[] = { | 726 | const struct option record_options[] = { |
841 | OPT_CALLBACK('e', "event", NULL, "event", | 727 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
842 | "event selector. use 'perf list' to list available events", | 728 | "event selector. use 'perf list' to list available events", |
843 | parse_events), | 729 | parse_events), |
844 | OPT_CALLBACK(0, "filter", NULL, "filter", | 730 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
845 | "event filter", parse_filter), | 731 | "event filter", parse_filter), |
846 | OPT_INTEGER('p', "pid", &target_pid, | 732 | OPT_INTEGER('p', "pid", &target_pid, |
847 | "record events on existing process id"), | 733 | "record events on existing process id"), |
@@ -884,6 +770,9 @@ const struct option record_options[] = { | |||
884 | "do not update the buildid cache"), | 770 | "do not update the buildid cache"), |
885 | OPT_BOOLEAN('B', "no-buildid", &no_buildid, | 771 | OPT_BOOLEAN('B', "no-buildid", &no_buildid, |
886 | "do not collect buildids in perf.data"), | 772 | "do not collect buildids in perf.data"), |
773 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | ||
774 | "monitor event in cgroup name only", | ||
775 | parse_cgroups), | ||
887 | OPT_END() | 776 | OPT_END() |
888 | }; | 777 | }; |
889 | 778 | ||
@@ -892,6 +781,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
892 | int err = -ENOMEM; | 781 | int err = -ENOMEM; |
893 | struct perf_evsel *pos; | 782 | struct perf_evsel *pos; |
894 | 783 | ||
784 | evsel_list = perf_evlist__new(NULL, NULL); | ||
785 | if (evsel_list == NULL) | ||
786 | return -ENOMEM; | ||
787 | |||
895 | argc = parse_options(argc, argv, record_options, record_usage, | 788 | argc = parse_options(argc, argv, record_options, record_usage, |
896 | PARSE_OPT_STOP_AT_NON_OPTION); | 789 | PARSE_OPT_STOP_AT_NON_OPTION); |
897 | if (!argc && target_pid == -1 && target_tid == -1 && | 790 | if (!argc && target_pid == -1 && target_tid == -1 && |
@@ -908,12 +801,19 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
908 | write_mode = WRITE_FORCE; | 801 | write_mode = WRITE_FORCE; |
909 | } | 802 | } |
910 | 803 | ||
804 | if (nr_cgroups && !system_wide) { | ||
805 | fprintf(stderr, "cgroup monitoring only available in" | ||
806 | " system-wide mode\n"); | ||
807 | usage_with_options(record_usage, record_options); | ||
808 | } | ||
809 | |||
911 | symbol__init(); | 810 | symbol__init(); |
912 | 811 | ||
913 | if (no_buildid_cache || no_buildid) | 812 | if (no_buildid_cache || no_buildid) |
914 | disable_buildid_cache(); | 813 | disable_buildid_cache(); |
915 | 814 | ||
916 | if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) { | 815 | if (evsel_list->nr_entries == 0 && |
816 | perf_evlist__add_default(evsel_list) < 0) { | ||
917 | pr_err("Not enough memory for event selector list\n"); | 817 | pr_err("Not enough memory for event selector list\n"); |
918 | goto out_symbol_exit; | 818 | goto out_symbol_exit; |
919 | } | 819 | } |
@@ -921,27 +821,19 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
921 | if (target_pid != -1) | 821 | if (target_pid != -1) |
922 | target_tid = target_pid; | 822 | target_tid = target_pid; |
923 | 823 | ||
924 | threads = thread_map__new(target_pid, target_tid); | 824 | if (perf_evlist__create_maps(evsel_list, target_pid, |
925 | if (threads == NULL) { | 825 | target_tid, cpu_list) < 0) |
926 | pr_err("Problems finding threads of monitor\n"); | ||
927 | usage_with_options(record_usage, record_options); | 826 | usage_with_options(record_usage, record_options); |
928 | } | ||
929 | 827 | ||
930 | cpus = cpu_map__new(cpu_list); | 828 | list_for_each_entry(pos, &evsel_list->entries, node) { |
931 | if (cpus == NULL) { | 829 | if (perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, |
932 | perror("failed to parse CPUs map"); | 830 | evsel_list->threads->nr) < 0) |
933 | return -1; | ||
934 | } | ||
935 | |||
936 | list_for_each_entry(pos, &evsel_list, node) { | ||
937 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | ||
938 | goto out_free_fd; | 831 | goto out_free_fd; |
939 | if (perf_header__push_event(pos->attr.config, event_name(pos))) | 832 | if (perf_header__push_event(pos->attr.config, event_name(pos))) |
940 | goto out_free_fd; | 833 | goto out_free_fd; |
941 | } | 834 | } |
942 | event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS * | 835 | |
943 | MAX_COUNTERS * threads->nr)); | 836 | if (perf_evlist__alloc_pollfd(evsel_list) < 0) |
944 | if (!event_array) | ||
945 | goto out_free_fd; | 837 | goto out_free_fd; |
946 | 838 | ||
947 | if (user_interval != ULLONG_MAX) | 839 | if (user_interval != ULLONG_MAX) |
@@ -959,16 +851,12 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
959 | } else { | 851 | } else { |
960 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 852 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
961 | err = -EINVAL; | 853 | err = -EINVAL; |
962 | goto out_free_event_array; | 854 | goto out_free_fd; |
963 | } | 855 | } |
964 | 856 | ||
965 | err = __cmd_record(argc, argv); | 857 | err = __cmd_record(argc, argv); |
966 | |||
967 | out_free_event_array: | ||
968 | free(event_array); | ||
969 | out_free_fd: | 858 | out_free_fd: |
970 | thread_map__delete(threads); | 859 | perf_evlist__delete_maps(evsel_list); |
971 | threads = NULL; | ||
972 | out_symbol_exit: | 860 | out_symbol_exit: |
973 | symbol__exit(); | 861 | symbol__exit(); |
974 | return err; | 862 | return err; |