2012-06-22 05:41:23

by Namhyung Kim

[permalink] [raw]
Subject: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

Hi,

Current build procedure of perf tools requires a couple of external
libraries. Although most of them are optional, elfutils' libelf is
mandatory for resolving symbols and build-id of the binaries being
profiled. For some reason, it makes embedded guys' life harder who
want to build and run perf tools on their boards.

This patchset tries to fix the problem by letting the perf could
be built without the libelf. The initial intent was just providing
perf record command to gather performance events interested on the
system and copies perf.data to development box to be used by (full-
fledged) perf report for analysis.

However it's changed to have most of perf commands as is because
perf can resolve kernel symbols without libelf and many of perf
commands deal (mostly) with kernel events - so no need to restrict
capability of perf tools. Therefore, the end result is not so
minimalistic actually and the only thing it cannot do is "perf probe".

And then I realized that the perf record needs to know about the
build-id's anyway. :( So I implemented a poor man's version of elf
parser only for parsing the build-id info.

In addition, I changed my mind to keep unrelated configurations -
i.g. tui/gui and perf/python support - enabled by default so that
it can be configured by a find-grained config method in the future.

To build a minimal perf tools explicitly, pass NO_LIBELF=1 to make.
Or, if the system doesn't provide the elfutils it'll detect that
and converts to the minimal build mode automatically. The resulting
perf report will not display symbol names in userland:

# Samples: 3K of event 'cycles'
# Event count (approx.): 3740267998
#
# Overhead Command Shared Object Symbol
# ........ ....... ................. ..............................
#
99.70% noploop noploop [.] 0x000000000000066d
0.10% noploop [kernel.kallsyms] [k] free_pgd_range
0.03% noploop [kernel.kallsyms] [k] native_write_msr_safe
0.03% noploop [kernel.kallsyms] [k] raise_softirq
0.03% noploop [kernel.kallsyms] [k] rb_insert_color

But perf buildid-list can show the build-id's:

$ ./perf buildid-list --with-hits
5eaf1839576cc801053e63300762def90a77a305 [kernel.kallsyms]
43a7a2b399b6ee2ff29c6bdadbda6bff88712ed4 /home/namhyung/bin/noploop

So it can be packed using perf archive command and copied and
analized with full-fledged perf tools on a development machine.

This patchset is based on current tip 32c46e579b68 ("Merge tag
'perf-core-for-mingo' ...") and tested on x86_64 and ARM.

Any comments are welcome.
Namhyung

v2:
* change make option name to NO_LIBELF
* make it default when libelf doesn't exist (by Ingo)
* get rid of elf-minimal.h and use elf.h (by Jiri)


Namhyung Kim (8):
perf evsel: Fix a build failure on cross compilation
tools lib traceevent: Make dependency files regeneratable
tools lib traceevent: Detect build environment changes
perf symbols: Introduce symbol__elf_init()
perf symbols: Do not use ELF's symbol binding constants
perf tools: Split out util/symbol-elf.c
perf tools: Support minimal build without libelf
perf symbols: Implement poor man's ELF parser

tools/lib/traceevent/Makefile | 18 +-
tools/perf/Makefile | 58 ++-
tools/perf/builtin-buildid-list.c | 4 +-
tools/perf/builtin-inject.c | 5 +-
tools/perf/builtin-top.c | 5 +-
tools/perf/command-list.txt | 2 +-
tools/perf/perf.c | 2 +
tools/perf/ui/browsers/map.c | 5 +-
tools/perf/util/evsel.c | 1 -
tools/perf/util/generate-cmdlist.sh | 15 +
tools/perf/util/map.c | 3 +-
tools/perf/util/symbol-elf.c | 719 ++++++++++++++++++++++++++++++++++
tools/perf/util/symbol-minimal.c | 261 ++++++++++++
tools/perf/util/symbol.c | 740 +----------------------------------
tools/perf/util/symbol.h | 20 +
15 files changed, 1110 insertions(+), 748 deletions(-)
create mode 100644 tools/perf/util/symbol-elf.c
create mode 100644 tools/perf/util/symbol-minimal.c

--
1.7.10.2


2012-06-22 05:41:28

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 4/8] perf symbols: Introduce symbol__elf_init()

From: Namhyung Kim <[email protected]>

The symbol__elf_init() is for initializing internal libelf
data structure and getting rid of its dependency outside of
ELF/symboling handling code.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/builtin-buildid-list.c | 4 +---
tools/perf/util/symbol.c | 8 +++++++-
tools/perf/util/symbol.h | 1 +
3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 6b2bcfbde150..7d6842826a0c 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -16,8 +16,6 @@
#include "util/session.h"
#include "util/symbol.h"

-#include <libelf.h>
-
static const char *input_name;
static bool force;
static bool show_kernel;
@@ -71,7 +69,7 @@ static int perf_session__list_build_ids(void)
{
struct perf_session *session;

- elf_version(EV_CURRENT);
+ symbol__elf_init();

session = perf_session__new(input_name, O_RDONLY, force, false,
&build_id__mark_dso_hit_ops);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 3e2e5ea0f03f..38447ad42ad1 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1424,6 +1424,11 @@ out_close:
return err;
}

+void symbol__elf_init(void)
+{
+ elf_version(EV_CURRENT);
+}
+
static bool dso__build_id_equal(const struct dso *dso, u8 *build_id)
{
return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0;
@@ -2614,7 +2619,8 @@ int symbol__init(void)

symbol_conf.priv_size = ALIGN(symbol_conf.priv_size, sizeof(u64));

- elf_version(EV_CURRENT);
+ symbol__elf_init();
+
if (symbol_conf.sort_by_name)
symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
sizeof(struct symbol));
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index af0752b1aca1..295a9aa1cc78 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -296,6 +296,7 @@ void machines__destroy_guest_kernel_maps(struct rb_root *machines);

int symbol__init(void);
void symbol__exit(void);
+void symbol__elf_init(void);
size_t symbol__fprintf_symname_offs(const struct symbol *sym,
const struct addr_location *al, FILE *fp);
size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
--
1.7.10.2

2012-06-22 05:41:33

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 8/8] perf symbols: Implement poor man's ELF parser

From: Namhyung Kim <[email protected]>

Implement a minimal elf parser for getting build-id.
It assumes that required elf.h header is provided
by libc header on the system and the parser only
looks for PT_NOTE program header to check build-id.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/util/symbol-minimal.c | 242 ++++++++++++++++++++++++++++++++++++--
1 file changed, 235 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
index fa37f3bb88d1..9f3c681ca008 100644
--- a/tools/perf/util/symbol-minimal.c
+++ b/tools/perf/util/symbol-minimal.c
@@ -1,18 +1,240 @@
#include "symbol.h"

+#include <elf.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <byteswap.h>
+#include <sys/stat.h>

-int filename__read_build_id(const char *filename __used, void *bf __used,
- size_t size __used)
+
+static bool check_need_swap(int file_endian)
{
- return -1;
+ const int data = 1;
+ u8 *check = (u8 *)&data;
+ int host_endian;
+
+ if (check[0] == 1)
+ host_endian = ELFDATA2LSB;
+ else
+ host_endian = ELFDATA2MSB;
+
+ return host_endian != file_endian;
}

-int sysfs__read_build_id(const char *filename __used, void *build_id __used,
- size_t size __used)
+#define NOTE_ALIGN(sz) (((sz) + 3) & ~3)
+
+#define NT_GNU_BUILD_ID 3
+
+static int read_build_id(void *note_data, size_t note_len, void *bf,
+ size_t size, bool need_swap)
{
+ struct {
+ u32 n_namesz;
+ u32 n_descsz;
+ u32 n_type;
+ } *nhdr;
+ void *ptr;
+
+ ptr = note_data;
+ while (ptr < (note_data + note_len)) {
+ const char *name;
+ size_t namesz, descsz;
+
+ nhdr = ptr;
+ if (need_swap) {
+ nhdr->n_namesz = bswap_32(nhdr->n_namesz);
+ nhdr->n_descsz = bswap_32(nhdr->n_descsz);
+ nhdr->n_type = bswap_32(nhdr->n_type);
+ }
+
+ namesz = NOTE_ALIGN(nhdr->n_namesz);
+ descsz = NOTE_ALIGN(nhdr->n_descsz);
+
+ ptr += sizeof(*nhdr);
+ name = ptr;
+ ptr += namesz;
+ if (nhdr->n_type == NT_GNU_BUILD_ID &&
+ nhdr->n_namesz == sizeof("GNU")) {
+ if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+ size_t sz = min(size, descsz);
+ memcpy(bf, ptr, sz);
+ memset(bf + sz, 0, size - sz);
+ return 0;
+ }
+ }
+ ptr += descsz;
+ }
+
return -1;
}

+/*
+ * Just try PT_NOTE header otherwise fails
+ */
+int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+ FILE *fp;
+ int ret = -1;
+ bool need_swap = false;
+ u8 e_ident[EI_NIDENT];
+ size_t buf_size;
+ void *buf;
+ int i;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL)
+ return -1;
+
+ if (fread(e_ident, sizeof(e_ident), 1, fp) != 1)
+ goto out;
+
+ if (memcmp(e_ident, ELFMAG, SELFMAG) ||
+ e_ident[EI_VERSION] != EV_CURRENT)
+ goto out;
+
+ need_swap = check_need_swap(e_ident[EI_DATA]);
+
+ /* for simplicity */
+ fseek(fp, 0, SEEK_SET);
+
+ if (e_ident[EI_CLASS] == ELFCLASS32) {
+ Elf32_Ehdr ehdr;
+ Elf32_Phdr *phdr;
+
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+ goto out;
+
+ if (need_swap) {
+ ehdr.e_phoff = bswap_32(ehdr.e_phoff);
+ ehdr.e_phentsize = bswap_16(ehdr.e_phentsize);
+ ehdr.e_phnum = bswap_16(ehdr.e_phnum);
+ }
+
+ buf_size = ehdr.e_phentsize * ehdr.e_phnum;
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ goto out;
+
+ fseek(fp, ehdr.e_phoff, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) {
+ void *tmp;
+
+ if (need_swap) {
+ phdr->p_type = bswap_32(phdr->p_type);
+ phdr->p_offset = bswap_32(phdr->p_offset);
+ phdr->p_filesz = bswap_32(phdr->p_filesz);
+ }
+
+ if (phdr->p_type != PT_NOTE)
+ continue;
+
+ buf_size = phdr->p_filesz;
+ tmp = realloc(buf, buf_size);
+ if (tmp == NULL)
+ goto out_free;
+
+ buf = tmp;
+ fseek(fp, phdr->p_offset, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ ret = read_build_id(buf, buf_size, bf, size, need_swap);
+ if (ret == 0)
+ ret = size;
+ break;
+ }
+ } else {
+ Elf64_Ehdr ehdr;
+ Elf64_Phdr *phdr;
+
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+ goto out;
+
+ if (need_swap) {
+ ehdr.e_phoff = bswap_64(ehdr.e_phoff);
+ ehdr.e_phentsize = bswap_16(ehdr.e_phentsize);
+ ehdr.e_phnum = bswap_16(ehdr.e_phnum);
+ }
+
+ buf_size = ehdr.e_phentsize * ehdr.e_phnum;
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ goto out;
+
+ fseek(fp, ehdr.e_phoff, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) {
+ void *tmp;
+
+ if (need_swap) {
+ phdr->p_type = bswap_32(phdr->p_type);
+ phdr->p_offset = bswap_64(phdr->p_offset);
+ phdr->p_filesz = bswap_64(phdr->p_filesz);
+ }
+
+ if (phdr->p_type != PT_NOTE)
+ continue;
+
+ buf_size = phdr->p_filesz;
+ tmp = realloc(buf, buf_size);
+ if (tmp == NULL)
+ goto out_free;
+
+ buf = tmp;
+ fseek(fp, phdr->p_offset, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ ret = read_build_id(buf, buf_size, bf, size, need_swap);
+ if (ret == 0)
+ ret = size;
+ break;
+ }
+ }
+out_free:
+ free(buf);
+out:
+ fclose(fp);
+ return ret;
+}
+
+int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
+{
+ int fd;
+ int ret = -1;
+ struct stat stbuf;
+ size_t buf_size;
+ void *buf;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (fstat(fd, &stbuf) < 0)
+ goto out;
+
+ buf_size = stbuf.st_size;
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ goto out;
+
+ if (read(fd, buf, buf_size) != (ssize_t) buf_size)
+ goto out_free;
+
+ ret = read_build_id(buf, buf_size, build_id, size, false);
+out_free:
+ free(buf);
+out:
+ close(fd);
+ return ret;
+}
+
int dso__synthesize_plt_symbols(struct dso *dso __used, char *name __used,
struct map *map __used,
symbol_filter_t filter __used)
@@ -20,11 +242,17 @@ int dso__synthesize_plt_symbols(struct dso *dso __used, char *name __used,
return 0;
}

-int dso__load_sym(struct dso *dso __used, struct map *map __used,
- const char *name __used, int fd __used,
+int dso__load_sym(struct dso *dso, struct map *map __used,
+ const char *name, int fd __used,
symbol_filter_t filter __used, int kmodule __used,
int want_symtab __used)
{
+ unsigned char *build_id[BUILD_ID_SIZE];
+
+ if (filename__read_build_id(name, build_id, BUILD_ID_SIZE) > 0) {
+ dso__set_build_id(dso, build_id);
+ return 1;
+ }
return 0;
}

--
1.7.10.2

2012-06-22 05:41:46

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 5/8] perf symbols: Do not use ELF's symbol binding constants

From: Namhyung Kim <[email protected]>

There's no need to use the ELF's internal value directly.
Define and use our own - it's required to eliminated the
dependency of libelf.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/builtin-top.c | 5 ++---
tools/perf/ui/browsers/map.c | 5 ++---
tools/perf/util/symbol.c | 40 ++++++++++++++++++++++++++--------------
tools/perf/util/symbol.h | 4 ++++
4 files changed, 34 insertions(+), 20 deletions(-)

diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index e3cab5f088f8..e0977175f689 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -42,7 +42,6 @@
#include "util/debug.h"

#include <assert.h>
-#include <elf.h>
#include <fcntl.h>

#include <stdio.h>
@@ -181,8 +180,8 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
"Please report to [email protected]\n",
ip, map->dso->long_name, dso__symtab_origin(map->dso),
map->start, map->end, sym->start, sym->end,
- sym->binding == STB_GLOBAL ? 'g' :
- sym->binding == STB_LOCAL ? 'l' : 'w', sym->name,
+ sym->binding == SYMBIND_GLOBAL ? 'g' :
+ sym->binding == SYMBIND_LOCAL ? 'l' : 'w', sym->name,
err ? "[unknown]" : uts.machine,
err ? "[unknown]" : uts.release, perf_version_string);
if (use_browser <= 0)
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
index 98851d55a53e..f2059664b23f 100644
--- a/tools/perf/ui/browsers/map.c
+++ b/tools/perf/ui/browsers/map.c
@@ -1,5 +1,4 @@
#include "../libslang.h"
-#include <elf.h>
#include <newt.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
@@ -61,8 +60,8 @@ static void map_browser__write(struct ui_browser *self, void *nd, int row)
ui_browser__set_percent_color(self, 0, current_entry);
slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",
mb->addrlen, sym->start, mb->addrlen, sym->end,
- sym->binding == STB_GLOBAL ? 'g' :
- sym->binding == STB_LOCAL ? 'l' : 'w');
+ sym->binding == SYMBIND_GLOBAL ? 'g' :
+ sym->binding == SYMBIND_LOCAL ? 'l' : 'w');
width = self->width - ((mb->addrlen * 2) + 4);
if (width > 0)
slsmg_write_nstring(sym->name, width);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 38447ad42ad1..db807a8535d1 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -114,16 +114,16 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return SYMBOL_B;

/* Prefer a non weak symbol over a weak one */
- a = syma->binding == STB_WEAK;
- b = symb->binding == STB_WEAK;
+ a = syma->binding == SYMBIND_WEAK;
+ b = symb->binding == SYMBIND_WEAK;
if (b && !a)
return SYMBOL_A;
if (a && !b)
return SYMBOL_B;

/* Prefer a global symbol over a non global one */
- a = syma->binding == STB_GLOBAL;
- b = symb->binding == STB_GLOBAL;
+ a = syma->binding == SYMBIND_GLOBAL;
+ b = symb->binding == SYMBIND_GLOBAL;
if (a && !b)
return SYMBOL_A;
if (b && !a)
@@ -259,8 +259,8 @@ static size_t symbol__fprintf(struct symbol *sym, FILE *fp)
{
return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
sym->start, sym->end,
- sym->binding == STB_GLOBAL ? 'g' :
- sym->binding == STB_LOCAL ? 'l' : 'w',
+ sym->binding == SYMBIND_GLOBAL ? 'g' :
+ sym->binding == SYMBIND_LOCAL ? 'l' : 'w',
sym->name);
}

@@ -609,12 +609,12 @@ struct process_kallsyms_args {
struct dso *dso;
};

-static u8 kallsyms2elf_type(char type)
+static u8 kallsyms_binding(char type)
{
if (type == 'W')
- return STB_WEAK;
+ return SYMBIND_WEAK;

- return isupper(type) ? STB_GLOBAL : STB_LOCAL;
+ return isupper(type) ? SYMBIND_GLOBAL : SYMBIND_LOCAL;
}

static int map__process_kallsym_symbol(void *arg, const char *name,
@@ -628,7 +628,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
return 0;

sym = symbol__new(start, end - start + 1,
- kallsyms2elf_type(type), name);
+ kallsyms_binding(type), name);
if (sym == NULL)
return -ENOMEM;
/*
@@ -851,7 +851,7 @@ static int dso__load_perf_map(struct dso *dso, struct map *map,
if (len + 2 >= line_len)
continue;

- sym = symbol__new(start, size, STB_GLOBAL, line + len);
+ sym = symbol__new(start, size, SYMBIND_GLOBAL, line + len);

if (sym == NULL)
goto out_delete_line;
@@ -1064,7 +1064,7 @@ dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map,
"%s@plt", elf_sym__name(&sym, symstrs));

f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- STB_GLOBAL, sympltname);
+ SYMBIND_GLOBAL, sympltname);
if (!f)
goto out_elf_end;

@@ -1086,7 +1086,7 @@ dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map,
"%s@plt", elf_sym__name(&sym, symstrs));

f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- STB_GLOBAL, sympltname);
+ SYMBIND_GLOBAL, sympltname);
if (!f)
goto out_elf_end;

@@ -1157,6 +1157,18 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
return -1;
}

+static int elf_sym__binding(const GElf_Sym *sym)
+{
+ switch (GELF_ST_BIND(sym->st_info)) {
+ case STB_GLOBAL:
+ return SYMBIND_GLOBAL;
+ case STB_WEAK:
+ return SYMBIND_WEAK;
+ default:
+ return SYMBIND_LOCAL;
+ }
+}
+
static int dso__swap_init(struct dso *dso, unsigned char eidata)
{
static unsigned int const endian = 1;
@@ -1390,7 +1402,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,
elf_name = demangled;
new_symbol:
f = symbol__new(sym.st_value, sym.st_size,
- GELF_ST_BIND(sym.st_info), elf_name);
+ elf_sym__binding(&sym), elf_name);
free(demangled);
if (!f)
goto out_elf_end;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 295a9aa1cc78..bd91d2aa8262 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -50,6 +50,10 @@ char *strxfrchar(char *s, char from, char to);

#define BUILD_ID_SIZE 20

+#define SYMBIND_LOCAL 0
+#define SYMBIND_GLOBAL 1
+#define SYMBIND_WEAK 2
+
/** struct symbol - symtab entry
*
* @ignore - resolvable but tools ignore it (e.g. idle routines)
--
1.7.10.2

2012-06-22 05:42:03

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 6/8] perf tools: Split out util/symbol-elf.c

From: Namhyung Kim <[email protected]>

Factor out the dependency of ELF handling into separate
symbol-elf.c file. It is a preparation of building a
minimalistic version perf tools which doesn't depend on
the elfutils.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/Makefile | 1 +
tools/perf/util/symbol-elf.c | 719 +++++++++++++++++++++++++++++++++++++++++
tools/perf/util/symbol.c | 732 +-----------------------------------------
tools/perf/util/symbol.h | 15 +
4 files changed, 743 insertions(+), 724 deletions(-)
create mode 100644 tools/perf/util/symbol-elf.c

diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index d698c118a602..0f50a773da08 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -354,6 +354,7 @@ LIB_OBJS += $(OUTPUT)util/usage.o
LIB_OBJS += $(OUTPUT)util/wrapper.o
LIB_OBJS += $(OUTPUT)util/sigchain.o
LIB_OBJS += $(OUTPUT)util/symbol.o
+LIB_OBJS += $(OUTPUT)util/symbol-elf.o
LIB_OBJS += $(OUTPUT)util/color.o
LIB_OBJS += $(OUTPUT)util/pager.o
LIB_OBJS += $(OUTPUT)util/header.o
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
new file mode 100644
index 000000000000..a6dbf8ac332d
--- /dev/null
+++ b/tools/perf/util/symbol-elf.c
@@ -0,0 +1,719 @@
+#include <libelf.h>
+#include <gelf.h>
+#include <elf.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "symbol.h"
+#include "debug.h"
+
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+/**
+ * elf_symtab__for_each_symbol - iterate thru all the symbols
+ *
+ * @syms: struct elf_symtab instance to iterate
+ * @idx: uint32_t idx
+ * @sym: GElf_Sym iterator
+ */
+#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
+ for (idx = 0, gelf_getsym(syms, idx, &sym);\
+ idx < nr_syms; \
+ idx++, gelf_getsym(syms, idx, &sym))
+
+static inline uint8_t elf_sym__type(const GElf_Sym *sym)
+{
+ return GELF_ST_TYPE(sym->st_info);
+}
+
+static inline int elf_sym__is_function(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_FUNC &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static inline bool elf_sym__is_object(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_OBJECT &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static inline int elf_sym__is_label(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_NOTYPE &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF &&
+ sym->st_shndx != SHN_ABS;
+}
+
+static inline const char *elf_sec__name(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return secstrs->d_buf + shdr->sh_name;
+}
+
+static inline int elf_sec__is_text(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
+}
+
+static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
+}
+
+static inline const char *elf_sym__name(const GElf_Sym *sym,
+ const Elf_Data *symstrs)
+{
+ return symstrs->d_buf + sym->st_name;
+}
+
+static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sym__is_function(sym);
+ case MAP__VARIABLE:
+ return elf_sym__is_object(sym);
+ default:
+ return false;
+ }
+}
+
+static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs,
+ enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sec__is_text(shdr, secstrs);
+ case MAP__VARIABLE:
+ return elf_sec__is_data(shdr, secstrs);
+ default:
+ return false;
+ }
+}
+
+static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
+{
+ Elf_Scn *sec = NULL;
+ GElf_Shdr shdr;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ gelf_getshdr(sec, &shdr);
+
+ if ((addr >= shdr.sh_addr) &&
+ (addr < (shdr.sh_addr + shdr.sh_size)))
+ return cnt;
+
+ ++cnt;
+ }
+
+ return -1;
+}
+
+static int elf_sym__binding(const GElf_Sym *sym)
+{
+ switch (GELF_ST_BIND(sym->st_info)) {
+ case STB_GLOBAL:
+ return SYMBIND_GLOBAL;
+ case STB_WEAK:
+ return SYMBIND_WEAK;
+ default:
+ return SYMBIND_LOCAL;
+ }
+}
+
+static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+ GElf_Shdr *shp, const char *name,
+ size_t *idx)
+{
+ Elf_Scn *sec = NULL;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ char *str;
+
+ gelf_getshdr(sec, shp);
+ str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+ if (!strcmp(name, str)) {
+ if (idx)
+ *idx = cnt;
+ break;
+ }
+ ++cnt;
+ }
+
+ return sec;
+}
+
+#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
+ for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
+ idx < nr_entries; \
+ ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
+
+#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
+ for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
+ idx < nr_entries; \
+ ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
+
+/*
+ * We need to check if we have a .dynsym, so that we can handle the
+ * .plt, synthesizing its symbols, that aren't on the symtabs (be it
+ * .dynsym or .symtab).
+ * And always look at the original dso, not at debuginfo packages, that
+ * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
+ */
+int dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map,
+ symbol_filter_t filter)
+{
+ uint32_t nr_rel_entries, idx;
+ GElf_Sym sym;
+ u64 plt_offset;
+ GElf_Shdr shdr_plt;
+ struct symbol *f;
+ GElf_Shdr shdr_rel_plt, shdr_dynsym;
+ Elf_Data *reldata, *syms, *symstrs;
+ Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
+ size_t dynsym_idx;
+ GElf_Ehdr ehdr;
+ char sympltname[1024];
+ Elf *elf;
+ int nr = 0, symidx, fd, err = 0;
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ goto out_close;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ goto out_elf_end;
+
+ scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym,
+ ".dynsym", &dynsym_idx);
+ if (scn_dynsym == NULL)
+ goto out_elf_end;
+
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
+ ".rela.plt", NULL);
+ if (scn_plt_rel == NULL) {
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
+ ".rel.plt", NULL);
+ if (scn_plt_rel == NULL)
+ goto out_elf_end;
+ }
+
+ err = -1;
+
+ if (shdr_rel_plt.sh_link != dynsym_idx)
+ goto out_elf_end;
+
+ if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
+ goto out_elf_end;
+
+ /*
+ * Fetch the relocation section to find the idxes to the GOT
+ * and the symbols in the .dynsym they refer to.
+ */
+ reldata = elf_getdata(scn_plt_rel, NULL);
+ if (reldata == NULL)
+ goto out_elf_end;
+
+ syms = elf_getdata(scn_dynsym, NULL);
+ if (syms == NULL)
+ goto out_elf_end;
+
+ scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
+ if (scn_symstrs == NULL)
+ goto out_elf_end;
+
+ symstrs = elf_getdata(scn_symstrs, NULL);
+ if (symstrs == NULL)
+ goto out_elf_end;
+
+ nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
+ plt_offset = shdr_plt.sh_offset;
+
+ if (shdr_rel_plt.sh_type == SHT_RELA) {
+ GElf_Rela pos_mem, *pos;
+
+ elf_section__for_each_rela(reldata, pos, pos_mem, idx,
+ nr_rel_entries) {
+ symidx = GELF_R_SYM(pos->r_info);
+ plt_offset += shdr_plt.sh_entsize;
+ gelf_getsym(syms, symidx, &sym);
+ snprintf(sympltname, sizeof(sympltname),
+ "%s@plt", elf_sym__name(&sym, symstrs));
+
+ f = symbol__new(plt_offset, shdr_plt.sh_entsize,
+ SYMBIND_GLOBAL, sympltname);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&dso->symbols[map->type], f);
+ ++nr;
+ }
+ }
+ } else if (shdr_rel_plt.sh_type == SHT_REL) {
+ GElf_Rel pos_mem, *pos;
+ elf_section__for_each_rel(reldata, pos, pos_mem, idx,
+ nr_rel_entries) {
+ symidx = GELF_R_SYM(pos->r_info);
+ plt_offset += shdr_plt.sh_entsize;
+ gelf_getsym(syms, symidx, &sym);
+ snprintf(sympltname, sizeof(sympltname),
+ "%s@plt", elf_sym__name(&sym, symstrs));
+
+ f = symbol__new(plt_offset, shdr_plt.sh_entsize,
+ SYMBIND_GLOBAL, sympltname);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&dso->symbols[map->type], f);
+ ++nr;
+ }
+ }
+ }
+
+ err = 0;
+out_elf_end:
+ elf_end(elf);
+out_close:
+ close(fd);
+
+ if (err == 0)
+ return nr;
+out:
+ pr_debug("%s: problems reading %s PLT info.\n",
+ __func__, dso->long_name);
+ return 0;
+}
+
+/*
+ * Align offset to 4 bytes as needed for note name and descriptor data.
+ */
+#define NOTE_ALIGN(n) (((n) + 3) & -4U)
+
+static int elf_read_build_id(Elf *elf, void *bf, size_t size)
+{
+ int err = -1;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ Elf_Scn *sec;
+ Elf_Kind ek;
+ void *ptr;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ ek = elf_kind(elf);
+ if (ek != ELF_K_ELF)
+ goto out;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_err("%s: cannot get elf header.\n", __func__);
+ goto out;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".note.gnu.build-id", NULL);
+ if (sec == NULL) {
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".notes", NULL);
+ if (sec == NULL)
+ goto out;
+ }
+
+ data = elf_getdata(sec, NULL);
+ if (data == NULL)
+ goto out;
+
+ ptr = data->d_buf;
+ while (ptr < (data->d_buf + data->d_size)) {
+ GElf_Nhdr *nhdr = ptr;
+ size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
+ descsz = NOTE_ALIGN(nhdr->n_descsz);
+ const char *name;
+
+ ptr += sizeof(*nhdr);
+ name = ptr;
+ ptr += namesz;
+ if (nhdr->n_type == NT_GNU_BUILD_ID &&
+ nhdr->n_namesz == sizeof("GNU")) {
+ if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+ size_t sz = min(size, descsz);
+ memcpy(bf, ptr, sz);
+ memset(bf + sz, 0, size - sz);
+ err = descsz;
+ break;
+ }
+ }
+ ptr += descsz;
+ }
+
+out:
+ return err;
+}
+
+int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+ int fd, err = -1;
+ Elf *elf;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
+ goto out_close;
+ }
+
+ err = elf_read_build_id(elf, bf, size);
+
+ elf_end(elf);
+out_close:
+ close(fd);
+out:
+ return err;
+}
+
+int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
+{
+ int fd, err = -1;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ while (1) {
+ char bf[BUFSIZ];
+ GElf_Nhdr nhdr;
+ size_t namesz, descsz;
+
+ if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
+ break;
+
+ namesz = NOTE_ALIGN(nhdr.n_namesz);
+ descsz = NOTE_ALIGN(nhdr.n_descsz);
+ if (nhdr.n_type == NT_GNU_BUILD_ID &&
+ nhdr.n_namesz == sizeof("GNU")) {
+ if (read(fd, bf, namesz) != (ssize_t)namesz)
+ break;
+ if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
+ size_t sz = min(descsz, size);
+ if (read(fd, build_id, sz) == (ssize_t)sz) {
+ memset(build_id + sz, 0, size - sz);
+ err = 0;
+ break;
+ }
+ } else if (read(fd, bf, descsz) != (ssize_t)descsz)
+ break;
+ } else {
+ int n = namesz + descsz;
+ if (read(fd, bf, n) != n)
+ break;
+ }
+ }
+ close(fd);
+out:
+ return err;
+}
+
+static int dso__swap_init(struct dso *dso, unsigned char eidata)
+{
+ static unsigned int const endian = 1;
+
+ dso->needs_swap = DSO_SWAP__NO;
+
+ switch (eidata) {
+ case ELFDATA2LSB:
+ /* We are big endian, DSO is little endian. */
+ if (*(unsigned char const *)&endian != 1)
+ dso->needs_swap = DSO_SWAP__YES;
+ break;
+
+ case ELFDATA2MSB:
+ /* We are little endian, DSO is big endian. */
+ if (*(unsigned char const *)&endian != 0)
+ dso->needs_swap = DSO_SWAP__YES;
+ break;
+
+ default:
+ pr_err("unrecognized DSO data encoding %d\n", eidata);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int dso__load_sym(struct dso *dso, struct map *map, const char *name, int fd,
+ symbol_filter_t filter, int kmodule, int want_symtab)
+{
+ struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL;
+ struct map *curr_map = map;
+ struct dso *curr_dso = dso;
+ Elf_Data *symstrs, *secstrs;
+ uint32_t nr_syms;
+ int err = -1;
+ uint32_t idx;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr, opdshdr;
+ Elf_Data *syms, *opddata = NULL;
+ GElf_Sym sym;
+ Elf_Scn *sec, *sec_strndx, *opdsec;
+ Elf *elf;
+ int nr = 0;
+ size_t opdidx = 0;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
+ goto out_close;
+ }
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_debug("%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ if (dso__swap_init(dso, ehdr.e_ident[EI_DATA]))
+ goto out_elf_end;
+
+ /* Always reject images with a mismatched build-id: */
+ if (dso->has_build_id) {
+ u8 build_id[BUILD_ID_SIZE];
+
+ if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0)
+ goto out_elf_end;
+
+ if (!dso__build_id_equal(dso, build_id))
+ goto out_elf_end;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
+ if (sec == NULL) {
+ if (want_symtab)
+ goto out_elf_end;
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
+ if (sec == NULL)
+ goto out_elf_end;
+ }
+
+ opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
+ if (opdshdr.sh_type != SHT_PROGBITS)
+ opdsec = NULL;
+ if (opdsec)
+ opddata = elf_rawdata(opdsec, NULL);
+
+ syms = elf_getdata(sec, NULL);
+ if (syms == NULL)
+ goto out_elf_end;
+
+ sec = elf_getscn(elf, shdr.sh_link);
+ if (sec == NULL)
+ goto out_elf_end;
+
+ symstrs = elf_getdata(sec, NULL);
+ if (symstrs == NULL)
+ goto out_elf_end;
+
+ sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
+ if (sec_strndx == NULL)
+ goto out_elf_end;
+
+ secstrs = elf_getdata(sec_strndx, NULL);
+ if (secstrs == NULL)
+ goto out_elf_end;
+
+ nr_syms = shdr.sh_size / shdr.sh_entsize;
+
+ memset(&sym, 0, sizeof(sym));
+ if (dso->kernel == DSO_TYPE_USER) {
+ dso->adjust_symbols = (ehdr.e_type == ET_EXEC ||
+ elf_section_by_name(elf, &ehdr, &shdr,
+ ".gnu.prelink_undo",
+ NULL) != NULL);
+ } else {
+ dso->adjust_symbols = 0;
+ }
+ elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
+ struct symbol *f;
+ const char *elf_name = elf_sym__name(&sym, symstrs);
+ char *demangled = NULL;
+ int is_label = elf_sym__is_label(&sym);
+ const char *section_name;
+
+ if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
+ strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
+ kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
+
+ if (!is_label && !elf_sym__is_a(&sym, map->type))
+ continue;
+
+ /* Reject ARM ELF "mapping symbols": these aren't unique and
+ * don't identify functions, so will confuse the profile
+ * output: */
+ if (ehdr.e_machine == EM_ARM) {
+ if (!strcmp(elf_name, "$a") ||
+ !strcmp(elf_name, "$d") ||
+ !strcmp(elf_name, "$t"))
+ continue;
+ }
+
+ if (opdsec && sym.st_shndx == opdidx) {
+ u32 offset = sym.st_value - opdshdr.sh_addr;
+ u64 *opd = opddata->d_buf + offset;
+ sym.st_value = DSO__SWAP(dso, u64, *opd);
+ sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
+ }
+
+ sec = elf_getscn(elf, sym.st_shndx);
+ if (!sec)
+ goto out_elf_end;
+
+ gelf_getshdr(sec, &shdr);
+
+ if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
+ continue;
+
+ section_name = elf_sec__name(&shdr, secstrs);
+
+ /* On ARM, symbols for thumb functions have 1 added to
+ * the symbol address as a flag - remove it */
+ if ((ehdr.e_machine == EM_ARM) &&
+ (map->type == MAP__FUNCTION) &&
+ (sym.st_value & 1))
+ --sym.st_value;
+
+ if (dso->kernel != DSO_TYPE_USER || kmodule) {
+ char dso_name[PATH_MAX];
+
+ if (strcmp(section_name,
+ (curr_dso->short_name +
+ dso->short_name_len)) == 0)
+ goto new_symbol;
+
+ if (strcmp(section_name, ".text") == 0) {
+ curr_map = map;
+ curr_dso = dso;
+ goto new_symbol;
+ }
+
+ snprintf(dso_name, sizeof(dso_name),
+ "%s%s", dso->short_name, section_name);
+
+ curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);
+ if (curr_map == NULL) {
+ u64 start = sym.st_value;
+
+ if (kmodule)
+ start += map->start + shdr.sh_offset;
+
+ curr_dso = dso__new(dso_name);
+ if (curr_dso == NULL)
+ goto out_elf_end;
+ curr_dso->kernel = dso->kernel;
+ curr_dso->long_name = dso->long_name;
+ curr_dso->long_name_len = dso->long_name_len;
+ curr_map = map__new2(start, curr_dso,
+ map->type);
+ if (curr_map == NULL) {
+ dso__delete(curr_dso);
+ goto out_elf_end;
+ }
+ curr_map->map_ip = identity__map_ip;
+ curr_map->unmap_ip = identity__map_ip;
+ curr_dso->symtab_type = dso->symtab_type;
+ map_groups__insert(kmap->kmaps, curr_map);
+ dsos__add(&dso->node, curr_dso);
+ dso__set_loaded(curr_dso, map->type);
+ } else
+ curr_dso = curr_map->dso;
+
+ goto new_symbol;
+ }
+
+ if (curr_dso->adjust_symbols) {
+ pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
+ "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__,
+ (u64)sym.st_value, (u64)shdr.sh_addr,
+ (u64)shdr.sh_offset);
+ sym.st_value -= shdr.sh_addr - shdr.sh_offset;
+ }
+ /*
+ * We need to figure out if the object was created from C++ sources
+ * DWARF DW_compile_unit has this, but we don't always have access
+ * to it...
+ */
+ demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
+ if (demangled != NULL)
+ elf_name = demangled;
+new_symbol:
+ f = symbol__new(sym.st_value, sym.st_size,
+ elf_sym__binding(&sym), elf_name);
+ free(demangled);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(curr_map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&curr_dso->symbols[curr_map->type], f);
+ nr++;
+ }
+ }
+
+ /*
+ * For misannotated, zeroed, ASM function sizes.
+ */
+ if (nr > 0) {
+ symbols__fixup_duplicate(&dso->symbols[map->type]);
+ symbols__fixup_end(&dso->symbols[map->type]);
+ if (kmap) {
+ /*
+ * We need to fixup this here too because we create new
+ * maps here, for things like vsyscall sections.
+ */
+ __map_groups__fixup_end(kmap->kmaps, map->type);
+ }
+ }
+ err = nr;
+out_elf_end:
+ elf_end(elf);
+out_close:
+ return err;
+}
+
+void symbol__elf_init(void)
+{
+ elf_version(EV_CURRENT);
+}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index db807a8535d1..8209ff3481b1 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -15,9 +15,6 @@
#include "symbol.h"
#include "strlist.h"

