All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] perf tools: Add symfs option for off-box analysis using specified tree
@ 2010-12-08  2:42 David Ahern
  2010-12-08 20:56 ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 6+ messages in thread
From: David Ahern @ 2010-12-08  2:42 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel; +Cc: David Ahern

The symfs argument allows analysis of perf.data file using a locally
accessible filesystem tree with debug symbols - e.g., tree created
during image builds, sshfs mount, loop mounted KVM disk images,
USB keys, initrds, etc. Anything with an OS tree can be analyzed from
anywhere without the need to populate a local data store with
build-ids.

Signed-off-by: David Ahern <daahern@cisco.com>
---
 tools/perf/builtin-annotate.c  |   16 +++++--
 tools/perf/builtin-diff.c      |    2 +
 tools/perf/builtin-report.c    |    2 +
 tools/perf/builtin-timechart.c |    2 +
 tools/perf/util/hist.c         |   14 +++++-
 tools/perf/util/symbol.c       |   82 +++++++++++++++++++++++++---------------
 tools/perf/util/symbol.h       |    1 +
 7 files changed, 80 insertions(+), 39 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 569a276..0c47bee 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -280,7 +280,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
 	struct map *map = he->ms.map;
 	struct dso *dso = map->dso;
 	struct symbol *sym = he->ms.sym;
-	const char *filename = dso->long_name, *d_filename;
+	const char *filename = dso->long_name;
+	char d_filename[PATH_MAX];
 	u64 len;
 	LIST_HEAD(head);
 	struct objdump_line *pos, *n;
@@ -288,10 +289,13 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
 	if (hist_entry__annotate(he, &head, 0) < 0)
 		return -1;
 
-	if (full_paths)
-		d_filename = filename;
-	else
-		d_filename = basename(filename);
+	if (full_paths) {
+		snprintf(d_filename, sizeof(d_filename), "%s%s",
+			 symbol_conf.symfs, filename);
+	} else {
+		snprintf(d_filename, sizeof(d_filename), "%s/%s",
+			 symbol_conf.symfs, basename(filename));
+	}
 
 	len = sym->end - sym->start;
 
@@ -437,6 +441,8 @@ static const struct option options[] = {
 		    "print matching source lines (may be slow)"),
 	OPT_BOOLEAN('P', "full-paths", &full_paths,
 		    "Don't shorten the displayed pathnames"),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 5e1a043..71126a6 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -192,6 +192,8 @@ static const struct option options[] = {
 	OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
 		   "separator for columns, no spaces will be added between "
 		   "columns '.' is reserved."),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 904519f..57b0f49 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -479,6 +479,8 @@ static const struct option options[] = {
 		   "columns '.' is reserved."),
 	OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
 		    "Only display entries resolved to a symbol"),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index d2fc461..965aa4e 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -1021,6 +1021,8 @@ static const struct option options[] = {
 	OPT_CALLBACK('p', "process", NULL, "process",
 		      "process selector. Pass a pid or process name.",
 		       parse_process),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 2022e87..b24ae53 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1092,6 +1092,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
 	FILE *file;
 	int err = 0;
 	u64 len;
+	char symfs_filename[PATH_MAX];
+
+	if (filename) {
+		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
+		 symbol_conf.symfs, filename);
+	}
 
 	if (filename == NULL) {
 		if (dso->has_build_id) {
@@ -1100,9 +1106,9 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
 			return -ENOMEM;
 		}
 		goto fallback;
-	} else if (readlink(filename, command, sizeof(command)) < 0 ||
+	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
 		   strstr(command, "[kernel.kallsyms]") ||
-		   access(filename, R_OK)) {
+		   access(symfs_filename, R_OK)) {
 		free(filename);
 fallback:
 		/*
@@ -1111,6 +1117,8 @@ fallback:
 		 * DSO is the same as when 'perf record' ran.
 		 */
 		filename = dso->long_name;
+		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
+		 symbol_conf.symfs, filename);
 		free_filename = false;
 	}
 
@@ -1137,7 +1145,7 @@ fallback:
 		 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
 		 map__rip_2objdump(map, sym->start),
 		 map__rip_2objdump(map, sym->end),
-		 filename, filename);
+		 symfs_filename, filename);
 
 	pr_debug("Executing: %s\n", command);
 
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index a348906..7a0d284 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -41,6 +41,7 @@ struct symbol_conf symbol_conf = {
 	.exclude_other	  = true,
 	.use_modules	  = true,
 	.try_vmlinux_path = true,
+	.symfs           = "",
 };
 
 int dso__name_len(const struct dso *self)
@@ -833,8 +834,11 @@ static int dso__synthesize_plt_symbols(struct  dso *self, struct map *map,
 	char sympltname[1024];
 	Elf *elf;
 	int nr = 0, symidx, fd, err = 0;
+	char name[PATH_MAX];
 
-	fd = open(self->long_name, O_RDONLY);
+	snprintf(name, sizeof(name), "%s%s",
+		 symbol_conf.symfs, self->long_name);
+	fd = open(name, O_RDONLY);
 	if (fd < 0)
 		goto out;
 
@@ -1446,16 +1450,19 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
 	     self->origin++) {
 		switch (self->origin) {
 		case DSO__ORIG_BUILD_ID_CACHE:
-			if (dso__build_id_filename(self, name, size) == NULL)
+			/* skip the locally configured cache if a symfs is given */
+			if ((strlen(symbol_conf.symfs) != 0) ||
+			    (dso__build_id_filename(self, name, size) == NULL)) {
 				continue;
+			}
 			break;
 		case DSO__ORIG_FEDORA:
-			snprintf(name, size, "/usr/lib/debug%s.debug",
-				 self->long_name);
+			snprintf(name, size, "%s/usr/lib/debug%s.debug",
+				 symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_UBUNTU:
-			snprintf(name, size, "/usr/lib/debug%s",
-				 self->long_name);
+			snprintf(name, size, "%s/usr/lib/debug%s",
+				 symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_BUILDID: {
 			char build_id_hex[BUILD_ID_SIZE * 2 + 1];
@@ -1467,12 +1474,13 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
 					  sizeof(self->build_id),
 					  build_id_hex);
 			snprintf(name, size,
-				 "/usr/lib/debug/.build-id/%.2s/%s.debug",
-				 build_id_hex, build_id_hex + 2);
+				 "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
+				 symbol_conf.symfs, build_id_hex, build_id_hex + 2);
 			}
 			break;
 		case DSO__ORIG_DSO:
-			snprintf(name, size, "%s", self->long_name);
+			snprintf(name, size, "%s%s",
+			     symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_GUEST_KMODULE:
 			if (map->groups && map->groups->machine)
@@ -1778,17 +1786,20 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
 			     const char *vmlinux, symbol_filter_t filter)
 {
 	int err = -1, fd;
+	char symfs_vmlinux[PATH_MAX];
 
-	fd = open(vmlinux, O_RDONLY);
+	snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s",
+		 symbol_conf.symfs, vmlinux);
+	fd = open(symfs_vmlinux, O_RDONLY);
 	if (fd < 0)
 		return -1;
 
 	dso__set_loaded(self, map->type);
-	err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
+	err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
 	close(fd);
 
 	if (err > 0)
-		pr_debug("Using %s for symbols\n", vmlinux);
+		pr_debug("Using %s for symbols\n", symfs_vmlinux);
 
 	return err;
 }
@@ -1861,6 +1872,10 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
 			goto out_fixup;
 	}
 
