From 48664deb37128132802d3c3143d5a2609ab1e9d7 Mon Sep 17 00:00:00 2001 From: "ach61@arcadians.cl.cam.ac.uk" Date: Mon, 15 Aug 2005 19:04:28 +0000 Subject: PDB: watchpoints for process context --- tools/debugger/pdb/Domain.ml | 1 + tools/debugger/pdb/Domain.mli | 1 + tools/debugger/pdb/Makefile | 3 +- tools/debugger/pdb/PDB.ml | 43 +- tools/debugger/pdb/Process.ml | 5 + tools/debugger/pdb/Process.mli | 4 +- tools/debugger/pdb/debugger.ml | 30 +- tools/debugger/pdb/linux-2.6-module/debug.c | 535 +++++++++++++++++++-- tools/debugger/pdb/linux-2.6-module/module.c | 21 +- tools/debugger/pdb/linux-2.6-module/pdb_debug.h | 9 +- tools/debugger/pdb/linux-2.6-module/pdb_module.h | 48 +- .../pdb/linux-2.6-patches/i386_ksyms.patch | 10 +- tools/debugger/pdb/pdb_caml_domain.c | 48 ++ tools/debugger/pdb/pdb_caml_process.c | 94 +++- tools/debugger/pdb/readme | 18 +- 15 files changed, 787 insertions(+), 83 deletions(-) diff --git a/tools/debugger/pdb/Domain.ml b/tools/debugger/pdb/Domain.ml index 29754b47f8..dc3fd2fb30 100644 --- a/tools/debugger/pdb/Domain.ml +++ b/tools/debugger/pdb/Domain.ml @@ -36,6 +36,7 @@ let string_of_context ctx = Printf.sprintf "{domain} domain: %d, vcpu: %d" ctx.domain ctx.vcpu +external read_register : context_t -> int -> int32 = "dom_read_register" external read_registers : context_t -> registers = "dom_read_registers" external write_register : context_t -> register -> int32 -> unit = "dom_write_register" diff --git a/tools/debugger/pdb/Domain.mli b/tools/debugger/pdb/Domain.mli index f9c92a30f5..d9cf115c4f 100644 --- a/tools/debugger/pdb/Domain.mli +++ b/tools/debugger/pdb/Domain.mli @@ -22,6 +22,7 @@ val get_vcpu : context_t -> int val string_of_context : context_t -> string +val read_register : context_t -> int -> int32 val read_registers : context_t -> registers val write_register : context_t -> register -> int32 -> unit val read_memory : context_t -> int32 -> int -> int list diff --git a/tools/debugger/pdb/Makefile b/tools/debugger/pdb/Makefile index 9652a11013..85f8beaff7 100644 --- a/tools/debugger/pdb/Makefile +++ b/tools/debugger/pdb/Makefile @@ -33,7 +33,8 @@ LIBDIRS += ../libxendebug LIBS += unix str # bc = byte-code, dc = debug byte-code -all : patches dc +# patches = patch linux domU source code +all : dc SOURCES += pdb_caml_xc.c SOURCES += pdb_caml_domain.c pdb_caml_process.c diff --git a/tools/debugger/pdb/PDB.ml b/tools/debugger/pdb/PDB.ml index 3c4b063362..da9ad7d2c6 100644 --- a/tools/debugger/pdb/PDB.ml +++ b/tools/debugger/pdb/PDB.ml @@ -219,6 +219,17 @@ let add_default_context sock = (***************************************************************************) +let read_register ctx register = (* register is int32 because of sscanf *) + match ctx with + | Void -> 0l (* default for startup *) + | Domain d -> Domain.read_register d register + | Process p -> + begin + Process.read_register p register; + raise No_reply + end + | _ -> raise (Unimplemented "read registers") + let read_registers ctx = match ctx with | Void -> Intel.null_registers (* default for startup *) @@ -278,15 +289,43 @@ let step ctx = let insert_memory_breakpoint ctx addr len = match ctx with | Domain d -> Domain.insert_memory_breakpoint d addr len - | Process p -> Process.insert_memory_breakpoint p addr len + | Process p -> + begin + Process.insert_memory_breakpoint p addr len; + raise No_reply + end | _ -> raise (Unimplemented "insert memory breakpoint") let remove_memory_breakpoint ctx addr len = match ctx with | Domain d -> Domain.remove_memory_breakpoint d addr len - | Process p -> Process.remove_memory_breakpoint p addr len + | Process p -> + begin + Process.remove_memory_breakpoint p addr len; + raise No_reply + end | _ -> raise (Unimplemented "remove memory breakpoint") +let insert_watchpoint ctx kind addr len = + match ctx with +(* | Domain d -> Domain.insert_watchpoint d kind addr len TODO *) + | Process p -> + begin + Process.insert_watchpoint p kind addr len; + raise No_reply + end + | _ -> raise (Unimplemented "insert watchpoint") + +let remove_watchpoint ctx kind addr len = + match ctx with +(* | Domain d -> Domain.remove_watchpoint d kind addr len TODO *) + | Process p -> + begin + Process.remove_watchpoint p kind addr len; + raise No_reply + end + | _ -> raise (Unimplemented "remove watchpoint") + let pause ctx = match ctx with diff --git a/tools/debugger/pdb/Process.ml b/tools/debugger/pdb/Process.ml index 5585da0c5c..ad98241de4 100644 --- a/tools/debugger/pdb/Process.ml +++ b/tools/debugger/pdb/Process.ml @@ -54,6 +54,7 @@ let attach_debugger proc_ctx dom_ctx = proc_ctx.ring <- Xen_domain.get_ring dom_ctx; _attach_debugger proc_ctx +external read_register : context_t -> int -> unit = "proc_read_register" external read_registers : context_t -> unit = "proc_read_registers" external write_register : context_t -> register -> int32 -> unit = "proc_write_register" @@ -69,6 +70,10 @@ external insert_memory_breakpoint : context_t -> int32 -> int -> unit = "proc_insert_memory_breakpoint" external remove_memory_breakpoint : context_t -> int32 -> int -> unit = "proc_remove_memory_breakpoint" +external insert_watchpoint : context_t -> int -> int32 -> int -> unit = + "proc_insert_watchpoint" +external remove_watchpoint : context_t -> int -> int32 -> int -> unit = + "proc_remove_watchpoint" let pause ctx = pause_target ctx diff --git a/tools/debugger/pdb/Process.mli b/tools/debugger/pdb/Process.mli index ed5b8bfe87..733b681f59 100644 --- a/tools/debugger/pdb/Process.mli +++ b/tools/debugger/pdb/Process.mli @@ -26,7 +26,7 @@ val attach_debugger : context_t -> Xen_domain.context_t -> unit val detach_debugger : context_t -> unit val pause : context_t -> unit - +val read_register : context_t -> int -> unit val read_registers : context_t -> unit val write_register : context_t -> register -> int32 -> unit val read_memory : context_t -> int32 -> int -> unit @@ -37,3 +37,5 @@ val step : context_t -> unit val insert_memory_breakpoint : context_t -> int32 -> int -> unit val remove_memory_breakpoint : context_t -> int32 -> int -> unit +val insert_watchpoint : context_t -> int -> int32 -> int -> unit +val remove_watchpoint : context_t -> int -> int32 -> int -> unit diff --git a/tools/debugger/pdb/debugger.ml b/tools/debugger/pdb/debugger.ml index 6d20b68294..aa741a4566 100644 --- a/tools/debugger/pdb/debugger.ml +++ b/tools/debugger/pdb/debugger.ml @@ -53,10 +53,20 @@ let gdb_step ctx = PDB.step ctx; raise No_reply +(** + Read Register Command. + return register as a 4-byte value. + *) +let gdb_read_register ctx command = + let read_reg register = + (Printf.sprintf "%08lx" (Util.flip_int32 (PDB.read_register ctx register))) + in + Scanf.sscanf command "p%x" read_reg + (** Read Registers Command. - returns 16 4-byte registers in a particular defined by gdb. + returns 16 4-byte registers in a particular format defined by gdb. *) let gdb_read_registers ctx = let regs = PDB.read_registers ctx in @@ -100,7 +110,7 @@ let gdb_read_memory ctx command = with Failure s -> "E02" in - Scanf.sscanf command "m%lx,%d" read_mem + Scanf.sscanf command "m%lx,%x" read_mem @@ -218,16 +228,24 @@ let pdb_extensions command sock = (** Insert Breakpoint or Watchpoint Packet *) + +let bwc_watch_write = 102 (* from pdb_module.h *) +let bwc_watch_read = 103 +let bwc_watch_access = 104 + let gdb_insert_bwcpoint ctx command = let insert cmd addr length = try match cmd with | 0 -> PDB.insert_memory_breakpoint ctx addr length; "OK" + | 2 -> PDB.insert_watchpoint ctx bwc_watch_write addr length; "OK" + | 3 -> PDB.insert_watchpoint ctx bwc_watch_read addr length; "OK" + | 4 -> PDB.insert_watchpoint ctx bwc_watch_access addr length; "OK" | _ -> "" with Failure s -> "E03" in - Scanf.sscanf command "Z%d,%lx,%d" insert + Scanf.sscanf command "Z%d,%lx,%x" insert (** Remove Breakpoint or Watchpoint Packet @@ -237,6 +255,9 @@ let gdb_remove_bwcpoint ctx command = try match cmd with | 0 -> PDB.remove_memory_breakpoint ctx addr length; "OK" + | 2 -> PDB.remove_watchpoint ctx bwc_watch_write addr length; "OK" + | 3 -> PDB.remove_watchpoint ctx bwc_watch_read addr length; "OK" + | 4 -> PDB.remove_watchpoint ctx bwc_watch_access addr length; "OK" | _ -> "" with Failure s -> "E04" @@ -260,6 +281,7 @@ let process_command command sock = | 'k' -> gdb_kill () | 'm' -> gdb_read_memory ctx command | 'M' -> gdb_write_memory ctx command + | 'p' -> gdb_read_register ctx command | 'P' -> gdb_write_register ctx command | 'q' -> gdb_query command | 's' -> gdb_step ctx @@ -270,7 +292,7 @@ let process_command command sock = | 'Z' -> gdb_insert_bwcpoint ctx command | _ -> print_endline (Printf.sprintf "unknown gdb command [%s]" command); - "E02" + "" with Unimplemented s -> print_endline (Printf.sprintf "loser. unimplemented command [%s][%s]" diff --git a/tools/debugger/pdb/linux-2.6-module/debug.c b/tools/debugger/pdb/linux-2.6-module/debug.c index 983e02dccc..ae1064ebad 100644 --- a/tools/debugger/pdb/linux-2.6-module/debug.c +++ b/tools/debugger/pdb/linux-2.6-module/debug.c @@ -9,33 +9,143 @@ #include #include #include +#include #include #include "pdb_module.h" #include "pdb_debug.h" -#define BWC_DEBUG 1 -#define BWC_INT3 3 + +static int pdb_debug_fn (struct pt_regs *regs, long error_code, + unsigned int condition); +static int pdb_int3_fn (struct pt_regs *regs, long error_code); +static int pdb_page_fault_fn (struct pt_regs *regs, long error_code, + unsigned int condition); + +/***********************************************************************/ + typedef struct bwcpoint /* break/watch/catch point */ { struct list_head list; memory_t address; - u32 domain; - u32 process; - u8 old_value; /* old value for software bkpt */ + int length; + u8 type; /* BWC_??? */ + u8 mode; /* for BWC_PAGE, the current protection mode */ + u32 process; + u8 error; /* error occured when enabling: don't disable. */ + + /* original values */ + u8 orig_bkpt; /* single byte breakpoint */ + pte_t orig_pte; + + struct list_head watchpt_read_list; /* read watchpoints on this page */ + struct list_head watchpt_write_list; /* write */ + struct list_head watchpt_access_list; /* access */ + struct list_head watchpt_disabled_list; /* disabled */ + + struct bwcpoint *parent; /* watchpoint: bwc_watch (the page) */ + struct bwcpoint *watchpoint; /* bwc_watch_step: original watchpoint */ } bwcpoint_t, *bwcpoint_p; -static bwcpoint_t bwcpoint_list; +static struct list_head bwcpoint_list = LIST_HEAD_INIT(bwcpoint_list); + +#define _pdb_bwcpoint_alloc(_var) \ +{ \ + if ( (_var = kmalloc(sizeof(bwcpoint_t), GFP_KERNEL)) == NULL ) \ + printk("error: unable to allocate memory %d\n", __LINE__); \ + else { \ + memset(_var, 0, sizeof(bwcpoint_t)); \ + INIT_LIST_HEAD(&_var->watchpt_read_list); \ + INIT_LIST_HEAD(&_var->watchpt_write_list); \ + INIT_LIST_HEAD(&_var->watchpt_access_list); \ + INIT_LIST_HEAD(&_var->watchpt_disabled_list); \ + } \ +} + +/***********************************************************************/ + +static void _pdb_bwc_print_list (struct list_head *, char *, int); + +static void +_pdb_bwc_print (bwcpoint_p bwc, char *label, int level) +{ + printk("%s%03d 0x%08lx:0x%02x %c\n", label, bwc->type, + bwc->address, bwc->length, bwc->error ? 'e' : '-'); + + if ( !list_empty(&bwc->watchpt_read_list) ) + _pdb_bwc_print_list(&bwc->watchpt_read_list, "r", level); + if ( !list_empty(&bwc->watchpt_write_list) ) + _pdb_bwc_print_list(&bwc->watchpt_write_list, "w", level); + if ( !list_empty(&bwc->watchpt_access_list) ) + _pdb_bwc_print_list(&bwc->watchpt_access_list, "a", level); + if ( !list_empty(&bwc->watchpt_disabled_list) ) + _pdb_bwc_print_list(&bwc->watchpt_disabled_list, "d", level); +} + +static void +_pdb_bwc_print_list (struct list_head *bwc_list, char *label, int level) +{ + struct list_head *ptr; + int counter = 0; + + list_for_each(ptr, bwc_list) + { + bwcpoint_p bwc = list_entry(ptr, bwcpoint_t, list); + printk(" %s[%02d]%s ", level > 0 ? " " : "", counter++, + level > 0 ? "" : " "); + _pdb_bwc_print(bwc, label, level+1); + } + + if (counter == 0) + { + printk(" empty list\n"); + } +} void -pdb_initialize_bwcpoint (void) +pdb_bwc_print_list (void) +{ + _pdb_bwc_print_list(&bwcpoint_list, " ", 0); +} + +bwcpoint_p +pdb_search_watchpoint (u32 process, memory_t address) { - memset((void *) &bwcpoint_list, 0, sizeof(bwcpoint_t)); - INIT_LIST_HEAD(&bwcpoint_list.list); + bwcpoint_p bwc_watch = (bwcpoint_p) 0; + bwcpoint_p bwc_entry = (bwcpoint_p) 0; + struct list_head *ptr; - return; + list_for_each(ptr, &bwcpoint_list) /* find bwc page entry */ + { + bwc_watch = list_entry(ptr, bwcpoint_t, list); + if (bwc_watch->address == (address & PAGE_MASK)) break; + } + + if ( !bwc_watch ) + { + return (bwcpoint_p) 0; + } + +#define __pdb_search_watchpoint_list(__list) \ + list_for_each(ptr, (__list)) \ + { \ + bwc_entry = list_entry(ptr, bwcpoint_t, list); \ + if ( bwc_entry->process == process && \ + bwc_entry->address <= address && \ + bwc_entry->address + bwc_entry->length > address ) \ + return bwc_entry; \ + } + + __pdb_search_watchpoint_list(&bwc_watch->watchpt_read_list); + __pdb_search_watchpoint_list(&bwc_watch->watchpt_write_list); + __pdb_search_watchpoint_list(&bwc_watch->watchpt_access_list); + +#undef __pdb_search_watchpoint_list + + return (bwcpoint_p) 0; } +/*************************************************************/ int pdb_suspend (struct task_struct *target) @@ -136,6 +246,35 @@ _pdb_set_register (struct task_struct *target, int reg, unsigned long val) return; } +int +pdb_read_register (struct task_struct *target, pdb_op_rd_reg_p op) +{ + int rc = 0; + + switch (op->reg) + { + case 0: op->value = _pdb_get_register(target, LINUX_EAX); break; + case 1: op->value = _pdb_get_register(target, LINUX_ECX); break; + case 2: op->value = _pdb_get_register(target, LINUX_EDX); break; + case 3: op->value = _pdb_get_register(target, LINUX_EBX); break; + case 4: op->value = _pdb_get_register(target, LINUX_ESP); break; + case 5: op->value = _pdb_get_register(target, LINUX_EBP); break; + case 6: op->value = _pdb_get_register(target, LINUX_ESI); break; + case 7: op->value = _pdb_get_register(target, LINUX_EDI); break; + case 8: op->value = _pdb_get_register(target, LINUX_EIP); break; + case 9: op->value = _pdb_get_register(target, LINUX_EFL); break; + + case 10: op->value = _pdb_get_register(target, LINUX_CS); break; + case 11: op->value = _pdb_get_register(target, LINUX_SS); break; + case 12: op->value = _pdb_get_register(target, LINUX_DS); break; + case 13: op->value = _pdb_get_register(target, LINUX_ES); break; + case 14: op->value = _pdb_get_register(target, LINUX_FS); break; + case 15: op->value = _pdb_get_register(target, LINUX_GS); break; + } + + return rc; +} + int pdb_read_registers (struct task_struct *target, pdb_op_rd_regs_p op) { @@ -209,18 +348,14 @@ pdb_step (struct task_struct *target) eflags |= X86_EFLAGS_TF; _pdb_set_register(target, LINUX_EFL, eflags); - bkpt = kmalloc(sizeof(bwcpoint_t), GFP_KERNEL); - if ( bkpt == NULL ) - { - printk("error: unable to allocation memory\n"); - return -1; - } + _pdb_bwcpoint_alloc(bkpt); + if ( bkpt == NULL ) return -1; bkpt->process = target->pid; bkpt->address = 0; bkpt->type = BWC_DEBUG; - list_add(&bkpt->list, &bwcpoint_list.list); + list_add_tail(&bkpt->list, &bwcpoint_list); wake_up_process(target); @@ -237,31 +372,27 @@ pdb_insert_memory_breakpoint (struct task_struct *target, printk("insert breakpoint %d:%lx len: %d\n", target->pid, address, length); - bkpt = kmalloc(sizeof(bwcpoint_t), GFP_KERNEL); - if ( bkpt == NULL ) - { - printk("error: unable to allocation memory\n"); - return -1; - } - if ( length != 1 ) { printk("error: breakpoint length should be 1\n"); - kfree(bkpt); return -1; } + _pdb_bwcpoint_alloc(bkpt); + if ( bkpt == NULL ) return -1; + bkpt->process = target->pid; bkpt->address = address; bkpt->type = BWC_INT3; - pdb_access_memory(target, address, &bkpt->old_value, 1, 0); - pdb_access_memory(target, address, &breakpoint_opcode, 1, 1); + pdb_access_memory(target, address, &bkpt->orig_bkpt, 1, PDB_MEM_READ); + pdb_access_memory(target, address, &breakpoint_opcode, 1, PDB_MEM_WRITE); - list_add(&bkpt->list, &bwcpoint_list.list); + list_add_tail(&bkpt->list, &bwcpoint_list); printk("breakpoint_set %d:%lx OLD: 0x%x\n", - target->pid, address, bkpt->old_value); + target->pid, address, bkpt->orig_bkpt); + pdb_bwc_print_list(); return rc; } @@ -276,7 +407,7 @@ pdb_remove_memory_breakpoint (struct task_struct *target, printk ("remove breakpoint %d:%lx\n", target->pid, address); struct list_head *entry; - list_for_each(entry, &bwcpoint_list.list) + list_for_each(entry, &bwcpoint_list) { bkpt = list_entry(entry, bwcpoint_t, list); if ( target->pid == bkpt->process && @@ -285,17 +416,223 @@ pdb_remove_memory_breakpoint (struct task_struct *target, break; } - if (bkpt == &bwcpoint_list || bkpt == NULL) + if (entry == &bwcpoint_list) { printk ("error: no breakpoint found\n"); return -1; } + pdb_access_memory(target, address, &bkpt->orig_bkpt, 1, PDB_MEM_WRITE); + list_del(&bkpt->list); + kfree(bkpt); - pdb_access_memory(target, address, &bkpt->old_value, 1, 1); + pdb_bwc_print_list(); - kfree(bkpt); + return rc; +} + +#define PDB_PTE_UPDATE 1 +#define PDB_PTE_RESTORE 2 + +int +pdb_change_pte (struct task_struct *target, bwcpoint_p bwc, int mode) +{ + int rc = 0; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ptep; + + pgd = pgd_offset(target->mm, bwc->address); + if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) return -1; + + pud = pud_offset(pgd, bwc->address); + if (pud_none(*pud) || unlikely(pud_bad(*pud))) return -2; + + pmd = pmd_offset(pud, bwc->address); + if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) return -3; + + ptep = pte_offset_map(pmd, bwc->address); + if (!ptep) return -4; + + switch ( mode ) + { + case PDB_PTE_UPDATE: /* added or removed a watchpoint. update pte. */ + { + pte_t new_pte; + + if ( pte_val(bwc->parent->orig_pte) == 0 ) /* new watchpoint page */ + { + bwc->parent->orig_pte = *ptep; + } + + new_pte = bwc->parent->orig_pte; + + if ( !list_empty(&bwc->parent->watchpt_read_list) || + !list_empty(&bwc->parent->watchpt_access_list) ) + { + new_pte = pte_rdprotect(new_pte); + } + + if ( !list_empty(&bwc->parent->watchpt_write_list) || + !list_empty(&bwc->parent->watchpt_access_list) ) + { + new_pte = pte_wrprotect(new_pte); + } + + if ( pte_val(new_pte) != pte_val(*ptep) ) + { + *ptep = new_pte; + flush_tlb_mm(target->mm); + } + break; + } + case PDB_PTE_RESTORE : /* suspend watchpoint by restoring original pte */ + { + *ptep = bwc->parent->orig_pte; + flush_tlb_mm(target->mm); + break; + } + default : + { + printk("(linux) unknown mode %d %d\n", mode, __LINE__); + break; + } + } + + pte_unmap(ptep); /* can i flush the tlb before pte_unmap? */ + + return rc; +} + +int +pdb_insert_watchpoint (struct task_struct *target, pdb_op_watchpt_p watchpt) +{ + int rc = 0; + + bwcpoint_p bwc_watch; + bwcpoint_p bwc_entry; + struct list_head *ptr; + unsigned long page = watchpt->address & PAGE_MASK; + struct list_head *watchpoint_list; + + printk("insert watchpoint: %d %x %x\n", + watchpt->type, watchpt->address, watchpt->length); + + list_for_each(ptr, &bwcpoint_list) /* find existing bwc page entry */ + { + bwc_watch = list_entry(ptr, bwcpoint_t, list); + + if (bwc_watch->address == page) goto got_bwc_watch; + } + + _pdb_bwcpoint_alloc(bwc_watch); /* create new bwc:watch */ + if ( bwc_watch == NULL ) return -1; + + bwc_watch->type = BWC_WATCH; + bwc_watch->process = target->pid; + bwc_watch->address = page; + + list_add_tail(&bwc_watch->list, &bwcpoint_list); + + got_bwc_watch: + + switch (watchpt->type) + { + case BWC_WATCH_READ: + watchpoint_list = &bwc_watch->watchpt_read_list; break; + case BWC_WATCH_WRITE: + watchpoint_list = &bwc_watch->watchpt_write_list; break; + case BWC_WATCH_ACCESS: + watchpoint_list = &bwc_watch->watchpt_access_list; break; + default: + printk("unknown type %d\n", watchpt->type); return -2; + } + + _pdb_bwcpoint_alloc(bwc_entry); /* create new bwc:entry */ + if ( bwc_entry == NULL ) return -1; + + bwc_entry->process = target->pid; + bwc_entry->address = watchpt->address; + bwc_entry->length = watchpt->length; + bwc_entry->type = watchpt->type; + bwc_entry->parent = bwc_watch; + + list_add_tail(&bwc_entry->list, watchpoint_list); + pdb_change_pte(target, bwc_entry, PDB_PTE_UPDATE); + + pdb_bwc_print_list(); + + return rc; +} + +int +pdb_remove_watchpoint (struct task_struct *target, pdb_op_watchpt_p watchpt) +{ + int rc = 0; + bwcpoint_p bwc_watch = (bwcpoint_p) NULL; + bwcpoint_p bwc_entry = (bwcpoint_p) NULL; + unsigned long page = watchpt->address & PAGE_MASK; + struct list_head *ptr; + struct list_head *watchpoint_list; + + printk("remove watchpoint: %d %x %x\n", + watchpt->type, watchpt->address, watchpt->length); + + list_for_each(ptr, &bwcpoint_list) /* find bwc page entry */ + { + bwc_watch = list_entry(ptr, bwcpoint_t, list); + if (bwc_watch->address == page) break; + } + + if ( !bwc_watch ) + { + printk("(linux) delete watchpoint: can't find bwc page 0x%08x\n", + watchpt->address); + return -1; + } + + switch (watchpt->type) + { + case BWC_WATCH_READ: + watchpoint_list = &bwc_watch->watchpt_read_list; break; + case BWC_WATCH_WRITE: + watchpoint_list = &bwc_watch->watchpt_write_list; break; + case BWC_WATCH_ACCESS: + watchpoint_list = &bwc_watch->watchpt_access_list; break; + default: + printk("unknown type %d\n", watchpt->type); return -2; + } + + list_for_each(ptr, watchpoint_list) /* find watchpoint */ + { + bwc_entry = list_entry(ptr, bwcpoint_t, list); + if ( bwc_entry->address == watchpt->address && + bwc_entry->length == watchpt->length ) break; + } + + if ( !bwc_entry ) /* or ptr == watchpoint_list */ + { + printk("(linux) delete watchpoint: can't find watchpoint 0x%08x\n", + watchpt->address); + return -1; + } + + list_del(&bwc_entry->list); + pdb_change_pte(target, bwc_entry, PDB_PTE_UPDATE); + kfree(bwc_entry); + + + if ( list_empty(&bwc_watch->watchpt_read_list) && + list_empty(&bwc_watch->watchpt_write_list) && + list_empty(&bwc_watch->watchpt_access_list) ) + { + list_del(&bwc_watch->list); + kfree(bwc_watch); + } + + pdb_bwc_print_list(); return rc; } @@ -312,16 +649,24 @@ pdb_exceptions_notify (struct notifier_block *self, unsigned long val, switch (val) { case DIE_DEBUG: - if (pdb_debug_fn(args->regs, args->trapnr, args->err)) + if ( pdb_debug_fn(args->regs, args->trapnr, args->err) ) return NOTIFY_STOP; break; case DIE_TRAP: - if (args->trapnr == 3 && pdb_int3_fn(args->regs, args->err)) + if ( args->trapnr == 3 && pdb_int3_fn(args->regs, args->err) ) return NOTIFY_STOP; break; case DIE_INT3: /* without kprobes, we should never see DIE_INT3 */ - case DIE_GPF: + if ( pdb_int3_fn(args->regs, args->err) ) + return NOTIFY_STOP; + break; case DIE_PAGE_FAULT: + if ( pdb_page_fault_fn(args->regs, args->trapnr, args->err) ) + return NOTIFY_STOP; + break; + case DIE_GPF: + printk("---------------GPF\n"); + break; default: break; } @@ -330,70 +675,110 @@ pdb_exceptions_notify (struct notifier_block *self, unsigned long val, } -int +static int pdb_debug_fn (struct pt_regs *regs, long error_code, unsigned int condition) { pdb_response_t resp; bwcpoint_p bkpt = NULL; - struct list_head *entry; - list_for_each(entry, &bwcpoint_list.list) + + printk("pdb_debug_fn\n"); + + list_for_each(entry, &bwcpoint_list) { bkpt = list_entry(entry, bwcpoint_t, list); if ( current->pid == bkpt->process && - bkpt->type == BWC_DEBUG ) + (bkpt->type == BWC_DEBUG || /* single step */ + bkpt->type == BWC_WATCH_STEP)) /* single step over watchpoint */ break; } - if (bkpt == &bwcpoint_list || bkpt == NULL) + if (entry == &bwcpoint_list) { printk("not my debug 0x%x 0x%lx\n", current->pid, regs->eip); return 0; } - list_del(&bkpt->list); - pdb_suspend(current); - printk("(pdb) debug pid: %d, eip: 0x%08lx\n", current->pid, regs->eip); + printk("(pdb) %s pid: %d, eip: 0x%08lx\n", + bkpt->type == BWC_DEBUG ? "debug" : "watch-step", + current->pid, regs->eip); regs->eflags &= ~X86_EFLAGS_TF; set_tsk_thread_flag(current, TIF_SINGLESTEP); - resp.operation = PDB_OPCODE_STEP; + switch (bkpt->type) + { + case BWC_DEBUG: + resp.operation = PDB_OPCODE_STEP; + break; + case BWC_WATCH_STEP: + { + struct list_head *watchpoint_list; + bwcpoint_p watch_page = bkpt->watchpoint->parent; + + switch (bkpt->watchpoint->type) + { + case BWC_WATCH_READ: + watchpoint_list = &watch_page->watchpt_read_list; break; + case BWC_WATCH_WRITE: + watchpoint_list = &watch_page->watchpt_write_list; break; + case BWC_WATCH_ACCESS: + watchpoint_list = &watch_page->watchpt_access_list; break; + default: + printk("unknown type %d\n", bkpt->watchpoint->type); return 0; + } + + resp.operation = PDB_OPCODE_WATCHPOINT; + list_del_init(&bkpt->watchpoint->list); + list_add_tail(&bkpt->watchpoint->list, watchpoint_list); + pdb_change_pte(current, bkpt->watchpoint, PDB_PTE_UPDATE); + pdb_bwc_print_list(); + break; + } + default: + printk("unknown breakpoint type %d %d\n", __LINE__, bkpt->type); + return 0; + } + resp.process = current->pid; resp.status = PDB_RESPONSE_OKAY; pdb_send_response(&resp); + list_del(&bkpt->list); + kfree(bkpt); + return 1; } -int +static int pdb_int3_fn (struct pt_regs *regs, long error_code) { pdb_response_t resp; bwcpoint_p bkpt = NULL; + memory_t address = regs->eip - 1; struct list_head *entry; - list_for_each(entry, &bwcpoint_list.list) + list_for_each(entry, &bwcpoint_list) { bkpt = list_entry(entry, bwcpoint_t, list); if ( current->pid == bkpt->process && - regs->eip == bkpt->address && + address == bkpt->address && bkpt->type == BWC_INT3 ) break; } - if (bkpt == &bwcpoint_list || bkpt == NULL) + if (entry == &bwcpoint_list) { - printk("not my int3 bkpt 0x%x 0x%lx\n", current->pid, regs->eip); + printk("not my int3 bkpt 0x%x 0x%lx\n", current->pid, address); return 0; } - printk("(pdb) int3 pid: %d, eip: 0x%08lx\n", current->pid, regs->eip); + printk("(pdb) int3 pid: %d, eip: 0x%08lx\n", current->pid, address); pdb_suspend(current); @@ -406,6 +791,54 @@ pdb_int3_fn (struct pt_regs *regs, long error_code) return 1; } +static int +pdb_page_fault_fn (struct pt_regs *regs, long error_code, + unsigned int condition) +{ + unsigned long cr2; + unsigned long cr3; + bwcpoint_p bwc; + bwcpoint_p watchpt; + bwcpoint_p bkpt; + + __asm__ __volatile__ ("movl %%cr3,%0" : "=r" (cr3) : ); + __asm__ __volatile__ ("movl %%cr2,%0" : "=r" (cr2) : ); + + bwc = pdb_search_watchpoint(current->pid, cr2); + if ( !bwc ) + { + return 0; /* not mine */ + } + + printk("page_fault cr2:%08lx err:%lx eip:%08lx\n", + cr2, error_code, regs->eip); + + /* disable the watchpoint */ + watchpt = bwc->watchpoint; + list_del_init(&bwc->list); + list_add_tail(&bwc->list, &bwc->parent->watchpt_disabled_list); + pdb_change_pte(current, bwc, PDB_PTE_RESTORE); + + /* single step the faulting instruction */ + regs->eflags |= X86_EFLAGS_TF; + + /* create a bwcpoint entry so we know what to do once we regain control */ + _pdb_bwcpoint_alloc(bkpt); + if ( bkpt == NULL ) return -1; + + bkpt->process = current->pid; + bkpt->address = 0; + bkpt->type = BWC_WATCH_STEP; + bkpt->watchpoint = bwc; + + /* add to head so we see it first the next time we break */ + list_add(&bkpt->list, &bwcpoint_list); + + pdb_bwc_print_list(); + return 1; +} + + /* * Local variables: * mode: C diff --git a/tools/debugger/pdb/linux-2.6-module/module.c b/tools/debugger/pdb/linux-2.6-module/module.c index c75b9cd647..b8093f9d3f 100644 --- a/tools/debugger/pdb/linux-2.6-module/module.c +++ b/tools/debugger/pdb/linux-2.6-module/module.c @@ -98,6 +98,11 @@ pdb_process_request (pdb_request_t *request) printk("(linux) detach 0x%x\n", request->process); resp.status = PDB_RESPONSE_OKAY; break; + case PDB_OPCODE_RD_REG : + resp.u.rd_reg.reg = request->u.rd_reg.reg; + pdb_read_register(target, &resp.u.rd_reg); + resp.status = PDB_RESPONSE_OKAY; + break; case PDB_OPCODE_RD_REGS : pdb_read_registers(target, &resp.u.rd_regs); resp.status = PDB_RESPONSE_OKAY; @@ -108,14 +113,16 @@ pdb_process_request (pdb_request_t *request) break; case PDB_OPCODE_RD_MEM : pdb_access_memory(target, request->u.rd_mem.address, - &resp.u.rd_mem.data, request->u.rd_mem.length, 0); + &resp.u.rd_mem.data, request->u.rd_mem.length, + PDB_MEM_READ); resp.u.rd_mem.address = request->u.rd_mem.address; resp.u.rd_mem.length = request->u.rd_mem.length; resp.status = PDB_RESPONSE_OKAY; break; case PDB_OPCODE_WR_MEM : pdb_access_memory(target, request->u.wr_mem.address, - &request->u.wr_mem.data, request->u.wr_mem.length, 1); + &request->u.wr_mem.data, request->u.wr_mem.length, + PDB_MEM_WRITE); resp.status = PDB_RESPONSE_OKAY; break; case PDB_OPCODE_CONTINUE : @@ -137,6 +144,14 @@ pdb_process_request (pdb_request_t *request) request->u.bkpt.length); resp.status = PDB_RESPONSE_OKAY; break; + case PDB_OPCODE_SET_WATCHPT : + pdb_insert_watchpoint(target, &request->u.watchpt); + resp.status = PDB_RESPONSE_OKAY; + break; + case PDB_OPCODE_CLR_WATCHPT : + pdb_remove_watchpoint(target, &request->u.watchpt); + resp.status = PDB_RESPONSE_OKAY; + break; default: printk("(pdb) unknown request operation %d\n", request->operation); resp.status = PDB_RESPONSE_ERROR; @@ -249,8 +264,6 @@ pdb_initialize (void) printk("----\npdb initialize %s %s\n", __DATE__, __TIME__); - pdb_initialize_bwcpoint(); - /* if ( xen_start_info.flags & SIF_INITDOMAIN ) return 1; diff --git a/tools/debugger/pdb/linux-2.6-module/pdb_debug.h b/tools/debugger/pdb/linux-2.6-module/pdb_debug.h index dfcbc4375f..2d30eae625 100644 --- a/tools/debugger/pdb/linux-2.6-module/pdb_debug.h +++ b/tools/debugger/pdb/linux-2.6-module/pdb_debug.h @@ -6,6 +6,7 @@ void pdb_initialize_bwcpoint (void); int pdb_suspend (struct task_struct *target); int pdb_resume (struct task_struct *target); +int pdb_read_register (struct task_struct *target, pdb_op_rd_reg_p op); int pdb_read_registers (struct task_struct *target, pdb_op_rd_regs_p op); int pdb_write_register (struct task_struct *target, pdb_op_wr_reg_p op); int pdb_read_memory (struct task_struct *target, pdb_op_rd_mem_req_p req, @@ -20,14 +21,14 @@ int pdb_insert_memory_breakpoint (struct task_struct *target, memory_t address, u32 length); int pdb_remove_memory_breakpoint (struct task_struct *target, memory_t address, u32 length); +int pdb_insert_watchpoint (struct task_struct *target, + pdb_op_watchpt_p watchpt); +int pdb_remove_watchpoint (struct task_struct *target, + pdb_op_watchpt_p watchpt); int pdb_exceptions_notify (struct notifier_block *self, unsigned long val, void *data); -int pdb_debug_fn (struct pt_regs *regs, long error_code, - unsigned int condition); -int pdb_int3_fn (struct pt_regs *regs, long error_code); - /* module.c */ void pdb_send_response (pdb_response_t *response); diff --git a/tools/debugger/pdb/linux-2.6-module/pdb_module.h b/tools/debugger/pdb/linux-2.6-module/pdb_module.h index fc95bdb47a..a7e679ecdf 100644 --- a/tools/debugger/pdb/linux-2.6-module/pdb_module.h +++ b/tools/debugger/pdb/linux-2.6-module/pdb_module.h @@ -14,20 +14,27 @@ typedef struct pdb_op_attach #define PDB_OPCODE_DETACH 3 -#define PDB_OPCODE_RD_REGS 4 +#define PDB_OPCODE_RD_REG 4 +typedef struct pdb_op_rd_reg +{ + u32 reg; + u32 value; +} pdb_op_rd_reg_t, *pdb_op_rd_reg_p; + +#define PDB_OPCODE_RD_REGS 5 typedef struct pdb_op_rd_regs { u32 reg[GDB_REGISTER_FRAME_SIZE]; } pdb_op_rd_regs_t, *pdb_op_rd_regs_p; -#define PDB_OPCODE_WR_REG 5 +#define PDB_OPCODE_WR_REG 6 typedef struct pdb_op_wr_reg { u32 reg; u32 value; } pdb_op_wr_reg_t, *pdb_op_wr_reg_p; -#define PDB_OPCODE_RD_MEM 6 +#define PDB_OPCODE_RD_MEM 7 typedef struct pdb_op_rd_mem_req { u32 address; @@ -41,7 +48,7 @@ typedef struct pdb_op_rd_mem_resp u8 data[1024]; } pdb_op_rd_mem_resp_t, *pdb_op_rd_mem_resp_p; -#define PDB_OPCODE_WR_MEM 7 +#define PDB_OPCODE_WR_MEM 8 typedef struct pdb_op_wr_mem { u32 address; @@ -49,17 +56,34 @@ typedef struct pdb_op_wr_mem u8 data[1024]; /* arbitrary */ } pdb_op_wr_mem_t, *pdb_op_wr_mem_p; -#define PDB_OPCODE_CONTINUE 8 -#define PDB_OPCODE_STEP 9 +#define PDB_OPCODE_CONTINUE 9 +#define PDB_OPCODE_STEP 10 -#define PDB_OPCODE_SET_BKPT 10 -#define PDB_OPCODE_CLR_BKPT 11 +#define PDB_OPCODE_SET_BKPT 11 +#define PDB_OPCODE_CLR_BKPT 12 typedef struct pdb_op_bkpt { u32 address; u32 length; } pdb_op_bkpt_t, *pdb_op_bkpt_p; +#define PDB_OPCODE_SET_WATCHPT 13 +#define PDB_OPCODE_CLR_WATCHPT 14 +#define PDB_OPCODE_WATCHPOINT 15 +typedef struct pdb_op_watchpt +{ +#define BWC_DEBUG 1 +#define BWC_INT3 3 +#define BWC_WATCH 100 /* pdb: watchpoint page */ +#define BWC_WATCH_STEP 101 /* pdb: watchpoint single step */ +#define BWC_WATCH_WRITE 102 +#define BWC_WATCH_READ 103 +#define BWC_WATCH_ACCESS 104 + u32 type; + u32 address; + u32 length; +} pdb_op_watchpt_t, *pdb_op_watchpt_p; + typedef struct { @@ -68,10 +92,12 @@ typedef struct union { pdb_op_attach_t attach; + pdb_op_rd_reg_t rd_reg; pdb_op_wr_reg_t wr_reg; pdb_op_rd_mem_req_t rd_mem; pdb_op_wr_mem_t wr_mem; pdb_op_bkpt_t bkpt; + pdb_op_watchpt_t watchpt; } u; } pdb_request_t, *pdb_request_p; @@ -87,6 +113,7 @@ typedef struct { s16 status; /* PDB_RESPONSE_??? */ union { + pdb_op_rd_reg_t rd_reg; pdb_op_rd_regs_t rd_regs; pdb_op_rd_mem_resp_t rd_mem; } u; @@ -95,6 +122,11 @@ typedef struct { DEFINE_RING_TYPES(pdb, pdb_request_t, pdb_response_t); + +/* from access_process_vm */ +#define PDB_MEM_READ 0 +#define PDB_MEM_WRITE 1 + #endif diff --git a/tools/debugger/pdb/linux-2.6-patches/i386_ksyms.patch b/tools/debugger/pdb/linux-2.6-patches/i386_ksyms.patch index 031d41b917..0efd8db532 100644 --- a/tools/debugger/pdb/linux-2.6-patches/i386_ksyms.patch +++ b/tools/debugger/pdb/linux-2.6-patches/i386_ksyms.patch @@ -1,7 +1,15 @@ diff -u linux-2.6.12/arch/xen/i386/kernel/i386_ksyms.c linux-2.6.12-pdb/arch/xen/i386/kernel/i386_ksyms.c --- linux-2.6.12/arch/xen/i386/kernel/i386_ksyms.c 2005-07-31 22:36:50.000000000 +0100 +++ linux-2.6.12-pdb/arch/xen/i386/kernel/i386_ksyms.c 2005-08-01 10:57:31.000000000 +0100 -@@ -172,6 +172,7 @@ +@@ -151,6 +151,7 @@ + /* TLB flushing */ + EXPORT_SYMBOL(flush_tlb_page); + #endif ++EXPORT_SYMBOL(flush_tlb_mm); + + #ifdef CONFIG_X86_IO_APIC + EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector); +@@ -172,6 +173,7 @@ EXPORT_SYMBOL_GPL(unset_nmi_callback); EXPORT_SYMBOL(register_die_notifier); diff --git a/tools/debugger/pdb/pdb_caml_domain.c b/tools/debugger/pdb/pdb_caml_domain.c index 7621eb45f6..934524ba0c 100644 --- a/tools/debugger/pdb/pdb_caml_domain.c +++ b/tools/debugger/pdb/pdb_caml_domain.c @@ -42,6 +42,54 @@ typedef struct /****************************************************************************/ +/* + * dom_read_register : context_t -> int -> int32 + */ +value +dom_read_register (value context, value reg) +{ + CAMLparam2(context, reg); + CAMLlocal1(result); + + int my_reg = Int_val(reg); + cpu_user_regs_t *regs; + context_t ctx; + + decode_context(&ctx, context); + + if ( xendebug_read_registers(xc_handle, ctx.domain, ctx.vcpu, ®s) ) + { + printf("(pdb) read registers error!\n"); fflush(stdout); + failwith("read registers error"); + } + + dump_regs(regs); + + result = caml_alloc_tuple(16); + + switch (my_reg) + { + case GDB_EAX: result = caml_copy_int32(regs->eax); break; + case GDB_ECX: result = caml_copy_int32(regs->ecx); break; + case GDB_EDX: result = caml_copy_int32(regs->edx); break; + case GDB_EBX: result = caml_copy_int32(regs->ebx); break; + case GDB_ESP: result = caml_copy_int32(regs->esp); break; + case GDB_EBP: result = caml_copy_int32(regs->ebp); break; + case GDB_ESI: result = caml_copy_int32(regs->esi); break; + case GDB_EDI: result = caml_copy_int32(regs->edi); break; + case GDB_EIP: result = caml_copy_int32(regs->eip); break; + case GDB_EFL: result = caml_copy_int32(regs->eflags); break; + case GDB_CS: result = caml_copy_int32(regs->cs); break; + case GDB_SS: result = caml_copy_int32(regs->ss); break; + case GDB_DS: result = caml_copy_int32(regs->ds); break; + case GDB_ES: result = caml_copy_int32(regs->es); break; + case GDB_FS: result = caml_copy_int32(regs->fs); break; + case GDB_GS: result = caml_copy_int32(regs->gs); break; + } + + CAMLreturn(result); +} + /* * dom_read_registers : context_t -> int32 */ diff --git a/tools/debugger/pdb/pdb_caml_process.c b/tools/debugger/pdb/pdb_caml_process.c index 0f911fa754..760a182732 100644 --- a/tools/debugger/pdb/pdb_caml_process.c +++ b/tools/debugger/pdb/pdb_caml_process.c @@ -113,6 +113,12 @@ process_handle_response (value ring) case PDB_OPCODE_DETACH : break; + case PDB_OPCODE_RD_REG : + { + sprintf(&msg[0], "%08x", _flip(resp->u.rd_reg.value)); + break; + } + case PDB_OPCODE_RD_REGS : { int loop; @@ -161,16 +167,22 @@ process_handle_response (value ring) } case PDB_OPCODE_SET_BKPT : + case PDB_OPCODE_CLR_BKPT : + case PDB_OPCODE_SET_WATCHPT : + case PDB_OPCODE_CLR_WATCHPT : { break; } - case PDB_OPCODE_CLR_BKPT : + + case PDB_OPCODE_WATCHPOINT : { + sprintf(msg, "S05"); break; } default : - printf("(linux) UNKNOWN MESSAGE TYPE IN RESPONSE\n"); + printf("(linux) UNKNOWN MESSAGE TYPE IN RESPONSE %d\n", + resp->operation); break; } @@ -260,6 +272,32 @@ proc_pause_target (value context) } +/* + * proc_read_register : context_t -> int -> unit + */ +value +proc_read_register (value context, value reg) +{ + CAMLparam1(context); + + pdb_request_t req; + context_t ctx; + int my_reg = Int_val(reg); + + decode_context(&ctx, context); + + req.operation = PDB_OPCODE_RD_REG; + req.process = ctx.process; + req.u.rd_reg.reg = my_reg; + req.u.rd_reg.value = 0; + + send_request (ctx.ring, ctx.evtchn, &req); + + CAMLreturn(Val_unit); +} + + + /* * proc_read_registers : context_t -> unit */ @@ -443,7 +481,7 @@ proc_step_target (value context) /* - * proc_insert_memory_breakpoint : context_t -> int32 -> int list -> unit + * proc_insert_memory_breakpoint : context_t -> int32 -> int -> unit */ value proc_insert_memory_breakpoint (value context, value address, value length) @@ -466,7 +504,7 @@ proc_insert_memory_breakpoint (value context, value address, value length) } /* - * proc_remove_memory_breakpoint : context_t -> int32 -> int list -> unit + * proc_remove_memory_breakpoint : context_t -> int32 -> int -> unit */ value proc_remove_memory_breakpoint (value context, value address, value length) @@ -488,6 +526,54 @@ proc_remove_memory_breakpoint (value context, value address, value length) CAMLreturn(Val_unit); } +/* + * proc_insert_watchpoint : context_t -> bwcpoint_t -> int32 -> int -> unit + */ +value +proc_insert_watchpoint (value context, value kind, value address, value length) +{ + CAMLparam3(context, address, length); + + context_t ctx; + pdb_request_t req; + + decode_context(&ctx, context); + + req.operation = PDB_OPCODE_SET_WATCHPT; + req.process = ctx.process; + req.u.watchpt.type = Int_val(kind); + req.u.watchpt.address = (memory_t) Int32_val(address); + req.u.watchpt.length = Int_val(length); + + send_request(ctx.ring, ctx.evtchn, &req); + + CAMLreturn(Val_unit); +} + +/* + * proc_remove_watchpoint : context_t -> bwcpoint_t -> int32 -> int -> unit + */ +value +proc_remove_watchpoint (value context, value kind, value address, value length) +{ + CAMLparam3(context, address, length); + + context_t ctx; + pdb_request_t req; + + decode_context(&ctx, context); + + req.operation = PDB_OPCODE_CLR_WATCHPT; + req.process = ctx.process; + req.u.watchpt.type = Int_val(kind); + req.u.watchpt.address = (memory_t) Int32_val(address); + req.u.watchpt.length = Int_val(length); + + send_request(ctx.ring, ctx.evtchn, &req); + + CAMLreturn(Val_unit); +} + /* * Local variables: diff --git a/tools/debugger/pdb/readme b/tools/debugger/pdb/readme index 9ed26be4a6..6ea537b404 100644 --- a/tools/debugger/pdb/readme +++ b/tools/debugger/pdb/readme @@ -1,9 +1,9 @@ -PDB 0.3 +PDB 0.3.3 http://www.cl.cam.ac.uk/netos/pdb Alex Ho -June 2005 +August 2005 This is the latest incarnation of the pervasive debugger. @@ -79,6 +79,11 @@ Usage Process PDB can also debug a process running in a Linux 2.6 domain. + You will need to patch the Linux 2.6 domain U tree to export some + additional symbols for the pdb module + + % make -C linux-2.6-patches + After running PDB in domain 0, insert the pdb module in dom u: % insmod linux-2.6-module/pdb.ko @@ -87,7 +92,14 @@ Process (gdb) maint packet x context = process + Read, write, and access watchpoint should also work for processes, + use the "rwatch", "watch" and "awatch" gdb commands respectively. + + If you are having trouble with GDB 5.3 (i386-redhat-linux-gnu), + try GDB 6.3 (configured with --target=i386-linux-gnu). + + To Do -- watchpoints +- watchpoints for domains - support for SMP -- cgit v1.2.3