PATCH[2/3 RFC] PERF, Add remapping support for jit'ed code to perf

From: Carl Love
Date: Fri Feb 06 2015 - 11:41:17 EST


This is a repost to LKML of the patches I sent to the linux-perf-users mailing list.
I am reposting it to lkml per Arnaldo Carvalho's request.


-------------------------------------------------------------------------

Added uevent call to process the remap request.

The patch adds the calls to process the uevent which contains the
remap request. An mmap2 request is generated by the function
machine__process_uevents_event() which then calls the function
machine__process_mmap2_event() to do the remapping.

This patch is not done. I have been requested to post the patch so people
can see where I am at with it. I appreciate feedback on the overall approach
an suggestions for improvements. It has only been tested on a very simple
workload. In the test, the callback library forces a remap for every method
add just to exercise the remapping code. The jit library is also under
development. The goal is the jit library will only call for a remap when
it is really needed.

Signed-off-by: Carl Love <cel@xxxxxxxxxx>
---
tools/perf/builtin-record.c | 2 ++
tools/perf/builtin-report.c | 1 +
tools/perf/builtin-script.c | 5 +++
tools/perf/util/event.c | 81 +++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/event.h | 27 +++++++++++++++
tools/perf/util/machine.c | 37 +++++++++++++++++++++
tools/perf/util/machine.h | 2 ++
tools/perf/util/map.c | 11 ++++--
tools/perf/util/record.c | 2 ++
tools/perf/util/session.c | 4 +++
tools/perf/util/tool.h | 3 +-
11 files changed, 172 insertions(+), 3 deletions(-)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 8648c6d..9b836f3 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -146,6 +146,8 @@ static int record__open(struct record *rec)

