2013-08-13 11:55:09

by Stephane Eranian

[permalink] [raw]
Subject: [PATCH v1 0/2] perf: add new PERF_RECORD_MMAP2 record type

This patch series introduces a new record type for mmaps. This
new record is called PERF_RECORD_MMAP2. It provides more information
than the existing PERF_RECORD_MMAP. For each file-backed or
shared memory segment mapping, the device major, minor and the
inode numbers are recorded. This triplet may be used to
disambiguate virtual file-backed mappings between processes.

The triplet could not be fitted into the existing PERF_RECORD_MMAP
record without breaking backward compatibility. This is why we
introduce this new record type.

To activate this new record type, the mmap2 bit must be set
in perf_event_attr struct. It supersedes the mmap bit, but is
distinct from mmap_data. For mmap2 data records, both mmap2 and
mmap_data must be set. For code, only mmap2 must be set.

The series also provides the perf tool support for this new
MMAP2 record type. Perf record activates this mode by default.
The maj, min, ino are not yet exploited by perf report but
will be in subsequent perf tools.

Patch is relative to tip.git

Signed-off-by: Stephane Eranian <[email protected]>
---

Stephane Eranian (2):
perf: add attr->mmap2 attribute to an event
perf tools: add attr->mmap2 support

include/uapi/linux/perf_event.h | 22 +++++++++++++++-
kernel/events/core.c | 41 ++++++++++++++++++++++++++---
tools/perf/builtin-annotate.c | 1 +
tools/perf/builtin-inject.c | 15 +++++++++++
tools/perf/builtin-mem.c | 1 +
tools/perf/builtin-report.c | 1 +
tools/perf/builtin-script.c | 1 +
tools/perf/util/build-id.c | 1 +
tools/perf/util/event.c | 55 ++++++++++++++++++++++++++++++---------
tools/perf/util/event.h | 18 +++++++++++++
tools/perf/util/evsel.c | 5 ++--
tools/perf/util/header.c | 3 +++
tools/perf/util/machine.c | 51 +++++++++++++++++++++++++++++++++++-
tools/perf/util/machine.h | 1 +
tools/perf/util/map.c | 7 ++++-
tools/perf/util/map.h | 6 +++--
tools/perf/util/session.c | 25 +++++++++++++++++-
tools/perf/util/tool.h | 1 +
18 files changed, 231 insertions(+), 24 deletions(-)

--
1.7.9.5


2013-08-13 11:55:13

by Stephane Eranian

[permalink] [raw]
Subject: [PATCH v1 1/2] perf: add attr->mmap2 attribute to an event

Adds a new PERF_RECORD_MMAP2 record type.

Used to request mmap records with more information about
the mapping, including device major, minor and the inode
number for mappings associated with files or shared memory
segments. Works for code and data (with attr->mmap_data set).

Existing PERF_RECORD_MMAP record is unmodified by this patch.

