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:
Tao Liu 2024-09-04 19:49:31 +12:00 committed by Lianbo Jiang
parent 7c8a7dddda
commit 1fd80c623c
7 changed files with 109 additions and 77 deletions

View File

@ -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 *)&regval))
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 *)&regval))
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
View File

@ -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 */

View File

@ -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);
}

View File

@ -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
View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}