2010-01-04 18:19:43

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 1/4] perf symbols: Generalise the kallsyms parsing routine

From: Arnaldo Carvalho de Melo <[email protected]>

Will be used to find an specific symbol by name on 'perf record' to support
relocation reference symbols to support relocatable kernels.

Still have to conver the perf trace tools to use it instead of their current
reimplementation.

Cc: Frédéric Weisbecker <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/symbol.c | 76 +++++++++++++++++++++++++++++----------------
tools/perf/util/symbol.h | 2 +
2 files changed, 51 insertions(+), 27 deletions(-)

diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 79ca6a0..b9e0da5 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -383,16 +383,12 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
return ret;
}

-/*
- * Loads the function entries in /proc/kallsyms into kernel_map->dso,
- * so that we can in the next step set the symbol ->end address and then
- * call kernel_maps__split_kallsyms.
- */
-static int dso__load_all_kallsyms(struct dso *self, struct map *map)
+int kallsyms__parse(void *arg, int (*process_symbol)(void *arg, const char *name,
+ char type, u64 start))
{
char *line = NULL;
size_t n;
- struct rb_root *root = &self->symbols[map->type];
+ int err = 0;
FILE *file = fopen("/proc/kallsyms", "r");

if (file == NULL)
@@ -400,7 +396,6 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map)

while (!feof(file)) {
u64 start;
- struct symbol *sym;
int line_len, len;
char symbol_type;
char *symbol_name;
@@ -421,35 +416,62 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map)
continue;

symbol_type = toupper(line[len]);
- if (!symbol_type__is_a(symbol_type, map->type))
- continue;
-
symbol_name = line + len + 2;
- /*
- * Will fix up the end later, when we have all symbols sorted.
- */
- sym = symbol__new(start, 0, symbol_name);

- if (sym == NULL)
- goto out_delete_line;
- /*
- * We will pass the symbols to the filter later, in
- * map__split_kallsyms, when we have split the maps per module
- */
- symbols__insert(root, sym);
+ err = process_symbol(arg, symbol_name, symbol_type, start);
+ if (err)
+ break;
}

free(line);
fclose(file);
+ return err;

- return 0;
-
-out_delete_line:
- free(line);
out_failure:
return -1;
}

