[RFC/PATCH LGUEST X86_64 04/13] Useful debugging

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



plain text document attachment (lguest64-debug-utils.patch)
This patch contains some nice features used to debug the lguest64 guest.

It has a way to print page tables for either the host or the guest.

It incorporates kallsyms, and can do a nice back trace of a guest
when it crashes.  The guest needs kallsyms obviously compiled in.
Note: This code needs to be fixed to be more secure!

Implements a lgdebug_print that can be used within the host that
will only print when lguest_debug is true.  There's a hypercall
that the guest can call to turn this on.

There's also a function called lguest_set_debug(n) the makes it
easy for the guest to turn it on. Where n=1 will turn on debugging
prints, and n=0 will turn it off. (well n!=0 will turn it on).

Signed-off-by: Steven Rostedt <srostedt@xxxxxxxxxx>
Cc: Glauber de Oliveira Costa <glommer@xxxxxxxxx>
Cc: Chris Wright <chrisw@xxxxxxxxxxxx>



Index: work-pv/arch/x86_64/lguest/lguest_debug.c
===================================================================
--- /dev/null
+++ work-pv/arch/x86_64/lguest/lguest_debug.c
@@ -0,0 +1,532 @@
+/*
+    lguest debug utils. Modified from various other parts of Linux.
+    What was modified is Copyright 2007 Steven Rostedt, Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+*/
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/freezer.h>
+#include <linux/kallsyms.h>
+#include <asm/paravirt.h>
+#include <asm/hv_vm.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include "lguest.h"
+
+int lguest_debug;
+
+static DEFINE_SPINLOCK(lgdebug_print_lock);
+#define LGDEBUG_BUF_SIZ 1024
+static char lgdebug_print_buf[LGDEBUG_BUF_SIZ];
+
+void lgdebug_vprint(const char *fmt, va_list ap)
+{
+	unsigned long flags;
+
+	if (!lguest_debug)
+		return;
+
+	spin_lock_irqsave(&lgdebug_print_lock, flags);
+	vsnprintf(lgdebug_print_buf, LGDEBUG_BUF_SIZ-1, fmt, ap);
+	printk("%s", lgdebug_print_buf);
+	spin_unlock_irqrestore(&lgdebug_print_lock, flags);
+}
+
+void lgdebug_print(const char *fmt, ...)
+{
+	va_list ap;
+
+	if (!lguest_debug)
+		return;
+
+	/* irq save? */
+	va_start(ap, fmt);
+	lgdebug_vprint(fmt, ap);
+	va_end(ap);
+}
+
+void lguest_dump_vcpu_regs(struct lguest_vcpu *vcpu)
+{
+	struct lguest_regs *regs = &vcpu->regs;
+
+	printk("Printing VCPU %d regs cr3: %016llx\n", vcpu->id, regs->cr3);
+	printk("RIP: %04llx: ", regs->cs & 0xffff);
+	lguest_print_address(vcpu, regs->rip);
+	printk("RSP: %04llx:%016llx  EFLAGS: %08llx\n", regs->ss, regs->rsp,
+		regs->rflags);
+	printk("RAX: %016llx RBX: %016llx RCX: %016llx\n",
+	       regs->rax, regs->rbx, regs->rcx);
+	printk("RDX: %016llx RSI: %016llx RDI: %016llx\n",
+	       regs->rdx, regs->rsi, regs->rdi);
+	printk("RBP: %016llx R08: %016llx R09: %016llx\n",
+	       regs->rbp, regs->r8, regs->r9);
+	printk("R10: %016llx R11: %016llx R12: %016llx\n",
+	       regs->r10, regs->r11, regs->r12);
+	printk("R13: %016llx R14: %016llx R15: %016llx\n",
+	       regs->r13, regs->r14, regs->r15);
+
+	printk("errcode: %llx   trapnum: %llx\n",
+	       regs->errcode, regs->trapnum);
+
+	lguest_dump_trace(vcpu, regs);
+}
+
+struct guest_ksym_stuff {
+	unsigned long *addresses;
+	unsigned long num_syms;
+	u8 *names;
+	u8 *token_table;
+	u16 *token_index;
+	unsigned long *markers;
+};
+
+static struct lguest_text_ptr *get_text_segs(struct lguest_vcpu *vcpu)
+{
+	struct lguest_guest_info *linfo = vcpu->guest;
+	struct lguest_text_ptr *segs, **p;
+	struct lguest_text_ptr *g;
+	unsigned long addr;
+	int i;
+
+	if (!linfo->lguest_data)
+		return NULL;
+
+	addr = lhread_u64(vcpu, (u64)&linfo->lguest_data->text);
+	if (!addr)
+		return NULL;
+
+	g = (struct lguest_text_ptr*)addr;
+
+	p = &segs;
+
+	/* only allow for 10 segs */
+	for (i=0; i < 10; i++) {
+		*p = kmalloc(sizeof(*segs), GFP_KERNEL);
+		if (!*p)
+			goto free_me;
+		(*p)->start = lhread_u64(vcpu, (u64)&g->start);
+		(*p)->end = lhread_u64(vcpu, (u64)&g->end);
+		addr = lhread_u64(vcpu, (u64)&g->next);
+		p = (struct lguest_text_ptr**)&((*p)->next);
+		if (!addr)
+			break;
+		g = (struct lguest_text_ptr*)addr;
+	}
+	*p = NULL;
+
+	return segs;
+
+free_me:
+	while (segs) {
+		g = (struct lguest_text_ptr*)segs->next;
+		kfree(segs);
+		segs = g;
+	}
+	return NULL;
+}
+
+static int is_text_seg(struct lguest_text_ptr *segs, unsigned long addr)
+{
+	while (segs) {
+		if (addr >= segs->start &&
+		    addr <= segs->end)
+			return 1;
+		segs = (struct lguest_text_ptr*)segs->next;
+	}
+	return 0;
+}
+
+static void put_text_segs(struct lguest_text_ptr *segs)
+{
+	struct lguest_text_ptr *p;
+
+	while (segs) {
+		p = (struct lguest_text_ptr*)segs->next;
+		kfree(segs);
+		segs = p;
+	}
+}
+
+static unsigned int expand_symbol(struct lguest_vcpu *vcpu,
+				  struct guest_ksym_stuff *kstuff,
+				  unsigned int off, char *result)
+{
+	int len, skipped_first = 0;
+	const u8 *tptr, *data;
+
+	/* get the compressed symbol length from the first symbol byte */
+	data = &kstuff->names[off];
+
+	len = lhread_u8(vcpu, (u64)data);
+
+	data++;
+
+	/* update the offset to return the offset for the next symbol on
+	 * the compressed stream */
+	off += len + 1;
+
+	/* for every byte on the compressed symbol data, copy the table
+	   entry for that byte */
+	while(len) {
+		u8 idx;
+		u16 tok;
+		idx = lhread_u8(vcpu, (u64)data);
+		tok = lhread_u16(vcpu, (u64)(&kstuff->token_index[idx]));
+		tptr = &kstuff->token_table[ tok ];
+		data++;
+		len--;
+
+		idx = lhread_u8(vcpu, (u64)tptr);
+		while (idx) {
+			if(skipped_first) {
+				*result = idx;
+				result++;
+			} else
+				skipped_first = 1;
+			tptr++;
+			idx = lhread_u8(vcpu, (u64)tptr);
+		}
+	}
+
+	*result = '\0';
+
+	/* return to offset to the next symbol */
+	return off;
+}
+
+static unsigned long get_symbol_pos(struct lguest_vcpu *vcpu,
+				    struct guest_ksym_stuff *kstuff,
+				    unsigned long addr,
+				    unsigned long *symbolsize,
+				    unsigned long *offset)
+{
+	unsigned long symbol_start = 0, symbol_end = 0;
+	unsigned long i, low, high, mid;
+
+	/* do a binary search on the sorted kallsyms_addresses array */
+	low = 0;
+	high = kstuff->num_syms;
+
+	while (high - low > 1) {
+		mid = (low + high) / 2;
+		if (kstuff->addresses[mid] <= addr)
+			low = mid;
+		else
+			high = mid;
+	}
+
+	/*
+	 * search for the first aliased symbol. Aliased
+	 * symbols are symbols with the same address
+	 */
+	while (low && kstuff->addresses[low-1] == kstuff->addresses[low])
+		--low;
+
+	symbol_start = kstuff->addresses[low];
+
+	/* Search for next non-aliased symbol */
+	for (i = low + 1; i < kstuff->num_syms; i++) {
+		if (kstuff->addresses[i] > symbol_start) {
+			symbol_end = kstuff->addresses[i];
+			break;
+		}
+	}
+
+	/* if we found no next symbol, we use the end of the section */
+	if (!symbol_end) {
+		return (unsigned long)(-1UL);
+#if 0
+		if (is_kernel_inittext(addr))
+			symbol_end = (unsigned long)_einittext;
+		else if (all_var)
+			symbol_end = (unsigned long)_end;
+		else
+			symbol_end = (unsigned long)_etext;
+#endif
+	}
+
+	*symbolsize = symbol_end - symbol_start;
+	*offset = addr - symbol_start;
+
+	return low;
+}
+
+static int is_ksym_addr(struct lguest_guest_info *linfo,
+			unsigned long addr)
+{
+	/* need to look up the segs */
+	return 1;
+}
+
+static unsigned int get_symbol_offset(struct lguest_vcpu *vcpu,
+				      struct guest_ksym_stuff *kstuff,
+				      unsigned long pos)
+{
+	const u8 *name;
+	int i;
+	unsigned long idx;
+
+	idx = lhread_u64(vcpu, (u64)&kstuff->markers[pos>>8]);
+
+	/* use the closest marker we have. We have markers every 256 positions,
+	 * so that should be close enough */
+	name = &kstuff->names[ idx ];
+
+	/* sequentially scan all the symbols up to the point we're searching for.
+	 * Every symbol is stored in a [<len>][<len> bytes of data] format, so we
+	 * just need to add the len to the current pointer for every symbol we
+	 * wish to skip */
+	for(i = 0; i < (pos&0xFF); i++) {
+		u8 c;
+		c = lhread_u8(vcpu, (u64)name);
+		name = name + c + 1;
+	}
+
+	return name - kstuff->names;
+}
+
+static const char *lguest_syms_lookup(struct lguest_vcpu *vcpu,
+				      unsigned long addr,
+				      unsigned long *symbolsize,
+				      unsigned long *offset,
+				      char **modname, char *namebuf)
+{
+	struct lguest_guest_info *linfo = vcpu->guest;
+	struct lguest_data *data = linfo->lguest_data;
+	struct guest_ksym_stuff kstuff;
+	const char *msym;
+	unsigned long *ptr;
+	int i;
+
+	kstuff.addresses = (unsigned long*)lhread_u64(vcpu, (u64)&data->kallsyms_addresses);
+	kstuff.num_syms = lhread_u64(vcpu, (u64)&data->kallsyms_num_syms);
+	kstuff.names = (u8*)lhread_u64(vcpu, (u64)&data->kallsyms_names);
+	kstuff.token_table = (u8*)lhread_u64(vcpu, (u64)&data->kallsyms_token_table);
+	kstuff.token_index = (u16*)lhread_u64(vcpu, (u64)&data->kallsyms_token_index);
+	kstuff.markers = (unsigned long*)lhread_u64(vcpu, (u64)&data->kallsyms_markers);
+
+	if (!kstuff.addresses || !kstuff.num_syms || !kstuff.names ||
+	    !kstuff.token_table || !kstuff.token_index || !kstuff.markers)
+		return NULL;
+
+	/* FIXME: Validate all the kstuff here!! */
+
+	ptr = kmalloc(sizeof(unsigned long)*kstuff.num_syms, GFP_KERNEL);
+	if (!ptr)
+		return NULL;
+
+	for (i=0; i < kstuff.num_syms; i++) {
+		/* FIXME: do this better! */
+		ptr[i] = lhread_u64(vcpu, (u64)&kstuff.addresses[i]);
+		if (i && ptr[i] < ptr[i-1]) {
+			kill_guest(linfo, "bad kallsyms table\n");
+			kstuff.addresses = ptr;
+			goto out;
+		}
+	}
+	kstuff.addresses = ptr;
+
+	namebuf[KSYM_NAME_LEN] = 0;
+	namebuf[0] = 0;
+
+	if (is_ksym_addr(linfo, addr)) {
+		unsigned long pos;
+
+		pos = get_symbol_pos(vcpu, &kstuff, addr, symbolsize, offset);
+		if (pos == (unsigned long)(-1UL))
+			goto out;
+
+		/* Grab name */
+		expand_symbol(vcpu, &kstuff,
+			      get_symbol_offset(vcpu, &kstuff, pos), namebuf);
+		*modname = NULL;
+		kfree(kstuff.addresses);
+		return namebuf;
+	}
+
+	/* see if it's in a module */
+	msym = module_address_lookup(addr, symbolsize, offset, modname);
+	if (msym) {
+		kfree(kstuff.addresses);
+		return strncpy(namebuf, msym, KSYM_NAME_LEN);
+	}
+
+out:
+	kfree(kstuff.addresses);
+	return NULL;
+}
+
+void lguest_print_address(struct lguest_vcpu *vcpu, unsigned long address)
+{
+	unsigned long offset = 0, symsize;
+	const char *symname;
+	char *modname;
+	char *delim = ":";
+	char namebuf[KSYM_NAME_LEN+1];
+
+	symname = lguest_syms_lookup(vcpu, address, &symsize, &offset,
+				     &modname, namebuf);
+	if (!symname) {
+		printk(" [<%016lx>]\n", address);
+		return;
+	}
+	if (!modname)
+		modname = delim = "";
+	printk(" [<%016lx>] %s%s%s%s+0x%lx/0x%lx\n",
+	       address, delim, modname, delim, symname, offset, symsize);
+
+}
+
+void lguest_dump_trace(struct lguest_vcpu *vcpu, struct lguest_regs *regs)
+{
+	unsigned long stack = regs->rsp;
+	unsigned long stack_end = (regs->rsp & PAGE_MASK) + PAGE_SIZE;
+	unsigned long start_kernel_map;
+	unsigned long page_offset;
+	unsigned long addr;
+	struct lguest_text_ptr *segs;
+
+	printk("Stack Dump:\n");
+	start_kernel_map = vcpu->guest->start_kernel_map;
+	page_offset = vcpu->guest->page_offset;
+
+	segs = get_text_segs(vcpu);
+	if (!segs)
+		return;
+
+	for (; stack < stack_end; stack += sizeof(stack)) {
+		addr = lhread_u64(vcpu, guest_pa(vcpu->guest, stack));
+		if (is_text_seg(segs, addr)) {
+			lguest_print_address(vcpu, addr);
+		}
+	}
+
+	put_text_segs(segs);
+}
+
+static u64 read_page(struct lguest_vcpu *vcpu, u64 page, u64 idx)
+{
+	u64 *ptr;
+
+	if (!vcpu) {
+		ptr = __va(page);
+		return ptr[idx];
+	}
+
+	return lhread_u64(vcpu, page+idx*sizeof(u64));
+}
+
+static void print_pte(u64 pte, u64 pgd_idx, u64 pud_idx, u64 pmd_idx, u64 pte_idx)
+{
+	printk("           %3llx: %llx\n", pte_idx, pte);
+	printk ("               (%llx)\n",
+		((pgd_idx&(1<<8)?(-1ULL):0ULL)<<48) |
+		(pgd_idx<<PGDIR_SHIFT) |
+		(pud_idx<<PUD_SHIFT) |
+		(pmd_idx<<PMD_SHIFT) |
+		(pte_idx<<PAGE_SHIFT));
+}
+
+static void print_pmd(struct lguest_vcpu *vcpu,
+		      u64 pmd, u64 pgd_idx, u64 pud_idx, u64 pmd_idx)
+{
+	u64 pte;
+	u64 ptr;
+	u64 i;
+
+	printk("        %3llx: %llx\n", pmd_idx, pmd);
+
+	/* 2M page? */
+	if (pmd & (1<<7)) {
+		printk ("            (%llx)\n",
+			((pgd_idx&(1<<8)?(-1ULL):0ULL)<<48) |
+			(pgd_idx<<PGDIR_SHIFT) |
+			(pud_idx<<PUD_SHIFT) |
+			(pmd_idx<<PMD_SHIFT));
+	} else {
+		pte = pmd & ~(0xfff) & ~(1UL << 63);
+		for (i=0; i < PTRS_PER_PTE; i++) {
+			ptr = read_page(vcpu, pte, i);
+			if (ptr)
+				print_pte(ptr, pgd_idx, pud_idx, pmd_idx, i);
+		}
+	}
+}
+
+static void print_pud(struct lguest_vcpu *vcpu,
+		      u64 pud, u64 pgd_idx, u64 pud_idx)
+{
+	u64 pmd;
+	u64 ptr;
+	u64 i;
+
+	printk("     %3llx: %llx\n", pud_idx, pud);
+
+	pmd = pud & ~(0xfff) & ~(1UL << 63);
+	for (i=0; i < PTRS_PER_PMD; i++) {
+		ptr = read_page(vcpu, pmd, i);
+		if (ptr)
+			print_pmd(vcpu, ptr, pgd_idx, pud_idx, i);
+	}
+}
+
+static void print_pgd(struct lguest_vcpu *vcpu,
+		      u64 pgd, u64 pgd_idx)
+{
+	u64 pud;
+	u64 ptr;
+	u64 i;
+
+	printk(" %3llx:  %llx\n", pgd_idx, pgd);
+	pud = pgd & ~(0xfff) & ~(1UL << 63);
+	for (i=0; i < PTRS_PER_PUD; i++) {
+		ptr = read_page(vcpu, pud, i);
+		if (ptr)
+			print_pud(vcpu, ptr, pgd_idx, i);
+	}
+
+}
+
+static void print_page_tables(struct lguest_vcpu *vcpu,
+			      u64 cr3)
+{
+	u64 pgd;
+	u64 ptr;
+	u64 i;
+
+	printk("cr3: %016llx\n", cr3);
+	pgd = cr3;
+
+	for (i=0; i < PTRS_PER_PGD; i++) {
+		ptr = read_page(vcpu, pgd, i);
+		if (ptr)
+			print_pgd(vcpu, ptr, i);
+	}
+}
+
+void lguest_print_page_tables(u64 *cr3)
+{
+	if (!cr3) {
+		printk("NULL cr3 pointer????\n");
+		return;
+	}
+	print_page_tables(NULL, __pa(cr3));
+}
+
+void lguest_print_guest_page_tables(struct lguest_vcpu *vcpu, u64 cr3)
+{
+	print_page_tables(vcpu, cr3);
+}

--

_______________________________________________
Virtualization mailing list
Virtualization@xxxxxxxxxxxxxx
https://lists.osdl.org/mailman/listinfo/virtualization


[Index of Archives]     [KVM Development]     [Libvirt Development]     [Libvirt Users]     [CentOS Virtualization]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux