perf symbols: Look for vmlinux in more places

Now that we can check the buildid to see if it really matches,
this can be done safely:

  vmlinux
  /boot/vmlinux
  /boot/vmlinux-<uts.release>
  /lib/modules/<uts.release>/build/vmlinux
  /usr/lib/debug/lib/modules/%s/vmlinux

More can be added - if you know about distros that put the
vmlinux somewhere else please let us know.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1259001550-8194-1-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 74b5b8a..44d81d5 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -34,6 +34,8 @@
 static int dso__load_kernel_sym(struct dso *self, struct map *map,
 				symbol_filter_t filter);
 unsigned int symbol__priv_size;
+static int vmlinux_path__nr_entries;
+static char **vmlinux_path;
 
 static struct rb_root kernel_maps;
 
@@ -1386,15 +1388,43 @@
 static int dso__load_kernel_sym(struct dso *self, struct map *map,
 				symbol_filter_t filter)
 {
-	int err = dso__load_vmlinux(self, map, self->name, filter);
+	int err;
+	bool is_kallsyms;
 
+	if (vmlinux_path != NULL) {
+		int i;
+		pr_debug("Looking at the vmlinux_path (%d entries long)\n",
+			 vmlinux_path__nr_entries);
+		for (i = 0; i < vmlinux_path__nr_entries; ++i) {
+			err = dso__load_vmlinux(self, map, vmlinux_path[i],
+						filter);
+			if (err > 0) {
+				pr_debug("Using %s for symbols\n",
+					 vmlinux_path[i]);
+				dso__set_long_name(self,
+						   strdup(vmlinux_path[i]));
+				goto out_fixup;
+			}
+		}
+	}
+
+	is_kallsyms = self->long_name[0] == '[';
+	if (is_kallsyms)
+		goto do_kallsyms;
+
+	err = dso__load_vmlinux(self, map, self->long_name, filter);
 	if (err <= 0) {
+		pr_info("The file %s cannot be used, "
+			"trying to use /proc/kallsyms...", self->long_name);
+		sleep(2);
+do_kallsyms:
 		err = kernel_maps__load_kallsyms(filter);
-		if (err > 0)
+		if (err > 0 && !is_kallsyms)
                         dso__set_long_name(self, strdup("[kernel.kallsyms]"));
 	}
 
 	if (err > 0) {
+out_fixup:
 		map__fixup_start(map);
 		map__fixup_end(map);
 	}
@@ -1403,9 +1433,7 @@
 }
 
 LIST_HEAD(dsos);
-struct dso	*vdso;
-
-const char	*vmlinux_name = "vmlinux";
+struct dso *vdso;
 
 static void dsos__add(struct dso *dso)
 {
@@ -1457,9 +1485,9 @@
 	return ret;
 }
 
-static int kernel_maps__create_kernel_map(void)
+static int kernel_maps__create_kernel_map(const char *vmlinux_name)
 {
-	struct dso *kernel = dso__new(vmlinux_name);
+	struct dso *kernel = dso__new(vmlinux_name ?: "[kernel.kallsyms]");
 
 	if (kernel == NULL)
 		return -1;
@@ -1468,10 +1496,10 @@
 	if (kernel_map == NULL)
 		goto out_delete_kernel_dso;
 
-	kernel_map->map_ip = kernel_map->unmap_ip = identity__map_ip;
+	kernel_map->map_ip	 = kernel_map->unmap_ip = identity__map_ip;
+	kernel->short_name	 = "[kernel]";
+	kernel->kernel		 = 1;
 
-	kernel->short_name = "[kernel]";
-	kernel->kernel = 1;
 	vdso = dso__new("[vdso]");
 	if (vdso == NULL)
 		goto out_delete_kernel_map;
@@ -1494,11 +1522,72 @@
 	return -1;
 }
 
-int kernel_maps__init(bool use_modules)
+static void vmlinux_path__exit(void)
 {
-	if (kernel_maps__create_kernel_map() < 0)
+	while (--vmlinux_path__nr_entries >= 0) {
+		free(vmlinux_path[vmlinux_path__nr_entries]);
+		vmlinux_path[vmlinux_path__nr_entries] = NULL;
+	}
+
+	free(vmlinux_path);
+	vmlinux_path = NULL;
+}
+
+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;
+
+	vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux");
+	if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+		goto out_fail;
+	++vmlinux_path__nr_entries;
+	vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux");
+	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;
+
+	return 0;
+
+out_fail:
+	vmlinux_path__exit();
+	return -1;
+}
+
+int kernel_maps__init(const char *vmlinux_name, bool try_vmlinux_path,
+		      bool use_modules)
+{
+	if (try_vmlinux_path && vmlinux_path__init() < 0)
+		return -1;
+
+	if (kernel_maps__create_kernel_map(vmlinux_name) < 0) {
+		vmlinux_path__exit();
+		return -1;
+	}
+
 	if (use_modules && kernel_maps__create_module_maps() < 0)
 		pr_debug("Failed to load list of modules in use, "
 			 "continuing...\n");