+struct process_kallsyms_args {
+ struct map *map;
+ struct dso *dso;
+};
+
+static int map__process_kallsym_symbol(void *arg, const char *name,
+ char type, u64 start)
+{
+ struct symbol *sym;
+ struct process_kallsyms_args *a = arg;
+ struct rb_root *root = &a->dso->symbols[a->map->type];
+
+ if (!symbol_type__is_a(type, a->map->type))
+ return 0;
+
+ /*
+ * Will fix up the end later, when we have all symbols sorted.
+ */
+ sym = symbol__new(start, 0, name);
+
+ if (sym == NULL)
+ return -ENOMEM;
+ /*
+ * We will pass the symbols to the filter later, in
+ * map__split_kallsyms, when we have split the maps per module
+ */
+ symbols__insert(root, sym);
+ return 0;
+}
+
+/*
+ * Loads the function entries in /proc/kallsyms into kernel_map->dso,
+ * so that we can in the next step set the symbol ->end address and then
+ * call kernel_maps__split_kallsyms.
+ */
+static int dso__load_all_kallsyms(struct dso *self, struct map *map)
+{
+ struct process_kallsyms_args args = { .map = map, .dso = self, };
+ return kallsyms__parse(&args, map__process_kallsym_symbol);
+}
+
/*
* Split the symbols into maps, making sure there are no overlaps, i.e. the
* kernel range is broken in several maps, named [kernel].N, as we don't have
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index f27e158..21313e8 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -135,6 +135,8 @@ int filename__read_build_id(const char *filename, void *bf, size_t size);
int sysfs__read_build_id(const char *filename, void *bf, size_t size);
bool dsos__read_build_ids(void);
int build_id__sprintf(u8 *self, int len, char *bf);
+int kallsyms__parse(void *arg, int (*process_symbol)(void *arg, const char *name,
+ char type, u64 start));

int symbol__init(void);
int perf_session__create_kernel_maps(struct perf_session *self);
--
1.6.2.5


2010-01-04 18:19:46

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 4/4] perf session: Keep pointers to the vmlinux maps

From: Arnaldo Carvalho de Melo <[email protected]>

So that tools such as 'perf probe' don't have to lookup '[kernel.kallsyms]' but
instead access them directly after perf_session__create_kernel_maps or
map_groups__create_kernel_maps.

Cc: Masami Hiramatsu <[email protected]>
Cc: Frédéric Weisbecker <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/builtin-probe.c | 4 +---
tools/perf/util/session.h | 1 +
tools/perf/util/symbol.c | 29 +++++++++++++----------------
3 files changed, 15 insertions(+), 19 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index c1e6774..ffdd3fe 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -235,9 +235,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
session.psession = perf_session__new(NULL, O_WRONLY, false);
if (session.psession == NULL)
die("Failed to init perf_session.");
- session.kmap = map_groups__find_by_name(&session.psession->kmaps,
- MAP__FUNCTION,
- "[kernel.kallsyms]");
+ session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION];
if (!session.kmap)
die("Could not find kernel map.\n");

diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 77c5ee2..8db37bb 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -18,6 +18,7 @@ struct perf_session {
struct map_groups kmaps;
struct rb_root threads;
struct thread *last_match;
+ struct map *vmlinux_maps[MAP__NR_TYPES];
struct events_stats events_stats;
unsigned long event_total[PERF_RECORD_MAX];
unsigned long unknown_events;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 5dffcd1..e290429 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1662,7 +1662,7 @@ size_t dsos__fprintf_buildid(FILE *fp)
__dsos__fprintf_buildid(&dsos__user, fp));
}

-static struct dso *dsos__create_kernel( const char *vmlinux)
+static struct dso *dsos__create_kernel(const char *vmlinux)
{
struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]");

@@ -1691,29 +1691,26 @@ out_delete_kernel_dso:
return NULL;
}

-static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux)
+static int map_groups__create_kernel_maps(struct map_groups *self,
+ struct map *vmlinux_maps[MAP__NR_TYPES],
+ const char *vmlinux)
{
- struct map *functions, *variables;
struct dso *kernel = dsos__create_kernel(vmlinux);
+ enum map_type type;

if (kernel == NULL)
return -1;

- functions = map__new2(0, kernel, MAP__FUNCTION);
- if (functions == NULL)
- return -1;
+ for (type = 0; type < MAP__NR_TYPES; ++type) {
+ vmlinux_maps[type] = map__new2(0, kernel, type);
+ if (vmlinux_maps[type] == NULL)
+ return -1;

- variables = map__new2(0, kernel, MAP__VARIABLE);
- if (variables == NULL) {
- map__delete(functions);
- return -1;
+ vmlinux_maps[type]->map_ip =
+ vmlinux_maps[type]->unmap_ip = identity__map_ip;
+ map_groups__insert(self, vmlinux_maps[type]);
}

- functions->map_ip = functions->unmap_ip =
- variables->map_ip = variables->unmap_ip = identity__map_ip;
- map_groups__insert(self, functions);
- map_groups__insert(self, variables);
-
return 0;
}

@@ -1824,7 +1821,7 @@ out_free_comm_list:

int perf_session__create_kernel_maps(struct perf_session *self)
{
- if (map_groups__create_kernel_maps(&self->kmaps,
+ if (map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps,
symbol_conf.vmlinux_name) < 0)
return -1;

--
1.6.2.5

2010-01-04 18:19:52

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 2/4] perf symbols: Export symbol_type__is_a

From: Arnaldo Carvalho de Melo <[email protected]>

Will be needed by the new HEADER_DSO_INFO feature that will be a
HEADER_BUILD_ID superset, replacing it.

Cc: Frédéric Weisbecker <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/symbol.c | 2 +-
tools/perf/util/symbol.h | 2 ++
2 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b9e0da5..5dffcd1 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -64,7 +64,7 @@ static void dso__set_sorted_by_name(struct dso *self, enum map_type type)
self->sorted_by_name |= (1 << type);
}

-static bool symbol_type__is_a(char symbol_type, enum map_type map_type)
+bool symbol_type__is_a(char symbol_type, enum map_type map_type)
{
switch (map_type) {
case MAP__FUNCTION:
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 21313e8..b2b5330 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -139,6 +139,8 @@ int kallsyms__parse(void *arg, int (*process_symbol)(void *arg, const char *name
char type, u64 start));

int symbol__init(void);
+bool symbol_type__is_a(char symbol_type, enum map_type map_type);
+
int perf_session__create_kernel_maps(struct perf_session *self);

extern struct list_head dsos__user, dsos__kernel;
--
1.6.2.5

2010-01-04 18:20:21

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 3/4] perf tools: Create write_padded routine out of __dsos__write_buildid_table

From: Arnaldo Carvalho de Melo <[email protected]>

Will be used by other options where padding is needed.

Cc: Frédéric Weisbecker <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Paul Mackerras <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/header.c | 22 ++++++++++++++++------
1 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 709e325..942f7da 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -173,6 +173,20 @@ static int do_write(int fd, const void *buf, size_t size)
return 0;
}

+#define NAME_ALIGN 64
+
+static int write_padded(int fd, const void *bf, size_t count,
+ size_t count_aligned)
+{
+ static const char zero_buf[NAME_ALIGN];
+ int err = do_write(fd, bf, count);
+
+ if (!err)
+ err = do_write(fd, zero_buf, count_aligned - count);
+
+ return err;
+}
+
#define dsos__for_each_with_build_id(pos, head) \
list_for_each_entry(pos, head, node) \
if (!pos->has_build_id) \
@@ -181,9 +195,7 @@ static int do_write(int fd, const void *buf, size_t size)

static int __dsos__write_buildid_table(struct list_head *head, int fd)
{
-#define NAME_ALIGN 64
struct dso *pos;
- static const char zero_buf[NAME_ALIGN];

dsos__for_each_with_build_id(pos, head) {
int err;
@@ -197,10 +209,8 @@ static int __dsos__write_buildid_table(struct list_head *head, int fd)
err = do_write(fd, &b, sizeof(b));
if (err < 0)
return err;
- err = do_write(fd, pos->long_name, pos->long_name_len + 1);
- if (err < 0)
- return err;
- err = do_write(fd, zero_buf, len - pos->long_name_len - 1);
+ err = write_padded(fd, pos->long_name,
+ pos->long_name_len + 1, len);
if (err < 0)
return err;
}
--
1.6.2.5

2010-01-04 20:07:47

by Masami Hiramatsu

[permalink] [raw]
Subject: Re: [PATCH 4/4] perf session: Keep pointers to the vmlinux maps

Arnaldo Carvalho de Melo wrote:
> From: Arnaldo Carvalho de Melo <[email protected]>
>
> So that tools such as 'perf probe' don't have to lookup '[kernel.kallsyms]' but
> instead access them directly after perf_session__create_kernel_maps or
> map_groups__create_kernel_maps.


Looks good for me:-) Thanks.

Acked-by: Masami Hiramatsu <[email protected]>

>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Frédéric Weisbecker <[email protected]>
> Cc: Mike Galbraith <[email protected]>
> Cc: Peter Zijlstra <[email protected]>
> Cc: Paul Mackerras <[email protected]>
> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
> ---
> tools/perf/builtin-probe.c | 4 +---
> tools/perf/util/session.h | 1 +
> tools/perf/util/symbol.c | 29 +++++++++++++----------------
> 3 files changed, 15 insertions(+), 19 deletions(-)
>
> diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
> index c1e6774..ffdd3fe 100644
> --- a/tools/perf/builtin-probe.c
> +++ b/tools/perf/builtin-probe.c
> @@ -235,9 +235,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
> session.psession = perf_session__new(NULL, O_WRONLY, false);
> if (session.psession == NULL)
> die("Failed to init perf_session.");
> - session.kmap = map_groups__find_by_name(&session.psession->kmaps,
> - MAP__FUNCTION,
> - "[kernel.kallsyms]");
> + session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION];
> if (!session.kmap)
> die("Could not find kernel map.\n");
>
> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> index 77c5ee2..8db37bb 100644
> --- a/tools/perf/util/session.h
> +++ b/tools/perf/util/session.h
> @@ -18,6 +18,7 @@ struct perf_session {
> struct map_groups kmaps;
> struct rb_root threads;
> struct thread *last_match;
> + struct map *vmlinux_maps[MAP__NR_TYPES];
> struct events_stats events_stats;
> unsigned long event_total[PERF_RECORD_MAX];
> unsigned long unknown_events;
> diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
> index 5dffcd1..e290429 100644
> --- a/tools/perf/util/symbol.c
> +++ b/tools/perf/util/symbol.c
> @@ -1662,7 +1662,7 @@ size_t dsos__fprintf_buildid(FILE *fp)
> __dsos__fprintf_buildid(&dsos__user, fp));
> }
>
> -static struct dso *dsos__create_kernel( const char *vmlinux)
> +static struct dso *dsos__create_kernel(const char *vmlinux)
> {
> struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]");
>
> @@ -1691,29 +1691,26 @@ out_delete_kernel_dso:
> return NULL;
> }
>
> -static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux)
> +static int map_groups__create_kernel_maps(struct map_groups *self,
> + struct map *vmlinux_maps[MAP__NR_TYPES],
> + const char *vmlinux)
> {
> - struct map *functions, *variables;
> struct dso *kernel = dsos__create_kernel(vmlinux);
> + enum map_type type;
>
> if (kernel == NULL)
> return -1;
>
> - functions = map__new2(0, kernel, MAP__FUNCTION);
> - if (functions == NULL)
> - return -1;
> + for (type = 0; type < MAP__NR_TYPES; ++type) {
> + vmlinux_maps[type] = map__new2(0, kernel, type);
> + if (vmlinux_maps[type] == NULL)
> + return -1;
>
> - variables = map__new2(0, kernel, MAP__VARIABLE);
> - if (variables == NULL) {
> - map__delete(functions);
> - return -1;
> + vmlinux_maps[type]->map_ip =
> + vmlinux_maps[type]->unmap_ip = identity__map_ip;
> + map_groups__insert(self, vmlinux_maps[type]);
> }
>
> - functions->map_ip = functions->unmap_ip =
> - variables->map_ip = variables->unmap_ip = identity__map_ip;
> - map_groups__insert(self, functions);
> - map_groups__insert(self, variables);
> -
> return 0;
> }
>
> @@ -1824,7 +1821,7 @@ out_free_comm_list:
>
> int perf_session__create_kernel_maps(struct perf_session *self)
> {
> - if (map_groups__create_kernel_maps(&self->kmaps,
> + if (map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps,
> symbol_conf.vmlinux_name) < 0)
> return -1;
>

--
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: [email protected]