-#include <libelf.h>
-#include <gelf.h>
-#include <elf.h>
#include <limits.h>
#include <sys/utsname.h>

@@ -25,14 +22,7 @@
#define KSYM_NAME_LEN 256
#endif

-#ifndef NT_GNU_BUILD_ID
-#define NT_GNU_BUILD_ID 3
-#endif

-static bool dso__build_id_equal(const struct dso *dso, u8 *build_id);
-static int elf_read_build_id(Elf *elf, void *bf, size_t size);
-static void dsos__add(struct list_head *head, struct dso *dso);
-static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
static int dso__load_kernel_sym(struct dso *dso, struct map *map,
symbol_filter_t filter);
static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
@@ -144,7 +134,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return SYMBOL_B;
}

-static void symbols__fixup_duplicate(struct rb_root *symbols)
+void symbols__fixup_duplicate(struct rb_root *symbols)
{
struct rb_node *nd;
struct symbol *curr, *next;
@@ -173,7 +163,7 @@ again:
}
}

-static void symbols__fixup_end(struct rb_root *symbols)
+void symbols__fixup_end(struct rb_root *symbols)
{
struct rb_node *nd, *prevnd = rb_first(symbols);
struct symbol *curr, *prev;
@@ -196,7 +186,7 @@ static void symbols__fixup_end(struct rb_root *symbols)
curr->end = roundup(curr->start, 4096);
}

-static void __map_groups__fixup_end(struct map_groups *mg, enum map_type type)
+void __map_groups__fixup_end(struct map_groups *mg, enum map_type type)
{
struct map *prev, *curr;
struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]);
@@ -226,8 +216,7 @@ static void map_groups__fixup_end(struct map_groups *mg)
__map_groups__fixup_end(mg, i);
}

