2015-12-03 12:41:38

by Vineet Gupta

[permalink] [raw]
Subject: [RFC 00/17] ARC Dwarf unwinder improvements

Hi guys,

In light of perf -g stalling (as unwinder was taking ~3million cycles for non
existent entries), I've revamped the dwarf unwinder.

There are some optim tweaks and much of it is "De-generalization" for things
which we can safely assume on ARC.

Crude Instrumentation shows following improvements per unwinder call:
- Avg time come down from ~4650 cycles to ~2794 cycles (+40%)
- Max time come down 9793 cycles to 5987 cycles

This is on a SMP FPGA config @ 75 MHz

It seems much of time (65%) is taken for binary lookup thru ~12k FDE entries,
roughly 13 lookups, each likely a dcache miss.


git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc # topic-unwinder-rework-4-instrument

-Vineet

Vineet Gupta (17):
ARC: dw2 unwind: Elide generation of const propagated clones
ARC: dw2 unwind: remove unused cruft
ARC: dw2 unwind: Remove handling of for signal frame
ARC: dw2 unwind: Remove FP based unwinding
ARC: dw2 unwind: Better printing
ARC: dw2 unwind: Don't verify Main FDE Table size everytime
ARC: dw2 unwind: Refactor the FDE lookup table (eh_frame_header) code
ARC: dw2 unwind: Don't verify FDE lookup table metadata
ARC: dw2 unwind: Use striaght forward code to implement binary lookup
ARC: dw2 unwind: CIE parsing/validation done only once at startup
ARC: dw2 unwind: Elide REG_INVALID check
ARC: dw2 unwind: Elide a loop if DW_CFA_register not present
ARC: dw2 unwind: Assume all regs to be unsigned long
ARC: dw2 unwind: No need for __get_user
ARC: dw2 unwind: Single exit point for instrumentation
ARC: dw2 unwind: skip regs not updated
xxx: instrument

arch/arc/include/asm/unwind.h | 47 +--
arch/arc/kernel/Makefile | 1 +
arch/arc/kernel/unwind.c | 806 ++++++++++++++++--------------------------
3 files changed, 313 insertions(+), 541 deletions(-)

--
1.9.1


2015-12-03 12:41:42

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 01/17] ARC: dw2 unwind: Elide generation of const propagated clones

arc_unwind_core() is entry point into the actual dwarf unwinder and it
gets called by various kernel APIs which provide the unwinding context
(e.g. current task vs. a specific task, current pt_regs vs. some other
crash context's pt_regs...)

Currently multiple const propagated clones of arc_unwind_core() are
generated, which seems superfluous. The only performance critical call
is for perf callgrap unwinding, which being in a different compilation
unit, uses ths vanilla, non cp version anyways.

So prevent the clone functions generation

bloat-o-meter report

| add/remove: 0/1 grow/shrink: 4/0 up/down: 40/-1152 (-1112)
| function old new delta
| save_stack_trace 4 16 +12
| save_stack_trace_tsk 4 14 +10
| get_wchan 4 14 +10
| show_stacktrace 40 48 +8
| arc_unwind_core.constprop 1152 - -1152

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/Makefile | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index e7f3625a19b5..f90e5fd6d5c8 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -7,6 +7,7 @@

# Pass UTS_MACHINE for user_regset definition
CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
+CFLAGS_stacktrace.o += -fno-ipa-cp-clone

obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o process.o devtree.o
obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o clk.o
--
1.9.1

2015-12-03 12:42:18

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 02/17] ARC: dw2 unwind: remove unused cruft

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/include/asm/unwind.h | 34 +++++++++-------------------------
arch/arc/kernel/unwind.c | 15 +--------------
2 files changed, 10 insertions(+), 39 deletions(-)

diff --git a/arch/arc/include/asm/unwind.h b/arch/arc/include/asm/unwind.h
index 7ca628b6ee2a..0e7f48e2686a 100644
--- a/arch/arc/include/asm/unwind.h
+++ b/arch/arc/include/asm/unwind.h
@@ -72,6 +72,15 @@ struct unwind_frame_info {

#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1))

+#define EXTRA_INFO(f) { \
+ BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \
+ % FIELD_SIZEOF(struct unwind_frame_info, f)) \
+ + offsetof(struct unwind_frame_info, f) \
+ / FIELD_SIZEOF(struct unwind_frame_info, f), \
+ FIELD_SIZEOF(struct unwind_frame_info, f) \
+ }
+#define PTREGS_INFO(f) EXTRA_INFO(regs.f)
+
#define UNW_REGISTER_INFO \
PTREGS_INFO(r0), \
PTREGS_INFO(r1), \
@@ -117,31 +126,6 @@ extern void *unwind_add_table(struct module *module, const void *table_start,
unsigned long table_size);
extern void unwind_remove_table(void *handle, int init_only);

-static inline int
-arch_unwind_init_running(struct unwind_frame_info *info,
- int (*callback) (struct unwind_frame_info *info,
- void *arg),
- void *arg)
-{
- return 0;
-}
-
-static inline int arch_unw_user_mode(const struct unwind_frame_info *info)
-{
- return 0;
-}
-
-static inline void arch_unw_init_blocked(struct unwind_frame_info *info)
-{
- return;
-}
-
-static inline void arch_unw_init_frame_info(struct unwind_frame_info *info,
- struct pt_regs *regs)
-{
- return;
-}
-
#else

#define UNW_PC(frame) ((void)(frame), 0)
diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 7352475451f6..44983b4d9e78 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -43,23 +43,10 @@ do { \

#define MAX_STACK_DEPTH 8

-#define EXTRA_INFO(f) { \
- BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \
- % FIELD_SIZEOF(struct unwind_frame_info, f)) \
- + offsetof(struct unwind_frame_info, f) \
- / FIELD_SIZEOF(struct unwind_frame_info, f), \
- FIELD_SIZEOF(struct unwind_frame_info, f) \
- }
-#define PTREGS_INFO(f) EXTRA_INFO(regs.f)
-
static const struct {
unsigned offs:BITS_PER_LONG / 2;
unsigned width:BITS_PER_LONG / 2;
-} reg_info[] = {
-UNW_REGISTER_INFO};
-
-#undef PTREGS_INFO
-#undef EXTRA_INFO
+} reg_info[] = { UNW_REGISTER_INFO };

#ifndef REG_INVALID
#define REG_INVALID(r) (reg_info[r].width == 0)
--
1.9.1

2015-12-03 12:44:14

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 03/17] ARC: dw2 unwind: Remove handling of for signal frame

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/include/asm/unwind.h | 3 ---
arch/arc/kernel/unwind.c | 11 ++---------
2 files changed, 2 insertions(+), 12 deletions(-)

diff --git a/arch/arc/include/asm/unwind.h b/arch/arc/include/asm/unwind.h
index 0e7f48e2686a..559ef55abce1 100644
--- a/arch/arc/include/asm/unwind.h
+++ b/arch/arc/include/asm/unwind.h
@@ -116,9 +116,6 @@ struct unwind_frame_info {
PTREGS_INFO(r31), \
PTREGS_INFO(r63)

-#define UNW_DEFAULT_RA(raItem, dataAlign) \
- ((raItem).where == Memory && !((raItem).value * (dataAlign) + 4))
-
extern int arc_unwind(struct unwind_frame_info *frame);
extern void arc_unwind_init(void);
extern void arc_unwind_setup(void);
diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 44983b4d9e78..2f4a67f5a863 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -869,7 +869,7 @@ int arc_unwind(struct unwind_frame_info *frame)
#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
const u32 *fde = NULL, *cie = NULL;
const u8 *ptr = NULL, *end = NULL;
- unsigned long pc = UNW_PC(frame) - frame->call_frame;
+ unsigned long pc = UNW_PC(frame);
unsigned long startLoc = 0, endLoc = 0, cfa;
unsigned i;
signed ptrType = -1;
@@ -988,7 +988,6 @@ int arc_unwind(struct unwind_frame_info *frame)
state.cieEnd = ptr; /* keep here temporarily */
ptr = (const u8 *)(cie + 2);
end = (const u8 *)(cie + 1) + *cie;
- frame->call_frame = 1;
if ((state.version = *ptr) != 1)
cie = NULL; /* unsupported version */
else if (*++ptr) {
@@ -1003,7 +1002,7 @@ int arc_unwind(struct unwind_frame_info *frame)
case 'R':
continue;
case 'S':
- frame->call_frame = 0;
+ /* signal frame not handled */
continue;
default:
break;
@@ -1147,12 +1146,6 @@ int arc_unwind(struct unwind_frame_info *frame)
unw_debug("\n");
#endif

- /* update frame */
-#ifndef CONFIG_AS_CFI_SIGNAL_FRAME
- if (frame->call_frame
- && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign))
- frame->call_frame = 0;
-#endif
cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs;
startLoc = min_t(unsigned long, UNW_SP(frame), cfa);
endLoc = max_t(unsigned long, UNW_SP(frame), cfa);
--
1.9.1

2015-12-03 12:41:58

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 04/17] ARC: dw2 unwind: Remove FP based unwinding