+	/* do not try local files if a symfs was given */
+	if (strlen(symbol_conf.symfs) != 0)
+		return -1;
+
 	/*
 	 * Say the kernel DSO was created when processing the build-id header table,
 	 * we have a build-id, so check if it is the same as the running kernel,
@@ -2210,9 +2225,6 @@ static int vmlinux_path__init(void)
 	struct utsname uts;
 	char bf[PATH_MAX];
 
-	if (uname(&uts) < 0)
-		return -1;
-
 	vmlinux_path = malloc(sizeof(char *) * 5);
 	if (vmlinux_path == NULL)
 		return -1;
@@ -2225,22 +2237,30 @@ static int vmlinux_path__init(void)
 	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
 		goto out_fail;
 	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
-		 uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
+
+	/* if no symfs has been given try running kernel version too */
+	if (strlen(symbol_conf.symfs) == 0) {
+		if (uname(&uts) < 0)
+			return -1;
+
+		snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+		snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux",
+			 uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+		snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
+			 uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+	}
 
 	return 0;
 
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 038f220..bbe90d1 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -85,6 +85,7 @@ struct symbol_conf {
        struct strlist	*dso_list,
 			*comm_list,
 			*sym_list;
+	const char	*symfs;
 };
 
 extern struct symbol_conf symbol_conf;
-- 
1.7.2.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] perf tools: Add symfs option for off-box analysis using specified tree
  2010-12-08  2:42 [PATCH] perf tools: Add symfs option for off-box analysis using specified tree David Ahern
@ 2010-12-08 20:56 ` Arnaldo Carvalho de Melo
  2010-12-08 22:22   ` David S. Ahern
  0 siblings, 1 reply; 6+ messages in thread
From: Arnaldo Carvalho de Melo @ 2010-12-08 20:56 UTC (permalink / raw)
  To: David Ahern; +Cc: linux-perf-users, linux-kernel

Em Tue, Dec 07, 2010 at 07:42:54PM -0700, David Ahern escreveu:
> The symfs argument allows analysis of perf.data file using a locally
> accessible filesystem tree with debug symbols - e.g., tree created
> during image builds, sshfs mount, loop mounted KVM disk images,
> USB keys, initrds, etc. Anything with an OS tree can be analyzed from
> anywhere without the need to populate a local data store with
> build-ids.
> 
> Signed-off-by: David Ahern <daahern@cisco.com>
> ---
>  tools/perf/builtin-annotate.c  |   16 +++++--
>  tools/perf/builtin-diff.c      |    2 +
>  tools/perf/builtin-report.c    |    2 +
>  tools/perf/builtin-timechart.c |    2 +
>  tools/perf/util/hist.c         |   14 +++++-
>  tools/perf/util/symbol.c       |   82 +++++++++++++++++++++++++---------------
>  tools/perf/util/symbol.h       |    1 +
>  7 files changed, 80 insertions(+), 39 deletions(-)
> 
> diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
> index 569a276..0c47bee 100644
> --- a/tools/perf/builtin-annotate.c
> +++ b/tools/perf/builtin-annotate.c
> @@ -280,7 +280,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
>  	struct map *map = he->ms.map;
>  	struct dso *dso = map->dso;
>  	struct symbol *sym = he->ms.sym;
> -	const char *filename = dso->long_name, *d_filename;
> +	const char *filename = dso->long_name;
> +	char d_filename[PATH_MAX];
>  	u64 len;
>  	LIST_HEAD(head);
>  	struct objdump_line *pos, *n;
> @@ -288,10 +289,13 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
>  	if (hist_entry__annotate(he, &head, 0) < 0)
>  		return -1;
>  
> -	if (full_paths)
> -		d_filename = filename;
> -	else
> -		d_filename = basename(filename);
> +	if (full_paths) {
> +		snprintf(d_filename, sizeof(d_filename), "%s%s",
> +			 symbol_conf.symfs, filename);
> +	} else {
> +		snprintf(d_filename, sizeof(d_filename), "%s/%s",
> +			 symbol_conf.symfs, basename(filename));
> +	}

Are you sure about the one above? I guess you don't need to concat here,
its just informative message, if you're using --symfs, you know
everything is from there, and in the !full_paths case the resulting
filename will be bogus, right?
  
>  	len = sym->end - sym->start;
>  
> @@ -437,6 +441,8 @@ static const struct option options[] = {
>  		    "print matching source lines (may be slow)"),
>  	OPT_BOOLEAN('P', "full-paths", &full_paths,
>  		    "Don't shorten the displayed pathnames"),
> +	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
> +		    "Look for files with symbols relative to this directory"),
>  	OPT_END()
>  };
>  
> diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
> index 5e1a043..71126a6 100644
> --- a/tools/perf/builtin-diff.c
> +++ b/tools/perf/builtin-diff.c
> @@ -192,6 +192,8 @@ static const struct option options[] = {
>  	OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
>  		   "separator for columns, no spaces will be added between "
>  		   "columns '.' is reserved."),
> +	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
> +		    "Look for files with symbols relative to this directory"),
>  	OPT_END()
>  };
>  
> diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
> index 904519f..57b0f49 100644
> --- a/tools/perf/builtin-report.c
> +++ b/tools/perf/builtin-report.c
> @@ -479,6 +479,8 @@ static const struct option options[] = {
>  		   "columns '.' is reserved."),
>  	OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
>  		    "Only display entries resolved to a symbol"),
> +	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
> +		    "Look for files with symbols relative to this directory"),
>  	OPT_END()
>  };
>  
> diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
> index d2fc461..965aa4e 100644
> --- a/tools/perf/builtin-timechart.c
> +++ b/tools/perf/builtin-timechart.c
> @@ -1021,6 +1021,8 @@ static const struct option options[] = {
>  	OPT_CALLBACK('p', "process", NULL, "process",
>  		      "process selector. Pass a pid or process name.",
>  		       parse_process),
> +	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
> +		    "Look for files with symbols relative to this directory"),
>  	OPT_END()
>  };
>  
> diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
> index 2022e87..b24ae53 100644
> --- a/tools/perf/util/hist.c
> +++ b/tools/perf/util/hist.c
> @@ -1092,6 +1092,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
>  	FILE *file;
>  	int err = 0;
>  	u64 len;
> +	char symfs_filename[PATH_MAX];
> +
> +	if (filename) {
> +		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
> +		 symbol_conf.symfs, filename);

