diff mbox

kvm tools: Lookup symbol based on RIP for 'kill -3'

Message ID 1305144589-21229-1-git-send-email-penberg@kernel.org (mailing list archive)
State New, archived
Headers show

Commit Message

Pekka Enberg May 11, 2011, 8:09 p.m. UTC
To make debugging easier, look up symbol from guest kernel image based on RIP
when user does 'kill -3' to the hypervisor.

Example output looks as follows:

  Code:
  -----
  rip: [<ffffffff812cb3a0>] delay_loop+30 (/home/penberg/linux/arch/x86/lib/delay.c:32)

Cc: Asias He <asias.hejun@gmail.com>
Cc: Avi Kivity <avi@redhat.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Prasad Joshi <prasadjoshi124@gmail.com>
Cc: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
---
 tools/kvm/Makefile             |    2 +
 tools/kvm/include/kvm/kvm.h    |    2 +
 tools/kvm/include/kvm/symbol.h |   12 +++++
 tools/kvm/kvm-cpu.c            |    9 ++++
 tools/kvm/kvm-run.c            |   43 +++++++++++++++--
 tools/kvm/symbol.c             |   98 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 161 insertions(+), 5 deletions(-)
 create mode 100644 tools/kvm/include/kvm/symbol.h
 create mode 100644 tools/kvm/symbol.c

Comments

Cyrill Gorcunov May 11, 2011, 8:22 p.m. UTC | #1
On 05/12/2011 12:09 AM, Pekka Enberg wrote:
> To make debugging easier, look up symbol from guest kernel image based on RIP
> when user does 'kill -3' to the hypervisor.
> 
> Example output looks as follows:
> 

FWIW, works pretty well for me. Probably we need some -e option for vmlinux path in future.
Ingo Molnar May 12, 2011, 7:25 a.m. UTC | #2
* Pekka Enberg <penberg@kernel.org> wrote:

> To make debugging easier, look up symbol from guest kernel image based on RIP
> when user does 'kill -3' to the hypervisor.
> 
> Example output looks as follows:
> 
>   Code:
>   -----
>   rip: [<ffffffff812cb3a0>] delay_loop+30 (/home/penberg/linux/arch/x86/lib/delay.c:32)

Hm, it does not work here:

 rip: [<ffffffff8103921f>] <unknown>

While ../../vmlinux definitely has it:

ffffffff810391d9 <default_idle>:

ffffffff8103921f:       eb 01                   jmp    ffffffff81039222 <default_idle+0x49>

Does libbfd require debug symbols? That's sad as i almost never use debug 
symbols.

It would be nice to import the relevant elflib magic from tools/perf/.

Thanks,

	Ingo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ingo Molnar May 12, 2011, 7:28 a.m. UTC | #3
Another suggestion: would anyone be interested in adding a 'kvm debug' command 
that does the kill -3 magic?

Initially it could be something very simple like:

	int cmd_debug()
	{
		system("kill -3 $(pidof kvm)");
	}

:-)

as i always have to remember this sequence to debug the guest - while typing 
'kvm debug' would be a no-brainer.

(Maybe make it 'kvm debug guest', to keep the 'kvm debug' namespace clean.)

Thanks,

	Ingo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index fb839fc..1aaed24 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -24,6 +24,7 @@  OBJS	+= main.o
 OBJS	+= mmio.o
 OBJS	+= pci.o
 OBJS	+= rtc.o
+OBJS	+= symbol.o
 OBJS	+= term.o
 OBJS	+= util.o
 OBJS	+= virtio/blk.o
@@ -50,6 +51,7 @@  OBJS	+= bios/bios.o
 
 LIBS	+= -lrt
 LIBS	+= -lpthread
+LIBS	+= -lbfd
 
 # Additional ARCH settings for x86
 ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
diff --git a/tools/kvm/include/kvm/kvm.h b/tools/kvm/include/kvm/kvm.h
index 501c74c..b310d50 100644
--- a/tools/kvm/include/kvm/kvm.h
+++ b/tools/kvm/include/kvm/kvm.h
@@ -28,6 +28,8 @@  struct kvm {
 	u16			boot_sp;
 
 	struct interrupt_table	interrupt_table;
+
+	const char		*vmlinux;
 };
 
 struct kvm *kvm__init(const char *kvm_dev, unsigned long ram_size);
diff --git a/tools/kvm/include/kvm/symbol.h b/tools/kvm/include/kvm/symbol.h
new file mode 100644
index 0000000..eaa84ea
--- /dev/null
+++ b/tools/kvm/include/kvm/symbol.h
@@ -0,0 +1,12 @@ 
+#ifndef KVM__SYMBOL_H
+#define KVM__SYMBOL_H
+
+#include <stddef.h>
+
+struct kvm;
+
+void symbol__init(const char *vmlinux);
+
+char *symbol__lookup(struct kvm *kvm, unsigned long addr, char *sym, size_t size);
+
+#endif /* KVM__SYMBOL_H */
diff --git a/tools/kvm/kvm-cpu.c b/tools/kvm/kvm-cpu.c
index 8a27e02..a507fa4 100644
--- a/tools/kvm/kvm-cpu.c
+++ b/tools/kvm/kvm-cpu.c
@@ -1,5 +1,6 @@ 
 #include "kvm/kvm-cpu.h"
 
+#include "kvm/symbol.h"
 #include "kvm/util.h"
 #include "kvm/kvm.h"
 
@@ -9,6 +10,7 @@ 
 #include <sys/mman.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <string.h>
 #include <errno.h>
 #include <stdio.h>
 
@@ -282,11 +284,14 @@  void kvm_cpu__show_registers(struct kvm_cpu *vcpu)
 	printf("\n");
 }
 
+#define MAX_SYM_LEN		128
+
 void kvm_cpu__show_code(struct kvm_cpu *vcpu)
 {
 	unsigned int code_bytes = 64;
 	unsigned int code_prologue = code_bytes * 43 / 64;
 	unsigned int code_len = code_bytes;
+	char sym[MAX_SYM_LEN];
 	unsigned char c;
 	unsigned int i;
 	u8 *ip;
@@ -302,6 +307,10 @@  void kvm_cpu__show_code(struct kvm_cpu *vcpu)
 	printf("\n Code:\n");
 	printf(  " -----\n");
 
+	symbol__lookup(vcpu->kvm, vcpu->regs.rip, sym, MAX_SYM_LEN);
+
+	printf(" rip: [<%016lx>] %s\n\n", (unsigned long) vcpu->regs.rip, sym);
+
 	for (i = 0; i < code_len; i++, ip++) {
 		if (!host_ptr_in_ram(vcpu->kvm, ip))
 			break;
diff --git a/tools/kvm/kvm-run.c b/tools/kvm/kvm-run.c
index 84f05cb..91a194e 100644
--- a/tools/kvm/kvm-run.c
+++ b/tools/kvm/kvm-run.c
@@ -26,6 +26,7 @@ 
 #include <kvm/ioport.h>
 #include <kvm/threadpool.h>
 #include <kvm/barrier.h>
+#include <kvm/symbol.h>
 
 /* header files for gitish interface  */
 #include <kvm/kvm-run.h>
@@ -52,6 +53,7 @@  static u64 ram_size;
 static u8  image_count;
 static const char *kernel_cmdline;
 static const char *kernel_filename;
+static const char *vmlinux_filename;
 static const char *initrd_filename;
 static const char *image_filename[MAX_DISK_IMAGES];
 static const char *console;
@@ -214,17 +216,25 @@  panic_kvm:
 }
 
 static char kernel[PATH_MAX];
-const char *host_kernels[] = {
+
+static const char *host_kernels[] = {
 	"/boot/vmlinuz",
 	"/boot/bzImage",
 	NULL
 };
-const char *default_kernels[] = {
+
+static const char *default_kernels[] = {
 	"./bzImage",
 	"../../arch/x86/boot/bzImage",
 	NULL
 };
 
+static const char *default_vmlinux[] = {
+	"../../../vmlinux",
+	"../../vmlinux",
+	NULL
+};
+
 static void kernel_usage_with_options(void)
 {
 	const char **k;
@@ -317,6 +327,23 @@  static const char *find_kernel(void)
 	return NULL;
 }
 
+static const char *find_vmlinux(void)
+{
+	const char **vmlinux;
+
+	vmlinux = &default_vmlinux[0];
+	while (*vmlinux) {
+		struct stat st;
+
+		if (stat(*vmlinux, &st) < 0 || !S_ISREG(st.st_mode)) {
+			vmlinux++;
+			continue;
+		}
+		return *vmlinux;
+	}
+	return NULL;
+}
+
 static int root_device(char *dev, long *part)
 {
 	struct stat st;
@@ -359,13 +386,13 @@  static char *host_image(char *cmd_line, size_t size)
 
 int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 {
+	struct virtio_net_parameters net_params;
 	static char real_cmdline[2048];
 	unsigned int nr_online_cpus;
-	int max_cpus;
 	int exit_code = 0;
-	int i;
-	struct virtio_net_parameters net_params;
+	int max_cpus;
 	char *hi;
+	int i;
 
 	signal(SIGALRM, handle_sigalrm);
 	signal(SIGQUIT, handle_sigquit);
@@ -399,6 +426,8 @@  int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 		return EINVAL;
 	}
 
+	vmlinux_filename = find_vmlinux();
+
 	if (nrcpus < 1 || nrcpus > KVM_NR_CPUS)
 		die("Number of CPUs %d is out of [1;%d] range", nrcpus, KVM_NR_CPUS);
 
@@ -433,6 +462,8 @@  int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 	if (!script)
 		script = DEFAULT_SCRIPT;
 
+	symbol__init(vmlinux_filename);
+
 	term_init();
 
 	kvm = kvm__init(kvm_dev, ram_size);
@@ -482,6 +513,8 @@  int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 				real_cmdline))
 		die("unable to load kernel %s", kernel_filename);
 
+	kvm->vmlinux		= vmlinux_filename;
+
 	ioport__setup_legacy();
 
 	rtc__init();
diff --git a/tools/kvm/symbol.c b/tools/kvm/symbol.c
new file mode 100644
index 0000000..56dd346
--- /dev/null
+++ b/tools/kvm/symbol.c
@@ -0,0 +1,98 @@ 
+#include "kvm/symbol.h"
+
+#include "kvm/kvm.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <bfd.h>
+
+static bfd		*abfd;
+
+void symbol__init(const char *vmlinux)
+{
+	if (!vmlinux)
+		return;
+
+	bfd_init();
+
+	abfd		= bfd_openr(vmlinux, NULL);
+}
+
+static asymbol *lookup(asymbol **symbols, int nr_symbols, const char *symbol_name)
+{
+	int i;
+
+	for (i = 0; i < nr_symbols; i++) {
+		asymbol *symbol = symbols[i];
+
+		if (!strcmp(bfd_asymbol_name(symbol), symbol_name))
+			return symbol;
+	}
+
+	return NULL;
+}
+
+char *symbol__lookup(struct kvm *kvm, unsigned long addr, char *sym, size_t size)
+{
+	const char *filename;
+	bfd_vma sym_offset;
+	bfd_vma sym_start;
+	asection *section;
+	unsigned int line;
+	const char *func;
+	long symtab_size;
+	asymbol *symbol;
+	asymbol **syms;
+	int nr_syms;
+	char *s;
+
+	if (!abfd)
+		goto not_found;
+
+	if (!bfd_check_format(abfd, bfd_object))
+		goto not_found;
+
+	symtab_size	= bfd_get_symtab_upper_bound(abfd);
+	if (!symtab_size)
+		goto not_found;
+
+	syms		= malloc(symtab_size);
+	if (!syms)
+		goto not_found;
+
+	nr_syms		= bfd_canonicalize_symtab(abfd, syms);
+
+	section		= bfd_get_section_by_name(abfd, ".debug_aranges");
+	if (!section)
+		goto not_found;
+
+	if (!bfd_find_nearest_line(abfd, section, NULL, addr, &filename, &func, &line))
+		goto not_found;
+
+	if (!func)
+		goto not_found;
+
+	symbol		= lookup(syms, nr_syms, func);
+	if (!symbol)
+		goto not_found;
+
+	sym_start	= bfd_asymbol_value(symbol);
+
+	sym_offset	= addr - sym_start;
+
+	snprintf(sym, size, "%s+%llx (%s:%i)", func, (long long) sym_offset, filename, line);
+
+	sym[size - 1] = '\0';
+
+	free(syms);
+
+	return sym;
+
+not_found:
+	s = strncpy(sym, "<unknown>", size);
+
+	sym[size - 1] = '\0';
+
+	return s;
+}