FP is disabled for ARC and even if it was enabled, it won't help with
unwinding given ARC ABI so remove it.

Typical ABI would
- save BLINK (return address) on stack
- save FP on stack
- anchor FP for this frame
- save any callee-regs and/or carve frame for local vars

Thus FP remains fixed for frame and can be used to determine BLINK.

ARC ABI historically required saving callee-regs before anchoring FP,
thus rendering it useless for finding BLINK.

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/include/asm/unwind.h | 11 +----------
arch/arc/kernel/unwind.c | 45 ++-----------------------------------------
2 files changed, 3 insertions(+), 53 deletions(-)

diff --git a/arch/arc/include/asm/unwind.h b/arch/arc/include/asm/unwind.h
index 559ef55abce1..03ace2cc8bc5 100644
--- a/arch/arc/include/asm/unwind.h
+++ b/arch/arc/include/asm/unwind.h
@@ -58,17 +58,7 @@ struct unwind_frame_info {
#define UNW_PC(frame) ((frame)->regs.r63)
#define UNW_SP(frame) ((frame)->regs.r28)
#define UNW_BLINK(frame) ((frame)->regs.r31)
-
-/* Rajesh FIXME */
-#ifdef CONFIG_FRAME_POINTER
#define UNW_FP(frame) ((frame)->regs.r27)
-#define FRAME_RETADDR_OFFSET 4
-#define FRAME_LINK_OFFSET 0
-#define STACK_BOTTOM_UNW(tsk) STACK_LIMIT((tsk)->thread.ksp)
-#define STACK_TOP_UNW(tsk) ((tsk)->thread.ksp)
-#else
-#define UNW_FP(frame) ((void)(frame), 0)
-#endif

#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1))

@@ -128,6 +118,7 @@ extern void unwind_remove_table(void *handle, int init_only);
#define UNW_PC(frame) ((void)(frame), 0)
#define UNW_SP(frame) ((void)(frame), 0)
#define UNW_FP(frame) ((void)(frame), 0)
+#define UNW_FP(frame) ((void)(frame), 0)

static inline void arc_unwind_init(void)
{
diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 2f4a67f5a863..0993a81e112b 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -1057,50 +1057,9 @@ int arc_unwind(struct unwind_frame_info *frame)
fde = NULL;
}
}
- if (cie == NULL || fde == NULL) {
-#ifdef CONFIG_FRAME_POINTER
- unsigned long top, bottom;
-
- top = STACK_TOP_UNW(frame->task);
- bottom = STACK_BOTTOM_UNW(frame->task);
-#if FRAME_RETADDR_OFFSET < 0
- if (UNW_SP(frame) < top && UNW_FP(frame) <= UNW_SP(frame)
- && bottom < UNW_FP(frame)
-#else
- if (UNW_SP(frame) > top && UNW_FP(frame) >= UNW_SP(frame)
- && bottom > UNW_FP(frame)
-#endif
- && !((UNW_SP(frame) | UNW_FP(frame))
- & (sizeof(unsigned long) - 1))) {
- unsigned long link;
-
- if (!__get_user(link, (unsigned long *)
- (UNW_FP(frame) + FRAME_LINK_OFFSET))
-#if FRAME_RETADDR_OFFSET < 0
- && link > bottom && link < UNW_FP(frame)
-#else
- && link > UNW_FP(frame) && link < bottom
-#endif
- && !(link & (sizeof(link) - 1))
- && !__get_user(UNW_PC(frame),
- (unsigned long *)(UNW_FP(frame)
- + FRAME_RETADDR_OFFSET)))
- {
- UNW_SP(frame) =
- UNW_FP(frame) + FRAME_RETADDR_OFFSET
-#if FRAME_RETADDR_OFFSET < 0
- -
-#else
- +
-#endif
- sizeof(UNW_PC(frame));
- UNW_FP(frame) = link;
- return 0;
- }
- }
-#endif
+ if (cie == NULL || fde == NULL)
return -ENXIO;
- }
+
state.org = startLoc;
memcpy(&state.cfa, &badCFA, sizeof(state.cfa));

--
1.9.1

2015-12-03 12:42:05

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 05/17] ARC: dw2 unwind: Better printing

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 123 +++++++++++++++++------------------------------
1 file changed, 45 insertions(+), 78 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 0993a81e112b..2bb3c1c048bb 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -629,40 +629,40 @@ static signed fde_pointer_type(const u32 *cie)
return DW_EH_PE_native | DW_EH_PE_abs;
}

-static int advance_loc(unsigned long delta, struct unwind_state *state)
+static int advance_loc(unsigned long delta, struct unwind_state *state, char *str)
{
state->loc += delta * state->codeAlign;

/* FIXME_Rajesh: Probably we are defining for the initial range as well;
return delta > 0;
*/
- unw_debug("delta %3lu => loc 0x%lx: ", delta, state->loc);
+ unw_debug("%sdelta %3lu => loc 0x%lx\n", str, delta, state->loc);
return 1;
}

static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value,
- struct unwind_state *state)
+ struct unwind_state *state, char *str)
{
if (reg < ARRAY_SIZE(state->regs)) {
state->regs[reg].where = where;
state->regs[reg].value = value;

#ifdef UNWIND_DEBUG
- unw_debug("r%lu: ", reg);
switch (where) {
case Nowhere:
- unw_debug("s ");
+ unw_debug("%sr%lu: s \n", str, reg);
break;
case Memory:
- unw_debug("c(%lu) ", value);
+ unw_debug("%sr%lu: c(%lu) \n", str, reg, value);
break;
case Register:
- unw_debug("r(%lu) ", value);
+ unw_debug("%sr%lu: r(%lu) \n", str, reg, value);
break;
case Value:
- unw_debug("v(%lu) ", value);
+ unw_debug("%sr%lu: v(%lu) \n", str, reg, value);
break;
default:
+ unw_debug("%sr%lu: ???\n", str, reg);
break;
}
#endif
@@ -689,6 +689,7 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
return result;
}
for (ptr.p8 = start; result && ptr.p8 < end;) {
+ char *str = NULL;
switch (*ptr.p8 >> 6) {
uleb128_t value;

@@ -707,59 +708,52 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
unw_debug("cfa_set_loc: 0x%lx ", state->loc);
break;
case DW_CFA_advance_loc1:
- unw_debug("\ncfa advance loc1:");
+ str = "cfa advance loc1:";
result = ptr.p8 < end
- && advance_loc(*ptr.p8++, state);
+ && advance_loc(*ptr.p8++, state, str);
break;
case DW_CFA_advance_loc2:
value = *ptr.p8++;
value += *ptr.p8++ << 8;
- unw_debug("\ncfa advance loc2:");
+ str = "cfa advance loc2:";
result = ptr.p8 <= end + 2
- /* && advance_loc(*ptr.p16++, state); */
- && advance_loc(value, state);
+ /* && advance_loc(*ptr.p16++, state, str); */
+ && advance_loc(value, state, str);
break;
case DW_CFA_advance_loc4:
- unw_debug("\ncfa advance loc4:");
+ str = "cfa advance loc4:";
result = ptr.p8 <= end + 4
- && advance_loc(*ptr.p32++, state);
+ && advance_loc(*ptr.p32++, state, str);
break;
case DW_CFA_offset_extended:
value = get_uleb128(&ptr.p8, end);
- unw_debug("cfa_offset_extended: ");
- set_rule(value, Memory,
- get_uleb128(&ptr.p8, end), state);
+ str = "cfa_offset_extended: ";
+ set_rule(value, Memory, get_uleb128(&ptr.p8, end), state, str);
break;
case DW_CFA_val_offset:
value = get_uleb128(&ptr.p8, end);
- set_rule(value, Value,
- get_uleb128(&ptr.p8, end), state);
+ set_rule(value, Value, get_uleb128(&ptr.p8, end), state, str);
break;
case DW_CFA_offset_extended_sf:
value = get_uleb128(&ptr.p8, end);
- set_rule(value, Memory,
- get_sleb128(&ptr.p8, end), state);
+ set_rule(value, Memory, get_sleb128(&ptr.p8, end), state, str);
break;
case DW_CFA_val_offset_sf:
value = get_uleb128(&ptr.p8, end);
- set_rule(value, Value,
- get_sleb128(&ptr.p8, end), state);
+ set_rule(value, Value, get_sleb128(&ptr.p8, end), state, str);
break;
case DW_CFA_restore_extended:
- unw_debug("cfa_restore_extended: ");
+ str = "cfa_restore_extended: ";
case DW_CFA_undefined:
- unw_debug("cfa_undefined: ");
+ str = "cfa_undefined: ";
case DW_CFA_same_value:
- unw_debug("cfa_same_value: ");
- set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0,
- state);
+ str = "cfa_same_value: ";
+ set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, state, str);
break;
case DW_CFA_register:
- unw_debug("cfa_register: ");
+ str = "cfa_register: ";
value = get_uleb128(&ptr.p8, end);
- set_rule(value,
- Register,
- get_uleb128(&ptr.p8, end), state);
+ set_rule(value, Register, get_uleb128(&ptr.p8, end), state, str);
break;
case DW_CFA_remember_state:
unw_debug("cfa_remember_state: ");
@@ -798,8 +792,8 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
/*nobreak*/
case DW_CFA_def_cfa_offset:
state->cfa.offs = get_uleb128(&ptr.p8, end);
- unw_debug("cfa_def_cfa_offset: 0x%lx ",
- state->cfa.offs);
+ unw_debug("cfa_def_cfa_offset: r%ld: %ld\n",
+ state->cfa.reg, state->cfa.offs);
break;
case DW_CFA_def_cfa_sf:
state->cfa.reg = get_uleb128(&ptr.p8, end);
@@ -824,7 +818,7 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
Memory,
(uleb128_t) 0 - get_uleb128(&ptr.p8,
end),
- state);
+ state, str);
break;
case DW_CFA_GNU_window_save:
default:
@@ -834,18 +828,17 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
}
break;
case 1:
- unw_debug("\ncfa_adv_loc: ");
- result = advance_loc(*ptr.p8++ & 0x3f, state);
+ str = "cfa_adv_loc: ";
+ result = advance_loc(*ptr.p8++ & 0x3f, state, str);
break;
case 2:
- unw_debug("cfa_offset: ");
+ str = "cfa_offset: ";
value = *ptr.p8++ & 0x3f;
- set_rule(value, Memory, get_uleb128(&ptr.p8, end),
- state);
+ set_rule(value, Memory, get_uleb128(&ptr.p8, end), state, str);
break;
case 3:
- unw_debug("cfa_restore: ");
- set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state);
+ str = "cfa_restore: ";
+ set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state, str);
break;
}

@@ -879,15 +872,16 @@ int arc_unwind(struct unwind_frame_info *frame)
unsigned long *fptr;
unsigned long addr;

- unw_debug("\n\nUNWIND FRAME:\n");
- unw_debug("PC: 0x%lx BLINK: 0x%lx, SP: 0x%lx, FP: 0x%x\n",
- UNW_PC(frame), UNW_BLINK(frame), UNW_SP(frame),
- UNW_FP(frame));
+ unw_debug("\nUNWIND FRAME: -------------------------------------\n");
+ unw_debug("PC\t\t: 0x%lx %pS\nr31 [BLINK]\t: 0x%lx %pS\nr28 [SP]\t: 0x%lx\nr27 [FP]\t: 0x%lx\n",
+ UNW_PC(frame), (void *)UNW_PC(frame),
+ UNW_BLINK(frame), (void *)UNW_BLINK(frame),
+ UNW_SP(frame), UNW_FP(frame));

if (UNW_PC(frame) == 0)
return -EINVAL;

-#ifdef UNWIND_DEBUG
+#ifdef UNWIND_DEBUG0
{
unsigned long *sptr = (unsigned long *)UNW_SP(frame);
unw_debug("\nStack Dump:\n");
@@ -1063,7 +1057,7 @@ int arc_unwind(struct unwind_frame_info *frame)
state.org = startLoc;
memcpy(&state.cfa, &badCFA, sizeof(state.cfa));

- unw_debug("\nProcess instructions\n");
+ unw_debug("\nProcess CFA\n");

/* process instructions
* For ARC, we optimize by having blink(retAddrReg) with
@@ -1078,33 +1072,6 @@ int arc_unwind(struct unwind_frame_info *frame)
|| state.cfa.offs % sizeof(unsigned long))
return -EIO;

-#ifdef UNWIND_DEBUG
- unw_debug("\n");
-
- unw_debug("\nRegister State Based on the rules parsed from FDE:\n");
- for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
-
- if (REG_INVALID(i))
- continue;
-
- switch (state.regs[i].where) {
- case Nowhere:
- break;
- case Memory:
- unw_debug(" r%d: c(%lu),", i, state.regs[i].value);
- break;
- case Register:
- unw_debug(" r%d: r(%lu),", i, state.regs[i].value);
- break;
- case Value:
- unw_debug(" r%d: v(%lu),", i, state.regs[i].value);
- break;
- }
- }
-
- unw_debug("\n");
-#endif
-
cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs;
startLoc = min_t(unsigned long, UNW_SP(frame), cfa);
endLoc = max_t(unsigned long, UNW_SP(frame), cfa);
@@ -1113,7 +1080,7 @@ int arc_unwind(struct unwind_frame_info *frame)
endLoc = max(STACK_LIMIT(cfa), cfa);
}

- unw_debug("\nCFA reg: 0x%lx, offset: 0x%lx => 0x%lx\n",
+ unw_debug("\nCFA reg: r%ld, off: %ld => [SP] 0x%lx\n",
state.cfa.reg, state.cfa.offs, cfa);

for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
@@ -1232,7 +1199,7 @@ int arc_unwind(struct unwind_frame_info *frame)

break;
}
- unw_debug("r%d: 0x%lx ", i, *fptr);
+ unw_debug("r%d: 0x%lx\n", i, *fptr);
}

return 0;
--
1.9.1

2015-12-03 12:42:15

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 06/17] ARC: dw2 unwind: Don't verify Main FDE Table size everytime

This is already done at boot time in setup_unwind_table()

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 2bb3c1c048bb..f57a0d50185c 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -892,8 +892,7 @@ int arc_unwind(struct unwind_frame_info *frame)
#endif

table = find_table(pc);
- if (table != NULL
- && !(table->size & (sizeof(*fde) - 1))) {
+ if (table != NULL) {
const u8 *hdr = table->header;
unsigned long tableSize;

--
1.9.1

2015-12-03 12:43:55

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 07/17] ARC: dw2 unwind: Refactor the FDE lookup table (eh_frame_header) code

- Reduce 1 level of indenatation
- Use struct members to identify what's going on !
- Nothing semantical

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 175 ++++++++++++++++++++++-------------------------
1 file changed, 83 insertions(+), 92 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index f57a0d50185c..9f5ed6873c52 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -101,6 +101,20 @@ static const struct {
typedef unsigned long uleb128_t;
typedef signed long sleb128_t;

+struct eh_frame_hdr_table_entry {
+ unsigned long start, fde;
+};
+
+struct eh_frame_header {
+ u8 version;
+ u8 eh_frame_ptr_enc;
+ u8 fde_count_enc;
+ u8 table_enc;
+ unsigned long eh_frame_ptr;
+ unsigned int fde_count;
+ struct eh_frame_hdr_table_entry table[];
+} __attribute__ ((__packed__));
+
static struct unwind_table {
struct {
unsigned long pc;
@@ -108,7 +122,7 @@ static struct unwind_table {
} core, init;
const void *address;
unsigned long size;
- const unsigned char *header;
+ struct eh_frame_header *header;
unsigned long hdrsz;
struct unwind_table *link;
const char *name;
@@ -185,7 +199,7 @@ static void init_unwind_table(struct unwind_table *table, const char *name,

table->hdrsz = header_size;
smp_wmb();
- table->header = header_start;
+ table->header = (struct eh_frame_header *)header_start;
table->link = NULL;
table->name = name;
}
@@ -202,10 +216,6 @@ static const u32 bad_cie, not_fde;
static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *);
static signed fde_pointer_type(const u32 *cie);

-struct eh_frame_hdr_table_entry {
- unsigned long start, fde;
-};
-
static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2)
{
const struct eh_frame_hdr_table_entry *e1 = p1;
@@ -235,15 +245,7 @@ static void __init setup_unwind_table(struct unwind_table *table,
unsigned long tableSize = table->size, hdrSize;
unsigned n;
const u32 *fde;
- struct {
- u8 version;
- u8 eh_frame_ptr_enc;
- u8 fde_count_enc;
- u8 table_enc;
- unsigned long eh_frame_ptr;
- unsigned int fde_count;
- struct eh_frame_hdr_table_entry table[];
- } __attribute__ ((__packed__)) *header;
+ struct eh_frame_header *header;

if (table->header)
return;
@@ -326,7 +328,7 @@ static void __init setup_unwind_table(struct unwind_table *table,

table->hdrsz = hdrSize;
smp_wmb();
- table->header = (const void *)header;
+ table->header = header;
}

static void *__init balloc(unsigned long sz)
@@ -871,6 +873,8 @@ int arc_unwind(struct unwind_frame_info *frame)
struct unwind_state state;
unsigned long *fptr;
unsigned long addr;
+ struct eh_frame_header *hdr;
+ unsigned long hdrEntrySz;

unw_debug("\nUNWIND FRAME: -------------------------------------\n");
unw_debug("PC\t\t: 0x%lx %pS\nr31 [BLINK]\t: 0x%lx %pS\nr28 [SP]\t: 0x%lx\nr27 [FP]\t: 0x%lx\n",
@@ -892,88 +896,75 @@ int arc_unwind(struct unwind_frame_info *frame)
#endif

table = find_table(pc);
- if (table != NULL) {
- const u8 *hdr = table->header;
- unsigned long tableSize;
-
- smp_rmb();
- if (hdr && hdr[0] == 1) {
- switch (hdr[3] & DW_EH_PE_FORM) {
- case DW_EH_PE_native:
- tableSize = sizeof(unsigned long);
- break;
- case DW_EH_PE_data2:
- tableSize = 2;
- break;
- case DW_EH_PE_data4:
- tableSize = 4;
- break;
- case DW_EH_PE_data8:
- tableSize = 8;
- break;
- default:
- tableSize = 0;
- break;
- }
- ptr = hdr + 4;
- end = hdr + table->hdrsz;
- if (tableSize && read_pointer(&ptr, end, hdr[1])
- == (unsigned long)table->address
- && (i = read_pointer(&ptr, end, hdr[2])) > 0
- && i == (end - ptr) / (2 * tableSize)
- && !((end - ptr) % (2 * tableSize))) {
- do {
- const u8 *cur =
- ptr + (i / 2) * (2 * tableSize);
-
- startLoc = read_pointer(&cur,
- cur + tableSize,
- hdr[3]);
- if (pc < startLoc)
- i /= 2;
- else {
- ptr = cur - tableSize;
- i = (i + 1) / 2;
- }
- } while (startLoc && i > 1);
- if (i == 1
- && (startLoc = read_pointer(&ptr,
- ptr + tableSize,
- hdr[3])) != 0
- && pc >= startLoc)
- fde = (void *)read_pointer(&ptr,
- ptr +
- tableSize,
- hdr[3]);
- }
+ if (table == NULL)
+ return -EINVAL;
+
+ hdr = table->header;
+
+ smp_rmb();
+ if (hdr && hdr->version == 1) {
+ switch (hdr->table_enc & DW_EH_PE_FORM) {
+ case DW_EH_PE_native:
+ hdrEntrySz = sizeof(unsigned long);
+ break;
+ case DW_EH_PE_data2:
+ hdrEntrySz = 2;
+ break;
+ case DW_EH_PE_data4:
+ hdrEntrySz = 4;
+ break;
+ case DW_EH_PE_data8:
+ hdrEntrySz = 8;
+ break;
+ default:
+ hdrEntrySz = 0;
+ break;
}

- if (fde != NULL) {
- cie = cie_for_fde(fde, table);
- 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 (!(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;
- cie = NULL;
+ ptr = (const u8*)(hdr->eh_frame_ptr);
+ end = (const u8*)(hdr) + table->hdrsz;
+ if (hdrEntrySz
+ && read_pointer(&ptr, end, hdr->eh_frame_ptr_enc) /* eh_frame_ptr */
+ == (unsigned long)table->address
+ && (i = read_pointer(&ptr, end, hdr->fde_count_enc)) > 0 /* fde_count */
+ && i == (end - ptr) / (2 * hdrEntrySz)
+ && !((end - ptr) % (2 * hdrEntrySz))) {
+ do {
+ const u8 *cur = ptr + (i / 2) * (2 * hdrEntrySz);
+
+ startLoc = read_pointer(&cur, cur + hdrEntrySz, hdr->table_enc);
+ if (pc < startLoc)
+ i /= 2;
+ else {
+ ptr = cur - hdrEntrySz;
+ i = (i + 1) / 2;
}
- } else {
+ } while (startLoc && i > 1);
+ if (i == 1
+ && (startLoc = read_pointer(&ptr, ptr + hdrEntrySz, hdr->table_enc)) != 0
+ && pc >= startLoc)
+ fde = (void *)read_pointer(&ptr, ptr + hdrEntrySz, hdr->table_enc);
+ }
+ }
+
+ if (fde != NULL) {
+ cie = cie_for_fde(fde, table);
+ 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 (!(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;
cie = NULL;
}
+ } else {
+ fde = NULL;
+ cie = NULL;
}
}
if (cie != NULL) {
--
1.9.1

2015-12-03 12:44:42

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 08/17] ARC: dw2 unwind: Don't verify FDE lookup table metadata

FDE Lookup table (eh_frame_header or it's equivalent constructed by hand)
is already setup correctly in setup_unwind_table(). There's no point
to re-parse it's header for every unwind call

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 37 ++++++++-----------------------------
1 file changed, 8 insertions(+), 29 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 9f5ed6873c52..9b34038a7582 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -900,35 +900,16 @@ int arc_unwind(struct unwind_frame_info *frame)
return -EINVAL;

hdr = table->header;
+ if (hdr == NULL)
+ return -EINVAL;

- smp_rmb();
- if (hdr && hdr->version == 1) {
- switch (hdr->table_enc & DW_EH_PE_FORM) {
- case DW_EH_PE_native:
- hdrEntrySz = sizeof(unsigned long);
- break;
- case DW_EH_PE_data2:
- hdrEntrySz = 2;
- break;
- case DW_EH_PE_data4:
- hdrEntrySz = 4;
- break;
- case DW_EH_PE_data8:
- hdrEntrySz = 8;
- break;
- default:
- hdrEntrySz = 0;
- break;
- }
+ hdrEntrySz = sizeof(unsigned long);
+ BUILD_BUG_ON(hdrEntrySz != sizeof(hdr->table[0].start));
+
+ ptr = (const u8*)(hdr->table);
+ end = (const u8*)(hdr) + table->hdrsz;
+ i = hdr->fde_count;

- ptr = (const u8*)(hdr->eh_frame_ptr);
- end = (const u8*)(hdr) + table->hdrsz;
- if (hdrEntrySz
- && read_pointer(&ptr, end, hdr->eh_frame_ptr_enc) /* eh_frame_ptr */
- == (unsigned long)table->address
- && (i = read_pointer(&ptr, end, hdr->fde_count_enc)) > 0 /* fde_count */
- && i == (end - ptr) / (2 * hdrEntrySz)
- && !((end - ptr) % (2 * hdrEntrySz))) {
do {
const u8 *cur = ptr + (i / 2) * (2 * hdrEntrySz);

@@ -944,8 +925,6 @@ int arc_unwind(struct unwind_frame_info *frame)
&& (startLoc = read_pointer(&ptr, ptr + hdrEntrySz, hdr->table_enc)) != 0
&& pc >= startLoc)
fde = (void *)read_pointer(&ptr, ptr + hdrEntrySz, hdr->table_enc);
- }
- }

if (fde != NULL) {
cie = cie_for_fde(fde, table);
--
1.9.1

2015-12-03 12:42:22

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 09/17] ARC: dw2 unwind: Use striaght forward code to implement binary lookup

The {start, pc} entries are already word aligned, no need to use
read_pointer() inefficiency !

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 40 +++++++++++++++++-----------------------
1 file changed, 17 insertions(+), 23 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 9b34038a7582..6a09ffa0b697 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -866,7 +866,7 @@ int arc_unwind(struct unwind_frame_info *frame)
const u8 *ptr = NULL, *end = NULL;
unsigned long pc = UNW_PC(frame);
unsigned long startLoc = 0, endLoc = 0, cfa;
- unsigned i;
+ unsigned i, s, e;
signed ptrType = -1;
uleb128_t retAddrReg = 0;
const struct unwind_table *table;
@@ -874,7 +874,6 @@ int arc_unwind(struct unwind_frame_info *frame)
unsigned long *fptr;
unsigned long addr;
struct eh_frame_header *hdr;
- unsigned long hdrEntrySz;

unw_debug("\nUNWIND FRAME: -------------------------------------\n");
unw_debug("PC\t\t: 0x%lx %pS\nr31 [BLINK]\t: 0x%lx %pS\nr28 [SP]\t: 0x%lx\nr27 [FP]\t: 0x%lx\n",
@@ -903,28 +902,23 @@ int arc_unwind(struct unwind_frame_info *frame)
if (hdr == NULL)
return -EINVAL;

- hdrEntrySz = sizeof(unsigned long);
- BUILD_BUG_ON(hdrEntrySz != sizeof(hdr->table[0].start));
+ s = i = 0;
+ e = hdr->fde_count - 1;

- ptr = (const u8*)(hdr->table);
- end = (const u8*)(hdr) + table->hdrsz;
- i = hdr->fde_count;
-
- do {
- const u8 *cur = ptr + (i / 2) * (2 * hdrEntrySz);
-
- startLoc = read_pointer(&cur, cur + hdrEntrySz, hdr->table_enc);
- if (pc < startLoc)
- i /= 2;
- else {
- ptr = cur - hdrEntrySz;
- i = (i + 1) / 2;
- }
- } while (startLoc && i > 1);
- if (i == 1
- && (startLoc = read_pointer(&ptr, ptr + hdrEntrySz, hdr->table_enc)) != 0
- && pc >= startLoc)
- fde = (void *)read_pointer(&ptr, ptr + hdrEntrySz, hdr->table_enc);
+ do {
+ if (pc < startLoc)
+ e = i;
+ else
+ s = i;
+ i = s + (e - s) / 2;
+ startLoc = hdr->table[i].start;
+ unw_debug("s %d e %d i %d startLoc %lx\n", s, e, i, startLoc)
+ } while ((e - s) > 1);
+
+ if (pc >= startLoc)
+ fde = (u32 *)hdr->table[i].fde;
+ else
+ return -EINVAL;

if (fde != NULL) {
cie = cie_for_fde(fde, table);
--
1.9.1

2015-12-03 12:42:30

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 10/17] ARC: dw2 unwind: CIE parsing/validation done only once at startup

There is only 1 CIE per unwind table and applicable to all FDEs, so
validate it only once. No need to do validate it when prcoessing the FDE
itself.

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 336 +++++++++++++++++++++--------------------------
1 file changed, 153 insertions(+), 183 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 6a09ffa0b697..f2a486d9dac2 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -122,6 +122,13 @@ static struct unwind_table {
} core, init;
const void *address;
unsigned long size;
+ struct cie {
+ unsigned version:8, aug:8, pad:16;
+ uleb128_t codeAlign;
+ sleb128_t dataAlign;
+ int fde_pointer_type;
+ uleb128_t retAddrReg;
+ } cie;
struct eh_frame_header *header;
unsigned long hdrsz;
struct unwind_table *link;
@@ -140,20 +147,18 @@ struct unwind_item {

struct unwind_state {
uleb128_t loc, org;
- const u8 *cieStart, *cieEnd;
uleb128_t codeAlign;
sleb128_t dataAlign;
struct cfa {
uleb128_t reg, offs;
} cfa;
struct unwind_item regs[ARRAY_SIZE(reg_info)];
- unsigned stackDepth:8;
- unsigned version:8;
+ unsigned stackDepth;
const u8 *label;
const u8 *stack[MAX_STACK_DEPTH];
};

-static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 };
+static struct cfa seed_CFA = { ARRAY_SIZE(reg_info), 1 };

static struct unwind_table *find_table(unsigned long pc)
{
@@ -214,7 +219,7 @@ void __init arc_unwind_init(void)

static const u32 bad_cie, not_fde;
static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *);
-static signed fde_pointer_type(const u32 *cie);
+static int cie_validate(const u32 *cie, struct cie *t_cie);

static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2)
{
@@ -245,7 +250,10 @@ static void __init setup_unwind_table(struct unwind_table *table,
unsigned long tableSize = table->size, hdrSize;
unsigned n;
const u32 *fde;
+ int ptrType;
struct eh_frame_header *header;
+ int n_cie = 0, len = 0;
+ char cie_orig[64];

if (table->header)
return;
@@ -261,19 +269,26 @@ static void __init setup_unwind_table(struct unwind_table *table,
tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde;
tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) {
const u32 *cie = cie_for_fde(fde, table);
- signed ptrType;

- if (cie == &not_fde)
+ if (cie == &not_fde) {
+ if (n_cie++ == 0) {
+ len = cie_validate(&fde[0], &table->cie);
+ if (!len)
+ panic("Invalid CIE\n");
+
+ memcpy(&cie_orig[0], &fde[0], len);
+ } else {
+ if (memcmp(&fde[0], &cie_orig[0], len) != 0)
+ panic("Multiple CIEs not same\n");
+ }
continue;
+ }
if (cie == NULL || cie == &bad_cie)
return;
- ptrType = fde_pointer_type(cie);
- if (ptrType < 0)
- return;

ptr = (const u8 *)(fde + 2);
if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde,
- ptrType)) {
+ table->cie.fde_pointer_type)) {
/* FIXME_Rajesh We have 4 instances of null addresses
* instead of the initial loc addr
* return;
@@ -303,19 +318,17 @@ static void __init setup_unwind_table(struct unwind_table *table,

BUILD_BUG_ON(offsetof(typeof(*header), table)
% __alignof(typeof(*header->table)));
+
+ ptrType = table->cie.fde_pointer_type;
for (fde = table->address, tableSize = table->size, n = 0;
tableSize;
tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) {
- /* const u32 *cie = fde + 1 - fde[1] / sizeof(*fde); */
- const u32 *cie = (const u32 *)(fde[1]);
-
if (fde[1] == 0xffffffff)
continue; /* this is a CIE */
ptr = (const u8 *)(fde + 2);
header->table[n].start = read_pointer(&ptr,
- (const u8 *)(fde + 1) +
- *fde,
- fde_pointer_type(cie));
+ (const u8 *)(fde + 1) + *fde,
+ ptrType);
header->table[n].fde = (unsigned long)fde;
++n;
}
@@ -572,65 +585,6 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end,
return value;
}

-static signed fde_pointer_type(const u32 *cie)
-{
- const u8 *ptr = (const u8 *)(cie + 2);
- unsigned version = *ptr;
-
- if (version != 1)
- return -1; /* unsupported */
-
- if (*++ptr) {
- const char *aug;
- const u8 *end = (const u8 *)(cie + 1) + *cie;
- uleb128_t len;
-
- /* check if augmentation size is first (and thus present) */
- if (*ptr != 'z')
- return -1;
-
- /* check if augmentation string is nul-terminated */
- aug = (const void *)ptr;
- ptr = memchr(aug, 0, end - ptr);
- if (ptr == NULL)
- return -1;
-
- ++ptr; /* skip terminator */
- get_uleb128(&ptr, end); /* skip code alignment */
- get_sleb128(&ptr, end); /* skip data alignment */
- /* skip return address column */
- version <= 1 ? (void) ++ptr : (void)get_uleb128(&ptr, end);
- len = get_uleb128(&ptr, end); /* augmentation length */
-
- if (ptr + len < ptr || ptr + len > end)
- return -1;
-
- end = ptr + len;
- while (*++aug) {
- if (ptr >= end)
- return -1;
- switch (*aug) {
- case 'L':
- ++ptr;
- break;
- case 'P':{
- signed ptrType = *ptr++;
-
- if (!read_pointer(&ptr, end, ptrType)
- || ptr > end)
- return -1;
- }
- break;
- case 'R':
- return *ptr;
- default:
- return -1;
- }
- }
- }
- return DW_EH_PE_native | DW_EH_PE_abs;
-}
-
static int advance_loc(unsigned long delta, struct unwind_state *state, char *str)
{
state->loc += delta * state->codeAlign;
@@ -682,14 +636,6 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
int result = 1;
u8 opcode;

- if (start != state->cieStart) {
- state->loc = state->org;
- result =
- processCFI(state->cieStart, state->cieEnd, 0, ptrType,
- state);
- if (targetLoc == 0 && state->label == NULL)
- return result;
- }
for (ptr.p8 = start; result && ptr.p8 < end;) {
char *str = NULL;
switch (*ptr.p8 >> 6) {
@@ -775,7 +721,7 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,

state->label =
state->stack[state->stackDepth - 1];
- memcpy(&state->cfa, &badCFA,
+ memcpy(&state->cfa, &seed_CFA,
sizeof(state->cfa));
memset(state->regs, 0,
sizeof(state->regs));
@@ -857,12 +803,102 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
targetLoc < state->loc && */ state->label == NULL));
}

+/*
+ * Returns length of CIE (0 if invalid)
+ */
+static int cie_validate(const u32 *cie, struct cie *t_cie)
+{
+ const u8 *ptr = NULL, *end = NULL;
+ u8 version;
+ uleb128_t retAddrReg = 0;
+ int ptrType = DW_EH_PE_native | DW_EH_PE_abs;
+ struct unwind_state state;
+
+ if (cie == NULL)
+ return 0;
+
+ ptr = (const u8 *)(cie + 2);
+ end = (const u8 *)(cie + 1) + *cie;
+ if ((version = *ptr) != 1)
+ return 0; /* unsupported version */
+
+ t_cie->version = version;
+
+ if (*++ptr) {
+ /* check if augmentation size is first (thus present) */
+ if (*ptr == 'z') {
+ t_cie->aug = 1;
+ while (++ptr < end && *ptr) {
+ switch (*ptr) {
+ /* chk for ignorable or already handled
+ * nul-terminated augmentation string */
+ case 'L':
+ case 'P':
+ case 'R':
+ continue;
+ case 'S': /* signal frame */
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ if (ptr >= end || *ptr)
+ return 0;
+ }
+ ++ptr; /* skip terminator */
+
+ t_cie->codeAlign = get_uleb128(&ptr, end);
+ t_cie->dataAlign = get_sleb128(&ptr, end);
+
+ if (t_cie->codeAlign == 0 || t_cie->dataAlign == 0 || ptr >= end)
+ return 0;
+
+ retAddrReg = version <= 1 ? *ptr++ : get_uleb128(&ptr, end);
+ t_cie->retAddrReg = retAddrReg;
+
+ /* skip augmentation data */
+ if (((const char *)(cie + 2))[1] == 'z') {
+ get_uleb128(&ptr, end); /* augSize */
+
+ if (*ptr++ == 'R') /* FDE pointer encoding type */
+ ptrType = *ptr;
+ }
+ t_cie->fde_pointer_type = ptrType;
+
+ if (ptr > end
+ || retAddrReg >= ARRAY_SIZE(reg_info)
+ || REG_INVALID(retAddrReg)
+ || reg_info[retAddrReg].width != sizeof(unsigned long))
+ return 0;
+
+ unw_debug("\nDwarf Unwinder setup: CIE Info:\n");
+ unw_debug("code Align: %lu\n", t_cie->codeAlign);
+ unw_debug("data Align: %ld\n", t_cie->dataAlign);
+ unw_debug("Return Address register r%d\n", (int)t_cie->retAddrReg);
+ unw_debug("FDE pointer type %d\n", ptrType);
+ unw_debug("CFI Instructions for CIE:\n");
+
+ memset(&state, sizeof(state), 0);
+
+ /* CIE has rules for Default CFA */
+ if (!processCFI(ptr, end, 0, ptrType, &state)
+ || state.cfa.reg >= ARRAY_SIZE(reg_info)
+ || state.cfa.offs % sizeof(unsigned long))
+ return 0;
+
+ memcpy(&seed_CFA, &state.cfa, sizeof(state.cfa));
+
+ return end - (const u8 *)cie;
+}
+
/* Unwind to previous to frame. Returns 0 if successful, negative
* number in case of an error. */
int arc_unwind(struct unwind_frame_info *frame)
{
#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
- const u32 *fde = NULL, *cie = NULL;
+ const u32 *fde = NULL;
const u8 *ptr = NULL, *end = NULL;
unsigned long pc = UNW_PC(frame);
unsigned long startLoc = 0, endLoc = 0, cfa;
@@ -920,107 +956,41 @@ int arc_unwind(struct unwind_frame_info *frame)
else
return -EINVAL;

- if (fde != NULL) {
- cie = cie_for_fde(fde, table);
- 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 (!(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;
- cie = NULL;
- }
- } else {
- fde = NULL;
- cie = NULL;
- }
- }
- if (cie != NULL) {
- memset(&state, 0, sizeof(state));
- state.cieEnd = ptr; /* keep here temporarily */
- ptr = (const u8 *)(cie + 2);
- end = (const u8 *)(cie + 1) + *cie;
- if ((state.version = *ptr) != 1)
- cie = NULL; /* unsupported version */
- else if (*++ptr) {
- /* check if augmentation size is first (thus present) */
- if (*ptr == 'z') {
- while (++ptr < end && *ptr) {
- switch (*ptr) {
- /* chk for ignorable or already handled
- * nul-terminated augmentation string */
- case 'L':
- case 'P':
- case 'R':
- continue;
- case 'S':
- /* signal frame not handled */
- continue;
- default:
- break;
- }
- break;
- }
- }
- if (ptr >= end || *ptr)
- cie = NULL;
- }
- ++ptr;
- }
- if (cie != NULL) {
- /* get code aligment factor */
- state.codeAlign = get_uleb128(&ptr, end);
- /* get data aligment factor */
- state.dataAlign = get_sleb128(&ptr, end);
- if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end)
- cie = NULL;
- else {
- retAddrReg =
- state.version <= 1 ? *ptr++ : get_uleb128(&ptr,
- end);
- unw_debug("CIE Frame Info:\n");
- unw_debug("return Address register 0x%lx\n",
- retAddrReg);
- unw_debug("data Align: %ld\n", state.dataAlign);
- unw_debug("code Align: %lu\n", state.codeAlign);
- /* skip augmentation */
- if (((const char *)(cie + 2))[1] == 'z') {
- uleb128_t augSize = get_uleb128(&ptr, end);
-
- ptr += augSize;
- }
- if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info)
- || REG_INVALID(retAddrReg)
- || reg_info[retAddrReg].width !=
- sizeof(unsigned long))
- cie = NULL;
- }
+ memset(&state, 0, sizeof(state));
+ ptr = (const u8 *)(fde + 2);
+ end = (const u8 *)(fde + 1) + *fde;
+
+ ptrType = table->cie.fde_pointer_type;
+ if (read_pointer(&ptr, end, ptrType) != startLoc)
+ return -EINVAL;
+
+ if (!(ptrType & DW_EH_PE_indirect))
+ ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed;
+
+ endLoc = startLoc + read_pointer(&ptr, end, ptrType);
+
+ /* For symbols not present, this is mostly hit (not startLoc check above) */
+ if (pc >= endLoc) {
+ unw_debug("Unwindo info missing for PC %lx: {%lx,%lx}\n",
+ pc, startLoc, endLoc);
+ return -EINVAL;
}
- if (cie != NULL) {
- state.cieStart = ptr;
- ptr = state.cieEnd;
- state.cieEnd = end;
- end = (const u8 *)(fde + 1) + *fde;
- /* skip augmentation */
- if (((const char *)(cie + 2))[1] == 'z') {
- uleb128_t augSize = get_uleb128(&ptr, end);
-
- if ((ptr += augSize) > end)
- fde = NULL;
- }
+
+ if (table->cie.aug) {
+ uleb128_t augSize = get_uleb128(&ptr, end);
+
+ if ((ptr += augSize) > end)
+ return -EINVAL;
}
- if (cie == NULL || fde == NULL)
- return -ENXIO;

- state.org = startLoc;
- memcpy(&state.cfa, &badCFA, sizeof(state.cfa));
+ state.org = state.loc = startLoc;
+ memcpy(&state.cfa, &seed_CFA, sizeof(state.cfa));
+ state.codeAlign = table->cie.codeAlign;
+ state.dataAlign = table->cie.dataAlign;
+
+ retAddrReg = table->cie.retAddrReg;

- unw_debug("\nProcess CFA\n");
+ unw_debug("\nProcess FDE:\n");

/* process instructions
* For ARC, we optimize by having blink(retAddrReg) with
--
1.9.1

2015-12-03 12:45:12

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 11/17] ARC: dw2 unwind: Elide REG_INVALID check

REG_INVALID checks if reg_info[*].width is zero, which is compile time
contant set to NON zero. No point in checking it.

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 20 ++------------------
1 file changed, 2 insertions(+), 18 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index f2a486d9dac2..b1a520afcbcb 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -48,10 +48,6 @@ static const struct {
unsigned width:BITS_PER_LONG / 2;
} reg_info[] = { UNW_REGISTER_INFO };

-#ifndef REG_INVALID
-#define REG_INVALID(r) (reg_info[r].width == 0)
-#endif
-
#define DW_CFA_nop 0x00
#define DW_CFA_set_loc 0x01
#define DW_CFA_advance_loc1 0x02
@@ -868,9 +864,7 @@ static int cie_validate(const u32 *cie, struct cie *t_cie)
t_cie->fde_pointer_type = ptrType;

if (ptr > end
- || retAddrReg >= ARRAY_SIZE(reg_info)
- || REG_INVALID(retAddrReg)
- || reg_info[retAddrReg].width != sizeof(unsigned long))
+ || retAddrReg >= ARRAY_SIZE(reg_info))
return 0;

unw_debug("\nDwarf Unwinder setup: CIE Info:\n");
@@ -1017,19 +1011,11 @@ int arc_unwind(struct unwind_frame_info *frame)
state.cfa.reg, state.cfa.offs, cfa);

for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
- if (REG_INVALID(i)) {
- if (state.regs[i].where == Nowhere)
- continue;
- return -EIO;
- }
switch (state.regs[i].where) {
default:
break;
case Register:
- 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)
+ if (state.regs[i].value >= ARRAY_SIZE(reg_info))
return -EIO;
switch (reg_info[state.regs[i].value].width) {
case sizeof(u8):
@@ -1061,8 +1047,6 @@ int arc_unwind(struct unwind_frame_info *frame)
fptr = (unsigned long *)(&frame->regs);
for (i = 0; i < ARRAY_SIZE(state.regs); ++i, fptr++) {

- if (REG_INVALID(i))
- continue;
switch (state.regs[i].where) {
case Nowhere:
if (reg_info[i].width != sizeof(UNW_SP(frame))
--
1.9.1

2015-12-03 12:42:39

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 12/17] ARC: dw2 unwind: Elide a loop if DW_CFA_register not present

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index b1a520afcbcb..6eb377c102c6 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -149,7 +149,7 @@ struct unwind_state {
uleb128_t reg, offs;
} cfa;
struct unwind_item regs[ARRAY_SIZE(reg_info)];
- unsigned stackDepth;
+ unsigned stackDepth:8, has_cfa_register:8;
const u8 *label;
const u8 *stack[MAX_STACK_DEPTH];
};
@@ -697,6 +697,7 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
case DW_CFA_register:
str = "cfa_register: ";
value = get_uleb128(&ptr.p8, end);
+ state->has_cfa_register = 1;
set_rule(value, Register, get_uleb128(&ptr.p8, end), state, str);
break;
case DW_CFA_remember_state:
@@ -1010,11 +1011,18 @@ int arc_unwind(struct unwind_frame_info *frame)
unw_debug("\nCFA reg: r%ld, off: %ld => [SP] 0x%lx\n",
state.cfa.reg, state.cfa.offs, cfa);

- for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
- switch (state.regs[i].where) {
- default:
- break;
- case Register:
+ /*
+ * For DW_CFA_register instruction, save registers initial state
+ * Note that a seperate pass is needed (vs. doing this in main loop below)
+ * to capture state of those regs at time of entry into function since
+ * the main loop below can possibly clobber them due to CFA ops of
+ * function being processed
+ *
+ * Since ARC gcc doesn't seem to generate those (ABI thing ?) try to
+ * optimize out the loop
+ */
+ for (i = 0; state.has_cfa_register && i < ARRAY_SIZE(state.regs); ++i) {
+ if (state.regs[i].where == Register) {
if (state.regs[i].value >= ARRAY_SIZE(reg_info))
return -EIO;
switch (reg_info[state.regs[i].value].width) {
@@ -1039,7 +1047,6 @@ int arc_unwind(struct unwind_frame_info *frame)
default:
return -EIO;
}
- break;
}
}

--
1.9.1

2015-12-03 12:42:42

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 13/17] ARC: dw2 unwind: Assume all regs to be unsigned long

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/include/asm/unwind.h | 3 +-
arch/arc/kernel/unwind.c | 74 +++----------------------------------------
2 files changed, 6 insertions(+), 71 deletions(-)

diff --git a/arch/arc/include/asm/unwind.h b/arch/arc/include/asm/unwind.h
index 03ace2cc8bc5..a1dbcf6cb361 100644
--- a/arch/arc/include/asm/unwind.h
+++ b/arch/arc/include/asm/unwind.h
@@ -66,8 +66,7 @@ struct unwind_frame_info {
BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \
% FIELD_SIZEOF(struct unwind_frame_info, f)) \
+ offsetof(struct unwind_frame_info, f) \
- / FIELD_SIZEOF(struct unwind_frame_info, f), \
- FIELD_SIZEOF(struct unwind_frame_info, f) \
+ / FIELD_SIZEOF(struct unwind_frame_info, f) \
}
#define PTREGS_INFO(f) EXTRA_INFO(regs.f)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 6eb377c102c6..b2053b55d076 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -45,7 +45,6 @@ do { \

static const struct {
unsigned offs:BITS_PER_LONG / 2;
- unsigned width:BITS_PER_LONG / 2;
} reg_info[] = { UNW_REGISTER_INFO };

#define DW_CFA_nop 0x00
@@ -996,7 +995,6 @@ int arc_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))
return -EIO;