evlist__for_each(evlist, pos) {
try_again:
+ pos->attr.uevents = 1;
+
if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
if (verbose)
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 072ae8a..5403c6f 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -607,6 +607,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
.sample = process_sample_event,
.mmap = perf_event__process_mmap,
.mmap2 = perf_event__process_mmap2,
+ .uevents = perf_event__process_uevents,
.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 ce304df..59bc24d 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -608,6 +608,11 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
evlist = *pevlist;
evsel = perf_evlist__last(*pevlist);

+ /* enable the jit data delivery. This needs to be tied to a command
+ * line argument or something. FIXME
+ */
+ evsel->attr.uevents = 1;
+
if (evsel->attr.type >= PERF_TYPE_MAX)
return 0;

diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 6c6d044..d4c2f20 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -29,6 +29,7 @@ static const char *perf_event__names[] = {
[PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
[PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND",
[PERF_RECORD_ID_INDEX] = "ID_INDEX",
+ [PERF_RECORD_UEVENT] = "UEVENT",
};

const char *perf_event__name(unsigned int id)
@@ -154,6 +155,70 @@ static int perf_event__synthesize_fork(struct perf_tool *tool,
return 0;
}

+int perf_event__synthesize_jit_mmap_events(union perf_event *event,
+ char *filename,
+ u32 pid, u32 tid)
+{
+ FILE *fp;
+ unsigned long long start;
+ int len;
+ unsigned long long min_start = 0, max_stop = 0;
+ char sym_name[100];
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ fprintf(stderr,
+ "ERROR, perf_event__synthesize_jit_mmap_events() %s.\n",
+ filename);
+ }
+
+ event->header.type = PERF_RECORD_MMAP2;
+ /* need to determine the start of the address space and the maximum
+ * size used in the map file.
+ */
+ while (1) {
+ int n;
+
+ /* format of the file is:
+ <start addr> <length> <symbol name>
+ */
+ n = fscanf(fp, "%llx %x %s \n",
+ &start, &len, sym_name);
+
+ if (n == -1)
+ break; /* finished reading */
+
+ if (n != 3) {
+ fprintf(stderr,
+ "ERROR, machine__process_uevents_event() failed reading file %s\n",
+ filename);
+ return -1;
+ }
+ if ((min_start == 0) || (start < min_start))
+ min_start = start;
+
+ if ((max_stop == 0) || (start + len > max_stop))
+ max_stop = start+len;
+ }
+ fclose(fp);
+
+ event->header.type = PERF_RECORD_MMAP2;
+ event->mmap2.start = min_start;
+ event->mmap2.len = max_stop - min_start;
+ event->mmap2.pid = pid;
+ event->mmap2.tid = tid;
+ event->mmap2.pgoff = min_start;
+ event->mmap2.maj = 0;
+ event->mmap2.min = 0;
+ event->mmap2.ino = 0;
+ event->mmap2.ino_generation = 0;
+ event->mmap2.prot = 0;
+ event->mmap2.flags = 0;
+
+ strcpy(event->mmap2.filename, filename);
+ return 0;
+}
+
int perf_event__synthesize_mmap_events(struct perf_tool *tool,
union perf_event *event,
pid_t pid, pid_t tgid,
@@ -634,6 +699,19 @@ int perf_event__process_lost(struct perf_tool *tool __maybe_unused,
return machine__process_lost_event(machine, event, sample);
}

+int perf_event__process_uevents(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine)
+{
+ return machine__process_uevents_event(machine, event, sample);
+}
+
+size_t perf_event__fprintf_uevent(union perf_event *event, FILE *fp)
+{
+ return fprintf(fp, " %d/%d: (CARLL, FIXME NOT DONE YET\n", event->uevent.type, event->uevent.size);
+}
+
size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
{
return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n",
@@ -716,6 +794,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
case PERF_RECORD_MMAP2:
ret += perf_event__fprintf_mmap2(event, fp);
break;
+ case PERF_RECORD_UEVENT:
+ ret += perf_event__fprintf_uevent(event, fp);
+ break;
default:
ret += fprintf(fp, "\n");
}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index c4ffe2b..7dc6515 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -3,6 +3,7 @@

#include <limits.h>
#include <stdio.h>
+#include <string.h>

#include "../perf.h"
#include "map.h"
@@ -52,6 +53,23 @@ struct lost_event {
u64 lost;
};

+/* Copy of the perf data struct from the library. Need to have it
+ * in some common include file. For now it is a hack.
+ */
+struct jit_data_st {
+ char map_filename[50];
+ u32 pid;
+ u32 tid;
+};
+
+struct uevent_event {
+ struct perf_event_header header;
+ u32 type;
+ u32 size;
+ char data[50]; // NEEDS WORK
+ char __padding[-50 & 7];
+};
+
/*
* PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
*/
@@ -296,6 +314,7 @@ union perf_event {
struct tracing_data_event tracing_data;
struct build_id_event build_id;
struct id_index_event id_index;
+ struct uevent_event uevent;
};

void perf_event__print_totals(void);
@@ -347,6 +366,13 @@ int perf_event__process_exit(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct machine *machine);
+int perf_event__synthesize_jit_mmap_events(union perf_event *event,
+ char *filename,
+ u32 pid, u32 tid);
+int perf_event__process_uevents(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
int perf_event__process(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
@@ -387,6 +413,7 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
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_uevent(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/machine.c b/tools/perf/util/machine.c
index 94de3e4..887020e 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -460,6 +460,41 @@ int machine__process_lost_event(struct machine *machine __maybe_unused,
return 0;
}

+int machine__process_uevents_event(struct machine *machine __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused)
+{
+ union perf_event *new_event;
+ struct jit_data_st *jit_data;
+ int rc = 0;
+
+ /* the binary data is sent in a character string, scarey but it seems
+ * to work. Need to consider if this is really the right way of
+ * doing things.
+ */
+ jit_data = (struct jit_data_st *) event->uevent.data;
+
+ new_event = malloc(sizeof(new_event->mmap2) + machine->id_hdr_size);
+ if (new_event == NULL) {
+ fprintf(stderr,
+ "ERROR, machine__process_uevents_event() failed to malloc event\n");
+ return -1;
+ }
+
+ event = new_event;
+
+ /* setup event */
+ rc = perf_event__synthesize_jit_mmap_events(event,
+ jit_data->map_filename,
+ jit_data->pid,
+ jit_data->tid);
+
+
+ /* call machine__process_mmap_event() to do the remap of the file */
+ rc = machine__process_mmap2_event(machine, event, sample);
+ return rc;
+}
+
struct map *machine__new_module(struct machine *machine, u64 start,
const char *filename)
{
@@ -1307,6 +1342,8 @@ int machine__process_event(struct machine *machine, union perf_event *event,
ret = machine__process_exit_event(machine, event, sample); break;
case PERF_RECORD_LOST:
ret = machine__process_lost_event(machine, event, sample); break;
+ case PERF_RECORD_UEVENT:
+ ret = machine__process_uevents_event(machine, event, sample); break;
default:
ret = -1;
break;
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index e8b7779..d3f8f52 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -85,6 +85,8 @@ 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,
struct perf_sample *sample);
+int machine__process_uevents_event(struct machine *machine, union perf_event *event,
+ struct perf_sample *sample);
int machine__process_event(struct machine *machine, union perf_event *event,
struct perf_sample *sample);

diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 62ca9f2..babcf36 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -41,6 +41,12 @@ static inline int is_android_lib(const char *filename)
!strncmp(filename, "/system/lib", 11);
}

+static inline int is_jit_map(const char *filename)
+{
+ return (strncmp(filename, "/tmp/perf-", 10) == 0);
+}
+
+
static inline bool replace_android_lib(const char *filename, char *newfilename)
{
const char *libname;
@@ -149,12 +155,13 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
if (map != NULL) {
char newfilename[PATH_MAX];
struct dso *dso;
- int anon, no_dso, vdso, android;
+ int anon, no_dso, vdso, android, jit;

android = is_android_lib(filename);
anon = is_anon_memory(filename);
vdso = is_vdso_map(filename);
no_dso = is_no_dso_memory(filename);
+ jit = is_jit_map(filename);

map->maj = d_maj;
map->min = d_min;
@@ -184,7 +191,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,

map__init(map, type, start, start + len, pgoff, dso);

- if (anon || no_dso) {
+ if (anon || no_dso || jit) {
map->map_ip = map->unmap_ip = identity__map_ip;

/*
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c
index 8acd0df..a0abb25 100644
--- a/tools/perf/util/record.c
+++ b/tools/perf/util/record.c
@@ -24,6 +24,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
goto out_delete;

evsel = perf_evlist__first(evlist);
+ evsel->attr.uevents = 1; // CARLL ADDED, don't remember if this is needed or not? Doesn't hurt anything at the moment.

while (1) {
fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags);
@@ -221,6 +222,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
} else {
cpu = evlist->cpus->map[0];
}
+ evsel->attr.uevents = 1; // CARLL ADDED, don't remember if this was needed or not. FIX

while (1) {
fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1,
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 5f0e05a..cf19604 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -253,6 +253,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->exit = process_event_stub;
if (tool->lost == NULL)
tool->lost = perf_event__process_lost;
+ if (tool->uevents == NULL)
+ tool->uevents = process_event_stub;
if (tool->read == NULL)
tool->read = process_event_sample_stub;
if (tool->throttle == NULL)
@@ -885,6 +887,8 @@ int perf_session__deliver_event(struct perf_session *session,
if (tool->lost == perf_event__process_lost)
session->stats.total_lost += event->lost.lost;
return tool->lost(tool, event, sample, machine);
+ case PERF_RECORD_UEVENT:
+ return tool->uevents(tool, event, sample, machine);
case PERF_RECORD_READ:
return tool->read(tool, event, sample, evsel, machine);
case PERF_RECORD_THROTTLE:
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index bb2708b..ed67a12 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -35,7 +35,8 @@ struct perf_tool {
exit,
lost,
throttle,
- unthrottle;
+ unthrottle,
+ uevents;
event_attr_op attr;
event_op2 tracing_data;
event_op2 finished_round,
--
1.8.3.1





--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/