Commit 31582b09 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb:
  kgdb: fix gdb serial thread queries
  kgdb: fix kgdb_validate_break_address to perform a mem write
  kgdb: remove the requirement for CONFIG_FRAME_POINTER
parents df1efe6f 25fc9999
...@@ -98,6 +98,24 @@ ...@@ -98,6 +98,24 @@
"Kernel debugging" select "KGDB: kernel debugging with remote gdb". "Kernel debugging" select "KGDB: kernel debugging with remote gdb".
</para> </para>
<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>
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 Next you should choose one of more I/O drivers to interconnect debugging
host and debugged target. Early boot debugging requires a KGDB host and debugged target. Early boot debugging requires a KGDB
I/O driver that supports early debugging and the driver must be I/O driver that supports early debugging and the driver must be
......
...@@ -56,12 +56,14 @@ ...@@ -56,12 +56,14 @@
static int kgdb_break_asap; static int kgdb_break_asap;
#define KGDB_MAX_THREAD_QUERY 17
struct kgdb_state { struct kgdb_state {
int ex_vector; int ex_vector;
int signo; int signo;
int err_code; int err_code;
int cpu; int cpu;
int pass_exception; int pass_exception;
unsigned long thr_query;
unsigned long threadid; unsigned long threadid;
long kgdb_usethreadid; long kgdb_usethreadid;
struct pt_regs *linux_regs; struct pt_regs *linux_regs;
...@@ -166,13 +168,6 @@ early_param("nokgdbroundup", opt_nokgdbroundup); ...@@ -166,13 +168,6 @@ early_param("nokgdbroundup", opt_nokgdbroundup);
* Weak aliases for breakpoint management, * Weak aliases for breakpoint management,
* can be overriden by architectures when needed: * 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 __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
{ {
int err; int err;
...@@ -191,6 +186,25 @@ int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) ...@@ -191,6 +186,25 @@ int __weak kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle)
(char *)bundle, BREAK_INSTR_SIZE); (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) unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs)
{ {
return instruction_pointer(regs); return instruction_pointer(regs);
...@@ -433,9 +447,14 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val) ...@@ -433,9 +447,14 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val)
{ {
int hex_val; int hex_val;
int num = 0; int num = 0;
int negate = 0;
*long_val = 0; *long_val = 0;
if (**ptr == '-') {
negate = 1;
(*ptr)++;
}
while (**ptr) { while (**ptr) {
hex_val = hex(**ptr); hex_val = hex(**ptr);
if (hex_val < 0) if (hex_val < 0)
...@@ -446,6 +465,9 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val) ...@@ -446,6 +465,9 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val)
(*ptr)++; (*ptr)++;
} }
if (negate)
*long_val = -*long_val;
return num; return num;
} }
...@@ -515,10 +537,16 @@ static void int_to_threadref(unsigned char *id, int value) ...@@ -515,10 +537,16 @@ static void int_to_threadref(unsigned char *id, int value)
static struct task_struct *getthread(struct pt_regs *regs, int tid) 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) if (tid == 0 || tid == -1)
return idle_task(-tid); 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 * find_task_by_pid_ns() does not take the tasklist lock anymore
...@@ -725,14 +753,15 @@ setundefined: ...@@ -725,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) static inline int shadow_pid(int realpid)
{ {
if (realpid) if (realpid)
return realpid; return realpid;
return -1-raw_smp_processor_id(); return -raw_smp_processor_id() - 2;
} }
static char gdbmsgbuf[BUFMAX + 1]; static char gdbmsgbuf[BUFMAX + 1];
...@@ -826,7 +855,7 @@ static void gdb_cmd_getregs(struct kgdb_state *ks) ...@@ -826,7 +855,7 @@ static void gdb_cmd_getregs(struct kgdb_state *ks)
local_debuggerinfo = kgdb_info[ks->cpu].debuggerinfo; local_debuggerinfo = kgdb_info[ks->cpu].debuggerinfo;
} else { } else {
local_debuggerinfo = NULL; local_debuggerinfo = NULL;
for (i = 0; i < NR_CPUS; i++) { for_each_online_cpu(i) {
/* /*
* Try to find the task on some other * Try to find the task on some other
* or possibly this node if we do not * or possibly this node if we do not
...@@ -960,10 +989,13 @@ static int gdb_cmd_reboot(struct kgdb_state *ks) ...@@ -960,10 +989,13 @@ static int gdb_cmd_reboot(struct kgdb_state *ks)
/* Handle the 'q' query packets */ /* Handle the 'q' query packets */
static void gdb_cmd_query(struct kgdb_state *ks) 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]; unsigned char thref[8];
char *ptr; char *ptr;
int i; int i;
int cpu;
int finished = 0;
switch (remcom_in_buffer[1]) { switch (remcom_in_buffer[1]) {
case 's': case 's':
...@@ -973,22 +1005,34 @@ static void gdb_cmd_query(struct kgdb_state *ks) ...@@ -973,22 +1005,34 @@ static void gdb_cmd_query(struct kgdb_state *ks)
break; break;
} }
if (remcom_in_buffer[1] == 'f') i = 0;
ks->threadid = 1;
remcom_out_buffer[0] = 'm'; remcom_out_buffer[0] = 'm';
ptr = remcom_out_buffer + 1; ptr = remcom_out_buffer + 1;
if (remcom_in_buffer[1] == 'f') {
for (i = 0; i < 17; ks->threadid++) { /* Each cpu is a shadow thread */
thread = getthread(ks->linux_regs, ks->threadid); for_each_online_cpu(cpu) {
if (thread) { ks->thr_query = 0;
int_to_threadref(thref, ks->threadid); int_to_threadref(thref, -cpu - 2);
pack_threadid(ptr, thref); pack_threadid(ptr, thref);
ptr += BUF_THREAD_ID_SIZE; ptr += BUF_THREAD_ID_SIZE;
*(ptr++) = ','; *(ptr++) = ',';
i++; 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'; *(--ptr) = '\0';
break; break;
...@@ -1011,15 +1055,15 @@ static void gdb_cmd_query(struct kgdb_state *ks) ...@@ -1011,15 +1055,15 @@ static void gdb_cmd_query(struct kgdb_state *ks)
error_packet(remcom_out_buffer, -EINVAL); error_packet(remcom_out_buffer, -EINVAL);
break; break;
} }
if (ks->threadid > 0) { if ((int)ks->threadid > 0) {
kgdb_mem2hex(getthread(ks->linux_regs, kgdb_mem2hex(getthread(ks->linux_regs,
ks->threadid)->comm, ks->threadid)->comm,
remcom_out_buffer, 16); remcom_out_buffer, 16);
} else { } else {
static char tmpstr[23 + BUF_THREAD_ID_SIZE]; static char tmpstr[23 + BUF_THREAD_ID_SIZE];
sprintf(tmpstr, "Shadow task %d for pid 0", sprintf(tmpstr, "shadowCPU%d",
(int)(-ks->threadid-1)); (int)(-ks->threadid - 2));
kgdb_mem2hex(tmpstr, remcom_out_buffer, strlen(tmpstr)); kgdb_mem2hex(tmpstr, remcom_out_buffer, strlen(tmpstr));
} }
break; break;
......
...@@ -4,14 +4,17 @@ config HAVE_ARCH_KGDB ...@@ -4,14 +4,17 @@ config HAVE_ARCH_KGDB
menuconfig KGDB menuconfig KGDB
bool "KGDB: kernel debugging with remote gdb" bool "KGDB: kernel debugging with remote gdb"
select FRAME_POINTER
depends on HAVE_ARCH_KGDB depends on HAVE_ARCH_KGDB
depends on DEBUG_KERNEL && EXPERIMENTAL depends on DEBUG_KERNEL && EXPERIMENTAL
help help
If you say Y here, it will be possible to remotely debug the If you say Y here, it will be possible to remotely debug the
kernel using gdb. Documentation of kernel debugger is available kernel using gdb. It is recommended but not required, that
at http://kgdb.sourceforge.net as well as in DocBook form you also turn on the kernel config option
in Documentation/DocBook/. If unsure, say N. 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 if KGDB
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment