diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 12:25:52 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 12:25:52 -0400 |
commit | 19504828b4bee5e471bcd35e214bc6fd0d380692 (patch) | |
tree | 30d4ffb6783daf9fadd47548c035646d3f0f073e | |
parent | 57d19e80f459dd845fb3cfeba8e6df8471bac142 (diff) | |
parent | 3cb6d1540880e767d911b79eb49578de2190f428 (diff) |
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
perf tools: Fix sample size bit operations
perf tools: Fix ommitted mmap data update on remap
watchdog: Change the default timeout and configure nmi watchdog period based on watchdog_thresh
watchdog: Disable watchdog when thresh is zero
watchdog: Only disable/enable watchdog if neccessary
watchdog: Fix rounding bug in get_sample_period()
perf tools: Propagate event parse error handling
perf tools: Robustify dynamic sample content fetch
perf tools: Pre-check sample size before parsing
perf tools: Move evlist sample helpers to evlist area
perf tools: Remove junk code in mmap size handling
perf tools: Check we are able to read the event size on mmap
-rw-r--r-- | arch/x86/kernel/apic/hw_nmi.c | 4 | ||||
-rw-r--r-- | include/linux/nmi.h | 7 | ||||
-rw-r--r-- | include/linux/sched.h | 1 | ||||
-rw-r--r-- | kernel/sysctl.c | 12 | ||||
-rw-r--r-- | kernel/watchdog.c | 52 | ||||
-rw-r--r-- | tools/perf/builtin-test.c | 9 | ||||
-rw-r--r-- | tools/perf/builtin-top.c | 7 | ||||
-rw-r--r-- | tools/perf/util/event.c | 46 | ||||
-rw-r--r-- | tools/perf/util/event.h | 12 | ||||
-rw-r--r-- | tools/perf/util/evlist.c | 31 | ||||
-rw-r--r-- | tools/perf/util/evlist.h | 3 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 32 | ||||
-rw-r--r-- | tools/perf/util/header.c | 31 | ||||
-rw-r--r-- | tools/perf/util/header.h | 2 | ||||
-rw-r--r-- | tools/perf/util/include/linux/list.h | 2 | ||||
-rw-r--r-- | tools/perf/util/python.c | 13 | ||||
-rw-r--r-- | tools/perf/util/session.c | 50 | ||||
-rw-r--r-- | tools/perf/util/session.h | 2 |
18 files changed, 216 insertions, 100 deletions
diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c index 5260fe91bcb6..d5e57db0f7be 100644 --- a/arch/x86/kernel/apic/hw_nmi.c +++ b/arch/x86/kernel/apic/hw_nmi.c | |||
@@ -19,9 +19,9 @@ | |||
19 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
20 | 20 | ||
21 | #ifdef CONFIG_HARDLOCKUP_DETECTOR | 21 | #ifdef CONFIG_HARDLOCKUP_DETECTOR |
22 | u64 hw_nmi_get_sample_period(void) | 22 | u64 hw_nmi_get_sample_period(int watchdog_thresh) |
23 | { | 23 | { |
24 | return (u64)(cpu_khz) * 1000 * 60; | 24 | return (u64)(cpu_khz) * 1000 * watchdog_thresh; |
25 | } | 25 | } |
26 | #endif | 26 | #endif |
27 | 27 | ||
diff --git a/include/linux/nmi.h b/include/linux/nmi.h index c536f8545f74..2d304efc89df 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h | |||
@@ -45,11 +45,12 @@ static inline bool trigger_all_cpu_backtrace(void) | |||
45 | 45 | ||
46 | #ifdef CONFIG_LOCKUP_DETECTOR | 46 | #ifdef CONFIG_LOCKUP_DETECTOR |
47 | int hw_nmi_is_cpu_stuck(struct pt_regs *); | 47 | int hw_nmi_is_cpu_stuck(struct pt_regs *); |
48 | u64 hw_nmi_get_sample_period(void); | 48 | u64 hw_nmi_get_sample_period(int watchdog_thresh); |
49 | extern int watchdog_enabled; | 49 | extern int watchdog_enabled; |
50 | extern int watchdog_thresh; | ||
50 | struct ctl_table; | 51 | struct ctl_table; |
51 | extern int proc_dowatchdog_enabled(struct ctl_table *, int , | 52 | extern int proc_dowatchdog(struct ctl_table *, int , |
52 | void __user *, size_t *, loff_t *); | 53 | void __user *, size_t *, loff_t *); |
53 | #endif | 54 | #endif |
54 | 55 | ||
55 | #endif | 56 | #endif |
diff --git a/include/linux/sched.h b/include/linux/sched.h index 885c4f242ad7..340f5ee57334 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -315,7 +315,6 @@ extern int proc_dowatchdog_thresh(struct ctl_table *table, int write, | |||
315 | void __user *buffer, | 315 | void __user *buffer, |
316 | size_t *lenp, loff_t *ppos); | 316 | size_t *lenp, loff_t *ppos); |
317 | extern unsigned int softlockup_panic; | 317 | extern unsigned int softlockup_panic; |
318 | extern int softlockup_thresh; | ||
319 | void lockup_detector_init(void); | 318 | void lockup_detector_init(void); |
320 | #else | 319 | #else |
321 | static inline void touch_softlockup_watchdog(void) | 320 | static inline void touch_softlockup_watchdog(void) |
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c0bb32414b17..3dd0c46fa3bb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -730,14 +730,16 @@ static struct ctl_table kern_table[] = { | |||
730 | .data = &watchdog_enabled, | 730 | .data = &watchdog_enabled, |
731 | .maxlen = sizeof (int), | 731 | .maxlen = sizeof (int), |
732 | .mode = 0644, | 732 | .mode = 0644, |
733 | .proc_handler = proc_dowatchdog_enabled, | 733 | .proc_handler = proc_dowatchdog, |
734 | .extra1 = &zero, | ||
735 | .extra2 = &one, | ||
734 | }, | 736 | }, |
735 | { | 737 | { |
736 | .procname = "watchdog_thresh", | 738 | .procname = "watchdog_thresh", |
737 | .data = &softlockup_thresh, | 739 | .data = &watchdog_thresh, |
738 | .maxlen = sizeof(int), | 740 | .maxlen = sizeof(int), |
739 | .mode = 0644, | 741 | .mode = 0644, |
740 | .proc_handler = proc_dowatchdog_thresh, | 742 | .proc_handler = proc_dowatchdog, |
741 | .extra1 = &neg_one, | 743 | .extra1 = &neg_one, |
742 | .extra2 = &sixty, | 744 | .extra2 = &sixty, |
743 | }, | 745 | }, |
@@ -755,7 +757,9 @@ static struct ctl_table kern_table[] = { | |||
755 | .data = &watchdog_enabled, | 757 | .data = &watchdog_enabled, |
756 | .maxlen = sizeof (int), | 758 | .maxlen = sizeof (int), |
757 | .mode = 0644, | 759 | .mode = 0644, |
758 | .proc_handler = proc_dowatchdog_enabled, | 760 | .proc_handler = proc_dowatchdog, |
761 | .extra1 = &zero, | ||
762 | .extra2 = &one, | ||
759 | }, | 763 | }, |
760 | #endif | 764 | #endif |
761 | #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86) | 765 | #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86) |
diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 14733d4d156b..6e63097fa73a 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c | |||
@@ -28,7 +28,7 @@ | |||
28 | #include <linux/perf_event.h> | 28 | #include <linux/perf_event.h> |
29 | 29 | ||
30 | int watchdog_enabled = 1; | 30 | int watchdog_enabled = 1; |
31 | int __read_mostly softlockup_thresh = 60; | 31 | int __read_mostly watchdog_thresh = 10; |
32 | 32 | ||
33 | static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts); | 33 | static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts); |
34 | static DEFINE_PER_CPU(struct task_struct *, softlockup_watchdog); | 34 | static DEFINE_PER_CPU(struct task_struct *, softlockup_watchdog); |
@@ -91,6 +91,17 @@ static int __init nosoftlockup_setup(char *str) | |||
91 | __setup("nosoftlockup", nosoftlockup_setup); | 91 | __setup("nosoftlockup", nosoftlockup_setup); |
92 | /* */ | 92 | /* */ |
93 | 93 | ||
94 | /* | ||
95 | * Hard-lockup warnings should be triggered after just a few seconds. Soft- | ||
96 | * lockups can have false positives under extreme conditions. So we generally | ||
97 | * want a higher threshold for soft lockups than for hard lockups. So we couple | ||
98 | * the thresholds with a factor: we make the soft threshold twice the amount of | ||
99 | * time the hard threshold is. | ||
100 | */ | ||
101 | static int get_softlockup_thresh() | ||
102 | { | ||
103 | return watchdog_thresh * 2; | ||
104 | } | ||
94 | 105 | ||
95 | /* | 106 | /* |
96 | * Returns seconds, approximately. We don't need nanosecond | 107 | * Returns seconds, approximately. We don't need nanosecond |
@@ -105,12 +116,12 @@ static unsigned long get_timestamp(int this_cpu) | |||
105 | static unsigned long get_sample_period(void) | 116 | static unsigned long get_sample_period(void) |
106 | { | 117 | { |
107 | /* | 118 | /* |
108 | * convert softlockup_thresh from seconds to ns | 119 | * convert watchdog_thresh from seconds to ns |
109 | * the divide by 5 is to give hrtimer 5 chances to | 120 | * the divide by 5 is to give hrtimer 5 chances to |
110 | * increment before the hardlockup detector generates | 121 | * increment before the hardlockup detector generates |
111 | * a warning | 122 | * a warning |
112 | */ | 123 | */ |
113 | return softlockup_thresh / 5 * NSEC_PER_SEC; | 124 | return get_softlockup_thresh() * (NSEC_PER_SEC / 5); |
114 | } | 125 | } |
115 | 126 | ||
116 | /* Commands for resetting the watchdog */ | 127 | /* Commands for resetting the watchdog */ |
@@ -182,7 +193,7 @@ static int is_softlockup(unsigned long touch_ts) | |||
182 | unsigned long now = get_timestamp(smp_processor_id()); | 193 | unsigned long now = get_timestamp(smp_processor_id()); |
183 | 194 | ||
184 | /* Warn about unreasonable delays: */ | 195 | /* Warn about unreasonable delays: */ |
185 | if (time_after(now, touch_ts + softlockup_thresh)) | 196 | if (time_after(now, touch_ts + get_softlockup_thresh())) |
186 | return now - touch_ts; | 197 | return now - touch_ts; |
187 | 198 | ||
188 | return 0; | 199 | return 0; |
@@ -359,7 +370,7 @@ static int watchdog_nmi_enable(int cpu) | |||
359 | 370 | ||
360 | /* Try to register using hardware perf events */ | 371 | /* Try to register using hardware perf events */ |
361 | wd_attr = &wd_hw_attr; | 372 | wd_attr = &wd_hw_attr; |
362 | wd_attr->sample_period = hw_nmi_get_sample_period(); | 373 | wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh); |
363 | event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback); | 374 | event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback); |
364 | if (!IS_ERR(event)) { | 375 | if (!IS_ERR(event)) { |
365 | printk(KERN_INFO "NMI watchdog enabled, takes one hw-pmu counter.\n"); | 376 | printk(KERN_INFO "NMI watchdog enabled, takes one hw-pmu counter.\n"); |
@@ -501,28 +512,25 @@ static void watchdog_disable_all_cpus(void) | |||
501 | /* sysctl functions */ | 512 | /* sysctl functions */ |
502 | #ifdef CONFIG_SYSCTL | 513 | #ifdef CONFIG_SYSCTL |
503 | /* | 514 | /* |
504 | * proc handler for /proc/sys/kernel/nmi_watchdog | 515 | * proc handler for /proc/sys/kernel/nmi_watchdog,watchdog_thresh |
505 | */ | 516 | */ |
506 | 517 | ||
507 | int proc_dowatchdog_enabled(struct ctl_table *table, int write, | 518 | int proc_dowatchdog(struct ctl_table *table, int write, |
508 | void __user *buffer, size_t *length, loff_t *ppos) | 519 | void __user *buffer, size_t *lenp, loff_t *ppos) |
509 | { | 520 | { |
510 | proc_dointvec(table, write, buffer, length, ppos); | 521 | int ret; |
511 | 522 | ||
512 | if (write) { | 523 | ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); |
513 | if (watchdog_enabled) | 524 | if (ret || !write) |
514 | watchdog_enable_all_cpus(); | 525 | goto out; |
515 | else | ||
516 | watchdog_disable_all_cpus(); | ||
517 | } | ||
518 | return 0; | ||
519 | } | ||
520 | 526 | ||
521 | int proc_dowatchdog_thresh(struct ctl_table *table, int write, | 527 | if (watchdog_enabled && watchdog_thresh) |
522 | void __user *buffer, | 528 | watchdog_enable_all_cpus(); |
523 | size_t *lenp, loff_t *ppos) | 529 | else |
524 | { | 530 | watchdog_disable_all_cpus(); |
525 | return proc_dointvec_minmax(table, write, buffer, lenp, ppos); | 531 | |
532 | out: | ||
533 | return ret; | ||
526 | } | 534 | } |
527 | #endif /* CONFIG_SYSCTL */ | 535 | #endif /* CONFIG_SYSCTL */ |
528 | 536 | ||
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 2f9a337b182f..b67186228c89 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -474,6 +474,7 @@ static int test__basic_mmap(void) | |||
474 | unsigned int nr_events[nsyscalls], | 474 | unsigned int nr_events[nsyscalls], |
475 | expected_nr_events[nsyscalls], i, j; | 475 | expected_nr_events[nsyscalls], i, j; |
476 | struct perf_evsel *evsels[nsyscalls], *evsel; | 476 | struct perf_evsel *evsels[nsyscalls], *evsel; |
477 | int sample_size = perf_sample_size(attr.sample_type); | ||
477 | 478 | ||
478 | for (i = 0; i < nsyscalls; ++i) { | 479 | for (i = 0; i < nsyscalls; ++i) { |
479 | char name[64]; | 480 | char name[64]; |
@@ -558,7 +559,13 @@ static int test__basic_mmap(void) | |||
558 | goto out_munmap; | 559 | goto out_munmap; |
559 | } | 560 | } |
560 | 561 | ||
561 | perf_event__parse_sample(event, attr.sample_type, false, &sample); | 562 | err = perf_event__parse_sample(event, attr.sample_type, sample_size, |
563 | false, &sample); | ||
564 | if (err) { | ||
565 | pr_err("Can't parse sample, err = %d\n", err); | ||
566 | goto out_munmap; | ||
567 | } | ||
568 | |||
562 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 569 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
563 | if (evsel == NULL) { | 570 | if (evsel == NULL) { |
564 | pr_debug("event with id %" PRIu64 | 571 | pr_debug("event with id %" PRIu64 |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index ebfc7cf5f63b..2d7934e9de38 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -805,9 +805,14 @@ static void perf_session__mmap_read_idx(struct perf_session *self, int idx) | |||
805 | { | 805 | { |
806 | struct perf_sample sample; | 806 | struct perf_sample sample; |
807 | union perf_event *event; | 807 | union perf_event *event; |
808 | int ret; | ||
808 | 809 | ||
809 | while ((event = perf_evlist__mmap_read(top.evlist, idx)) != NULL) { | 810 | while ((event = perf_evlist__mmap_read(top.evlist, idx)) != NULL) { |
810 | perf_session__parse_sample(self, event, &sample); | 811 | ret = perf_session__parse_sample(self, event, &sample); |
812 | if (ret) { | ||
813 | pr_err("Can't parse sample, err = %d\n", ret); | ||
814 | continue; | ||
815 | } | ||
811 | 816 | ||
812 | if (event->header.type == PERF_RECORD_SAMPLE) | 817 | if (event->header.type == PERF_RECORD_SAMPLE) |
813 | perf_event__process_sample(event, &sample, self); | 818 | perf_event__process_sample(event, &sample, self); |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1023f67633a4..252b72a5e59e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -9,21 +9,21 @@ | |||
9 | #include "thread_map.h" | 9 | #include "thread_map.h" |
10 | 10 | ||
11 | static const char *perf_event__names[] = { | 11 | static const char *perf_event__names[] = { |
12 | [0] = "TOTAL", | 12 | [0] = "TOTAL", |
13 | [PERF_RECORD_MMAP] = "MMAP", | 13 | [PERF_RECORD_MMAP] = "MMAP", |
14 | [PERF_RECORD_LOST] = "LOST", | 14 | [PERF_RECORD_LOST] = "LOST", |
15 | [PERF_RECORD_COMM] = "COMM", | 15 | [PERF_RECORD_COMM] = "COMM", |
16 | [PERF_RECORD_EXIT] = "EXIT", | 16 | [PERF_RECORD_EXIT] = "EXIT", |
17 | [PERF_RECORD_THROTTLE] = "THROTTLE", | 17 | [PERF_RECORD_THROTTLE] = "THROTTLE", |
18 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", | 18 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", |
19 | [PERF_RECORD_FORK] = "FORK", | 19 | [PERF_RECORD_FORK] = "FORK", |
20 | [PERF_RECORD_READ] = "READ", | 20 | [PERF_RECORD_READ] = "READ", |
21 | [PERF_RECORD_SAMPLE] = "SAMPLE", | 21 | [PERF_RECORD_SAMPLE] = "SAMPLE", |
22 | [PERF_RECORD_HEADER_ATTR] = "ATTR", | 22 | [PERF_RECORD_HEADER_ATTR] = "ATTR", |
23 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", | 23 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", |
24 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", | 24 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", |
25 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", | 25 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", |
26 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", | 26 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", |
27 | }; | 27 | }; |
28 | 28 | ||
29 | const char *perf_event__name(unsigned int id) | 29 | const char *perf_event__name(unsigned int id) |
@@ -35,6 +35,22 @@ const char *perf_event__name(unsigned int id) | |||
35 | return perf_event__names[id]; | 35 | return perf_event__names[id]; |
36 | } | 36 | } |
37 | 37 | ||
38 | int perf_sample_size(u64 sample_type) | ||
39 | { | ||
40 | u64 mask = sample_type & PERF_SAMPLE_MASK; | ||
41 | int size = 0; | ||
42 | int i; | ||
43 | |||
44 | for (i = 0; i < 64; i++) { | ||
45 | if (mask & (1UL << i)) | ||
46 | size++; | ||
47 | } | ||
48 | |||
49 | size *= sizeof(u64); | ||
50 | |||
51 | return size; | ||
52 | } | ||
53 | |||
38 | static struct perf_sample synth_sample = { | 54 | static struct perf_sample synth_sample = { |
39 | .pid = -1, | 55 | .pid = -1, |
40 | .tid = -1, | 56 | .tid = -1, |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 9c35170fb379..c08332871408 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -56,6 +56,13 @@ struct read_event { | |||
56 | u64 id; | 56 | u64 id; |
57 | }; | 57 | }; |
58 | 58 | ||
59 | |||
60 | #define PERF_SAMPLE_MASK \ | ||
61 | (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ | ||
62 | PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \ | ||
63 | PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | \ | ||
64 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD) | ||
65 | |||
59 | struct sample_event { | 66 | struct sample_event { |
60 | struct perf_event_header header; | 67 | struct perf_event_header header; |
61 | u64 array[]; | 68 | u64 array[]; |
@@ -75,6 +82,8 @@ struct perf_sample { | |||
75 | struct ip_callchain *callchain; | 82 | struct ip_callchain *callchain; |
76 | }; | 83 | }; |
77 | 84 | ||
85 | int perf_sample_size(u64 sample_type); | ||
86 | |||
78 | #define BUILD_ID_SIZE 20 | 87 | #define BUILD_ID_SIZE 20 |
79 | 88 | ||
80 | struct build_id_event { | 89 | struct build_id_event { |
@@ -178,6 +187,7 @@ int perf_event__preprocess_sample(const union perf_event *self, | |||
178 | const char *perf_event__name(unsigned int id); | 187 | const char *perf_event__name(unsigned int id); |
179 | 188 | ||
180 | int perf_event__parse_sample(const union perf_event *event, u64 type, | 189 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
181 | bool sample_id_all, struct perf_sample *sample); | 190 | int sample_size, bool sample_id_all, |
191 | struct perf_sample *sample); | ||
182 | 192 | ||
183 | #endif /* __PERF_RECORD_H */ | 193 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 23eb22b05d27..50aa34879c33 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -459,3 +459,34 @@ int perf_evlist__set_filters(struct perf_evlist *evlist) | |||
459 | 459 | ||
460 | return 0; | 460 | return 0; |
461 | } | 461 | } |
462 | |||
463 | u64 perf_evlist__sample_type(struct perf_evlist *evlist) | ||
464 | { | ||
465 | struct perf_evsel *pos; | ||
466 | u64 type = 0; | ||
467 | |||
468 | list_for_each_entry(pos, &evlist->entries, node) { | ||
469 | if (!type) | ||
470 | type = pos->attr.sample_type; | ||
471 | else if (type != pos->attr.sample_type) | ||
472 | die("non matching sample_type"); | ||
473 | } | ||
474 | |||
475 | return type; | ||
476 | } | ||
477 | |||
478 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) | ||
479 | { | ||
480 | bool value = false, first = true; | ||
481 | struct perf_evsel *pos; | ||
482 | |||
483 | list_for_each_entry(pos, &evlist->entries, node) { | ||
484 | if (first) { | ||
485 | value = pos->attr.sample_id_all; | ||
486 | first = false; | ||
487 | } else if (value != pos->attr.sample_id_all) | ||
488 | die("non matching sample_id_all"); | ||
489 | } | ||
490 | |||
491 | return value; | ||
492 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 7109d7add14e..0a1ef1f051f0 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -66,4 +66,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | |||
66 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 66 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
67 | int perf_evlist__set_filters(struct perf_evlist *evlist); | 67 | int perf_evlist__set_filters(struct perf_evlist *evlist); |
68 | 68 | ||
69 | u64 perf_evlist__sample_type(struct perf_evlist *evlist); | ||
70 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist); | ||
71 | |||
69 | #endif /* __PERF_EVLIST_H */ | 72 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d6fd59beb860..ee0fe0dffa71 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -303,8 +303,20 @@ static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | |||
303 | return 0; | 303 | return 0; |
304 | } | 304 | } |
305 | 305 | ||
306 | static bool sample_overlap(const union perf_event *event, | ||
307 | const void *offset, u64 size) | ||
308 | { | ||
309 | const void *base = event; | ||
310 | |||
311 | if (offset + size > base + event->header.size) | ||
312 | return true; | ||
313 | |||
314 | return false; | ||
315 | } | ||
316 | |||
306 | int perf_event__parse_sample(const union perf_event *event, u64 type, | 317 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
307 | bool sample_id_all, struct perf_sample *data) | 318 | int sample_size, bool sample_id_all, |
319 | struct perf_sample *data) | ||
308 | { | 320 | { |
309 | const u64 *array; | 321 | const u64 *array; |
310 | 322 | ||
@@ -319,6 +331,9 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
319 | 331 | ||
320 | array = event->sample.array; | 332 | array = event->sample.array; |
321 | 333 | ||
334 | if (sample_size + sizeof(event->header) > event->header.size) | ||
335 | return -EFAULT; | ||
336 | |||
322 | if (type & PERF_SAMPLE_IP) { | 337 | if (type & PERF_SAMPLE_IP) { |
323 | data->ip = event->ip.ip; | 338 | data->ip = event->ip.ip; |
324 | array++; | 339 | array++; |
@@ -369,14 +384,29 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
369 | } | 384 | } |
370 | 385 | ||
371 | if (type & PERF_SAMPLE_CALLCHAIN) { | 386 | if (type & PERF_SAMPLE_CALLCHAIN) { |
387 | if (sample_overlap(event, array, sizeof(data->callchain->nr))) | ||
388 | return -EFAULT; | ||
389 | |||
372 | data->callchain = (struct ip_callchain *)array; | 390 | data->callchain = (struct ip_callchain *)array; |
391 | |||
392 | if (sample_overlap(event, array, data->callchain->nr)) | ||
393 | return -EFAULT; | ||
394 | |||
373 | array += 1 + data->callchain->nr; | 395 | array += 1 + data->callchain->nr; |
374 | } | 396 | } |
375 | 397 | ||
376 | if (type & PERF_SAMPLE_RAW) { | 398 | if (type & PERF_SAMPLE_RAW) { |
377 | u32 *p = (u32 *)array; | 399 | u32 *p = (u32 *)array; |
400 | |||
401 | if (sample_overlap(event, array, sizeof(u32))) | ||
402 | return -EFAULT; | ||
403 | |||
378 | data->raw_size = *p; | 404 | data->raw_size = *p; |
379 | p++; | 405 | p++; |
406 | |||
407 | if (sample_overlap(event, p, data->raw_size)) | ||
408 | return -EFAULT; | ||
409 | |||
380 | data->raw_data = p; | 410 | data->raw_data = p; |
381 | } | 411 | } |
382 | 412 | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 93862a8027ea..0717bebc7649 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -934,37 +934,6 @@ out_delete_evlist: | |||
934 | return -ENOMEM; | 934 | return -ENOMEM; |
935 | } | 935 | } |
936 | 936 | ||
937 | u64 perf_evlist__sample_type(struct perf_evlist *evlist) | ||
938 | { | ||
939 | struct perf_evsel *pos; | ||
940 | u64 type = 0; | ||
941 | |||
942 | list_for_each_entry(pos, &evlist->entries, node) { | ||
943 | if (!type) | ||
944 | type = pos->attr.sample_type; | ||
945 | else if (type != pos->attr.sample_type) | ||
946 | die("non matching sample_type"); | ||
947 | } | ||
948 | |||
949 | return type; | ||
950 | } | ||
951 | |||
952 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) | ||
953 | { | ||
954 | bool value = false, first = true; | ||
955 | struct perf_evsel *pos; | ||
956 | |||
957 | list_for_each_entry(pos, &evlist->entries, node) { | ||
958 | if (first) { | ||
959 | value = pos->attr.sample_id_all; | ||
960 | first = false; | ||
961 | } else if (value != pos->attr.sample_id_all) | ||
962 | die("non matching sample_id_all"); | ||
963 | } | ||
964 | |||
965 | return value; | ||
966 | } | ||
967 | |||
968 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | 937 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, |
969 | perf_event__handler_t process, | 938 | perf_event__handler_t process, |
970 | struct perf_session *session) | 939 | struct perf_session *session) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 456661d7f10e..1886256768a1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -64,8 +64,6 @@ int perf_header__write_pipe(int fd); | |||
64 | int perf_header__push_event(u64 id, const char *name); | 64 | int perf_header__push_event(u64 id, const char *name); |
65 | char *perf_header__find_event(u64 id); | 65 | char *perf_header__find_event(u64 id); |
66 | 66 | ||
67 | u64 perf_evlist__sample_type(struct perf_evlist *evlist); | ||
68 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist); | ||
69 | void perf_header__set_feat(struct perf_header *header, int feat); | 67 | void perf_header__set_feat(struct perf_header *header, int feat); |
70 | void perf_header__clear_feat(struct perf_header *header, int feat); | 68 | void perf_header__clear_feat(struct perf_header *header, int feat); |
71 | bool perf_header__has_feat(const struct perf_header *header, int feat); | 69 | bool perf_header__has_feat(const struct perf_header *header, int feat); |
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index 99358d61e9a5..1d928a0ce997 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h | |||
@@ -1,4 +1,6 @@ | |||
1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
2 | #include <linux/prefetch.h> | ||
3 | |||
2 | #include "../../../../include/linux/list.h" | 4 | #include "../../../../include/linux/list.h" |
3 | 5 | ||
4 | #ifndef PERF_LIST_H | 6 | #ifndef PERF_LIST_H |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index b5c7d818001c..69436b3200a4 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -675,6 +675,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
675 | union perf_event *event; | 675 | union perf_event *event; |
676 | int sample_id_all = 1, cpu; | 676 | int sample_id_all = 1, cpu; |
677 | static char *kwlist[] = {"sample_id_all", NULL, NULL}; | 677 | static char *kwlist[] = {"sample_id_all", NULL, NULL}; |
678 | int err; | ||
678 | 679 | ||
679 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, | 680 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, |
680 | &cpu, &sample_id_all)) | 681 | &cpu, &sample_id_all)) |
@@ -690,11 +691,17 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
690 | return PyErr_NoMemory(); | 691 | return PyErr_NoMemory(); |
691 | 692 | ||
692 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 693 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
693 | perf_event__parse_sample(event, first->attr.sample_type, sample_id_all, | 694 | err = perf_event__parse_sample(event, first->attr.sample_type, |
694 | &pevent->sample); | 695 | perf_sample_size(first->attr.sample_type), |
696 | sample_id_all, &pevent->sample); | ||
697 | if (err) { | ||
698 | pr_err("Can't parse sample, err = %d\n", err); | ||
699 | goto end; | ||
700 | } | ||
701 | |||
695 | return pyevent; | 702 | return pyevent; |
696 | } | 703 | } |
697 | 704 | end: | |
698 | Py_INCREF(Py_None); | 705 | Py_INCREF(Py_None); |
699 | return Py_None; | 706 | return Py_None; |
700 | } | 707 | } |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fff66741f18d..64500fc78799 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -97,6 +97,7 @@ out: | |||
97 | void perf_session__update_sample_type(struct perf_session *self) | 97 | void perf_session__update_sample_type(struct perf_session *self) |
98 | { | 98 | { |
99 | self->sample_type = perf_evlist__sample_type(self->evlist); | 99 | self->sample_type = perf_evlist__sample_type(self->evlist); |
100 | self->sample_size = perf_sample_size(self->sample_type); | ||
100 | self->sample_id_all = perf_evlist__sample_id_all(self->evlist); | 101 | self->sample_id_all = perf_evlist__sample_id_all(self->evlist); |
101 | perf_session__id_header_size(self); | 102 | perf_session__id_header_size(self); |
102 | } | 103 | } |
@@ -479,6 +480,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
479 | struct perf_sample sample; | 480 | struct perf_sample sample; |
480 | u64 limit = os->next_flush; | 481 | u64 limit = os->next_flush; |
481 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 482 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; |
483 | int ret; | ||
482 | 484 | ||
483 | if (!ops->ordered_samples || !limit) | 485 | if (!ops->ordered_samples || !limit) |
484 | return; | 486 | return; |
@@ -487,9 +489,12 @@ static void flush_sample_queue(struct perf_session *s, | |||
487 | if (iter->timestamp > limit) | 489 | if (iter->timestamp > limit) |
488 | break; | 490 | break; |
489 | 491 | ||
490 | perf_session__parse_sample(s, iter->event, &sample); | 492 | ret = perf_session__parse_sample(s, iter->event, &sample); |
491 | perf_session_deliver_event(s, iter->event, &sample, ops, | 493 | if (ret) |
492 | iter->file_offset); | 494 | pr_err("Can't parse sample, err = %d\n", ret); |
495 | else | ||
496 | perf_session_deliver_event(s, iter->event, &sample, ops, | ||
497 | iter->file_offset); | ||
493 | 498 | ||
494 | os->last_flush = iter->timestamp; | 499 | os->last_flush = iter->timestamp; |
495 | list_del(&iter->list); | 500 | list_del(&iter->list); |
@@ -805,7 +810,9 @@ static int perf_session__process_event(struct perf_session *session, | |||
805 | /* | 810 | /* |
806 | * For all kernel events we get the sample data | 811 | * For all kernel events we get the sample data |
807 | */ | 812 | */ |
808 | perf_session__parse_sample(session, event, &sample); | 813 | ret = perf_session__parse_sample(session, event, &sample); |
814 | if (ret) | ||
815 | return ret; | ||
809 | 816 | ||
810 | /* Preprocess sample records - precheck callchains */ | 817 | /* Preprocess sample records - precheck callchains */ |
811 | if (perf_session__preprocess_sample(session, event, &sample)) | 818 | if (perf_session__preprocess_sample(session, event, &sample)) |
@@ -953,6 +960,30 @@ out_err: | |||
953 | return err; | 960 | return err; |
954 | } | 961 | } |
955 | 962 | ||
963 | static union perf_event * | ||
964 | fetch_mmaped_event(struct perf_session *session, | ||
965 | u64 head, size_t mmap_size, char *buf) | ||
966 | { | ||
967 | union perf_event *event; | ||
968 | |||
969 | /* | ||
970 | * Ensure we have enough space remaining to read | ||
971 | * the size of the event in the headers. | ||
972 | */ | ||
973 | if (head + sizeof(event->header) > mmap_size) | ||
974 | return NULL; | ||
975 | |||
976 | event = (union perf_event *)(buf + head); | ||
977 | |||
978 | if (session->header.needs_swap) | ||
979 | perf_event_header__bswap(&event->header); | ||
980 | |||
981 | if (head + event->header.size > mmap_size) | ||
982 | return NULL; | ||
983 | |||
984 | return event; | ||
985 | } | ||
986 | |||
956 | int __perf_session__process_events(struct perf_session *session, | 987 | int __perf_session__process_events(struct perf_session *session, |
957 | u64 data_offset, u64 data_size, | 988 | u64 data_offset, u64 data_size, |
958 | u64 file_size, struct perf_event_ops *ops) | 989 | u64 file_size, struct perf_event_ops *ops) |
@@ -1007,15 +1038,8 @@ remap: | |||
1007 | file_pos = file_offset + head; | 1038 | file_pos = file_offset + head; |
1008 | 1039 | ||
1009 | more: | 1040 | more: |
1010 | event = (union perf_event *)(buf + head); | 1041 | event = fetch_mmaped_event(session, head, mmap_size, buf); |
1011 | 1042 | if (!event) { | |
1012 | if (session->header.needs_swap) | ||
1013 | perf_event_header__bswap(&event->header); | ||
1014 | size = event->header.size; | ||
1015 | if (size == 0) | ||
1016 | size = 8; | ||
1017 | |||
1018 | if (head + event->header.size > mmap_size) { | ||
1019 | if (mmaps[map_idx]) { | 1043 | if (mmaps[map_idx]) { |
1020 | munmap(mmaps[map_idx], mmap_size); | 1044 | munmap(mmaps[map_idx], mmap_size); |
1021 | mmaps[map_idx] = NULL; | 1045 | mmaps[map_idx] = NULL; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 8daaa2d15396..66d4e1490879 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -43,6 +43,7 @@ struct perf_session { | |||
43 | */ | 43 | */ |
44 | struct hists hists; | 44 | struct hists hists; |
45 | u64 sample_type; | 45 | u64 sample_type; |
46 | int sample_size; | ||
46 | int fd; | 47 | int fd; |
47 | bool fd_pipe; | 48 | bool fd_pipe; |
48 | bool repipe; | 49 | bool repipe; |
@@ -159,6 +160,7 @@ static inline int perf_session__parse_sample(struct perf_session *session, | |||
159 | struct perf_sample *sample) | 160 | struct perf_sample *sample) |
160 | { | 161 | { |
161 | return perf_event__parse_sample(event, session->sample_type, | 162 | return perf_event__parse_sample(event, session->sample_type, |
163 | session->sample_size, | ||
162 | session->sample_id_all, sample); | 164 | session->sample_id_all, sample); |
163 | } | 165 | } |
164 | 166 | ||