-static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
- const char *name)
+struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name)
{
size_t namelen = strlen(name) + 1;
struct symbol *sym = calloc(1, (symbol_conf.priv_size +
@@ -361,7 +350,7 @@ void dso__set_build_id(struct dso *dso, void *build_id)
dso->has_build_id = 1;
}

-static void symbols__insert(struct rb_root *symbols, struct symbol *sym)
+void symbols__insert(struct rb_root *symbols, struct symbol *sym)
{
struct rb_node **p = &symbols->rb_node;
struct rb_node *parent = NULL;
@@ -875,573 +864,7 @@ out_failure:
return -1;
}

-/**
- * elf_symtab__for_each_symbol - iterate thru all the symbols
- *
- * @syms: struct elf_symtab instance to iterate
- * @idx: uint32_t idx
- * @sym: GElf_Sym iterator
- */
-#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
- for (idx = 0, gelf_getsym(syms, idx, &sym);\
- idx < nr_syms; \
- idx++, gelf_getsym(syms, idx, &sym))
-
-static inline uint8_t elf_sym__type(const GElf_Sym *sym)
-{
- return GELF_ST_TYPE(sym->st_info);
-}
-
-static inline int elf_sym__is_function(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_FUNC &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF;
-}
-
-static inline bool elf_sym__is_object(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_OBJECT &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF;
-}
-
-static inline int elf_sym__is_label(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_NOTYPE &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF &&
- sym->st_shndx != SHN_ABS;
-}
-
-static inline const char *elf_sec__name(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return secstrs->d_buf + shdr->sh_name;
-}
-
-static inline int elf_sec__is_text(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
-}
-
-static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
-}
-
-static inline const char *elf_sym__name(const GElf_Sym *sym,
- const Elf_Data *symstrs)
-{
- return symstrs->d_buf + sym->st_name;
-}
-
-static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
- GElf_Shdr *shp, const char *name,
- size_t *idx)
-{
- Elf_Scn *sec = NULL;
- size_t cnt = 1;
-
- while ((sec = elf_nextscn(elf, sec)) != NULL) {
- char *str;
-
- gelf_getshdr(sec, shp);
- str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
- if (!strcmp(name, str)) {
- if (idx)
- *idx = cnt;
- break;
- }
- ++cnt;
- }
-
- return sec;
-}
-
-#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
- for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
- idx < nr_entries; \
- ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
-
-#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
- for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
- idx < nr_entries; \
- ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
-
-/*
- * We need to check if we have a .dynsym, so that we can handle the
- * .plt, synthesizing its symbols, that aren't on the symtabs (be it
- * .dynsym or .symtab).
- * And always look at the original dso, not at debuginfo packages, that
- * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
- */
-static int
-dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map,
- symbol_filter_t filter)
-{
- uint32_t nr_rel_entries, idx;
- GElf_Sym sym;
- u64 plt_offset;
- GElf_Shdr shdr_plt;
- struct symbol *f;
- GElf_Shdr shdr_rel_plt, shdr_dynsym;
- Elf_Data *reldata, *syms, *symstrs;
- Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
- size_t dynsym_idx;
- GElf_Ehdr ehdr;
- char sympltname[1024];
- Elf *elf;
- int nr = 0, symidx, fd, err = 0;
-
- fd = open(name, O_RDONLY);
- if (fd < 0)
- goto out;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- goto out_close;
-
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out_elf_end;
-
- scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym,
- ".dynsym", &dynsym_idx);
- if (scn_dynsym == NULL)
- goto out_elf_end;
-
- scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
- ".rela.plt", NULL);
- if (scn_plt_rel == NULL) {
- scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
- ".rel.plt", NULL);
- if (scn_plt_rel == NULL)
- goto out_elf_end;
- }
-
- err = -1;
-
- if (shdr_rel_plt.sh_link != dynsym_idx)
- goto out_elf_end;
-
- if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
- goto out_elf_end;
-
- /*
- * Fetch the relocation section to find the idxes to the GOT
- * and the symbols in the .dynsym they refer to.
- */
- reldata = elf_getdata(scn_plt_rel, NULL);
- if (reldata == NULL)
- goto out_elf_end;
-
- syms = elf_getdata(scn_dynsym, NULL);
- if (syms == NULL)
- goto out_elf_end;
-
- scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
- if (scn_symstrs == NULL)
- goto out_elf_end;
-
- symstrs = elf_getdata(scn_symstrs, NULL);
- if (symstrs == NULL)
- goto out_elf_end;
-
- nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
- plt_offset = shdr_plt.sh_offset;
-
- if (shdr_rel_plt.sh_type == SHT_RELA) {
- GElf_Rela pos_mem, *pos;
-
- elf_section__for_each_rela(reldata, pos, pos_mem, idx,
- nr_rel_entries) {
- symidx = GELF_R_SYM(pos->r_info);
- plt_offset += shdr_plt.sh_entsize;
- gelf_getsym(syms, symidx, &sym);
- snprintf(sympltname, sizeof(sympltname),
- "%s@plt", elf_sym__name(&sym, symstrs));
-
- f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- SYMBIND_GLOBAL, sympltname);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(map, f))
- symbol__delete(f);
- else {
- symbols__insert(&dso->symbols[map->type], f);
- ++nr;
- }
- }
- } else if (shdr_rel_plt.sh_type == SHT_REL) {
- GElf_Rel pos_mem, *pos;
- elf_section__for_each_rel(reldata, pos, pos_mem, idx,
- nr_rel_entries) {
- symidx = GELF_R_SYM(pos->r_info);
- plt_offset += shdr_plt.sh_entsize;
- gelf_getsym(syms, symidx, &sym);
- snprintf(sympltname, sizeof(sympltname),
- "%s@plt", elf_sym__name(&sym, symstrs));
-
- f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- SYMBIND_GLOBAL, sympltname);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(map, f))
- symbol__delete(f);
- else {
- symbols__insert(&dso->symbols[map->type], f);
- ++nr;
- }
- }
- }
-
- err = 0;
-out_elf_end:
- elf_end(elf);
-out_close:
- close(fd);
-
- if (err == 0)
- return nr;
-out:
- pr_debug("%s: problems reading %s PLT info.\n",
- __func__, dso->long_name);
- return 0;
-}
-
-static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type)
-{
- switch (type) {
- case MAP__FUNCTION:
- return elf_sym__is_function(sym);
- case MAP__VARIABLE:
- return elf_sym__is_object(sym);
- default:
- return false;
- }
-}
-
-static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs,
- enum map_type type)
-{
- switch (type) {
- case MAP__FUNCTION:
- return elf_sec__is_text(shdr, secstrs);
- case MAP__VARIABLE:
- return elf_sec__is_data(shdr, secstrs);
- default:
- return false;
- }
-}
-
-static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
-{
- Elf_Scn *sec = NULL;
- GElf_Shdr shdr;
- size_t cnt = 1;
-
- while ((sec = elf_nextscn(elf, sec)) != NULL) {
- gelf_getshdr(sec, &shdr);
-
- if ((addr >= shdr.sh_addr) &&
- (addr < (shdr.sh_addr + shdr.sh_size)))
- return cnt;
-
- ++cnt;
- }
-
- return -1;
-}
-
-static int elf_sym__binding(const GElf_Sym *sym)
-{
- switch (GELF_ST_BIND(sym->st_info)) {
- case STB_GLOBAL:
- return SYMBIND_GLOBAL;
- case STB_WEAK:
- return SYMBIND_WEAK;
- default:
- return SYMBIND_LOCAL;
- }
-}
-
-static int dso__swap_init(struct dso *dso, unsigned char eidata)
-{
- static unsigned int const endian = 1;
-
- dso->needs_swap = DSO_SWAP__NO;
-
- switch (eidata) {
- case ELFDATA2LSB:
- /* We are big endian, DSO is little endian. */
- if (*(unsigned char const *)&endian != 1)
- dso->needs_swap = DSO_SWAP__YES;
- break;
-
- case ELFDATA2MSB:
- /* We are little endian, DSO is big endian. */
- if (*(unsigned char const *)&endian != 0)
- dso->needs_swap = DSO_SWAP__YES;
- break;
-
- default:
- pr_err("unrecognized DSO data encoding %d\n", eidata);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int dso__load_sym(struct dso *dso, struct map *map, const char *name,
- int fd, symbol_filter_t filter, int kmodule,
- int want_symtab)
-{
- struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL;
- struct map *curr_map = map;
- struct dso *curr_dso = dso;
- Elf_Data *symstrs, *secstrs;
- uint32_t nr_syms;
- int err = -1;
- uint32_t idx;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr, opdshdr;
- Elf_Data *syms, *opddata = NULL;
- GElf_Sym sym;
- Elf_Scn *sec, *sec_strndx, *opdsec;
- Elf *elf;
- int nr = 0;
- size_t opdidx = 0;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL) {
- pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
- goto out_close;
- }
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_debug("%s: cannot get elf header.\n", __func__);
- goto out_elf_end;
- }
-
- if (dso__swap_init(dso, ehdr.e_ident[EI_DATA]))
- goto out_elf_end;
-
- /* Always reject images with a mismatched build-id: */
- if (dso->has_build_id) {
- u8 build_id[BUILD_ID_SIZE];
-
- if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0)
- goto out_elf_end;
-
- if (!dso__build_id_equal(dso, build_id))
- goto out_elf_end;
- }
-
- sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
- if (sec == NULL) {
- if (want_symtab)
- goto out_elf_end;
-
- sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
- if (sec == NULL)
- goto out_elf_end;
- }
-
- opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
- if (opdshdr.sh_type != SHT_PROGBITS)
- opdsec = NULL;
- if (opdsec)
- opddata = elf_rawdata(opdsec, NULL);
-
- syms = elf_getdata(sec, NULL);
- if (syms == NULL)
- goto out_elf_end;
-
- sec = elf_getscn(elf, shdr.sh_link);
- if (sec == NULL)
- goto out_elf_end;
-
- symstrs = elf_getdata(sec, NULL);
- if (symstrs == NULL)
- goto out_elf_end;
-
- sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
- if (sec_strndx == NULL)
- goto out_elf_end;
-
- secstrs = elf_getdata(sec_strndx, NULL);
- if (secstrs == NULL)
- goto out_elf_end;
-
- nr_syms = shdr.sh_size / shdr.sh_entsize;
-
- memset(&sym, 0, sizeof(sym));
- if (dso->kernel == DSO_TYPE_USER) {
- dso->adjust_symbols = (ehdr.e_type == ET_EXEC ||
- elf_section_by_name(elf, &ehdr, &shdr,
- ".gnu.prelink_undo",
- NULL) != NULL);
- } else {
- dso->adjust_symbols = 0;
- }
- elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
- struct symbol *f;
- const char *elf_name = elf_sym__name(&sym, symstrs);
- char *demangled = NULL;
- int is_label = elf_sym__is_label(&sym);
- const char *section_name;
-
- if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
- strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
- kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
-
- if (!is_label && !elf_sym__is_a(&sym, map->type))
- continue;
-
- /* Reject ARM ELF "mapping symbols": these aren't unique and
- * don't identify functions, so will confuse the profile
- * output: */
- if (ehdr.e_machine == EM_ARM) {
- if (!strcmp(elf_name, "$a") ||
- !strcmp(elf_name, "$d") ||
- !strcmp(elf_name, "$t"))
- continue;
- }
-
- if (opdsec && sym.st_shndx == opdidx) {
- u32 offset = sym.st_value - opdshdr.sh_addr;
- u64 *opd = opddata->d_buf + offset;
- sym.st_value = DSO__SWAP(dso, u64, *opd);
- sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
- }
-
- sec = elf_getscn(elf, sym.st_shndx);
- if (!sec)
- goto out_elf_end;
-
- gelf_getshdr(sec, &shdr);
-
- if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
- continue;
-
- section_name = elf_sec__name(&shdr, secstrs);
-
- /* On ARM, symbols for thumb functions have 1 added to
- * the symbol address as a flag - remove it */
- if ((ehdr.e_machine == EM_ARM) &&
- (map->type == MAP__FUNCTION) &&
- (sym.st_value & 1))
- --sym.st_value;
-
- if (dso->kernel != DSO_TYPE_USER || kmodule) {
- char dso_name[PATH_MAX];
-
- if (strcmp(section_name,
- (curr_dso->short_name +
- dso->short_name_len)) == 0)
- goto new_symbol;
-
- if (strcmp(section_name, ".text") == 0) {
- curr_map = map;
- curr_dso = dso;
- goto new_symbol;
- }
-
- snprintf(dso_name, sizeof(dso_name),
- "%s%s", dso->short_name, section_name);
-
- curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);
- if (curr_map == NULL) {
- u64 start = sym.st_value;
-
- if (kmodule)
- start += map->start + shdr.sh_offset;
-
- curr_dso = dso__new(dso_name);
- if (curr_dso == NULL)
- goto out_elf_end;
- curr_dso->kernel = dso->kernel;
- curr_dso->long_name = dso->long_name;
- curr_dso->long_name_len = dso->long_name_len;
- curr_map = map__new2(start, curr_dso,
- map->type);
- if (curr_map == NULL) {
- dso__delete(curr_dso);
- goto out_elf_end;
- }
- curr_map->map_ip = identity__map_ip;
- curr_map->unmap_ip = identity__map_ip;
- curr_dso->symtab_type = dso->symtab_type;
- map_groups__insert(kmap->kmaps, curr_map);
- dsos__add(&dso->node, curr_dso);
- dso__set_loaded(curr_dso, map->type);
- } else
- curr_dso = curr_map->dso;
-
- goto new_symbol;
- }
-
- if (curr_dso->adjust_symbols) {
- pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
- "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__,
- (u64)sym.st_value, (u64)shdr.sh_addr,
- (u64)shdr.sh_offset);
- sym.st_value -= shdr.sh_addr - shdr.sh_offset;
- }
- /*
- * We need to figure out if the object was created from C++ sources
- * DWARF DW_compile_unit has this, but we don't always have access
- * to it...
- */
- demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
- if (demangled != NULL)
- elf_name = demangled;
-new_symbol:
- f = symbol__new(sym.st_value, sym.st_size,
- elf_sym__binding(&sym), elf_name);
- free(demangled);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(curr_map, f))
- symbol__delete(f);
- else {
- symbols__insert(&curr_dso->symbols[curr_map->type], f);
- nr++;
- }
- }
-
- /*
- * For misannotated, zeroed, ASM function sizes.
- */
- if (nr > 0) {
- symbols__fixup_duplicate(&dso->symbols[map->type]);
- symbols__fixup_end(&dso->symbols[map->type]);
- if (kmap) {
- /*
- * We need to fixup this here too because we create new
- * maps here, for things like vsyscall sections.
- */
- __map_groups__fixup_end(kmap->kmaps, map->type);
- }
- }
- err = nr;
-out_elf_end:
- elf_end(elf);
-out_close:
- return err;
-}
-
-void symbol__elf_init(void)
-{
- elf_version(EV_CURRENT);
-}
-
-static bool dso__build_id_equal(const struct dso *dso, u8 *build_id)
+bool dso__build_id_equal(const struct dso *dso, u8 *build_id)
{
return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0;
}
@@ -1468,145 +891,6 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
return have_build_id;
}