@@ -1025,28 +1023,8 @@ int arc_unwind(struct unwind_frame_info *frame)
if (state.regs[i].where == Register) {
if (state.regs[i].value >= ARRAY_SIZE(reg_info))
return -EIO;
- switch (reg_info[state.regs[i].value].width) {
- case sizeof(u8):
- state.regs[i].value =
- FRAME_REG(state.regs[i].value, const u8);
- break;
- case sizeof(u16):
- state.regs[i].value =
- FRAME_REG(state.regs[i].value, const u16);
- break;
- case sizeof(u32):
- state.regs[i].value =
- FRAME_REG(state.regs[i].value, const u32);
- break;
-#ifdef CONFIG_64BIT
- case sizeof(u64):
- state.regs[i].value =
- FRAME_REG(state.regs[i].value, const u64);
- break;
-#endif
- default:
- return -EIO;
- }
+
+ state.regs[i].value = FRAME_REG(state.regs[i].value, unsigned long);
}
}

@@ -1056,35 +1034,15 @@ int arc_unwind(struct unwind_frame_info *frame)

switch (state.regs[i].where) {
case Nowhere:
- if (reg_info[i].width != sizeof(UNW_SP(frame))
- || &FRAME_REG(i, __typeof__(UNW_SP(frame)))
+ if (&FRAME_REG(i, __typeof__(UNW_SP(frame)))
!= &UNW_SP(frame))
continue;
UNW_SP(frame) = cfa;
break;
case Register:
- switch (reg_info[i].width) {
- case sizeof(u8):
- FRAME_REG(i, u8) = state.regs[i].value;
- break;
- case sizeof(u16):
- FRAME_REG(i, u16) = state.regs[i].value;
- break;
- case sizeof(u32):
- FRAME_REG(i, u32) = state.regs[i].value;
- break;
-#ifdef CONFIG_64BIT
- case sizeof(u64):
- FRAME_REG(i, u64) = state.regs[i].value;
- break;
-#endif
- default:
- return -EIO;
- }
+ FRAME_REG(i, unsigned long) = state.regs[i].value;
break;
case Value:
- if (reg_info[i].width != sizeof(unsigned long))
- return -EIO;
FRAME_REG(i, unsigned long) = cfa + state.regs[i].value
* state.dataAlign;
break;
@@ -1098,29 +1056,7 @@ int arc_unwind(struct unwind_frame_info *frame)
|| addr + sizeof(unsigned long) > endLoc)
return -EIO;

- switch (reg_info[i].width) {
- case sizeof(u8):
- __get_user(FRAME_REG(i, u8),
- (u8 __user *)addr);
- break;
- case sizeof(u16):
- __get_user(FRAME_REG(i, u16),
- (u16 __user *)addr);
- break;
- case sizeof(u32):
- __get_user(FRAME_REG(i, u32),
- (u32 __user *)addr);
- break;
-#ifdef CONFIG_64BIT
- case sizeof(u64):
- __get_user(FRAME_REG(i, u64),
- (u64 __user *)addr);
- break;
-#endif
- default:
- return -EIO;
- }
-
+ __get_user(FRAME_REG(i, unsigned long), (unsigned long __user *)addr);
break;
}
unw_debug("r%d: 0x%lx\n", i, *fptr);
--
1.9.1

2015-12-03 12:42:48

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 14/17] ARC: dw2 unwind: No need for __get_user

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index b2053b55d076..4f739716f908 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -1056,7 +1056,7 @@ int arc_unwind(struct unwind_frame_info *frame)
|| addr + sizeof(unsigned long) > endLoc)
return -EIO;

- __get_user(FRAME_REG(i, unsigned long), (unsigned long __user *)addr);
+ FRAME_REG(i, unsigned long) = *(unsigned long *)addr;
break;
}
unw_debug("r%d: 0x%lx\n", i, *fptr);
--
1.9.1

2015-12-03 12:45:09

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 15/17] ARC: dw2 unwind: Single exit point for instrumentation

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 31 +++++++++++++++++++------------
1 file changed, 19 insertions(+), 12 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 4f739716f908..e02d974ea789 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -904,6 +904,7 @@ int arc_unwind(struct unwind_frame_info *frame)
unsigned long *fptr;
unsigned long addr;
struct eh_frame_header *hdr;
+ int ret = -EINVAL;

unw_debug("\nUNWIND FRAME: -------------------------------------\n");
unw_debug("PC\t\t: 0x%lx %pS\nr31 [BLINK]\t: 0x%lx %pS\nr28 [SP]\t: 0x%lx\nr27 [FP]\t: 0x%lx\n",
@@ -912,7 +913,7 @@ int arc_unwind(struct unwind_frame_info *frame)
UNW_SP(frame), UNW_FP(frame));

if (UNW_PC(frame) == 0)
- return -EINVAL;
+ goto bad_unw;

#ifdef UNWIND_DEBUG0
{
@@ -926,11 +927,11 @@ int arc_unwind(struct unwind_frame_info *frame)

table = find_table(pc);
if (table == NULL)
- return -EINVAL;
+ goto bad_unw;

hdr = table->header;
if (hdr == NULL)
- return -EINVAL;
+ goto bad_unw;

s = i = 0;
e = hdr->fde_count - 1;
@@ -948,7 +949,7 @@ int arc_unwind(struct unwind_frame_info *frame)
if (pc >= startLoc)
fde = (u32 *)hdr->table[i].fde;
else
- return -EINVAL;
+ goto bad_unw;

memset(&state, 0, sizeof(state));
ptr = (const u8 *)(fde + 2);
@@ -956,7 +957,7 @@ int arc_unwind(struct unwind_frame_info *frame)

ptrType = table->cie.fde_pointer_type;
if (read_pointer(&ptr, end, ptrType) != startLoc)
- return -EINVAL;
+ goto bad_unw;

if (!(ptrType & DW_EH_PE_indirect))
ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed;
@@ -967,14 +968,15 @@ int arc_unwind(struct unwind_frame_info *frame)
if (pc >= endLoc) {
unw_debug("Unwindo info missing for PC %lx: {%lx,%lx}\n",
pc, startLoc, endLoc);
- return -EINVAL;
+ ret = -ENOENT;
+ goto bad_unw;
}

if (table->cie.aug) {
uleb128_t augSize = get_uleb128(&ptr, end);

if ((ptr += augSize) > end)
- return -EINVAL;
+ goto bad_unw;
}

state.org = state.loc = startLoc;
@@ -995,8 +997,10 @@ int arc_unwind(struct unwind_frame_info *frame)
|| state.loc > endLoc
/* || state.regs[retAddrReg].where == Nowhere */
|| state.cfa.reg >= ARRAY_SIZE(reg_info)
- || state.cfa.offs % sizeof(unsigned long))
- return -EIO;
+ || state.cfa.offs % sizeof(unsigned long)) {
+ ret = -EIO;
+ goto bad_unw;
+ }

cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs;
startLoc = min_t(unsigned long, UNW_SP(frame), cfa);
@@ -1022,7 +1026,7 @@ int arc_unwind(struct unwind_frame_info *frame)
for (i = 0; state.has_cfa_register && i < ARRAY_SIZE(state.regs); ++i) {
if (state.regs[i].where == Register) {
if (state.regs[i].value >= ARRAY_SIZE(reg_info))
- return -EIO;
+ goto bad_unw;

state.regs[i].value = FRAME_REG(state.regs[i].value, unsigned long);
}
@@ -1054,7 +1058,7 @@ int arc_unwind(struct unwind_frame_info *frame)
|| addr < startLoc
|| addr + sizeof(unsigned long) < addr
|| addr + sizeof(unsigned long) > endLoc)
- return -EIO;
+ goto bad_unw;

FRAME_REG(i, unsigned long) = *(unsigned long *)addr;
break;
@@ -1062,7 +1066,10 @@ int arc_unwind(struct unwind_frame_info *frame)
unw_debug("r%d: 0x%lx\n", i, *fptr);
}

- return 0;
+ ret = 0;
+bad_unw:
+
+ return ret;
#undef FRAME_REG
}
EXPORT_SYMBOL(arc_unwind);
--
1.9.1

2015-12-03 12:43:38

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 16/17] ARC: dw2 unwind: skip regs not updated

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index e02d974ea789..d5ffb4a78104 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -142,13 +142,14 @@ struct unwind_item {

struct unwind_state {
uleb128_t loc, org;
+ uleb128_t first_reg;
uleb128_t codeAlign;
sleb128_t dataAlign;
struct cfa {
uleb128_t reg, offs;
} cfa;
struct unwind_item regs[ARRAY_SIZE(reg_info)];
- unsigned stackDepth:8, has_cfa_register:8;
+ unsigned stackDepth:8, has_cfa_register:8, pad:16;
const u8 *label;
const u8 *stack[MAX_STACK_DEPTH];
};
@@ -597,6 +598,7 @@ static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value,
if (reg < ARRAY_SIZE(state->regs)) {
state->regs[reg].where = where;
state->regs[reg].value = value;
+ state->first_reg = min(reg, state->first_reg);

#ifdef UNWIND_DEBUG
switch (where) {
@@ -979,6 +981,7 @@ int arc_unwind(struct unwind_frame_info *frame)
goto bad_unw;
}

+ state.first_reg = 13;
state.org = state.loc = startLoc;
memcpy(&state.cfa, &seed_CFA, sizeof(state.cfa));
state.codeAlign = table->cie.codeAlign;
@@ -1032,9 +1035,9 @@ int arc_unwind(struct unwind_frame_info *frame)
}
}

- unw_debug("\nRegister state after evaluation with realtime Stack:\n");
+ unw_debug("\nRegister state after evaluation with realtime Stack @ %ld :\n", state.first_reg);
fptr = (unsigned long *)(&frame->regs);
- for (i = 0; i < ARRAY_SIZE(state.regs); ++i, fptr++) {
+ for (i = state.first_reg; i < ARRAY_SIZE(state.regs); ++i, fptr++) {

switch (state.regs[i].where) {
case Nowhere:
--
1.9.1

2015-12-03 12:43:03

by Vineet Gupta

[permalink] [raw]
Subject: [PATCH 17/17] xxx: instrument

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/unwind.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index d5ffb4a78104..91f310465970 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -889,6 +889,19 @@ static int cie_validate(const u32 *cie, struct cie *t_cie)
return end - (const u8 *)cie;
}

+#define UNWIND_INSTRUMENT
+
+#ifdef UNWIND_INSTRUMENT
+u32 unw_t_min = 0xffffff, unw_t_max, unw_t_n;
+u64 unw_t_avg;
+
+static unsigned int get_c(void)
+{
+ write_aux_reg(0x600, 0x42); // ARC_REG_MCIP_CMD
+ return read_aux_reg(0x602); // ARC_REG_MCIP_READBACK
+}
+#endif
+
/* Unwind to previous to frame. Returns 0 if successful, negative
* number in case of an error. */
int arc_unwind(struct unwind_frame_info *frame)
@@ -907,7 +920,9 @@ int arc_unwind(struct unwind_frame_info *frame)
unsigned long addr;
struct eh_frame_header *hdr;
int ret = -EINVAL;
-
+#ifdef UNWIND_INSTRUMENT
+ unsigned int t0 = get_c(), t1, delta;
+#endif
unw_debug("\nUNWIND FRAME: -------------------------------------\n");
unw_debug("PC\t\t: 0x%lx %pS\nr31 [BLINK]\t: 0x%lx %pS\nr28 [SP]\t: 0x%lx\nr27 [FP]\t: 0x%lx\n",
UNW_PC(frame), (void *)UNW_PC(frame),
@@ -1072,6 +1087,15 @@ int arc_unwind(struct unwind_frame_info *frame)
ret = 0;
bad_unw:

+#ifdef UNWIND_INSTRUMENT
+ t1 = get_c();
+ delta = t1 - t0;
+ unw_t_min = min(unw_t_min, delta);
+ unw_t_max = max(unw_t_max, delta);
+ unw_t_n++;
+ unw_t_avg += delta;
+ printk("unw %d %s %lx\n", delta, !ret ? "O K":"nok", pc);
+#endif
return ret;
#undef FRAME_REG
}
--
1.9.1