use_clockid    :  1, /* use @clockid for time fields */
                                context_switch :  1, /* context switch data */
                                write_backward :  1, /* Write ring buffer from end to beginning */
-                               __reserved_1   : 36;
+                               namespaces     :  1, /* include namespaces data */
+                               __reserved_1   : 35;
 
        union {
                __u32           wakeup_events;    /* wakeup every n events */
        __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 {
 
        /*
         */
        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 */
 };
 
 
 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.
 
+--namespaces::
+Record events of type PERF_RECORD_NAMESPACES.
+
 --transaction::
 Record transaction flags for transaction related events.
 
 
                        .comm   = perf_event__process_comm,
                        .exit   = perf_event__process_exit,
                        .fork   = perf_event__process_fork,
+                       .namespaces = perf_event__process_namespaces,
                        .ordered_events = true,
                        .ordering_requires_timestamps = true,
                },
 
        .exit   = perf_event__process_exit,
        .fork   = perf_event__process_fork,
        .lost   = perf_event__process_lost,
+       .namespaces = perf_event__process_namespaces,
        .ordered_events = true,
        .ordering_requires_timestamps = true,
 };
 
        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,
                                   union perf_event *event,
                                   struct perf_sample *sample,
                session->itrace_synth_opts = &inject->itrace_synth_opts;
                inject->itrace_synth_opts.inject = true;
                inject->tool.comm           = perf_event__repipe_comm;
+               inject->tool.namespaces     = perf_event__repipe_namespaces;
                inject->tool.exit           = perf_event__repipe_exit;
                inject->tool.id_index       = perf_event__repipe_id_index;
                inject->tool.auxtrace_info  = perf_event__process_auxtrace_info;
 
        .comm            = perf_event__process_comm,
        .mmap            = perf_event__process_mmap,
        .mmap2           = perf_event__process_mmap2,
+       .namespaces      = perf_event__process_namespaces,
        .ordered_events  = true,
 };
 
 
        struct perf_tool eops = {
                .sample                 = process_sample_event,
                .comm                   = perf_event__process_comm,
+               .namespaces             = perf_event__process_namespaces,
                .ordered_events         = true,
        };
        struct perf_data_file file = {
        kvm->tool.exit   = perf_event__process_exit;
        kvm->tool.fork   = perf_event__process_fork;
        kvm->tool.lost   = process_lost_event;
+       kvm->tool.namespaces  = perf_event__process_namespaces;
        kvm->tool.ordered_events = true;
        perf_tool__fill_defaults(&kvm->tool);
 
 
        struct perf_tool eops = {
                .sample          = process_sample_event,
                .comm            = perf_event__process_comm,
+               .namespaces      = perf_event__process_namespaces,
                .ordered_events  = true,
        };
        struct perf_data_file file = {
 
                        .lost           = perf_event__process_lost,
                        .fork           = perf_event__process_fork,
                        .build_id       = perf_event__process_build_id,
+                       .namespaces     = perf_event__process_namespaces,
                        .ordered_events = true,
                },
                .input_name              = "perf.data",
 
        signal(SIGTERM, sig_handler);
        signal(SIGSEGV, sigsegv_handler);
 
+       if (rec->opts.record_namespaces)
+               tool->namespace_events = true;
+
        if (rec->opts.auxtrace_snapshot_mode || rec->switch_output.enabled) {
                signal(SIGUSR2, snapshot_sig_handler);
                if (rec->opts.auxtrace_snapshot_mode)
                .fork           = perf_event__process_fork,
                .exit           = perf_event__process_exit,
                .comm           = perf_event__process_comm,
+               .namespaces     = perf_event__process_namespaces,
                .mmap           = perf_event__process_mmap,
                .mmap2          = perf_event__process_mmap2,
                .ordered_events = true,
                          "opts", "AUX area tracing Snapshot Mode", ""),
        OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout,
                        "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,
                    "Record context switch events"),
        OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel,
 
                        .mmap            = perf_event__process_mmap,
                        .mmap2           = perf_event__process_mmap2,
                        .comm            = perf_event__process_comm,
+                       .namespaces      = perf_event__process_namespaces,
                        .exit            = perf_event__process_exit,
                        .fork            = perf_event__process_fork,
                        .lost            = perf_event__process_lost,
 
                .tool = {
                        .sample          = perf_sched__process_tracepoint_sample,
                        .comm            = perf_event__process_comm,
+                       .namespaces      = perf_event__process_namespaces,
                        .lost            = perf_event__process_lost,
                        .fork            = perf_sched__process_fork_event,
                        .ordered_events = true,
 
                        .mmap            = perf_event__process_mmap,
                        .mmap2           = perf_event__process_mmap2,
                        .comm            = perf_event__process_comm,
+                       .namespaces      = perf_event__process_namespaces,
                        .exit            = perf_event__process_exit,
                        .fork            = perf_event__process_fork,
                        .attr            = process_attr,
 
        trace->tool.exit          = perf_event__process_exit;
        trace->tool.fork          = perf_event__process_fork;
        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.namespaces    = perf_event__process_namespaces;
 
        trace->tool.ordered_events = true;
        trace->tool.ordering_requires_timestamps = true;
 
        bool         running_time;
        bool         full_auxtrace;
        bool         auxtrace_snapshot_mode;
+       bool         record_namespaces;
        bool         record_switch_events;
        bool         all_kernel;
        bool         all_user;
 
 libperf-y += session.o
 libperf-$(CONFIG_AUDIT) += syscalltbl.o
 libperf-y += ordered-events.o
+libperf-y += namespaces.o
 libperf-y += comm.o
 libperf-y += thread.o
 libperf-y += thread_map.o
 
                        .lost            = perf_event__process_lost,
                        .tracing_data    = perf_event__process_tracing_data,
                        .build_id        = perf_event__process_build_id,
+                       .namespaces      = perf_event__process_namespaces,
                        .ordered_events  = true,
                        .ordering_requires_timestamps = true,
                },
 
        [PERF_RECORD_LOST_SAMPLES]              = "LOST_SAMPLES",
        [PERF_RECORD_SWITCH]                    = "SWITCH",
        [PERF_RECORD_SWITCH_CPU_WIDE]           = "SWITCH_CPU_WIDE",
+       [PERF_RECORD_NAMESPACES]                = "NAMESPACES",
        [PERF_RECORD_HEADER_ATTR]               = "ATTR",
        [PERF_RECORD_HEADER_EVENT_TYPE]         = "EVENT_TYPE",
        [PERF_RECORD_HEADER_TRACING_DATA]       = "TRACING_DATA",
        [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)
 {
        if (id >= ARRAY_SIZE(perf_event__names))
        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,
                                          union perf_event *event,
                                          struct machine *machine,
        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,
                             union perf_event *event,
                             struct perf_sample *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,
                             union perf_event *event,
                             struct perf_sample *sample,
        case PERF_RECORD_MMAP:
                ret += perf_event__fprintf_mmap(event, fp);
                break;
+       case PERF_RECORD_NAMESPACES:
+               ret += perf_event__fprintf_namespaces(event, fp);
+               break;
        case PERF_RECORD_MMAP2:
                ret += perf_event__fprintf_mmap2(event, fp);
                break;
 
        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 perf_event_header header;
        u32 pid, ppid;
        struct mmap_event               mmap;
        struct mmap2_event              mmap2;
        struct comm_event               comm;
+       struct namespaces_event         namespaces;
        struct fork_event               fork;
        struct lost_event               lost;
        struct lost_samples_event       lost_samples;
                               union perf_event *event,
                               struct perf_sample *sample,
                               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,
                             union perf_event *event,
                             struct perf_sample *sample,
 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_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);
 
 u64 kallsyms__get_function_start(const char *kallsyms_filename,
 
        attr->mmap2 = track && !perf_missing_features.mmap2;
        attr->comm  = track;
 
+       if (opts->record_namespaces)
+               attr->namespaces  = track;
+
        if (opts->record_switch_events)
                attr->context_switch = track;
 
 
 #include <symbol/kallsyms.h>
 #include "unwind.h"
 #include "linux/hash.h"
+#include "asm/bug.h"
 
 static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock);
 
        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,
                                union perf_event *event, struct perf_sample *sample __maybe_unused)
 {
                ret = machine__process_comm_event(machine, event, sample); break;
        case PERF_RECORD_MMAP:
                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:
                ret = machine__process_mmap2_event(machine, event, sample); break;
        case PERF_RECORD_FORK:
 
                                        union perf_event *event);
 int machine__process_switch_event(struct machine *machine,
                                  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,
                                struct perf_sample *sample);
 int machine__process_mmap2_event(struct machine *machine, union perf_event *event,
 
--- /dev/null
+/*
+ * 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);
+}
 
--- /dev/null
+/*
+ * 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 */
 
                return tool->mmap2(tool, event, sample, machine);
        case PERF_RECORD_COMM:
                return tool->comm(tool, event, sample, machine);
+       case PERF_RECORD_NAMESPACES:
+               return tool->namespaces(tool, event, sample, machine);
        case PERF_RECORD_FORK:
                return tool->fork(tool, event, sample, machine);
        case PERF_RECORD_EXIT:
                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 */
        thread__put(thread);
        return err;
 
 #include "thread-stack.h"
 #include "util.h"
 #include "debug.h"
+#include "namespaces.h"
 #include "comm.h"
 #include "unwind.h"
 
                thread->tid = tid;
                thread->ppid = -1;
                thread->cpu = -1;
+               INIT_LIST_HEAD(&thread->namespaces_list);
                INIT_LIST_HEAD(&thread->comm_list);
 
                comm_str = malloc(32);
 
 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));
 
                map_groups__put(thread->mg);
                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);
                comm__free(comm);
        }
        }
 }
 
+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)
 {
        if (list_empty(&thread->comm_list))
 
        bool                    comm_set;
        int                     comm_len;
        bool                    dead; /* if set thread has exited */
+       struct list_head        namespaces_list;
        struct list_head        comm_list;
        u64                     db_id;
 
 };
 
 struct machine;
+struct namespaces;
 struct comm;
 
 struct thread *thread__new(pid_t pid, pid_t tid);
        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,
                       bool exec);
 static inline int thread__set_comm(struct thread *thread, const char *comm,
 
        event_op        mmap,
                        mmap2,
                        comm,
+                       namespaces,
                        fork,
                        exit,
                        lost,
        event_op3       auxtrace;
        bool            ordered_events;
        bool            ordering_requires_timestamps;
+       bool            namespace_events;
 };
 
 #endif /* __PERF_TOOL_H */