|
@@ -31,6 +31,7 @@ static const char *perf_event__names[] = {
|
|
|
[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",
|
|
@@ -49,6 +50,16 @@ static const char *perf_event__names[] = {
|
|
|
[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))
|
|
@@ -58,6 +69,13 @@ const char *perf_event__name(unsigned int 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,
|
|
|
union perf_event *event,
|
|
|
struct machine *machine,
|
|
@@ -203,6 +221,58 @@ pid_t perf_event__synthesize_comm(struct perf_tool *tool,
|
|
|
return tgid;
|
|
|
}
|
|
|
|
|
|
+static void perf_event__get_ns_link_info(pid_t pid, const char *ns,
|
|
|
+ struct perf_ns_link_info *ns_link_info)
|
|
|
+{
|
|
|
+ struct stat64 st;
|
|
|
+ char proc_ns[128];
|
|
|
+
|
|
|
+ sprintf(proc_ns, "/proc/%u/ns/%s", pid, ns);
|
|
|
+ if (stat64(proc_ns, &st) == 0) {
|
|
|
+ ns_link_info->dev = st.st_dev;
|
|
|
+ ns_link_info->ino = st.st_ino;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int perf_event__synthesize_namespaces(struct perf_tool *tool,
|
|
|
+ union perf_event *event,
|
|
|
+ pid_t pid, pid_t tgid,
|
|
|
+ perf_event__handler_t process,
|
|
|
+ struct machine *machine)
|
|
|
+{
|
|
|
+ u32 idx;
|
|
|
+ struct perf_ns_link_info *ns_link_info;
|
|
|
+
|
|
|
+ if (!tool || !tool->namespace_events)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ memset(&event->namespaces, 0, (sizeof(event->namespaces) +
|
|
|
+ (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) +
|
|
|
+ machine->id_hdr_size));
|
|
|
+
|
|
|
+ event->namespaces.pid = tgid;
|
|
|
+ event->namespaces.tid = pid;
|
|
|
+
|
|
|
+ event->namespaces.nr_namespaces = NR_NAMESPACES;
|
|
|
+
|
|
|
+ ns_link_info = event->namespaces.link_info;
|
|
|
+
|
|
|
+ for (idx = 0; idx < event->namespaces.nr_namespaces; idx++)
|
|
|
+ perf_event__get_ns_link_info(pid, perf_ns__name(idx),
|
|
|
+ &ns_link_info[idx]);
|
|
|
+
|
|
|
+ event->namespaces.header.type = PERF_RECORD_NAMESPACES;
|
|
|
+
|
|
|
+ event->namespaces.header.size = (sizeof(event->namespaces) +
|
|
|
+ (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) +
|
|
|
+ machine->id_hdr_size);
|
|
|
+
|
|
|
+ if (perf_tool__process_synth_event(tool, event, machine, process) != 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int perf_event__synthesize_fork(struct perf_tool *tool,
|
|
|
union perf_event *event,
|
|
|
pid_t pid, pid_t tgid, pid_t ppid,
|
|
@@ -434,8 +504,9 @@ int perf_event__synthesize_modules(struct perf_tool *tool,
|
|
|
static int __event__synthesize_thread(union perf_event *comm_event,
|
|
|
union perf_event *mmap_event,
|
|
|
union perf_event *fork_event,
|
|
|
+ union perf_event *namespaces_event,
|
|
|
pid_t pid, int full,
|
|
|
- perf_event__handler_t process,
|
|
|
+ perf_event__handler_t process,
|
|
|
struct perf_tool *tool,
|
|
|
struct machine *machine,
|
|
|
bool mmap_data,
|
|
@@ -455,6 +526,11 @@ static int __event__synthesize_thread(union perf_event *comm_event,
|
|
|
if (tgid == -1)
|
|
|
return -1;
|
|
|
|
|
|
+ if (perf_event__synthesize_namespaces(tool, namespaces_event, pid,
|
|
|
+ tgid, process, machine) < 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+
|
|
|
return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid,
|
|
|
process, machine, mmap_data,
|
|
|
proc_map_timeout);
|
|
@@ -488,6 +564,11 @@ static int __event__synthesize_thread(union perf_event *comm_event,
|
|
|
if (perf_event__synthesize_fork(tool, fork_event, _pid, tgid,
|
|
|
ppid, process, machine) < 0)
|
|
|
break;
|
|
|
+
|
|
|
+ if (perf_event__synthesize_namespaces(tool, namespaces_event, _pid,
|
|
|
+ tgid, process, machine) < 0)
|
|
|
+ break;
|
|
|
+
|
|
|
/*
|
|
|
* Send the prepared comm event
|
|
|
*/
|
|
@@ -516,6 +597,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
|
|
unsigned int proc_map_timeout)
|
|
|
{
|
|
|
union perf_event *comm_event, *mmap_event, *fork_event;
|
|
|
+ union perf_event *namespaces_event;
|
|
|
int err = -1, thread, j;
|
|
|
|
|
|
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,
|
|
|
if (fork_event == NULL)
|
|
|
goto out_free_mmap;
|
|
|
|
|
|
+ namespaces_event = malloc(sizeof(namespaces_event->namespaces) +
|
|
|
+ (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) +
|
|
|
+ machine->id_hdr_size);
|
|
|
+ if (namespaces_event == NULL)
|
|
|
+ goto out_free_fork;
|
|
|
+
|
|
|
err = 0;
|
|
|
for (thread = 0; thread < threads->nr; ++thread) {
|
|
|
if (__event__synthesize_thread(comm_event, mmap_event,
|
|
|
- fork_event,
|
|
|
+ fork_event, namespaces_event,
|
|
|
thread_map__pid(threads, thread), 0,
|
|
|
process, tool, machine,
|
|
|
mmap_data, proc_map_timeout)) {
|
|
@@ -559,7 +647,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
|
|
/* if not, generate events for it */
|
|
|
if (need_leader &&
|
|
|
__event__synthesize_thread(comm_event, mmap_event,
|
|
|
- fork_event,
|
|
|
+ fork_event, namespaces_event,
|
|
|
comm_event->comm.pid, 0,
|
|
|
process, tool, machine,
|
|
|
mmap_data, proc_map_timeout)) {
|
|
@@ -568,6 +656,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ free(namespaces_event);
|
|
|
+out_free_fork:
|
|
|
free(fork_event);
|
|
|
out_free_mmap:
|
|
|
free(mmap_event);
|
|
@@ -587,6 +677,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
|
|
|
char proc_path[PATH_MAX];
|
|
|
struct dirent *dirent;
|
|
|
union perf_event *comm_event, *mmap_event, *fork_event;
|
|
|
+ union perf_event *namespaces_event;
|
|
|
int err = -1;
|
|
|
|
|
|
if (machine__is_default_guest(machine))
|
|
@@ -604,11 +695,17 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
|
|
|
if (fork_event == NULL)
|
|
|
goto out_free_mmap;
|
|
|
|
|
|
+ namespaces_event = malloc(sizeof(namespaces_event->namespaces) +
|
|
|
+ (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) +
|
|
|
+ machine->id_hdr_size);
|
|
|
+ if (namespaces_event == NULL)
|
|
|
+ goto out_free_fork;
|
|
|
+
|
|
|
snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir);
|
|
|
proc = opendir(proc_path);
|
|
|
|
|
|
if (proc == NULL)
|
|
|
- goto out_free_fork;
|
|
|
+ goto out_free_namespaces;
|
|
|
|
|
|
while ((dirent = readdir(proc)) != NULL) {
|
|
|
char *end;
|
|
@@ -620,13 +717,16 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
|
|
|
* We may race with exiting thread, so don't stop just because
|
|
|
* one thread couldn't be synthesized.
|
|
|
*/
|
|
|
- __event__synthesize_thread(comm_event, mmap_event, fork_event, pid,
|
|
|
- 1, process, tool, machine, mmap_data,
|
|
|
+ __event__synthesize_thread(comm_event, mmap_event, fork_event,
|
|
|
+ namespaces_event, pid, 1, process,
|
|
|
+ tool, machine, mmap_data,
|
|
|
proc_map_timeout);
|
|
|
}
|
|
|
|
|
|
err = 0;
|
|
|
closedir(proc);
|
|
|
+out_free_namespaces:
|
|
|
+ free(namespaces_event);
|
|
|
out_free_fork:
|
|
|
free(fork_event);
|
|
|
out_free_mmap:
|
|
@@ -1008,6 +1108,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);
|
|
|
}
|
|
|
|
|
|
+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,
|
|
@@ -1016,6 +1143,14 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused,
|
|
|
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,
|
|
@@ -1196,6 +1331,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
|
|
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;
|