2008-08-01 15:28:44

by Jason Wessel

[permalink] [raw]
Subject: [git pull] kgdb 2.6.27 fixes


Linus, please pull the kgdb git tree fixes for 2.6.27.

git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb.git for_linus


Summary:

Several regressions have been fixed for which there was no automated test.
- There is no requirement for CONFIG_FRAME_POINTER.
- When using CONFIG_DEBUG_RODATA kgdb silently failed to set
breakpoints instead of reporting an error.
- The ability to query for the thread list from the debugger
would silently hang the kernel.

---

Jason Wessel (3):
kgdb: remove the requirement for CONFIG_FRAME_POINTER
kgdb: fix kgdb_validate_break_address to perform a mem write
kgdb: fix gdb serial thread queries

Documentation/DocBook/kgdb.tmpl | 18 +++++++
kernel/kgdb.c | 94 ++++++++++++++++++++++++++++----------
lib/Kconfig.kgdb | 11 +++--
nus, please pull kgdb the kgdb git tree.
3 files changed, 94 insertions(+), 29 deletions(-)


2008-08-01 15:28:20

by Jason Wessel

[permalink] [raw]
Subject: [PATCH 1/3] kgdb: remove the requirement for CONFIG_FRAME_POINTER

There is no technical reason that the kgdb core requires frame
pointers. It is up to the end user of KGDB to decide if they need
them or not.

[ [email protected]: removed frame pointers on mips ]

Signed-off-by: Jason Wessel <[email protected]>
---
Documentation/DocBook/kgdb.tmpl | 8 ++++++++
lib/Kconfig.kgdb | 11 +++++++----
2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/Documentation/DocBook/kgdb.tmpl b/Documentation/DocBook/kgdb.tmpl
index e8acd1f..54d3b15 100644
--- a/Documentation/DocBook/kgdb.tmpl
+++ b/Documentation/DocBook/kgdb.tmpl
@@ -98,6 +98,14 @@
"Kernel debugging" select "KGDB: kernel debugging with remote gdb".
</para>
<para>
+ It is advised, but not required that you turn on the
+ CONFIG_FRAME_POINTER kernel option. This option inserts code to
+ into the compiled executable which saves the frame information in
+ registers or on the stack at different points which will allow a
+ debugger such as gdb to more accurately construct stack back traces
+ while debugging the kernel.
+ </para>
+ <para>
Next you should choose one of more I/O drivers to interconnect debugging
host and debugged target. Early boot debugging requires a KGDB
I/O driver that supports early debugging and the driver must be
diff --git a/lib/Kconfig.kgdb b/lib/Kconfig.kgdb
index 2cfd272..9b5d1d7 100644
--- a/lib/Kconfig.kgdb
+++ b/lib/Kconfig.kgdb
@@ -4,14 +4,17 @@ config HAVE_ARCH_KGDB

menuconfig KGDB
bool "KGDB: kernel debugging with remote gdb"
- select FRAME_POINTER
depends on HAVE_ARCH_KGDB
depends on DEBUG_KERNEL && EXPERIMENTAL
help
If you say Y here, it will be possible to remotely debug the
- kernel using gdb. Documentation of kernel debugger is available
- at http://kgdb.sourceforge.net as well as in DocBook form
- in Documentation/DocBook/. If unsure, say N.
+ kernel using gdb. It is recommended but not required, that
+ you also turn on the kernel config option
+ CONFIG_FRAME_POINTER to aid in producing more reliable stack
+ backtraces in the external debugger. Documentation of
+ kernel debugger is available at http://kgdb.sourceforge.net
+ as well as in DocBook form in Documentation/DocBook/. If
+ unsure, say N.

if KGDB

--
1.5.5.1

2008-08-01 15:28:33

by Jason Wessel

[permalink] [raw]
Subject: [PATCH 3/3] kgdb: fix gdb serial thread queries

The command "info threads" did not work correctly with kgdb. It would
result in a silent kernel hang if used.

This patach addresses several problems.
- Fix use of deprecated NR_CPUS
- Fix kgdb to not walk linearly through the pid space
- Correctly implement shadow pids
- Change the threads per query to a #define
- Fix kgdb_hex2long to work with negated values

The threads 0 and -1 are reserved to represent the current task. That
means that CPU 0 will start with a shadow thread id of -2, and CPU 1
will have a shadow thread id of -3, etc...

>From the debugger you can switch to a shadow thread to see what one of
the other cpus was doing, however it is not possible to execute run
control operations on any other cpu execept the cpu executing the
kgdb_handle_exception().

Signed-off-by: Jason Wessel <[email protected]>
---
kernel/kgdb.c | 68 +++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 50 insertions(+), 18 deletions(-)

diff --git a/kernel/kgdb.c b/kernel/kgdb.c
index c0d45b2..eaa21fc 100644
--- a/kernel/kgdb.c
+++ b/kernel/kgdb.c
@@ -56,12 +56,14 @@

static int kgdb_break_asap;

+#define KGDB_MAX_THREAD_QUERY 17
struct kgdb_state {
int ex_vector;
int signo;
int err_code;
int cpu;
int pass_exception;
+ unsigned long thr_query;
unsigned long threadid;
long kgdb_usethreadid;
struct pt_regs *linux_regs;
@@ -445,9 +447,14 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val)
{
int hex_val;
int num = 0;
+ int negate = 0;

*long_val = 0;

+ if (**ptr == '-') {
+ negate = 1;
+ (*ptr)++;
+ }
while (**ptr) {
hex_val = hex(**ptr);
if (hex_val < 0)
@@ -458,6 +465,9 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val)
(*ptr)++;
}

+ if (negate)
+ *long_val = -*long_val;
+
return num;
}

@@ -527,10 +537,16 @@ static void int_to_threadref(unsigned char *id, int value)
static struct task_struct *getthread(struct pt_regs *regs, int tid)
{
/*
- * Non-positive TIDs are remapped idle tasks:
+ * Non-positive TIDs are remapped to the cpu shadow information
*/
- if (tid <= 0)
- return idle_task(-tid);
+ if (tid == 0 || tid == -1)
+ tid = -atomic_read(&kgdb_active) - 2;
+ if (tid < 0) {
+ if (kgdb_info[-tid - 2].task)
+ return kgdb_info[-tid - 2].task;
+ else
+ return idle_task(-tid - 2);
+ }

/*
* find_task_by_pid_ns() does not take the tasklist lock anymore
@@ -737,14 +753,15 @@ setundefined:
}

/*
- * Remap normal tasks to their real PID, idle tasks to -1 ... -NR_CPUs:
+ * Remap normal tasks to their real PID,
+ * CPU shadow threads are mapped to -CPU - 2
*/
static inline int shadow_pid(int realpid)
{
if (realpid)
return realpid;

- return -1-raw_smp_processor_id();
+ return -raw_smp_processor_id() - 2;
}

static char gdbmsgbuf[BUFMAX + 1];
@@ -838,7 +855,7 @@ static void gdb_cmd_getregs(struct kgdb_state *ks)
local_debuggerinfo = kgdb_info[ks->cpu].debuggerinfo;
} else {
local_debuggerinfo = NULL;
- for (i = 0; i < NR_CPUS; i++) {
+ for_each_online_cpu(i) {
/*
* Try to find the task on some other
* or possibly this node if we do not
@@ -972,10 +989,13 @@ static int gdb_cmd_reboot(struct kgdb_state *ks)
/* Handle the 'q' query packets */
static void gdb_cmd_query(struct kgdb_state *ks)
{
- struct task_struct *thread;
+ struct task_struct *g;
+ struct task_struct *p;
unsigned char thref[8];
char *ptr;
int i;
+ int cpu;
+ int finished = 0;

switch (remcom_in_buffer[1]) {
case 's':
@@ -985,22 +1005,34 @@ static void gdb_cmd_query(struct kgdb_state *ks)
break;
}

- if (remcom_in_buffer[1] == 'f')
- ks->threadid = 1;
-
+ i = 0;
remcom_out_buffer[0] = 'm';
ptr = remcom_out_buffer + 1;
-
- for (i = 0; i < 17; ks->threadid++) {
- thread = getthread(ks->linux_regs, ks->threadid);
- if (thread) {
- int_to_threadref(thref, ks->threadid);
+ if (remcom_in_buffer[1] == 'f') {
+ /* Each cpu is a shadow thread */
+ for_each_online_cpu(cpu) {
+ ks->thr_query = 0;
+ int_to_threadref(thref, -cpu - 2);
pack_threadid(ptr, thref);
ptr += BUF_THREAD_ID_SIZE;
*(ptr++) = ',';
i++;
}
}
+
+ do_each_thread(g, p) {
+ if (i >= ks->thr_query && !finished) {
+ int_to_threadref(thref, p->pid);
+ pack_threadid(ptr, thref);
+ ptr += BUF_THREAD_ID_SIZE;
+ *(ptr++) = ',';
+ ks->thr_query++;
+ if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0)
+ finished = 1;
+ }
+ i++;
+ } while_each_thread(g, p);
+
*(--ptr) = '\0';
break;

@@ -1023,15 +1055,15 @@ static void gdb_cmd_query(struct kgdb_state *ks)
error_packet(remcom_out_buffer, -EINVAL);
break;
}
- if (ks->threadid > 0) {
+ if ((int)ks->threadid > 0) {
kgdb_mem2hex(getthread(ks->linux_regs,
ks->threadid)->comm,
remcom_out_buffer, 16);
} else {
static char tmpstr[23 + BUF_THREAD_ID_SIZE];

- sprintf(tmpstr, "Shadow task %d for pid 0",
- (int)(-ks->threadid-1));
+ sprintf(tmpstr, "shadowCPU%d",
+ (int)(-ks->threadid - 2));
kgdb_mem2hex(tmpstr, remcom_out_buffer, strlen(tmpstr));
}
break;
--
1.5.5.1

2008-08-01 15:28:58

by Jason Wessel

[permalink] [raw]
Subject: [PATCH 2/3] kgdb: fix kgdb_validate_break_address to perform a mem write

A regression to the kgdb core was found in the case of using the
CONFIG_DEBUG_RODATA kernel option. When this option is on, a breakpoint
cannot be written into any readonly memory page. When an external
debugger requests a breakpoint to get set, the
kgdb_validate_break_address() was only checking to see if the address
to place the breakpoint was readable and lacked a write check.

This patch changes the validate routine to try reading (via the
breakpoint set request) and also to try immediately writing the break
point. If either fails, an error is correctly returned and the
debugger behaves correctly. Then an end user can make the
descision to use hardware breakpoints.

Also update the documentation to reflect that using
CONFIG_DEBUG_RODATA will inhibit the use of software breakpoints.

Signed-off-by: Jason Wessel <[email protected]>
---
Documentation/DocBook/kgdb.tmpl | 10 ++++++++++
kernel/kgdb.c | 26 +++++++++++++++++++-------
2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/Documentation/DocBook/kgdb.tmpl b/Documentation/DocBook/kgdb.tmpl
index 54d3b15..372dec2 100644
--- a/Documentation/DocBook/kgdb.tmpl
+++ b/Documentation/DocBook/kgdb.tmpl
@@ -106,6 +106,16 @@
while debugging the kernel.
</para>
<para>
+ If the architecture that you are using supports the kernel option
+ CONFIG_DEBUG_RODATA, you should consider turning it off. This
+ option will prevent the use of software breakpoints because it
+ marks certain regions of the kernel's memory space as read-only.
+ If kgdb supports it for the architecture you are using, you can
+ use hardware breakpoints if you desire to run with the
+ CONFIG_DEBUG_RODATA option turned on, else you need to turn off
+ this option.
+ </para>
+ <para>
Next you should choose one of more I/O drivers to interconnect debugging
host and debugged target. Early boot debugging requires a KGDB
I/O driver that supports early debugging and the driver must be
diff --git a/kernel/kgdb.c b/kernel/kgdb.c
index 3ec23c3..c0d45b2 100644
--- a/kernel/kgdb.c
+++ b/kernel/kgdb.c
@@ -166,13 +166,6 @@ early_param("nokgdbroundup", opt_nokgdbroundup);
* Weak aliases for breakpoint management,
* can be overriden by architectures when needed:
*/
-int __weak kgdb_validate_break_address(unsigned long addr)
-{
- char tmp_variable[BREAK_INSTR_SIZE];
-
- return probe_kernel_read(tmp_variable, (char *)addr, BREAK_INSTR_SIZE);
-}
-
int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
{
int err;
@@ -191,6 +184,25 @@ int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle)
(char *)bundle, BREAK_INSTR_SIZE);
}

+int __weak kgdb_validate_break_address(unsigned long addr)
+{
+ char tmp_variable[BREAK_INSTR_SIZE];
+ int err;
+ /* Validate setting the breakpoint and then removing it. In the
+ * remove fails, the kernel needs to emit a bad message because we
+ * are deep trouble not being able to put things back the way we
+ * found them.
+ */
+ err = kgdb_arch_set_breakpoint(addr, tmp_variable);
+ if (err)
+ return err;
+ err = kgdb_arch_remove_breakpoint(addr, tmp_variable);
+ if (err)
+ printk(KERN_ERR "KGDB: Critical breakpoint error, kernel "
+ "memory destroyed at: %lx", addr);
+ return err;
+}
+
unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs)
{
return instruction_pointer(regs);
--
1.5.5.1