Commit 3eddddcf authored by Jeff Dike's avatar Jeff Dike Committed by Linus Torvalds

[PATCH] uml: breakpoint an arbitrary thread

This patch implements a stack trace for a thread, not unlike sysrq-t does.
The advantage to this is that a break point can be placed on showreqs, so that
upon showing the stack, you jump immediately into the debugger.  While sysrq-t
does the same thing, sysrq-t shows *all* threads stacks.  It also doesn't work
right now.  In the future, I thought it might be acceptable to make this show
all pids stacks, but perhaps leaving well enough alone and just using sysrq-t
would be okay.  For now, upon receiving the stack command, UML switches
context to that thread, dumps its registers, and then switches context back to
the original thread.  Since UML compacts all threads into one of 4 host
threads, this sort of mechanism could be expanded in the future to include
other debugging helpers that sysrq does not cover.

Note by jdike - The main benefit to this is that it brings an arbitrary thread
back into context, where it can be examined by gdb.  The fact that it dumps it
stack is secondary.  This provides the capability to examine a sleeping
thread, which has existed in tt mode, but not in skas mode until now.

Also, the other threads, that sysrq doesn't cover, can be gdb-ed directly
anyway.

Signed-off-by: Allan Graves<allan.graves@gmail.com>
Signed-off-by: default avatarJeff Dike <jdike@addtoit.com>
Cc: Paolo Giarrusso <blaisorblade@yahoo.it>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent f6e34c6a
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "os.h" #include "os.h"
#include "umid.h" #include "umid.h"
#include "irq_kern.h" #include "irq_kern.h"
#include "choose-mode.h"
static int do_unlink_socket(struct notifier_block *notifier, static int do_unlink_socket(struct notifier_block *notifier,
unsigned long what, void *data) unsigned long what, void *data)
...@@ -276,6 +277,7 @@ void mconsole_proc(struct mc_request *req) ...@@ -276,6 +277,7 @@ void mconsole_proc(struct mc_request *req)
go - continue the UML after a 'stop' \n\ go - continue the UML after a 'stop' \n\
log <string> - make UML enter <string> into the kernel log\n\ log <string> - make UML enter <string> into the kernel log\n\
proc <file> - returns the contents of the UML's /proc/<file>\n\ proc <file> - returns the contents of the UML's /proc/<file>\n\
stack <pid> - returns the stack of the specified pid\n\
" "
void mconsole_help(struct mc_request *req) void mconsole_help(struct mc_request *req)
...@@ -479,6 +481,56 @@ void mconsole_sysrq(struct mc_request *req) ...@@ -479,6 +481,56 @@ void mconsole_sysrq(struct mc_request *req)
} }
#endif #endif
/* Mconsole stack trace
* Added by Allan Graves, Jeff Dike
* Dumps a stacks registers to the linux console.
* Usage stack <pid>.
*/
void do_stack(struct mc_request *req)
{
char *ptr = req->request.data;
int pid_requested= -1;
struct task_struct *from = NULL;
struct task_struct *to = NULL;
/* Would be nice:
* 1) Send showregs output to mconsole.
* 2) Add a way to stack dump all pids.
*/
ptr += strlen("stack");
while(isspace(*ptr)) ptr++;
/* Should really check for multiple pids or reject bad args here */
/* What do the arguments in mconsole_reply mean? */
if(sscanf(ptr, "%d", &pid_requested) == 0){
mconsole_reply(req, "Please specify a pid", 1, 0);
return;
}
from = current;
to = find_task_by_pid(pid_requested);
if((to == NULL) || (pid_requested == 0)) {
mconsole_reply(req, "Couldn't find that pid", 1, 0);
return;
}
to->thread.saved_task = current;
switch_to(from, to, from);
mconsole_reply(req, "Stack Dumped to console and message log", 0, 0);
}
void mconsole_stack(struct mc_request *req)
{
/* This command doesn't work in TT mode, so let's check and then
* get out of here
*/
CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
1, 0),
do_stack(req));
}
/* Changed by mconsole_setup, which is __setup, and called before SMP is /* Changed by mconsole_setup, which is __setup, and called before SMP is
* active. * active.
*/ */
......
...@@ -30,6 +30,7 @@ static struct mconsole_command commands[] = { ...@@ -30,6 +30,7 @@ static struct mconsole_command commands[] = {
{ "go", mconsole_go, MCONSOLE_INTR }, { "go", mconsole_go, MCONSOLE_INTR },
{ "log", mconsole_log, MCONSOLE_INTR }, { "log", mconsole_log, MCONSOLE_INTR },
{ "proc", mconsole_proc, MCONSOLE_PROC }, { "proc", mconsole_proc, MCONSOLE_PROC },
{ "stack", mconsole_stack, MCONSOLE_INTR },
}; };
/* Initialized in mconsole_init, which is an initcall */ /* Initialized in mconsole_init, which is an initcall */
......
...@@ -81,6 +81,7 @@ extern void mconsole_stop(struct mc_request *req); ...@@ -81,6 +81,7 @@ extern void mconsole_stop(struct mc_request *req);
extern void mconsole_go(struct mc_request *req); extern void mconsole_go(struct mc_request *req);
extern void mconsole_log(struct mc_request *req); extern void mconsole_log(struct mc_request *req);
extern void mconsole_proc(struct mc_request *req); extern void mconsole_proc(struct mc_request *req);
extern void mconsole_stack(struct mc_request *req);
extern int mconsole_get_request(int fd, struct mc_request *req); extern int mconsole_get_request(int fd, struct mc_request *req);
extern int mconsole_notify(char *sock_name, int type, const void *data, extern int mconsole_notify(char *sock_name, int type, const void *data,
......
...@@ -119,7 +119,14 @@ void *_switch_to(void *prev, void *next, void *last) ...@@ -119,7 +119,14 @@ void *_switch_to(void *prev, void *next, void *last)
to->thread.prev_sched = from; to->thread.prev_sched = from;
set_current(to); set_current(to);
do {
current->thread.saved_task = NULL ;
CHOOSE_MODE_PROC(switch_to_tt, switch_to_skas, prev, next); CHOOSE_MODE_PROC(switch_to_tt, switch_to_skas, prev, next);
if(current->thread.saved_task)
show_regs(&(current->thread.regs));
next= current->thread.saved_task;
prev= current;
} while(current->thread.saved_task);
return(current->thread.prev_sched); return(current->thread.prev_sched);
......
...@@ -21,6 +21,7 @@ struct thread_struct { ...@@ -21,6 +21,7 @@ struct thread_struct {
* copy_thread) to mark that we are begin called from userspace (fork / * copy_thread) to mark that we are begin called from userspace (fork /
* vfork / clone), and reset to 0 after. It is left to 0 when called * vfork / clone), and reset to 0 after. It is left to 0 when called
* from kernelspace (i.e. kernel_thread() or fork_idle(), as of 2.6.11). */ * from kernelspace (i.e. kernel_thread() or fork_idle(), as of 2.6.11). */
struct task_struct *saved_task;
int forking; int forking;
int nsyscalls; int nsyscalls;
struct pt_regs regs; struct pt_regs regs;
......
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