-/*
- * Align offset to 4 bytes as needed for note name and descriptor data.
- */
-#define NOTE_ALIGN(n) (((n) + 3) & -4U)
-
-static int elf_read_build_id(Elf *elf, void *bf, size_t size)
-{
- int err = -1;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- Elf_Data *data;
- Elf_Scn *sec;
- Elf_Kind ek;
- void *ptr;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- ek = elf_kind(elf);
- if (ek != ELF_K_ELF)
- goto out;
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_err("%s: cannot get elf header.\n", __func__);
- goto out;
- }
-
- sec = elf_section_by_name(elf, &ehdr, &shdr,
- ".note.gnu.build-id", NULL);
- if (sec == NULL) {
- sec = elf_section_by_name(elf, &ehdr, &shdr,
- ".notes", NULL);
- if (sec == NULL)
- goto out;
- }
-
- data = elf_getdata(sec, NULL);
- if (data == NULL)
- goto out;
-
- ptr = data->d_buf;
- while (ptr < (data->d_buf + data->d_size)) {
- GElf_Nhdr *nhdr = ptr;
- size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
- descsz = NOTE_ALIGN(nhdr->n_descsz);
- const char *name;
-
- ptr += sizeof(*nhdr);
- name = ptr;
- ptr += namesz;
- if (nhdr->n_type == NT_GNU_BUILD_ID &&
- nhdr->n_namesz == sizeof("GNU")) {
- if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
- size_t sz = min(size, descsz);
- memcpy(bf, ptr, sz);
- memset(bf + sz, 0, size - sz);
- err = descsz;
- break;
- }
- }
- ptr += descsz;
- }
-
-out:
- return err;
-}
-
-int filename__read_build_id(const char *filename, void *bf, size_t size)
-{
- int fd, err = -1;
- Elf *elf;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto out;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL) {
- pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
- goto out_close;
- }
-
- err = elf_read_build_id(elf, bf, size);
-
- elf_end(elf);
-out_close:
- close(fd);
-out:
- return err;
-}
-
-int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
-{
- int fd, err = -1;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto out;
-
- while (1) {
- char bf[BUFSIZ];
- GElf_Nhdr nhdr;
- size_t namesz, descsz;
-
- if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
- break;
-
- namesz = NOTE_ALIGN(nhdr.n_namesz);
- descsz = NOTE_ALIGN(nhdr.n_descsz);
- if (nhdr.n_type == NT_GNU_BUILD_ID &&
- nhdr.n_namesz == sizeof("GNU")) {
- if (read(fd, bf, namesz) != (ssize_t)namesz)
- break;
- if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
- size_t sz = min(descsz, size);
- if (read(fd, build_id, sz) == (ssize_t)sz) {
- memset(build_id + sz, 0, size - sz);
- err = 0;
- break;
- }
- } else if (read(fd, bf, descsz) != (ssize_t)descsz)
- break;
- } else {
- int n = namesz + descsz;
- if (read(fd, bf, n) != n)
- break;
- }
- }
- close(fd);
-out:
- return err;
-}
-
char dso__symtab_origin(const struct dso *dso)
{
static const char origin[] = {
@@ -1919,7 +1203,7 @@ static int machine__set_modules_path(struct machine *machine)
* they are loaded) and for vmlinux, where only after we load all the
* symbols we'll know where it starts and ends.
*/
-static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
+struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
{
struct map *map = calloc(1, (sizeof(*map) +
(dso->kernel ? sizeof(struct kmap) : 0)));
@@ -2236,7 +1520,7 @@ out_try_fixup:
return err;
}

-static void dsos__add(struct list_head *head, struct dso *dso)
+void dsos__add(struct list_head *head, struct dso *dso)
{
list_add_tail(&dso->node, head);
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index bd91d2aa8262..d6ee62a49e8f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -233,6 +233,7 @@ void dso__sort_by_name(struct dso *dso, enum map_type type);

struct dso *__dsos__findnew(struct list_head *head, const char *name);

+void dsos__add(struct list_head *head, struct dso *dso);
int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter);
int dso__load_vmlinux(struct dso *dso, struct map *map,
const char *vmlinux, symbol_filter_t filter);
@@ -285,11 +286,14 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
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(struct list_head *head, bool with_hits);
+bool dso__build_id_equal(const struct dso *dso, u8 *build_id);
int build_id__sprintf(const u8 *build_id, int len, char *bf);
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
char type, u64 start, u64 end));

+struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
+
void machine__destroy_kernel_maps(struct machine *machine);
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel);
int machine__create_kernel_maps(struct machine *machine);
@@ -301,6 +305,8 @@ void machines__destroy_guest_kernel_maps(struct rb_root *machines);
int symbol__init(void);
void symbol__exit(void);
void symbol__elf_init(void);
+struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name);
+void symbols__insert(struct rb_root *symbols, struct symbol *sym);
size_t symbol__fprintf_symname_offs(const struct symbol *sym,
const struct addr_location *al, FILE *fp);
size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
@@ -308,4 +314,13 @@ bool symbol_type__is_a(char symbol_type, enum map_type map_type);

size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);

+void symbols__fixup_duplicate(struct rb_root *symbols);
+void symbols__fixup_end(struct rb_root *symbols);
+void __map_groups__fixup_end(struct map_groups *mg, enum map_type type);
+
+int dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map,
+ symbol_filter_t filter);
+int dso__load_sym(struct dso *dso, struct map *map, const char *name, int fd,
+ symbol_filter_t filter, int kmodule, int want_symtab);
+
#endif /* __PERF_SYMBOL */
--
1.7.10.2

2012-06-22 05:42:00

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 7/8] perf tools: Support minimal build without libelf

From: Namhyung Kim <[email protected]>

Now we have isolated all ELF-specific stuff, it's possible
to build without libelf. The output binary can do most of
jobs but lacks (user level) symbol information - kernel
symbols are still accessable thanks to the kallsyms.

For now, only 'perf probe' command is removed since it
depends on libelf/libdw heavily.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/Makefile | 57 ++++++++++++++++++++++++++---------
tools/perf/builtin-inject.c | 5 ++-
tools/perf/command-list.txt | 2 +-
tools/perf/perf.c | 2 ++
tools/perf/util/generate-cmdlist.sh | 15 +++++++++
tools/perf/util/map.c | 3 +-
tools/perf/util/symbol-minimal.c | 33 ++++++++++++++++++++
7 files changed, 100 insertions(+), 17 deletions(-)
create mode 100644 tools/perf/util/symbol-minimal.c

diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 0f50a773da08..34eaaff89b62 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -37,7 +37,12 @@ include config/utilities.mak
#
# Define NO_NEWT if you do not want TUI support.
#
+# Define NO_GTK2 if you do not want GTK+ GUI support.
+#
# Define NO_DEMANGLE if you do not want C++ symbol demangling.
+#
+# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds)
+#

$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
@@ -445,34 +450,57 @@ PYRF_OBJS += $(OUTPUT)util/xyarray.o
-include config.mak.autogen
-include config.mak

-ifndef NO_DWARF
-FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
-ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
- msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
+ifdef NO_LIBELF
NO_DWARF := 1
-endif # Dwarf support
-endif # NO_DWARF
-
--include arch/$(ARCH)/Makefile
-
-ifneq ($(OUTPUT),)
- BASIC_CFLAGS += -I$(OUTPUT)
-endif
-
+ NO_DEMANGLE := 1
+else
FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y)
FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS)
ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y)
msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
else
- msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel);
+ NO_LIBELF := 1
+ NO_DWARF := 1
+ NO_DEMANGLE := 1
endif
endif
+endif # NO_LIBELF
+
+-include arch/$(ARCH)/Makefile
+
+ifneq ($(OUTPUT),)
+ BASIC_CFLAGS += -I$(OUTPUT)
+endif
+
+ifdef NO_LIBELF
+BASIC_CFLAGS += -DNO_LIBELF_SUPPORT
+
+EXTLIBS := $(filter-out -lelf,$(EXTLIBS))
+
+# Remove ELF/DWARF dependent codes
+LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS))
+LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS))
+LIB_OBJS := $(filter-out $(OUTPUT)util/probe-event.o,$(LIB_OBJS))
+LIB_OBJS := $(filter-out $(OUTPUT)util/probe-finder.o,$(LIB_OBJS))
+
+BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS))
+
+# Use minimal symbol handling
+LIB_OBJS += $(OUTPUT)util/symbol-minimal.o
+
+else # NO_LIBELF

ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y)
BASIC_CFLAGS += -DLIBELF_NO_MMAP
endif

+FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
+ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
+ msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
+ NO_DWARF := 1
+endif # Dwarf support
+
ifndef NO_DWARF
ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
@@ -483,6 +511,7 @@ else
LIB_OBJS += $(OUTPUT)util/dwarf-aux.o
endif # PERF_HAVE_DWARF_REGS
endif # NO_DWARF
+endif # NO_LIBELF

ifdef NO_NEWT
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 3beab489afc5..64d8ba2fb7bc 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -191,10 +191,13 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
* If this fails, too bad, let the other side
* account this as unresolved.
*/
- } else
+ } else {
+#ifndef NO_LIBELF_SUPPORT
pr_warning("no symbols found in %s, maybe "
"install a debug package?\n",
al.map->dso->long_name);
+#endif
+ }
}
}

diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index d695fe40fbff..0303ec692274 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -18,7 +18,7 @@ perf-stat mainporcelain common
perf-timechart mainporcelain common
perf-top mainporcelain common
perf-script mainporcelain common
-perf-probe mainporcelain common
+perf-probe mainporcelain full
perf-kmem mainporcelain common
perf-lock mainporcelain common
perf-kvm mainporcelain common
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 2b2e225a4d4c..a1450ccac291 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -313,7 +313,9 @@ static void handle_internal_command(int argc, const char **argv)
{ "version", cmd_version, 0 },
{ "script", cmd_script, 0 },
{ "sched", cmd_sched, 0 },
+#ifndef NO_LIBELF_SUPPORT
{ "probe", cmd_probe, 0 },
+#endif
{ "kmem", cmd_kmem, 0 },
{ "lock", cmd_lock, 0 },
{ "kvm", cmd_kvm, 0 },
diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh
index f06f6fd148f8..389590c1ad21 100755
--- a/tools/perf/util/generate-cmdlist.sh
+++ b/tools/perf/util/generate-cmdlist.sh
@@ -21,4 +21,19 @@ do
p
}' "Documentation/perf-$cmd.txt"
done
+
+echo "#ifndef NO_LIBELF_SUPPORT"
+sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt |
+sort |
+while read cmd
+do
+ sed -n '
+ /^NAME/,/perf-'"$cmd"'/H
+ ${
+ x
+ s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
+ p
+ }' "Documentation/perf-$cmd.txt"
+done
+echo "#endif /* NO_LIBELF_SUPPORT */"
echo "};"
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 35ae56864e4f..f29ac8b80f4e 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -135,6 +135,7 @@ int map__load(struct map *self, symbol_filter_t filter)
pr_warning(", continuing without symbols\n");
return -1;
} else if (nr == 0) {
+#ifndef NO_LIBELF_SUPPORT
const size_t len = strlen(name);
const size_t real_len = len - sizeof(DSO__DELETED);

@@ -147,7 +148,7 @@ int map__load(struct map *self, symbol_filter_t filter)
pr_warning("no symbols found in %s, maybe install "
"a debug package?\n", name);
}
-
+#endif
return -1;
}
/*
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
new file mode 100644
index 000000000000..fa37f3bb88d1
--- /dev/null
+++ b/tools/perf/util/symbol-minimal.c
@@ -0,0 +1,33 @@
+#include "symbol.h"
+
+
+int filename__read_build_id(const char *filename __used, void *bf __used,
+ size_t size __used)
+{
+ return -1;
+}
+
+int sysfs__read_build_id(const char *filename __used, void *build_id __used,
+ size_t size __used)
+{
+ return -1;
+}
+
+int dso__synthesize_plt_symbols(struct dso *dso __used, char *name __used,
+ struct map *map __used,
+ symbol_filter_t filter __used)
+{
+ return 0;
+}
+
+int dso__load_sym(struct dso *dso __used, struct map *map __used,
+ const char *name __used, int fd __used,
+ symbol_filter_t filter __used, int kmodule __used,
+ int want_symtab __used)
+{
+ return 0;
+}
+
+void symbol__elf_init(void)
+{
+}
--
1.7.10.2

2012-06-22 05:41:25

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 1/8] perf evsel: Fix a build failure on cross compilation

From: Namhyung Kim <[email protected]>

The commit c410431cefefd ("perf tools: Reconstruct event
with modifiers from perf_event_attr") added the line, but
it's broken since it needs to go up 3 directories to get
to the kernel root directory, not 2.

However host gcc contains /usr/local/include in its
search path, so that it can find the perf_event.h in
/usr/include/linux. This is why we didn't notice the
problem yet. But when I tried to cross compile, it
appears like:

CC util/evsel.o
util/evsel.c:18:44: error: ../../include/linux/perf_event.h: No such file or directory
make: *** [util/evsel.o] Error 1

Looking at the source, it isn't needed at all as evsel.h
already included the perf_event.h. So simply removing it
would solve the problem.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/util/evsel.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 876f639d69ed..3d1f6968f175 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -15,7 +15,6 @@
#include "cpumap.h"
#include "thread_map.h"
#include "target.h"
-#include "../../include/linux/perf_event.h"

#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
--
1.7.10.2

2012-06-22 05:42:45

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 2/8] tools lib traceevent: Make dependency files regeneratable

From: Namhyung Kim <[email protected]>

Ingo reported that libtraceevent doesn't clean out
dependency (.d) files and it can cause a build error
when the libgcc package upgraded:

comet:~/tip/tools/perf> make -j
SUBDIR ../lib/traceevent/
make[1]: *** No rule to make target `/usr/lib/gcc/x86_64-redhat-linux/4.7.0/include/stddef.h',
needed by `event-parse.o'. Stop.
make: *** [../lib/traceevent//libtraceevent.a] Error 2

So this patch makes the .d files depends on the source
and header files also, so that it can be re-generated
as needed.

NOTE: This code is copied from the GNU make manual page
(4.14 Generating Prerequisites Automatically).

Reported-by: Ingo Molnar <[email protected]>
Signed-off-by: Namhyung Kim <[email protected]>
---
tools/lib/traceevent/Makefile | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile
index 3d69aa9ff51e..68a1d088d77e 100644
--- a/tools/lib/traceevent/Makefile
+++ b/tools/lib/traceevent/Makefile
@@ -250,8 +250,12 @@ endef
all_objs := $(sort $(ALL_OBJS))
all_deps := $(all_objs:%.o=.%.d)

+# let .d file also depends on the source and header files
define check_deps
- $(CC) -M $(CFLAGS) $< > $@;
+ @set -e; $(RM) $@; \
+ $(CC) -M $(CFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ $(RM) $@.$$$$
endef

$(gui_deps): ks_version.h
--
1.7.10.2

2012-06-22 05:42:58

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 3/8] tools lib traceevent: Detect build environment changes

From: Namhyung Kim <[email protected]>

Cross compiling perf requires setting ARCH and
CROSS_COMPILE variables, but libtraceevent couldn't
detect the changes so it ends up believing no
recompiling is required. Thus the linker failed like:

LINK perf
../lib/traceevent//libtraceevent.a: member ../lib/traceevent//libtraceevent.a(event-parse.o) in archive is not an object
collect2: ld returned 1 exit status
make: *** [perf] Error 1

This patch fixes this by adding TRACEEVENT-CFLAGS
file like PERF-CFLAGS to track those changes.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/lib/traceevent/Makefile | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile
index 68a1d088d77e..56daa768b9e3 100644
--- a/tools/lib/traceevent/Makefile
+++ b/tools/lib/traceevent/Makefile
@@ -207,7 +207,7 @@ libtraceevent.so: $(PEVENT_LIB_OBJS)
libtraceevent.a: $(PEVENT_LIB_OBJS)
$(Q)$(do_build_static_lib)

-$(PEVENT_LIB_OBJS): %.o: $(src)/%.c
+$(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS
$(Q)$(do_fpic_compile)

define make_version.h
@@ -272,6 +272,16 @@ ifneq ($(dep_includes),)
include $(dep_includes)
endif

+### Detect environment changes
+TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
+
+TRACEEVENT-CFLAGS: force
+ @FLAGS='$(TRACK_CFLAGS)'; \
+ if test x"$$FLAGS" != x"`cat TRACEEVENT-CFLAGS 2>/dev/null`" ; then \
+ echo 1>&2 " * new build flags or cross compiler"; \
+ echo "$$FLAGS" >TRACEEVENT-CFLAGS; \
+ fi
+
tags: force
$(RM) tags
find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px
--
1.7.10.2

2012-06-22 09:48:10

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

On Fri, 2012-06-22 at 14:37 +0900, Namhyung Kim wrote:
> And then I realized that the perf record needs to know about the
> build-id's anyway. :( So I implemented a poor man's version of elf
> parser only for parsing the build-id info.

Why? the very first versions didn't know about any of that nonsense :-)
It works just fine as long as you don't go change binaries around.

That said, you did the work already, so no objection, just saying
builtids aren't that important.

2012-06-22 12:43:36

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 5/8] perf symbols: Do not use ELF's symbol binding constants

Em Fri, Jun 22, 2012 at 02:37:39PM +0900, Namhyung Kim escreveu:
> From: Namhyung Kim <[email protected]>
>
> There's no need to use the ELF's internal value directly.
> Define and use our own - it's required to eliminated the
> dependency of libelf.

Why don't you set STB_GLOBAL, etc to the expected values when libelf is
not present? That way no changes need to be made to symbol.c

Ditto for GELF_ST_BIND.

I.e. keep the subset of libelf.h that we use, providing those
definitions on the poor man's libelf.h we should use when the "real
thing" is not available.

- Arnaldo

> Signed-off-by: Namhyung Kim <[email protected]>
> ---
> tools/perf/builtin-top.c | 5 ++---
> tools/perf/ui/browsers/map.c | 5 ++---
> tools/perf/util/symbol.c | 40 ++++++++++++++++++++++++++--------------
> tools/perf/util/symbol.h | 4 ++++
> 4 files changed, 34 insertions(+), 20 deletions(-)
>
> diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
> index e3cab5f088f8..e0977175f689 100644
> --- a/tools/perf/builtin-top.c
> +++ b/tools/perf/builtin-top.c
> @@ -42,7 +42,6 @@
> #include "util/debug.h"
>
> #include <assert.h>
> -#include <elf.h>
> #include <fcntl.h>
>
> #include <stdio.h>
> @@ -181,8 +180,8 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
> "Please report to [email protected]\n",
> ip, map->dso->long_name, dso__symtab_origin(map->dso),
> map->start, map->end, sym->start, sym->end,
> - sym->binding == STB_GLOBAL ? 'g' :
> - sym->binding == STB_LOCAL ? 'l' : 'w', sym->name,
> + sym->binding == SYMBIND_GLOBAL ? 'g' :
> + sym->binding == SYMBIND_LOCAL ? 'l' : 'w', sym->name,
> err ? "[unknown]" : uts.machine,
> err ? "[unknown]" : uts.release, perf_version_string);
> if (use_browser <= 0)
> diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c
> index 98851d55a53e..f2059664b23f 100644
> --- a/tools/perf/ui/browsers/map.c
> +++ b/tools/perf/ui/browsers/map.c
> @@ -1,5 +1,4 @@
> #include "../libslang.h"
> -#include <elf.h>
> #include <newt.h>
> #include <inttypes.h>
> #include <sys/ttydefaults.h>
> @@ -61,8 +60,8 @@ static void map_browser__write(struct ui_browser *self, void *nd, int row)
> ui_browser__set_percent_color(self, 0, current_entry);
> slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",
> mb->addrlen, sym->start, mb->addrlen, sym->end,
> - sym->binding == STB_GLOBAL ? 'g' :
> - sym->binding == STB_LOCAL ? 'l' : 'w');
> + sym->binding == SYMBIND_GLOBAL ? 'g' :
> + sym->binding == SYMBIND_LOCAL ? 'l' : 'w');
> width = self->width - ((mb->addrlen * 2) + 4);
> if (width > 0)
> slsmg_write_nstring(sym->name, width);
> diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
> index 38447ad42ad1..db807a8535d1 100644
> --- a/tools/perf/util/symbol.c
> +++ b/tools/perf/util/symbol.c
> @@ -114,16 +114,16 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
> return SYMBOL_B;
>
> /* Prefer a non weak symbol over a weak one */
> - a = syma->binding == STB_WEAK;
> - b = symb->binding == STB_WEAK;
> + a = syma->binding == SYMBIND_WEAK;
> + b = symb->binding == SYMBIND_WEAK;
> if (b && !a)
> return SYMBOL_A;
> if (a && !b)
> return SYMBOL_B;
>
> /* Prefer a global symbol over a non global one */
> - a = syma->binding == STB_GLOBAL;
> - b = symb->binding == STB_GLOBAL;
> + a = syma->binding == SYMBIND_GLOBAL;
> + b = symb->binding == SYMBIND_GLOBAL;
> if (a && !b)
> return SYMBOL_A;
> if (b && !a)
> @@ -259,8 +259,8 @@ static size_t symbol__fprintf(struct symbol *sym, FILE *fp)
> {
> return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
> sym->start, sym->end,
> - sym->binding == STB_GLOBAL ? 'g' :
> - sym->binding == STB_LOCAL ? 'l' : 'w',
> + sym->binding == SYMBIND_GLOBAL ? 'g' :
> + sym->binding == SYMBIND_LOCAL ? 'l' : 'w',
> sym->name);
> }
>
> @@ -609,12 +609,12 @@ struct process_kallsyms_args {
> struct dso *dso;
> };
>
> -static u8 kallsyms2elf_type(char type)
> +static u8 kallsyms_binding(char type)
> {
> if (type == 'W')
> - return STB_WEAK;
> + return SYMBIND_WEAK;
>
> - return isupper(type) ? STB_GLOBAL : STB_LOCAL;
> + return isupper(type) ? SYMBIND_GLOBAL : SYMBIND_LOCAL;
> }
>
> static int map__process_kallsym_symbol(void *arg, const char *name,
> @@ -628,7 +628,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
> return 0;
>
> sym = symbol__new(start, end - start + 1,
> - kallsyms2elf_type(type), name);
> + kallsyms_binding(type), name);
> if (sym == NULL)
> return -ENOMEM;
> /*
> @@ -851,7 +851,7 @@ static int dso__load_perf_map(struct dso *dso, struct map *map,
> if (len + 2 >= line_len)
> continue;
>
> - sym = symbol__new(start, size, STB_GLOBAL, line + len);
> + sym = symbol__new(start, size, SYMBIND_GLOBAL, line + len);
>
> if (sym == NULL)
> goto out_delete_line;
> @@ -1064,7 +1064,7 @@ dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map,
> "%s@plt", elf_sym__name(&sym, symstrs));
>
> f = symbol__new(plt_offset, shdr_plt.sh_entsize,
> - STB_GLOBAL, sympltname);
> + SYMBIND_GLOBAL, sympltname);
> if (!f)
> goto out_elf_end;
>
> @@ -1086,7 +1086,7 @@ dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map,
> "%s@plt", elf_sym__name(&sym, symstrs));
>
> f = symbol__new(plt_offset, shdr_plt.sh_entsize,
> - STB_GLOBAL, sympltname);
> + SYMBIND_GLOBAL, sympltname);
> if (!f)
> goto out_elf_end;
>
> @@ -1157,6 +1157,18 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
> return -1;
> }
>
> +static int elf_sym__binding(const GElf_Sym *sym)
> +{
> + switch (GELF_ST_BIND(sym->st_info)) {
> + case STB_GLOBAL:
> + return SYMBIND_GLOBAL;
> + case STB_WEAK:
> + return SYMBIND_WEAK;
> + default:
> + return SYMBIND_LOCAL;
> + }
> +}
> +
> static int dso__swap_init(struct dso *dso, unsigned char eidata)
> {
> static unsigned int const endian = 1;
> @@ -1390,7 +1402,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name,
> elf_name = demangled;
> new_symbol:
> f = symbol__new(sym.st_value, sym.st_size,
> - GELF_ST_BIND(sym.st_info), elf_name);
> + elf_sym__binding(&sym), elf_name);
> free(demangled);
> if (!f)
> goto out_elf_end;
> diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
> index 295a9aa1cc78..bd91d2aa8262 100644
> --- a/tools/perf/util/symbol.h
> +++ b/tools/perf/util/symbol.h
> @@ -50,6 +50,10 @@ char *strxfrchar(char *s, char from, char to);
>
> #define BUILD_ID_SIZE 20
>
> +#define SYMBIND_LOCAL 0
> +#define SYMBIND_GLOBAL 1
> +#define SYMBIND_WEAK 2
> +
> /** struct symbol - symtab entry
> *
> * @ignore - resolvable but tools ignore it (e.g. idle routines)
> --
> 1.7.10.2

2012-06-22 15:05:20

by Namhyung Kim

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

Hi Peter,

2012-06-22 (금), 11:47 +0200, Peter Zijlstra:
> On Fri, 2012-06-22 at 14:37 +0900, Namhyung Kim wrote:
> > And then I realized that the perf record needs to know about the
> > build-id's anyway. :( So I implemented a poor man's version of elf
> > parser only for parsing the build-id info.
>
> Why? the very first versions didn't know about any of that nonsense :-)
> It works just fine as long as you don't go change binaries around.
>
> That said, you did the work already, so no objection, just saying
> builtids aren't that important.

I'm not sure I understood you correctly. But 'perf record' needs to know
about the build-id's to save them to perf.data for 'perf report' later.
And 'perf archive' also needs to know about them to select necessary
binaries for the session.

--
Regards,
Namhyung Kim

2012-06-22 15:18:39

by David Ahern

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

On 6/22/12 9:05 AM, Namhyung Kim wrote:
> Hi Peter,
>
> 2012-06-22 (금), 11:47 +0200, Peter Zijlstra:
>> On Fri, 2012-06-22 at 14:37 +0900, Namhyung Kim wrote:
>>> And then I realized that the perf record needs to know about the
>>> build-id's anyway. :( So I implemented a poor man's version of elf
>>> parser only for parsing the build-id info.
>>
>> Why? the very first versions didn't know about any of that nonsense :-)
>> It works just fine as long as you don't go change binaries around.
>>
>> That said, you did the work already, so no objection, just saying
>> builtids aren't that important.
>
> I'm not sure I understood you correctly. But 'perf record' needs to know
> about the build-id's to save them to perf.data for 'perf report' later.
> And 'perf archive' also needs to know about them to select necessary
> binaries for the session.
>

And build-id's are not required for report (-B option for record).

Also, the intent is for a small footprint binary for embedded systems.
On such a system I would expect binaries and libraries to be stripped,
so no point in running perf-archive.


David

2012-06-22 15:19:11

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH 5/8] perf symbols: Do not use ELF's symbol binding constants

2012-06-22 (금), 09:43 -0300, Arnaldo Carvalho de Melo:
> Em Fri, Jun 22, 2012 at 02:37:39PM +0900, Namhyung Kim escreveu:
> > From: Namhyung Kim <[email protected]>
> >
> > There's no need to use the ELF's internal value directly.
> > Define and use our own - it's required to eliminated the
> > dependency of libelf.
>
> Why don't you set STB_GLOBAL, etc to the expected values when libelf is
> not present? That way no changes need to be made to symbol.c
>
> Ditto for GELF_ST_BIND.
>
> I.e. keep the subset of libelf.h that we use, providing those
> definitions on the poor man's libelf.h we should use when the "real
> thing" is not available.
>

I just tried to be independent to (lib)elf as much as possible. And I
thought that using same macro name might cause a bit of confusion - at
least for me - so I wanted to use more descriptive and generic name.

But it's not a big deal. If you insist on using the same name is the
better way, I can change it.


--
Regards,
Namhyung Kim

2012-06-22 15:29:57

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 5/8] perf symbols: Do not use ELF's symbol binding constants

Em Sat, Jun 23, 2012 at 12:19:02AM +0900, Namhyung Kim escreveu:
> 2012-06-22 (금), 09:43 -0300, Arnaldo Carvalho de Melo:
> > Why don't you set STB_GLOBAL, etc to the expected values when libelf is
> > not present? That way no changes need to be made to symbol.c

> > Ditto for GELF_ST_BIND.

> > I.e. keep the subset of libelf.h that we use, providing those
> > definitions on the poor man's libelf.h we should use when the "real
> > thing" is not available.

> I just tried to be independent to (lib)elf as much as possible. And I
> thought that using same macro name might cause a bit of confusion - at
> least for me - so I wanted to use more descriptive and generic name.

> But it's not a big deal. If you insist on using the same name is the
> better way, I can change it.

I think its better to use the well know names, libelf is not just one
implementation, there are several, and we are interested in people
experienced with those APIs to feel at ease when looking at our code :-)

Also as a general practice I try hard to reduce patch size, so
restricting the changes to allow building with an alternative, stripped
down libelf to the stripped down libelf headers seems like a good path
to follow.

- Arnaldo

2012-06-22 15:30:12

by Namhyung Kim

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

2012-06-22 (금), 09:18 -0600, David Ahern:
> On 6/22/12 9:05 AM, Namhyung Kim wrote:
> > 2012-06-22 (금), 11:47 +0200, Peter Zijlstra:
> >> On Fri, 2012-06-22 at 14:37 +0900, Namhyung Kim wrote:
> >>> And then I realized that the perf record needs to know about the
> >>> build-id's anyway. :( So I implemented a poor man's version of elf
> >>> parser only for parsing the build-id info.
> >>
> >> Why? the very first versions didn't know about any of that nonsense :-)
> >> It works just fine as long as you don't go change binaries around.
> >>
> >> That said, you did the work already, so no objection, just saying
> >> builtids aren't that important.
> >
> > I'm not sure I understood you correctly. But 'perf record' needs to know
> > about the build-id's to save them to perf.data for 'perf report' later.
> > And 'perf archive' also needs to know about them to select necessary
> > binaries for the session.
> >
>
> And build-id's are not required for report (-B option for record).
>
> Also, the intent is for a small footprint binary for embedded systems.
> On such a system I would expect binaries and libraries to be stripped,
> so no point in running perf-archive.
>

But is there a chance that binaries on host still contains symbol (and
debug) information even for those cases?

--
Regards,
Namhyung Kim

2012-06-22 15:32:42

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

Em Fri, Jun 22, 2012 at 09:18:34AM -0600, David Ahern escreveu:
> On 6/22/12 9:05 AM, Namhyung Kim wrote:
> >2012-06-22 (금), 11:47 +0200, Peter Zijlstra:
> >>On Fri, 2012-06-22 at 14:37 +0900, Namhyung Kim wrote:
> >>>And then I realized that the perf record needs to know about the
> >>>build-id's anyway. :( So I implemented a poor man's version of elf
> >>>parser only for parsing the build-id info.

> >>Why? the very first versions didn't know about any of that nonsense :-)
> >>It works just fine as long as you don't go change binaries around.
> >>
> >>That said, you did the work already, so no objection, just saying
> >>builtids aren't that important.

> >I'm not sure I understood you correctly. But 'perf record' needs to know
> >about the build-id's to save them to perf.data for 'perf report' later.
> >And 'perf archive' also needs to know about them to select necessary
> >binaries for the session.

> And build-id's are not required for report (-B option for record).

> Also, the intent is for a small footprint binary for embedded
> systems. On such a system I would expect binaries and libraries to
> be stripped, so no point in running perf-archive.

Right, build ids are not a strict requirement, its just a safeguard,
when available, to prevent WTF moments at post processing time (report,
annotate, etc) if different binaries are used to resolve symbols.

- Arnaldo

2012-06-22 15:35:51

by David Ahern

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

On 6/22/12 9:30 AM, Namhyung Kim wrote:
>> And build-id's are not required for report (-B option for record).
>>
>> Also, the intent is for a small footprint binary for embedded systems.
>> On such a system I would expect binaries and libraries to be stripped,
>> so no point in running perf-archive.
>>
>
> But is there a chance that binaries on host still contains symbol (and
> debug) information even for those cases?
>

AFAIK embedded systems do not have the luxury of symbols; the goal is as
small as possible. All debugging information is left on the build
servers for off line analysis. That's certainly true for the product I
work on.

David

2012-06-22 16:14:55

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

Em Fri, Jun 22, 2012 at 09:35:37AM -0600, David Ahern escreveu:
> On 6/22/12 9:30 AM, Namhyung Kim wrote:
> >>And build-id's are not required for report (-B option for record).

> >>Also, the intent is for a small footprint binary for embedded systems.
> >>On such a system I would expect binaries and libraries to be stripped,
> >>so no point in running perf-archive.

> >But is there a chance that binaries on host still contains symbol (and
> >debug) information even for those cases?
>
> AFAIK embedded systems do not have the luxury of symbols; the goal
> is as small as possible. All debugging information is left on the
> build servers for off line analysis. That's certainly true for the
> product I work on.

I think at least the .dymsym symbol table is present in DSOs, aka
libraries, otherwise how will they dynamicly link?

- Arnaldo

2012-06-22 16:24:17

by David Ahern

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

On 6/22/12 10:14 AM, Arnaldo Carvalho de Melo wrote:
> I think at least the .dymsym symbol table is present in DSOs, aka
> libraries, otherwise how will they dynamicly link?
>
> - Arnaldo
>

yes. I meant debug information wise - everything is stripped during
packaging phase, so onbox analysis is severely limited in address to
symbol resolutions.

David

2012-06-25 00:54:59

by Namhyung Kim

[permalink] [raw]
Subject: Re: [RFC/PATCHSET 0/8] perf tools: Minimal build without libelf dependency (v2)

Hi, David

On Fri, 22 Jun 2012 10:24:13 -0600, David Ahern wrote:
> On 6/22/12 10:14 AM, Arnaldo Carvalho de Melo wrote:
>> I think at least the .dymsym symbol table is present in DSOs, aka
>> libraries, otherwise how will they dynamicly link?
>>
> yes. I meant debug information wise - everything is stripped during
> packaging phase, so onbox analysis is severely limited in address to
> symbol resolutions.
>

Right, I was saying about a case of the onbox recording and off-line
analysis - I hope I didn't use vague words here. And I thought that
supporting build-id is helpful (not crucial, though) for the case too.

Thanks,
Namhyung

2012-06-28 16:15:54

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 2/8] tools lib traceevent: Make dependency files regeneratable

Em Fri, Jun 22, 2012 at 02:37:36PM +0900, Namhyung Kim escreveu:
> From: Namhyung Kim <[email protected]>
>
> Ingo reported that libtraceevent doesn't clean out
> dependency (.d) files and it can cause a build error
> when the libgcc package upgraded:

Thanks, applied to perf/core.

- Arnaldo