Above you have tab/space damage

> +	}
>  
>  	if (filename == NULL) {
>  		if (dso->has_build_id) {
> @@ -1100,9 +1106,9 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
>  			return -ENOMEM;
>  		}
>  		goto fallback;
> -	} else if (readlink(filename, command, sizeof(command)) < 0 ||
> +	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
>  		   strstr(command, "[kernel.kallsyms]") ||
> -		   access(filename, R_OK)) {
> +		   access(symfs_filename, R_OK)) {
>  		free(filename);
>  fallback:
>  		/*
> @@ -1111,6 +1117,8 @@ fallback:
>  		 * DSO is the same as when 'perf record' ran.
>  		 */
>  		filename = dso->long_name;
> +		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
> +		 symbol_conf.symfs, filename);

Ditto above

>  		free_filename = false;
>  	}
>  
> @@ -1137,7 +1145,7 @@ fallback:
>  		 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
>  		 map__rip_2objdump(map, sym->start),
>  		 map__rip_2objdump(map, sym->end),
> -		 filename, filename);
> +		 symfs_filename, filename);
>  
>  	pr_debug("Executing: %s\n", command);
>  
> diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
> index a348906..7a0d284 100644
> --- a/tools/perf/util/symbol.c
> +++ b/tools/perf/util/symbol.c
> @@ -41,6 +41,7 @@ struct symbol_conf symbol_conf = {
>  	.exclude_other	  = true,
>  	.use_modules	  = true,
>  	.try_vmlinux_path = true,
> +	.symfs           = "",
>  };
>  
>  int dso__name_len(const struct dso *self)
> @@ -833,8 +834,11 @@ static int dso__synthesize_plt_symbols(struct  dso *self, struct map *map,
>  	char sympltname[1024];
>  	Elf *elf;
>  	int nr = 0, symidx, fd, err = 0;
> +	char name[PATH_MAX];
>  
> -	fd = open(self->long_name, O_RDONLY);
> +	snprintf(name, sizeof(name), "%s%s",
> +		 symbol_conf.symfs, self->long_name);
> +	fd = open(name, O_RDONLY);
>  	if (fd < 0)
>  		goto out;
>  
> @@ -1446,16 +1450,19 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
>  	     self->origin++) {
>  		switch (self->origin) {
>  		case DSO__ORIG_BUILD_ID_CACHE:
> -			if (dso__build_id_filename(self, name, size) == NULL)
> +			/* skip the locally configured cache if a symfs is given */
> +			if ((strlen(symbol_conf.symfs) != 0) ||

I suggest you use:

			if (symbol_conf.symfs[1] ||

cheaper :)

> +			    (dso__build_id_filename(self, name, size) == NULL)) {
>  				continue;
> +			}
>  			break;
>  		case DSO__ORIG_FEDORA:
> -			snprintf(name, size, "/usr/lib/debug%s.debug",
> -				 self->long_name);
> +			snprintf(name, size, "%s/usr/lib/debug%s.debug",
> +				 symbol_conf.symfs, self->long_name);
>  			break;
>  		case DSO__ORIG_UBUNTU:
> -			snprintf(name, size, "/usr/lib/debug%s",
> -				 self->long_name);
> +			snprintf(name, size, "%s/usr/lib/debug%s",
> +				 symbol_conf.symfs, self->long_name);
>  			break;
>  		case DSO__ORIG_BUILDID: {
>  			char build_id_hex[BUILD_ID_SIZE * 2 + 1];
> @@ -1467,12 +1474,13 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
>  					  sizeof(self->build_id),
>  					  build_id_hex);
>  			snprintf(name, size,
> -				 "/usr/lib/debug/.build-id/%.2s/%s.debug",
> -				 build_id_hex, build_id_hex + 2);
> +				 "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
> +				 symbol_conf.symfs, build_id_hex, build_id_hex + 2);
>  			}
>  			break;
>  		case DSO__ORIG_DSO:
> -			snprintf(name, size, "%s", self->long_name);
> +			snprintf(name, size, "%s%s",
> +			     symbol_conf.symfs, self->long_name);
>  			break;
>  		case DSO__ORIG_GUEST_KMODULE:
>  			if (map->groups && map->groups->machine)
> @@ -1778,17 +1786,20 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
>  			     const char *vmlinux, symbol_filter_t filter)
>  {
>  	int err = -1, fd;
> +	char symfs_vmlinux[PATH_MAX];
> -	fd = open(vmlinux, O_RDONLY);
> +	snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s",
> +		 symbol_conf.symfs, vmlinux);

Its userspace, so stack is plenty, but I guess if using asprintf
wouldn't be better...

I.e. if we were programming some kernel module, creating a 4K stack
variable would be considered bad practice, so I think it is here as
well.
  
> +	fd = open(symfs_vmlinux, O_RDONLY);
>  	if (fd < 0)
>  		return -1;
>  
>  	dso__set_loaded(self, map->type);
> -	err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
> +	err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
>  	close(fd);
>  
>  	if (err > 0)
> -		pr_debug("Using %s for symbols\n", vmlinux);
> +		pr_debug("Using %s for symbols\n", symfs_vmlinux);
>  
>  	return err;
>  }
> @@ -1861,6 +1872,10 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
>  			goto out_fixup;
>  	}
>  
> +	/* do not try local files if a symfs was given */
> +	if (strlen(symbol_conf.symfs) != 0)
> +		return -1;
> +
>  	/*
>  	 * Say the kernel DSO was created when processing the build-id header table,
>  	 * we have a build-id, so check if it is the same as the running kernel,
> @@ -2210,9 +2225,6 @@ static int vmlinux_path__init(void)
>  	struct utsname uts;
>  	char bf[PATH_MAX];
>  
> -	if (uname(&uts) < 0)
> -		return -1;
> -
>  	vmlinux_path = malloc(sizeof(char *) * 5);
>  	if (vmlinux_path == NULL)
>  		return -1;
> @@ -2225,22 +2237,30 @@ static int vmlinux_path__init(void)
>  	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
>  		goto out_fail;
>  	++vmlinux_path__nr_entries;
> -	snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
> -	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
> -	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
> -		goto out_fail;
> -	++vmlinux_path__nr_entries;
> -	snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
> -	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
> -	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
> -		goto out_fail;
> -	++vmlinux_path__nr_entries;
> -	snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
> -		 uts.release);
> -	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
> -	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
> -		goto out_fail;
> -	++vmlinux_path__nr_entries;
> +
> +	/* if no symfs has been given try running kernel version too */
> +	if (strlen(symbol_conf.symfs) == 0) {

Do it as:

	if (!symbol_conf.symfs[1])
		return 0;

And then the patch can be made smaller.

> +		if (uname(&uts) < 0)
> +			return -1;
> +
> +		snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
> +		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
> +		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
> +			goto out_fail;
> +		++vmlinux_path__nr_entries;
> +		snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux",
> +			 uts.release);
> +		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
> +		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
> +			goto out_fail;
> +		++vmlinux_path__nr_entries;
> +		snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
> +			 uts.release);
> +		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
> +		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
> +			goto out_fail;
> +		++vmlinux_path__nr_entries;
> +	}
>  
>  	return 0;
>  
> diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
> index 038f220..bbe90d1 100644
> --- a/tools/perf/util/symbol.h
> +++ b/tools/perf/util/symbol.h
> @@ -85,6 +85,7 @@ struct symbol_conf {
>         struct strlist	*dso_list,
>  			*comm_list,
>  			*sym_list;
> +	const char	*symfs;
>  };
>  
>  extern struct symbol_conf symbol_conf;
> -- 
> 1.7.2.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-perf-users" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] perf tools: Add symfs option for off-box analysis using specified tree
  2010-12-08 20:56 ` Arnaldo Carvalho de Melo
@ 2010-12-08 22:22   ` David S. Ahern
  2010-12-09  0:29     ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 6+ messages in thread
From: David S. Ahern @ 2010-12-08 22:22 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: linux-perf-users, linux-kernel



On 12/08/10 13:56, Arnaldo Carvalho de Melo wrote:
> Em Tue, Dec 07, 2010 at 07:42:54PM -0700, David Ahern escreveu:
>> The symfs argument allows analysis of perf.data file using a locally
>> accessible filesystem tree with debug symbols - e.g., tree created
>> during image builds, sshfs mount, loop mounted KVM disk images,
>> USB keys, initrds, etc. Anything with an OS tree can be analyzed from
>> anywhere without the need to populate a local data store with
>> build-ids.
>>
>> Signed-off-by: David Ahern <daahern@cisco.com>
>> ---
>>  tools/perf/builtin-annotate.c  |   16 +++++--
>>  tools/perf/builtin-diff.c      |    2 +
>>  tools/perf/builtin-report.c    |    2 +
>>  tools/perf/builtin-timechart.c |    2 +
>>  tools/perf/util/hist.c         |   14 +++++-
>>  tools/perf/util/symbol.c       |   82 +++++++++++++++++++++++++---------------
>>  tools/perf/util/symbol.h       |    1 +
>>  7 files changed, 80 insertions(+), 39 deletions(-)
>>
>> diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
>> index 569a276..0c47bee 100644
>> --- a/tools/perf/builtin-annotate.c
>> +++ b/tools/perf/builtin-annotate.c
>> @@ -280,7 +280,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
>>  	struct map *map = he->ms.map;
>>  	struct dso *dso = map->dso;
>>  	struct symbol *sym = he->ms.sym;
>> -	const char *filename = dso->long_name, *d_filename;
>> +	const char *filename = dso->long_name;
>> +	char d_filename[PATH_MAX];
>>  	u64 len;
>>  	LIST_HEAD(head);
>>  	struct objdump_line *pos, *n;
>> @@ -288,10 +289,13 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
>>  	if (hist_entry__annotate(he, &head, 0) < 0)
>>  		return -1;
>>  
>> -	if (full_paths)
>> -		d_filename = filename;
>> -	else
>> -		d_filename = basename(filename);
>> +	if (full_paths) {
>> +		snprintf(d_filename, sizeof(d_filename), "%s%s",
>> +			 symbol_conf.symfs, filename);
>> +	} else {
>> +		snprintf(d_filename, sizeof(d_filename), "%s/%s",
>> +			 symbol_conf.symfs, basename(filename));
>> +	}
> 
> Are you sure about the one above? I guess you don't need to concat here,
> its just informative message, if you're using --symfs, you know
> everything is from there, and in the !full_paths case the resulting
> filename will be bogus, right?

Ok. I removed.

>   
>>  	len = sym->end - sym->start;
>>  
>> @@ -437,6 +441,8 @@ static const struct option options[] = {
>>  		    "print matching source lines (may be slow)"),
>>  	OPT_BOOLEAN('P', "full-paths", &full_paths,
>>  		    "Don't shorten the displayed pathnames"),
>> +	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
>> +		    "Look for files with symbols relative to this directory"),
>>  	OPT_END()
>>  };

In which case the above is not needed.

>> diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
>> index 2022e87..b24ae53 100644
>> --- a/tools/perf/util/hist.c
>> +++ b/tools/perf/util/hist.c
>> @@ -1092,6 +1092,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
>>  	FILE *file;
>>  	int err = 0;
>>  	u64 len;
>> +	char symfs_filename[PATH_MAX];
>> +
>> +	if (filename) {
>> +		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
>> +		 symbol_conf.symfs, filename);
> 
> Above you have tab/space damage

fixed

> 
>> +	}
>>  
>>  	if (filename == NULL) {
>>  		if (dso->has_build_id) {
>> @@ -1100,9 +1106,9 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
>>  			return -ENOMEM;
>>  		}
>>  		goto fallback;
>> -	} else if (readlink(filename, command, sizeof(command)) < 0 ||
>> +	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
>>  		   strstr(command, "[kernel.kallsyms]") ||
>> -		   access(filename, R_OK)) {
>> +		   access(symfs_filename, R_OK)) {
>>  		free(filename);
>>  fallback:
>>  		/*
>> @@ -1111,6 +1117,8 @@ fallback:
>>  		 * DSO is the same as when 'perf record' ran.
>>  		 */
>>  		filename = dso->long_name;
>> +		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
>> +		 symbol_conf.symfs, filename);
> 
> Ditto above

fixed

> 
>>  		free_filename = false;
>>  	}
>>  
>> @@ -1137,7 +1145,7 @@ fallback:
>>  		 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
>>  		 map__rip_2objdump(map, sym->start),
>>  		 map__rip_2objdump(map, sym->end),
>> -		 filename, filename);
>> +		 symfs_filename, filename);
>>  
>>  	pr_debug("Executing: %s\n", command);
>>  
>> diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
>> index a348906..7a0d284 100644
>> --- a/tools/perf/util/symbol.c
>> +++ b/tools/perf/util/symbol.c
>> @@ -41,6 +41,7 @@ struct symbol_conf symbol_conf = {
>>  	.exclude_other	  = true,
>>  	.use_modules	  = true,
>>  	.try_vmlinux_path = true,
>> +	.symfs           = "",
>>  };
>>  
>>  int dso__name_len(const struct dso *self)
>> @@ -833,8 +834,11 @@ static int dso__synthesize_plt_symbols(struct  dso *self, struct map *map,
>>  	char sympltname[1024];
>>  	Elf *elf;
>>  	int nr = 0, symidx, fd, err = 0;
>> +	char name[PATH_MAX];
>>  
>> -	fd = open(self->long_name, O_RDONLY);
>> +	snprintf(name, sizeof(name), "%s%s",
>> +		 symbol_conf.symfs, self->long_name);
>> +	fd = open(name, O_RDONLY);
>>  	if (fd < 0)
>>  		goto out;
>>  
>> @@ -1446,16 +1450,19 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
>>  	     self->origin++) {
>>  		switch (self->origin) {
>>  		case DSO__ORIG_BUILD_ID_CACHE:
>> -			if (dso__build_id_filename(self, name, size) == NULL)
>> +			/* skip the locally configured cache if a symfs is given */
>> +			if ((strlen(symbol_conf.symfs) != 0) ||
> 
> I suggest you use:
> 
> 			if (symbol_conf.symfs[1] ||
> 
> cheaper :)

Changed. I presume you meant: symbol_conf.symfs[0]  -- ie., element 0
has something other than '\0' which means strlen > 0

...

>> @@ -1778,17 +1786,20 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
>>  			     const char *vmlinux, symbol_filter_t filter)
>>  {
>>  	int err = -1, fd;
>> +	char symfs_vmlinux[PATH_MAX];
>> -	fd = open(vmlinux, O_RDONLY);
>> +	snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s",
>> +		 symbol_conf.symfs, vmlinux);
> 
> Its userspace, so stack is plenty, but I guess if using asprintf
> wouldn't be better...
> 
> I.e. if we were programming some kernel module, creating a 4K stack
> variable would be considered bad practice, so I think it is here as
> well.

Declaring symfs_vmlinux on the stack is consistent with other PATH_MAX
declarations within perf, and especially within util/symbol.c.

>   
>> +	fd = open(symfs_vmlinux, O_RDONLY);
>>  	if (fd < 0)
>>  		return -1;
>>  
>>  	dso__set_loaded(self, map->type);
>> -	err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
>> +	err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
>>  	close(fd);
>>  
>>  	if (err > 0)
>> -		pr_debug("Using %s for symbols\n", vmlinux);
>> +		pr_debug("Using %s for symbols\n", symfs_vmlinux);
>>  
>>  	return err;
>>  }
>> @@ -1861,6 +1872,10 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
>>  			goto out_fixup;
>>  	}
>>  
>> +	/* do not try local files if a symfs was given */
>> +	if (strlen(symbol_conf.symfs) != 0)
>> +		return -1;
>> +

changed that strlen as well.


