提交 6d0185ea 编写于 作者: J Jan Beulich 提交者: Andi Kleen

[PATCH] unwinder: Add debugging output to the Dwarf2 unwinder

Add debugging printks to the unwinder to allow easier debugging
when something goes wrong with it.

This can be controlled with the new unwinder_debug=N option
Most output is given by N=1

AK: Added documentation of unwinder_debug=
Signed-off-by: NJan Beulich <jbeulich@novell.com>
Signed-off-by: NAndi Kleen <ak@suse.de>
上级 3807fd46
...@@ -1735,6 +1735,9 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -1735,6 +1735,9 @@ and is between 256 and 4096 characters. It is defined in the file
norandmaps Don't use address space randomization norandmaps Don't use address space randomization
Equivalent to echo 0 > /proc/sys/kernel/randomize_va_space Equivalent to echo 0 > /proc/sys/kernel/randomize_va_space
unwind_debug=N N > 0 will enable dwarf2 unwinder debugging
This is useful to get more information why
you got a "dwarf2 unwinder stuck"
______________________________________________________________________ ______________________________________________________________________
......
...@@ -137,6 +137,17 @@ struct unwind_state { ...@@ -137,6 +137,17 @@ struct unwind_state {
static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 };
static unsigned unwind_debug;
static int __init unwind_debug_setup(char *s)
{
unwind_debug = simple_strtoul(s, NULL, 0);
return 1;
}
__setup("unwind_debug=", unwind_debug_setup);
#define dprintk(lvl, fmt, args...) \
((void)(lvl > unwind_debug \
|| printk(KERN_DEBUG "unwind: " fmt "\n", ##args)))
static struct unwind_table *find_table(unsigned long pc) static struct unwind_table *find_table(unsigned long pc)
{ {
struct unwind_table *table; struct unwind_table *table;
...@@ -281,6 +292,7 @@ static void __init setup_unwind_table(struct unwind_table *table, ...@@ -281,6 +292,7 @@ static void __init setup_unwind_table(struct unwind_table *table,
hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int)
+ 2 * n * sizeof(unsigned long); + 2 * n * sizeof(unsigned long);
dprintk(2, "Binary lookup table size for %s: %lu bytes", table->name, hdrSize);
header = alloc(hdrSize); header = alloc(hdrSize);
if (!header) if (!header)
return; return;
...@@ -500,13 +512,17 @@ static unsigned long read_pointer(const u8 **pLoc, ...@@ -500,13 +512,17 @@ static unsigned long read_pointer(const u8 **pLoc,
const unsigned long *pul; const unsigned long *pul;
} ptr; } ptr;
if (ptrType < 0 || ptrType == DW_EH_PE_omit) if (ptrType < 0 || ptrType == DW_EH_PE_omit) {
dprintk(1, "Invalid pointer encoding %02X (%p,%p).", ptrType, *pLoc, end);
return 0; return 0;
}
ptr.p8 = *pLoc; ptr.p8 = *pLoc;
switch(ptrType & DW_EH_PE_FORM) { switch(ptrType & DW_EH_PE_FORM) {
case DW_EH_PE_data2: case DW_EH_PE_data2:
if (end < (const void *)(ptr.p16u + 1)) if (end < (const void *)(ptr.p16u + 1)) {
dprintk(1, "Data16 overrun (%p,%p).", ptr.p8, end);
return 0; return 0;
}
if(ptrType & DW_EH_PE_signed) if(ptrType & DW_EH_PE_signed)
value = get_unaligned(ptr.p16s++); value = get_unaligned(ptr.p16s++);
else else
...@@ -514,8 +530,10 @@ static unsigned long read_pointer(const u8 **pLoc, ...@@ -514,8 +530,10 @@ static unsigned long read_pointer(const u8 **pLoc,
break; break;
case DW_EH_PE_data4: case DW_EH_PE_data4:
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
if (end < (const void *)(ptr.p32u + 1)) if (end < (const void *)(ptr.p32u + 1)) {
dprintk(1, "Data32 overrun (%p,%p).", ptr.p8, end);
return 0; return 0;
}
if(ptrType & DW_EH_PE_signed) if(ptrType & DW_EH_PE_signed)
value = get_unaligned(ptr.p32s++); value = get_unaligned(ptr.p32s++);
else else
...@@ -527,8 +545,10 @@ static unsigned long read_pointer(const u8 **pLoc, ...@@ -527,8 +545,10 @@ static unsigned long read_pointer(const u8 **pLoc,
BUILD_BUG_ON(sizeof(u32) != sizeof(value)); BUILD_BUG_ON(sizeof(u32) != sizeof(value));
#endif #endif
case DW_EH_PE_native: case DW_EH_PE_native:
if (end < (const void *)(ptr.pul + 1)) if (end < (const void *)(ptr.pul + 1)) {
dprintk(1, "DataUL overrun (%p,%p).", ptr.p8, end);
return 0; return 0;
}
value = get_unaligned(ptr.pul++); value = get_unaligned(ptr.pul++);
break; break;
case DW_EH_PE_leb128: case DW_EH_PE_leb128:
...@@ -536,10 +556,14 @@ static unsigned long read_pointer(const u8 **pLoc, ...@@ -536,10 +556,14 @@ static unsigned long read_pointer(const u8 **pLoc,
value = ptrType & DW_EH_PE_signed value = ptrType & DW_EH_PE_signed
? get_sleb128(&ptr.p8, end) ? get_sleb128(&ptr.p8, end)
: get_uleb128(&ptr.p8, end); : get_uleb128(&ptr.p8, end);
if ((const void *)ptr.p8 > end) if ((const void *)ptr.p8 > end) {
dprintk(1, "DataLEB overrun (%p,%p).", ptr.p8, end);
return 0; return 0;
}
break; break;
default: default:
dprintk(2, "Cannot decode pointer type %02X (%p,%p).",
ptrType, ptr.p8, end);
return 0; return 0;
} }
switch(ptrType & DW_EH_PE_ADJUST) { switch(ptrType & DW_EH_PE_ADJUST) {
...@@ -549,11 +573,16 @@ static unsigned long read_pointer(const u8 **pLoc, ...@@ -549,11 +573,16 @@ static unsigned long read_pointer(const u8 **pLoc,
value += (unsigned long)*pLoc; value += (unsigned long)*pLoc;
break; break;
default: default:
dprintk(2, "Cannot adjust pointer type %02X (%p,%p).",
ptrType, *pLoc, end);
return 0; return 0;
} }
if ((ptrType & DW_EH_PE_indirect) if ((ptrType & DW_EH_PE_indirect)
&& probe_kernel_address((unsigned long *)value, value)) && probe_kernel_address((unsigned long *)value, value)) {
dprintk(1, "Cannot read indirect value %lx (%p,%p).",
value, *pLoc, end);
return 0; return 0;
}
*pLoc = ptr.p8; *pLoc = ptr.p8;
return value; return value;
...@@ -702,8 +731,10 @@ static int processCFI(const u8 *start, ...@@ -702,8 +731,10 @@ static int processCFI(const u8 *start,
state->label = NULL; state->label = NULL;
return 1; return 1;
} }
if (state->stackDepth >= MAX_STACK_DEPTH) if (state->stackDepth >= MAX_STACK_DEPTH) {
dprintk(1, "State stack overflow (%p,%p).", ptr.p8, end);
return 0; return 0;
}
state->stack[state->stackDepth++] = ptr.p8; state->stack[state->stackDepth++] = ptr.p8;
break; break;
case DW_CFA_restore_state: case DW_CFA_restore_state:
...@@ -718,8 +749,10 @@ static int processCFI(const u8 *start, ...@@ -718,8 +749,10 @@ static int processCFI(const u8 *start,
result = processCFI(start, end, 0, ptrType, state); result = processCFI(start, end, 0, ptrType, state);
state->loc = loc; state->loc = loc;
state->label = label; state->label = label;
} else } else {
dprintk(1, "State stack underflow (%p,%p).", ptr.p8, end);
return 0; return 0;
}
break; break;
case DW_CFA_def_cfa: case DW_CFA_def_cfa:
state->cfa.reg = get_uleb128(&ptr.p8, end); state->cfa.reg = get_uleb128(&ptr.p8, end);
...@@ -751,6 +784,7 @@ static int processCFI(const u8 *start, ...@@ -751,6 +784,7 @@ static int processCFI(const u8 *start,
break; break;
case DW_CFA_GNU_window_save: case DW_CFA_GNU_window_save:
default: default:
dprintk(1, "Unrecognized CFI op %02X (%p,%p).", ptr.p8[-1], ptr.p8 - 1, end);
result = 0; result = 0;
break; break;
} }
...@@ -766,12 +800,17 @@ static int processCFI(const u8 *start, ...@@ -766,12 +800,17 @@ static int processCFI(const u8 *start,
set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state);
break; break;
} }
if (ptr.p8 > end) if (ptr.p8 > end) {
dprintk(1, "Data overrun (%p,%p).", ptr.p8, end);
result = 0; result = 0;
}
if (result && targetLoc != 0 && targetLoc < state->loc) if (result && targetLoc != 0 && targetLoc < state->loc)
return 1; return 1;
} }
if (result && ptr.p8 < end)
dprintk(1, "Data underrun (%p,%p).", ptr.p8, end);
return result return result
&& ptr.p8 == end && ptr.p8 == end
&& (targetLoc == 0 && (targetLoc == 0
...@@ -843,6 +882,8 @@ int unwind(struct unwind_frame_info *frame) ...@@ -843,6 +882,8 @@ int unwind(struct unwind_frame_info *frame)
hdr[3]); hdr[3]);
} }
} }
if(hdr && !fde)
dprintk(3, "Binary lookup for %lx failed.", pc);
if (fde != NULL) { if (fde != NULL) {
cie = cie_for_fde(fde, table); cie = cie_for_fde(fde, table);
...@@ -864,6 +905,8 @@ int unwind(struct unwind_frame_info *frame) ...@@ -864,6 +905,8 @@ int unwind(struct unwind_frame_info *frame)
fde = NULL; fde = NULL;
} else } else
fde = NULL; fde = NULL;
if(!fde)
dprintk(1, "Binary lookup result for %lx discarded.", pc);
} }
if (fde == NULL) { if (fde == NULL) {
for (fde = table->address, tableSize = table->size; for (fde = table->address, tableSize = table->size;
...@@ -895,6 +938,8 @@ int unwind(struct unwind_frame_info *frame) ...@@ -895,6 +938,8 @@ int unwind(struct unwind_frame_info *frame)
if (pc >= startLoc && pc < endLoc) if (pc >= startLoc && pc < endLoc)
break; break;
} }
if(!fde)
dprintk(3, "Linear lookup for %lx failed.", pc);
} }
} }
if (cie != NULL) { if (cie != NULL) {
...@@ -928,6 +973,8 @@ int unwind(struct unwind_frame_info *frame) ...@@ -928,6 +973,8 @@ int unwind(struct unwind_frame_info *frame)
if (ptr >= end || *ptr) if (ptr >= end || *ptr)
cie = NULL; cie = NULL;
} }
if(!cie)
dprintk(1, "CIE unusable (%p,%p).", ptr, end);
++ptr; ++ptr;
} }
if (cie != NULL) { if (cie != NULL) {
...@@ -938,9 +985,11 @@ int unwind(struct unwind_frame_info *frame) ...@@ -938,9 +985,11 @@ int unwind(struct unwind_frame_info *frame)
if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end)
cie = NULL; cie = NULL;
else if (UNW_PC(frame) % state.codeAlign else if (UNW_PC(frame) % state.codeAlign
|| UNW_SP(frame) % sleb128abs(state.dataAlign)) || UNW_SP(frame) % sleb128abs(state.dataAlign)) {
dprintk(1, "Input pointer(s) misaligned (%lx,%lx).",
UNW_PC(frame), UNW_SP(frame));
return -EPERM; return -EPERM;
else { } else {
retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end);
/* skip augmentation */ /* skip augmentation */
if (((const char *)(cie + 2))[1] == 'z') { if (((const char *)(cie + 2))[1] == 'z') {
...@@ -954,6 +1003,8 @@ int unwind(struct unwind_frame_info *frame) ...@@ -954,6 +1003,8 @@ int unwind(struct unwind_frame_info *frame)
|| reg_info[retAddrReg].width != sizeof(unsigned long)) || reg_info[retAddrReg].width != sizeof(unsigned long))
cie = NULL; cie = NULL;
} }
if(!cie)
dprintk(1, "CIE validation failed (%p,%p).", ptr, end);
} }
if (cie != NULL) { if (cie != NULL) {
state.cieStart = ptr; state.cieStart = ptr;
...@@ -967,6 +1018,8 @@ int unwind(struct unwind_frame_info *frame) ...@@ -967,6 +1018,8 @@ int unwind(struct unwind_frame_info *frame)
if ((ptr += augSize) > end) if ((ptr += augSize) > end)
fde = NULL; fde = NULL;
} }
if(!fde)
dprintk(1, "FDE validation failed (%p,%p).", ptr, end);
} }
if (cie == NULL || fde == NULL) { if (cie == NULL || fde == NULL) {
#ifdef CONFIG_FRAME_POINTER #ifdef CONFIG_FRAME_POINTER
...@@ -1025,8 +1078,10 @@ int unwind(struct unwind_frame_info *frame) ...@@ -1025,8 +1078,10 @@ int unwind(struct unwind_frame_info *frame)
|| state.cfa.reg >= ARRAY_SIZE(reg_info) || state.cfa.reg >= ARRAY_SIZE(reg_info)
|| reg_info[state.cfa.reg].width != sizeof(unsigned long) || reg_info[state.cfa.reg].width != sizeof(unsigned long)
|| FRAME_REG(state.cfa.reg, unsigned long) % sizeof(unsigned long) || FRAME_REG(state.cfa.reg, unsigned long) % sizeof(unsigned long)
|| state.cfa.offs % sizeof(unsigned long)) || state.cfa.offs % sizeof(unsigned long)) {
dprintk(1, "Unusable unwind info (%p,%p).", ptr, end);
return -EIO; return -EIO;
}
/* update frame */ /* update frame */
#ifndef CONFIG_AS_CFI_SIGNAL_FRAME #ifndef CONFIG_AS_CFI_SIGNAL_FRAME
if(frame->call_frame if(frame->call_frame
...@@ -1051,6 +1106,8 @@ int unwind(struct unwind_frame_info *frame) ...@@ -1051,6 +1106,8 @@ int unwind(struct unwind_frame_info *frame)
if (REG_INVALID(i)) { if (REG_INVALID(i)) {
if (state.regs[i].where == Nowhere) if (state.regs[i].where == Nowhere)
continue; continue;
dprintk(1, "Cannot restore register %u (%d).",
i, state.regs[i].where);
return -EIO; return -EIO;
} }
switch(state.regs[i].where) { switch(state.regs[i].where) {
...@@ -1059,8 +1116,11 @@ int unwind(struct unwind_frame_info *frame) ...@@ -1059,8 +1116,11 @@ int unwind(struct unwind_frame_info *frame)
case Register: case Register:
if (state.regs[i].value >= ARRAY_SIZE(reg_info) if (state.regs[i].value >= ARRAY_SIZE(reg_info)
|| REG_INVALID(state.regs[i].value) || REG_INVALID(state.regs[i].value)
|| reg_info[i].width > reg_info[state.regs[i].value].width) || reg_info[i].width > reg_info[state.regs[i].value].width) {
dprintk(1, "Cannot restore register %u from register %lu.",
i, state.regs[i].value);
return -EIO; return -EIO;
}
switch(reg_info[state.regs[i].value].width) { switch(reg_info[state.regs[i].value].width) {
#define CASE(n) \ #define CASE(n) \
case sizeof(u##n): \ case sizeof(u##n): \
...@@ -1070,6 +1130,9 @@ int unwind(struct unwind_frame_info *frame) ...@@ -1070,6 +1130,9 @@ int unwind(struct unwind_frame_info *frame)
CASES; CASES;
#undef CASE #undef CASE
default: default:
dprintk(1, "Unsupported register size %u (%lu).",
reg_info[state.regs[i].value].width,
state.regs[i].value);
return -EIO; return -EIO;
} }
break; break;
...@@ -1094,12 +1157,17 @@ int unwind(struct unwind_frame_info *frame) ...@@ -1094,12 +1157,17 @@ int unwind(struct unwind_frame_info *frame)
CASES; CASES;
#undef CASE #undef CASE
default: default:
dprintk(1, "Unsupported register size %u (%u).",
reg_info[i].width, i);
return -EIO; return -EIO;
} }
break; break;
case Value: case Value:
if (reg_info[i].width != sizeof(unsigned long)) if (reg_info[i].width != sizeof(unsigned long)) {
dprintk(1, "Unsupported value size %u (%u).",
reg_info[i].width, i);
return -EIO; return -EIO;
}
FRAME_REG(i, unsigned long) = cfa + state.regs[i].value FRAME_REG(i, unsigned long) = cfa + state.regs[i].value
* state.dataAlign; * state.dataAlign;
break; break;
...@@ -1111,8 +1179,11 @@ int unwind(struct unwind_frame_info *frame) ...@@ -1111,8 +1179,11 @@ int unwind(struct unwind_frame_info *frame)
% sizeof(unsigned long) % sizeof(unsigned long)
|| addr < startLoc || addr < startLoc
|| addr + sizeof(unsigned long) < addr || addr + sizeof(unsigned long) < addr
|| addr + sizeof(unsigned long) > endLoc) || addr + sizeof(unsigned long) > endLoc) {
dprintk(1, "Bad memory location %lx (%lx).",
addr, state.regs[i].value);
return -EIO; return -EIO;
}
switch(reg_info[i].width) { switch(reg_info[i].width) {
#define CASE(n) case sizeof(u##n): \ #define CASE(n) case sizeof(u##n): \
probe_kernel_address((u##n *)addr, FRAME_REG(i, u##n)); \ probe_kernel_address((u##n *)addr, FRAME_REG(i, u##n)); \
...@@ -1120,6 +1191,8 @@ int unwind(struct unwind_frame_info *frame) ...@@ -1120,6 +1191,8 @@ int unwind(struct unwind_frame_info *frame)
CASES; CASES;
#undef CASE #undef CASE
default: default:
dprintk(1, "Unsupported memory size %u (%u).",
reg_info[i].width, i);
return -EIO; return -EIO;
} }
} }
...@@ -1128,9 +1201,15 @@ int unwind(struct unwind_frame_info *frame) ...@@ -1128,9 +1201,15 @@ int unwind(struct unwind_frame_info *frame)
} }
if (UNW_PC(frame) % state.codeAlign if (UNW_PC(frame) % state.codeAlign
|| UNW_SP(frame) % sleb128abs(state.dataAlign) || UNW_SP(frame) % sleb128abs(state.dataAlign)) {
|| (pc == UNW_PC(frame) && sp == UNW_SP(frame))) dprintk(1, "Output pointer(s) misaligned (%lx,%lx).",
UNW_PC(frame), UNW_SP(frame));
return -EIO; return -EIO;
}
if (pc == UNW_PC(frame) && sp == UNW_SP(frame)) {
dprintk(1, "No progress (%lx,%lx).", pc, sp);
return -EIO;
}
return 0; return 0;
#undef CASES #undef CASES
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册