kdb
[Top] [All Lists]

[PATCH-updated] access user space addresses/switch process context

To: Keith Owens <kaos@xxxxxxx>
Subject: [PATCH-updated] access user space addresses/switch process context
From: "Vamsi Krishna S ." <vamsi@xxxxxxxxxx>
Date: Wed, 16 Apr 2003 20:54:13 +0530
Cc: kdb <kdb@xxxxxxxxxxx>
Reply-to: vamsi@xxxxxxxxxx
Sender: kdb-bounce@xxxxxxxxxxx
User-agent: Mutt/1.2.5i
Hi,

Here is the updated patch. Changes from the previous version:
- implement kdb_follow_page in mm/memory.c itself to be able to
  use follow_page() directly.
- support for writing into ("mm") user space addresses
- export kdb_get[put]userarea_size so that kdb modules will
  be able to use kdb_get[put]word.
- implement this for ia64 along with i386.

Please apply.

Thanks,
Vamsi.
-- 
Vamsi Krishna S.
Linux Technology Center,
IBM Software Lab, Bangalore.
Ph: +91 80 5044959
Internet: vamsi@xxxxxxxxxx

-- description from previous mail --

Here is a patch to implement a command to switch the current
user process context whilst inside KDB to look at the stack,
disassemble code, display memory etc. of different processes.

Presently, KDB refuses to access user space addresses. A few
developers requested for a way to access process memory and
switch process contexts from within KDB. What it is required
for are those occasions where the system is hung and there
is no kernel panic.  During hardware development it is not
uncommon for test to just stop and the server to be hung.
When this is caused by a process waiting for a resource to
be freed, it is essential to be able to see who is holding
the resource. Additionally, in a hang situation, the process
that is causing the problem may not be the current process
when you enter the debugger.

This patch (against 2.4.20 + KDB v4.1) provides:
- a way to switch the current process ("pid" command)
- a way to look at user space registers of the current
  process ("rd u" command)
- a way to access user space addresses in the context of
  a given process (normal md/mdr/id commands with
  user space addresses)

Note that "bt" can be made to work unreliably in user
contexts too, but I have left it out for now.
--
diff -urN -X /home/vamsi/.dontdiff 2420-kdb4.1-pure/arch/i386/kdb/kdbasupport.c 
2420-kdb4.1/arch/i386/kdb/kdbasupport.c
--- 2420-kdb4.1-pure/arch/i386/kdb/kdbasupport.c        2003-04-16 
18:00:58.000000000 +0530
+++ 2420-kdb4.1/arch/i386/kdb/kdbasupport.c     2003-04-16 18:02:14.000000000 
+0530
@@ -723,7 +723,7 @@
                /* User registers:  %%e[a-c]x, etc */
                regname++;
                regs = (struct pt_regs *)
-                       (current->thread.esp0 - sizeof(struct pt_regs));
+                       (kdb_current_task->thread.esp0 - sizeof(struct 
pt_regs));
        }
 
        for (i=0; i<ndbreglist; i++) {
@@ -823,7 +823,7 @@
        if (regname[0] == '%') {
                regname++;
                regs = (struct pt_regs *)
-                       (current->thread.esp0 - sizeof(struct pt_regs));
+                       (kdb_current_task->thread.esp0 - sizeof(struct 
pt_regs));
        }
 
        for (i=0; i<ndbreglist; i++) {
@@ -883,6 +883,7 @@
  *     d               Debug registers
  *     c               Control registers
  *     u               User registers at most recent entry to kernel
+ *                     for the process currently selected with "pid" command.
  * Following not yet implemented:
  *     m               Model Specific Registers (extra defines register #)
  *     r               Memory Type Range Registers (extra defines register)
@@ -900,7 +901,7 @@
         && (type[0] == 'u')) {
                type = NULL;
                regs = (struct pt_regs *)
-                       (current->thread.esp0 - sizeof(struct pt_regs));
+                       (kdb_current_task->thread.esp0 - sizeof(struct 
pt_regs));
        }
 
        if (type == NULL) {
diff -urN -X /home/vamsi/.dontdiff 2420-kdb4.1-pure/arch/ia64/kdb/kdbasupport.c 
2420-kdb4.1/arch/ia64/kdb/kdbasupport.c
--- 2420-kdb4.1-pure/arch/ia64/kdb/kdbasupport.c        2003-04-16 
18:01:01.000000000 +0530
+++ 2420-kdb4.1/arch/ia64/kdb/kdbasupport.c     2003-04-16 18:02:18.000000000 
+0530
@@ -672,7 +672,7 @@
        if (regname[0] == '%') {
                regname++;
                regs = (struct pt_regs *)
-                       (current->thread.ksp - sizeof(struct pt_regs));
+                       (kdb_current_task->thread.ksp - sizeof(struct pt_regs));
        }
 
        if (!regs) {
@@ -779,7 +779,7 @@
         && (type[0] == 'u')) {
                type = NULL;
                regs = (struct pt_regs *)
-                       (current->thread.ksp - sizeof(struct pt_regs));
+                       (kdb_current_task->thread.ksp - sizeof(struct pt_regs));
        }
 
        if (type == NULL) {
diff -urN -X /home/vamsi/.dontdiff 2420-kdb4.1-pure/include/asm-i386/kdb.h 
2420-kdb4.1/include/asm-i386/kdb.h
--- 2420-kdb4.1-pure/include/asm-i386/kdb.h     2003-04-16 18:00:58.000000000 
+0530
+++ 2420-kdb4.1/include/asm-i386/kdb.h  2003-04-16 19:06:49.000000000 +0530
@@ -78,6 +78,9 @@
 
 #include <asm/uaccess.h>
 
+extern int kdb_getuserarea_size(void *, unsigned long, size_t);
+extern int kdb_putuserarea_size(unsigned long, void *, size_t);
+
 static inline int
 __kdba_putarea_size(unsigned long to_xxx, void *from, size_t size)
 {
@@ -86,6 +89,11 @@
        char c;
        c = *((volatile char *)from);
        c = *((volatile char *)from + size - 1);
+       
+       if (to_xxx < PAGE_OFFSET) {
+               return kdb_putuserarea_size(to_xxx, from, size);
+       }
+
        set_fs(KERNEL_DS);
        r = __copy_to_user((void *)to_xxx, from, size);
        set_fs(oldfs);
@@ -99,6 +107,11 @@
        int r;
        *((volatile char *)to) = '\0';
        *((volatile char *)to + size - 1) = '\0';
+
+       if (from_xxx < PAGE_OFFSET) {
+               return kdb_getuserarea_size(to, from_xxx, size);
+       }
+
        set_fs(KERNEL_DS);
        switch (size) {
        case 1:
diff -urN -X /home/vamsi/.dontdiff 
2420-kdb4.1-pure/include/asm-i386/kmap_types.h 
2420-kdb4.1/include/asm-i386/kmap_types.h
--- 2420-kdb4.1-pure/include/asm-i386/kmap_types.h      2003-04-16 
18:35:03.000000000 +0530
+++ 2420-kdb4.1/include/asm-i386/kmap_types.h   2003-04-16 18:34:32.000000000 
+0530
@@ -8,6 +8,7 @@
        KM_USER0,
        KM_USER1,
        KM_BH_IRQ,
+       KM_KDB,
        KM_TYPE_NR
 };
 
diff -urN -X /home/vamsi/.dontdiff 2420-kdb4.1-pure/include/asm-ia64/kdb.h 
2420-kdb4.1/include/asm-ia64/kdb.h
--- 2420-kdb4.1-pure/include/asm-ia64/kdb.h     2003-04-16 18:01:01.000000000 
+0530
+++ 2420-kdb4.1/include/asm-ia64/kdb.h  2003-04-16 18:57:58.000000000 +0530
@@ -76,6 +76,9 @@
 
 #include <asm/uaccess.h>
 
+extern int kdb_getuserarea_size(void *, unsigned long, size_t);
+extern int kdb_putuserarea_size(void *, unsigned long, size_t);
+
 static inline int
 __kdba_putarea_size(unsigned long to_xxx, void *from, size_t size)
 {
@@ -84,6 +87,11 @@
        char c;
        c = *((volatile char *)from);
        c = *((volatile char *)from + size - 1);
+
+       if (to_xxx < PAGE_OFFSET) {
+               return kdb_putuserarea_size(to_xxx, from, size);
+       }
+
 #ifdef VPERNODE_BASE /* if present, the new CONFIG_NUMA code */
        if (to_xxx >= VPERNODE_BASE && to_xxx < VGLOBAL_BASE) {
                to_xxx = __imva(to_xxx);
@@ -102,6 +110,11 @@
        int r;
        *((volatile char *)to) = '\0';
        *((volatile char *)to + size - 1) = '\0';
+
+       if (from_xxx < PAGE_OFFSET) {
+               return kdb_getuserarea_size(to, from_xxx, size);
+       }
+
        set_fs(KERNEL_DS);
        switch (size) {
        case 1:
diff -urN -X /home/vamsi/.dontdiff 2420-kdb4.1-pure/include/linux/kdb.h 
2420-kdb4.1/include/linux/kdb.h
--- 2420-kdb4.1-pure/include/linux/kdb.h        2003-04-16 18:00:50.000000000 
+0530
+++ 2420-kdb4.1/include/linux/kdb.h     2003-04-16 19:07:42.000000000 +0530
@@ -333,4 +333,7 @@
 #endif
 }
 
+extern struct task_struct *kdb_current_task;
+extern struct page * kdb_follow_page(struct mm_struct *, unsigned long, int); 
/* from mm/memory.c */
+
 #endif /* !_KDB_H */
diff -urN -X /home/vamsi/.dontdiff 2420-kdb4.1-pure/kdb/kdbmain.c 
2420-kdb4.1/kdb/kdbmain.c
--- 2420-kdb4.1-pure/kdb/kdbmain.c      2003-04-16 18:00:50.000000000 +0530
+++ 2420-kdb4.1/kdb/kdbmain.c   2003-04-16 18:27:44.000000000 +0530
@@ -76,6 +76,8 @@
 
 volatile int kdb_state[NR_CPUS];               /* Per cpu state */
 
+struct task_struct *kdb_current_task;
+
 #ifdef CONFIG_KDB_OFF
 int kdb_on = 0;                                /* Default is off */
 #else
@@ -1067,6 +1069,8 @@
 
        kdba_local_arch_setup();
 
+       kdb_current_task = current;
+
        while (1) {
                /*
                 * Initialize pager context.
@@ -2781,6 +2785,59 @@
 }
 
 /*
+ * kdb_pid
+ *
+ *     This function implements the 'pid' command which switches
+ *     the currently active process.
+ *
+ *     pid [<pid>]
+ *
+ * Inputs:
+ *     argc    argument count
+ *     argv    argument vector
+ *     envp    environment vector
+ *     regs    registers at time kdb was entered.
+ * Outputs:
+ *     None.
+ * Returns:
+ *     zero for success, a kdb diagnostic if error
+ * Locking:
+ *     none.
+ * Remarks:
+ */
+
+
+int
+kdb_pid(int argc, const char **argv, const char **envp, struct pt_regs *regs)
+{
+       struct task_struct      *p;
+       unsigned long val;
+       int diag;
+
+       if (argc > 1)
+               return KDB_ARGCOUNT;
+
+       if (argc) {
+               diag = kdbgetularg(argv[1], &val);
+               if (diag)
+                       return KDB_BADINT;
+       
+               p = find_task_by_pid((pid_t)val);
+               if (!p) {
+                       kdb_printf("No task with pid=%d\n", (pid_t)val);
+                       return 0;
+               }
+
+               kdb_current_task = p;
+       }
+
+       kdb_printf("KDB current process is %s(pid=%d)\n", 
kdb_current_task->comm, 
+                  kdb_current_task->pid);
+
+       return 0;
+}
+
+/*
  * kdb_ll
  *
  *     This function implements the 'll' command which follows a linked
@@ -3245,6 +3302,7 @@
        kdb_register_repeat("?", kdb_help, "",         "Display Help Message", 
0, KDB_REPEAT_NONE);
        kdb_register_repeat("cpu", kdb_cpu, "<cpunum>","Switch to new cpu", 0, 
KDB_REPEAT_NONE);
        kdb_register_repeat("ps", kdb_ps, "",           "Display active task 
list", 0, KDB_REPEAT_NONE);
+       kdb_register_repeat("pid", kdb_pid, "<pidnum>", "Switch to another 
task", 0, KDB_REPEAT_NONE);
        kdb_register_repeat("reboot", kdb_reboot, "",  "Reboot the machine 
immediately", 0, KDB_REPEAT_NONE);
        kdb_register_repeat("sections", kdb_sections, "",  "List kernel and 
module sections", 0, KDB_REPEAT_NONE);
 #if defined(CONFIG_MODULES)
@@ -3376,6 +3434,8 @@
 EXPORT_SYMBOL(kdb_unregister);
 EXPORT_SYMBOL(kdb_getarea_size);
 EXPORT_SYMBOL(kdb_putarea_size);
+EXPORT_SYMBOL(kdb_getuserarea_size);
+EXPORT_SYMBOL(kdb_putuserarea_size);
 EXPORT_SYMBOL(kdb_getword);
 EXPORT_SYMBOL(kdb_putword);
 EXPORT_SYMBOL(kdbgetularg);
diff -urN -X /home/vamsi/.dontdiff 2420-kdb4.1-pure/kdb/kdbsupport.c 
2420-kdb4.1/kdb/kdbsupport.c
--- 2420-kdb4.1-pure/kdb/kdbsupport.c   2003-04-16 18:00:50.000000000 +0530
+++ 2420-kdb4.1/kdb/kdbsupport.c        2003-04-16 19:11:44.000000000 +0530
@@ -40,6 +40,7 @@
 #include <linux/kallsyms.h>
 #include <linux/stddef.h>
 #include <linux/vmalloc.h>
+#include <linux/highmem.h>
 #include <asm/uaccess.h>
 
 #include <linux/kdb.h>
@@ -800,3 +801,61 @@
        else
                kdb_printf("0x%lx\n", val);
 }
+
+static struct page * kdb_get_one_user_page(struct task_struct *tsk, unsigned 
long start,
+               int len, int write)
+{
+       struct mm_struct *mm = tsk->mm;
+       unsigned int flags;
+       struct vm_area_struct * vma;
+
+       /* shouldn't cross a page boundary. temporary restriction. */
+       if ((start & PAGE_MASK) != ((start+len) & PAGE_MASK)) {
+               kdb_printf("%s: crosses page boundary: addr=%08lx, len=%d\n", 
+                       __FUNCTION__, start, len);
+               return NULL;
+       }
+
+       start = PAGE_ALIGN(start);
+       flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
+
+       vma = find_extend_vma(mm, start);
+
+       /* may be we can allow access to VM_IO pages inside KDB? */
+       if (!vma || (vma->vm_flags & VM_IO) || !(flags & vma->vm_flags))
+               return NULL;
+
+       return kdb_follow_page(mm, start, write);
+}
+
+int kdb_getuserarea_size(void *to, unsigned long from, size_t size)
+{
+       struct page *page;
+       void * vaddr;
+
+       page = kdb_get_one_user_page(kdb_current_task, from, size, 0);
+       if (!page)
+               return size;
+
+       vaddr = kmap_atomic(page, KM_KDB);
+       memcpy(to, vaddr+ (from & (PAGE_SIZE - 1)), size);
+       kunmap_atomic(vaddr, KM_KDB);
+
+       return 0;
+}
+
+int kdb_putuserarea_size(unsigned long to, void *from, size_t size)
+{
+       struct page *page;
+       void * vaddr;
+
+       page = kdb_get_one_user_page(kdb_current_task, to, size, 1);
+       if (!page)
+               return size;
+
+       vaddr = kmap_atomic(page, KM_KDB);
+       memcpy(vaddr+ (to & (PAGE_SIZE - 1)), from, size);
+       kunmap_atomic(vaddr, KM_KDB);
+
+       return 0;
+}
diff -urN -X /home/vamsi/.dontdiff 2420-kdb4.1-pure/mm/memory.c 
2420-kdb4.1/mm/memory.c
--- 2420-kdb4.1-pure/mm/memory.c        2003-04-16 18:23:53.000000000 +0530
+++ 2420-kdb4.1/mm/memory.c     2003-04-16 18:44:28.000000000 +0530
@@ -1494,3 +1494,15 @@
        }
        return page;
 }
+
+#ifdef CONFIG_KDB
+struct page * kdb_follow_page(struct mm_struct *mm, unsigned long address, int 
write) 
+{
+       struct page *page = follow_page(mm, address, write);
+
+       if (!page)
+               return get_page_map(page);
+
+       return page;
+}
+#endif

<Prev in Thread] Current Thread [Next in Thread>