summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Hunt <hunt@redhat.com>2008-03-25 12:26:46 -0400
committerMartin Hunt <hunt@redhat.com>2008-03-25 12:26:46 -0400
commitb56639bb84656464efbf88912f68c36e7f099d49 (patch)
tree81a92638ed92906dd4ba5cff6dd869b5ea3ab23e
parent874381d3fc30dddd9f8584f3f6a9a36a1a4ad9df (diff)
downloadsystemtap-steved-b56639bb84656464efbf88912f68c36e7f099d49.tar.gz
systemtap-steved-b56639bb84656464efbf88912f68c36e7f099d49.tar.xz
systemtap-steved-b56639bb84656464efbf88912f68c36e7f099d49.zip
Cleanup.
-rw-r--r--.gitignore1
-rw-r--r--runtime/ChangeLog5
-rw-r--r--runtime/stack-x86_64.c7
-rw-r--r--runtime/unwind.c107
-rw-r--r--runtime/unwind/i386.h11
-rw-r--r--runtime/unwind/x86_64.h12
6 files changed, 91 insertions, 52 deletions
diff --git a/.gitignore b/.gitignore
index 1c02452b..90261357 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ CVS
.metadata
.project
.settings
+*.o
diff --git a/runtime/ChangeLog b/runtime/ChangeLog
index b52ddf7b..bea8b3a3 100644
--- a/runtime/ChangeLog
+++ b/runtime/ChangeLog
@@ -1,3 +1,8 @@
+2008-03-25 Martin Hunt <hunt@redhat.com>
+
+ * unwind.c (unwind): Return a positive number to indicate
+ that unwinding is done.
+
2008-03-17 Eugene Teo <eteo@redhat.com>
PR 5947
diff --git a/runtime/stack-x86_64.c b/runtime/stack-x86_64.c
index 3ba99201..e2eb4aa2 100644
--- a/runtime/stack-x86_64.c
+++ b/runtime/stack-x86_64.c
@@ -30,16 +30,15 @@ static void __stp_stack_print(struct pt_regs *regs, int verbose, int levels)
struct unwind_frame_info info;
arch_unw_init_frame_info(&info, regs);
- /* we haven't actually executed the instruction at the IP yet. */
- UNW_PC(&info) -= 1;
-
while (!arch_unw_user_mode(&info)) {
int ret = unwind(&info);
- dbug_unwind(1, "ret=%d PC=%lx\n", ret, UNW_PC(&info));
+ dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info));
if (ret < 0) {
_stp_stack_print_fallback(UNW_SP(&info), verbose);
break;
}
+ if (ret)
+ break;
_stp_func_print(UNW_PC(&info), verbose, 1);
}
}
diff --git a/runtime/unwind.c b/runtime/unwind.c
index 9a5d61e8..b6c9fd75 100644
--- a/runtime/unwind.c
+++ b/runtime/unwind.c
@@ -1,12 +1,16 @@
-/*
+/* -*- linux-c -*-
+ * kernel stack unwinding
+ * Copyright (C) 2008 Red Hat Inc.
+ *
+ * Based on old kernel code that is
* Copyright (C) 2002-2006 Novell, Inc.
* Jan Beulich <jbeulich@novell.com>
+ *
* This code is released under version 2 of the GNU GPL.
*
- * A simple API for unwinding kernel stacks. This is used for
- * debugging and error reporting purposes. The kernel doesn't need
- * full-blown stack unwinding with all the bells and whistles, so there
- * is not much point in implementing the full Dwarf2 unwind API.
+ * This code currently does stack unwinding in the
+ * kernel and modules. It will need some extension to handle
+ * userspace unwinding.
*/
#include "unwind/unwind.h"
@@ -231,8 +235,8 @@ static const u32 *cie_for_fde(const u32 *fde, const struct _stp_module *m)
/* CIE_pointer must be a proper offset */
if ((fde[1] & (sizeof(*fde) - 1)) || fde[1] > (unsigned long)(fde + 1) - (unsigned long)m->unwind_data) {
dbug_unwind(1, "fde[1]=%lx fde+1=%lx, unwind_data=%lx %lx\n",
- (unsigned long)fde[1], (unsigned long)(fde + 1),
- (unsigned long)m->unwind_data, (unsigned long)(fde + 1) - (unsigned long)m->unwind_data);
+ (unsigned long)fde[1], (unsigned long)(fde + 1),
+ (unsigned long)m->unwind_data, (unsigned long)(fde + 1) - (unsigned long)m->unwind_data);
return NULL; /* this is not a valid FDE */
}
@@ -400,6 +404,7 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, s
} ptr;
int result = 1;
+ dbug_unwind(1, "targetLoc=%lx state->loc=%lx\n", targetLoc, state->loc);
if (start != state->cieStart) {
state->loc = state->org;
result = processCFI(state->cieStart, state->cieEnd, 0, ptrType, state);
@@ -542,6 +547,7 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, s
dbug_unwind(1, "case 3\n");
break;
}
+ dbug_unwind(1, "targetLoc=%lx state->loc=%lx\n", targetLoc, state->loc);
if (ptr.p8 > end)
result = 0;
if (result && targetLoc != 0 && targetLoc < state->loc)
@@ -560,9 +566,6 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, struct _stp_module *m)
u32 *fde = NULL;
unsigned num, tableSize, t2;
- /* TOTO: only really need to test hdr == NULL */
- /* The rest should be validated at load time */
-
if (hdr == NULL || hdr[0] != 1)
return NULL;
@@ -614,12 +617,13 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, struct _stp_module *m)
if (num == 1 && (startLoc = read_pointer(&ptr, ptr + tableSize, hdr[3])) != 0 && pc >= startLoc)
fde = (void *)read_pointer(&ptr, ptr + tableSize, hdr[3]);
- dbug_unwind(1, "returning %lx", fde);
+ dbug_unwind(1, "returning fde=%lx startLoc=%lx", fde, startLoc);
return fde;
}
/* Unwind to previous to frame. Returns 0 if successful, negative
- * number in case of an error. */
+ * number in case of an error. A positive return means unwinding is finished; */
+/* don't try to fallback to dumping addresses on the stack. */
int unwind(struct unwind_frame_info *frame)
{
#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
@@ -646,32 +650,36 @@ int unwind(struct unwind_frame_info *frame)
if (unlikely(m->unwind_data_len == 0 || m->unwind_data_len & (sizeof(*fde) - 1))) {
dbug_unwind(1, "Module %s: unwind_data_len=%d", m->name, m->unwind_data_len);
- goto done;
+ goto err;
}
fde = _stp_search_unwind_hdr(pc, m);
dbug_unwind(1, "%s: fde=%lx\n", m->name, fde);
+ /* found the fde, now set startLoc and endLoc */
if (fde != NULL) {
cie = cie_for_fde(fde, m);
ptr = (const u8 *)(fde + 2);
- if (cie != NULL
- && cie != &bad_cie
- && cie != &not_fde
- && (ptrType = fde_pointer_type(cie)) >= 0
- && read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType) == startLoc) {
+ if (likely(cie != NULL && cie != &bad_cie && cie != &not_fde)) {
+ ptrType = fde_pointer_type(cie);
+ startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType);
if (!(ptrType & DW_EH_PE_indirect))
ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed;
endLoc = startLoc + read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType);
- if (pc >= endLoc)
- fde = NULL;
- } else
+ if (pc > endLoc) {
+ dbug_unwind(1, "pc (%lx) > endLoc(%x)\n", pc, endLoc);
+ /* finished? */
+ goto done;
+ }
+ } else {
+ dbug_unwind(1, "fde found in header, but cie is bad!\n");
fde = NULL;
+ }
}
if (fde == NULL) {
for (fde = m->unwind_data, tableSize = m->unwind_data_len; cie = NULL, tableSize > sizeof(*fde)
&& tableSize - sizeof(*fde) >= *fde; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) {
- // dbug_unwind(1,"fde=%lx tableSize=%d\n", (long)*fde, (int)tableSize);
+ dbug_unwind(3, "fde=%lx tableSize=%d\n", (long)*fde, (int)tableSize);
cie = cie_for_fde(fde, m);
if (cie == &bad_cie) {
cie = NULL;
@@ -682,13 +690,13 @@ int unwind(struct unwind_frame_info *frame)
ptr = (const u8 *)(fde + 2);
startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType);
- // dbug_unwind(1,"startLoc=%p\n",(u64)startLoc);
+ dbug_unwind(3, "startLoc=%p\n", (u64)startLoc);
if (!startLoc)
continue;
if (!(ptrType & DW_EH_PE_indirect))
ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed;
endLoc = startLoc + read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType);
- // dbug_unwind(1,"endLoc=%p\n",(u64)endLoc);
+ dbug_unwind(3, "endLoc=%p\n", (u64)endLoc);
if (pc >= startLoc && pc < endLoc)
break;
}
@@ -696,7 +704,7 @@ int unwind(struct unwind_frame_info *frame)
dbug_unwind(1, "cie=%lx fde=%lx startLoc=%lx endLoc=%lx\n", cie, fde, startLoc, endLoc);
if (cie == NULL || fde == NULL)
- goto done;
+ goto err;
/* found the CIE and FDE */
@@ -707,7 +715,7 @@ int unwind(struct unwind_frame_info *frame)
frame->call_frame = 1;
if ((state.version = *ptr) != 1) {
dbug_unwind(1, "CIE version number is %d. 1 is supported.\n", state.version);
- goto done; /* unsupported version */
+ goto err; /* unsupported version */
}
if (*++ptr) {
/* check if augmentation size is first (and thus present) */
@@ -731,7 +739,7 @@ int unwind(struct unwind_frame_info *frame)
}
if (ptr >= end || *ptr) {
dbug_unwind(1, "Problem parsing the augmentation string.\n");
- goto done;
+ goto err;
}
}
++ptr;
@@ -741,7 +749,7 @@ int unwind(struct unwind_frame_info *frame)
/* get data aligment factor */
state.dataAlign = get_sleb128(&ptr, end);
if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end)
- goto done;;
+ goto err;;
retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end);
@@ -753,7 +761,7 @@ int unwind(struct unwind_frame_info *frame)
if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info)
|| REG_INVALID(retAddrReg)
|| reg_info[retAddrReg].width != sizeof(unsigned long))
- goto done;
+ goto err;
state.cieStart = ptr;
ptr = state.cieEnd;
@@ -764,7 +772,7 @@ int unwind(struct unwind_frame_info *frame)
if (((const char *)(cie + 2))[1] == 'z') {
uleb128_t augSize = get_uleb128(&ptr, end);
if ((ptr += augSize) > end)
- goto done;
+ goto err;
}
state.org = startLoc;
@@ -774,7 +782,7 @@ int unwind(struct unwind_frame_info *frame)
|| state.loc > endLoc || state.regs[retAddrReg].where == Nowhere || state.cfa.reg >= ARRAY_SIZE(reg_info)
|| reg_info[state.cfa.reg].width != sizeof(unsigned long)
|| state.cfa.offs % sizeof(unsigned long))
- goto done;
+ goto err;
/* update frame */
dbug_unwind(1, "cie=%lx fde=%lx\n", cie, fde);
@@ -801,10 +809,10 @@ int unwind(struct unwind_frame_info *frame)
if (REG_INVALID(i)) {
if (state.regs[i].where == Nowhere)
continue;
- dbug_unwind(1, "REG_INVALID %d\n", i);
- goto done;
+ dbug_unwind(2, "REG_INVALID %d\n", i);
+ goto err;
}
- dbug_unwind(1, "register %d. where=%d\n", i, state.regs[i].where);
+ dbug_unwind(2, "register %d. where=%d\n", i, state.regs[i].where);
switch (state.regs[i].where) {
default:
break;
@@ -812,8 +820,8 @@ int unwind(struct unwind_frame_info *frame)
if (state.regs[i].value >= ARRAY_SIZE(reg_info)
|| REG_INVALID(state.regs[i].value)
|| reg_info[i].width > reg_info[state.regs[i].value].width) {
- dbug_unwind(1, "case Register bad\n");
- goto done;
+ dbug_unwind(2, "case Register bad\n");
+ goto err;
}
switch (reg_info[state.regs[i].value].width) {
#define CASE(n) \
@@ -824,17 +832,17 @@ int unwind(struct unwind_frame_info *frame)
CASES;
#undef CASE
default:
- dbug_unwind(1, "default\n");
- goto done;
+ dbug_unwind(2, "default\n");
+ goto err;
}
break;
}
}
for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
- dbug_unwind(1, "register %d. invalid=%d\n", i, REG_INVALID(i));
+ dbug_unwind(2, "register %d. invalid=%d\n", i, REG_INVALID(i));
if (REG_INVALID(i))
continue;
- dbug_unwind(1, "register %d. where=%d\n", i, state.regs[i].where);
+ dbug_unwind(2, "register %d. where=%d\n", i, state.regs[i].where);
switch (state.regs[i].where) {
case Nowhere:
if (reg_info[i].width != sizeof(UNW_SP(frame))
@@ -851,20 +859,20 @@ int unwind(struct unwind_frame_info *frame)
CASES;
#undef CASE
default:
- dbug_unwind(1, "default\n");
- goto done;
+ dbug_unwind(2, "default\n");
+ goto err;
}
break;
case Value:
if (reg_info[i].width != sizeof(unsigned long)) {
- dbug_unwind(1, "Value\n");
- goto done;
+ dbug_unwind(2, "Value\n");
+ goto err;
}
FRAME_REG(i, unsigned long) = cfa + state.regs[i].value * state.dataAlign;
break;
case Memory:{
unsigned long addr = cfa + state.regs[i].value * state.dataAlign;
- dbug_unwind(1, "addr=%lx width=%d\n", addr, reg_info[i].width);
+ dbug_unwind(2, "addr=%lx width=%d\n", addr, reg_info[i].width);
switch (reg_info[i].width) {
#define CASE(n) case sizeof(u##n): \
if (unlikely(__stp_get_user(FRAME_REG(i, u##n), (u##n *)addr))) \
@@ -874,8 +882,8 @@ int unwind(struct unwind_frame_info *frame)
CASES;
#undef CASE
default:
- dbug_unwind(1, "default\n");
- goto done;
+ dbug_unwind(2, "default\n");
+ goto err;
}
}
break;
@@ -887,9 +895,12 @@ int unwind(struct unwind_frame_info *frame)
copy_failed:
dbug_unwind(1, "_stp_get_user failed to access memory\n");
-done:
+err:
read_unlock(&m->lock);
return -EIO;
+done:
+ read_unlock(&m->lock);
+ return 1;
#undef CASES
#undef FRAME_REG
}
diff --git a/runtime/unwind/i386.h b/runtime/unwind/i386.h
index de68c67b..1a6b678b 100644
--- a/runtime/unwind/i386.h
+++ b/runtime/unwind/i386.h
@@ -1,3 +1,14 @@
+/* -*- linux-c -*-
+ *
+ * 32-bit x86 dwarf unwinder header file
+ * Copyright (C) 2008 Red Hat Inc.
+ * Copyright (C) 2002-2006 Novell, Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
#ifndef _STP_I386_UNWIND_H
#define _STP_I386_UNWIND_H
diff --git a/runtime/unwind/x86_64.h b/runtime/unwind/x86_64.h
index eddf276d..3c4a97be 100644
--- a/runtime/unwind/x86_64.h
+++ b/runtime/unwind/x86_64.h
@@ -1,3 +1,14 @@
+/* -*- linux-c -*-
+ *
+ * x86_64 dwarf unwinder header file
+ * Copyright (C) 2008 Red Hat Inc.
+ * Copyright (C) 2002-2006 Novell, Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
#ifndef _STP_X86_64_UNWIND_H
#define _STP_X86_64_UNWIND_H
@@ -67,6 +78,7 @@ static inline void arch_unw_init_frame_info(struct unwind_frame_info *info,
/*const*/ struct pt_regs *regs)
{
info->regs = *regs;
+ info->call_frame = 1;
}
static inline void arch_unw_init_blocked(struct unwind_frame_info *info)