diff options
50 files changed, 799 insertions, 74 deletions
diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt index 41ef9d8efe95..5ea85059db3b 100644 --- a/Documentation/trace/kprobetrace.txt +++ b/Documentation/trace/kprobetrace.txt | |||
| @@ -8,8 +8,9 @@ Overview | |||
| 8 | -------- | 8 | -------- |
| 9 | These events are similar to tracepoint based events. Instead of Tracepoint, | 9 | These events are similar to tracepoint based events. Instead of Tracepoint, |
| 10 | this is based on kprobes (kprobe and kretprobe). So it can probe wherever | 10 | this is based on kprobes (kprobe and kretprobe). So it can probe wherever |
| 11 | kprobes can probe (this means, all functions body except for __kprobes | 11 | kprobes can probe (this means, all functions except those with |
| 12 | functions). Unlike the Tracepoint based event, this can be added and removed | 12 | __kprobes/nokprobe_inline annotation and those marked NOKPROBE_SYMBOL). |
| 13 | Unlike the Tracepoint based event, this can be added and removed | ||
| 13 | dynamically, on the fly. | 14 | dynamically, on the fly. |
| 14 | 15 | ||
| 15 | To enable this feature, build your kernel with CONFIG_KPROBE_EVENTS=y. | 16 | To enable this feature, build your kernel with CONFIG_KPROBE_EVENTS=y. |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 000fdb211c7d..f19a82362851 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -1112,6 +1112,7 @@ extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks | |||
| 1112 | 1112 | ||
| 1113 | extern void perf_event_exec(void); | 1113 | extern void perf_event_exec(void); |
| 1114 | extern void perf_event_comm(struct task_struct *tsk, bool exec); | 1114 | extern void perf_event_comm(struct task_struct *tsk, bool exec); |
| 1115 | extern void perf_event_namespaces(struct task_struct *tsk); | ||
| 1115 | extern void perf_event_fork(struct task_struct *tsk); | 1116 | extern void perf_event_fork(struct task_struct *tsk); |
| 1116 | 1117 | ||
| 1117 | /* Callchains */ | 1118 | /* Callchains */ |
| @@ -1315,6 +1316,7 @@ static inline int perf_unregister_guest_info_callbacks | |||
| 1315 | static inline void perf_event_mmap(struct vm_area_struct *vma) { } | 1316 | static inline void perf_event_mmap(struct vm_area_struct *vma) { } |
| 1316 | static inline void perf_event_exec(void) { } | 1317 | static inline void perf_event_exec(void) { } |
| 1317 | static inline void perf_event_comm(struct task_struct *tsk, bool exec) { } | 1318 | static inline void perf_event_comm(struct task_struct *tsk, bool exec) { } |
| 1319 | static inline void perf_event_namespaces(struct task_struct *tsk) { } | ||
| 1318 | static inline void perf_event_fork(struct task_struct *tsk) { } | 1320 | static inline void perf_event_fork(struct task_struct *tsk) { } |
| 1319 | static inline void perf_event_init(void) { } | 1321 | static inline void perf_event_init(void) { } |
| 1320 | static inline int perf_swevent_get_recursion_context(void) { return -1; } | 1322 | static inline int perf_swevent_get_recursion_context(void) { return -1; } |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index c66a485a24ac..bec0aad0e15c 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
| @@ -344,7 +344,8 @@ struct perf_event_attr { | |||
| 344 | use_clockid : 1, /* use @clockid for time fields */ | 344 | use_clockid : 1, /* use @clockid for time fields */ |
| 345 | context_switch : 1, /* context switch data */ | 345 | context_switch : 1, /* context switch data */ |
| 346 | write_backward : 1, /* Write ring buffer from end to beginning */ | 346 | write_backward : 1, /* Write ring buffer from end to beginning */ |
| 347 | __reserved_1 : 36; | 347 | namespaces : 1, /* include namespaces data */ |
| 348 | __reserved_1 : 35; | ||
| 348 | 349 | ||
| 349 | union { | 350 | union { |
| 350 | __u32 wakeup_events; /* wakeup every n events */ | 351 | __u32 wakeup_events; /* wakeup every n events */ |
| @@ -610,6 +611,23 @@ struct perf_event_header { | |||
| 610 | __u16 size; | 611 | __u16 size; |
| 611 | }; | 612 | }; |
| 612 | 613 | ||
| 614 | struct perf_ns_link_info { | ||
| 615 | __u64 dev; | ||
| 616 | __u64 ino; | ||
| 617 | }; | ||
| 618 | |||
| 619 | enum { | ||
| 620 | NET_NS_INDEX = 0, | ||
| 621 | UTS_NS_INDEX = 1, | ||
| 622 | IPC_NS_INDEX = 2, | ||
| 623 | PID_NS_INDEX = 3, | ||
| 624 | USER_NS_INDEX = 4, | ||
| 625 | MNT_NS_INDEX = 5, | ||
| 626 | CGROUP_NS_INDEX = 6, | ||
| 627 | |||
| 628 | NR_NAMESPACES, /* number of available namespaces */ | ||
| 629 | }; | ||
| 630 | |||
| 613 | enum perf_event_type { | 631 | enum perf_event_type { |
| 614 | 632 | ||
| 615 | /* | 633 | /* |
| @@ -862,6 +880,18 @@ enum perf_event_type { | |||
| 862 | */ | 880 | */ |
| 863 | PERF_RECORD_SWITCH_CPU_WIDE = 15, | 881 | PERF_RECORD_SWITCH_CPU_WIDE = 15, |
| 864 | 882 | ||
| 883 | /* | ||
| 884 | * struct { | ||
| 885 | * struct perf_event_header header; | ||
| 886 | * u32 pid; | ||
| 887 | * u32 tid; | ||
| 888 | * u64 nr_namespaces; | ||
| 889 | * { u64 dev, inode; } [nr_namespaces]; | ||
| 890 | * struct sample_id sample_id; | ||
| 891 | * }; | ||
| 892 | */ | ||
| 893 | PERF_RECORD_NAMESPACES = 16, | ||
| 894 | |||
| 865 | PERF_RECORD_MAX, /* non-ABI */ | 895 | PERF_RECORD_MAX, /* non-ABI */ |
| 866 | }; | 896 | }; |
| 867 | 897 | ||
diff --git a/kernel/events/core.c b/kernel/events/core.c index 6f41548f2e32..16c877a121c8 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
| @@ -48,6 +48,8 @@ | |||
| 48 | #include <linux/parser.h> | 48 | #include <linux/parser.h> |
| 49 | #include <linux/sched/clock.h> | 49 | #include <linux/sched/clock.h> |
| 50 | #include <linux/sched/mm.h> | 50 | #include <linux/sched/mm.h> |
| 51 | #include <linux/proc_ns.h> | ||
| 52 | #include <linux/mount.h> | ||
| 51 | 53 | ||
| 52 | #include "internal.h" | 54 | #include "internal.h" |
| 53 | 55 | ||
| @@ -379,6 +381,7 @@ static DEFINE_PER_CPU(struct pmu_event_list, pmu_sb_events); | |||
| 379 | 381 | ||
| 380 | static atomic_t nr_mmap_events __read_mostly; | 382 | static atomic_t nr_mmap_events __read_mostly; |
| 381 | static atomic_t nr_comm_events __read_mostly; | 383 | static atomic_t nr_comm_events __read_mostly; |
| 384 | static atomic_t nr_namespaces_events __read_mostly; | ||
| 382 | static atomic_t nr_task_events __read_mostly; | 385 | static atomic_t nr_task_events __read_mostly; |
| 383 | static atomic_t nr_freq_events __read_mostly; | 386 | static atomic_t nr_freq_events __read_mostly; |
| 384 | static atomic_t nr_switch_events __read_mostly; | 387 | static atomic_t nr_switch_events __read_mostly; |
| @@ -3991,6 +3994,8 @@ static void unaccount_event(struct perf_event *event) | |||
| 3991 | atomic_dec(&nr_mmap_events); | 3994 | atomic_dec(&nr_mmap_events); |
| 3992 | if (event->attr.comm) | 3995 | if (event->attr.comm) |
| 3993 | atomic_dec(&nr_comm_events); | 3996 | atomic_dec(&nr_comm_events); |
| 3997 | if (event->attr.namespaces) | ||
| 3998 | atomic_dec(&nr_namespaces_events); | ||
| 3994 | if (event->attr.task) | 3999 | if (event->attr.task) |
| 3995 | atomic_dec(&nr_task_events); | 4000 | atomic_dec(&nr_task_events); |
| 3996 | if (event->attr.freq) | 4001 | if (event->attr.freq) |
| @@ -6491,6 +6496,7 @@ static void perf_event_task(struct task_struct *task, | |||
| 6491 | void perf_event_fork(struct task_struct *task) | 6496 | void perf_event_fork(struct task_struct *task) |
| 6492 | { | 6497 | { |
| 6493 | perf_event_task(task, NULL, 1); | 6498 | perf_event_task(task, NULL, 1); |
| 6499 | perf_event_namespaces(task); | ||
| 6494 | } | 6500 | } |
| 6495 | 6501 | ||
| 6496 | /* | 6502 | /* |
| @@ -6593,6 +6599,132 @@ void perf_event_comm(struct task_struct *task, bool exec) | |||
| 6593 | } | 6599 | } |
| 6594 | 6600 | ||
| 6595 | /* | 6601 | /* |
| 6602 | * namespaces tracking | ||
| 6603 | */ | ||
| 6604 | |||
| 6605 | struct perf_namespaces_event { | ||
| 6606 | struct task_struct *task; | ||
| 6607 | |||
| 6608 | struct { | ||
| 6609 | struct perf_event_header header; | ||
| 6610 | |||
| 6611 | u32 pid; | ||
| 6612 | u32 tid; | ||
| 6613 | u64 nr_namespaces; | ||
| 6614 | struct perf_ns_link_info link_info[NR_NAMESPACES]; | ||
| 6615 | } event_id; | ||
| 6616 | }; | ||
| 6617 | |||
| 6618 | static int perf_event_namespaces_match(struct perf_event *event) | ||
| 6619 | { | ||
| 6620 | return event->attr.namespaces; | ||
| 6621 | } | ||
| 6622 | |||
| 6623 | static void perf_event_namespaces_output(struct perf_event *event, | ||
| 6624 | void *data) | ||
| 6625 | { | ||
| 6626 | struct perf_namespaces_event *namespaces_event = data; | ||
| 6627 | struct perf_output_handle handle; | ||
| 6628 | struct perf_sample_data sample; | ||
| 6629 | int ret; | ||
| 6630 | |||
| 6631 | if (!perf_event_namespaces_match(event)) | ||
| 6632 | return; | ||
| 6633 | |||
| 6634 | perf_event_header__init_id(&namespaces_event->event_id.header, | ||
| 6635 | &sample, event); | ||
| 6636 | ret = perf_output_begin(&handle, event, | ||
| 6637 | namespaces_event->event_id.header.size); | ||
| 6638 | if (ret) | ||
| 6639 | return; | ||
| 6640 | |||
| 6641 | namespaces_event->event_id.pid = perf_event_pid(event, | ||
| 6642 | namespaces_event->task); | ||
| 6643 | namespaces_event->event_id.tid = perf_event_tid(event, | ||
| 6644 | namespaces_event->task); | ||
| 6645 | |||
| 6646 | perf_output_put(&handle, namespaces_event->event_id); | ||
| 6647 | |||
| 6648 | perf_event__output_id_sample(event, &handle, &sample); | ||
| 6649 | |||
| 6650 | perf_output_end(&handle); | ||
| 6651 | } | ||
| 6652 | |||
| 6653 | static void perf_fill_ns_link_info(struct perf_ns_link_info *ns_link_info, | ||
| 6654 | struct task_struct *task, | ||
| 6655 | const struct proc_ns_operations *ns_ops) | ||
| 6656 | { | ||
| 6657 | struct path ns_path; | ||
| 6658 | struct inode *ns_inode; | ||
| 6659 | void *error; | ||
| 6660 | |||
| 6661 | error = ns_get_path(&ns_path, task, ns_ops); | ||
| 6662 | if (!error) { | ||
| 6663 | ns_inode = ns_path.dentry->d_inode; | ||
| 6664 | ns_link_info->dev = new_encode_dev(ns_inode->i_sb->s_dev); | ||
| 6665 | ns_link_info->ino = ns_inode->i_ino; | ||
| 6666 | } | ||
| 6667 | } | ||
| 6668 | |||
| 6669 | void perf_event_namespaces(struct task_struct *task) | ||
| 6670 | { | ||
| 6671 | struct perf_namespaces_event namespaces_event; | ||
| 6672 | struct perf_ns_link_info *ns_link_info; | ||
| 6673 | |||
| 6674 | if (!atomic_read(&nr_namespaces_events)) | ||
| 6675 | return; | ||
| 6676 | |||
| 6677 | namespaces_event = (struct perf_namespaces_event){ | ||
| 6678 | .task = task, | ||
| 6679 | .event_id = { | ||
| 6680 | .header = { | ||
| 6681 | .type = PERF_RECORD_NAMESPACES, | ||
| 6682 | .misc = 0, | ||
| 6683 | .size = sizeof(namespaces_event.event_id), | ||
| 6684 | }, | ||
| 6685 | /* .pid */ | ||
| 6686 | /* .tid */ | ||
| 6687 | .nr_namespaces = NR_NAMESPACES, | ||
| 6688 | /* .link_info[NR_NAMESPACES] */ | ||
| 6689 | }, | ||
| 6690 | }; | ||
| 6691 | |||
| 6692 | ns_link_info = namespaces_event.event_id.link_info; | ||
| 6693 | |||
| 6694 | perf_fill_ns_link_info(&ns_link_info[MNT_NS_INDEX], | ||
| 6695 | task, &mntns_operations); | ||
| 6696 | |||
| 6697 | #ifdef CONFIG_USER_NS | ||
| 6698 | perf_fill_ns_link_info(&ns_link_info[USER_NS_INDEX], | ||
| 6699 | task, &userns_operations); | ||
| 6700 | #endif | ||
| 6701 | #ifdef CONFIG_NET_NS | ||
| 6702 | perf_fill_ns_link_info(&ns_link_info[NET_NS_INDEX], | ||
| 6703 | task, &netns_operations); | ||
| 6704 | #endif | ||
| 6705 | #ifdef CONFIG_UTS_NS | ||
| 6706 | perf_fill_ns_link_info(&ns_link_info[UTS_NS_INDEX], | ||
| 6707 | task, &utsns_operations); | ||
| 6708 | #endif | ||
| 6709 | #ifdef CONFIG_IPC_NS | ||
| 6710 | perf_fill_ns_link_info(&ns_link_info[IPC_NS_INDEX], | ||
| 6711 | task, &ipcns_operations); | ||
| 6712 | #endif | ||
| 6713 | #ifdef CONFIG_PID_NS | ||
| 6714 | perf_fill_ns_link_info(&ns_link_info[PID_NS_INDEX], | ||
| 6715 | task, &pidns_operations); | ||
| 6716 | #endif | ||
| 6717 | #ifdef CONFIG_CGROUPS | ||
| 6718 | perf_fill_ns_link_info(&ns_link_info[CGROUP_NS_INDEX], | ||
| 6719 | task, &cgroupns_operations); | ||
| 6720 | #endif | ||
| 6721 | |||
| 6722 | perf_iterate_sb(perf_event_namespaces_output, | ||
| 6723 | &namespaces_event, | ||
| 6724 | NULL); | ||
| 6725 | } | ||
| 6726 | |||
| 6727 | /* | ||
| 6596 | * mmap tracking | 6728 | * mmap tracking |
| 6597 | */ | 6729 | */ |
| 6598 | 6730 | ||
| @@ -9146,6 +9278,8 @@ static void account_event(struct perf_event *event) | |||
| 9146 | atomic_inc(&nr_mmap_events); | 9278 | atomic_inc(&nr_mmap_events); |
| 9147 | if (event->attr.comm) | 9279 | if (event->attr.comm) |
| 9148 | atomic_inc(&nr_comm_events); | 9280 | atomic_inc(&nr_comm_events); |
| 9281 | if (event->attr.namespaces) | ||
| 9282 | atomic_inc(&nr_namespaces_events); | ||
| 9149 | if (event->attr.task) | 9283 | if (event->attr.task) |
| 9150 | atomic_inc(&nr_task_events); | 9284 | atomic_inc(&nr_task_events); |
| 9151 | if (event->attr.freq) | 9285 | if (event->attr.freq) |
| @@ -9691,6 +9825,11 @@ SYSCALL_DEFINE5(perf_event_open, | |||
| 9691 | return -EACCES; | 9825 | return -EACCES; |
| 9692 | } | 9826 | } |
| 9693 | 9827 | ||
| 9828 | if (attr.namespaces) { | ||
| 9829 | if (!capable(CAP_SYS_ADMIN)) | ||
| 9830 | return -EACCES; | ||
| 9831 | } | ||
| 9832 | |||
| 9694 | if (attr.freq) { | 9833 | if (attr.freq) { |
| 9695 | if (attr.sample_freq > sysctl_perf_event_sample_rate) | 9834 | if (attr.sample_freq > sysctl_perf_event_sample_rate) |
| 9696 | return -EINVAL; | 9835 | return -EINVAL; |
diff --git a/kernel/fork.c b/kernel/fork.c index 6c463c80e93d..afa2947286cd 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -2352,6 +2352,8 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) | |||
| 2352 | } | 2352 | } |
| 2353 | } | 2353 | } |
| 2354 | 2354 | ||
| 2355 | perf_event_namespaces(current); | ||
| 2356 | |||
| 2355 | bad_unshare_cleanup_cred: | 2357 | bad_unshare_cleanup_cred: |
| 2356 | if (new_cred) | 2358 | if (new_cred) |
| 2357 | put_cred(new_cred); | 2359 | put_cred(new_cred); |
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 448759d4a263..4780ec236035 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
| @@ -1740,11 +1740,12 @@ void unregister_kprobes(struct kprobe **kps, int num) | |||
| 1740 | } | 1740 | } |
| 1741 | EXPORT_SYMBOL_GPL(unregister_kprobes); | 1741 | EXPORT_SYMBOL_GPL(unregister_kprobes); |
| 1742 | 1742 | ||
| 1743 | int __weak __kprobes kprobe_exceptions_notify(struct notifier_block *self, | 1743 | int __weak kprobe_exceptions_notify(struct notifier_block *self, |
| 1744 | unsigned long val, void *data) | 1744 | unsigned long val, void *data) |
| 1745 | { | 1745 | { |
| 1746 | return NOTIFY_DONE; | 1746 | return NOTIFY_DONE; |
| 1747 | } | 1747 | } |
| 1748 | NOKPROBE_SYMBOL(kprobe_exceptions_notify); | ||
| 1748 | 1749 | ||
| 1749 | static struct notifier_block kprobe_exceptions_nb = { | 1750 | static struct notifier_block kprobe_exceptions_nb = { |
| 1750 | .notifier_call = kprobe_exceptions_notify, | 1751 | .notifier_call = kprobe_exceptions_notify, |
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 782102e59eed..f6c5d330059a 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #include <linux/file.h> | 26 | #include <linux/file.h> |
| 27 | #include <linux/syscalls.h> | 27 | #include <linux/syscalls.h> |
| 28 | #include <linux/cgroup.h> | 28 | #include <linux/cgroup.h> |
| 29 | #include <linux/perf_event.h> | ||
| 29 | 30 | ||
| 30 | static struct kmem_cache *nsproxy_cachep; | 31 | static struct kmem_cache *nsproxy_cachep; |
| 31 | 32 | ||
| @@ -262,6 +263,8 @@ SYSCALL_DEFINE2(setns, int, fd, int, nstype) | |||
| 262 | goto out; | 263 | goto out; |
| 263 | } | 264 | } |
| 264 | switch_task_namespaces(tsk, new_nsproxy); | 265 | switch_task_namespaces(tsk, new_nsproxy); |
| 266 | |||
| 267 | perf_event_namespaces(tsk); | ||
| 265 | out: | 268 | out: |
| 266 | fput(file); | 269 | fput(file); |
| 267 | return err; | 270 | return err; |
diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h index c66a485a24ac..bec0aad0e15c 100644 --- a/tools/include/uapi/linux/perf_event.h +++ b/tools/include/uapi/linux/perf_event.h | |||
| @@ -344,7 +344,8 @@ struct perf_event_attr { | |||
| 344 | use_clockid : 1, /* use @clockid for time fields */ | 344 | use_clockid : 1, /* use @clockid for time fields */ |
| 345 | context_switch : 1, /* context switch data */ | 345 | context_switch : 1, /* context switch data */ |
| 346 | write_backward : 1, /* Write ring buffer from end to beginning */ | 346 | write_backward : 1, /* Write ring buffer from end to beginning */ |
| 347 | __reserved_1 : 36; | 347 | namespaces : 1, /* include namespaces data */ |
| 348 | __reserved_1 : 35; | ||
| 348 | 349 | ||
| 349 | union { | 350 | union { |
| 350 | __u32 wakeup_events; /* wakeup every n events */ | 351 | __u32 wakeup_events; /* wakeup every n events */ |
| @@ -610,6 +611,23 @@ struct perf_event_header { | |||
| 610 | __u16 size; | 611 | __u16 size; |
| 611 | }; | 612 | }; |
| 612 | 613 | ||
| 614 | struct perf_ns_link_info { | ||
| 615 | __u64 dev; | ||
| 616 | __u64 ino; | ||
| 617 | }; | ||
| 618 | |||
| 619 | enum { | ||
| 620 | NET_NS_INDEX = 0, | ||
| 621 | UTS_NS_INDEX = 1, | ||
| 622 | IPC_NS_INDEX = 2, | ||
| 623 | PID_NS_INDEX = 3, | ||
| 624 | USER_NS_INDEX = 4, | ||
| 625 | MNT_NS_INDEX = 5, | ||
| 626 | CGROUP_NS_INDEX = 6, | ||
| 627 | |||
| 628 | NR_NAMESPACES, /* number of available namespaces */ | ||
| 629 | }; | ||
| 630 | |||
| 613 | enum perf_event_type { | 631 | enum perf_event_type { |
| 614 | 632 | ||
| 615 | /* | 633 | /* |
| @@ -862,6 +880,18 @@ enum perf_event_type { | |||
| 862 | */ | 880 | */ |
| 863 | PERF_RECORD_SWITCH_CPU_WIDE = 15, | 881 | PERF_RECORD_SWITCH_CPU_WIDE = 15, |
| 864 | 882 | ||
| 883 | /* | ||
| 884 | * struct { | ||
| 885 | * struct perf_event_header header; | ||
| 886 | * u32 pid; | ||
| 887 | * u32 tid; | ||
| 888 | * u64 nr_namespaces; | ||
| 889 | * { u64 dev, inode; } [nr_namespaces]; | ||
| 890 | * struct sample_id sample_id; | ||
| 891 | * }; | ||
| 892 | */ | ||
| 893 | PERF_RECORD_NAMESPACES = 16, | ||
| 894 | |||
| 865 | PERF_RECORD_MAX, /* non-ABI */ | 895 | PERF_RECORD_MAX, /* non-ABI */ |
| 866 | }; | 896 | }; |
| 867 | 897 | ||
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 3db3db9278be..643cc4ba6872 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore | |||
| @@ -31,3 +31,5 @@ config.mak.autogen | |||
| 31 | .config-detected | 31 | .config-detected |
| 32 | util/intel-pt-decoder/inat-tables.c | 32 | util/intel-pt-decoder/inat-tables.c |
| 33 | arch/*/include/generated/ | 33 | arch/*/include/generated/ |
| 34 | pmu-events/pmu-events.c | ||
| 35 | pmu-events/jevents | ||
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index b16003ec14a7..ea3789d05e5e 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
| @@ -347,6 +347,9 @@ Enable weightened sampling. An additional weight is recorded per sample and can | |||
| 347 | displayed with the weight and local_weight sort keys. This currently works for TSX | 347 | displayed with the weight and local_weight sort keys. This currently works for TSX |
| 348 | abort events and some memory events in precise mode on modern Intel CPUs. | 348 | abort events and some memory events in precise mode on modern Intel CPUs. |
| 349 | 349 | ||
| 350 | --namespaces:: | ||
| 351 | Record events of type PERF_RECORD_NAMESPACES. | ||
| 352 | |||
| 350 | --transaction:: | 353 | --transaction:: |
| 351 | Record transaction flags for transaction related events. | 354 | Record transaction flags for transaction related events. |
| 352 | 355 | ||
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 33f91906f5dc..e9a61f5485eb 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -72,7 +72,8 @@ OPTIONS | |||
| 72 | --sort=:: | 72 | --sort=:: |
| 73 | Sort histogram entries by given key(s) - multiple keys can be specified | 73 | Sort histogram entries by given key(s) - multiple keys can be specified |
| 74 | in CSV format. Following sort keys are available: | 74 | in CSV format. Following sort keys are available: |
| 75 | pid, comm, dso, symbol, parent, cpu, socket, srcline, weight, local_weight. | 75 | pid, comm, dso, symbol, parent, cpu, socket, srcline, weight, |
| 76 | local_weight, cgroup_id. | ||
| 76 | 77 | ||
| 77 | Each key has following meaning: | 78 | Each key has following meaning: |
| 78 | 79 | ||
| @@ -92,6 +93,7 @@ OPTIONS | |||
| 92 | - weight: Event specific weight, e.g. memory latency or transaction | 93 | - weight: Event specific weight, e.g. memory latency or transaction |
| 93 | abort cost. This is the global weight. | 94 | abort cost. This is the global weight. |
| 94 | - local_weight: Local weight version of the weight above. | 95 | - local_weight: Local weight version of the weight above. |
| 96 | - cgroup_id: ID derived from cgroup namespace device and inode numbers. | ||
| 95 | - transaction: Transaction abort flags. | 97 | - transaction: Transaction abort flags. |
| 96 | - overhead: Overhead percentage of sample | 98 | - overhead: Overhead percentage of sample |
| 97 | - overhead_sys: Overhead percentage of sample running in system mode | 99 | - overhead_sys: Overhead percentage of sample running in system mode |
| @@ -173,6 +175,9 @@ OPTIONS | |||
| 173 | By default, every sort keys not specified in -F will be appended | 175 | By default, every sort keys not specified in -F will be appended |
| 174 | automatically. | 176 | automatically. |
| 175 | 177 | ||
| 178 | If the keys starts with a prefix '+', then it will append the specified | ||
| 179 | field(s) to the default field order. For example: perf report -F +period,sample. | ||
| 180 | |||
| 176 | -p:: | 181 | -p:: |
| 177 | --parent=<regex>:: | 182 | --parent=<regex>:: |
| 178 | A regex filter to identify parent. The parent is a caller of this | 183 | A regex filter to identify parent. The parent is a caller of this |
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index d33deddb0146..a092a2499e8f 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt | |||
| @@ -132,6 +132,10 @@ OPTIONS for 'perf sched timehist' | |||
| 132 | --migrations:: | 132 | --migrations:: |
| 133 | Show migration events. | 133 | Show migration events. |
| 134 | 134 | ||
| 135 | -n:: | ||
| 136 | --next:: | ||
| 137 | Show next task. | ||
| 138 | |||
| 135 | -I:: | 139 | -I:: |
| 136 | --idle-hist:: | 140 | --idle-hist:: |
| 137 | Show idle-related events only. | 141 | Show idle-related events only. |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 4ed5f239ba7d..62c9b0c77a3a 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
| @@ -248,6 +248,9 @@ OPTIONS | |||
| 248 | --show-mmap-events | 248 | --show-mmap-events |
| 249 | Display mmap related events (e.g. MMAP, MMAP2). | 249 | Display mmap related events (e.g. MMAP, MMAP2). |
| 250 | 250 | ||
| 251 | --show-namespace-events | ||
| 252 | Display namespace events i.e. events of type PERF_RECORD_NAMESPACES. | ||
| 253 | |||
| 251 | --show-switch-events | 254 | --show-switch-events |
| 252 | Display context switch events i.e. events of type PERF_RECORD_SWITCH or | 255 | Display context switch events i.e. events of type PERF_RECORD_SWITCH or |
| 253 | PERF_RECORD_SWITCH_CPU_WIDE. | 256 | PERF_RECORD_SWITCH_CPU_WIDE. |
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c index 1030a6e504bb..39dbe512b9fc 100644 --- a/tools/perf/arch/powerpc/util/sym-handling.c +++ b/tools/perf/arch/powerpc/util/sym-handling.c | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "symbol.h" | 10 | #include "symbol.h" |
| 11 | #include "map.h" | 11 | #include "map.h" |
| 12 | #include "probe-event.h" | 12 | #include "probe-event.h" |
| 13 | #include "probe-file.h" | ||
| 13 | 14 | ||
| 14 | #ifdef HAVE_LIBELF_SUPPORT | 15 | #ifdef HAVE_LIBELF_SUPPORT |
| 15 | bool elf__needs_adjust_symbols(GElf_Ehdr ehdr) | 16 | bool elf__needs_adjust_symbols(GElf_Ehdr ehdr) |
| @@ -79,13 +80,18 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev, | |||
| 79 | * However, if the user specifies an offset, we fall back to using the | 80 | * However, if the user specifies an offset, we fall back to using the |
| 80 | * GEP since all userspace applications (objdump/readelf) show function | 81 | * GEP since all userspace applications (objdump/readelf) show function |
| 81 | * disassembly with offsets from the GEP. | 82 | * disassembly with offsets from the GEP. |
| 82 | * | ||
| 83 | * In addition, we shouldn't specify an offset for kretprobes. | ||
| 84 | */ | 83 | */ |
| 85 | if (pev->point.offset || (!pev->uprobes && pev->point.retprobe) || | 84 | if (pev->point.offset || !map || !sym) |
| 86 | !map || !sym) | ||
| 87 | return; | 85 | return; |
| 88 | 86 | ||
| 87 | /* For kretprobes, add an offset only if the kernel supports it */ | ||
| 88 | if (!pev->uprobes && pev->point.retprobe) { | ||
| 89 | #ifdef HAVE_LIBELF_SUPPORT | ||
| 90 | if (!kretprobe_offset_is_supported()) | ||
| 91 | #endif | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | |||
| 89 | lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym); | 95 | lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym); |
| 90 | 96 | ||
| 91 | if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) | 97 | if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 4f52d85f5ebc..e54b1f9fe1ee 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -393,6 +393,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 393 | .comm = perf_event__process_comm, | 393 | .comm = perf_event__process_comm, |
| 394 | .exit = perf_event__process_exit, | 394 | .exit = perf_event__process_exit, |
| 395 | .fork = perf_event__process_fork, | 395 | .fork = perf_event__process_fork, |
| 396 | .namespaces = perf_event__process_namespaces, | ||
| 396 | .ordered_events = true, | 397 | .ordered_events = true, |
| 397 | .ordering_requires_timestamps = true, | 398 | .ordering_requires_timestamps = true, |
| 398 | }, | 399 | }, |
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index e2b21723bbf8..5cd6d7a047b9 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c | |||
| @@ -2334,7 +2334,7 @@ out: | |||
| 2334 | 2334 | ||
| 2335 | static void perf_c2c_display(struct perf_session *session) | 2335 | static void perf_c2c_display(struct perf_session *session) |
| 2336 | { | 2336 | { |
| 2337 | if (c2c.use_stdio) | 2337 | if (use_browser == 0) |
| 2338 | perf_c2c__hists_fprintf(stdout, session); | 2338 | perf_c2c__hists_fprintf(stdout, session); |
| 2339 | else | 2339 | else |
| 2340 | perf_c2c__hists_browse(&c2c.hists.hists); | 2340 | perf_c2c__hists_browse(&c2c.hists.hists); |
| @@ -2536,7 +2536,7 @@ static int perf_c2c__report(int argc, const char **argv) | |||
| 2536 | OPT_BOOLEAN(0, "stdio", &c2c.use_stdio, "Use the stdio interface"), | 2536 | OPT_BOOLEAN(0, "stdio", &c2c.use_stdio, "Use the stdio interface"), |
| 2537 | #endif | 2537 | #endif |
| 2538 | OPT_BOOLEAN(0, "stats", &c2c.stats_only, | 2538 | OPT_BOOLEAN(0, "stats", &c2c.stats_only, |
| 2539 | "Use the stdio interface"), | 2539 | "Display only statistic tables (implies --stdio)"), |
| 2540 | OPT_BOOLEAN(0, "full-symbols", &c2c.symbol_full, | 2540 | OPT_BOOLEAN(0, "full-symbols", &c2c.symbol_full, |
| 2541 | "Display full length of symbols"), | 2541 | "Display full length of symbols"), |
| 2542 | OPT_BOOLEAN(0, "no-source", &no_source, | 2542 | OPT_BOOLEAN(0, "no-source", &no_source, |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 1b96a3122228..5e4803158672 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
| @@ -364,6 +364,7 @@ static struct perf_tool tool = { | |||
| 364 | .exit = perf_event__process_exit, | 364 | .exit = perf_event__process_exit, |
| 365 | .fork = perf_event__process_fork, | 365 | .fork = perf_event__process_fork, |
| 366 | .lost = perf_event__process_lost, | 366 | .lost = perf_event__process_lost, |
| 367 | .namespaces = perf_event__process_namespaces, | ||
| 367 | .ordered_events = true, | 368 | .ordered_events = true, |
| 368 | .ordering_requires_timestamps = true, | 369 | .ordering_requires_timestamps = true, |
| 369 | }; | 370 | }; |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index b9bc7e39833a..8d1d13b9bab6 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
| @@ -333,6 +333,18 @@ static int perf_event__repipe_comm(struct perf_tool *tool, | |||
| 333 | return err; | 333 | return err; |
| 334 | } | 334 | } |
| 335 | 335 | ||
| 336 | static int perf_event__repipe_namespaces(struct perf_tool *tool, | ||
| 337 | union perf_event *event, | ||
| 338 | struct perf_sample *sample, | ||
| 339 | struct machine *machine) | ||
| 340 | { | ||
| 341 | int err = perf_event__process_namespaces(tool, event, sample, machine); | ||
| 342 | |||
| 343 | perf_event__repipe(tool, event, sample, machine); | ||
| 344 | |||
| 345 | return err; | ||
| 346 | } | ||
| 347 | |||
| 336 | static int perf_event__repipe_exit(struct perf_tool *tool, | 348 | static int perf_event__repipe_exit(struct perf_tool *tool, |
| 337 | union perf_event *event, | 349 | union perf_event *event, |
| 338 | struct perf_sample *sample, | 350 | struct perf_sample *sample, |
| @@ -660,6 +672,7 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 660 | session->itrace_synth_opts = &inject->itrace_synth_opts; | 672 | session->itrace_synth_opts = &inject->itrace_synth_opts; |
| 661 | inject->itrace_synth_opts.inject = true; | 673 | inject->itrace_synth_opts.inject = true; |
| 662 | inject->tool.comm = perf_event__repipe_comm; | 674 | inject->tool.comm = perf_event__repipe_comm; |
| 675 | inject->tool.namespaces = perf_event__repipe_namespaces; | ||
| 663 | inject->tool.exit = perf_event__repipe_exit; | 676 | inject->tool.exit = perf_event__repipe_exit; |
| 664 | inject->tool.id_index = perf_event__repipe_id_index; | 677 | inject->tool.id_index = perf_event__repipe_id_index; |
| 665 | inject->tool.auxtrace_info = perf_event__process_auxtrace_info; | 678 | inject->tool.auxtrace_info = perf_event__process_auxtrace_info; |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 6da8d083e4e5..d509e74bc6e8 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
| @@ -964,6 +964,7 @@ static struct perf_tool perf_kmem = { | |||
| 964 | .comm = perf_event__process_comm, | 964 | .comm = perf_event__process_comm, |
| 965 | .mmap = perf_event__process_mmap, | 965 | .mmap = perf_event__process_mmap, |
| 966 | .mmap2 = perf_event__process_mmap2, | 966 | .mmap2 = perf_event__process_mmap2, |
| 967 | .namespaces = perf_event__process_namespaces, | ||
| 967 | .ordered_events = true, | 968 | .ordered_events = true, |
| 968 | }; | 969 | }; |
| 969 | 970 | ||
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 08fa88f62a24..18e6c38864bc 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
| @@ -1044,6 +1044,7 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
| 1044 | struct perf_tool eops = { | 1044 | struct perf_tool eops = { |
| 1045 | .sample = process_sample_event, | 1045 | .sample = process_sample_event, |
| 1046 | .comm = perf_event__process_comm, | 1046 | .comm = perf_event__process_comm, |
| 1047 | .namespaces = perf_event__process_namespaces, | ||
| 1047 | .ordered_events = true, | 1048 | .ordered_events = true, |
| 1048 | }; | 1049 | }; |
| 1049 | struct perf_data_file file = { | 1050 | struct perf_data_file file = { |
| @@ -1348,6 +1349,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1348 | kvm->tool.exit = perf_event__process_exit; | 1349 | kvm->tool.exit = perf_event__process_exit; |
| 1349 | kvm->tool.fork = perf_event__process_fork; | 1350 | kvm->tool.fork = perf_event__process_fork; |
| 1350 | kvm->tool.lost = process_lost_event; | 1351 | kvm->tool.lost = process_lost_event; |
| 1352 | kvm->tool.namespaces = perf_event__process_namespaces; | ||
| 1351 | kvm->tool.ordered_events = true; | 1353 | kvm->tool.ordered_events = true; |
| 1352 | perf_tool__fill_defaults(&kvm->tool); | 1354 | perf_tool__fill_defaults(&kvm->tool); |
| 1353 | 1355 | ||
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index ce3bfb48b26f..d750ccaa978f 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
| @@ -858,6 +858,7 @@ static int __cmd_report(bool display_info) | |||
| 858 | struct perf_tool eops = { | 858 | struct perf_tool eops = { |
| 859 | .sample = process_sample_event, | 859 | .sample = process_sample_event, |
| 860 | .comm = perf_event__process_comm, | 860 | .comm = perf_event__process_comm, |
| 861 | .namespaces = perf_event__process_namespaces, | ||
| 861 | .ordered_events = true, | 862 | .ordered_events = true, |
| 862 | }; | 863 | }; |
| 863 | struct perf_data_file file = { | 864 | struct perf_data_file file = { |
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 6114e07ca613..030a6cfdda59 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
| @@ -342,6 +342,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 342 | .lost = perf_event__process_lost, | 342 | .lost = perf_event__process_lost, |
| 343 | .fork = perf_event__process_fork, | 343 | .fork = perf_event__process_fork, |
| 344 | .build_id = perf_event__process_build_id, | 344 | .build_id = perf_event__process_build_id, |
| 345 | .namespaces = perf_event__process_namespaces, | ||
| 345 | .ordered_events = true, | 346 | .ordered_events = true, |
| 346 | }, | 347 | }, |
| 347 | .input_name = "perf.data", | 348 | .input_name = "perf.data", |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index bc84a375295d..04faef79a548 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -876,6 +876,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 876 | signal(SIGTERM, sig_handler); | 876 | signal(SIGTERM, sig_handler); |
| 877 | signal(SIGSEGV, sigsegv_handler); | 877 | signal(SIGSEGV, sigsegv_handler); |
| 878 | 878 | ||
| 879 | if (rec->opts.record_namespaces) | ||
| 880 | tool->namespace_events = true; | ||
| 881 | |||
| 879 | if (rec->opts.auxtrace_snapshot_mode || rec->switch_output.enabled) { | 882 | if (rec->opts.auxtrace_snapshot_mode || rec->switch_output.enabled) { |
| 880 | signal(SIGUSR2, snapshot_sig_handler); | 883 | signal(SIGUSR2, snapshot_sig_handler); |
| 881 | if (rec->opts.auxtrace_snapshot_mode) | 884 | if (rec->opts.auxtrace_snapshot_mode) |
| @@ -983,6 +986,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 983 | */ | 986 | */ |
| 984 | if (forks) { | 987 | if (forks) { |
| 985 | union perf_event *event; | 988 | union perf_event *event; |
| 989 | pid_t tgid; | ||
| 986 | 990 | ||
| 987 | event = malloc(sizeof(event->comm) + machine->id_hdr_size); | 991 | event = malloc(sizeof(event->comm) + machine->id_hdr_size); |
| 988 | if (event == NULL) { | 992 | if (event == NULL) { |
| @@ -996,10 +1000,30 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 996 | * cannot see a correct process name for those events. | 1000 | * cannot see a correct process name for those events. |
| 997 | * Synthesize COMM event to prevent it. | 1001 | * Synthesize COMM event to prevent it. |
| 998 | */ | 1002 | */ |
| 999 | perf_event__synthesize_comm(tool, event, | 1003 | tgid = perf_event__synthesize_comm(tool, event, |
| 1000 | rec->evlist->workload.pid, | 1004 | rec->evlist->workload.pid, |
| 1001 | process_synthesized_event, | 1005 | process_synthesized_event, |
| 1002 | machine); | 1006 | machine); |
| 1007 | free(event); | ||
| 1008 | |||
| 1009 | if (tgid == -1) | ||
| 1010 | goto out_child; | ||
| 1011 | |||
| 1012 | event = malloc(sizeof(event->namespaces) + | ||
| 1013 | (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + | ||
| 1014 | machine->id_hdr_size); | ||
| 1015 | if (event == NULL) { | ||
| 1016 | err = -ENOMEM; | ||
| 1017 | goto out_child; | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | /* | ||
| 1021 | * Synthesize NAMESPACES event for the command specified. | ||
| 1022 | */ | ||
| 1023 | perf_event__synthesize_namespaces(tool, event, | ||
| 1024 | rec->evlist->workload.pid, | ||
| 1025 | tgid, process_synthesized_event, | ||
| 1026 | machine); | ||
| 1003 | free(event); | 1027 | free(event); |
| 1004 | 1028 | ||
| 1005 | perf_evlist__start_workload(rec->evlist); | 1029 | perf_evlist__start_workload(rec->evlist); |
| @@ -1497,6 +1521,7 @@ static struct record record = { | |||
| 1497 | .fork = perf_event__process_fork, | 1521 | .fork = perf_event__process_fork, |
| 1498 | .exit = perf_event__process_exit, | 1522 | .exit = perf_event__process_exit, |
| 1499 | .comm = perf_event__process_comm, | 1523 | .comm = perf_event__process_comm, |
| 1524 | .namespaces = perf_event__process_namespaces, | ||
| 1500 | .mmap = perf_event__process_mmap, | 1525 | .mmap = perf_event__process_mmap, |
| 1501 | .mmap2 = perf_event__process_mmap2, | 1526 | .mmap2 = perf_event__process_mmap2, |
| 1502 | .ordered_events = true, | 1527 | .ordered_events = true, |
| @@ -1611,6 +1636,8 @@ static struct option __record_options[] = { | |||
| 1611 | "opts", "AUX area tracing Snapshot Mode", ""), | 1636 | "opts", "AUX area tracing Snapshot Mode", ""), |
| 1612 | OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout, | 1637 | OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout, |
| 1613 | "per thread proc mmap processing timeout in ms"), | 1638 | "per thread proc mmap processing timeout in ms"), |
| 1639 | OPT_BOOLEAN(0, "namespaces", &record.opts.record_namespaces, | ||
| 1640 | "Record namespaces events"), | ||
| 1614 | OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events, | 1641 | OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events, |
| 1615 | "Record context switch events"), | 1642 | "Record context switch events"), |
| 1616 | OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel, | 1643 | OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel, |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 0a88670e56f3..5ab8117c3bfd 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -394,8 +394,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
| 394 | fprintf(stdout, "\n\n"); | 394 | fprintf(stdout, "\n\n"); |
| 395 | } | 395 | } |
| 396 | 396 | ||
| 397 | if (sort_order == NULL && | 397 | if (!quiet) |
| 398 | parent_pattern == default_parent_pattern) | ||
| 399 | fprintf(stdout, "#\n# (%s)\n#\n", help); | 398 | fprintf(stdout, "#\n# (%s)\n#\n", help); |
| 400 | 399 | ||
| 401 | if (rep->show_threads) { | 400 | if (rep->show_threads) { |
| @@ -701,6 +700,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 701 | .mmap = perf_event__process_mmap, | 700 | .mmap = perf_event__process_mmap, |
| 702 | .mmap2 = perf_event__process_mmap2, | 701 | .mmap2 = perf_event__process_mmap2, |
| 703 | .comm = perf_event__process_comm, | 702 | .comm = perf_event__process_comm, |
| 703 | .namespaces = perf_event__process_namespaces, | ||
| 704 | .exit = perf_event__process_exit, | 704 | .exit = perf_event__process_exit, |
| 705 | .fork = perf_event__process_fork, | 705 | .fork = perf_event__process_fork, |
| 706 | .lost = perf_event__process_lost, | 706 | .lost = perf_event__process_lost, |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index b94cf0de715a..b92c4d97192c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
| @@ -221,6 +221,7 @@ struct perf_sched { | |||
| 221 | unsigned int max_stack; | 221 | unsigned int max_stack; |
| 222 | bool show_cpu_visual; | 222 | bool show_cpu_visual; |
| 223 | bool show_wakeups; | 223 | bool show_wakeups; |
| 224 | bool show_next; | ||
| 224 | bool show_migrations; | 225 | bool show_migrations; |
| 225 | bool show_state; | 226 | bool show_state; |
| 226 | u64 skipped_samples; | 227 | u64 skipped_samples; |
| @@ -1897,14 +1898,18 @@ static char task_state_char(struct thread *thread, int state) | |||
| 1897 | } | 1898 | } |
| 1898 | 1899 | ||
| 1899 | static void timehist_print_sample(struct perf_sched *sched, | 1900 | static void timehist_print_sample(struct perf_sched *sched, |
| 1901 | struct perf_evsel *evsel, | ||
| 1900 | struct perf_sample *sample, | 1902 | struct perf_sample *sample, |
| 1901 | struct addr_location *al, | 1903 | struct addr_location *al, |
| 1902 | struct thread *thread, | 1904 | struct thread *thread, |
| 1903 | u64 t, int state) | 1905 | u64 t, int state) |
| 1904 | { | 1906 | { |
| 1905 | struct thread_runtime *tr = thread__priv(thread); | 1907 | struct thread_runtime *tr = thread__priv(thread); |
| 1908 | const char *next_comm = perf_evsel__strval(evsel, sample, "next_comm"); | ||
| 1909 | const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid"); | ||
| 1906 | u32 max_cpus = sched->max_cpu + 1; | 1910 | u32 max_cpus = sched->max_cpu + 1; |
| 1907 | char tstr[64]; | 1911 | char tstr[64]; |
| 1912 | char nstr[30]; | ||
| 1908 | u64 wait_time; | 1913 | u64 wait_time; |
| 1909 | 1914 | ||
| 1910 | timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); | 1915 | timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); |
| @@ -1937,7 +1942,12 @@ static void timehist_print_sample(struct perf_sched *sched, | |||
| 1937 | if (sched->show_state) | 1942 | if (sched->show_state) |
| 1938 | printf(" %5c ", task_state_char(thread, state)); | 1943 | printf(" %5c ", task_state_char(thread, state)); |
| 1939 | 1944 | ||
| 1940 | if (sched->show_wakeups) | 1945 | if (sched->show_next) { |
| 1946 | snprintf(nstr, sizeof(nstr), "next: %s[%d]", next_comm, next_pid); | ||
| 1947 | printf(" %-*s", comm_width, nstr); | ||
| 1948 | } | ||
| 1949 | |||
| 1950 | if (sched->show_wakeups && !sched->show_next) | ||
| 1941 | printf(" %-*s", comm_width, ""); | 1951 | printf(" %-*s", comm_width, ""); |
| 1942 | 1952 | ||
| 1943 | if (thread->tid == 0) | 1953 | if (thread->tid == 0) |
| @@ -2531,7 +2541,7 @@ static int timehist_sched_change_event(struct perf_tool *tool, | |||
| 2531 | } | 2541 | } |
| 2532 | 2542 | ||
| 2533 | if (!sched->summary_only) | 2543 | if (!sched->summary_only) |
| 2534 | timehist_print_sample(sched, sample, &al, thread, t, state); | 2544 | timehist_print_sample(sched, evsel, sample, &al, thread, t, state); |
| 2535 | 2545 | ||
| 2536 | out: | 2546 | out: |
| 2537 | if (sched->hist_time.start == 0 && t >= ptime->start) | 2547 | if (sched->hist_time.start == 0 && t >= ptime->start) |
| @@ -3272,6 +3282,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 3272 | .tool = { | 3282 | .tool = { |
| 3273 | .sample = perf_sched__process_tracepoint_sample, | 3283 | .sample = perf_sched__process_tracepoint_sample, |
| 3274 | .comm = perf_event__process_comm, | 3284 | .comm = perf_event__process_comm, |
| 3285 | .namespaces = perf_event__process_namespaces, | ||
| 3275 | .lost = perf_event__process_lost, | 3286 | .lost = perf_event__process_lost, |
| 3276 | .fork = perf_sched__process_fork_event, | 3287 | .fork = perf_sched__process_fork_event, |
| 3277 | .ordered_events = true, | 3288 | .ordered_events = true, |
| @@ -3340,6 +3351,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 3340 | OPT_BOOLEAN('S', "with-summary", &sched.summary, | 3351 | OPT_BOOLEAN('S', "with-summary", &sched.summary, |
| 3341 | "Show all syscalls and summary with statistics"), | 3352 | "Show all syscalls and summary with statistics"), |
| 3342 | OPT_BOOLEAN('w', "wakeups", &sched.show_wakeups, "Show wakeup events"), | 3353 | OPT_BOOLEAN('w', "wakeups", &sched.show_wakeups, "Show wakeup events"), |
| 3354 | OPT_BOOLEAN('n', "next", &sched.show_next, "Show next task"), | ||
| 3343 | OPT_BOOLEAN('M', "migrations", &sched.show_migrations, "Show migration events"), | 3355 | OPT_BOOLEAN('M', "migrations", &sched.show_migrations, "Show migration events"), |
| 3344 | OPT_BOOLEAN('V', "cpu-visual", &sched.show_cpu_visual, "Add CPU visual"), | 3356 | OPT_BOOLEAN('V', "cpu-visual", &sched.show_cpu_visual, "Add CPU visual"), |
| 3345 | OPT_BOOLEAN('I', "idle-hist", &sched.idle_hist, "Show idle events only"), | 3357 | OPT_BOOLEAN('I', "idle-hist", &sched.idle_hist, "Show idle events only"), |
| @@ -3437,10 +3449,14 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 3437 | if (argc) | 3449 | if (argc) |
| 3438 | usage_with_options(timehist_usage, timehist_options); | 3450 | usage_with_options(timehist_usage, timehist_options); |
| 3439 | } | 3451 | } |
| 3440 | if (sched.show_wakeups && sched.summary_only) { | 3452 | if ((sched.show_wakeups || sched.show_next) && |
| 3441 | pr_err(" Error: -s and -w are mutually exclusive.\n"); | 3453 | sched.summary_only) { |
| 3454 | pr_err(" Error: -s and -[n|w] are mutually exclusive.\n"); | ||
| 3442 | parse_options_usage(timehist_usage, timehist_options, "s", true); | 3455 | parse_options_usage(timehist_usage, timehist_options, "s", true); |
| 3443 | parse_options_usage(NULL, timehist_options, "w", true); | 3456 | if (sched.show_wakeups) |
| 3457 | parse_options_usage(NULL, timehist_options, "w", true); | ||
| 3458 | if (sched.show_next) | ||
| 3459 | parse_options_usage(NULL, timehist_options, "n", true); | ||
| 3444 | return -EINVAL; | 3460 | return -EINVAL; |
| 3445 | } | 3461 | } |
| 3446 | 3462 | ||
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c0783b4f7b6c..66d62c98dff9 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -830,6 +830,7 @@ struct perf_script { | |||
| 830 | bool show_task_events; | 830 | bool show_task_events; |
| 831 | bool show_mmap_events; | 831 | bool show_mmap_events; |
| 832 | bool show_switch_events; | 832 | bool show_switch_events; |
| 833 | bool show_namespace_events; | ||
| 833 | bool allocated; | 834 | bool allocated; |
| 834 | struct cpu_map *cpus; | 835 | struct cpu_map *cpus; |
| 835 | struct thread_map *threads; | 836 | struct thread_map *threads; |
| @@ -1118,6 +1119,41 @@ out: | |||
| 1118 | return ret; | 1119 | return ret; |
| 1119 | } | 1120 | } |
| 1120 | 1121 | ||
| 1122 | static int process_namespaces_event(struct perf_tool *tool, | ||
| 1123 | union perf_event *event, | ||
| 1124 | struct perf_sample *sample, | ||
| 1125 | struct machine *machine) | ||
| 1126 | { | ||
| 1127 | struct thread *thread; | ||
| 1128 | struct perf_script *script = container_of(tool, struct perf_script, tool); | ||
| 1129 | struct perf_session *session = script->session; | ||
| 1130 | struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); | ||
| 1131 | int ret = -1; | ||
| 1132 | |||
| 1133 | thread = machine__findnew_thread(machine, event->namespaces.pid, | ||
| 1134 | event->namespaces.tid); | ||
| 1135 | if (thread == NULL) { | ||
| 1136 | pr_debug("problem processing NAMESPACES event, skipping it.\n"); | ||
| 1137 | return -1; | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | if (perf_event__process_namespaces(tool, event, sample, machine) < 0) | ||
| 1141 | goto out; | ||
| 1142 | |||
| 1143 | if (!evsel->attr.sample_id_all) { | ||
| 1144 | sample->cpu = 0; | ||
| 1145 | sample->time = 0; | ||
| 1146 | sample->tid = event->namespaces.tid; | ||
| 1147 | sample->pid = event->namespaces.pid; | ||
| 1148 | } | ||
| 1149 | print_sample_start(sample, thread, evsel); | ||
| 1150 | perf_event__fprintf(event, stdout); | ||
| 1151 | ret = 0; | ||
| 1152 | out: | ||
| 1153 | thread__put(thread); | ||
| 1154 | return ret; | ||
| 1155 | } | ||
| 1156 | |||
| 1121 | static int process_fork_event(struct perf_tool *tool, | 1157 | static int process_fork_event(struct perf_tool *tool, |
| 1122 | union perf_event *event, | 1158 | union perf_event *event, |
| 1123 | struct perf_sample *sample, | 1159 | struct perf_sample *sample, |
| @@ -1293,6 +1329,8 @@ static int __cmd_script(struct perf_script *script) | |||
| 1293 | } | 1329 | } |
| 1294 | if (script->show_switch_events) | 1330 | if (script->show_switch_events) |
| 1295 | script->tool.context_switch = process_switch_event; | 1331 | script->tool.context_switch = process_switch_event; |
| 1332 | if (script->show_namespace_events) | ||
| 1333 | script->tool.namespaces = process_namespaces_event; | ||
| 1296 | 1334 | ||
| 1297 | ret = perf_session__process_events(script->session); | 1335 | ret = perf_session__process_events(script->session); |
| 1298 | 1336 | ||
| @@ -2097,6 +2135,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 2097 | .mmap = perf_event__process_mmap, | 2135 | .mmap = perf_event__process_mmap, |
| 2098 | .mmap2 = perf_event__process_mmap2, | 2136 | .mmap2 = perf_event__process_mmap2, |
| 2099 | .comm = perf_event__process_comm, | 2137 | .comm = perf_event__process_comm, |
| 2138 | .namespaces = perf_event__process_namespaces, | ||
| 2100 | .exit = perf_event__process_exit, | 2139 | .exit = perf_event__process_exit, |
| 2101 | .fork = perf_event__process_fork, | 2140 | .fork = perf_event__process_fork, |
| 2102 | .attr = process_attr, | 2141 | .attr = process_attr, |
| @@ -2180,6 +2219,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 2180 | "Show the mmap events"), | 2219 | "Show the mmap events"), |
| 2181 | OPT_BOOLEAN('\0', "show-switch-events", &script.show_switch_events, | 2220 | OPT_BOOLEAN('\0', "show-switch-events", &script.show_switch_events, |
| 2182 | "Show context switch events (if recorded)"), | 2221 | "Show context switch events (if recorded)"), |
| 2222 | OPT_BOOLEAN('\0', "show-namespace-events", &script.show_namespace_events, | ||
| 2223 | "Show namespace events (if recorded)"), | ||
| 2183 | OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), | 2224 | OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), |
| 2184 | OPT_BOOLEAN(0, "ns", &nanosecs, | 2225 | OPT_BOOLEAN(0, "ns", &nanosecs, |
| 2185 | "Use 9 decimal places when displaying time"), | 2226 | "Use 9 decimal places when displaying time"), |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 256f1fac6f7e..912fedc5b42d 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -2415,8 +2415,9 @@ static int trace__replay(struct trace *trace) | |||
| 2415 | trace->tool.exit = perf_event__process_exit; | 2415 | trace->tool.exit = perf_event__process_exit; |
| 2416 | trace->tool.fork = perf_event__process_fork; | 2416 | trace->tool.fork = perf_event__process_fork; |
| 2417 | trace->tool.attr = perf_event__process_attr; | 2417 | trace->tool.attr = perf_event__process_attr; |
| 2418 | trace->tool.tracing_data = perf_event__process_tracing_data; | 2418 | trace->tool.tracing_data = perf_event__process_tracing_data; |
| 2419 | trace->tool.build_id = perf_event__process_build_id; | 2419 | trace->tool.build_id = perf_event__process_build_id; |
| 2420 | trace->tool.namespaces = perf_event__process_namespaces; | ||
| 2420 | 2421 | ||
| 2421 | trace->tool.ordered_events = true; | 2422 | trace->tool.ordered_events = true; |
| 2422 | trace->tool.ordering_requires_timestamps = true; | 2423 | trace->tool.ordering_requires_timestamps = true; |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index ac3efd396a72..2d0caf20ff3a 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
| @@ -9,6 +9,7 @@ perf-buildid-cache mainporcelain common | |||
| 9 | perf-buildid-list mainporcelain common | 9 | perf-buildid-list mainporcelain common |
| 10 | perf-data mainporcelain common | 10 | perf-data mainporcelain common |
| 11 | perf-diff mainporcelain common | 11 | perf-diff mainporcelain common |
| 12 | perf-c2c mainporcelain common | ||
| 12 | perf-config mainporcelain common | 13 | perf-config mainporcelain common |
| 13 | perf-evlist mainporcelain common | 14 | perf-evlist mainporcelain common |
| 14 | perf-ftrace mainporcelain common | 15 | perf-ftrace mainporcelain common |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 1c27d947c2fe..806c216a1078 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -50,6 +50,7 @@ struct record_opts { | |||
| 50 | bool running_time; | 50 | bool running_time; |
| 51 | bool full_auxtrace; | 51 | bool full_auxtrace; |
| 52 | bool auxtrace_snapshot_mode; | 52 | bool auxtrace_snapshot_mode; |
| 53 | bool record_namespaces; | ||
| 53 | bool record_switch_events; | 54 | bool record_switch_events; |
| 54 | bool all_kernel; | 55 | bool all_kernel; |
| 55 | bool all_user; | 56 | bool all_user; |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index fc4fb669ceee..2dc82bec10c0 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
| @@ -2308,7 +2308,7 @@ static int switch_data_file(void) | |||
| 2308 | return ret; | 2308 | return ret; |
| 2309 | 2309 | ||
| 2310 | memset(options, 0, sizeof(options)); | 2310 | memset(options, 0, sizeof(options)); |
| 2311 | memset(options, 0, sizeof(abs_path)); | 2311 | memset(abs_path, 0, sizeof(abs_path)); |
| 2312 | 2312 | ||
| 2313 | while ((dent = readdir(pwd_dir))) { | 2313 | while ((dent = readdir(pwd_dir))) { |
| 2314 | char path[PATH_MAX]; | 2314 | char path[PATH_MAX]; |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 5da376bc1afc..2ea5ee179a3b 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
| @@ -42,6 +42,7 @@ libperf-y += pstack.o | |||
| 42 | libperf-y += session.o | 42 | libperf-y += session.o |
| 43 | libperf-$(CONFIG_AUDIT) += syscalltbl.o | 43 | libperf-$(CONFIG_AUDIT) += syscalltbl.o |
| 44 | libperf-y += ordered-events.o | 44 | libperf-y += ordered-events.o |
| 45 | libperf-y += namespaces.o | ||
| 45 | libperf-y += comm.o | 46 | libperf-y += comm.o |
| 46 | libperf-y += thread.o | 47 | libperf-y += thread.o |
| 47 | libperf-y += thread_map.o | 48 | libperf-y += thread_map.o |
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 4e6cbc99f08e..89ece2445713 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c | |||
| @@ -1468,6 +1468,7 @@ int bt_convert__perf2ctf(const char *input, const char *path, | |||
| 1468 | .lost = perf_event__process_lost, | 1468 | .lost = perf_event__process_lost, |
| 1469 | .tracing_data = perf_event__process_tracing_data, | 1469 | .tracing_data = perf_event__process_tracing_data, |
| 1470 | .build_id = perf_event__process_build_id, | 1470 | .build_id = perf_event__process_build_id, |
| 1471 | .namespaces = perf_event__process_namespaces, | ||
| 1471 | .ordered_events = true, | 1472 | .ordered_events = true, |
| 1472 | .ordering_requires_timestamps = true, | 1473 | .ordering_requires_timestamps = true, |
| 1473 | }, | 1474 | }, |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 4ea7ce72ed9c..d082cb70445d 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -31,6 +31,7 @@ static const char *perf_event__names[] = { | |||
| 31 | [PERF_RECORD_LOST_SAMPLES] = "LOST_SAMPLES", | 31 | [PERF_RECORD_LOST_SAMPLES] = "LOST_SAMPLES", |
| 32 | [PERF_RECORD_SWITCH] = "SWITCH", | 32 | [PERF_RECORD_SWITCH] = "SWITCH", |
| 33 | [PERF_RECORD_SWITCH_CPU_WIDE] = "SWITCH_CPU_WIDE", | 33 | [PERF_RECORD_SWITCH_CPU_WIDE] = "SWITCH_CPU_WIDE", |
| 34 | [PERF_RECORD_NAMESPACES] = "NAMESPACES", | ||
| 34 | [PERF_RECORD_HEADER_ATTR] = "ATTR", | 35 | [PERF_RECORD_HEADER_ATTR] = "ATTR", |
| 35 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", | 36 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", |
| 36 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", | 37 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", |
| @@ -49,6 +50,16 @@ static const char *perf_event__names[] = { | |||
| 49 | [PERF_RECORD_TIME_CONV] = "TIME_CONV", | 50 | [PERF_RECORD_TIME_CONV] = "TIME_CONV", |
| 50 | }; | 51 | }; |
| 51 | 52 | ||
| 53 | static const char *perf_ns__names[] = { | ||
| 54 | [NET_NS_INDEX] = "net", | ||
| 55 | [UTS_NS_INDEX] = "uts", | ||
| 56 | [IPC_NS_INDEX] = "ipc", | ||
| 57 | [PID_NS_INDEX] = "pid", | ||
| 58 | [USER_NS_INDEX] = "user", | ||
| 59 | [MNT_NS_INDEX] = "mnt", | ||
| 60 | [CGROUP_NS_INDEX] = "cgroup", | ||
| 61 | }; | ||
| 62 | |||
| 52 | const char *perf_event__name(unsigned int id) | 63 | const char *perf_event__name(unsigned int id) |
| 53 | { | 64 | { |
| 54 | if (id >= ARRAY_SIZE(perf_event__names)) | 65 | if (id >= ARRAY_SIZE(perf_event__names)) |
| @@ -58,6 +69,13 @@ const char *perf_event__name(unsigned int id) | |||
| 58 | return perf_event__names[id]; | 69 | return perf_event__names[id]; |
| 59 | } | 70 | } |
| 60 | 71 | ||
| 72 | static const char *perf_ns__name(unsigned int id) | ||
| 73 | { | ||
| 74 | if (id >= ARRAY_SIZE(perf_ns__names)) | ||
| 75 | return "UNKNOWN"; | ||
| 76 | return perf_ns__names[id]; | ||
| 77 | } | ||
| 78 | |||
| 61 | static int perf_tool__process_synth_event(struct perf_tool *tool, | 79 | static int perf_tool__process_synth_event(struct perf_tool *tool, |
| 62 | union perf_event *event, | 80 | union perf_event *event, |
| 63 | struct machine *machine, | 81 | struct machine *machine, |
| @@ -203,6 +221,58 @@ pid_t perf_event__synthesize_comm(struct perf_tool *tool, | |||
| 203 | return tgid; | 221 | return tgid; |
| 204 | } | 222 | } |
| 205 | 223 | ||
| 224 | static void perf_event__get_ns_link_info(pid_t pid, const char *ns, | ||
| 225 | struct perf_ns_link_info *ns_link_info) | ||
| 226 | { | ||
| 227 | struct stat64 st; | ||
| 228 | char proc_ns[128]; | ||
| 229 | |||
| 230 | sprintf(proc_ns, "/proc/%u/ns/%s", pid, ns); | ||
| 231 | if (stat64(proc_ns, &st) == 0) { | ||
| 232 | ns_link_info->dev = st.st_dev; | ||
| 233 | ns_link_info->ino = st.st_ino; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | int perf_event__synthesize_namespaces(struct perf_tool *tool, | ||
| 238 | union perf_event *event, | ||
| 239 | pid_t pid, pid_t tgid, | ||
| 240 | perf_event__handler_t process, | ||
| 241 | struct machine *machine) | ||
| 242 | { | ||
| 243 | u32 idx; | ||
| 244 | struct perf_ns_link_info *ns_link_info; | ||
| 245 | |||
| 246 | if (!tool || !tool->namespace_events) | ||
| 247 | return 0; | ||
| 248 | |||
| 249 | memset(&event->namespaces, 0, (sizeof(event->namespaces) + | ||
| 250 | (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + | ||
| 251 | machine->id_hdr_size)); | ||
| 252 | |||
| 253 | event->namespaces.pid = tgid; | ||
| 254 | event->namespaces.tid = pid; | ||
| 255 | |||
| 256 | event->namespaces.nr_namespaces = NR_NAMESPACES; | ||
| 257 | |||
| 258 | ns_link_info = event->namespaces.link_info; | ||
| 259 | |||
| 260 | for (idx = 0; idx < event->namespaces.nr_namespaces; idx++) | ||
| 261 | perf_event__get_ns_link_info(pid, perf_ns__name(idx), | ||
| 262 | &ns_link_info[idx]); | ||
| 263 | |||
| 264 | event->namespaces.header.type = PERF_RECORD_NAMESPACES; | ||
| 265 | |||
| 266 | event->namespaces.header.size = (sizeof(event->namespaces) + | ||
| 267 | (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + | ||
| 268 | machine->id_hdr_size); | ||
| 269 | |||
| 270 | if (perf_tool__process_synth_event(tool, event, machine, process) != 0) | ||
| 271 | return -1; | ||
| 272 | |||
| 273 | return 0; | ||
| 274 | } | ||
| 275 | |||
| 206 | static int perf_event__synthesize_fork(struct perf_tool *tool, | 276 | static int perf_event__synthesize_fork(struct perf_tool *tool, |
| 207 | union perf_event *event, | 277 | union perf_event *event, |
| 208 | pid_t pid, pid_t tgid, pid_t ppid, | 278 | pid_t pid, pid_t tgid, pid_t ppid, |
| @@ -434,8 +504,9 @@ int perf_event__synthesize_modules(struct perf_tool *tool, | |||
| 434 | static int __event__synthesize_thread(union perf_event *comm_event, | 504 | static int __event__synthesize_thread(union perf_event *comm_event, |
| 435 | union perf_event *mmap_event, | 505 | union perf_event *mmap_event, |
| 436 | union perf_event *fork_event, | 506 | union perf_event *fork_event, |
| 507 | union perf_event *namespaces_event, | ||
| 437 | pid_t pid, int full, | 508 | pid_t pid, int full, |
| 438 | perf_event__handler_t process, | 509 | perf_event__handler_t process, |
| 439 | struct perf_tool *tool, | 510 | struct perf_tool *tool, |
| 440 | struct machine *machine, | 511 | struct machine *machine, |
| 441 | bool mmap_data, | 512 | bool mmap_data, |
| @@ -455,6 +526,11 @@ static int __event__synthesize_thread(union perf_event *comm_event, | |||
| 455 | if (tgid == -1) | 526 | if (tgid == -1) |
| 456 | return -1; | 527 | return -1; |
| 457 | 528 | ||
| 529 | if (perf_event__synthesize_namespaces(tool, namespaces_event, pid, | ||
| 530 | tgid, process, machine) < 0) | ||
| 531 | return -1; | ||
| 532 | |||
| 533 | |||
| 458 | return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, | 534 | return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, |
| 459 | process, machine, mmap_data, | 535 | process, machine, mmap_data, |
| 460 | proc_map_timeout); | 536 | proc_map_timeout); |
| @@ -488,6 +564,11 @@ static int __event__synthesize_thread(union perf_event *comm_event, | |||
| 488 | if (perf_event__synthesize_fork(tool, fork_event, _pid, tgid, | 564 | if (perf_event__synthesize_fork(tool, fork_event, _pid, tgid, |
| 489 | ppid, process, machine) < 0) | 565 | ppid, process, machine) < 0) |
| 490 | break; | 566 | break; |
| 567 | |||
| 568 | if (perf_event__synthesize_namespaces(tool, namespaces_event, _pid, | ||
| 569 | tgid, process, machine) < 0) | ||
| 570 | break; | ||
| 571 | |||
| 491 | /* | 572 | /* |
| 492 | * Send the prepared comm event | 573 | * Send the prepared comm event |
| 493 | */ | 574 | */ |
| @@ -516,6 +597,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
| 516 | unsigned int proc_map_timeout) | 597 | unsigned int proc_map_timeout) |
| 517 | { | 598 | { |
| 518 | union perf_event *comm_event, *mmap_event, *fork_event; | 599 | union perf_event *comm_event, *mmap_event, *fork_event; |
| 600 | union perf_event *namespaces_event; | ||
| 519 | int err = -1, thread, j; | 601 | int err = -1, thread, j; |
| 520 | 602 | ||
| 521 | comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); | 603 | comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); |
| @@ -530,10 +612,16 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
| 530 | if (fork_event == NULL) | 612 | if (fork_event == NULL) |
| 531 | goto out_free_mmap; | 613 | goto out_free_mmap; |
| 532 | 614 | ||
| 615 | namespaces_event = malloc(sizeof(namespaces_event->namespaces) + | ||
| 616 | (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + | ||
| 617 | machine->id_hdr_size); | ||
| 618 | if (namespaces_event == NULL) | ||
| 619 | goto out_free_fork; | ||
| 620 | |||
| 533 | err = 0; | 621 | err = 0; |
| 534 | for (thread = 0; thread < threads->nr; ++thread) { | 622 | for (thread = 0; thread < threads->nr; ++thread) { |
| 535 | if (__event__synthesize_thread(comm_event, mmap_event, | 623 | if (__event__synthesize_thread(comm_event, mmap_event, |
| 536 | fork_event, | 624 | fork_event, namespaces_event, |
| 537 | thread_map__pid(threads, thread), 0, | 625 | thread_map__pid(threads, thread), 0, |
| 538 | process, tool, machine, | 626 | process, tool, machine, |
| 539 | mmap_data, proc_map_timeout)) { | 627 | mmap_data, proc_map_timeout)) { |
| @@ -559,7 +647,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
| 559 | /* if not, generate events for it */ | 647 | /* if not, generate events for it */ |
| 560 | if (need_leader && | 648 | if (need_leader && |
| 561 | __event__synthesize_thread(comm_event, mmap_event, | 649 | __event__synthesize_thread(comm_event, mmap_event, |
| 562 | fork_event, | 650 | fork_event, namespaces_event, |
| 563 | comm_event->comm.pid, 0, | 651 | comm_event->comm.pid, 0, |
| 564 | process, tool, machine, | 652 | process, tool, machine, |
| 565 | mmap_data, proc_map_timeout)) { | 653 | mmap_data, proc_map_timeout)) { |
| @@ -568,6 +656,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
| 568 | } | 656 | } |
| 569 | } | 657 | } |
| 570 | } | 658 | } |
| 659 | free(namespaces_event); | ||
| 660 | out_free_fork: | ||
| 571 | free(fork_event); | 661 | free(fork_event); |
| 572 | out_free_mmap: | 662 | out_free_mmap: |
| 573 | free(mmap_event); | 663 | free(mmap_event); |
| @@ -587,6 +677,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
| 587 | char proc_path[PATH_MAX]; | 677 | char proc_path[PATH_MAX]; |
| 588 | struct dirent *dirent; | 678 | struct dirent *dirent; |
| 589 | union perf_event *comm_event, *mmap_event, *fork_event; | 679 | union perf_event *comm_event, *mmap_event, *fork_event; |
| 680 | union perf_event *namespaces_event; | ||
| 590 | int err = -1; | 681 | int err = -1; |
| 591 | 682 | ||
| 592 | if (machine__is_default_guest(machine)) | 683 | if (machine__is_default_guest(machine)) |
| @@ -604,11 +695,17 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
| 604 | if (fork_event == NULL) | 695 | if (fork_event == NULL) |
| 605 | goto out_free_mmap; | 696 | goto out_free_mmap; |
| 606 | 697 | ||
| 698 | namespaces_event = malloc(sizeof(namespaces_event->namespaces) + | ||
| 699 | (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + | ||
| 700 | machine->id_hdr_size); | ||
| 701 | if (namespaces_event == NULL) | ||
| 702 | goto out_free_fork; | ||
| 703 | |||
| 607 | snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir); | 704 | snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir); |
| 608 | proc = opendir(proc_path); | 705 | proc = opendir(proc_path); |
| 609 | 706 | ||
| 610 | if (proc == NULL) | 707 | if (proc == NULL) |
| 611 | goto out_free_fork; | 708 | goto out_free_namespaces; |
| 612 | 709 | ||
| 613 | while ((dirent = readdir(proc)) != NULL) { | 710 | while ((dirent = readdir(proc)) != NULL) { |
| 614 | char *end; | 711 | char *end; |
| @@ -620,13 +717,16 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
| 620 | * We may race with exiting thread, so don't stop just because | 717 | * We may race with exiting thread, so don't stop just because |
| 621 | * one thread couldn't be synthesized. | 718 | * one thread couldn't be synthesized. |
| 622 | */ | 719 | */ |
| 623 | __event__synthesize_thread(comm_event, mmap_event, fork_event, pid, | 720 | __event__synthesize_thread(comm_event, mmap_event, fork_event, |
| 624 | 1, process, tool, machine, mmap_data, | 721 | namespaces_event, pid, 1, process, |
| 722 | tool, machine, mmap_data, | ||
| 625 | proc_map_timeout); | 723 | proc_map_timeout); |
| 626 | } | 724 | } |
| 627 | 725 | ||
| 628 | err = 0; | 726 | err = 0; |
| 629 | closedir(proc); | 727 | closedir(proc); |
| 728 | out_free_namespaces: | ||
| 729 | free(namespaces_event); | ||
| 630 | out_free_fork: | 730 | out_free_fork: |
| 631 | free(fork_event); | 731 | free(fork_event); |
| 632 | out_free_mmap: | 732 | out_free_mmap: |
| @@ -1008,6 +1108,33 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) | |||
| 1008 | return fprintf(fp, "%s: %s:%d/%d\n", s, event->comm.comm, event->comm.pid, event->comm.tid); | 1108 | return fprintf(fp, "%s: %s:%d/%d\n", s, event->comm.comm, event->comm.pid, event->comm.tid); |
| 1009 | } | 1109 | } |
| 1010 | 1110 | ||
| 1111 | size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp) | ||
| 1112 | { | ||
| 1113 | size_t ret = 0; | ||
| 1114 | struct perf_ns_link_info *ns_link_info; | ||
| 1115 | u32 nr_namespaces, idx; | ||
| 1116 | |||
| 1117 | ns_link_info = event->namespaces.link_info; | ||
| 1118 | nr_namespaces = event->namespaces.nr_namespaces; | ||
| 1119 | |||
| 1120 | ret += fprintf(fp, " %d/%d - nr_namespaces: %u\n\t\t[", | ||
| 1121 | event->namespaces.pid, | ||
| 1122 | event->namespaces.tid, | ||
| 1123 | nr_namespaces); | ||
| 1124 | |||
| 1125 | for (idx = 0; idx < nr_namespaces; idx++) { | ||
| 1126 | if (idx && (idx % 4 == 0)) | ||
| 1127 | ret += fprintf(fp, "\n\t\t "); | ||
| 1128 | |||
| 1129 | ret += fprintf(fp, "%u/%s: %" PRIu64 "/%#" PRIx64 "%s", idx, | ||
| 1130 | perf_ns__name(idx), (u64)ns_link_info[idx].dev, | ||
| 1131 | (u64)ns_link_info[idx].ino, | ||
| 1132 | ((idx + 1) != nr_namespaces) ? ", " : "]\n"); | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | return ret; | ||
| 1136 | } | ||
| 1137 | |||
| 1011 | int perf_event__process_comm(struct perf_tool *tool __maybe_unused, | 1138 | int perf_event__process_comm(struct perf_tool *tool __maybe_unused, |
| 1012 | union perf_event *event, | 1139 | union perf_event *event, |
| 1013 | struct perf_sample *sample, | 1140 | struct perf_sample *sample, |
| @@ -1016,6 +1143,14 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused, | |||
| 1016 | return machine__process_comm_event(machine, event, sample); | 1143 | return machine__process_comm_event(machine, event, sample); |
| 1017 | } | 1144 | } |
| 1018 | 1145 | ||
| 1146 | int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused, | ||
| 1147 | union perf_event *event, | ||
| 1148 | struct perf_sample *sample, | ||
| 1149 | struct machine *machine) | ||
| 1150 | { | ||
| 1151 | return machine__process_namespaces_event(machine, event, sample); | ||
| 1152 | } | ||
| 1153 | |||
| 1019 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, | 1154 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, |
| 1020 | union perf_event *event, | 1155 | union perf_event *event, |
| 1021 | struct perf_sample *sample, | 1156 | struct perf_sample *sample, |
| @@ -1196,6 +1331,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) | |||
| 1196 | case PERF_RECORD_MMAP: | 1331 | case PERF_RECORD_MMAP: |
| 1197 | ret += perf_event__fprintf_mmap(event, fp); | 1332 | ret += perf_event__fprintf_mmap(event, fp); |
| 1198 | break; | 1333 | break; |
| 1334 | case PERF_RECORD_NAMESPACES: | ||
| 1335 | ret += perf_event__fprintf_namespaces(event, fp); | ||
| 1336 | break; | ||
| 1199 | case PERF_RECORD_MMAP2: | 1337 | case PERF_RECORD_MMAP2: |
| 1200 | ret += perf_event__fprintf_mmap2(event, fp); | 1338 | ret += perf_event__fprintf_mmap2(event, fp); |
| 1201 | break; | 1339 | break; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c735c53a26f8..e1d8166ebbd5 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -39,6 +39,13 @@ struct comm_event { | |||
| 39 | char comm[16]; | 39 | char comm[16]; |
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | struct namespaces_event { | ||
| 43 | struct perf_event_header header; | ||
| 44 | u32 pid, tid; | ||
| 45 | u64 nr_namespaces; | ||
| 46 | struct perf_ns_link_info link_info[]; | ||
| 47 | }; | ||
| 48 | |||
| 42 | struct fork_event { | 49 | struct fork_event { |
| 43 | struct perf_event_header header; | 50 | struct perf_event_header header; |
| 44 | u32 pid, ppid; | 51 | u32 pid, ppid; |
| @@ -485,6 +492,7 @@ union perf_event { | |||
| 485 | struct mmap_event mmap; | 492 | struct mmap_event mmap; |
| 486 | struct mmap2_event mmap2; | 493 | struct mmap2_event mmap2; |
| 487 | struct comm_event comm; | 494 | struct comm_event comm; |
| 495 | struct namespaces_event namespaces; | ||
| 488 | struct fork_event fork; | 496 | struct fork_event fork; |
| 489 | struct lost_event lost; | 497 | struct lost_event lost; |
| 490 | struct lost_samples_event lost_samples; | 498 | struct lost_samples_event lost_samples; |
| @@ -587,6 +595,10 @@ int perf_event__process_switch(struct perf_tool *tool, | |||
| 587 | union perf_event *event, | 595 | union perf_event *event, |
| 588 | struct perf_sample *sample, | 596 | struct perf_sample *sample, |
| 589 | struct machine *machine); | 597 | struct machine *machine); |
| 598 | int perf_event__process_namespaces(struct perf_tool *tool, | ||
| 599 | union perf_event *event, | ||
| 600 | struct perf_sample *sample, | ||
| 601 | struct machine *machine); | ||
| 590 | int perf_event__process_mmap(struct perf_tool *tool, | 602 | int perf_event__process_mmap(struct perf_tool *tool, |
| 591 | union perf_event *event, | 603 | union perf_event *event, |
| 592 | struct perf_sample *sample, | 604 | struct perf_sample *sample, |
| @@ -636,6 +648,12 @@ pid_t perf_event__synthesize_comm(struct perf_tool *tool, | |||
| 636 | perf_event__handler_t process, | 648 | perf_event__handler_t process, |
| 637 | struct machine *machine); | 649 | struct machine *machine); |
| 638 | 650 | ||
| 651 | int perf_event__synthesize_namespaces(struct perf_tool *tool, | ||
| 652 | union perf_event *event, | ||
| 653 | pid_t pid, pid_t tgid, | ||
| 654 | perf_event__handler_t process, | ||
| 655 | struct machine *machine); | ||
| 656 | |||
| 639 | int perf_event__synthesize_mmap_events(struct perf_tool *tool, | 657 | int perf_event__synthesize_mmap_events(struct perf_tool *tool, |
| 640 | union perf_event *event, | 658 | union perf_event *event, |
| 641 | pid_t pid, pid_t tgid, | 659 | pid_t pid, pid_t tgid, |
| @@ -653,6 +671,7 @@ size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); | |||
| 653 | size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp); | 671 | size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp); |
| 654 | size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp); | 672 | size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp); |
| 655 | size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp); | 673 | size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp); |
| 674 | size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp); | ||
| 656 | size_t perf_event__fprintf(union perf_event *event, FILE *fp); | 675 | size_t perf_event__fprintf(union perf_event *event, FILE *fp); |
| 657 | 676 | ||
| 658 | u64 kallsyms__get_function_start(const char *kallsyms_filename, | 677 | u64 kallsyms__get_function_start(const char *kallsyms_filename, |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ac59710b79e0..175dc2305aa8 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -932,6 +932,9 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
| 932 | attr->mmap2 = track && !perf_missing_features.mmap2; | 932 | attr->mmap2 = track && !perf_missing_features.mmap2; |
| 933 | attr->comm = track; | 933 | attr->comm = track; |
| 934 | 934 | ||
| 935 | if (opts->record_namespaces) | ||
| 936 | attr->namespaces = track; | ||
| 937 | |||
| 935 | if (opts->record_switch_events) | 938 | if (opts->record_switch_events) |
| 936 | attr->context_switch = track; | 939 | attr->context_switch = track; |
| 937 | 940 | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index eaf72a938fb4..e3b38f629504 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include "hist.h" | 3 | #include "hist.h" |
| 4 | #include "map.h" | 4 | #include "map.h" |
| 5 | #include "session.h" | 5 | #include "session.h" |
| 6 | #include "namespaces.h" | ||
| 6 | #include "sort.h" | 7 | #include "sort.h" |
| 7 | #include "evlist.h" | 8 | #include "evlist.h" |
| 8 | #include "evsel.h" | 9 | #include "evsel.h" |
| @@ -169,6 +170,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
| 169 | hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); | 170 | hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); |
| 170 | } | 171 | } |
| 171 | 172 | ||
| 173 | hists__new_col_len(hists, HISTC_CGROUP_ID, 20); | ||
| 172 | hists__new_col_len(hists, HISTC_CPU, 3); | 174 | hists__new_col_len(hists, HISTC_CPU, 3); |
| 173 | hists__new_col_len(hists, HISTC_SOCKET, 6); | 175 | hists__new_col_len(hists, HISTC_SOCKET, 6); |
| 174 | hists__new_col_len(hists, HISTC_MEM_LOCKED, 6); | 176 | hists__new_col_len(hists, HISTC_MEM_LOCKED, 6); |
| @@ -574,9 +576,14 @@ __hists__add_entry(struct hists *hists, | |||
| 574 | bool sample_self, | 576 | bool sample_self, |
| 575 | struct hist_entry_ops *ops) | 577 | struct hist_entry_ops *ops) |
| 576 | { | 578 | { |
| 579 | struct namespaces *ns = thread__namespaces(al->thread); | ||
| 577 | struct hist_entry entry = { | 580 | struct hist_entry entry = { |
| 578 | .thread = al->thread, | 581 | .thread = al->thread, |
| 579 | .comm = thread__comm(al->thread), | 582 | .comm = thread__comm(al->thread), |
| 583 | .cgroup_id = { | ||
| 584 | .dev = ns ? ns->link_info[CGROUP_NS_INDEX].dev : 0, | ||
| 585 | .ino = ns ? ns->link_info[CGROUP_NS_INDEX].ino : 0, | ||
| 586 | }, | ||
| 580 | .ms = { | 587 | .ms = { |
| 581 | .map = al->map, | 588 | .map = al->map, |
| 582 | .sym = al->sym, | 589 | .sym = al->sym, |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 2e839bf40bdd..ee3670a388df 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -30,6 +30,7 @@ enum hist_column { | |||
| 30 | HISTC_DSO, | 30 | HISTC_DSO, |
| 31 | HISTC_THREAD, | 31 | HISTC_THREAD, |
| 32 | HISTC_COMM, | 32 | HISTC_COMM, |
| 33 | HISTC_CGROUP_ID, | ||
| 33 | HISTC_PARENT, | 34 | HISTC_PARENT, |
| 34 | HISTC_CPU, | 35 | HISTC_CPU, |
| 35 | HISTC_SOCKET, | 36 | HISTC_SOCKET, |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index b9974fe41bc1..dfc600446586 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <symbol/kallsyms.h> | 13 | #include <symbol/kallsyms.h> |
| 14 | #include "unwind.h" | 14 | #include "unwind.h" |
| 15 | #include "linux/hash.h" | 15 | #include "linux/hash.h" |
| 16 | #include "asm/bug.h" | ||
| 16 | 17 | ||
| 17 | static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock); | 18 | static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock); |
| 18 | 19 | ||
| @@ -501,6 +502,37 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event | |||
| 501 | return err; | 502 | return err; |
| 502 | } | 503 | } |
| 503 | 504 | ||
| 505 | int machine__process_namespaces_event(struct machine *machine __maybe_unused, | ||
| 506 | union perf_event *event, | ||
| 507 | struct perf_sample *sample __maybe_unused) | ||
| 508 | { | ||
| 509 | struct thread *thread = machine__findnew_thread(machine, | ||
| 510 | event->namespaces.pid, | ||
| 511 | event->namespaces.tid); | ||
| 512 | int err = 0; | ||
| 513 | |||
| 514 | WARN_ONCE(event->namespaces.nr_namespaces > NR_NAMESPACES, | ||
| 515 | "\nWARNING: kernel seems to support more namespaces than perf" | ||
| 516 | " tool.\nTry updating the perf tool..\n\n"); | ||
| 517 | |||
| 518 | WARN_ONCE(event->namespaces.nr_namespaces < NR_NAMESPACES, | ||
| 519 | "\nWARNING: perf tool seems to support more namespaces than" | ||
| 520 | " the kernel.\nTry updating the kernel..\n\n"); | ||
| 521 | |||
| 522 | if (dump_trace) | ||
| 523 | perf_event__fprintf_namespaces(event, stdout); | ||
| 524 | |||
| 525 | if (thread == NULL || | ||
| 526 | thread__set_namespaces(thread, sample->time, &event->namespaces)) { | ||
| 527 | dump_printf("problem processing PERF_RECORD_NAMESPACES, skipping event.\n"); | ||
| 528 | err = -1; | ||
| 529 | } | ||
| 530 | |||
| 531 | thread__put(thread); | ||
| 532 | |||
| 533 | return err; | ||
| 534 | } | ||
| 535 | |||
| 504 | int machine__process_lost_event(struct machine *machine __maybe_unused, | 536 | int machine__process_lost_event(struct machine *machine __maybe_unused, |
| 505 | union perf_event *event, struct perf_sample *sample __maybe_unused) | 537 | union perf_event *event, struct perf_sample *sample __maybe_unused) |
| 506 | { | 538 | { |
| @@ -1538,6 +1570,8 @@ int machine__process_event(struct machine *machine, union perf_event *event, | |||
| 1538 | ret = machine__process_comm_event(machine, event, sample); break; | 1570 | ret = machine__process_comm_event(machine, event, sample); break; |
| 1539 | case PERF_RECORD_MMAP: | 1571 | case PERF_RECORD_MMAP: |
| 1540 | ret = machine__process_mmap_event(machine, event, sample); break; | 1572 | ret = machine__process_mmap_event(machine, event, sample); break; |
| 1573 | case PERF_RECORD_NAMESPACES: | ||
| 1574 | ret = machine__process_namespaces_event(machine, event, sample); break; | ||
| 1541 | case PERF_RECORD_MMAP2: | 1575 | case PERF_RECORD_MMAP2: |
| 1542 | ret = machine__process_mmap2_event(machine, event, sample); break; | 1576 | ret = machine__process_mmap2_event(machine, event, sample); break; |
| 1543 | case PERF_RECORD_FORK: | 1577 | case PERF_RECORD_FORK: |
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index a28305029711..3cdb1340f917 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
| @@ -97,6 +97,9 @@ int machine__process_itrace_start_event(struct machine *machine, | |||
| 97 | union perf_event *event); | 97 | union perf_event *event); |
| 98 | int machine__process_switch_event(struct machine *machine, | 98 | int machine__process_switch_event(struct machine *machine, |
| 99 | union perf_event *event); | 99 | union perf_event *event); |
| 100 | int machine__process_namespaces_event(struct machine *machine, | ||
| 101 | union perf_event *event, | ||
| 102 | struct perf_sample *sample); | ||
| 100 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, | 103 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, |
| 101 | struct perf_sample *sample); | 104 | struct perf_sample *sample); |
| 102 | int machine__process_mmap2_event(struct machine *machine, union perf_event *event, | 105 | int machine__process_mmap2_event(struct machine *machine, union perf_event *event, |
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c new file mode 100644 index 000000000000..2de8da64d90c --- /dev/null +++ b/tools/perf/util/namespaces.c | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * This program is free software; you can redistribute it and/or modify | ||
| 3 | * it under the terms of the GNU General Public License, version 2, as | ||
| 4 | * published by the Free Software Foundation. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2017 Hari Bathini, IBM Corporation | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "namespaces.h" | ||
| 10 | #include "util.h" | ||
| 11 | #include "event.h" | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <stdio.h> | ||
| 14 | |||
| 15 | struct namespaces *namespaces__new(struct namespaces_event *event) | ||
| 16 | { | ||
| 17 | struct namespaces *namespaces; | ||
| 18 | u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) * | ||
| 19 | sizeof(struct perf_ns_link_info)); | ||
| 20 | |||
| 21 | namespaces = zalloc(sizeof(struct namespaces) + link_info_size); | ||
| 22 | if (!namespaces) | ||
| 23 | return NULL; | ||
| 24 | |||
| 25 | namespaces->end_time = -1; | ||
| 26 | |||
| 27 | if (event) | ||
| 28 | memcpy(namespaces->link_info, event->link_info, link_info_size); | ||
| 29 | |||
| 30 | return namespaces; | ||
| 31 | } | ||
| 32 | |||
| 33 | void namespaces__free(struct namespaces *namespaces) | ||
| 34 | { | ||
| 35 | free(namespaces); | ||
| 36 | } | ||
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h new file mode 100644 index 000000000000..468f1e9a1484 --- /dev/null +++ b/tools/perf/util/namespaces.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | /* | ||
| 2 | * This program is free software; you can redistribute it and/or modify | ||
| 3 | * it under the terms of the GNU General Public License, version 2, as | ||
| 4 | * published by the Free Software Foundation. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2017 Hari Bathini, IBM Corporation | ||
| 7 | */ | ||
| 8 | |||
| 9 | #ifndef __PERF_NAMESPACES_H | ||
| 10 | #define __PERF_NAMESPACES_H | ||
| 11 | |||
| 12 | #include "../perf.h" | ||
| 13 | #include <linux/list.h> | ||
| 14 | |||
| 15 | struct namespaces_event; | ||
| 16 | |||
| 17 | struct namespaces { | ||
| 18 | struct list_head list; | ||
| 19 | u64 end_time; | ||
| 20 | struct perf_ns_link_info link_info[]; | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct namespaces *namespaces__new(struct namespaces_event *event); | ||
| 24 | void namespaces__free(struct namespaces *namespaces); | ||
| 25 | |||
| 26 | #endif /* __PERF_NAMESPACES_H */ | ||
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 28fb62c32678..c9bdc9ded0c3 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -757,7 +757,9 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs, | |||
| 757 | } | 757 | } |
| 758 | 758 | ||
| 759 | for (i = 0; i < ntevs; i++) { | 759 | for (i = 0; i < ntevs; i++) { |
| 760 | if (!tevs[i].point.address || tevs[i].point.retprobe) | 760 | if (!tevs[i].point.address) |
| 761 | continue; | ||
| 762 | if (tevs[i].point.retprobe && !kretprobe_offset_is_supported()) | ||
| 761 | continue; | 763 | continue; |
| 762 | /* If we found a wrong one, mark it by NULL symbol */ | 764 | /* If we found a wrong one, mark it by NULL symbol */ |
| 763 | if (kprobe_warn_out_range(tevs[i].point.symbol, | 765 | if (kprobe_warn_out_range(tevs[i].point.symbol, |
| @@ -1528,11 +1530,6 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
| 1528 | return -EINVAL; | 1530 | return -EINVAL; |
| 1529 | } | 1531 | } |
| 1530 | 1532 | ||
| 1531 | if (pp->retprobe && !pp->function) { | ||
| 1532 | semantic_error("Return probe requires an entry function.\n"); | ||
| 1533 | return -EINVAL; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { | 1533 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { |
| 1537 | semantic_error("Offset/Line/Lazy pattern can't be used with " | 1534 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
| 1538 | "return probe.\n"); | 1535 | "return probe.\n"); |
| @@ -2841,7 +2838,8 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, | |||
| 2841 | } | 2838 | } |
| 2842 | 2839 | ||
| 2843 | /* Note that the symbols in the kmodule are not relocated */ | 2840 | /* Note that the symbols in the kmodule are not relocated */ |
| 2844 | if (!pev->uprobes && !pp->retprobe && !pev->target) { | 2841 | if (!pev->uprobes && !pev->target && |
| 2842 | (!pp->retprobe || kretprobe_offset_is_supported())) { | ||
| 2845 | reloc_sym = kernel_get_ref_reloc_sym(); | 2843 | reloc_sym = kernel_get_ref_reloc_sym(); |
| 2846 | if (!reloc_sym) { | 2844 | if (!reloc_sym) { |
| 2847 | pr_warning("Relocated base symbol is not found!\n"); | 2845 | pr_warning("Relocated base symbol is not found!\n"); |
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 1a62daceb028..1542cd0d6799 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c | |||
| @@ -877,35 +877,33 @@ int probe_cache__show_all_caches(struct strfilter *filter) | |||
| 877 | return 0; | 877 | return 0; |
| 878 | } | 878 | } |
| 879 | 879 | ||
| 880 | enum ftrace_readme { | ||
| 881 | FTRACE_README_PROBE_TYPE_X = 0, | ||
| 882 | FTRACE_README_KRETPROBE_OFFSET, | ||
| 883 | FTRACE_README_END, | ||
| 884 | }; | ||
| 885 | |||
| 880 | static struct { | 886 | static struct { |
| 881 | const char *pattern; | 887 | const char *pattern; |
| 882 | bool avail; | 888 | bool avail; |
| 883 | bool checked; | 889 | } ftrace_readme_table[] = { |
| 884 | } probe_type_table[] = { | 890 | #define DEFINE_TYPE(idx, pat) \ |
| 885 | #define DEFINE_TYPE(idx, pat, def_avail) \ | 891 | [idx] = {.pattern = pat, .avail = false} |
| 886 | [idx] = {.pattern = pat, .avail = (def_avail)} | 892 | DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"), |
| 887 | DEFINE_TYPE(PROBE_TYPE_U, "* u8/16/32/64,*", true), | 893 | DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"), |
| 888 | DEFINE_TYPE(PROBE_TYPE_S, "* s8/16/32/64,*", true), | ||
| 889 | DEFINE_TYPE(PROBE_TYPE_X, "* x8/16/32/64,*", false), | ||
| 890 | DEFINE_TYPE(PROBE_TYPE_STRING, "* string,*", true), | ||
| 891 | DEFINE_TYPE(PROBE_TYPE_BITFIELD, | ||
| 892 | "* b<bit-width>@<bit-offset>/<container-size>", true), | ||
| 893 | }; | 894 | }; |
| 894 | 895 | ||
| 895 | bool probe_type_is_available(enum probe_type type) | 896 | static bool scan_ftrace_readme(enum ftrace_readme type) |
| 896 | { | 897 | { |
| 898 | int fd; | ||
| 897 | FILE *fp; | 899 | FILE *fp; |
| 898 | char *buf = NULL; | 900 | char *buf = NULL; |
| 899 | size_t len = 0; | 901 | size_t len = 0; |
| 900 | bool target_line = false; | 902 | bool ret = false; |
| 901 | bool ret = probe_type_table[type].avail; | 903 | static bool scanned = false; |
| 902 | int fd; | ||
| 903 | 904 | ||
| 904 | if (type >= PROBE_TYPE_END) | 905 | if (scanned) |
| 905 | return false; | 906 | goto result; |
| 906 | /* We don't have to check the type which supported by default */ | ||
| 907 | if (ret || probe_type_table[type].checked) | ||
| 908 | return ret; | ||
| 909 | 907 | ||
| 910 | fd = open_trace_file("README", false); | 908 | fd = open_trace_file("README", false); |
| 911 | if (fd < 0) | 909 | if (fd < 0) |
| @@ -917,21 +915,34 @@ bool probe_type_is_available(enum probe_type type) | |||
| 917 | return ret; | 915 | return ret; |
| 918 | } | 916 | } |
| 919 | 917 | ||
| 920 | while (getline(&buf, &len, fp) > 0 && !ret) { | 918 | while (getline(&buf, &len, fp) > 0) |
| 921 | if (!target_line) { | 919 | for (enum ftrace_readme i = 0; i < FTRACE_README_END; i++) |
| 922 | target_line = !!strstr(buf, " type: "); | 920 | if (!ftrace_readme_table[i].avail) |
| 923 | if (!target_line) | 921 | ftrace_readme_table[i].avail = |
| 924 | continue; | 922 | strglobmatch(buf, ftrace_readme_table[i].pattern); |
| 925 | } else if (strstr(buf, "\t ") != buf) | 923 | scanned = true; |
| 926 | break; | ||
| 927 | ret = strglobmatch(buf, probe_type_table[type].pattern); | ||
| 928 | } | ||
| 929 | /* Cache the result */ | ||
| 930 | probe_type_table[type].checked = true; | ||
| 931 | probe_type_table[type].avail = ret; | ||
| 932 | 924 | ||
| 933 | fclose(fp); | 925 | fclose(fp); |
| 934 | free(buf); | 926 | free(buf); |
| 935 | 927 | ||
| 936 | return ret; | 928 | result: |
| 929 | if (type >= FTRACE_README_END) | ||
| 930 | return false; | ||
| 931 | |||
| 932 | return ftrace_readme_table[type].avail; | ||
| 933 | } | ||
| 934 | |||
| 935 | bool probe_type_is_available(enum probe_type type) | ||
| 936 | { | ||
| 937 | if (type >= PROBE_TYPE_END) | ||
| 938 | return false; | ||
| 939 | else if (type == PROBE_TYPE_X) | ||
| 940 | return scan_ftrace_readme(FTRACE_README_PROBE_TYPE_X); | ||
| 941 | |||
| 942 | return true; | ||
| 943 | } | ||
| 944 | |||
| 945 | bool kretprobe_offset_is_supported(void) | ||
| 946 | { | ||
| 947 | return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET); | ||
| 937 | } | 948 | } |
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index a17a82eff8a0..dbf95a00864a 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h | |||
| @@ -65,6 +65,7 @@ struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache, | |||
| 65 | const char *group, const char *event); | 65 | const char *group, const char *event); |
| 66 | int probe_cache__show_all_caches(struct strfilter *filter); | 66 | int probe_cache__show_all_caches(struct strfilter *filter); |
| 67 | bool probe_type_is_available(enum probe_type type); | 67 | bool probe_type_is_available(enum probe_type type); |
| 68 | bool kretprobe_offset_is_supported(void); | ||
| 68 | #else /* ! HAVE_LIBELF_SUPPORT */ | 69 | #else /* ! HAVE_LIBELF_SUPPORT */ |
| 69 | static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused) | 70 | static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused) |
| 70 | { | 71 | { |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1dd617d116b5..ae42e742d461 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -1239,6 +1239,8 @@ static int machines__deliver_event(struct machines *machines, | |||
| 1239 | return tool->mmap2(tool, event, sample, machine); | 1239 | return tool->mmap2(tool, event, sample, machine); |
| 1240 | case PERF_RECORD_COMM: | 1240 | case PERF_RECORD_COMM: |
| 1241 | return tool->comm(tool, event, sample, machine); | 1241 | return tool->comm(tool, event, sample, machine); |
| 1242 | case PERF_RECORD_NAMESPACES: | ||
| 1243 | return tool->namespaces(tool, event, sample, machine); | ||
| 1242 | case PERF_RECORD_FORK: | 1244 | case PERF_RECORD_FORK: |
| 1243 | return tool->fork(tool, event, sample, machine); | 1245 | return tool->fork(tool, event, sample, machine); |
| 1244 | case PERF_RECORD_EXIT: | 1246 | case PERF_RECORD_EXIT: |
| @@ -1494,6 +1496,11 @@ int perf_session__register_idle_thread(struct perf_session *session) | |||
| 1494 | err = -1; | 1496 | err = -1; |
| 1495 | } | 1497 | } |
| 1496 | 1498 | ||
| 1499 | if (thread == NULL || thread__set_namespaces(thread, 0, NULL)) { | ||
| 1500 | pr_err("problem inserting idle task.\n"); | ||
| 1501 | err = -1; | ||
| 1502 | } | ||
| 1503 | |||
| 1497 | /* machine__findnew_thread() got the thread, so put it */ | 1504 | /* machine__findnew_thread() got the thread, so put it */ |
| 1498 | thread__put(thread); | 1505 | thread__put(thread); |
| 1499 | return err; | 1506 | return err; |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index f8f16c0e20b6..8b0d4e39f640 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -536,6 +536,46 @@ struct sort_entry sort_cpu = { | |||
| 536 | .se_width_idx = HISTC_CPU, | 536 | .se_width_idx = HISTC_CPU, |
| 537 | }; | 537 | }; |
| 538 | 538 | ||
| 539 | /* --sort cgroup_id */ | ||
| 540 | |||
| 541 | static int64_t _sort__cgroup_dev_cmp(u64 left_dev, u64 right_dev) | ||
| 542 | { | ||
| 543 | return (int64_t)(right_dev - left_dev); | ||
| 544 | } | ||
| 545 | |||
| 546 | static int64_t _sort__cgroup_inode_cmp(u64 left_ino, u64 right_ino) | ||
| 547 | { | ||
| 548 | return (int64_t)(right_ino - left_ino); | ||
| 549 | } | ||
| 550 | |||
| 551 | static int64_t | ||
| 552 | sort__cgroup_id_cmp(struct hist_entry *left, struct hist_entry *right) | ||
| 553 | { | ||
| 554 | int64_t ret; | ||
| 555 | |||
| 556 | ret = _sort__cgroup_dev_cmp(right->cgroup_id.dev, left->cgroup_id.dev); | ||
| 557 | if (ret != 0) | ||
| 558 | return ret; | ||
| 559 | |||
| 560 | return _sort__cgroup_inode_cmp(right->cgroup_id.ino, | ||
| 561 | left->cgroup_id.ino); | ||
| 562 | } | ||
| 563 | |||
| 564 | static int hist_entry__cgroup_id_snprintf(struct hist_entry *he, | ||
| 565 | char *bf, size_t size, | ||
| 566 | unsigned int width __maybe_unused) | ||
| 567 | { | ||
| 568 | return repsep_snprintf(bf, size, "%lu/0x%lx", he->cgroup_id.dev, | ||
| 569 | he->cgroup_id.ino); | ||
| 570 | } | ||
| 571 | |||
| 572 | struct sort_entry sort_cgroup_id = { | ||
| 573 | .se_header = "cgroup id (dev/inode)", | ||
| 574 | .se_cmp = sort__cgroup_id_cmp, | ||
| 575 | .se_snprintf = hist_entry__cgroup_id_snprintf, | ||
| 576 | .se_width_idx = HISTC_CGROUP_ID, | ||
| 577 | }; | ||
| 578 | |||
| 539 | /* --sort socket */ | 579 | /* --sort socket */ |
| 540 | 580 | ||
| 541 | static int64_t | 581 | static int64_t |
| @@ -846,6 +886,9 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, | |||
| 846 | static int64_t | 886 | static int64_t |
| 847 | sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) | 887 | sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) |
| 848 | { | 888 | { |
| 889 | if (!left->branch_info || !right->branch_info) | ||
| 890 | return cmp_null(left->branch_info, right->branch_info); | ||
| 891 | |||
| 849 | return left->branch_info->flags.cycles - | 892 | return left->branch_info->flags.cycles - |
| 850 | right->branch_info->flags.cycles; | 893 | right->branch_info->flags.cycles; |
| 851 | } | 894 | } |
| @@ -853,6 +896,8 @@ sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 853 | static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf, | 896 | static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf, |
| 854 | size_t size, unsigned int width) | 897 | size_t size, unsigned int width) |
| 855 | { | 898 | { |
| 899 | if (!he->branch_info) | ||
| 900 | return scnprintf(bf, size, "%-.*s", width, "N/A"); | ||
| 856 | if (he->branch_info->flags.cycles == 0) | 901 | if (he->branch_info->flags.cycles == 0) |
| 857 | return repsep_snprintf(bf, size, "%-*s", width, "-"); | 902 | return repsep_snprintf(bf, size, "%-*s", width, "-"); |
| 858 | return repsep_snprintf(bf, size, "%-*hd", width, | 903 | return repsep_snprintf(bf, size, "%-*hd", width, |
| @@ -1459,6 +1504,7 @@ static struct sort_dimension common_sort_dimensions[] = { | |||
| 1459 | DIM(SORT_TRANSACTION, "transaction", sort_transaction), | 1504 | DIM(SORT_TRANSACTION, "transaction", sort_transaction), |
| 1460 | DIM(SORT_TRACE, "trace", sort_trace), | 1505 | DIM(SORT_TRACE, "trace", sort_trace), |
| 1461 | DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size), | 1506 | DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size), |
| 1507 | DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id), | ||
| 1462 | }; | 1508 | }; |
| 1463 | 1509 | ||
| 1464 | #undef DIM | 1510 | #undef DIM |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index f583325a3743..baf20a399f34 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
| @@ -54,6 +54,11 @@ struct he_stat { | |||
| 54 | u32 nr_events; | 54 | u32 nr_events; |
| 55 | }; | 55 | }; |
| 56 | 56 | ||
| 57 | struct namespace_id { | ||
| 58 | u64 dev; | ||
| 59 | u64 ino; | ||
| 60 | }; | ||
| 61 | |||
| 57 | struct hist_entry_diff { | 62 | struct hist_entry_diff { |
| 58 | bool computed; | 63 | bool computed; |
| 59 | union { | 64 | union { |
| @@ -91,6 +96,7 @@ struct hist_entry { | |||
| 91 | struct map_symbol ms; | 96 | struct map_symbol ms; |
| 92 | struct thread *thread; | 97 | struct thread *thread; |
| 93 | struct comm *comm; | 98 | struct comm *comm; |
| 99 | struct namespace_id cgroup_id; | ||
| 94 | u64 ip; | 100 | u64 ip; |
| 95 | u64 transaction; | 101 | u64 transaction; |
| 96 | s32 socket; | 102 | s32 socket; |
| @@ -212,6 +218,7 @@ enum sort_type { | |||
| 212 | SORT_TRANSACTION, | 218 | SORT_TRANSACTION, |
| 213 | SORT_TRACE, | 219 | SORT_TRACE, |
| 214 | SORT_SYM_SIZE, | 220 | SORT_SYM_SIZE, |
| 221 | SORT_CGROUP_ID, | ||
| 215 | 222 | ||
| 216 | /* branch stack specific sort keys */ | 223 | /* branch stack specific sort keys */ |
| 217 | __SORT_BRANCH_STACK, | 224 | __SORT_BRANCH_STACK, |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 74e79d26b421..dcdb87a5d0a1 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "thread-stack.h" | 7 | #include "thread-stack.h" |
| 8 | #include "util.h" | 8 | #include "util.h" |
| 9 | #include "debug.h" | 9 | #include "debug.h" |
| 10 | #include "namespaces.h" | ||
| 10 | #include "comm.h" | 11 | #include "comm.h" |
| 11 | #include "unwind.h" | 12 | #include "unwind.h" |
| 12 | 13 | ||
| @@ -40,6 +41,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) | |||
| 40 | thread->tid = tid; | 41 | thread->tid = tid; |
| 41 | thread->ppid = -1; | 42 | thread->ppid = -1; |
| 42 | thread->cpu = -1; | 43 | thread->cpu = -1; |
| 44 | INIT_LIST_HEAD(&thread->namespaces_list); | ||
| 43 | INIT_LIST_HEAD(&thread->comm_list); | 45 | INIT_LIST_HEAD(&thread->comm_list); |
| 44 | 46 | ||
| 45 | comm_str = malloc(32); | 47 | comm_str = malloc(32); |
| @@ -66,7 +68,8 @@ err_thread: | |||
| 66 | 68 | ||
| 67 | void thread__delete(struct thread *thread) | 69 | void thread__delete(struct thread *thread) |
| 68 | { | 70 | { |
| 69 | struct comm *comm, *tmp; | 71 | struct namespaces *namespaces, *tmp_namespaces; |
| 72 | struct comm *comm, *tmp_comm; | ||
| 70 | 73 | ||
| 71 | BUG_ON(!RB_EMPTY_NODE(&thread->rb_node)); | 74 | BUG_ON(!RB_EMPTY_NODE(&thread->rb_node)); |
| 72 | 75 | ||
| @@ -76,7 +79,12 @@ void thread__delete(struct thread *thread) | |||
| 76 | map_groups__put(thread->mg); | 79 | map_groups__put(thread->mg); |
| 77 | thread->mg = NULL; | 80 | thread->mg = NULL; |
| 78 | } | 81 | } |
| 79 | list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { | 82 | list_for_each_entry_safe(namespaces, tmp_namespaces, |
| 83 | &thread->namespaces_list, list) { | ||
| 84 | list_del(&namespaces->list); | ||
| 85 | namespaces__free(namespaces); | ||
| 86 | } | ||
| 87 | list_for_each_entry_safe(comm, tmp_comm, &thread->comm_list, list) { | ||
| 80 | list_del(&comm->list); | 88 | list_del(&comm->list); |
| 81 | comm__free(comm); | 89 | comm__free(comm); |
| 82 | } | 90 | } |
| @@ -104,6 +112,38 @@ void thread__put(struct thread *thread) | |||
| 104 | } | 112 | } |
| 105 | } | 113 | } |
| 106 | 114 | ||
| 115 | struct namespaces *thread__namespaces(const struct thread *thread) | ||
| 116 | { | ||
| 117 | if (list_empty(&thread->namespaces_list)) | ||
| 118 | return NULL; | ||
| 119 | |||
| 120 | return list_first_entry(&thread->namespaces_list, struct namespaces, list); | ||
| 121 | } | ||
| 122 | |||
| 123 | int thread__set_namespaces(struct thread *thread, u64 timestamp, | ||
| 124 | struct namespaces_event *event) | ||
| 125 | { | ||
| 126 | struct namespaces *new, *curr = thread__namespaces(thread); | ||
| 127 | |||
| 128 | new = namespaces__new(event); | ||
| 129 | if (!new) | ||
| 130 | return -ENOMEM; | ||
| 131 | |||
| 132 | list_add(&new->list, &thread->namespaces_list); | ||
| 133 | |||
| 134 | if (timestamp && curr) { | ||
| 135 | /* | ||
| 136 | * setns syscall must have changed few or all the namespaces | ||
| 137 | * of this thread. Update end time for the namespaces | ||
| 138 | * previously used. | ||
| 139 | */ | ||
| 140 | curr = list_next_entry(new, list); | ||
| 141 | curr->end_time = timestamp; | ||
| 142 | } | ||
| 143 | |||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | |||
| 107 | struct comm *thread__comm(const struct thread *thread) | 147 | struct comm *thread__comm(const struct thread *thread) |
| 108 | { | 148 | { |
| 109 | if (list_empty(&thread->comm_list)) | 149 | if (list_empty(&thread->comm_list)) |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index e57188546465..4eb849e9098f 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -28,6 +28,7 @@ struct thread { | |||
| 28 | bool comm_set; | 28 | bool comm_set; |
| 29 | int comm_len; | 29 | int comm_len; |
| 30 | bool dead; /* if set thread has exited */ | 30 | bool dead; /* if set thread has exited */ |
| 31 | struct list_head namespaces_list; | ||
| 31 | struct list_head comm_list; | 32 | struct list_head comm_list; |
| 32 | u64 db_id; | 33 | u64 db_id; |
| 33 | 34 | ||
| @@ -40,6 +41,7 @@ struct thread { | |||
| 40 | }; | 41 | }; |
| 41 | 42 | ||
| 42 | struct machine; | 43 | struct machine; |
| 44 | struct namespaces; | ||
| 43 | struct comm; | 45 | struct comm; |
| 44 | 46 | ||
| 45 | struct thread *thread__new(pid_t pid, pid_t tid); | 47 | struct thread *thread__new(pid_t pid, pid_t tid); |
| @@ -62,6 +64,10 @@ static inline void thread__exited(struct thread *thread) | |||
| 62 | thread->dead = true; | 64 | thread->dead = true; |
| 63 | } | 65 | } |
| 64 | 66 | ||
| 67 | struct namespaces *thread__namespaces(const struct thread *thread); | ||
| 68 | int thread__set_namespaces(struct thread *thread, u64 timestamp, | ||
| 69 | struct namespaces_event *event); | ||
| 70 | |||
| 65 | int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp, | 71 | int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp, |
| 66 | bool exec); | 72 | bool exec); |
| 67 | static inline int thread__set_comm(struct thread *thread, const char *comm, | 73 | static inline int thread__set_comm(struct thread *thread, const char *comm, |
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index ac2590a3de2d..829471a1c6d7 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
| @@ -40,6 +40,7 @@ struct perf_tool { | |||
| 40 | event_op mmap, | 40 | event_op mmap, |
| 41 | mmap2, | 41 | mmap2, |
| 42 | comm, | 42 | comm, |
| 43 | namespaces, | ||
| 43 | fork, | 44 | fork, |
| 44 | exit, | 45 | exit, |
| 45 | lost, | 46 | lost, |
| @@ -66,6 +67,7 @@ struct perf_tool { | |||
| 66 | event_op3 auxtrace; | 67 | event_op3 auxtrace; |
| 67 | bool ordered_events; | 68 | bool ordered_events; |
| 68 | bool ordering_requires_timestamps; | 69 | bool ordering_requires_timestamps; |
| 70 | bool namespace_events; | ||
| 69 | }; | 71 | }; |
| 70 | 72 | ||
| 71 | #endif /* __PERF_TOOL_H */ | 73 | #endif /* __PERF_TOOL_H */ |
