[RFC v2] Use register value in elf note NT_PRSTATUS to do backtrace
by Wang Chao
Hi Dave and list,
I guess you still remember that a few weeks ago Wen has sent a patch which intends
to use register values in NT_PRSTATUS notes for backtracing if no panic task was
found. Thanks for your review and the suggestions are very useful.
However, Wen was busy with other work for these days, so I'll continue with this
work and now the 2rd version patch is attached.
v2: Address review feedbacks.
1) Set up a flag in pc->flags2 and it's determined during the diskdump file init
procedure.
2) Seperate code according to the that flag.
3) Also, we've done some tests on dumpfile of xen kernel and the trouble described
in the previous mail was gone.
So we're looking forward to your feedback and if you still have any problems with
it, please tell us.
Thanks,
Wang Chao
13 years, 6 months
HEAD'S UP -- problem with kernels built with gcc-4.6.0
by Dave Anderson
As a heads-up to those of you who are working with kernels
that were compiled with the new gcc-4.6.0.
I had thought that gcc-4.6.0 was painful only as far as compiling
the crash utility was concerned, where there were a bunch of new
"error: variable <variable> set but not used [-Werror=unused-but-set-variable]
messages that I fixed in crash-5.1.2 and -5.1.3. And you may be aware that
that those for-the-most-part useless warnings recently caused an LKML shitstorm
w/respect to building kernels.
But it's worse than that -- there is a problem with crash's embedded gdb
determining the member offsets of the (large) pglist_data structure if
the kernel was compiled with gcc-4.6.0. This is not specific to the
gdb-7.0 version that is built into crash, but with all gdb
versions as far as I can tell, certainly with gdb-7.2-48.el6
and gdb-7.2.50.20110328-31.fc15.
The problem is most clearly seen with "struct -o pglist_data", which
dumps the structure, showing the offset of each member.
For comparison, here is the output from a (good) 2.6.38-rc4 kernel
that was compiled with gcc-4.5.1:
crash> help -k | grep gcc_version
gcc_version: 4.5.1
crash> struct -o pglist_data
struct pglist_data {
[0x0] struct zone node_zones[4];
[0x1c00] struct zonelist node_zonelists[2];
[0x13e40] int nr_zones;
[0x13e44] spinlock_t node_size_lock;
[0x13e48] long unsigned int node_start_pfn;
[0x13e50] long unsigned int node_present_pages;
[0x13e58] long unsigned int node_spanned_pages;
[0x13e60] int node_id;
[0x13e68] wait_queue_head_t kswapd_wait;
[0x13e80] struct task_struct *kswapd;
[0x13e88] int kswapd_max_order;
[0x13e8c] enum zone_type classzone_idx;
}
SIZE: 0x13f00
crash>
While here is the output from a 2.6.38.2-9.fc15 kernel that
was compiled with gcc-4.6.0:
crash> help -k | grep gcc_version
gcc_version: 4.6.0
crash> struct -o pglist_data
struct pglist_data {
[0x0] struct zone node_zones[4];
[0x1c00] struct zonelist node_zonelists[2];
[0x0] int nr_zones;
[0x0] spinlock_t node_size_lock;
[0x0] long unsigned int node_start_pfn;
[0x0] long unsigned int node_present_pages;
[0x0] long unsigned int node_spanned_pages;
[0x0] int node_id;
[0x0] wait_queue_head_t kswapd_wait;
[0x0] struct task_struct *kswapd;
[0x0] int kswapd_max_order;
[0x0] enum zone_type classzone_idx;
}
SIZE: 0x13f00
crash>
It's interesting that it gets the size correct, but the member offset
values beyond the node_zonelists[] array are returned as 0.
Taking the crash utility out of the picture, the problem can be seen
by simply running "gdb vmlinux".
For example, with the first example above using the good kernel:
$ gdb vmlinux-2.6.38-rc4
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-48.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/vmlinux-2.6.38-rc4...done.
(gdb) ptype struct pglist_data
type = struct pglist_data {
struct zone node_zones[4];
struct zonelist node_zonelists[2];
int nr_zones;
spinlock_t node_size_lock;
long unsigned int node_start_pfn;
long unsigned int node_present_pages;
long unsigned int node_spanned_pages;
int node_id;
wait_queue_head_t kswapd_wait;
struct task_struct *kswapd;
int kswapd_max_order;
enum zone_type classzone_idx;
}
(gdb) p &((struct pglist_data *)(0x0)).node_zonelists[0]
$1 = (struct zonelist *) 0x1c00
(gdb) p &((struct pglist_data *)(0x0)).nr_zones
$2 = (int *) 0x13e40
(gdb) p &((struct pglist_data *)(0x0)).node_size_lock
$3 = (spinlock_t *) 0x13e44
(gdb) p &((struct pglist_data *)(0x0)).node_start_pfn
$4 = (long unsigned int *) 0x13e48
(gdb) p &((struct pglist_data *)(0x0)).node_present_pages
$5 = (long unsigned int *) 0x13e50
(gdb) p &((struct pglist_data *)(0x0)).node_spanned_pages
$6 = (long unsigned int *) 0x13e58
(gdb) p &((struct pglist_data *)(0x0)).node_id
$7 = (int *) 0x13e60
(gdb) p &((struct pglist_data *)(0x0)).kswapd
$8 = (struct task_struct **) 0x13e80
(gdb) p &((struct pglist_data *)(0x0)).kswapd_max_order
$9 = (int *) 0x13e88
(gdb) p &((struct pglist_data *)(0x0)).classzone_idx
$10 = (enum zone_type *) 0x13e8c
(gdb)
And then with the kernel compiled with gcc-4.6.0:
# gdb vmlinux-2.6.38.2-9.fc15
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-48.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/vmlinux-2.6.38.2-9.fc15...done.
(gdb) ptype struct pglist_data
type = struct pglist_data {
struct zone node_zones[4];
struct zonelist node_zonelists[2];
int nr_zones;
spinlock_t node_size_lock;
long unsigned int node_start_pfn;
long unsigned int node_present_pages;
long unsigned int node_spanned_pages;
int node_id;
wait_queue_head_t kswapd_wait;
struct task_struct *kswapd;
int kswapd_max_order;
enum zone_type classzone_idx;
}
(gdb) p &((struct pglist_data *)(0x0)).node_zonelists[0]
$1 = (struct zonelist *) 0x1c00
(gdb) p &((struct pglist_data *)(0x0)).nr_zones
$2 = (int *) 0x0
(gdb) p &((struct pglist_data *)(0x0)).node_size_lock
$3 = (spinlock_t *) 0x0
(gdb) p &((struct pglist_data *)(0x0)).node_start_pfn
$4 = (long unsigned int *) 0x0
(gdb) p &((struct pglist_data *)(0x0)).node_present_pages
$5 = (long unsigned int *) 0x0
(gdb) p &((struct pglist_data *)(0x0)).node_spanned_pages
$6 = (long unsigned int *) 0x0
(gdb) p &((struct pglist_data *)(0x0)).node_id
$7 = (int *) 0x0
(gdb) p &((struct pglist_data *)(0x0)).kswapd_wait
$8 = (wait_queue_head_t *) 0x0
(gdb) p &((struct pglist_data *)(0x0)).kswapd
$9 = (struct task_struct **) 0x0
(gdb) p &((struct pglist_data *)(0x0)).kswapd_max_order
$10 = (int *) 0x0
(gdb) p &((struct pglist_data *)(0x0)).classzone_idx
$11 = (enum zone_type *) 0x0
(gdb)
Anyway, given that the pglist_data structure is crucial to the
crash utility, the bogus offset data generates errors such as
the MEMORY value, as shown here on a 4GB system:
crash> sys
KERNEL: vmlinux-2.6.38.2-9.fc15.gz
DUMPFILE: vmcore.compressed
CPUS: 12
DATE: Thu May 5 16:01:44 2011
UPTIME: 00:02:45
LOAD AVERAGE: 1.26, 0.51, 0.20
TASKS: 171
NODENAME: amd-toonie2-02.lab.bos.redhat.com
RELEASE: 2.6.38.2-9.fc15.x86_64
VERSION: #1 SMP Wed Mar 30 16:55:57 UTC 2011
MACHINE: x86_64 (2400 Mhz)
MEMORY: 680 KB
PANIC: ""
crash>
Bogus "kmem -n" node data gets output:
crash> kmem -n
NODE SIZE PGLIST_DATA BOOTMEM_DATA NODE_ZONES
170 170 ffff88003ffec000 ---- ffff88003ffec000
ffff88003ffec700
ffff88003ffece00
ffff88003ffed500
MEM_MAP START_PADDR START_MAPNR
ffffea0000002530 aa000 170
ZONE NAME SIZE MEM_MAP START_PADDR START_MAPNR
0 DMA 4080 ffffea0000000380 10000 16
1 DMA32 258048 ffffea0000038000 1000000 4096
2 Normal 0 0 0 0
3 Movable 0 0 0 0
...
And on a system configured with CONFIG_SLUB, "kmem -s" fails miserably:
crash> kmem -s
CACHE NAME OBJSIZE ALLOCATED TOTAL SLABS SSIZE
kmem: page_to_nid: cannot determine node for pages: ffffea0000cb2bc0
kmem: page_to_nid: cannot determine node for pages: ffffea0000cece18
ffff880037bd0300 UDPLITEv6 984 0 0 0 32k
kmem: page_to_nid: cannot determine node for pages: ffffea0000cc8640
ffff880037bd0100 tw_sock_TCPv6 280 0 0 0 8k
kmem: page_to_nid: cannot determine node for pages: ffffea0000cd70c0
kmem: page_to_nid: cannot determine node for pages: ffffea0000c6bb90
kmem: page_to_nid: cannot determine node for pages: ffffea0000ca5bf0
ffff880037a7f100 dm_raid1_read_record 1064 0 0 0 32k
ffff880037a7f000 kcopyd_job 368 0 0 0 8k
ffff880037a7ef00 dm_uevent 2608 0 0 0 32k
ffff880037a7ee00 dm_rq_target_io 400 0 0 0 8k
kmem: page_to_nid: cannot determine node for pages: ffffea0000c304e0
...
And there may be other problems that I'm not aware of that are associated
with the pglist_data data structure members specifically -- and perhaps with
other data structures as well?
I filed a bugzilla with gdb, although it may likely be a bug with
the debuginfo data created by gcc-4.6.0. We'll see what happens...
In the meantime, I do have a workaround kludge for pglist_data members that
will be included in the upcoming crash-5.1.5 release.
Annoyed to no end,
Dave
13 years, 6 months
[PATCH] s390: Fix stack trace code for program checks
by Michael Holzheu
Hi Dave,
If we get a program check interrupt while we are on the task stack, crash
only shows the stack trace up to the program check interrupt.
Example:
#0 [3d2bb6e0] raw3215_make_room at 3e2c90
#1 [3d2bb730] con3215_notify at 3e3a26
#2 [3d2bb760] notifier_call_chain at 4c31d0
#3 [3d2bb7b8] atomic_notifier_call_chain at 4c3278
#4 [3d2bb810] panic at 4be372
#5 [3d2bb8b8] die at 10565e
#6 [3d2bb920] illegal_op at 106ed6
#7 [3d2bba10] pgm_exit at 1185fc
With this patch the stack trace looks like the following:
#00 [3d2bb6e0] raw3215_make_room at 3e2c90
#01 [3d2bb730] con3215_notify at 3e3a26
#02 [3d2bb760] notifier_call_chain at 4c31d0
#03 [3d2bb7b8] atomic_notifier_call_chain at 4c3278
#04 [3d2bb810] panic at 4be372
#05 [3d2bb8b8] die at 10565e
#06 [3d2bb920] illegal_op at 106ed6
#07 [3d2bba10] pgm_exit at 1185fc
- Interrupt -
#08 [3d2bbab0] rollback_registered at 402d82
#09 [3d2bbb78] unregister_netdevice at 402e04
#10 [3d2bbb98] unregister_netdev at 402e7a
#11 [3d2bbbb8] qeth_l3_remove_device at 3c000fc2574 [qeth_l3]
#12 [3d2bbc38] qeth_core_remove_device at 3c000d77d08 [qeth]
#13 [3d2bbc88] ccwgroup_remove at 3c000cc483a [ccwgroup]
#14 [3d2bbca8] __device_release_driver at 39c180
#15 [3d2bbcd0] device_release_driver at 39c33a
#16 [3d2bbcf8] bus_remove_device at 39b2e0
#17 [3d2bbd28] device_del at 398a8e
#18 [3d2bbd58] device_unregister at 398b72
#19 [3d2bbd78] ccwgroup_ungroup_callback at 3c000cc47c4 [ccwgroup]
#20 [3d2bbdb0] sysfs_schedule_callback_work at 2d0802
#21 [3d2bbdd0] worker_thread at 166f08
#22 [3d2bbe60] kthread at 16cfe8
#23 [3d2bbeb8] kernel_thread_starter at 109c06
For fixing this problem I also did some more rework/restructuring of the code. I tested
the fix with RHEL6 and verified that back traces still work on SLES10-11 and
RHEL4-6.
Michael
---
defs.h | 3
s390x.c | 311 +++++++++++++++++++++++++++++++++++-----------------------------
2 files changed, 176 insertions(+), 138 deletions(-)
--- a/defs.h
+++ b/defs.h
@@ -1548,6 +1548,8 @@ struct offset_table {
long module_sections_attrs;
long swap_info_struct_inuse_pages;
long s390_lowcore_psw_save_area;
+ long s390_stack_frame_back_chain;
+ long s390_stack_frame_r14;
long mm_struct_rss_stat;
long mm_rss_stat_count;
long module_module_init;
@@ -1688,6 +1690,7 @@ struct size_table { /* stash of
long unwind_idx;
long softirq_action;
long irq_data;
+ long s390_stack_frame;
};
struct array_table {
--- a/s390x.c
+++ b/s390x.c
@@ -42,6 +42,8 @@
#define LOWCORE_SIZE 8192
+#define S390X_PSW_MASK_PSTATE 0x0001000000000000UL
+
/*
* S390x prstatus ELF Note
*/
@@ -113,7 +115,17 @@ static struct line_number_hook s390x_lin
static int s390x_is_uvaddr(ulong, struct task_context *);
static int s390x_get_kvaddr_ranges(struct vaddr_range *);
-
+/*
+ * Read a unsigned long value from address
+ */
+static unsigned long readmem_ul(unsigned long addr)
+{
+ unsigned long rc;
+
+ readmem(addr, KVADDR, &rc, sizeof(rc), "readmem_ul", FAULT_ON_ERROR);
+ return rc;
+}
+
/*
* Initialize member offsets
*/
@@ -125,6 +137,17 @@ static void s390x_offsets_init(void)
else
MEMBER_OFFSET_INIT(s390_lowcore_psw_save_area, "_lowcore",
"psw_save_area");
+ if (!STRUCT_EXISTS("stack_frame")) {
+ ASSIGN_OFFSET(s390_stack_frame_back_chain) = 0;
+ ASSIGN_OFFSET(s390_stack_frame_r14) = 112;
+ ASSIGN_SIZE(s390_stack_frame) = 160;
+ } else {
+ ASSIGN_OFFSET(s390_stack_frame_back_chain) =
+ MEMBER_OFFSET("stack_frame", "back_chain");
+ ASSIGN_OFFSET(s390_stack_frame_r14) =
+ MEMBER_OFFSET("stack_frame", "gprs") + 8 * 8;
+ ASSIGN_SIZE(s390_stack_frame) = STRUCT_SIZE("stack_frame");
+ }
}
static struct s390x_cpu *s390x_cpu_vec;
@@ -796,39 +819,152 @@ s390x_get_lowcore(struct bt_info *bt, ch
/*
* Read interrupt stack (either "async_stack" or "panic_stack");
*/
-static void s390x_get_int_stack(char *stack_name, char* lc, char* int_stack,
- unsigned long* start, unsigned long* end)
+static void get_int_stack(char *stack_name, char *lc, unsigned long *start,
+ unsigned long *end)
{
unsigned long stack_addr;
+ *start = *end = 0;
if (!MEMBER_EXISTS("_lowcore", stack_name))
return;
stack_addr = ULONG(lc + MEMBER_OFFSET("_lowcore", stack_name));
if (stack_addr == 0)
return;
- readmem(stack_addr - INT_STACK_SIZE, KVADDR, int_stack,
- INT_STACK_SIZE, stack_name, FAULT_ON_ERROR);
*start = stack_addr - INT_STACK_SIZE;
*end = stack_addr;
}
/*
- * Unroll a kernel stack.
+ * Print hex data
*/
-static void
-s390x_back_trace_cmd(struct bt_info *bt)
+static void print_hex(unsigned long addr, int len, int cols)
{
- char* stack;
- char async_stack[INT_STACK_SIZE];
- char panic_stack[INT_STACK_SIZE];
- long ksp,backchain,old_backchain;
- int i=0, r14_offset,bc_offset, skip_first_frame=0;
- unsigned long async_start = 0, async_end = 0;
- unsigned long panic_start = 0, panic_end = 0;
- unsigned long stack_end, stack_start, stack_base;
- unsigned long r14;
- char buf[BUFSIZE];
- int cpu = bt->tc->processor;
+ int j, first = 1;
+
+ for (j = 0; j < len; j += 8) {
+ if (j % (cols * 8) == 0) {
+ if (!first)
+ fprintf(fp, "\n");
+ else
+ first = 0;
+ fprintf(fp, " %016lx: ", addr + j);
+ }
+ fprintf(fp, " %016lx", readmem_ul(addr + j));
+ }
+ if (len)
+ fprintf(fp, "\n");
+}
+
+/*
+ * Print hexdump of stack frame data
+ */
+static void print_frame_data(unsigned long sp, unsigned long high)
+{
+ unsigned long next_sp, len = high - sp;
+
+ next_sp = readmem_ul(sp + MEMBER_OFFSET("stack_frame", "back_chain"));
+ if (next_sp == 0)
+ len = MIN(len, SIZE(s390_stack_frame) + STRUCT_SIZE("pt_regs"));
+ else
+ len = MIN(len, next_sp - sp);
+ print_hex(sp, len, 2);
+}
+
+/*
+ * Print stack frame
+ */
+static void print_frame(struct bt_info *bt, int cnt, unsigned long sp,
+ unsigned long r14)
+{
+ struct load_module *lm;
+ char *sym;
+
+ if (BT_REFERENCE_CHECK(bt)) {
+ if (bt->ref->cmdflags & BT_REF_HEXVAL) {
+ if (r14 == bt->ref->hexval)
+ bt->ref->cmdflags |= BT_REF_FOUND;
+ } else {
+ if (STREQ(closest_symbol(r14), bt->ref->str))
+ bt->ref->cmdflags |= BT_REF_FOUND;
+ }
+ return;
+ }
+ fprintf(fp, " #%02i [%08lx] ", cnt, sp);
+ sym = closest_symbol(r14);
+ fprintf(fp, "%s at %lx", sym, r14);
+ if (module_symbol(r14, NULL, &lm, NULL, 0))
+ fprintf(fp, " [%s]", lm->mod_name);
+ fprintf(fp, "\n");
+ if (bt->flags & BT_LINE_NUMBERS)
+ s390x_dump_line_number(r14);
+}
+
+/*
+ * Print back trace for one stack
+ */
+static unsigned long show_trace(struct bt_info *bt, int cnt, unsigned long sp,
+ unsigned long low, unsigned long high)
+{
+ unsigned long reg, psw_addr;
+
+ while (1) {
+ if (sp < low || sp > high - SIZE(s390_stack_frame))
+ return sp;
+ reg = readmem_ul(sp + OFFSET(s390_stack_frame_r14));
+ if (!s390x_has_cpu(bt))
+ print_frame(bt, cnt++, sp, reg);
+ if (bt->flags & BT_FULL)
+ print_frame_data(sp, high);
+ /* Follow the backchain. */
+ while (1) {
+ low = sp;
+ sp = readmem_ul(sp +
+ OFFSET(s390_stack_frame_back_chain));
+ if (!sp) {
+ sp = low;
+ break;
+ }
+ if (sp <= low || sp > high - SIZE(s390_stack_frame))
+ return sp;
+ reg = readmem_ul(sp + OFFSET(s390_stack_frame_r14));
+ print_frame(bt, cnt++, sp, reg);
+ if (bt->flags & BT_FULL)
+ print_frame_data(sp, high);
+ }
+ /* Zero backchain detected, check for interrupt frame. */
+ sp += SIZE(s390_stack_frame);
+ if (sp <= low || sp > high - STRUCT_SIZE("pt_regs"))
+ return sp;
+ /* Check for user PSW */
+ reg = readmem_ul(sp + MEMBER_OFFSET("pt_regs", "psw"));
+ if (reg & S390X_PSW_MASK_PSTATE)
+ return sp;
+ /* Get new backchain from r15 */
+ reg = readmem_ul(sp + MEMBER_OFFSET("pt_regs", "gprs") +
+ 15 * sizeof(long));
+ /* Get address of interrupted function */
+ psw_addr = readmem_ul(sp + MEMBER_OFFSET("pt_regs", "psw") +
+ sizeof(long));
+ /* Check for loop (kernel_thread_starter) of second zero bc */
+ if (low == reg || reg == 0)
+ return reg;
+ fprintf(fp, " - Interrupt -\n");
+ print_frame(bt, cnt++, sp, psw_addr);
+ low = sp;
+ sp = reg;
+ cnt = 0;
+ }
+}
+
+/*
+ * Unroll a kernel stack
+ */
+static void s390x_back_trace_cmd(struct bt_info *bt)
+{
+ unsigned long low, high, sp = bt->stkptr;
+ int cpu = bt->tc->processor, cnt = 0;
+ char lowcore[LOWCORE_SIZE];
+ unsigned long psw_flags;
if (bt->hp && bt->hp->eip) {
error(WARNING,
@@ -838,13 +974,11 @@ s390x_back_trace_cmd(struct bt_info *bt)
fprintf(fp, " CPU offline\n");
return;
}
- ksp = bt->stkptr;
-
- /* print lowcore and get async stack when task has cpu */
- if(s390x_has_cpu(bt)){
- char lowcore[LOWCORE_SIZE];
- unsigned long psw_flags;
+ /*
+ * Print lowcore and print interrupt stacks when task has cpu
+ */
+ if (s390x_has_cpu(bt)) {
if (ACTIVE()) {
fprintf(fp,"(active)\n");
return;
@@ -852,128 +986,29 @@ s390x_back_trace_cmd(struct bt_info *bt)
s390x_get_lowcore(bt, lowcore);
psw_flags = ULONG(lowcore + OFFSET(s390_lowcore_psw_save_area));
- if(psw_flags & 0x1000000000000ULL){
+ if (psw_flags & S390X_PSW_MASK_PSTATE) {
fprintf(fp,"Task runs in userspace\n");
s390x_print_lowcore(lowcore,bt,0);
return;
}
- s390x_get_int_stack("async_stack", lowcore, async_stack,
- &async_start, &async_end);
- s390x_get_int_stack("panic_stack", lowcore, panic_stack,
- &panic_start, &panic_end);
s390x_print_lowcore(lowcore,bt,1);
fprintf(fp,"\n");
- skip_first_frame=1;
+ get_int_stack("panic_stack", lowcore, &low, &high);
+ sp = show_trace(bt, cnt, sp, low, high);
+ get_int_stack("async_stack", lowcore, &low, &high);
+ sp = show_trace(bt, cnt, sp, low, high);
}
-
- /* get task stack start and end */
- if(THIS_KERNEL_VERSION >= LINUX(2,6,0)){
- readmem(bt->task + OFFSET(task_struct_thread_info),KVADDR,
- &stack_start, sizeof(long), "thread info",
- FAULT_ON_ERROR);
+ /*
+ * Print task stack
+ */
+ if (THIS_KERNEL_VERSION >= LINUX(2, 6, 0)) {
+ readmem(bt->task + OFFSET(task_struct_thread_info), KVADDR,
+ &low, sizeof(long), "thread info", FAULT_ON_ERROR);
} else {
- stack_start = bt->task;
+ low = bt->task;
}
- stack_end = stack_start + KERNEL_STACK_SIZE;
-
- if(!STRUCT_EXISTS("stack_frame")){
- r14_offset = 112;
- bc_offset=0;
- } else {
- r14_offset = MEMBER_OFFSET("stack_frame","gprs") +
- 8 * S390X_WORD_SIZE;
- bc_offset = MEMBER_OFFSET("stack_frame","back_chain");
- }
- backchain = ksp;
- do {
- unsigned long r14_stack_off;
- struct load_module *lm;
- int j;
-
- /* Find stack: Either async, panic stack or task stack */
- if((backchain > stack_start) && (backchain < stack_end)){
- stack = bt->stackbuf;
- stack_base = stack_start;
- } else if((backchain > async_start) && (backchain < async_end)
- && s390x_has_cpu(bt)){
- stack = async_stack;
- stack_base = async_start;
- } else if((backchain > panic_start) && (backchain < panic_end)
- && s390x_has_cpu(bt)){
- stack = panic_stack;
- stack_base = panic_start;
- } else {
- /* invalid stackframe */
- break;
- }
- r14_stack_off=backchain - stack_base + r14_offset;
- r14 = ULONG(&stack[r14_stack_off]);
-
- /* print function name */
- if(BT_REFERENCE_CHECK(bt)){
- if(bt->ref->cmdflags & BT_REF_HEXVAL){
- if(r14 == bt->ref->hexval)
- bt->ref->cmdflags |= BT_REF_FOUND;
- } else {
- if(STREQ(closest_symbol(r14),bt->ref->str))
- bt->ref->cmdflags |= BT_REF_FOUND;
- }
- } else if(skip_first_frame){
- skip_first_frame=0;
- } else {
- fprintf(fp," #%i [%08lx] ",i,backchain);
- fprintf(fp,"%s at %lx", closest_symbol(r14), r14);
- if (module_symbol(r14, NULL, &lm, NULL, 0))
- fprintf(fp, " [%s]", lm->mod_name);
- fprintf(fp, "\n");
- if (bt->flags & BT_LINE_NUMBERS)
- s390x_dump_line_number(r14);
- i++;
- }
- old_backchain=backchain;
- backchain = ULONG(&stack[backchain - stack_base + bc_offset]);
-
- /* print stack content if -f is specified */
- if ((bt->flags & BT_FULL) && !BT_REFERENCE_CHECK(bt)) {
- int frame_size;
- if (backchain == 0) {
- frame_size = stack_base - old_backchain
- + KERNEL_STACK_SIZE;
- } else {
- frame_size = MIN((backchain - old_backchain),
- (stack_base - old_backchain +
- KERNEL_STACK_SIZE));
- }
- for (j = 0; j < frame_size; j += 8) {
- if(j % 16 == 0){
- fprintf(fp, "%s %016lx: ",
- j ? "\n" : "", old_backchain + j);
- }
- fprintf(fp," %s",
- format_stack_entry(bt, buf,
- ULONG(&stack[old_backchain - stack_base + j]), 0));
- }
- fprintf(fp, "\n");
- }
-
- /* Check for interrupt stackframe */
- if((backchain == 0) &&
- (stack == async_stack || stack == panic_stack)) {
- int pt_regs_off = old_backchain - stack_base + 160;
- unsigned long psw_flags;
-
- psw_flags = ULONG(&stack[pt_regs_off +
- MEMBER_OFFSET("pt_regs", "psw")]);
- if(psw_flags & 0x1000000000000ULL){
- /* User psw: should not happen */
- break;
- }
- backchain = ULONG(&stack[pt_regs_off +
- MEMBER_OFFSET("pt_regs", "gprs") +
- 15 * S390X_WORD_SIZE]);
- fprintf(fp," - Interrupt -\n");
- }
- } while(backchain != 0);
+ high = low + KERNEL_STACK_SIZE;
+ sp = show_trace(bt, cnt, sp, low, high);
}
/*
13 years, 6 months