Signed-off-by: Stephane Eranian <[email protected]>
---
include/uapi/linux/perf_event.h | 22 ++++++++++++++++++++-
kernel/events/core.c | 41 +++++++++++++++++++++++++++++++++++----
2 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index d80d223..1eec8f5 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -275,8 +275,9 @@ struct perf_event_attr {

exclude_callchain_kernel : 1, /* exclude kernel callchains */
exclude_callchain_user : 1, /* exclude user callchains */
+ mmap2 : 1, /* include mmap with inode data */

- __reserved_1 : 41;
+ __reserved_1 : 40;

union {
__u32 wakeup_events; /* wakeup every n events */
@@ -638,6 +639,25 @@ enum perf_event_type {
*/
PERF_RECORD_SAMPLE = 9,

+ /*
+ * The MMAP2 records are an augmented version of MMAP, they add
+ * maj, min, ino numbers to be used to uniquely identify each mapping
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * u32 maj;
+ * u32 min;
+ * u64 ino;
+ * char filename[];
+ * };
+ */
+ PERF_RECORD_MMAP2 = 10,
+
PERF_RECORD_MAX, /* non-ABI */
};

diff --git a/kernel/events/core.c b/kernel/events/core.c
index e82e700..838a0bb 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -4771,7 +4771,7 @@ perf_event_aux(perf_event_aux_output_cb output, void *data,
/*
* task tracking -- fork/exit
*
- * enabled by: attr.comm | attr.mmap | attr.mmap_data | attr.task
+ * enabled by: attr.comm | attr.mmap | attr.mmap2 | attr.mmap_data | attr.task
*/

struct perf_task_event {
@@ -4791,8 +4791,9 @@ struct perf_task_event {

static int perf_event_task_match(struct perf_event *event)
{
- return event->attr.comm || event->attr.mmap ||
- event->attr.mmap_data || event->attr.task;
+ return event->attr.comm || event->attr.mmap ||
+ event->attr.mmap2 || event->attr.mmap_data ||
+ event->attr.task;
}

static void perf_event_task_output(struct perf_event *event,
@@ -4987,6 +4988,8 @@ struct perf_mmap_event {

const char *file_name;
int file_size;
+ int maj, min;
+ u64 ino;

struct {
struct perf_event_header header;
@@ -5007,7 +5010,7 @@ static int perf_event_mmap_match(struct perf_event *event,
int executable = vma->vm_flags & VM_EXEC;

return (!executable && event->attr.mmap_data) ||
- (executable && event->attr.mmap);
+ (executable && (event->attr.mmap || event->attr.mmap2));
}

static void perf_event_mmap_output(struct perf_event *event,
@@ -5022,6 +5025,13 @@ static void perf_event_mmap_output(struct perf_event *event,
if (!perf_event_mmap_match(event, data))
return;

+ if (event->attr.mmap2) {
+ mmap_event->event_id.header.type = PERF_RECORD_MMAP2;
+ mmap_event->event_id.header.size += sizeof(mmap_event->maj);
+ mmap_event->event_id.header.size += sizeof(mmap_event->min);
+ mmap_event->event_id.header.size += sizeof(mmap_event->ino);
+ }
+
perf_event_header__init_id(&mmap_event->event_id.header, &sample, event);
ret = perf_output_begin(&handle, event,
mmap_event->event_id.header.size);
@@ -5032,6 +5042,13 @@ static void perf_event_mmap_output(struct perf_event *event,
mmap_event->event_id.tid = perf_event_tid(event, current);

perf_output_put(&handle, mmap_event->event_id);
+
+ if (event->attr.mmap2) {
+ perf_output_put(&handle, mmap_event->maj);
+ perf_output_put(&handle, mmap_event->min);
+ perf_output_put(&handle, mmap_event->ino);
+ }
+
__output_copy(&handle, mmap_event->file_name,
mmap_event->file_size);

@@ -5046,6 +5063,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
{
struct vm_area_struct *vma = mmap_event->vma;
struct file *file = vma->vm_file;
+ int maj = 0, min = 0;
+ u64 ino = 0;
unsigned int size;
char tmp[16];
char *buf = NULL;
@@ -5054,6 +5073,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
memset(tmp, 0, sizeof(tmp));

if (file) {
+ struct inode *inode;
+ dev_t dev;
/*
* d_path works from the end of the rb backwards, so we
* need to add enough zero bytes after the string to handle
@@ -5069,6 +5090,12 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
name = strncpy(tmp, "//toolong", sizeof(tmp));
goto got_name;
}
+ inode = file_inode(vma->vm_file);
+ dev = inode->i_sb->s_dev;
+ ino = inode->i_ino;
+ maj = MAJOR(dev);
+ min = MINOR(dev);
+
} else {
if (arch_vma_name(mmap_event->vma)) {
name = strncpy(tmp, arch_vma_name(mmap_event->vma),
@@ -5099,6 +5126,9 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)

mmap_event->file_name = name;
mmap_event->file_size = size;
+ mmap_event->maj = maj;
+ mmap_event->min = min;
+ mmap_event->ino = ino;

if (!(vma->vm_flags & VM_EXEC))
mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_DATA;
@@ -5135,6 +5165,9 @@ void perf_event_mmap(struct vm_area_struct *vma)
.len = vma->vm_end - vma->vm_start,
.pgoff = (u64)vma->vm_pgoff << PAGE_SHIFT,
},
+ /* .maj (attr_mmap2 only) */
+ /* .min (attr_mmap2 only) */
+ /* .ino (attr_mmap2 only) */
};

perf_event_mmap_event(&mmap_event);
--
1.7.9.5

2013-08-13 11:55:19

by Stephane Eranian

[permalink] [raw]
Subject: [PATCH v1 2/2] perf tools: add attr->mmap2 support

This patch adds support for the new PERF_RECORD_MMAP2
record type exposed by the kernel. This is an extended
PERF_RECORD_MMAP record. It adds for each file-backed
mapping the device major, minor number and the inode
number. This triplet uniquely identifies the source
of a file-backed mapping. It can be used to detect
identical virtual mappings between processes for instance.

The patch will prefer MMAP2 over MMAP.

Signed-off-by: Stephane Eranian <[email protected]>
---
tools/perf/builtin-annotate.c | 1 +
tools/perf/builtin-inject.c | 15 +++++++++++
tools/perf/builtin-mem.c | 1 +
tools/perf/builtin-report.c | 1 +
tools/perf/builtin-script.c | 1 +
tools/perf/util/build-id.c | 1 +
tools/perf/util/event.c | 55 ++++++++++++++++++++++++++++++++---------
tools/perf/util/event.h | 18 ++++++++++++++
tools/perf/util/evsel.c | 5 ++--
tools/perf/util/header.c | 3 +++
tools/perf/util/machine.c | 51 +++++++++++++++++++++++++++++++++++++-
tools/perf/util/machine.h | 1 +
tools/perf/util/map.c | 7 +++++-
tools/perf/util/map.h | 6 +++--
tools/perf/util/session.c | 25 ++++++++++++++++++-
tools/perf/util/tool.h | 1 +
16 files changed, 173 insertions(+), 19 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index db491e9..61e1781 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -276,6 +276,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
.tool = {
.sample = process_sample_event,
.mmap = perf_event__process_mmap,
+ .mmap2 = perf_event__process_mmap2,
.comm = perf_event__process_comm,
.exit = perf_event__process_exit,
.fork = perf_event__process_fork,
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index f012a98..4aeed15 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -123,6 +123,19 @@ static int perf_event__repipe_mmap(struct perf_tool *tool,
return err;
}

+static int perf_event__repipe_mmap2(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine)
+{
+ int err;
+
+ err = perf_event__process_mmap2(tool, event, sample, machine);
+ perf_event__repipe(tool, event, sample, machine);
+
+ return err;
+}
+
static int perf_event__repipe_fork(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
@@ -337,6 +350,7 @@ static int __cmd_inject(struct perf_inject *inject)

if (inject->build_ids || inject->sched_stat) {
inject->tool.mmap = perf_event__repipe_mmap;
+ inject->tool.mmap2 = perf_event__repipe_mmap2;
inject->tool.fork = perf_event__repipe_fork;
inject->tool.tracing_data = perf_event__repipe_tracing_data;
}
@@ -388,6 +402,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
.tool = {
.sample = perf_event__repipe_sample,
.mmap = perf_event__repipe,
+ .mmap2 = perf_event__repipe,
.comm = perf_event__repipe,
.fork = perf_event__repipe,
.exit = perf_event__repipe,
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index a8ff6d2..33022ff 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -192,6 +192,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
.tool = {
.sample = process_sample_event,
.mmap = perf_event__process_mmap,
+ .mmap2 = perf_event__process_mmap2,
.comm = perf_event__process_comm,
.lost = perf_event__process_lost,
.fork = perf_event__process_fork,
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index d785d89..ac1dd18 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -746,6 +746,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
.tool = {
.sample = process_sample_event,
.mmap = perf_event__process_mmap,
+ .mmap2 = perf_event__process_mmap2,
.comm = perf_event__process_comm,
.exit = perf_event__process_exit,
.fork = perf_event__process_fork,
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index cd616ff..fe40b93 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -520,6 +520,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
static struct perf_tool perf_script = {
.sample = process_sample_event,
.mmap = perf_event__process_mmap,
+ .mmap2 = perf_event__process_mmap2,
.comm = perf_event__process_comm,
.exit = perf_event__process_exit,
.fork = perf_event__process_fork,
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 3a0f508..8127f65 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -64,6 +64,7 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused,
struct perf_tool build_id__mark_dso_hit_ops = {
.sample = build_id__mark_dso_hit,
.mmap = perf_event__process_mmap,
+ .mmap2 = perf_event__process_mmap2,
.fork = perf_event__process_fork,
.exit = perf_event__exit_del_thread,
.attr = perf_event__process_attr,
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index cc7c0c9..1e0f03c 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -11,6 +11,7 @@
static const char *perf_event__names[] = {
[0] = "TOTAL",
[PERF_RECORD_MMAP] = "MMAP",
+ [PERF_RECORD_MMAP2] = "MMAP2",
[PERF_RECORD_LOST] = "LOST",
[PERF_RECORD_COMM] = "COMM",
[PERF_RECORD_EXIT] = "EXIT",
@@ -186,7 +187,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
return -1;
}

- event->header.type = PERF_RECORD_MMAP;
+ event->header.type = PERF_RECORD_MMAP2;
/*
* Just like the kernel, see __perf_event_mmap in kernel/perf_event.c
*/
@@ -197,7 +198,9 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
char prot[5];
char execname[PATH_MAX];
char anonstr[] = "//anon";
+ unsigned int ino;
size_t size;
+ ssize_t n;

if (fgets(bf, sizeof(bf), fp) == NULL)
break;
@@ -206,9 +209,16 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy(execname, "");

/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
- sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n",
- &event->mmap.start, &event->mmap.len, prot,
- &event->mmap.pgoff, execname);
+ n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n",
+ &event->mmap2.start, &event->mmap2.len, prot,
+ &event->mmap2.pgoff, &event->mmap2.maj,
+ &event->mmap2.min,
+ &ino, execname);
+
+ event->mmap2.ino = (u64)ino;
+
+ if (n != 8)
+ continue;

if (prot[2] != 'x')
continue;
@@ -217,15 +227,15 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
strcpy(execname, anonstr);

size = strlen(execname) + 1;
- memcpy(event->mmap.filename, execname, size);
+ memcpy(event->mmap2.filename, execname, size);
size = PERF_ALIGN(size, sizeof(u64));
- event->mmap.len -= event->mmap.start;
- event->mmap.header.size = (sizeof(event->mmap) -
- (sizeof(event->mmap.filename) - size));
- memset(event->mmap.filename + size, 0, machine->id_hdr_size);
- event->mmap.header.size += machine->id_hdr_size;
- event->mmap.pid = tgid;
- event->mmap.tid = pid;
+ event->mmap2.len -= event->mmap.start;
+ event->mmap2.header.size = (sizeof(event->mmap2) -
+ (sizeof(event->mmap2.filename) - size));
+ memset(event->mmap2.filename + size, 0, machine->id_hdr_size);
+ event->mmap2.header.size += machine->id_hdr_size;
+ event->mmap2.pid = tgid;
+ event->mmap2.tid = pid;

if (process(tool, event, &synth_sample, machine) != 0) {
rc = -1;
@@ -527,6 +537,16 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
event->mmap.len, event->mmap.pgoff, event->mmap.filename);
}

+size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
+{
+ return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64
+ " %02x:%02x %"PRIu64"]: %s\n",
+ event->mmap2.pid, event->mmap2.tid, event->mmap2.start,
+ event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj,
+ event->mmap2.min, event->mmap2.ino,
+ event->mmap2.filename);
+}
+
int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
@@ -535,6 +555,14 @@ int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,
return machine__process_mmap_event(machine, event);
}

+int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine)
+{
+ return machine__process_mmap2_event(machine, event);
+}
+
size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)
{
return fprintf(fp, "(%d:%d):(%d:%d)\n",
@@ -574,6 +602,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_MMAP2:
+ ret += perf_event__fprintf_mmap2(event, fp);
+ break;
default:
ret += fprintf(fp, "\n");
}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 6119a64..747622f 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -27,6 +27,18 @@ struct mmap_event {
char filename[PATH_MAX];
};

+struct mmap2_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
+ u32 maj;
+ u32 min;
+ u64 ino;
+ char filename[PATH_MAX];
+};
+
struct comm_event {
struct perf_event_header header;
u32 pid, tid;
@@ -168,6 +180,7 @@ union perf_event {
struct perf_event_header header;
struct ip_event ip;
struct mmap_event mmap;
+ struct mmap2_event mmap2;
struct comm_event comm;
struct fork_event fork;
struct lost_event lost;
@@ -217,6 +230,10 @@ int perf_event__process_mmap(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct machine *machine);
+int perf_event__process_mmap2(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
int perf_event__process_fork(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
@@ -245,6 +262,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,

size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);
size_t perf_event__fprintf(union perf_event *event, FILE *fp);

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 960394e..2bc0e4d 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -598,8 +598,9 @@ void perf_evsel__config(struct perf_evsel *evsel,
if (opts->sample_weight)
attr->sample_type |= PERF_SAMPLE_WEIGHT;

- attr->mmap = track;
- attr->comm = track;
+ attr->mmap = track;
+ attr->mmap2 = track;
+ attr->comm = track;

/*
* XXX see the function comment above
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index f558f83..72ec25c 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1348,6 +1348,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)

fprintf(fp, ", precise_ip = %d", evsel->attr.precise_ip);

+ fprintf(fp, ", attr_mmap2 = %d", evsel->attr.mmap2);
+ fprintf(fp, ", attr_mmap = %d", evsel->attr.mmap);
+ fprintf(fp, ", attr_mmap_data = %d", evsel->attr.mmap_data);
if (evsel->ids) {
fprintf(fp, ", id = {");
for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) {
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 6fcc358..ddfbdb9 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -967,6 +967,52 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
return -1;
}

+int machine__process_mmap2_event(struct machine *machine,
+ union perf_event *event)
+{
+ u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+ struct thread *thread;
+ struct map *map;
+ enum map_type type;
+ int ret = 0;
+
+ if (dump_trace)
+ perf_event__fprintf_mmap2(event, stdout);
+
+ if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
+ cpumode == PERF_RECORD_MISC_KERNEL) {
+ ret = machine__process_kernel_mmap_event(machine, event);
+ if (ret < 0)
+ goto out_problem;
+ return 0;
+ }
+
+ thread = machine__findnew_thread(machine, event->mmap2.pid);
+ if (thread == NULL)
+ goto out_problem;
+
+ if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA)
+ type = MAP__VARIABLE;
+ else
+ type = MAP__FUNCTION;
+
+ map = map__new(&machine->user_dsos, event->mmap2.start,
+ event->mmap2.len, event->mmap2.pgoff,
+ event->mmap2.pid, event->mmap2.maj,
+ event->mmap2.min, event->mmap2.ino,
+ event->mmap2.filename, type);
+
+ if (map == NULL)
+ goto out_problem;
+
+ thread__insert_map(thread, map);
+ return 0;
+
+out_problem:
+ dump_printf("problem processing PERF_RECORD_MMAP2, skipping event.\n");
+ return 0;
+}
+
int machine__process_mmap_event(struct machine *machine, union perf_event *event)
{
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
@@ -997,7 +1043,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event

map = map__new(&machine->user_dsos, event->mmap.start,
event->mmap.len, event->mmap.pgoff,
- event->mmap.pid, event->mmap.filename,
+ event->mmap.pid, 0, 0, 0,
+ event->mmap.filename,
type);

if (map == NULL)
@@ -1061,6 +1108,8 @@ int machine__process_event(struct machine *machine, union perf_event *event)
ret = machine__process_comm_event(machine, event); break;
case PERF_RECORD_MMAP:
ret = machine__process_mmap_event(machine, event); break;
+ case PERF_RECORD_MMAP2:
+ ret = machine__process_mmap2_event(machine, event); break;
case PERF_RECORD_FORK:
ret = machine__process_fork_event(machine, event); break;
case PERF_RECORD_EXIT:
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 5bb6244..195631a 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -44,6 +44,7 @@ int machine__process_exit_event(struct machine *machine, union perf_event *event
int machine__process_fork_event(struct machine *machine, union perf_event *event);
int machine__process_lost_event(struct machine *machine, union perf_event *event);
int machine__process_mmap_event(struct machine *machine, union perf_event *event);
+int machine__process_mmap2_event(struct machine *machine, union perf_event *event);
int machine__process_event(struct machine *machine, union perf_event *event);

typedef void (*machine__process_t)(struct machine *machine, void *data);
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 9e8304c..948e988 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -48,7 +48,8 @@ void map__init(struct map *map, enum map_type type,
}

struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
- u64 pgoff, u32 pid, char *filename,
+ u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
+ char *filename,
enum map_type type)
{
struct map *map = malloc(sizeof(*map));
@@ -62,6 +63,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
vdso = is_vdso_map(filename);
no_dso = is_no_dso_memory(filename);

+ map->maj = d_maj;
+ map->min = d_min;
+ map->ino = ino;
+
if (anon) {
snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
filename = newfilename;
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 2cc93cb..782fbaa 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -36,6 +36,8 @@ struct map {
bool erange_warned;
u32 priv;
u64 pgoff;
+ u32 maj, min; /* only valid from MMAP2 record */
+ u64 ino; /* only valid from MMAP2 record */

/* ip -> dso rip */
u64 (*map_ip)(struct map *, u64);
@@ -88,8 +90,8 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
void map__init(struct map *map, enum map_type type,
u64 start, u64 end, u64 pgoff, struct dso *dso);
struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
- u64 pgoff, u32 pid, char *filename,
- enum map_type type);
+ u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
+ char *filename, enum map_type type);
struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
void map__delete(struct map *map);
struct map *map__clone(struct map *map);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index dedaeb2..dd84077 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -351,6 +351,25 @@ static void perf_event__mmap_swap(union perf_event *event,
}
}

+static void perf_event__mmap2_swap(union perf_event *event,
+ bool sample_id_all)
+{
+ event->mmap2.pid = bswap_32(event->mmap2.pid);
+ event->mmap2.tid = bswap_32(event->mmap2.tid);
+ event->mmap2.start = bswap_64(event->mmap2.start);
+ event->mmap2.len = bswap_64(event->mmap2.len);
+ event->mmap2.pgoff = bswap_64(event->mmap2.pgoff);
+ event->mmap2.maj = bswap_32(event->mmap2.maj);
+ event->mmap2.min = bswap_32(event->mmap2.min);
+ event->mmap2.ino = bswap_64(event->mmap2.ino);
+
+ if (sample_id_all) {
+ void *data = &event->mmap2.filename;
+
+ data += PERF_ALIGN(strlen(data) + 1, sizeof(u64));
+ swap_sample_id_all(event, data);
+ }
+}
static void perf_event__task_swap(union perf_event *event, bool sample_id_all)
{
event->fork.pid = bswap_32(event->fork.pid);
@@ -455,6 +474,7 @@ typedef void (*perf_event__swap_op)(union perf_event *event,

static perf_event__swap_op perf_event__swap_ops[] = {
[PERF_RECORD_MMAP] = perf_event__mmap_swap,
+ [PERF_RECORD_MMAP2] = perf_event__mmap2_swap,
[PERF_RECORD_COMM] = perf_event__comm_swap,
[PERF_RECORD_FORK] = perf_event__task_swap,
[PERF_RECORD_EXIT] = perf_event__task_swap,
@@ -849,7 +869,8 @@ static struct machine *
(cpumode == PERF_RECORD_MISC_GUEST_USER))) {
u32 pid;

- if (event->header.type == PERF_RECORD_MMAP)
+ if (event->header.type == PERF_RECORD_MMAP
+ || event->header.type == PERF_RECORD_MMAP2)
pid = event->mmap.pid;
else
pid = event->ip.pid;
@@ -975,6 +996,8 @@ static int perf_session_deliver_event(struct perf_session *session,
sample, evsel, machine);
case PERF_RECORD_MMAP:
return tool->mmap(tool, event, sample, machine);
+ case PERF_RECORD_MMAP2:
+ return tool->mmap2(tool, event, sample, machine);
case PERF_RECORD_COMM:
return tool->comm(tool, event, sample, machine);
case PERF_RECORD_FORK:
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index 62b16b6..4385816 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -29,6 +29,7 @@ struct perf_tool {
event_sample sample,
read;
event_op mmap,
+ mmap2,
comm,
fork,
exit,
--
1.7.9.5

2013-08-13 23:34:51

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH v1 2/2] perf tools: add attr->mmap2 support

On Tue, Aug 13, 2013 at 01:55:57PM +0200, Stephane Eranian wrote:
> This patch adds support for the new PERF_RECORD_MMAP2
> record type exposed by the kernel. This is an extended
> PERF_RECORD_MMAP record. It adds for each file-backed
> mapping the device major, minor number and the inode
> number. This triplet uniquely identifies the source
> of a file-backed mapping. It can be used to detect
> identical virtual mappings between processes for instance.

Can you also add the generation number please?
That would make it even more unique.

-Andi

2013-08-14 10:30:11

by Stephane Eranian

[permalink] [raw]
Subject: Re: [PATCH v1 2/2] perf tools: add attr->mmap2 support

On Wed, Aug 14, 2013 at 1:34 AM, Andi Kleen <[email protected]> wrote:
> On Tue, Aug 13, 2013 at 01:55:57PM +0200, Stephane Eranian wrote:
>> This patch adds support for the new PERF_RECORD_MMAP2
>> record type exposed by the kernel. This is an extended
>> PERF_RECORD_MMAP record. It adds for each file-backed
>> mapping the device major, minor number and the inode
>> number. This triplet uniquely identifies the source
>> of a file-backed mapping. It can be used to detect
>> identical virtual mappings between processes for instance.
>
> Can you also add the generation number please?
> That would make it even more unique.
>
I assume you're talkng about inode->i_generation, right?
I could add that one easily. I am surprised to see:

mm/shmem.c:
static struct inode *shmem_get_inode(struct super_block *sb, const
struct inode *dir,
umode_t mode, dev_t dev, unsigned
long flags)
{
inode->i_generation = get_seconds();
}
I am assuming that the uniqueness for SHM is coming from the SHMID and therefore
a poor quality i_generation is sufficient here.

2013-08-14 16:44:55

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH v1 2/2] perf tools: add attr->mmap2 support

On Wed, Aug 14, 2013 at 12:30:08PM +0200, Stephane Eranian wrote:
> On Wed, Aug 14, 2013 at 1:34 AM, Andi Kleen <[email protected]> wrote:
> > On Tue, Aug 13, 2013 at 01:55:57PM +0200, Stephane Eranian wrote:
> >> This patch adds support for the new PERF_RECORD_MMAP2
> >> record type exposed by the kernel. This is an extended
> >> PERF_RECORD_MMAP record. It adds for each file-backed
> >> mapping the device major, minor number and the inode
> >> number. This triplet uniquely identifies the source
> >> of a file-backed mapping. It can be used to detect
> >> identical virtual mappings between processes for instance.
> >
> > Can you also add the generation number please?
> > That would make it even more unique.
> >
> I assume you're talkng about inode->i_generation, right?

Yes.

> I could add that one easily. I am surprised to see:
>
> mm/shmem.c:
> static struct inode *shmem_get_inode(struct super_block *sb, const
> struct inode *dir,
> umode_t mode, dev_t dev, unsigned
> long flags)
> {
> inode->i_generation = get_seconds();
> }
> I am assuming that the uniqueness for SHM is coming from the SHMID and therefore
> a poor quality i_generation is sufficient here.

Yes that's odd. But most file system should have a sensible generation
number.

-Andi

--
[email protected] -- Speaking for myself only

2013-08-15 10:26:38

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH v1 1/2] perf: add attr->mmap2 attribute to an event

On Tue, Aug 13, 2013 at 01:55:56PM +0200, Stephane Eranian wrote:

> @@ -638,6 +639,25 @@ enum perf_event_type {
> */
> PERF_RECORD_SAMPLE = 9,
>
> + /*
> + * The MMAP2 records are an augmented version of MMAP, they add
> + * maj, min, ino numbers to be used to uniquely identify each mapping
> + *
> + * struct {
> + * struct perf_event_header header;
> + *
> + * u32 pid, tid;
> + * u64 addr;
> + * u64 len;
> + * u64 pgoff;
> + * u32 maj;
> + * u32 min;
> + * u64 ino;
> + * char filename[];

Did you forget the sample_id stuff?

struct sample_id sample_id;

> + * };
> + */
> + PERF_RECORD_MMAP2 = 10,
> +
> PERF_RECORD_MAX, /* non-ABI */
> };

2013-08-19 09:14:20

by Stephane Eranian

[permalink] [raw]
Subject: Re: [PATCH v1 1/2] perf: add attr->mmap2 attribute to an event

On Thu, Aug 15, 2013 at 12:26 PM, Peter Zijlstra <[email protected]> wrote:
> On Tue, Aug 13, 2013 at 01:55:56PM +0200, Stephane Eranian wrote:
>
>> @@ -638,6 +639,25 @@ enum perf_event_type {
>> */
>> PERF_RECORD_SAMPLE = 9,
>>
>> + /*
>> + * The MMAP2 records are an augmented version of MMAP, they add
>> + * maj, min, ino numbers to be used to uniquely identify each mapping
>> + *
>> + * struct {
>> + * struct perf_event_header header;
>> + *
>> + * u32 pid, tid;
>> + * u64 addr;
>> + * u64 len;
>> + * u64 pgoff;
>> + * u32 maj;
>> + * u32 min;
>> + * u64 ino;
>> + * char filename[];
>
> Did you forget the sample_id stuff?
>
> struct sample_id sample_id;
>
Ok, will add that and the i_generation field.

2013-08-21 09:55:09

by Stephane Eranian

[permalink] [raw]
Subject: Re: [PATCH v1 1/2] perf: add attr->mmap2 attribute to an event

On Thu, Aug 15, 2013 at 12:26 PM, Peter Zijlstra <[email protected]> wrote:
>
> On Tue, Aug 13, 2013 at 01:55:56PM +0200, Stephane Eranian wrote:
>
> > @@ -638,6 +639,25 @@ enum perf_event_type {
> > */
> > PERF_RECORD_SAMPLE = 9,
> >
> > + /*
> > + * The MMAP2 records are an augmented version of MMAP, they add
> > + * maj, min, ino numbers to be used to uniquely identify each mapping
> > + *
> > + * struct {
> > + * struct perf_event_header header;
> > + *
> > + * u32 pid, tid;
> > + * u64 addr;
> > + * u64 len;
> > + * u64 pgoff;
> > + * u32 maj;
> > + * u32 min;
> > + * u64 ino;
> > + * char filename[];
>
> Did you forget the sample_id stuff?
>
> struct sample_id sample_id;
>
This is added automatically by the kernel if attr->sample_id_all is
set. I checked the perf tool. It enables this when you're sampling
on more than one event. So everything works as expected.
I assume you comment was about adding the sample_id in the
description of the struct. I will add that.


>
> > + * };
> > + */
> > + PERF_RECORD_MMAP2 = 10,
> > +
> > PERF_RECORD_MAX, /* non-ABI */
> > };

2013-08-21 09:56:47

by Stephane Eranian

[permalink] [raw]
Subject: Re: [PATCH v1 1/2] perf: add attr->mmap2 attribute to an event

On Mon, Aug 19, 2013 at 11:14 AM, Stephane Eranian <[email protected]> wrote:
> On Thu, Aug 15, 2013 at 12:26 PM, Peter Zijlstra <[email protected]> wrote:
>> On Tue, Aug 13, 2013 at 01:55:56PM +0200, Stephane Eranian wrote:
>>
>>> @@ -638,6 +639,25 @@ enum perf_event_type {
>>> */
>>> PERF_RECORD_SAMPLE = 9,
>>>
>>> + /*
>>> + * The MMAP2 records are an augmented version of MMAP, they add
>>> + * maj, min, ino numbers to be used to uniquely identify each mapping
>>> + *
>>> + * struct {
>>> + * struct perf_event_header header;
>>> + *
>>> + * u32 pid, tid;
>>> + * u64 addr;
>>> + * u64 len;
>>> + * u64 pgoff;
>>> + * u32 maj;
>>> + * u32 min;
>>> + * u64 ino;
>>> + * char filename[];
>>
>> Did you forget the sample_id stuff?
>>
>> struct sample_id sample_id;
>>
> Ok, will add that and the i_generation field.

Added that now, works fine. Except it won't be availa when synthesizing
MMAP records from /proc/PID/maps because it is not displayed there.
There is not much we can do about this. And of course, there is not i_generation
for kernel level ELF images.