>>  	/*
>>  	 * Say the kernel DSO was created when processing the build-id header table,
>>  	 * we have a build-id, so check if it is the same as the running kernel,
>> @@ -2210,9 +2225,6 @@ static int vmlinux_path__init(void)
>>  	struct utsname uts;
>>  	char bf[PATH_MAX];
>>  
>> -	if (uname(&uts) < 0)
>> -		return -1;
>> -
>>  	vmlinux_path = malloc(sizeof(char *) * 5);
>>  	if (vmlinux_path == NULL)
>>  		return -1;
>> @@ -2225,22 +2237,30 @@ static int vmlinux_path__init(void)
>>  	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
>>  		goto out_fail;
>>  	++vmlinux_path__nr_entries;
>> -	snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
>> -	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
>> -	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
>> -		goto out_fail;
>> -	++vmlinux_path__nr_entries;
>> -	snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
>> -	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
>> -	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
>> -		goto out_fail;
>> -	++vmlinux_path__nr_entries;
>> -	snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
>> -		 uts.release);
>> -	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
>> -	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
>> -		goto out_fail;
>> -	++vmlinux_path__nr_entries;
>> +
>> +	/* if no symfs has been given try running kernel version too */
>> +	if (strlen(symbol_conf.symfs) == 0) {
> 
> Do it as:
> 
> 	if (!symbol_conf.symfs[1])
> 		return 0;
> 
> And then the patch can be made smaller.

Done.


I also added the symfs option to the Documentation files. Will send an
updated patch.

David

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] perf tools: Add symfs option for off-box analysis using specified tree
  2010-12-08 22:22   ` David S. Ahern
@ 2010-12-09  0:29     ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 6+ messages in thread
From: Arnaldo Carvalho de Melo @ 2010-12-09  0:29 UTC (permalink / raw)
  To: David S. Ahern; +Cc: linux-perf-users, linux-kernel

Em Wed, Dec 08, 2010 at 03:22:37PM -0700, David S. Ahern escreveu:
> On 12/08/10 13:56, Arnaldo Carvalho de Melo wrote:
> > Em Tue, Dec 07, 2010 at 07:42:54PM -0700, David Ahern escreveu:
> >> +			if ((strlen(symbol_conf.symfs) != 0) ||
> > 
> > I suggest you use:
> > 
> > 			if (symbol_conf.symfs[1] ||
> > 
> > cheaper :)
> 
> Changed. I presume you meant: symbol_conf.symfs[0]  -- ie., element 0
> has something other than '\0' which means strlen > 0

Right, off-by-one mistake by me, sorry
 
> >> @@ -1778,17 +1786,20 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
> >>  			     const char *vmlinux, symbol_filter_t filter)
> >>  {
> >>  	int err = -1, fd;
> >> +	char symfs_vmlinux[PATH_MAX];
> >> -	fd = open(vmlinux, O_RDONLY);
> >> +	snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s",
> >> +		 symbol_conf.symfs, vmlinux);
> > 
> > Its userspace, so stack is plenty, but I guess if using asprintf
> > wouldn't be better...
> > 
> > I.e. if we were programming some kernel module, creating a 4K stack
> > variable would be considered bad practice, so I think it is here as
> > well.
> 
> Declaring symfs_vmlinux on the stack is consistent with other PATH_MAX
> declarations within perf, and especially within util/symbol.c.

Ok, so keep it like that and I'll then work on a follow on patch to
remove these stack abuses.
 
> >> +	if (strlen(symbol_conf.symfs) == 0) {
> > 
> > Do it as:
> > 
> > 	if (!symbol_conf.symfs[1])
> > 		return 0;
> > 
> > And then the patch can be made smaller.
> 
> Done.
> 
> 
> I also added the symfs option to the Documentation files. Will send an
> updated patch.

Thanks a lot!

- Arnaldo

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH] perf tools: Add symfs option for off-box analysis using specified tree
@ 2010-11-29 16:58 David Ahern
  0 siblings, 0 replies; 6+ messages in thread
From: David Ahern @ 2010-11-29 16:58 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel; +Cc: David Ahern

The symfs argument allows analysis of perf.data file using a locally
accessible filesystem tree with debug symbols - e.g., tree created
during image builds, sshfs mount, loop mounted KVM disk images,
USB keys, initrds, etc. Anything with an OS tree can be analyzed from
anywhere without the need to populate a local data store with
build-ids.

Signed-off-by: David Ahern <daahern@cisco.com>
---
 tools/perf/builtin-annotate.c  |   16 +++++--
 tools/perf/builtin-diff.c      |    2 +
 tools/perf/builtin-report.c    |    2 +
 tools/perf/builtin-timechart.c |    2 +
 tools/perf/util/hist.c         |   14 +++++-
 tools/perf/util/symbol.c       |   82 +++++++++++++++++++++++++---------------
 tools/perf/util/symbol.h       |    1 +
 7 files changed, 80 insertions(+), 39 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 6d5604d8d..1db38da 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -280,7 +280,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
 	struct map *map = he->ms.map;
 	struct dso *dso = map->dso;
 	struct symbol *sym = he->ms.sym;
-	const char *filename = dso->long_name, *d_filename;
+	const char *filename = dso->long_name;
+	char d_filename[PATH_MAX];
 	u64 len;
 	LIST_HEAD(head);
 	struct objdump_line *pos, *n;
@@ -288,10 +289,13 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
 	if (hist_entry__annotate(he, &head, 0) < 0)
 		return -1;
 
-	if (full_paths)
-		d_filename = filename;
-	else
-		d_filename = basename(filename);
+	if (full_paths) {
+		snprintf(d_filename, sizeof(d_filename), "%s%s",
+			 symbol_conf.symfs, filename);
+	} else {
+		snprintf(d_filename, sizeof(d_filename), "%s/%s",
+			 symbol_conf.symfs, basename(filename));
+	}
 
 	len = sym->end - sym->start;
 
@@ -437,6 +441,8 @@ static const struct option options[] = {
 		    "print matching source lines (may be slow)"),
 	OPT_BOOLEAN('P', "full-paths", &full_paths,
 		    "Don't shorten the displayed pathnames"),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index fca1d44..85a8179 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -191,6 +191,8 @@ static const struct option options[] = {
 	OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
 		   "separator for columns, no spaces will be added between "
 		   "columns '.' is reserved."),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index b0c6995..371b184 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -480,6 +480,8 @@ static const struct option options[] = {
 		   "columns '.' is reserved."),
 	OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
 		    "Only display entries resolved to a symbol"),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 9bcc38f..fa07ece 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -1018,6 +1018,8 @@ static const struct option options[] = {
 	OPT_CALLBACK('p', "process", NULL, "process",
 		      "process selector. Pass a pid or process name.",
 		       parse_process),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 2022e87..b24ae53 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1092,6 +1092,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
 	FILE *file;
 	int err = 0;
 	u64 len;
+	char symfs_filename[PATH_MAX];
+
+	if (filename) {
+		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
+		 symbol_conf.symfs, filename);
+	}
 
 	if (filename == NULL) {
 		if (dso->has_build_id) {
@@ -1100,9 +1106,9 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
 			return -ENOMEM;
 		}
 		goto fallback;
-	} else if (readlink(filename, command, sizeof(command)) < 0 ||
+	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
 		   strstr(command, "[kernel.kallsyms]") ||
-		   access(filename, R_OK)) {
+		   access(symfs_filename, R_OK)) {
 		free(filename);
 fallback:
 		/*
@@ -1111,6 +1117,8 @@ fallback:
 		 * DSO is the same as when 'perf record' ran.
 		 */
 		filename = dso->long_name;
+		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
+		 symbol_conf.symfs, filename);
 		free_filename = false;
 	}
 
@@ -1137,7 +1145,7 @@ fallback:
 		 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
 		 map__rip_2objdump(map, sym->start),
 		 map__rip_2objdump(map, sym->end),
-		 filename, filename);
+		 symfs_filename, filename);
 
 	pr_debug("Executing: %s\n", command);
 
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index f40c076..9e2cc44 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -41,6 +41,7 @@ struct symbol_conf symbol_conf = {
 	.exclude_other	  = true,
 	.use_modules	  = true,
 	.try_vmlinux_path = true,
+	.symfs           = "",
 };
 
 int dso__name_len(const struct dso *self)
@@ -833,8 +834,11 @@ static int dso__synthesize_plt_symbols(struct  dso *self, struct map *map,
 	char sympltname[1024];
 	Elf *elf;
 	int nr = 0, symidx, fd, err = 0;
+	char name[PATH_MAX];
 
-	fd = open(self->long_name, O_RDONLY);
+	snprintf(name, sizeof(name), "%s%s",
+		 symbol_conf.symfs, self->long_name);
+	fd = open(name, O_RDONLY);
 	if (fd < 0)
 		goto out;
 
@@ -1446,16 +1450,19 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
 	     self->origin++) {
 		switch (self->origin) {
 		case DSO__ORIG_BUILD_ID_CACHE:
-			if (dso__build_id_filename(self, name, size) == NULL)
+			/* skip the locally configured cache if a symfs is given */
+			if ((strlen(symbol_conf.symfs) != 0) ||
+			    (dso__build_id_filename(self, name, size) == NULL)) {
 				continue;
+			}
 			break;
 		case DSO__ORIG_FEDORA:
-			snprintf(name, size, "/usr/lib/debug%s.debug",
-				 self->long_name);
+			snprintf(name, size, "%s/usr/lib/debug%s.debug",
+				 symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_UBUNTU:
-			snprintf(name, size, "/usr/lib/debug%s",
-				 self->long_name);
+			snprintf(name, size, "%s/usr/lib/debug%s",
+				 symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_BUILDID: {
 			char build_id_hex[BUILD_ID_SIZE * 2 + 1];
@@ -1467,12 +1474,13 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
 					  sizeof(self->build_id),
 					  build_id_hex);
 			snprintf(name, size,
-				 "/usr/lib/debug/.build-id/%.2s/%s.debug",
-				 build_id_hex, build_id_hex + 2);
+				 "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
+				 symbol_conf.symfs, build_id_hex, build_id_hex + 2);
 			}
 			break;
 		case DSO__ORIG_DSO:
-			snprintf(name, size, "%s", self->long_name);
+			snprintf(name, size, "%s%s",
+			     symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_GUEST_KMODULE:
 			if (map->groups && map->groups->machine)
@@ -1778,17 +1786,20 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
 			     const char *vmlinux, symbol_filter_t filter)
 {
 	int err = -1, fd;
+	char symfs_vmlinux[PATH_MAX];
 
-	fd = open(vmlinux, O_RDONLY);
+	snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s",
+		 symbol_conf.symfs, vmlinux);
+	fd = open(symfs_vmlinux, O_RDONLY);
 	if (fd < 0)
 		return -1;
 
 	dso__set_loaded(self, map->type);
-	err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
+	err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
 	close(fd);
 
 	if (err > 0)
-		pr_debug("Using %s for symbols\n", vmlinux);
+		pr_debug("Using %s for symbols\n", symfs_vmlinux);
 
 	return err;
 }
@@ -1866,6 +1877,10 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
 			goto out_fixup;
 	}
 
+	/* do not try local files if a symfs was given */
+	if (strlen(symbol_conf.symfs) != 0)
+		return -1;
+
 	/*
 	 * Say the kernel DSO was created when processing the build-id header table,
 	 * we have a build-id, so check if it is the same as the running kernel,
@@ -2215,9 +2230,6 @@ static int vmlinux_path__init(void)
 	struct utsname uts;
 	char bf[PATH_MAX];
 
-	if (uname(&uts) < 0)
-		return -1;
-
 	vmlinux_path = malloc(sizeof(char *) * 5);
 	if (vmlinux_path == NULL)
 		return -1;
@@ -2230,22 +2242,30 @@ static int vmlinux_path__init(void)
 	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
 		goto out_fail;
 	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
-		 uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
+
+	/* if no symfs has been given try running kernel version too */
+	if (strlen(symbol_conf.symfs) == 0) {
+		if (uname(&uts) < 0)
+			return -1;
+
+		snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+		snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux",
+			 uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+		snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
+			 uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+	}
 
 	return 0;
 
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 12defbe..bcd2f98 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -86,6 +86,7 @@ struct symbol_conf {
        struct strlist	*dso_list,
 			*comm_list,
 			*sym_list;
+	const char	*symfs;
 };
 
 extern struct symbol_conf symbol_conf;
-- 
1.7.2.3


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH] perf tools: Add symfs option for off-box analysis using specified tree
@ 2010-11-22 14:45 David Ahern
  0 siblings, 0 replies; 6+ messages in thread
From: David Ahern @ 2010-11-22 14:45 UTC (permalink / raw)
  To: linux-perf-users; +Cc: David Ahern

The symfs argument allows analysis of perf.data file using a locally
accessible filesystem tree with debug symbols - e.g., loop mounted
KVM disk images, NFS, USB keys, initrds, etc. Anything with an OS tree
can be analyzed from anywhere without the need to populate a local
data store with build-ids.

Signed-off-by: David Ahern <daahern@cisco.com>
---
 tools/perf/builtin-annotate.c  |   16 +++++--
 tools/perf/builtin-diff.c      |    2 +
 tools/perf/builtin-report.c    |    2 +
 tools/perf/builtin-timechart.c |    2 +
 tools/perf/util/hist.c         |   14 +++++-
 tools/perf/util/symbol.c       |   82 +++++++++++++++++++++++++---------------
 tools/perf/util/symbol.h       |    1 +
 7 files changed, 80 insertions(+), 39 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 6d5604d8d..1db38da 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -280,7 +280,8 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
 	struct map *map = he->ms.map;
 	struct dso *dso = map->dso;
 	struct symbol *sym = he->ms.sym;
-	const char *filename = dso->long_name, *d_filename;
+	const char *filename = dso->long_name;
+	char d_filename[PATH_MAX];
 	u64 len;
 	LIST_HEAD(head);
 	struct objdump_line *pos, *n;
@@ -288,10 +289,13 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
 	if (hist_entry__annotate(he, &head, 0) < 0)
 		return -1;
 
-	if (full_paths)
-		d_filename = filename;
-	else
-		d_filename = basename(filename);
+	if (full_paths) {
+		snprintf(d_filename, sizeof(d_filename), "%s%s",
+			 symbol_conf.symfs, filename);
+	} else {
+		snprintf(d_filename, sizeof(d_filename), "%s/%s",
+			 symbol_conf.symfs, basename(filename));
+	}
 
 	len = sym->end - sym->start;
 
@@ -437,6 +441,8 @@ static const struct option options[] = {
 		    "print matching source lines (may be slow)"),
 	OPT_BOOLEAN('P', "full-paths", &full_paths,
 		    "Don't shorten the displayed pathnames"),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index fca1d44..85a8179 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -191,6 +191,8 @@ static const struct option options[] = {
 	OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
 		   "separator for columns, no spaces will be added between "
 		   "columns '.' is reserved."),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 5de405d..9d552da 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -478,6 +478,8 @@ static const struct option options[] = {
 		   "columns '.' is reserved."),
 	OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
 		    "Only display entries resolved to a symbol"),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 9bcc38f..fa07ece 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -1018,6 +1018,8 @@ static const struct option options[] = {
 	OPT_CALLBACK('p', "process", NULL, "process",
 		      "process selector. Pass a pid or process name.",
 		       parse_process),
+	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
+		    "Look for files with symbols relative to this directory"),
 	OPT_END()
 };
 
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 2022e87..b24ae53 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1092,6 +1092,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
 	FILE *file;
 	int err = 0;
 	u64 len;
+	char symfs_filename[PATH_MAX];
+
+	if (filename) {
+		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
+		 symbol_conf.symfs, filename);
+	}
 
 	if (filename == NULL) {
 		if (dso->has_build_id) {
@@ -1100,9 +1106,9 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
 			return -ENOMEM;
 		}
 		goto fallback;
-	} else if (readlink(filename, command, sizeof(command)) < 0 ||
+	} else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
 		   strstr(command, "[kernel.kallsyms]") ||
-		   access(filename, R_OK)) {
+		   access(symfs_filename, R_OK)) {
 		free(filename);
 fallback:
 		/*
@@ -1111,6 +1117,8 @@ fallback:
 		 * DSO is the same as when 'perf record' ran.
 		 */
 		filename = dso->long_name;
+		snprintf(symfs_filename, sizeof(symfs_filename), "%s%s",
+		 symbol_conf.symfs, filename);
 		free_filename = false;
 	}
 
@@ -1137,7 +1145,7 @@ fallback:
 		 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
 		 map__rip_2objdump(map, sym->start),
 		 map__rip_2objdump(map, sym->end),
-		 filename, filename);
+		 symfs_filename, filename);
 
 	pr_debug("Executing: %s\n", command);
 
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 861be8b..3503f1c 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -41,6 +41,7 @@ struct symbol_conf symbol_conf = {
 	.exclude_other	  = true,
 	.use_modules	  = true,
 	.try_vmlinux_path = true,
+	.symfs           = "",
 };
 
 int dso__name_len(const struct dso *self)
@@ -831,8 +832,11 @@ static int dso__synthesize_plt_symbols(struct  dso *self, struct map *map,
 	char sympltname[1024];
 	Elf *elf;
 	int nr = 0, symidx, fd, err = 0;
+	char name[PATH_MAX];
 
-	fd = open(self->long_name, O_RDONLY);
+	snprintf(name, sizeof(name), "%s%s",
+		 symbol_conf.symfs, self->long_name);
+	fd = open(name, O_RDONLY);
 	if (fd < 0)
 		goto out;
 
@@ -1444,16 +1448,19 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
 	     self->origin++) {
 		switch (self->origin) {
 		case DSO__ORIG_BUILD_ID_CACHE:
-			if (dso__build_id_filename(self, name, size) == NULL)
+			/* skip the locally configured cache if a symfs is given */
+			if ((strlen(symbol_conf.symfs) != 0) ||
+			    (dso__build_id_filename(self, name, size) == NULL)) {
 				continue;
+			}
 			break;
 		case DSO__ORIG_FEDORA:
-			snprintf(name, size, "/usr/lib/debug%s.debug",
-				 self->long_name);
+			snprintf(name, size, "%s/usr/lib/debug%s.debug",
+				 symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_UBUNTU:
-			snprintf(name, size, "/usr/lib/debug%s",
-				 self->long_name);
+			snprintf(name, size, "%s/usr/lib/debug%s",
+				 symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_BUILDID: {
 			char build_id_hex[BUILD_ID_SIZE * 2 + 1];
@@ -1465,12 +1472,13 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
 					  sizeof(self->build_id),
 					  build_id_hex);
 			snprintf(name, size,
-				 "/usr/lib/debug/.build-id/%.2s/%s.debug",
-				 build_id_hex, build_id_hex + 2);
+				 "%s/usr/lib/debug/.build-id/%.2s/%s.debug",
+				 symbol_conf.symfs, build_id_hex, build_id_hex + 2);
 			}
 			break;
 		case DSO__ORIG_DSO:
-			snprintf(name, size, "%s", self->long_name);
+			snprintf(name, size, "%s%s",
+			     symbol_conf.symfs, self->long_name);
 			break;
 		case DSO__ORIG_GUEST_KMODULE:
 			if (!map->groups || !map->groups->machine)
@@ -1776,17 +1784,20 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
 			     const char *vmlinux, symbol_filter_t filter)
 {
 	int err = -1, fd;
+	char symfs_vmlinux[PATH_MAX];
 
-	fd = open(vmlinux, O_RDONLY);
+	snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s",
+		 symbol_conf.symfs, vmlinux);
+	fd = open(symfs_vmlinux, O_RDONLY);
 	if (fd < 0)
 		return -1;
 
 	dso__set_loaded(self, map->type);
-	err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
+	err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0);
 	close(fd);
 
 	if (err > 0)
-		pr_debug("Using %s for symbols\n", vmlinux);
+		pr_debug("Using %s for symbols\n", symfs_vmlinux);
 
 	return err;
 }
@@ -1859,6 +1870,10 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
 			goto out_fixup;
 	}
 
+	/* do not try local files if a symfs was given */
+	if (strlen(symbol_conf.symfs) != 0)
+		return -1;
+
 	/*
 	 * Say the kernel DSO was created when processing the build-id header table,
 	 * we have a build-id, so check if it is the same as the running kernel,
@@ -2208,9 +2223,6 @@ static int vmlinux_path__init(void)
 	struct utsname uts;
 	char bf[PATH_MAX];
 
-	if (uname(&uts) < 0)
-		return -1;
-
 	vmlinux_path = malloc(sizeof(char *) * 5);
 	if (vmlinux_path == NULL)
 		return -1;
@@ -2223,22 +2235,30 @@ static int vmlinux_path__init(void)
 	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
 		goto out_fail;
 	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
-	snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
-		 uts.release);
-	vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
-	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
-		goto out_fail;
-	++vmlinux_path__nr_entries;
+
+	/* if no symfs has been given try running kernel version too */
+	if (strlen(symbol_conf.symfs) == 0) {
+		if (uname(&uts) < 0)
+			return -1;
+
+		snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+		snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux",
+			 uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+		snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
+			 uts.release);
+		vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
+		if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+			goto out_fail;
+		++vmlinux_path__nr_entries;
+	}
 
 	return 0;
 
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 038f220..bbe90d1 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -85,6 +85,7 @@ struct symbol_conf {
        struct strlist	*dso_list,
 			*comm_list,
 			*sym_list;
+	const char	*symfs;
 };
 
 extern struct symbol_conf symbol_conf;
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2010-12-09  0:29 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-08  2:42 [PATCH] perf tools: Add symfs option for off-box analysis using specified tree David Ahern
2010-12-08 20:56 ` Arnaldo Carvalho de Melo
2010-12-08 22:22   ` David S. Ahern
2010-12-09  0:29     ` Arnaldo Carvalho de Melo
  -- strict thread matches above, loose matches on Subject: below --
2010-11-29 16:58 David Ahern
2010-11-22 14:45 David Ahern

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.