mirror of https://github.com/crash-utility/crash
Preparing for gdb stack unwind support
There are 3 designs for supporting arbitrary tasks stack unwinding: 1) One gdb thread represent a task[1][2]. 2) One gdb thread represent a cpu[3]. 3) Leaving only one gdb thread[4]. 1 & 2 have a flaw that, when there are lots of tasks/cpus, it will slow the startup of crash, introduce complexity of the registers context synchronization between crash and gdb, hard to cover live debug mode etc. So here we used the 3rd design. Related discussions: [1]: https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg00524.html [2]: https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg00529.html [3]: https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg00471.html [4]: https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg00541.html To switch task, or view arbitrary tasks stack unwinding, we will reuse the current gdb thread, and load the target task's regcache to the thread. This will simplify many code. Note: this will change the behavior of "info threads" and "thread x", E.g: Before: crash> gdb thread [Current thread is 1 (CPU 0)] crash> info threads Id Target Id Frame * 1 CPU 0 <unavailable> in ?? () 2 CPU 1 <unavailable> in ?? () 3 CPU 2 <unavailable> in ?? () ... crash> thread 2 [Switching to thread 2 (CPU 1)] #0 <unavailable> in ?? () After: crash> gdb thread [Current thread is 1 (10715 bash)] crash> info threads Id Target Id Frame * 1 10715 bash 0xc0000000002bde04 in crash_setup_regs ... crash> thread 2 gdb: gdb request failed: thread 2 As a result, the "info threads" and "thread x" will be less useful. We will extend cmd "set" later to implement a similar function. E.g: crash> set <pid|task> Then the task context of crash and gdb will both be switched to pid/task, so the following command: "bt" "gdb bt" will output the same task context. [lijiang: squash these four patches(see the Link) into one patch] Link: https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg01085.html Link: https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg01087.html Link: https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg01086.html Link: https://www.mail-archive.com/devel@lists.crash-utility.osci.io/msg01088.html Co-developed-by: Aditya Gupta <adityag@linux.ibm.com> Co-developed-by: Alexey Makhalov <alexey.makhalov@broadcom.com> Signed-off-by: Tao Liu <ltao@redhat.com>
This commit is contained in:
parent
7c8a7dddda
commit
1fd80c623c
|
@ -26,10 +26,10 @@
|
|||
void crash_target_init (void);
|
||||
|
||||
extern "C" int gdb_readmem_callback(unsigned long, void *, int, int);
|
||||
extern "C" int crash_get_nr_cpus(void);
|
||||
extern "C" int crash_get_cpu_reg (int cpu, int regno, const char *regname,
|
||||
extern "C" int crash_get_current_task_reg (int regno, const char *regname,
|
||||
int regsize, void *val);
|
||||
|
||||
extern "C" int gdb_change_thread_context (void);
|
||||
extern "C" void crash_get_current_task_info(unsigned long *pid, char **comm);
|
||||
|
||||
/* The crash target. */
|
||||
|
||||
|
@ -60,30 +60,40 @@ public:
|
|||
bool has_registers () override { return true; }
|
||||
bool thread_alive (ptid_t ptid) override { return true; }
|
||||
std::string pid_to_str (ptid_t ptid) override
|
||||
{ return string_printf ("CPU %ld", ptid.tid ()); }
|
||||
{
|
||||
unsigned long pid;
|
||||
char *comm;
|
||||
crash_get_current_task_info(&pid, &comm);
|
||||
return string_printf ("%ld %s", pid, comm);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* We just get all the registers, so we don't use regno. */
|
||||
static void supply_registers(struct regcache *regcache, int regno)
|
||||
{
|
||||
gdb_byte regval[16];
|
||||
struct gdbarch *arch = regcache->arch ();
|
||||
const char *regname = gdbarch_register_name(arch, regno);
|
||||
int regsize = register_size(arch, regno);
|
||||
|
||||
if (regsize > sizeof (regval))
|
||||
error (_("fatal error: buffer size is not enough to fit register value"));
|
||||
|
||||
if (crash_get_current_task_reg (regno, regname, regsize, (void *)®val))
|
||||
regcache->raw_supply (regno, regval);
|
||||
else
|
||||
regcache->raw_supply (regno, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
crash_target::fetch_registers (struct regcache *regcache, int regno)
|
||||
{
|
||||
gdb_byte regval[16];
|
||||
int cpu = inferior_ptid.tid();
|
||||
struct gdbarch *arch = regcache->arch ();
|
||||
|
||||
for (int r = 0; r < gdbarch_num_regs (arch); r++)
|
||||
{
|
||||
const char *regname = gdbarch_register_name(arch, r);
|
||||
int regsize = register_size (arch, r);
|
||||
if (regsize > sizeof (regval))
|
||||
error (_("fatal error: buffer size is not enough to fit register value"));
|
||||
|
||||
if (crash_get_cpu_reg (cpu, r, regname, regsize, (void *)®val))
|
||||
regcache->raw_supply (r, regval);
|
||||
else
|
||||
regcache->raw_supply (r, NULL);
|
||||
}
|
||||
if (regno >= 0) {
|
||||
supply_registers(regcache, regno);
|
||||
} else if (regno == -1) {
|
||||
for (int r = 0; r < gdbarch_num_regs (regcache->arch ()); r++)
|
||||
supply_registers(regcache, r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -110,7 +120,6 @@ crash_target::xfer_partial (enum target_object object, const char *annex,
|
|||
void
|
||||
crash_target_init (void)
|
||||
{
|
||||
int nr_cpus = crash_get_nr_cpus();
|
||||
crash_target *target = new crash_target ();
|
||||
|
||||
/* Own the target until it is successfully pushed. */
|
||||
|
@ -119,13 +128,11 @@ crash_target_init (void)
|
|||
push_target (std::move (target_holder));
|
||||
|
||||
inferior_appeared (current_inferior (), CRASH_INFERIOR_PID);
|
||||
for (int i = 0; i < nr_cpus; i++)
|
||||
{
|
||||
thread_info *thread = add_thread_silent (target,
|
||||
ptid_t(CRASH_INFERIOR_PID, 0, i));
|
||||
if (!i)
|
||||
switch_to_thread (thread);
|
||||
}
|
||||
|
||||
/*Only create 1 gdb threads to view tasks' stack unwinding*/
|
||||
thread_info *thread = add_thread_silent (target,
|
||||
ptid_t(CRASH_INFERIOR_PID, 0, 0));
|
||||
switch_to_thread (thread);
|
||||
|
||||
/* Fetch all registers from core file. */
|
||||
target_fetch_registers (get_current_regcache (), -1);
|
||||
|
@ -133,3 +140,11 @@ crash_target_init (void)
|
|||
/* Now, set up the frame cache. */
|
||||
reinit_frame_cache ();
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
gdb_change_thread_context (void)
|
||||
{
|
||||
target_fetch_registers(get_current_regcache(), -1);
|
||||
reinit_frame_cache();
|
||||
return TRUE;
|
||||
}
|
||||
|
|
7
defs.h
7
defs.h
|
@ -1080,7 +1080,7 @@ struct machdep_table {
|
|||
void (*get_irq_affinity)(int);
|
||||
void (*show_interrupts)(int, ulong *);
|
||||
int (*is_page_ptr)(ulong, physaddr_t *);
|
||||
int (*get_cpu_reg)(int, int, const char *, int, void *);
|
||||
int (*get_current_task_reg)(int, const char *, int, void *);
|
||||
int (*is_cpu_prstatus_valid)(int cpu);
|
||||
};
|
||||
|
||||
|
@ -6079,7 +6079,7 @@ extern char *help_map[];
|
|||
* task.c
|
||||
*/
|
||||
void task_init(void);
|
||||
int set_context(ulong, ulong);
|
||||
int set_context(ulong, ulong, uint);
|
||||
void show_context(struct task_context *);
|
||||
ulong pid_to_task(ulong);
|
||||
ulong task_to_pid(ulong);
|
||||
|
@ -8115,4 +8115,7 @@ enum x86_64_regnum {
|
|||
LAST_REGNUM
|
||||
};
|
||||
|
||||
/* crash_target.c */
|
||||
extern int gdb_change_thread_context (void);
|
||||
|
||||
#endif /* !GDB_COMMON */
|
||||
|
|
|
@ -1067,30 +1067,13 @@ unsigned long crash_get_kaslr_offset(void)
|
|||
}
|
||||
|
||||
/* Callbacks for crash_target */
|
||||
int crash_get_nr_cpus(void);
|
||||
int crash_get_cpu_reg (int cpu, int regno, const char *regname,
|
||||
int regsize, void *val);
|
||||
|
||||
int crash_get_nr_cpus(void)
|
||||
int crash_get_current_task_reg (int regno, const char *regname,
|
||||
int regsize, void *value);
|
||||
int crash_get_current_task_reg (int regno, const char *regname,
|
||||
int regsize, void *value)
|
||||
{
|
||||
if (SADUMP_DUMPFILE())
|
||||
return sadump_get_nr_cpus();
|
||||
else if (DISKDUMP_DUMPFILE())
|
||||
return diskdump_get_nr_cpus();
|
||||
else if (KDUMP_DUMPFILE())
|
||||
return kdump_get_nr_cpus();
|
||||
else if (VMSS_DUMPFILE())
|
||||
return vmware_vmss_get_nr_cpus();
|
||||
|
||||
/* Just CPU #0 */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int crash_get_cpu_reg (int cpu, int regno, const char *regname,
|
||||
int regsize, void *value)
|
||||
{
|
||||
if (!machdep->get_cpu_reg)
|
||||
return FALSE;
|
||||
return machdep->get_cpu_reg(cpu, regno, regname, regsize, value);
|
||||
if (!machdep->get_current_task_reg)
|
||||
return FALSE;
|
||||
return machdep->get_current_task_reg(regno, regname, regsize, value);
|
||||
}
|
||||
|
||||
|
|
11
kernel.c
11
kernel.c
|
@ -6508,15 +6508,20 @@ set_cpu(int cpu)
|
|||
if (hide_offline_cpu(cpu))
|
||||
error(FATAL, "invalid cpu number: cpu %d is OFFLINE\n", cpu);
|
||||
|
||||
if ((task = get_active_task(cpu)))
|
||||
set_context(task, NO_PID);
|
||||
task = get_active_task(cpu);
|
||||
|
||||
/* Check if context is already set to given cpu */
|
||||
if (task == CURRENT_TASK())
|
||||
return;
|
||||
|
||||
if (task)
|
||||
set_context(task, NO_PID, TRUE);
|
||||
else
|
||||
error(FATAL, "cannot determine active task on cpu %ld\n", cpu);
|
||||
|
||||
show_context(CURRENT_CONTEXT());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Collect the irq_desc[] entry along with its associated handler and
|
||||
* action structures.
|
||||
|
|
34
task.c
34
task.c
|
@ -120,6 +120,7 @@ static int has_sched_policy(ulong, ulong);
|
|||
static ulong task_policy(ulong);
|
||||
static ulong sched_policy_bit_from_str(const char *);
|
||||
static ulong make_sched_policy(const char *);
|
||||
void crash_get_current_task_info(unsigned long *, char **);
|
||||
|
||||
static struct sched_policy_info {
|
||||
ulong value;
|
||||
|
@ -679,7 +680,7 @@ task_init(void)
|
|||
if (ACTIVE()) {
|
||||
active_pid = REMOTE() ? pc->server_pid :
|
||||
LOCAL_ACTIVE() ? pc->program_pid : 1;
|
||||
set_context(NO_TASK, active_pid);
|
||||
set_context(NO_TASK, active_pid, FALSE);
|
||||
tt->this_task = pid_to_task(active_pid);
|
||||
}
|
||||
else {
|
||||
|
@ -691,7 +692,7 @@ task_init(void)
|
|||
else if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE())
|
||||
map_cpus_to_prstatus_kdump_cmprs();
|
||||
please_wait("determining panic task");
|
||||
set_context(get_panic_context(), NO_PID);
|
||||
set_context(get_panic_context(), NO_PID, TRUE);
|
||||
please_wait_done();
|
||||
}
|
||||
|
||||
|
@ -2992,9 +2993,9 @@ refresh_context(ulong curtask, ulong curpid)
|
|||
struct task_context *tc;
|
||||
|
||||
if (task_exists(curtask) && pid_exists(curpid)) {
|
||||
set_context(curtask, NO_PID);
|
||||
set_context(curtask, NO_PID, FALSE);
|
||||
} else {
|
||||
set_context(tt->this_task, NO_PID);
|
||||
set_context(tt->this_task, NO_PID, FALSE);
|
||||
|
||||
complain = TRUE;
|
||||
if (STREQ(args[0], "set") && (argcnt == 2) &&
|
||||
|
@ -3060,7 +3061,7 @@ sort_context_array(void)
|
|||
curtask = CURRENT_TASK();
|
||||
qsort((void *)tt->context_array, (size_t)tt->running_tasks,
|
||||
sizeof(struct task_context), sort_by_pid);
|
||||
set_context(curtask, NO_PID);
|
||||
set_context(curtask, NO_PID, TRUE);
|
||||
|
||||
sort_context_by_task();
|
||||
}
|
||||
|
@ -3107,7 +3108,7 @@ sort_context_array_by_last_run(void)
|
|||
curtask = CURRENT_TASK();
|
||||
qsort((void *)tt->context_array, (size_t)tt->running_tasks,
|
||||
sizeof(struct task_context), sort_by_last_run);
|
||||
set_context(curtask, NO_PID);
|
||||
set_context(curtask, NO_PID, TRUE);
|
||||
|
||||
sort_context_by_task();
|
||||
}
|
||||
|
@ -5288,12 +5289,16 @@ comm_exists(char *s)
|
|||
* that pid is selected.
|
||||
*/
|
||||
int
|
||||
set_context(ulong task, ulong pid)
|
||||
set_context(ulong task, ulong pid, uint update_gdb_thread)
|
||||
{
|
||||
int i;
|
||||
struct task_context *tc;
|
||||
int found;
|
||||
|
||||
if (CURRENT_CONTEXT() &&
|
||||
(CURRENT_TASK() == task || CURRENT_PID() == pid))
|
||||
return TRUE;
|
||||
|
||||
tc = FIRST_CONTEXT();
|
||||
|
||||
for (i = 0, found = FALSE; i < RUNNING_TASKS(); i++, tc++) {
|
||||
|
@ -5308,7 +5313,12 @@ set_context(ulong task, ulong pid)
|
|||
|
||||
if (found) {
|
||||
CURRENT_CONTEXT() = tc;
|
||||
return TRUE;
|
||||
|
||||
/* change the selected thread in gdb, according to current context */
|
||||
if (update_gdb_thread)
|
||||
return gdb_change_thread_context();
|
||||
else
|
||||
return TRUE;
|
||||
} else {
|
||||
if (task)
|
||||
error(INFO, "cannot set context for task: %lx\n", task);
|
||||
|
@ -11287,3 +11297,11 @@ check_stack_end_magic:
|
|||
if (!total)
|
||||
fprintf(fp, "No stack overflows detected\n");
|
||||
}
|
||||
|
||||
void crash_get_current_task_info(unsigned long *pid, char **comm)
|
||||
{
|
||||
struct task_context *tc = CURRENT_CONTEXT();
|
||||
|
||||
*pid = tc->pid;
|
||||
*comm = tc->comm;
|
||||
}
|
||||
|
|
8
tools.c
8
tools.c
|
@ -1871,7 +1871,7 @@ cmd_set(void)
|
|||
return;
|
||||
|
||||
if (ACTIVE()) {
|
||||
set_context(tt->this_task, NO_PID);
|
||||
set_context(tt->this_task, NO_PID, TRUE);
|
||||
show_context(CURRENT_CONTEXT());
|
||||
return;
|
||||
}
|
||||
|
@ -1880,7 +1880,7 @@ cmd_set(void)
|
|||
error(INFO, "no panic task found!\n");
|
||||
return;
|
||||
}
|
||||
set_context(tt->panic_task, NO_PID);
|
||||
set_context(tt->panic_task, NO_PID, TRUE);
|
||||
show_context(CURRENT_CONTEXT());
|
||||
return;
|
||||
|
||||
|
@ -2559,14 +2559,14 @@ cmd_set(void)
|
|||
case STR_PID:
|
||||
pid = value;
|
||||
task = NO_TASK;
|
||||
if (set_context(task, pid))
|
||||
if (set_context(task, pid, TRUE))
|
||||
show_context(CURRENT_CONTEXT());
|
||||
break;
|
||||
|
||||
case STR_TASK:
|
||||
task = value;
|
||||
pid = NO_PID;
|
||||
if (set_context(task, pid))
|
||||
if (set_context(task, pid, TRUE))
|
||||
show_context(CURRENT_CONTEXT());
|
||||
break;
|
||||
|
||||
|
|
22
x86_64.c
22
x86_64.c
|
@ -126,7 +126,7 @@ static int x86_64_get_framesize(struct bt_info *, ulong, ulong, char *);
|
|||
static void x86_64_framesize_debug(struct bt_info *);
|
||||
static void x86_64_get_active_set(void);
|
||||
static int x86_64_get_kvaddr_ranges(struct vaddr_range *);
|
||||
static int x86_64_get_cpu_reg(int, int, const char *, int, void *);
|
||||
static int x86_64_get_cpu_reg(int, const char *, int, void *);
|
||||
static int x86_64_verify_paddr(uint64_t);
|
||||
static void GART_init(void);
|
||||
static void x86_64_exception_stacks_init(void);
|
||||
|
@ -195,7 +195,7 @@ x86_64_init(int when)
|
|||
machdep->machspec->irq_eframe_link = UNINITIALIZED;
|
||||
machdep->machspec->irq_stack_gap = UNINITIALIZED;
|
||||
machdep->get_kvaddr_ranges = x86_64_get_kvaddr_ranges;
|
||||
machdep->get_cpu_reg = x86_64_get_cpu_reg;
|
||||
machdep->get_current_task_reg = x86_64_get_cpu_reg;
|
||||
if (machdep->cmdline_args[0])
|
||||
parse_cmdline_args();
|
||||
if ((string = pc->read_vmcoreinfo("relocate"))) {
|
||||
|
@ -9077,14 +9077,22 @@ x86_64_get_kvaddr_ranges(struct vaddr_range *vrp)
|
|||
}
|
||||
|
||||
static int
|
||||
x86_64_get_cpu_reg(int cpu, int regno, const char *name,
|
||||
x86_64_get_cpu_reg(int regno, const char *name,
|
||||
int size, void *value)
|
||||
{
|
||||
if (regno >= LAST_REGNUM)
|
||||
return FALSE;
|
||||
struct task_context *tc;
|
||||
|
||||
if (VMSS_DUMPFILE())
|
||||
return vmware_vmss_get_cpu_reg(cpu, regno, name, size, value);
|
||||
tc = CURRENT_CONTEXT();
|
||||
if (!tc)
|
||||
return FALSE;
|
||||
|
||||
if (regno >= LAST_REGNUM)
|
||||
return FALSE;
|
||||
/*
|
||||
* Task is active, grab CPU's registers
|
||||
*/
|
||||
if (is_task_active(tc->task) && VMSS_DUMPFILE())
|
||||
return vmware_vmss_get_cpu_reg(tc->processor, regno, name, size, value);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue