Commit 1fcbe027 authored by Chris Metcalf's avatar Chris Metcalf

arch/tile: support backtracing on TILE-Gx

This functionality was stubbed out until recently.  Now we support our
normal backtracing API on TILE-Gx as well as on TILE64/TILEPro.
This change includes a tweak to the instruction encoding caused by
adding addxli for compat mode.
Signed-off-by: default avatarChris Metcalf <cmetcalf@tilera.com>
parent 32020eff
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
#include <arch/chip.h> #include <arch/chip.h>
#if CHIP_VA_WIDTH() > 32 #if defined(__tile__)
typedef unsigned long VirtualAddress;
#elif CHIP_VA_WIDTH() > 32
typedef unsigned long long VirtualAddress; typedef unsigned long long VirtualAddress;
#else #else
typedef unsigned int VirtualAddress; typedef unsigned int VirtualAddress;
......
...@@ -19,9 +19,6 @@ ...@@ -19,9 +19,6 @@
#include <arch/chip.h> #include <arch/chip.h>
#if TILE_CHIP < 10
#include <asm/opcode-tile.h> #include <asm/opcode-tile.h>
...@@ -29,6 +26,27 @@ ...@@ -29,6 +26,27 @@
#define TREG_LR 55 #define TREG_LR 55
#if TILE_CHIP >= 10
#define tile_bundle_bits tilegx_bundle_bits
#define TILE_MAX_INSTRUCTIONS_PER_BUNDLE TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE
#define TILE_BUNDLE_ALIGNMENT_IN_BYTES TILEGX_BUNDLE_ALIGNMENT_IN_BYTES
#define tile_decoded_instruction tilegx_decoded_instruction
#define tile_mnemonic tilegx_mnemonic
#define parse_insn_tile parse_insn_tilegx
#define TILE_OPC_IRET TILEGX_OPC_IRET
#define TILE_OPC_ADDI TILEGX_OPC_ADDI
#define TILE_OPC_ADDLI TILEGX_OPC_ADDLI
#define TILE_OPC_INFO TILEGX_OPC_INFO
#define TILE_OPC_INFOL TILEGX_OPC_INFOL
#define TILE_OPC_JRP TILEGX_OPC_JRP
#define TILE_OPC_MOVE TILEGX_OPC_MOVE
#define OPCODE_STORE TILEGX_OPC_ST
typedef long long bt_int_reg_t;
#else
#define OPCODE_STORE TILE_OPC_SW
typedef int bt_int_reg_t;
#endif
/** A decoded bundle used for backtracer analysis. */ /** A decoded bundle used for backtracer analysis. */
struct BacktraceBundle { struct BacktraceBundle {
tile_bundle_bits bits; tile_bundle_bits bits;
...@@ -41,7 +59,7 @@ struct BacktraceBundle { ...@@ -41,7 +59,7 @@ struct BacktraceBundle {
/* This implementation only makes sense for native tools. */ /* This implementation only makes sense for native tools. */
/** Default function to read memory. */ /** Default function to read memory. */
static bool bt_read_memory(void *result, VirtualAddress addr, static bool bt_read_memory(void *result, VirtualAddress addr,
size_t size, void *extra) unsigned int size, void *extra)
{ {
/* FIXME: this should do some horrible signal stuff to catch /* FIXME: this should do some horrible signal stuff to catch
* SEGV cleanly and fail. * SEGV cleanly and fail.
...@@ -106,6 +124,12 @@ static bool bt_has_addi_sp(const struct BacktraceBundle *bundle, int *adjust) ...@@ -106,6 +124,12 @@ static bool bt_has_addi_sp(const struct BacktraceBundle *bundle, int *adjust)
find_matching_insn(bundle, TILE_OPC_ADDI, vals, 2); find_matching_insn(bundle, TILE_OPC_ADDI, vals, 2);
if (insn == NULL) if (insn == NULL)
insn = find_matching_insn(bundle, TILE_OPC_ADDLI, vals, 2); insn = find_matching_insn(bundle, TILE_OPC_ADDLI, vals, 2);
#if TILE_CHIP >= 10
if (insn == NULL)
insn = find_matching_insn(bundle, TILEGX_OPC_ADDXLI, vals, 2);
if (insn == NULL)
insn = find_matching_insn(bundle, TILEGX_OPC_ADDXI, vals, 2);
#endif
if (insn == NULL) if (insn == NULL)
return false; return false;
...@@ -190,13 +214,52 @@ static inline bool bt_has_move_r52_sp(const struct BacktraceBundle *bundle) ...@@ -190,13 +214,52 @@ static inline bool bt_has_move_r52_sp(const struct BacktraceBundle *bundle)
return find_matching_insn(bundle, TILE_OPC_MOVE, vals, 2) != NULL; return find_matching_insn(bundle, TILE_OPC_MOVE, vals, 2) != NULL;
} }
/** Does this bundle contain the instruction 'sw sp, lr'? */ /** Does this bundle contain a store of lr to sp? */
static inline bool bt_has_sw_sp_lr(const struct BacktraceBundle *bundle) static inline bool bt_has_sw_sp_lr(const struct BacktraceBundle *bundle)
{ {
static const int vals[2] = { TREG_SP, TREG_LR }; static const int vals[2] = { TREG_SP, TREG_LR };
return find_matching_insn(bundle, TILE_OPC_SW, vals, 2) != NULL; return find_matching_insn(bundle, OPCODE_STORE, vals, 2) != NULL;
}
#if TILE_CHIP >= 10
/** Track moveli values placed into registers. */
static inline void bt_update_moveli(const struct BacktraceBundle *bundle,
int moveli_args[])
{
int i;
for (i = 0; i < bundle->num_insns; i++) {
const struct tile_decoded_instruction *insn =
&bundle->insns[i];
if (insn->opcode->mnemonic == TILEGX_OPC_MOVELI) {
int reg = insn->operand_values[0];
moveli_args[reg] = insn->operand_values[1];
}
}
} }
/** Does this bundle contain an 'add sp, sp, reg' instruction
* from a register that we saw a moveli into, and if so, what
* is the value in the register?
*/
static bool bt_has_add_sp(const struct BacktraceBundle *bundle, int *adjust,
int moveli_args[])
{
static const int vals[2] = { TREG_SP, TREG_SP };
const struct tile_decoded_instruction *insn =
find_matching_insn(bundle, TILEGX_OPC_ADDX, vals, 2);
if (insn) {
int reg = insn->operand_values[2];
if (moveli_args[reg]) {
*adjust = moveli_args[reg];
return true;
}
}
return false;
}
#endif
/** Locates the caller's PC and SP for a program starting at the /** Locates the caller's PC and SP for a program starting at the
* given address. * given address.
*/ */
...@@ -227,6 +290,11 @@ static void find_caller_pc_and_caller_sp(CallerLocation *location, ...@@ -227,6 +290,11 @@ static void find_caller_pc_and_caller_sp(CallerLocation *location,
int next_bundle = 0; int next_bundle = 0;
VirtualAddress pc; VirtualAddress pc;
#if TILE_CHIP >= 10
/* Naively try to track moveli values to support addx for -m32. */
int moveli_args[TILEGX_NUM_REGISTERS] = { 0 };
#endif
/* Default to assuming that the caller's sp is the current sp. /* Default to assuming that the caller's sp is the current sp.
* This is necessary to handle the case where we start backtracing * This is necessary to handle the case where we start backtracing
* right at the end of the epilog. * right at the end of the epilog.
...@@ -380,7 +448,11 @@ static void find_caller_pc_and_caller_sp(CallerLocation *location, ...@@ -380,7 +448,11 @@ static void find_caller_pc_and_caller_sp(CallerLocation *location,
if (!sp_determined) { if (!sp_determined) {
int adjust; int adjust;
if (bt_has_addi_sp(&bundle, &adjust)) { if (bt_has_addi_sp(&bundle, &adjust)
#if TILE_CHIP >= 10
|| bt_has_add_sp(&bundle, &adjust, moveli_args)
#endif
) {
location->sp_location = SP_LOC_OFFSET; location->sp_location = SP_LOC_OFFSET;
if (adjust <= 0) { if (adjust <= 0) {
...@@ -427,6 +499,11 @@ static void find_caller_pc_and_caller_sp(CallerLocation *location, ...@@ -427,6 +499,11 @@ static void find_caller_pc_and_caller_sp(CallerLocation *location,
sp_determined = true; sp_determined = true;
} }
} }
#if TILE_CHIP >= 10
/* Track moveli arguments for -m32 mode. */
bt_update_moveli(&bundle, moveli_args);
#endif
} }
if (bt_has_iret(&bundle)) { if (bt_has_iret(&bundle)) {
...@@ -502,11 +579,10 @@ void backtrace_init(BacktraceIterator *state, ...@@ -502,11 +579,10 @@ void backtrace_init(BacktraceIterator *state,
break; break;
} }
/* The frame pointer should theoretically be aligned mod 8. If /* If the frame pointer is not aligned to the basic word size
* it's not even aligned mod 4 then something terrible happened * something terrible happened and we should mark it as invalid.
* and we should mark it as invalid.
*/ */
if (fp % 4 != 0) if (fp % sizeof(bt_int_reg_t) != 0)
fp = -1; fp = -1;
/* -1 means "don't know initial_frame_caller_pc". */ /* -1 means "don't know initial_frame_caller_pc". */
...@@ -547,9 +623,16 @@ void backtrace_init(BacktraceIterator *state, ...@@ -547,9 +623,16 @@ void backtrace_init(BacktraceIterator *state,
state->read_memory_func_extra = read_memory_func_extra; state->read_memory_func_extra = read_memory_func_extra;
} }
/* Handle the case where the register holds more bits than the VA. */
static bool valid_addr_reg(bt_int_reg_t reg)
{
return ((VirtualAddress)reg == reg);
}
bool backtrace_next(BacktraceIterator *state) bool backtrace_next(BacktraceIterator *state)
{ {
VirtualAddress next_fp, next_pc, next_frame[2]; VirtualAddress next_fp, next_pc;
bt_int_reg_t next_frame[2];
if (state->fp == -1) { if (state->fp == -1) {
/* No parent frame. */ /* No parent frame. */
...@@ -563,11 +646,9 @@ bool backtrace_next(BacktraceIterator *state) ...@@ -563,11 +646,9 @@ bool backtrace_next(BacktraceIterator *state)
} }
next_fp = next_frame[1]; next_fp = next_frame[1];
if (next_fp % 4 != 0) { if (!valid_addr_reg(next_frame[1]) ||
/* Caller's frame pointer is suspect, so give up. next_fp % sizeof(bt_int_reg_t) != 0) {
* Technically it should be aligned mod 8, but we will /* Caller's frame pointer is suspect, so give up. */
* be forgiving here.
*/
return false; return false;
} }
...@@ -585,7 +666,7 @@ bool backtrace_next(BacktraceIterator *state) ...@@ -585,7 +666,7 @@ bool backtrace_next(BacktraceIterator *state)
} else { } else {
/* Get the caller PC from the frame linkage area. */ /* Get the caller PC from the frame linkage area. */
next_pc = next_frame[0]; next_pc = next_frame[0];
if (next_pc == 0 || if (!valid_addr_reg(next_frame[0]) || next_pc == 0 ||
next_pc % TILE_BUNDLE_ALIGNMENT_IN_BYTES != 0) { next_pc % TILE_BUNDLE_ALIGNMENT_IN_BYTES != 0) {
/* The PC is suspect, so give up. */ /* The PC is suspect, so give up. */
return false; return false;
...@@ -599,23 +680,3 @@ bool backtrace_next(BacktraceIterator *state) ...@@ -599,23 +680,3 @@ bool backtrace_next(BacktraceIterator *state)
return true; return true;
} }
#else /* TILE_CHIP < 10 */
void backtrace_init(BacktraceIterator *state,
BacktraceMemoryReader read_memory_func,
void *read_memory_func_extra,
VirtualAddress pc, VirtualAddress lr,
VirtualAddress sp, VirtualAddress r52)
{
state->pc = pc;
state->sp = sp;
state->fp = -1;
state->initial_frame_caller_pc = -1;
state->read_memory_func = read_memory_func;
state->read_memory_func_extra = read_memory_func_extra;
}
bool backtrace_next(BacktraceIterator *state) { return false; }
#endif /* TILE_CHIP < 10 */
...@@ -108,7 +108,6 @@ static bool read_memory_func(void *result, VirtualAddress address, ...@@ -108,7 +108,6 @@ static bool read_memory_func(void *result, VirtualAddress address,
/* Return a pt_regs pointer for a valid fault handler frame */ /* Return a pt_regs pointer for a valid fault handler frame */
static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt) static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt)
{ {
#ifndef __tilegx__
const char *fault = NULL; /* happy compiler */ const char *fault = NULL; /* happy compiler */
char fault_buf[64]; char fault_buf[64];
VirtualAddress sp = kbt->it.sp; VirtualAddress sp = kbt->it.sp;
...@@ -146,7 +145,6 @@ static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt) ...@@ -146,7 +145,6 @@ static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt)
} }
if (!kbt->profile || (INT_MASK(p->faultnum) & QUEUED_INTERRUPTS) == 0) if (!kbt->profile || (INT_MASK(p->faultnum) & QUEUED_INTERRUPTS) == 0)
return p; return p;
#endif
return NULL; return NULL;
} }
...@@ -351,12 +349,6 @@ void tile_show_stack(struct KBacktraceIterator *kbt, int headers) ...@@ -351,12 +349,6 @@ void tile_show_stack(struct KBacktraceIterator *kbt, int headers)
kbt->task->pid, kbt->task->tgid, kbt->task->comm, kbt->task->pid, kbt->task->tgid, kbt->task->comm,
smp_processor_id(), get_cycles()); smp_processor_id(), get_cycles());
} }
#ifdef __tilegx__
if (kbt->is_current) {
__insn_mtspr(SPR_SIM_CONTROL,
SIM_DUMP_SPR_ARG(SIM_DUMP_BACKTRACE));
}
#endif
kbt->verbose = 1; kbt->verbose = 1;
i = 0; i = 0;
for (; !KBacktraceIterator_end(kbt); KBacktraceIterator_next(kbt)) { for (; !KBacktraceIterator_end(kbt); KBacktraceIterator_next(kbt)) {
......
...@@ -128,7 +128,9 @@ static int special_ill(bundle_bits bundle, int *sigp, int *codep) ...@@ -128,7 +128,9 @@ static int special_ill(bundle_bits bundle, int *sigp, int *codep)
#ifdef __tilegx__ #ifdef __tilegx__
if ((bundle & TILEGX_BUNDLE_MODE_MASK) != 0) if ((bundle & TILEGX_BUNDLE_MODE_MASK) != 0)
return 0; return 0;
if (get_Opcode_X1(bundle) != UNARY_OPCODE_X1) if (get_Opcode_X1(bundle) != RRR_0_OPCODE_X1)
return 0;
if (get_RRROpcodeExtension_X1(bundle) != UNARY_RRR_0_OPCODE_X1)
return 0; return 0;
if (get_UnaryOpcodeExtension_X1(bundle) != ILL_UNARY_OPCODE_X1) if (get_UnaryOpcodeExtension_X1(bundle) != ILL_UNARY_OPCODE_X1)
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment