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
This commit is contained in:
commit
31582b094d
|
@ -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 @@
|
||||||
|
|
||||||
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);
|
||||||
* 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)
|
||||||
(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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
(*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)
|
||||||
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:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue