linux-snps-arc.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [RFC 00/17] ARC Dwarf unwinder improvements
@ 2015-12-03 12:40 Vineet Gupta
  2015-12-03 12:40 ` [PATCH 01/17] ARC: dw2 unwind: Elide generation of const propagated clones Vineet Gupta
                   ` (16 more replies)
  0 siblings, 17 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:40 UTC (permalink / raw)
  To: linux-snps-arc

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

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 01/17] ARC: dw2 unwind: Elide generation of const propagated clones
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
@ 2015-12-03 12:40 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 02/17] ARC: dw2 unwind: remove unused cruft Vineet Gupta
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:40 UTC (permalink / raw)
  To: linux-snps-arc

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 <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 02/17] ARC: dw2 unwind: remove unused cruft
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
  2015-12-03 12:40 ` [PATCH 01/17] ARC: dw2 unwind: Elide generation of const propagated clones Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 03/17] ARC: dw2 unwind: Remove handling of for signal frame Vineet Gupta
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 03/17] ARC: dw2 unwind: Remove handling of for signal frame
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
  2015-12-03 12:40 ` [PATCH 01/17] ARC: dw2 unwind: Elide generation of const propagated clones Vineet Gupta
  2015-12-03 12:41 ` [PATCH 02/17] ARC: dw2 unwind: remove unused cruft Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 04/17] ARC: dw2 unwind: Remove FP based unwinding Vineet Gupta
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 04/17] ARC: dw2 unwind: Remove FP based unwinding
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (2 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 03/17] ARC: dw2 unwind: Remove handling of for signal frame Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 05/17] ARC: dw2 unwind: Better printing Vineet Gupta
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

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 <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 05/17] ARC: dw2 unwind: Better printing
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (3 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 04/17] ARC: dw2 unwind: Remove FP based unwinding Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 06/17] ARC: dw2 unwind: Don't verify Main FDE Table size everytime Vineet Gupta
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 06/17] ARC: dw2 unwind: Don't verify Main FDE Table size everytime
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (4 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 05/17] ARC: dw2 unwind: Better printing Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 07/17] ARC: dw2 unwind: Refactor the FDE lookup table (eh_frame_header) code Vineet Gupta
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

This is already done at boot time in setup_unwind_table()

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 07/17] ARC: dw2 unwind: Refactor the FDE lookup table (eh_frame_header) code
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (5 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 06/17] ARC: dw2 unwind: Don't verify Main FDE Table size everytime Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 08/17] ARC: dw2 unwind: Don't verify FDE lookup table metadata Vineet Gupta
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

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

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 08/17] ARC: dw2 unwind: Don't verify FDE lookup table metadata
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (6 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 07/17] ARC: dw2 unwind: Refactor the FDE lookup table (eh_frame_header) code Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 09/17] ARC: dw2 unwind: Use striaght forward code to implement binary lookup Vineet Gupta
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

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 <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 09/17] ARC: dw2 unwind: Use striaght forward code to implement binary lookup
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (7 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 08/17] ARC: dw2 unwind: Don't verify FDE lookup table metadata Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 10/17] ARC: dw2 unwind: CIE parsing/validation done only once at startup Vineet Gupta
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

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

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 10/17] ARC: dw2 unwind: CIE parsing/validation done only once at startup
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (8 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 09/17] ARC: dw2 unwind: Use striaght forward code to implement binary lookup Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 11/17] ARC: dw2 unwind: Elide REG_INVALID check Vineet Gupta
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

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 <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 11/17] ARC: dw2 unwind: Elide REG_INVALID check
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (9 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 10/17] ARC: dw2 unwind: CIE parsing/validation done only once at startup Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 12/17] ARC: dw2 unwind: Elide a loop if DW_CFA_register not present Vineet Gupta
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

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 <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 12/17] ARC: dw2 unwind: Elide a loop if DW_CFA_register not present
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (10 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 11/17] ARC: dw2 unwind: Elide REG_INVALID check Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 13/17] ARC: dw2 unwind: Assume all regs to be unsigned long Vineet Gupta
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 13/17] ARC: dw2 unwind: Assume all regs to be unsigned long
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (11 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 12/17] ARC: dw2 unwind: Elide a loop if DW_CFA_register not present Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 14/17] ARC: dw2 unwind: No need for __get_user Vineet Gupta
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 14/17] ARC: dw2 unwind: No need for __get_user
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (12 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 13/17] ARC: dw2 unwind: Assume all regs to be unsigned long Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 15/17] ARC: dw2 unwind: Single exit point for instrumentation Vineet Gupta
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 15/17] ARC: dw2 unwind: Single exit point for instrumentation
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (13 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 14/17] ARC: dw2 unwind: No need for __get_user Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 16/17] ARC: dw2 unwind: skip regs not updated Vineet Gupta
  2015-12-03 12:41 ` [PATCH 17/17] xxx: instrument Vineet Gupta
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 16/17] ARC: dw2 unwind: skip regs not updated
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (14 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 15/17] ARC: dw2 unwind: Single exit point for instrumentation Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  2015-12-03 12:41 ` [PATCH 17/17] xxx: instrument Vineet Gupta
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 17/17] xxx: instrument
  2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
                   ` (15 preceding siblings ...)
  2015-12-03 12:41 ` [PATCH 16/17] ARC: dw2 unwind: skip regs not updated Vineet Gupta
@ 2015-12-03 12:41 ` Vineet Gupta
  16 siblings, 0 replies; 18+ messages in thread
From: Vineet Gupta @ 2015-12-03 12:41 UTC (permalink / raw)
  To: linux-snps-arc

Signed-off-by: Vineet Gupta <vgupta at synopsys.com>
---
 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2015-12-03 12:41 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-03 12:40 [RFC 00/17] ARC Dwarf unwinder improvements Vineet Gupta
2015-12-03 12:40 ` [PATCH 01/17] ARC: dw2 unwind: Elide generation of const propagated clones Vineet Gupta
2015-12-03 12:41 ` [PATCH 02/17] ARC: dw2 unwind: remove unused cruft Vineet Gupta
2015-12-03 12:41 ` [PATCH 03/17] ARC: dw2 unwind: Remove handling of for signal frame Vineet Gupta
2015-12-03 12:41 ` [PATCH 04/17] ARC: dw2 unwind: Remove FP based unwinding Vineet Gupta
2015-12-03 12:41 ` [PATCH 05/17] ARC: dw2 unwind: Better printing Vineet Gupta
2015-12-03 12:41 ` [PATCH 06/17] ARC: dw2 unwind: Don't verify Main FDE Table size everytime Vineet Gupta
2015-12-03 12:41 ` [PATCH 07/17] ARC: dw2 unwind: Refactor the FDE lookup table (eh_frame_header) code Vineet Gupta
2015-12-03 12:41 ` [PATCH 08/17] ARC: dw2 unwind: Don't verify FDE lookup table metadata Vineet Gupta
2015-12-03 12:41 ` [PATCH 09/17] ARC: dw2 unwind: Use striaght forward code to implement binary lookup Vineet Gupta
2015-12-03 12:41 ` [PATCH 10/17] ARC: dw2 unwind: CIE parsing/validation done only once at startup Vineet Gupta
2015-12-03 12:41 ` [PATCH 11/17] ARC: dw2 unwind: Elide REG_INVALID check Vineet Gupta
2015-12-03 12:41 ` [PATCH 12/17] ARC: dw2 unwind: Elide a loop if DW_CFA_register not present Vineet Gupta
2015-12-03 12:41 ` [PATCH 13/17] ARC: dw2 unwind: Assume all regs to be unsigned long Vineet Gupta
2015-12-03 12:41 ` [PATCH 14/17] ARC: dw2 unwind: No need for __get_user Vineet Gupta
2015-12-03 12:41 ` [PATCH 15/17] ARC: dw2 unwind: Single exit point for instrumentation Vineet Gupta
2015-12-03 12:41 ` [PATCH 16/17] ARC: dw2 unwind: skip regs not updated Vineet Gupta
2015-12-03 12:41 ` [PATCH 17/17] xxx: instrument Vineet Gupta

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).