perf tools: Add PERF_RECORD_NAMESPACES to include namespaces related info
Introduce a new option to record PERF_RECORD_NAMESPACES events emitted by the kernel when fork, clone, setns or unshare are invoked. And update perf-record documentation with the new option to record namespace events. Committer notes: Combined it with a later patch to allow printing it via 'perf report -D' and be able to test the feature introduced in this patch. Had to move here also perf_ns__name(), that was introduced in another later patch. Also used PRIu64 and PRIx64 to fix the build in some enfironments wrt: util/event.c:1129:39: error: format '%lx' expects argument of type 'long unsigned int', but argument 6 has type 'long long unsigned int' [-Werror=format=] ret += fprintf(fp, "%u/%s: %lu/0x%lx%s", idx ^ Testing it: # perf record --namespaces -a ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 1.083 MB perf.data (423 samples) ] # # perf report -D <SNIP> 3 2028902078892 0x115140 [0xa0]: PERF_RECORD_NAMESPACES 14783/14783 - nr_namespaces: 7 [0/net: 3/0xf0000081, 1/uts: 3/0xeffffffe, 2/ipc: 3/0xefffffff, 3/pid: 3/0xeffffffc, 4/user: 3/0xeffffffd, 5/mnt: 3/0xf0000000, 6/cgroup: 3/0xeffffffb] 0x1151e0 [0x30]: event: 9 . . ... raw event: size 48 bytes . 0000: 09 00 00 00 02 00 30 00 c4 71 82 68 0c 7f 00 00 ......0..q.h.... . 0010: a9 39 00 00 a9 39 00 00 94 28 fe 63 d8 01 00 00 .9...9...(.c.... . 0020: 03 00 00 00 00 00 00 00 ce c4 02 00 00 00 00 00 ................ <SNIP> NAMESPACES events: 1 <SNIP> # Signed-off-by: Hari Bathini <hbathini@linux.vnet.ibm.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com> Cc: Aravinda Prasad <aravinda@linux.vnet.ibm.com> Cc: Brendan Gregg <brendan.d.gregg@gmail.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Eric Biederman <ebiederm@xmission.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Sargun Dhillon <sargun@sargun.me> Cc: Steven Rostedt <rostedt@goodmis.org> Link: http://lkml.kernel.org/r/148891930386.25309.18412039920746995488.stgit@hbathini.in.ibm.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
e422267322
commit
f3b3614a28
@ -344,7 +344,8 @@ struct perf_event_attr {
|
|||||||
use_clockid : 1, /* use @clockid for time fields */
|
use_clockid : 1, /* use @clockid for time fields */
|
||||||
context_switch : 1, /* context switch data */
|
context_switch : 1, /* context switch data */
|
||||||
write_backward : 1, /* Write ring buffer from end to beginning */
|
write_backward : 1, /* Write ring buffer from end to beginning */
|
||||||
__reserved_1 : 36;
|
namespaces : 1, /* include namespaces data */
|
||||||
|
__reserved_1 : 35;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
__u32 wakeup_events; /* wakeup every n events */
|
__u32 wakeup_events; /* wakeup every n events */
|
||||||
@ -610,6 +611,23 @@ struct perf_event_header {
|
|||||||
__u16 size;
|
__u16 size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct perf_ns_link_info {
|
||||||
|
__u64 dev;
|
||||||
|
__u64 ino;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NET_NS_INDEX = 0,
|
||||||
|
UTS_NS_INDEX = 1,
|
||||||
|
IPC_NS_INDEX = 2,
|
||||||
|
PID_NS_INDEX = 3,
|
||||||
|
USER_NS_INDEX = 4,
|
||||||
|
MNT_NS_INDEX = 5,
|
||||||
|
CGROUP_NS_INDEX = 6,
|
||||||
|
|
||||||
|
NR_NAMESPACES, /* number of available namespaces */
|
||||||
|
};
|
||||||
|
|
||||||
enum perf_event_type {
|
enum perf_event_type {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -862,6 +880,18 @@ enum perf_event_type {
|
|||||||
*/
|
*/
|
||||||
PERF_RECORD_SWITCH_CPU_WIDE = 15,
|
PERF_RECORD_SWITCH_CPU_WIDE = 15,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct {
|
||||||
|
* struct perf_event_header header;
|
||||||
|
* u32 pid;
|
||||||
|
* u32 tid;
|
||||||
|
* u64 nr_namespaces;
|
||||||
|
* { u64 dev, inode; } [nr_namespaces];
|
||||||
|
* struct sample_id sample_id;
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
PERF_RECORD_NAMESPACES = 16,
|
||||||
|
|
||||||
PERF_RECORD_MAX, /* non-ABI */
|
PERF_RECORD_MAX, /* non-ABI */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -347,6 +347,9 @@ Enable weightened sampling. An additional weight is recorded per sample and can
|
|||||||
displayed with the weight and local_weight sort keys. This currently works for TSX
|
displayed with the weight and local_weight sort keys. This currently works for TSX
|
||||||
abort events and some memory events in precise mode on modern Intel CPUs.
|
abort events and some memory events in precise mode on modern Intel CPUs.
|
||||||
|
|
||||||
|
--namespaces::
|
||||||
|
Record events of type PERF_RECORD_NAMESPACES.
|
||||||
|
|
||||||
--transaction::
|
--transaction::
|
||||||
Record transaction flags for transaction related events.
|
Record transaction flags for transaction related events.
|
||||||
|
|
||||||
|
@ -393,6 +393,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||||||
.comm = perf_event__process_comm,
|
.comm = perf_event__process_comm,
|
||||||
.exit = perf_event__process_exit,
|
.exit = perf_event__process_exit,
|
||||||
.fork = perf_event__process_fork,
|
.fork = perf_event__process_fork,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
.ordering_requires_timestamps = true,
|
.ordering_requires_timestamps = true,
|
||||||
},
|
},
|
||||||
|
@ -364,6 +364,7 @@ static struct perf_tool tool = {
|
|||||||
.exit = perf_event__process_exit,
|
.exit = perf_event__process_exit,
|
||||||
.fork = perf_event__process_fork,
|
.fork = perf_event__process_fork,
|
||||||
.lost = perf_event__process_lost,
|
.lost = perf_event__process_lost,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
.ordering_requires_timestamps = true,
|
.ordering_requires_timestamps = true,
|
||||||
};
|
};
|
||||||
|
@ -333,6 +333,18 @@ static int perf_event__repipe_comm(struct perf_tool *tool,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int perf_event__repipe_namespaces(struct perf_tool *tool,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
int err = perf_event__process_namespaces(tool, event, sample, machine);
|
||||||
|
|
||||||
|
perf_event__repipe(tool, event, sample, machine);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int perf_event__repipe_exit(struct perf_tool *tool,
|
static int perf_event__repipe_exit(struct perf_tool *tool,
|
||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
@ -660,6 +672,7 @@ static int __cmd_inject(struct perf_inject *inject)
|
|||||||
session->itrace_synth_opts = &inject->itrace_synth_opts;
|
session->itrace_synth_opts = &inject->itrace_synth_opts;
|
||||||
inject->itrace_synth_opts.inject = true;
|
inject->itrace_synth_opts.inject = true;
|
||||||
inject->tool.comm = perf_event__repipe_comm;
|
inject->tool.comm = perf_event__repipe_comm;
|
||||||
|
inject->tool.namespaces = perf_event__repipe_namespaces;
|
||||||
inject->tool.exit = perf_event__repipe_exit;
|
inject->tool.exit = perf_event__repipe_exit;
|
||||||
inject->tool.id_index = perf_event__repipe_id_index;
|
inject->tool.id_index = perf_event__repipe_id_index;
|
||||||
inject->tool.auxtrace_info = perf_event__process_auxtrace_info;
|
inject->tool.auxtrace_info = perf_event__process_auxtrace_info;
|
||||||
|
@ -964,6 +964,7 @@ static struct perf_tool perf_kmem = {
|
|||||||
.comm = perf_event__process_comm,
|
.comm = perf_event__process_comm,
|
||||||
.mmap = perf_event__process_mmap,
|
.mmap = perf_event__process_mmap,
|
||||||
.mmap2 = perf_event__process_mmap2,
|
.mmap2 = perf_event__process_mmap2,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1044,6 +1044,7 @@ static int read_events(struct perf_kvm_stat *kvm)
|
|||||||
struct perf_tool eops = {
|
struct perf_tool eops = {
|
||||||
.sample = process_sample_event,
|
.sample = process_sample_event,
|
||||||
.comm = perf_event__process_comm,
|
.comm = perf_event__process_comm,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
};
|
};
|
||||||
struct perf_data_file file = {
|
struct perf_data_file file = {
|
||||||
@ -1348,6 +1349,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
|||||||
kvm->tool.exit = perf_event__process_exit;
|
kvm->tool.exit = perf_event__process_exit;
|
||||||
kvm->tool.fork = perf_event__process_fork;
|
kvm->tool.fork = perf_event__process_fork;
|
||||||
kvm->tool.lost = process_lost_event;
|
kvm->tool.lost = process_lost_event;
|
||||||
|
kvm->tool.namespaces = perf_event__process_namespaces;
|
||||||
kvm->tool.ordered_events = true;
|
kvm->tool.ordered_events = true;
|
||||||
perf_tool__fill_defaults(&kvm->tool);
|
perf_tool__fill_defaults(&kvm->tool);
|
||||||
|
|
||||||
|
@ -858,6 +858,7 @@ static int __cmd_report(bool display_info)
|
|||||||
struct perf_tool eops = {
|
struct perf_tool eops = {
|
||||||
.sample = process_sample_event,
|
.sample = process_sample_event,
|
||||||
.comm = perf_event__process_comm,
|
.comm = perf_event__process_comm,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
};
|
};
|
||||||
struct perf_data_file file = {
|
struct perf_data_file file = {
|
||||||
|
@ -342,6 +342,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||||||
.lost = perf_event__process_lost,
|
.lost = perf_event__process_lost,
|
||||||
.fork = perf_event__process_fork,
|
.fork = perf_event__process_fork,
|
||||||
.build_id = perf_event__process_build_id,
|
.build_id = perf_event__process_build_id,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
},
|
},
|
||||||
.input_name = "perf.data",
|
.input_name = "perf.data",
|
||||||
|
@ -876,6 +876,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||||||
signal(SIGTERM, sig_handler);
|
signal(SIGTERM, sig_handler);
|
||||||
signal(SIGSEGV, sigsegv_handler);
|
signal(SIGSEGV, sigsegv_handler);
|
||||||
|
|
||||||
|
if (rec->opts.record_namespaces)
|
||||||
|
tool->namespace_events = true;
|
||||||
|
|
||||||
if (rec->opts.auxtrace_snapshot_mode || rec->switch_output.enabled) {
|
if (rec->opts.auxtrace_snapshot_mode || rec->switch_output.enabled) {
|
||||||
signal(SIGUSR2, snapshot_sig_handler);
|
signal(SIGUSR2, snapshot_sig_handler);
|
||||||
if (rec->opts.auxtrace_snapshot_mode)
|
if (rec->opts.auxtrace_snapshot_mode)
|
||||||
@ -1497,6 +1500,7 @@ static struct record record = {
|
|||||||
.fork = perf_event__process_fork,
|
.fork = perf_event__process_fork,
|
||||||
.exit = perf_event__process_exit,
|
.exit = perf_event__process_exit,
|
||||||
.comm = perf_event__process_comm,
|
.comm = perf_event__process_comm,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.mmap = perf_event__process_mmap,
|
.mmap = perf_event__process_mmap,
|
||||||
.mmap2 = perf_event__process_mmap2,
|
.mmap2 = perf_event__process_mmap2,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
@ -1611,6 +1615,8 @@ static struct option __record_options[] = {
|
|||||||
"opts", "AUX area tracing Snapshot Mode", ""),
|
"opts", "AUX area tracing Snapshot Mode", ""),
|
||||||
OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout,
|
OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout,
|
||||||
"per thread proc mmap processing timeout in ms"),
|
"per thread proc mmap processing timeout in ms"),
|
||||||
|
OPT_BOOLEAN(0, "namespaces", &record.opts.record_namespaces,
|
||||||
|
"Record namespaces events"),
|
||||||
OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
|
OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
|
||||||
"Record context switch events"),
|
"Record context switch events"),
|
||||||
OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel,
|
OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel,
|
||||||
|
@ -700,6 +700,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||||||
.mmap = perf_event__process_mmap,
|
.mmap = perf_event__process_mmap,
|
||||||
.mmap2 = perf_event__process_mmap2,
|
.mmap2 = perf_event__process_mmap2,
|
||||||
.comm = perf_event__process_comm,
|
.comm = perf_event__process_comm,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.exit = perf_event__process_exit,
|
.exit = perf_event__process_exit,
|
||||||
.fork = perf_event__process_fork,
|
.fork = perf_event__process_fork,
|
||||||
.lost = perf_event__process_lost,
|
.lost = perf_event__process_lost,
|
||||||
|
@ -3272,6 +3272,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||||||
.tool = {
|
.tool = {
|
||||||
.sample = perf_sched__process_tracepoint_sample,
|
.sample = perf_sched__process_tracepoint_sample,
|
||||||
.comm = perf_event__process_comm,
|
.comm = perf_event__process_comm,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.lost = perf_event__process_lost,
|
.lost = perf_event__process_lost,
|
||||||
.fork = perf_sched__process_fork_event,
|
.fork = perf_sched__process_fork_event,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
|
@ -2097,6 +2097,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||||||
.mmap = perf_event__process_mmap,
|
.mmap = perf_event__process_mmap,
|
||||||
.mmap2 = perf_event__process_mmap2,
|
.mmap2 = perf_event__process_mmap2,
|
||||||
.comm = perf_event__process_comm,
|
.comm = perf_event__process_comm,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.exit = perf_event__process_exit,
|
.exit = perf_event__process_exit,
|
||||||
.fork = perf_event__process_fork,
|
.fork = perf_event__process_fork,
|
||||||
.attr = process_attr,
|
.attr = process_attr,
|
||||||
|
@ -2415,8 +2415,9 @@ static int trace__replay(struct trace *trace)
|
|||||||
trace->tool.exit = perf_event__process_exit;
|
trace->tool.exit = perf_event__process_exit;
|
||||||
trace->tool.fork = perf_event__process_fork;
|
trace->tool.fork = perf_event__process_fork;
|
||||||
trace->tool.attr = perf_event__process_attr;
|
trace->tool.attr = perf_event__process_attr;
|
||||||
trace->tool.tracing_data = perf_event__process_tracing_data;
|
trace->tool.tracing_data = perf_event__process_tracing_data;
|
||||||
trace->tool.build_id = perf_event__process_build_id;
|
trace->tool.build_id = perf_event__process_build_id;
|
||||||
|
trace->tool.namespaces = perf_event__process_namespaces;
|
||||||
|
|
||||||
trace->tool.ordered_events = true;
|
trace->tool.ordered_events = true;
|
||||||
trace->tool.ordering_requires_timestamps = true;
|
trace->tool.ordering_requires_timestamps = true;
|
||||||
|
@ -50,6 +50,7 @@ struct record_opts {
|
|||||||
bool running_time;
|
bool running_time;
|
||||||
bool full_auxtrace;
|
bool full_auxtrace;
|
||||||
bool auxtrace_snapshot_mode;
|
bool auxtrace_snapshot_mode;
|
||||||
|
bool record_namespaces;
|
||||||
bool record_switch_events;
|
bool record_switch_events;
|
||||||
bool all_kernel;
|
bool all_kernel;
|
||||||
bool all_user;
|
bool all_user;
|
||||||
|
@ -42,6 +42,7 @@ libperf-y += pstack.o
|
|||||||
libperf-y += session.o
|
libperf-y += session.o
|
||||||
libperf-$(CONFIG_AUDIT) += syscalltbl.o
|
libperf-$(CONFIG_AUDIT) += syscalltbl.o
|
||||||
libperf-y += ordered-events.o
|
libperf-y += ordered-events.o
|
||||||
|
libperf-y += namespaces.o
|
||||||
libperf-y += comm.o
|
libperf-y += comm.o
|
||||||
libperf-y += thread.o
|
libperf-y += thread.o
|
||||||
libperf-y += thread_map.o
|
libperf-y += thread_map.o
|
||||||
|
@ -1468,6 +1468,7 @@ int bt_convert__perf2ctf(const char *input, const char *path,
|
|||||||
.lost = perf_event__process_lost,
|
.lost = perf_event__process_lost,
|
||||||
.tracing_data = perf_event__process_tracing_data,
|
.tracing_data = perf_event__process_tracing_data,
|
||||||
.build_id = perf_event__process_build_id,
|
.build_id = perf_event__process_build_id,
|
||||||
|
.namespaces = perf_event__process_namespaces,
|
||||||
.ordered_events = true,
|
.ordered_events = true,
|
||||||
.ordering_requires_timestamps = true,
|
.ordering_requires_timestamps = true,
|
||||||
},
|
},
|
||||||
|
@ -31,6 +31,7 @@ static const char *perf_event__names[] = {
|
|||||||
[PERF_RECORD_LOST_SAMPLES] = "LOST_SAMPLES",
|
[PERF_RECORD_LOST_SAMPLES] = "LOST_SAMPLES",
|
||||||
[PERF_RECORD_SWITCH] = "SWITCH",
|
[PERF_RECORD_SWITCH] = "SWITCH",
|
||||||
[PERF_RECORD_SWITCH_CPU_WIDE] = "SWITCH_CPU_WIDE",
|
[PERF_RECORD_SWITCH_CPU_WIDE] = "SWITCH_CPU_WIDE",
|
||||||
|
[PERF_RECORD_NAMESPACES] = "NAMESPACES",
|
||||||
[PERF_RECORD_HEADER_ATTR] = "ATTR",
|
[PERF_RECORD_HEADER_ATTR] = "ATTR",
|
||||||
[PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
|
[PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
|
||||||
[PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
|
[PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
|
||||||
@ -49,6 +50,16 @@ static const char *perf_event__names[] = {
|
|||||||
[PERF_RECORD_TIME_CONV] = "TIME_CONV",
|
[PERF_RECORD_TIME_CONV] = "TIME_CONV",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char *perf_ns__names[] = {
|
||||||
|
[NET_NS_INDEX] = "net",
|
||||||
|
[UTS_NS_INDEX] = "uts",
|
||||||
|
[IPC_NS_INDEX] = "ipc",
|
||||||
|
[PID_NS_INDEX] = "pid",
|
||||||
|
[USER_NS_INDEX] = "user",
|
||||||
|
[MNT_NS_INDEX] = "mnt",
|
||||||
|
[CGROUP_NS_INDEX] = "cgroup",
|
||||||
|
};
|
||||||
|
|
||||||
const char *perf_event__name(unsigned int id)
|
const char *perf_event__name(unsigned int id)
|
||||||
{
|
{
|
||||||
if (id >= ARRAY_SIZE(perf_event__names))
|
if (id >= ARRAY_SIZE(perf_event__names))
|
||||||
@ -58,6 +69,13 @@ const char *perf_event__name(unsigned int id)
|
|||||||
return perf_event__names[id];
|
return perf_event__names[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *perf_ns__name(unsigned int id)
|
||||||
|
{
|
||||||
|
if (id >= ARRAY_SIZE(perf_ns__names))
|
||||||
|
return "UNKNOWN";
|
||||||
|
return perf_ns__names[id];
|
||||||
|
}
|
||||||
|
|
||||||
static int perf_tool__process_synth_event(struct perf_tool *tool,
|
static int perf_tool__process_synth_event(struct perf_tool *tool,
|
||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct machine *machine,
|
struct machine *machine,
|
||||||
@ -1008,6 +1026,33 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)
|
|||||||
return fprintf(fp, "%s: %s:%d/%d\n", s, event->comm.comm, event->comm.pid, event->comm.tid);
|
return fprintf(fp, "%s: %s:%d/%d\n", s, event->comm.comm, event->comm.pid, event->comm.tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp)
|
||||||
|
{
|
||||||
|
size_t ret = 0;
|
||||||
|
struct perf_ns_link_info *ns_link_info;
|
||||||
|
u32 nr_namespaces, idx;
|
||||||
|
|
||||||
|
ns_link_info = event->namespaces.link_info;
|
||||||
|
nr_namespaces = event->namespaces.nr_namespaces;
|
||||||
|
|
||||||
|
ret += fprintf(fp, " %d/%d - nr_namespaces: %u\n\t\t[",
|
||||||
|
event->namespaces.pid,
|
||||||
|
event->namespaces.tid,
|
||||||
|
nr_namespaces);
|
||||||
|
|
||||||
|
for (idx = 0; idx < nr_namespaces; idx++) {
|
||||||
|
if (idx && (idx % 4 == 0))
|
||||||
|
ret += fprintf(fp, "\n\t\t ");
|
||||||
|
|
||||||
|
ret += fprintf(fp, "%u/%s: %" PRIu64 "/%#" PRIx64 "%s", idx,
|
||||||
|
perf_ns__name(idx), (u64)ns_link_info[idx].dev,
|
||||||
|
(u64)ns_link_info[idx].ino,
|
||||||
|
((idx + 1) != nr_namespaces) ? ", " : "]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int perf_event__process_comm(struct perf_tool *tool __maybe_unused,
|
int perf_event__process_comm(struct perf_tool *tool __maybe_unused,
|
||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
@ -1016,6 +1061,14 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused,
|
|||||||
return machine__process_comm_event(machine, event, sample);
|
return machine__process_comm_event(machine, event, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine)
|
||||||
|
{
|
||||||
|
return machine__process_namespaces_event(machine, event, sample);
|
||||||
|
}
|
||||||
|
|
||||||
int perf_event__process_lost(struct perf_tool *tool __maybe_unused,
|
int perf_event__process_lost(struct perf_tool *tool __maybe_unused,
|
||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
@ -1196,6 +1249,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
|||||||
case PERF_RECORD_MMAP:
|
case PERF_RECORD_MMAP:
|
||||||
ret += perf_event__fprintf_mmap(event, fp);
|
ret += perf_event__fprintf_mmap(event, fp);
|
||||||
break;
|
break;
|
||||||
|
case PERF_RECORD_NAMESPACES:
|
||||||
|
ret += perf_event__fprintf_namespaces(event, fp);
|
||||||
|
break;
|
||||||
case PERF_RECORD_MMAP2:
|
case PERF_RECORD_MMAP2:
|
||||||
ret += perf_event__fprintf_mmap2(event, fp);
|
ret += perf_event__fprintf_mmap2(event, fp);
|
||||||
break;
|
break;
|
||||||
|
@ -39,6 +39,13 @@ struct comm_event {
|
|||||||
char comm[16];
|
char comm[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct namespaces_event {
|
||||||
|
struct perf_event_header header;
|
||||||
|
u32 pid, tid;
|
||||||
|
u64 nr_namespaces;
|
||||||
|
struct perf_ns_link_info link_info[];
|
||||||
|
};
|
||||||
|
|
||||||
struct fork_event {
|
struct fork_event {
|
||||||
struct perf_event_header header;
|
struct perf_event_header header;
|
||||||
u32 pid, ppid;
|
u32 pid, ppid;
|
||||||
@ -485,6 +492,7 @@ union perf_event {
|
|||||||
struct mmap_event mmap;
|
struct mmap_event mmap;
|
||||||
struct mmap2_event mmap2;
|
struct mmap2_event mmap2;
|
||||||
struct comm_event comm;
|
struct comm_event comm;
|
||||||
|
struct namespaces_event namespaces;
|
||||||
struct fork_event fork;
|
struct fork_event fork;
|
||||||
struct lost_event lost;
|
struct lost_event lost;
|
||||||
struct lost_samples_event lost_samples;
|
struct lost_samples_event lost_samples;
|
||||||
@ -587,6 +595,10 @@ int perf_event__process_switch(struct perf_tool *tool,
|
|||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
struct machine *machine);
|
struct machine *machine);
|
||||||
|
int perf_event__process_namespaces(struct perf_tool *tool,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample,
|
||||||
|
struct machine *machine);
|
||||||
int perf_event__process_mmap(struct perf_tool *tool,
|
int perf_event__process_mmap(struct perf_tool *tool,
|
||||||
union perf_event *event,
|
union perf_event *event,
|
||||||
struct perf_sample *sample,
|
struct perf_sample *sample,
|
||||||
@ -653,6 +665,7 @@ size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp);
|
|||||||
size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp);
|
size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp);
|
||||||
size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp);
|
size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp);
|
||||||
size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp);
|
size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp);
|
||||||
|
size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp);
|
||||||
size_t perf_event__fprintf(union perf_event *event, FILE *fp);
|
size_t perf_event__fprintf(union perf_event *event, FILE *fp);
|
||||||
|
|
||||||
u64 kallsyms__get_function_start(const char *kallsyms_filename,
|
u64 kallsyms__get_function_start(const char *kallsyms_filename,
|
||||||
|
@ -932,6 +932,9 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
|
|||||||
attr->mmap2 = track && !perf_missing_features.mmap2;
|
attr->mmap2 = track && !perf_missing_features.mmap2;
|
||||||
attr->comm = track;
|
attr->comm = track;
|
||||||
|
|
||||||
|
if (opts->record_namespaces)
|
||||||
|
attr->namespaces = track;
|
||||||
|
|
||||||
if (opts->record_switch_events)
|
if (opts->record_switch_events)
|
||||||
attr->context_switch = track;
|
attr->context_switch = track;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <symbol/kallsyms.h>
|
#include <symbol/kallsyms.h>
|
||||||
#include "unwind.h"
|
#include "unwind.h"
|
||||||
#include "linux/hash.h"
|
#include "linux/hash.h"
|
||||||
|
#include "asm/bug.h"
|
||||||
|
|
||||||
static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock);
|
static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock);
|
||||||
|
|
||||||
@ -501,6 +502,37 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int machine__process_namespaces_event(struct machine *machine __maybe_unused,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample __maybe_unused)
|
||||||
|
{
|
||||||
|
struct thread *thread = machine__findnew_thread(machine,
|
||||||
|
event->namespaces.pid,
|
||||||
|
event->namespaces.tid);
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
WARN_ONCE(event->namespaces.nr_namespaces > NR_NAMESPACES,
|
||||||
|
"\nWARNING: kernel seems to support more namespaces than perf"
|
||||||
|
" tool.\nTry updating the perf tool..\n\n");
|
||||||
|
|
||||||
|
WARN_ONCE(event->namespaces.nr_namespaces < NR_NAMESPACES,
|
||||||
|
"\nWARNING: perf tool seems to support more namespaces than"
|
||||||
|
" the kernel.\nTry updating the kernel..\n\n");
|
||||||
|
|
||||||
|
if (dump_trace)
|
||||||
|
perf_event__fprintf_namespaces(event, stdout);
|
||||||
|
|
||||||
|
if (thread == NULL ||
|
||||||
|
thread__set_namespaces(thread, sample->time, &event->namespaces)) {
|
||||||
|
dump_printf("problem processing PERF_RECORD_NAMESPACES, skipping event.\n");
|
||||||
|
err = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread__put(thread);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int machine__process_lost_event(struct machine *machine __maybe_unused,
|
int machine__process_lost_event(struct machine *machine __maybe_unused,
|
||||||
union perf_event *event, struct perf_sample *sample __maybe_unused)
|
union perf_event *event, struct perf_sample *sample __maybe_unused)
|
||||||
{
|
{
|
||||||
@ -1538,6 +1570,8 @@ int machine__process_event(struct machine *machine, union perf_event *event,
|
|||||||
ret = machine__process_comm_event(machine, event, sample); break;
|
ret = machine__process_comm_event(machine, event, sample); break;
|
||||||
case PERF_RECORD_MMAP:
|
case PERF_RECORD_MMAP:
|
||||||
ret = machine__process_mmap_event(machine, event, sample); break;
|
ret = machine__process_mmap_event(machine, event, sample); break;
|
||||||
|
case PERF_RECORD_NAMESPACES:
|
||||||
|
ret = machine__process_namespaces_event(machine, event, sample); break;
|
||||||
case PERF_RECORD_MMAP2:
|
case PERF_RECORD_MMAP2:
|
||||||
ret = machine__process_mmap2_event(machine, event, sample); break;
|
ret = machine__process_mmap2_event(machine, event, sample); break;
|
||||||
case PERF_RECORD_FORK:
|
case PERF_RECORD_FORK:
|
||||||
|
@ -97,6 +97,9 @@ int machine__process_itrace_start_event(struct machine *machine,
|
|||||||
union perf_event *event);
|
union perf_event *event);
|
||||||
int machine__process_switch_event(struct machine *machine,
|
int machine__process_switch_event(struct machine *machine,
|
||||||
union perf_event *event);
|
union perf_event *event);
|
||||||
|
int machine__process_namespaces_event(struct machine *machine,
|
||||||
|
union perf_event *event,
|
||||||
|
struct perf_sample *sample);
|
||||||
int machine__process_mmap_event(struct machine *machine, union perf_event *event,
|
int machine__process_mmap_event(struct machine *machine, union perf_event *event,
|
||||||
struct perf_sample *sample);
|
struct perf_sample *sample);
|
||||||
int machine__process_mmap2_event(struct machine *machine, union perf_event *event,
|
int machine__process_mmap2_event(struct machine *machine, union perf_event *event,
|
||||||
|
36
tools/perf/util/namespaces.c
Normal file
36
tools/perf/util/namespaces.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License, version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Hari Bathini, IBM Corporation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "namespaces.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "event.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct namespaces *namespaces__new(struct namespaces_event *event)
|
||||||
|
{
|
||||||
|
struct namespaces *namespaces;
|
||||||
|
u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) *
|
||||||
|
sizeof(struct perf_ns_link_info));
|
||||||
|
|
||||||
|
namespaces = zalloc(sizeof(struct namespaces) + link_info_size);
|
||||||
|
if (!namespaces)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
namespaces->end_time = -1;
|
||||||
|
|
||||||
|
if (event)
|
||||||
|
memcpy(namespaces->link_info, event->link_info, link_info_size);
|
||||||
|
|
||||||
|
return namespaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
void namespaces__free(struct namespaces *namespaces)
|
||||||
|
{
|
||||||
|
free(namespaces);
|
||||||
|
}
|
26
tools/perf/util/namespaces.h
Normal file
26
tools/perf/util/namespaces.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License, version 2, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Hari Bathini, IBM Corporation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PERF_NAMESPACES_H
|
||||||
|
#define __PERF_NAMESPACES_H
|
||||||
|
|
||||||
|
#include "../perf.h"
|
||||||
|
#include <linux/list.h>
|
||||||
|
|
||||||
|
struct namespaces_event;
|
||||||
|
|
||||||
|
struct namespaces {
|
||||||
|
struct list_head list;
|
||||||
|
u64 end_time;
|
||||||
|
struct perf_ns_link_info link_info[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct namespaces *namespaces__new(struct namespaces_event *event);
|
||||||
|
void namespaces__free(struct namespaces *namespaces);
|
||||||
|
|
||||||
|
#endif /* __PERF_NAMESPACES_H */
|
@ -1239,6 +1239,8 @@ static int machines__deliver_event(struct machines *machines,
|
|||||||
return tool->mmap2(tool, event, sample, machine);
|
return tool->mmap2(tool, event, sample, machine);
|
||||||
case PERF_RECORD_COMM:
|
case PERF_RECORD_COMM:
|
||||||
return tool->comm(tool, event, sample, machine);
|
return tool->comm(tool, event, sample, machine);
|
||||||
|
case PERF_RECORD_NAMESPACES:
|
||||||
|
return tool->namespaces(tool, event, sample, machine);
|
||||||
case PERF_RECORD_FORK:
|
case PERF_RECORD_FORK:
|
||||||
return tool->fork(tool, event, sample, machine);
|
return tool->fork(tool, event, sample, machine);
|
||||||
case PERF_RECORD_EXIT:
|
case PERF_RECORD_EXIT:
|
||||||
@ -1494,6 +1496,11 @@ int perf_session__register_idle_thread(struct perf_session *session)
|
|||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thread == NULL || thread__set_namespaces(thread, 0, NULL)) {
|
||||||
|
pr_err("problem inserting idle task.\n");
|
||||||
|
err = -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* machine__findnew_thread() got the thread, so put it */
|
/* machine__findnew_thread() got the thread, so put it */
|
||||||
thread__put(thread);
|
thread__put(thread);
|
||||||
return err;
|
return err;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "thread-stack.h"
|
#include "thread-stack.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "namespaces.h"
|
||||||
#include "comm.h"
|
#include "comm.h"
|
||||||
#include "unwind.h"
|
#include "unwind.h"
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
|
|||||||
thread->tid = tid;
|
thread->tid = tid;
|
||||||
thread->ppid = -1;
|
thread->ppid = -1;
|
||||||
thread->cpu = -1;
|
thread->cpu = -1;
|
||||||
|
INIT_LIST_HEAD(&thread->namespaces_list);
|
||||||
INIT_LIST_HEAD(&thread->comm_list);
|
INIT_LIST_HEAD(&thread->comm_list);
|
||||||
|
|
||||||
comm_str = malloc(32);
|
comm_str = malloc(32);
|
||||||
@ -66,7 +68,8 @@ err_thread:
|
|||||||
|
|
||||||
void thread__delete(struct thread *thread)
|
void thread__delete(struct thread *thread)
|
||||||
{
|
{
|
||||||
struct comm *comm, *tmp;
|
struct namespaces *namespaces, *tmp_namespaces;
|
||||||
|
struct comm *comm, *tmp_comm;
|
||||||
|
|
||||||
BUG_ON(!RB_EMPTY_NODE(&thread->rb_node));
|
BUG_ON(!RB_EMPTY_NODE(&thread->rb_node));
|
||||||
|
|
||||||
@ -76,7 +79,12 @@ void thread__delete(struct thread *thread)
|
|||||||
map_groups__put(thread->mg);
|
map_groups__put(thread->mg);
|
||||||
thread->mg = NULL;
|
thread->mg = NULL;
|
||||||
}
|
}
|
||||||
list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) {
|
list_for_each_entry_safe(namespaces, tmp_namespaces,
|
||||||
|
&thread->namespaces_list, list) {
|
||||||
|
list_del(&namespaces->list);
|
||||||
|
namespaces__free(namespaces);
|
||||||
|
}
|
||||||
|
list_for_each_entry_safe(comm, tmp_comm, &thread->comm_list, list) {
|
||||||
list_del(&comm->list);
|
list_del(&comm->list);
|
||||||
comm__free(comm);
|
comm__free(comm);
|
||||||
}
|
}
|
||||||
@ -104,6 +112,38 @@ void thread__put(struct thread *thread)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct namespaces *thread__namespaces(const struct thread *thread)
|
||||||
|
{
|
||||||
|
if (list_empty(&thread->namespaces_list))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return list_first_entry(&thread->namespaces_list, struct namespaces, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
int thread__set_namespaces(struct thread *thread, u64 timestamp,
|
||||||
|
struct namespaces_event *event)
|
||||||
|
{
|
||||||
|
struct namespaces *new, *curr = thread__namespaces(thread);
|
||||||
|
|
||||||
|
new = namespaces__new(event);
|
||||||
|
if (!new)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
list_add(&new->list, &thread->namespaces_list);
|
||||||
|
|
||||||
|
if (timestamp && curr) {
|
||||||
|
/*
|
||||||
|
* setns syscall must have changed few or all the namespaces
|
||||||
|
* of this thread. Update end time for the namespaces
|
||||||
|
* previously used.
|
||||||
|
*/
|
||||||
|
curr = list_next_entry(new, list);
|
||||||
|
curr->end_time = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct comm *thread__comm(const struct thread *thread)
|
struct comm *thread__comm(const struct thread *thread)
|
||||||
{
|
{
|
||||||
if (list_empty(&thread->comm_list))
|
if (list_empty(&thread->comm_list))
|
||||||
|
@ -28,6 +28,7 @@ struct thread {
|
|||||||
bool comm_set;
|
bool comm_set;
|
||||||
int comm_len;
|
int comm_len;
|
||||||
bool dead; /* if set thread has exited */
|
bool dead; /* if set thread has exited */
|
||||||
|
struct list_head namespaces_list;
|
||||||
struct list_head comm_list;
|
struct list_head comm_list;
|
||||||
u64 db_id;
|
u64 db_id;
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ struct thread {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct machine;
|
struct machine;
|
||||||
|
struct namespaces;
|
||||||
struct comm;
|
struct comm;
|
||||||
|
|
||||||
struct thread *thread__new(pid_t pid, pid_t tid);
|
struct thread *thread__new(pid_t pid, pid_t tid);
|
||||||
@ -62,6 +64,10 @@ static inline void thread__exited(struct thread *thread)
|
|||||||
thread->dead = true;
|
thread->dead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct namespaces *thread__namespaces(const struct thread *thread);
|
||||||
|
int thread__set_namespaces(struct thread *thread, u64 timestamp,
|
||||||
|
struct namespaces_event *event);
|
||||||
|
|
||||||
int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp,
|
int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp,
|
||||||
bool exec);
|
bool exec);
|
||||||
static inline int thread__set_comm(struct thread *thread, const char *comm,
|
static inline int thread__set_comm(struct thread *thread, const char *comm,
|
||||||
|
@ -40,6 +40,7 @@ struct perf_tool {
|
|||||||
event_op mmap,
|
event_op mmap,
|
||||||
mmap2,
|
mmap2,
|
||||||
comm,
|
comm,
|
||||||
|
namespaces,
|
||||||
fork,
|
fork,
|
||||||
exit,
|
exit,
|
||||||
lost,
|
lost,
|
||||||
@ -66,6 +67,7 @@ struct perf_tool {
|
|||||||
event_op3 auxtrace;
|
event_op3 auxtrace;
|
||||||
bool ordered_events;
|
bool ordered_events;
|
||||||
bool ordering_requires_timestamps;
|
bool ordering_requires_timestamps;
|
||||||
|
bool namespace_events;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __PERF_TOOL_H */
|
#endif /* __PERF_TOOL_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user