forked from Imagelibrary/rtems
libdebugger: Fixes to debugging, ARM support, locking, and gcc-7.1 warnings.
- Add `printk` support to aid multi-core debugging. - Add lock trace to aid lock debugging. - Fixes to gcc-7.1 warnings. - Fixes from ticket #2879. - Add verbose command controls. - Change using the RTEMS sys/lock.h API to manage exception threads. - ARM hardware breakpoint fixes. Support for SMP stepping is not implemented, this requires use of the context id register. Closes #2879.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -221,13 +222,19 @@ static arm_debug_hwbreak hw_breaks[ARM_HW_BREAKPOINT_MAX];
|
||||
//static arm_debug_hwbreak hw_watches[ARM_HW_WATCHPOINT_MAX];
|
||||
|
||||
#if TARGET_DEBUG
|
||||
void rtems_debugger_printk_lock(rtems_interrupt_lock_context* lock_context);
|
||||
void rtems_debugger_printk_unlock(rtems_interrupt_lock_context* lock_context);
|
||||
|
||||
static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
|
||||
static void
|
||||
target_printk(const char* format, ...)
|
||||
{
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
rtems_debugger_printk_lock(&lock_context);
|
||||
vprintk(format, ap);
|
||||
rtems_debugger_printk_unlock(&lock_context);
|
||||
va_end(ap);
|
||||
}
|
||||
static const char*
|
||||
@@ -260,6 +267,34 @@ mode_label(int mode)
|
||||
#define mode_labels(_m) NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* CP register access.
|
||||
*/
|
||||
#define ARM_CP_INSTR(_opc, _cp, _op1, _val, _CRn, _CRm, _op2) \
|
||||
#_opc " p" #_cp ", " #_op1 ", %[" #_val "], c" #_CRn ", c" #_CRm ", " #_op2 "\n"
|
||||
|
||||
#define ARM_CP_WRITE(_cp, _op1, _val, _CRn, _CRm, _op2) \
|
||||
do { \
|
||||
ARM_SWITCH_REG; \
|
||||
asm volatile( \
|
||||
ASM_ARM_MODE \
|
||||
ARM_CP_INSTR(mcr, _cp, _op1, val, _CRn, _CRm, _op2) \
|
||||
ASM_THUMB_MODE \
|
||||
: ARM_SWITCH_REG_ASM \
|
||||
: [val] "r" (_val)); \
|
||||
} while (0)
|
||||
|
||||
#define ARM_CP_READ(_cp, _op1, _val, _CRn, _CRm, _op2) \
|
||||
do { \
|
||||
ARM_SWITCH_REG; \
|
||||
asm volatile( \
|
||||
ASM_ARM_MODE \
|
||||
ARM_CP_INSTR(mrc, _cp, _op1, val, _CRn, _CRm, _op2) \
|
||||
ASM_THUMB_MODE \
|
||||
: ARM_SWITCH_REG_ASM, \
|
||||
[val] "=&r" (_val)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Read and write a CP14 register.
|
||||
*
|
||||
@@ -268,38 +303,31 @@ mode_label(int mode)
|
||||
* registers, you cannot program it. This means there is a switch table to do
|
||||
* this.
|
||||
*/
|
||||
#define ARM_CP14_INSTR(_opc, _val, _CRn, _CRm, _opc2) \
|
||||
#_opc " p14, 0, %[" #_val "], c" #_CRn ", c" #_CRm ", " #_opc2 "\n"
|
||||
#define ARM_CP14_WRITE(_val, _CRn, _CRm, _op2) \
|
||||
ARM_CP_WRITE(14, 0, _val, _CRn, _CRm, _op2)
|
||||
|
||||
#define ARM_CP14_WRITE(_val, _CRn, _CRm, _opc2) \
|
||||
do { \
|
||||
ARM_SWITCH_REG; \
|
||||
asm volatile( \
|
||||
ASM_ARM_MODE \
|
||||
ARM_CP14_INSTR(mcr, val, _CRn, _CRm, _opc2) \
|
||||
ASM_THUMB_MODE \
|
||||
: ARM_SWITCH_REG_ASM \
|
||||
: [val] "r" (_val)); \
|
||||
} while (0)
|
||||
#define ARM_CP14_READ(_val, _CRn, _CRm, _op2) \
|
||||
ARM_CP_READ(14, 0, _val, _CRn, _CRm, _op2)
|
||||
|
||||
#define ARM_CP14_READ(_val, _CRn, _CRm, _opc2) \
|
||||
do { \
|
||||
ARM_SWITCH_REG; \
|
||||
asm volatile( \
|
||||
ASM_ARM_MODE \
|
||||
ARM_CP14_INSTR(mrc, val, _CRn, _CRm, _opc2) \
|
||||
ASM_THUMB_MODE \
|
||||
: ARM_SWITCH_REG_ASM, \
|
||||
[val] "=&r" (_val)); \
|
||||
} while (0)
|
||||
/*
|
||||
* Read and write a CP15 register.
|
||||
*
|
||||
* The Context ID register is a process level context and used to scope
|
||||
* hardware break points.
|
||||
*/
|
||||
#define ARM_CP15_WRITE(_val, _op1, _CRn, _CRm, _op2) \
|
||||
ARM_CP_WRITE(15, _op1, _val, _CRn, _CRm, _op2)
|
||||
|
||||
#define ARM_CP15_READ(_val, _op1, _CRn, _CRm, _op2) \
|
||||
ARM_CP_READ(15, _op1, _val, _CRn, _CRm, _op2)
|
||||
|
||||
static int
|
||||
arm_debug_probe(rtems_debugger_target* target)
|
||||
{
|
||||
#define ID_VALUE(_i, _h, _l) ((_i >> _l) & ((1 << ((_h - _l) + 1)) -1))
|
||||
uint32_t val;
|
||||
const char const* vl = "[Invalid version]";
|
||||
const char const* labels[] = {
|
||||
const char* vl = "[Invalid version]";
|
||||
const char* const labels[] = {
|
||||
"ARMv6 [v6]",
|
||||
"ARMv6 [v6.1]",
|
||||
"ARMv7 [v7, all CP14 registers]",
|
||||
@@ -471,39 +499,63 @@ arm_debug_break_write_value(int bp, uint32_t value)
|
||||
static void
|
||||
arm_debug_break_clear(void)
|
||||
{
|
||||
arm_debug_hwbreak* bp = &hw_breaks[0];
|
||||
int i;
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
arm_debug_hwbreak* bp = &hw_breaks[0];
|
||||
int i;
|
||||
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
||||
for (i = 0; i < hw_breakpoints; ++i, ++bp) {
|
||||
bp->enabled = false;
|
||||
bp->loaded = false;
|
||||
}
|
||||
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
||||
}
|
||||
|
||||
static inline void
|
||||
arm_debug_set_context_id(const uint32_t id)
|
||||
{
|
||||
ARM_CP15_WRITE(id, 0, 13, 0, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* You can only load the hardware breaks points when in the SVC mode or the
|
||||
* single step inverted break point will trigger.
|
||||
*/
|
||||
static void
|
||||
arm_debug_break_load(void)
|
||||
{
|
||||
arm_debug_hwbreak* bp = &hw_breaks[0];
|
||||
int i;
|
||||
for (i = 0; i < hw_breakpoints; ++i, ++bp) {
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
arm_debug_hwbreak* bp = &hw_breaks[0];
|
||||
int i;
|
||||
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
||||
if (bp->enabled && !bp->loaded) {
|
||||
arm_debug_set_context_id(0xdead1111);
|
||||
arm_debug_break_write_value(0, bp->value);
|
||||
arm_debug_break_write_control(0, bp->control);
|
||||
}
|
||||
++bp;
|
||||
for (i = 1; i < hw_breakpoints; ++i, ++bp) {
|
||||
if (bp->enabled && !bp->loaded) {
|
||||
bp->loaded = true;
|
||||
target_printk("]]} hwbp: %i: v:%08lx c:%08lx l:%08x\n",
|
||||
i, bp->value, bp->control, bp->length);
|
||||
arm_debug_break_write_value(i, bp->value);
|
||||
arm_debug_break_write_control(i, bp->control);
|
||||
}
|
||||
}
|
||||
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
||||
}
|
||||
|
||||
static void
|
||||
arm_debug_break_unload(void)
|
||||
{
|
||||
arm_debug_hwbreak* bp = &hw_breaks[0];
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
arm_debug_hwbreak* bp = &hw_breaks[0];
|
||||
int i;
|
||||
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
||||
arm_debug_set_context_id(0);
|
||||
for (i = 0; i < hw_breakpoints; ++i, ++bp) {
|
||||
bp->loaded = false;
|
||||
arm_debug_break_write_control(i, 0);
|
||||
}
|
||||
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
||||
}
|
||||
|
||||
#if NOT_USED_BUT_KEEPING
|
||||
@@ -937,9 +989,9 @@ rtems_debugger_target_enable(void)
|
||||
{
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
debug_session_active = true;
|
||||
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
||||
arm_debug_break_unload();
|
||||
arm_debug_break_clear();
|
||||
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
||||
rtems_debugger_target_set_vectors();
|
||||
rtems_interrupt_lock_release(&target_lock, &lock_context);
|
||||
return 0;
|
||||
@@ -953,6 +1005,8 @@ rtems_debugger_target_disable(void)
|
||||
void* text_begin;
|
||||
void* text_end;
|
||||
#endif
|
||||
arm_debug_break_unload();
|
||||
arm_debug_break_clear();
|
||||
rtems_interrupt_lock_acquire(&target_lock, &lock_context);
|
||||
debug_session_active = false;
|
||||
#if DOES_NOT_WORK
|
||||
@@ -1063,11 +1117,11 @@ rtems_debugger_target_write_regs(rtems_debugger_thread* thread)
|
||||
uint32_t* regs = &thread->registers[0];
|
||||
|
||||
/*
|
||||
* Only write to debugger controlled threads. Do not touch the registers
|
||||
* for threads blocked in the context switcher.
|
||||
* Only write to debugger controlled exception threads. Do not touch the
|
||||
* registers for threads blocked in the context switcher.
|
||||
*/
|
||||
if (rtems_debugger_thread_flag(thread,
|
||||
RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING)) {
|
||||
RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION)) {
|
||||
CPU_Exception_frame* frame = thread->frame;
|
||||
frame->register_r0 = regs[REG_R0];
|
||||
frame->register_r1 = regs[REG_R1];
|
||||
@@ -1131,81 +1185,76 @@ rtems_debugger_target_tcb_sp(rtems_debugger_thread* thread)
|
||||
int
|
||||
rtems_debugger_target_thread_stepping(rtems_debugger_thread* thread)
|
||||
{
|
||||
if (rtems_debugger_thread_flag(thread,
|
||||
(RTEMS_DEBUGGER_THREAD_FLAG_STEP |
|
||||
RTEMS_DEBUGGER_THREAD_FLAG_STEPPING))) {
|
||||
if (rtems_debugger_thread_flag(thread, RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
|
||||
/*
|
||||
* Single stepping and range stepping uses hardware debug breakpoint
|
||||
* 0. This is reserved for single stepping.
|
||||
*/
|
||||
CPU_Exception_frame* frame = thread->frame;
|
||||
arm_debug_hwbreak* bp = &hw_breaks[0];
|
||||
int i;
|
||||
for (i = 0; i < hw_breakpoints; ++i, ++bp) {
|
||||
if (!bp->enabled) {
|
||||
const uint32_t addr = (intptr_t) frame->register_pc;
|
||||
const bool thumb = (FRAME_SR & (1 << 5)) != 0 ? true : false;
|
||||
uint32_t bas;
|
||||
target_printk("[} stepping: %s\n", bp->enabled ? "yes" : "no");
|
||||
if (!bp->enabled) {
|
||||
const uint32_t addr = (intptr_t) frame->register_pc;
|
||||
const bool thumb = (FRAME_SR & (1 << 5)) != 0 ? true : false;
|
||||
uint32_t bas;
|
||||
|
||||
bp->enabled = true;
|
||||
bp->loaded = false;
|
||||
bp->address = frame->register_pc;
|
||||
bp->frame = frame;
|
||||
bp->length = sizeof(uint32_t);
|
||||
bp->enabled = true;
|
||||
bp->loaded = false;
|
||||
bp->address = frame->register_pc;
|
||||
bp->frame = frame;
|
||||
bp->length = sizeof(uint32_t);
|
||||
|
||||
if (thumb) {
|
||||
uint16_t instr = *((uint16_t*) frame->register_pc);
|
||||
switch (instr & 0xf800) {
|
||||
case 0xe800:
|
||||
case 0xf000:
|
||||
case 0xf800:
|
||||
break;
|
||||
default:
|
||||
bp->length = sizeof(uint16_t);
|
||||
break;
|
||||
}
|
||||
if (thumb) {
|
||||
uint16_t instr = *((uint16_t*) frame->register_pc);
|
||||
switch (instr & 0xf800) {
|
||||
case 0xe800:
|
||||
case 0xf000:
|
||||
case 0xf800:
|
||||
break;
|
||||
default:
|
||||
bp->length = sizeof(uint16_t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* See table C3-2 Effect of byte address selection on Breakpoint
|
||||
* generation and "Instruction address comparisoin programming
|
||||
* examples.
|
||||
*/
|
||||
if (thumb) {
|
||||
if ((addr & (1 << 1)) == 0) {
|
||||
bas = 0x3; /* b0011 */
|
||||
}
|
||||
else {
|
||||
bas = 0xc; /* b1100 */
|
||||
}
|
||||
/*
|
||||
* See table C3-2 Effect of byte address selection on Breakpoint
|
||||
* generation and "Instruction address comparision programming
|
||||
* examples.
|
||||
*/
|
||||
if (thumb) {
|
||||
if ((addr & (1 << 1)) == 0) {
|
||||
bas = 0x3; /* b0011 */
|
||||
}
|
||||
else {
|
||||
bas = 0xf; /* b1111 */
|
||||
bas = 0xc; /* b1100 */
|
||||
}
|
||||
|
||||
arm_debug_break_setup(bp,
|
||||
addr & ~0x3,
|
||||
ARM_HW_BP_UNLINKED_INSTR_MISMATCH,
|
||||
bas,
|
||||
ARM_HW_BP_PRIV_PL0_SUP_SYS);
|
||||
|
||||
/*
|
||||
* Save the interrupt state before stepping if set.
|
||||
*/
|
||||
#if ARM_PSR_HAS_INT_MASK
|
||||
if ((FRAME_SR & CPSR_INTS_MASK) != 0) {
|
||||
uint32_t int_state;
|
||||
int_state =
|
||||
(frame->register_cpsr & CPSR_INTS_MASK) << RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE;
|
||||
thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED | int_state;
|
||||
}
|
||||
/*
|
||||
* Mask the interrupt when stepping.
|
||||
*/
|
||||
FRAME_SR |= CPSR_INTS_MASK;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
else {
|
||||
bas = 0xf; /* b1111 */
|
||||
}
|
||||
|
||||
arm_debug_break_setup(bp,
|
||||
addr & ~0x3,
|
||||
ARM_HW_BP_UNLINKED_INSTR_MISMATCH,
|
||||
bas,
|
||||
ARM_HW_BP_PRIV_PL0_SUP_SYS);
|
||||
|
||||
/*
|
||||
* Save the interrupt state before stepping if set.
|
||||
*/
|
||||
#if ARM_PSR_HAS_INT_MASK
|
||||
if ((FRAME_SR & CPSR_INTS_MASK) != 0) {
|
||||
uint32_t int_state;
|
||||
int_state =
|
||||
(frame->register_cpsr & CPSR_INTS_MASK) << RTEMS_DEBUGGER_THREAD_FLAG_TARGET_BASE;
|
||||
thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_INTS_DISABLED | int_state;
|
||||
}
|
||||
/*
|
||||
* Mask the interrupt when stepping.
|
||||
*/
|
||||
FRAME_SR |= CPSR_INTS_MASK;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -1215,7 +1264,7 @@ int
|
||||
rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame)
|
||||
{
|
||||
int sig = RTEMS_DEBUGGER_SIGNAL_HUP;
|
||||
#if defined(ARM_EXCEPTION_RESET)
|
||||
#if defined(ARM_MULTILIB_ARCH_V4)
|
||||
switch (frame->vector) {
|
||||
case ARM_EXCEPTION_RESET:
|
||||
case ARM_EXCEPTION_SWI:
|
||||
@@ -1242,6 +1291,22 @@ rtems_debugger_target_exception_to_signal(CPU_Exception_frame* frame)
|
||||
return sig;
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_target_hwbreak_insert(void)
|
||||
{
|
||||
/*
|
||||
* Do nothing, load on exit of the exception handler.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_target_hwbreak_remove(void)
|
||||
{
|
||||
arm_debug_break_unload();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_target_hwbreak_control(rtems_debugger_target_watchpoint wp,
|
||||
bool insert,
|
||||
@@ -1262,7 +1327,7 @@ rtems_debugger_target_cache_sync(rtems_debugger_target_swbreak* swbreak)
|
||||
*/
|
||||
rtems_cache_flush_multiple_data_lines(swbreak->address,
|
||||
sizeof(breakpoint));
|
||||
rtems_cache_invalidate_multiple_instruction_lines(swbreak->address,
|
||||
sizeof(breakpoint));
|
||||
rtems_cache_instruction_sync_after_code_change(swbreak->address,
|
||||
sizeof(breakpoint));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -180,11 +181,29 @@ static int rtems_shell_main_debugger(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(argv[1], "verbose") == 0) {
|
||||
if (!rtems_debugger_running()) {
|
||||
printf("error: debugger not running.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc == 3 && strcasecmp(argv[2], "on") == 0) {
|
||||
rtems_debugger_set_verbose(true);
|
||||
}
|
||||
else if (argc == 3 && strcasecmp(argv[2], "off") == 0) {
|
||||
rtems_debugger_set_verbose(false);
|
||||
}
|
||||
else {
|
||||
printf("debugger verbose: not on or off\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(argv[1], "help") == 0) {
|
||||
printf("debugger [start/stop/help] ...\n" \
|
||||
" start -v -R remote -d device -t secs -P priority -l [stdout,stderr,kernel]\n" \
|
||||
" stop\n" \
|
||||
" remote-debug <on/off>\n" \
|
||||
" verbose <on/off>\n" \
|
||||
" help\n");
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -23,10 +24,16 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define RTEMS_DEBUGGER_VERBOSE_LOCK 0
|
||||
#define RTEMS_DEBUGGER_PRINT_PRINTK 1
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <rtems/bspIo.h>
|
||||
#include <rtems/score/smp.h>
|
||||
|
||||
#include <rtems/rtems-debugger.h>
|
||||
#include <rtems/debugger/rtems-debugger-server.h>
|
||||
#include <rtems/debugger/rtems-debugger-remote.h>
|
||||
@@ -50,15 +57,15 @@ typedef int (*rtems_debugger_command)(uint8_t* buffer, int size);
|
||||
|
||||
typedef struct rtems_debugger_packet
|
||||
{
|
||||
const char const* label;
|
||||
const char* const label;
|
||||
rtems_debugger_command command;
|
||||
} rtems_debugger_packet;
|
||||
|
||||
/**
|
||||
* Common error strings.
|
||||
*/
|
||||
static const char const* r_OK = "OK";
|
||||
static const char const* r_E01 = "E01";
|
||||
static const char* const r_OK = "OK";
|
||||
static const char* const r_E01 = "E01";
|
||||
|
||||
/*
|
||||
* Global Debugger.
|
||||
@@ -73,13 +80,59 @@ static const char const* r_E01 = "E01";
|
||||
*/
|
||||
rtems_debugger_server* rtems_debugger;
|
||||
|
||||
/**
|
||||
* Print lock ot make the prints sequential. This is to debug the debugger in
|
||||
* SMP.
|
||||
*/
|
||||
#if RTEMS_DEBUGGER_PRINT_PRINTK
|
||||
RTEMS_INTERRUPT_LOCK_DEFINE(static, printk_lock, "printk_lock")
|
||||
#endif
|
||||
|
||||
void
|
||||
rtems_debugger_printk_lock(rtems_interrupt_lock_context* lock_context)
|
||||
{
|
||||
rtems_interrupt_lock_acquire(&printk_lock, lock_context);
|
||||
}
|
||||
|
||||
void
|
||||
rtems_debugger_printk_unlock(rtems_interrupt_lock_context* lock_context)
|
||||
{
|
||||
rtems_interrupt_lock_release(&printk_lock, lock_context);
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_clean_printf(const char* format, ...)
|
||||
{
|
||||
int len;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
if (RTEMS_DEBUGGER_PRINT_PRINTK) {
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
rtems_debugger_printk_lock(&lock_context);
|
||||
len = vprintk(format, ap);
|
||||
rtems_debugger_printk_unlock(&lock_context);
|
||||
}
|
||||
else
|
||||
len = rtems_vprintf(&rtems_debugger->printer, format, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_printf(const char* format, ...)
|
||||
{
|
||||
int len;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
len = rtems_vprintf(&rtems_debugger->printer, format, ap);
|
||||
if (RTEMS_DEBUGGER_PRINT_PRINTK) {
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
rtems_debugger_printk_lock(&lock_context);
|
||||
printk("[CPU:%d] ", (int) _SMP_Get_current_processor ());
|
||||
len = vprintk(format, ap);
|
||||
rtems_debugger_printk_unlock(&lock_context);
|
||||
}
|
||||
else
|
||||
len = rtems_vprintf(&rtems_debugger->printer, format, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
@@ -158,74 +211,31 @@ thread_id_decode(const char* data, DB_UINT* pid, DB_UINT* tid)
|
||||
static inline bool
|
||||
check_pid(DB_UINT pid)
|
||||
{
|
||||
return pid == 0|| rtems_debugger->pid == (pid_t) pid;
|
||||
return pid == 0 || rtems_debugger->pid == (pid_t) pid;
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
rtems_debugger_lock(void)
|
||||
{
|
||||
if (rtems_debugger->lock != 0) {
|
||||
rtems_status_code sc;
|
||||
sc = rtems_semaphore_obtain(rtems_debugger->lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
rtems_debugger_printf("error: rtems-db: lock: %s\n",
|
||||
rtems_status_text(sc));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
_Mutex_recursive_Acquire(&rtems_debugger->lock);
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
rtems_debugger_unlock(void)
|
||||
{
|
||||
if (rtems_debugger->lock != 0) {
|
||||
rtems_status_code sc;
|
||||
sc = rtems_semaphore_release(rtems_debugger->lock);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
rtems_debugger_printf("error: rtems-db: unlock: %s\n",
|
||||
rtems_status_text(sc));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
_Mutex_recursive_Release(&rtems_debugger->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_debugger_lock_create(void)
|
||||
{
|
||||
#define LOCK_ATTRIBUTES \
|
||||
RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY | RTEMS_BINARY_SEMAPHORE
|
||||
rtems_status_code sc;
|
||||
sc = rtems_semaphore_create(rtems_build_name('G', 'D', 'B', 's'),
|
||||
1,
|
||||
LOCK_ATTRIBUTES,
|
||||
0,
|
||||
&rtems_debugger->lock);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
rtems_debugger_printf("error: rtems-db: sema create: %s\n",
|
||||
rtems_status_text(sc));
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
_Mutex_recursive_Initialize_named(&rtems_debugger->lock, "DBlock");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rtems_debugger_lock_destroy(void)
|
||||
{
|
||||
rtems_debugger_lock();
|
||||
if (rtems_debugger->lock != 0) {
|
||||
rtems_status_code sc;
|
||||
rtems_semaphore_release(rtems_debugger->lock);
|
||||
sc = rtems_semaphore_delete(rtems_debugger->lock);
|
||||
rtems_debugger->lock = 0;
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
rtems_debugger_printf("error: rtems-db: sema delete: %s\n",
|
||||
rtems_status_text(sc));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -333,52 +343,19 @@ rtems_debugger_connected(void)
|
||||
bool
|
||||
rtems_debugger_server_events_running(void)
|
||||
{
|
||||
bool running;
|
||||
rtems_debugger_lock();
|
||||
running = rtems_debugger->events_running;
|
||||
rtems_debugger_unlock();
|
||||
return running;
|
||||
return rtems_debugger->events_running;
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_server_events_wake(void)
|
||||
void
|
||||
rtems_debugger_server_events_signal(void)
|
||||
{
|
||||
rtems_status_code sc;
|
||||
int r = 0;
|
||||
sc = rtems_event_send(rtems_debugger->events_task, RTEMS_EVENT_1);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
rtems_debugger_printf("error: rtems-db: event send: %s\n",
|
||||
rtems_status_text(sc));
|
||||
errno = EIO;
|
||||
r = -1;
|
||||
}
|
||||
return r;
|
||||
_Condition_Signal(&rtems_debugger->server_cond);
|
||||
}
|
||||
|
||||
static int
|
||||
static void
|
||||
rtems_debugger_server_events_wait(void)
|
||||
{
|
||||
rtems_event_set out = 0;
|
||||
rtems_status_code sc;
|
||||
int r = 0;
|
||||
rtems_debugger_unlock();
|
||||
while (true) {
|
||||
sc = rtems_event_receive(RTEMS_EVENT_1,
|
||||
RTEMS_EVENT_ALL | RTEMS_WAIT,
|
||||
RTEMS_NO_TIMEOUT,
|
||||
&out);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
rtems_debugger_printf("error: rtems-db: event receive: %s\n",
|
||||
rtems_status_text(sc));
|
||||
errno = EIO;
|
||||
r = -1;
|
||||
break;
|
||||
}
|
||||
if (out == RTEMS_EVENT_1)
|
||||
break;
|
||||
}
|
||||
rtems_debugger_lock();
|
||||
return r;
|
||||
_Condition_Wait_recursive(&rtems_debugger->server_cond, &rtems_debugger->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -432,8 +409,8 @@ rtems_debugger_remote_send(void)
|
||||
size_t i = 0;
|
||||
rtems_debugger_printf("rtems-db: put:%4zu: ", rtems_debugger->output_level);
|
||||
while (i < rtems_debugger->output_level)
|
||||
rtems_debugger_printf("%c", (char) rtems_debugger->output[i++]);
|
||||
rtems_debugger_printf("\n");
|
||||
rtems_debugger_clean_printf("%c", (char) rtems_debugger->output[i++]);
|
||||
rtems_debugger_clean_printf("\n");
|
||||
}
|
||||
|
||||
while (size) {
|
||||
@@ -533,14 +510,14 @@ rtems_debugger_remote_packet_in(void)
|
||||
}
|
||||
|
||||
if (rtems_debugger->remote_debug)
|
||||
rtems_debugger_printf("%c", c);
|
||||
rtems_debugger_clean_printf("%c", c);
|
||||
|
||||
switch (state) {
|
||||
case 'H':
|
||||
switch (c) {
|
||||
case '+':
|
||||
if (rtems_debugger->remote_debug) {
|
||||
rtems_debugger_printf(" [[ACK%s]]\n",
|
||||
rtems_debugger_clean_printf(" [[ACK%s]]\n",
|
||||
rtems_debugger->ack_pending ? "" : "?");
|
||||
remote_debug_header = true;
|
||||
}
|
||||
@@ -548,7 +525,7 @@ rtems_debugger_remote_packet_in(void)
|
||||
break;
|
||||
case '-':
|
||||
if (rtems_debugger->remote_debug) {
|
||||
rtems_debugger_printf(" [[NACK]]\n");
|
||||
rtems_debugger_clean_printf(" [[NACK]]\n");
|
||||
remote_debug_header = true;
|
||||
}
|
||||
/*
|
||||
@@ -561,13 +538,13 @@ rtems_debugger_remote_packet_in(void)
|
||||
csum = 0;
|
||||
in = 0;
|
||||
if (junk && rtems_debugger->remote_debug) {
|
||||
rtems_debugger_printf("\b [[junk dropped]]\nrtems-db: get: : $");
|
||||
rtems_debugger_clean_printf("\b [[junk dropped]]\nrtems-db: get: : $");
|
||||
remote_debug_header = false;
|
||||
}
|
||||
break;
|
||||
case '\x3':
|
||||
if (rtems_debugger->remote_debug)
|
||||
rtems_debugger_printf("^C [[BREAK]]\n");
|
||||
rtems_debugger_clean_printf("^C [[BREAK]]\n");
|
||||
rtems_debugger->ack_pending = false;
|
||||
rtems_debugger->input[0] = '^';
|
||||
rtems_debugger->input[1] = 'C';
|
||||
@@ -586,7 +563,7 @@ rtems_debugger_remote_packet_in(void)
|
||||
csum = 0;
|
||||
in = 0;
|
||||
if (rtems_debugger->remote_debug) {
|
||||
rtems_debugger_printf("\n");
|
||||
rtems_debugger_clean_printf("\n");
|
||||
remote_debug_header = true;
|
||||
}
|
||||
}
|
||||
@@ -613,12 +590,12 @@ rtems_debugger_remote_packet_in(void)
|
||||
if (csum == rx_csum) {
|
||||
state = 'F';
|
||||
if (rtems_debugger->remote_debug)
|
||||
rtems_debugger_printf("\n");
|
||||
rtems_debugger_clean_printf("\n");
|
||||
rtems_debugger_remote_send_ack();
|
||||
}
|
||||
else {
|
||||
if (rtems_debugger->remote_debug) {
|
||||
rtems_debugger_printf(" [[invalid checksum]]\n");
|
||||
rtems_debugger_clean_printf(" [[invalid checksum]]\n");
|
||||
remote_debug_header = true;
|
||||
rtems_debugger_remote_send_nack();
|
||||
}
|
||||
@@ -627,7 +604,7 @@ rtems_debugger_remote_packet_in(void)
|
||||
break;
|
||||
case 'F':
|
||||
if (rtems_debugger->remote_debug)
|
||||
rtems_debugger_printf(" [[extra data: 0x%02x]]", (int) c);
|
||||
rtems_debugger_clean_printf(" [[extra data: 0x%02x]]", (int) c);
|
||||
break;
|
||||
default:
|
||||
rtems_debugger_printf("rtems-db: bad state\n");
|
||||
@@ -810,6 +787,9 @@ remote_packet_dispatch(const rtems_debugger_packet* packet,
|
||||
if (strncmp(p->label,
|
||||
(const char*) &buffer[0],
|
||||
strlen(p->label)) == 0) {
|
||||
if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE_CMDS))
|
||||
rtems_debugger_printf("rtems-db: cmd: %s [%d] '%s'\n",
|
||||
p->label, size, (const char*) buffer);
|
||||
r = p->command(buffer, size);
|
||||
break;
|
||||
}
|
||||
@@ -904,7 +884,7 @@ remote_gq_thread_extra_info(uint8_t* buffer, int size)
|
||||
DB_UINT tid = 0;
|
||||
bool extended;
|
||||
extended = thread_id_decode(comma + 1, &pid, &tid);
|
||||
if (!extended || (extended && check_pid(pid))) {
|
||||
if (extended || check_pid(pid)) {
|
||||
int r;
|
||||
r = rtems_debugger_thread_find_index(tid);
|
||||
if (r >= 0) {
|
||||
@@ -954,7 +934,7 @@ remote_gq_supported(uint8_t* buffer, int size)
|
||||
p = strchr((const char*) buffer, ':');
|
||||
if (p != NULL)
|
||||
++p;
|
||||
while (p != NULL && p != '\0') {
|
||||
while (p != NULL && *p != '\0') {
|
||||
bool echo = false;
|
||||
char* sc;
|
||||
sc = strchr(p, ';');
|
||||
@@ -1022,8 +1002,8 @@ remote_gq_supported(uint8_t* buffer, int size)
|
||||
static int
|
||||
remote_gq_attached(uint8_t* buffer, int size)
|
||||
{
|
||||
const char const* response = "1";
|
||||
const char* colon = strchr((const char*) buffer, ':');
|
||||
const char* response = "1";
|
||||
const char* colon = strchr((const char*) buffer, ':');
|
||||
if (colon != NULL) {
|
||||
DB_UINT pid = hex_decode_uint((const uint8_t*) colon + 1);
|
||||
if ((pid_t) pid != rtems_debugger->pid)
|
||||
@@ -1061,7 +1041,7 @@ remote_general_query(uint8_t* buffer, int size)
|
||||
static int
|
||||
remote_gs_non_stop(uint8_t* buffer, int size)
|
||||
{
|
||||
const char const* response = r_E01;
|
||||
const char* response = r_E01;
|
||||
char* p = strchr((char*) buffer, ':');
|
||||
if (p != NULL) {
|
||||
++p;
|
||||
@@ -1173,6 +1153,7 @@ remote_v_continue(uint8_t* buffer, int size)
|
||||
if (r == 0)
|
||||
resume = true;
|
||||
break;
|
||||
case 'S':
|
||||
case 's':
|
||||
if (thread != NULL) {
|
||||
r = rtems_debugger_thread_step(thread);
|
||||
@@ -1202,6 +1183,7 @@ remote_v_continue(uint8_t* buffer, int size)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rtems_debugger_printf("rtems-db: vCont: unkown action: %c\n", action);
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
@@ -1210,6 +1192,7 @@ remote_v_continue(uint8_t* buffer, int size)
|
||||
}
|
||||
}
|
||||
else {
|
||||
rtems_debugger_printf("rtems-db: vCont: no colon\n");
|
||||
ok = false;
|
||||
}
|
||||
semi = strchr(semi + 1, ';');
|
||||
@@ -1257,8 +1240,8 @@ remote_v_packets(uint8_t* buffer, int size)
|
||||
static int
|
||||
remote_thread_select(uint8_t* buffer, int size)
|
||||
{
|
||||
const char const* response = r_OK;
|
||||
int* index = NULL;
|
||||
const char* response = r_OK;
|
||||
int* index = NULL;
|
||||
|
||||
if (buffer[1] == 'g')
|
||||
index = &rtems_debugger->threads->selector_gen;
|
||||
@@ -1299,10 +1282,10 @@ remote_thread_select(uint8_t* buffer, int size)
|
||||
static int
|
||||
remote_thread_alive(uint8_t* buffer, int size)
|
||||
{
|
||||
const char const* response = r_E01;
|
||||
DB_UINT pid = 0;
|
||||
DB_UINT tid = 0;
|
||||
bool extended;
|
||||
const char* response = r_E01;
|
||||
DB_UINT pid = 0;
|
||||
DB_UINT tid = 0;
|
||||
bool extended;
|
||||
extended = thread_id_decode((const char*) &buffer[1], &pid, &tid);
|
||||
if (!extended || (extended && check_pid(pid))) {
|
||||
int r;
|
||||
@@ -1423,7 +1406,7 @@ static int
|
||||
remote_write_reg(uint8_t* buffer, int size)
|
||||
{
|
||||
rtems_debugger_threads* threads = rtems_debugger->threads;
|
||||
const char const* response = r_E01;
|
||||
const char* response = r_E01;
|
||||
if (threads->selector_gen >= 0
|
||||
&& threads->selector_gen < (int) threads->current.level) {
|
||||
const char* equals;
|
||||
@@ -1487,9 +1470,9 @@ remote_read_memory(uint8_t* buffer, int size)
|
||||
static int
|
||||
remote_write_memory(uint8_t* buffer, int size)
|
||||
{
|
||||
const char const* response = r_E01;
|
||||
const char* comma;
|
||||
const char* colon;
|
||||
const char* response = r_E01;
|
||||
const char* comma;
|
||||
const char* colon;
|
||||
comma = strchr((const char*) buffer, ',');
|
||||
colon = strchr((const char*) buffer, ':');
|
||||
if (comma != NULL && colon != NULL) {
|
||||
@@ -1684,9 +1667,7 @@ rtems_debugger_events(rtems_task_argument arg)
|
||||
rtems_debugger_target_enable();
|
||||
|
||||
while (rtems_debugger_server_events_running()) {
|
||||
r = rtems_debugger_server_events_wait();
|
||||
if (r < 0)
|
||||
break;
|
||||
rtems_debugger_server_events_wait();
|
||||
if (!rtems_debugger_server_events_running())
|
||||
break;
|
||||
r = rtems_debugger_thread_system_suspend();
|
||||
@@ -1767,7 +1748,7 @@ rtems_debugger_session(void)
|
||||
}
|
||||
|
||||
rtems_debugger->events_running = false;
|
||||
rtems_debugger_server_events_wake();
|
||||
rtems_debugger_server_events_signal();
|
||||
|
||||
rtems_debugger_unlock();
|
||||
|
||||
@@ -1837,6 +1818,8 @@ rtems_debugger_create(const char* remote,
|
||||
rtems_debugger->pid = getpid();
|
||||
rtems_debugger->remote_debug = false;
|
||||
|
||||
rtems_chain_initialize_empty(&rtems_debugger->exception_threads);
|
||||
|
||||
rtems_debugger->remote = rtems_debugger_remote_find(remote);
|
||||
if (rtems_debugger->remote== NULL) {
|
||||
rtems_printf(printer, "error: rtems-db: remote not found: %s\n", remote);
|
||||
@@ -1851,6 +1834,7 @@ rtems_debugger_create(const char* remote,
|
||||
rtems_debugger->remote->name, strerror(errno));
|
||||
free(rtems_debugger);
|
||||
rtems_debugger = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1907,7 +1891,6 @@ rtems_debugger_main(rtems_task_argument arg)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
||||
rtems_debugger_lock();
|
||||
rtems_debugger->server_running = true;
|
||||
rtems_debugger->server_finished = false;
|
||||
@@ -1949,6 +1932,7 @@ rtems_debugger_start(const char* remote,
|
||||
rtems_debugger_lock();
|
||||
rtems_debugger->server_running = false;
|
||||
rtems_debugger->server_finished = true;
|
||||
_Condition_Initialize_named(&rtems_debugger->server_cond, "DBserver");
|
||||
rtems_debugger_unlock();
|
||||
|
||||
r = rtems_debugger_task_create("DBSs",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -100,6 +101,8 @@ extern "C" {
|
||||
#define RTEMS_DEBUGGER_FLAG_NON_STOP (1 << 2)
|
||||
#define RTEMS_DEBUGGER_FLAG_VCONT (1 << 3)
|
||||
#define RTEMS_DEBUGGER_FLAG_MULTIPROCESS (1 << 4)
|
||||
#define RTEMS_DEBUGGER_FLAG_VERBOSE_LOCK (1 << 5)
|
||||
#define RTEMS_DEBUGGER_FLAG_VERBOSE_CMDS (1 << 6)
|
||||
|
||||
/**
|
||||
* Forward decl for the threads and targets.
|
||||
@@ -108,6 +111,12 @@ typedef struct rtems_debugger_remote rtems_debugger_remote;
|
||||
typedef struct rtems_debugger_threads rtems_debugger_threads;
|
||||
typedef struct rtems_debugger_target rtems_debugger_target;
|
||||
|
||||
/**
|
||||
* Local types for the RTEMS-X interface.
|
||||
*/
|
||||
typedef struct _Condition_Control rtems_rx_cond;
|
||||
typedef struct _Mutex_recursive_Control rtems_rx_mutex;
|
||||
|
||||
/**
|
||||
* Debugger data.
|
||||
*/
|
||||
@@ -116,10 +125,10 @@ typedef struct
|
||||
int port;
|
||||
int timeout;
|
||||
rtems_task_priority priority;
|
||||
rtems_id lock;
|
||||
rtems_id lock_output;
|
||||
rtems_rx_mutex lock;
|
||||
rtems_debugger_remote* remote;
|
||||
rtems_id server_task;
|
||||
rtems_rx_cond server_cond;
|
||||
volatile bool server_running;
|
||||
volatile bool server_finished;
|
||||
rtems_id events_task;
|
||||
@@ -134,6 +143,7 @@ typedef struct
|
||||
uint8_t input[RTEMS_DEBUGGER_BUFFER_SIZE];
|
||||
uint8_t output[RTEMS_DEBUGGER_BUFFER_SIZE];
|
||||
rtems_debugger_threads* threads;
|
||||
rtems_chain_control exception_threads;
|
||||
int signal;
|
||||
rtems_debugger_target* target;
|
||||
} rtems_debugger_server;
|
||||
@@ -147,41 +157,44 @@ extern rtems_debugger_server* rtems_debugger;
|
||||
* Debug server printer.
|
||||
*/
|
||||
extern int rtems_debugger_printf(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
|
||||
extern int rtems_debugger_clean_printf(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
|
||||
extern void rtems_debugger_printk_lock(rtems_interrupt_lock_context* lock_context);
|
||||
extern void rtems_debugger_printk_unlock(rtems_interrupt_lock_context* lock_context);
|
||||
|
||||
/**
|
||||
* Lock the Debugger.
|
||||
*/
|
||||
extern int rtems_debugger_lock(void);
|
||||
extern void rtems_debugger_lock(void);
|
||||
|
||||
/**
|
||||
* Unlock the Debugger.
|
||||
*/
|
||||
extern int rtems_debugger_unlock(void);
|
||||
extern void rtems_debugger_unlock(void);
|
||||
|
||||
/**
|
||||
* Is the server still running?
|
||||
*/
|
||||
bool rtems_debugger_server_running(void);
|
||||
extern bool rtems_debugger_server_running(void);
|
||||
|
||||
/**
|
||||
* Get the remote handle from the debugger.
|
||||
*/
|
||||
rtems_debugger_remote* rtems_debugger_remote_handle(void);
|
||||
extern rtems_debugger_remote* rtems_debugger_remote_handle(void);
|
||||
|
||||
/**
|
||||
* Is the debugger connected?
|
||||
*/
|
||||
bool rtems_debugger_connected(void);
|
||||
extern bool rtems_debugger_connected(void);
|
||||
|
||||
/**
|
||||
* Is the debugger events thread runnins?
|
||||
*/
|
||||
bool rtems_debugger_server_events_running(void);
|
||||
extern bool rtems_debugger_server_events_running(void);
|
||||
|
||||
/**
|
||||
* Wake events thread in the debug server.
|
||||
* Signal events thread in the debug server to run.
|
||||
*/
|
||||
extern int rtems_debugger_server_events_wake(void);
|
||||
extern void rtems_debugger_server_events_signal(void);
|
||||
|
||||
/**
|
||||
* Check if verbose is on.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -40,12 +41,15 @@
|
||||
#include "rtems-debugger-threads.h"
|
||||
|
||||
/**
|
||||
* Frame signature.
|
||||
* Exception local stack frame data to synchronise with the debugger
|
||||
* server's events loop processor.
|
||||
*/
|
||||
#define TARGET_FRAME_MAGIC_NUM (2)
|
||||
#define TARGET_FRAME_MAGIC 0xdeadbeef, 0xb2107016
|
||||
static const uint32_t
|
||||
frame_magic[TARGET_FRAME_MAGIC_NUM] = { TARGET_FRAME_MAGIC };
|
||||
typedef struct {
|
||||
rtems_chain_node node;
|
||||
rtems_id id;
|
||||
CPU_Exception_frame* frame;
|
||||
rtems_rx_cond cond;
|
||||
} rtems_debugger_exception;
|
||||
|
||||
#if TARGET_DEBUG
|
||||
#include <rtems/bspIo.h>
|
||||
@@ -53,9 +57,12 @@ static void target_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2);
|
||||
static void
|
||||
target_printk(const char* format, ...)
|
||||
{
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
rtems_debugger_printk_lock(&lock_context);
|
||||
vprintk(format, ap);
|
||||
rtems_debugger_printk_unlock(&lock_context);
|
||||
va_end(ap);
|
||||
}
|
||||
#else
|
||||
@@ -219,6 +226,11 @@ rtems_debugger_target_swbreak_insert(void)
|
||||
if (target->breakpoint_size > 4)
|
||||
memcpy(loc, &target->breakpoint[0], target->breakpoint_size);
|
||||
else {
|
||||
if (rtems_debugger_verbose())
|
||||
rtems_debugger_printf("rtems-db: bp: in: %p %p %d %d %d\n",
|
||||
loc, &target->breakpoint[0],
|
||||
(int) target->breakpoint_size,
|
||||
(int) i, (int) target->swbreaks.level);
|
||||
switch (target->breakpoint_size) {
|
||||
case 4:
|
||||
loc[3] = target->breakpoint[3];
|
||||
@@ -276,30 +288,22 @@ rtems_debugger_target_swbreak_remove(void)
|
||||
rtems_debugger_target_exc_action
|
||||
rtems_debugger_target_exception(CPU_Exception_frame* frame)
|
||||
{
|
||||
volatile const uint32_t magic[3] = {
|
||||
(uint32_t) frame, TARGET_FRAME_MAGIC
|
||||
};
|
||||
|
||||
(void) magic;
|
||||
|
||||
if (!rtems_interrupt_is_in_progress()) {
|
||||
rtems_debugger_threads* threads = rtems_debugger->threads;
|
||||
#if USE_THREAD_EXECUTING
|
||||
Thread_Control* thread = _Thread_Executing;
|
||||
#else
|
||||
const Per_CPU_Control* cpu = _Per_CPU_Get_snapshot();
|
||||
Thread_Control* thread = _Per_CPU_Get_executing(cpu);
|
||||
#endif
|
||||
rtems_id* excludes;
|
||||
Thread_Control* thread = _Thread_Get_executing();
|
||||
const rtems_id tid = thread->Object.id;
|
||||
rtems_id* excludes;
|
||||
DB_UINT pc;
|
||||
const rtems_debugger_thread_stepper* stepper;
|
||||
rtems_debugger_exception target_exception;
|
||||
size_t i;
|
||||
|
||||
target_printk("[} tid:%08" PRIx32 ": thread:%08" PRIxPTR
|
||||
" frame:%08" PRIxPTR "\n",
|
||||
tid, (intptr_t) thread, (intptr_t) frame);
|
||||
|
||||
rtems_debugger_lock();
|
||||
|
||||
/*
|
||||
* If the thread is the debugger recover.
|
||||
*/
|
||||
@@ -307,9 +311,11 @@ rtems_debugger_target_exception(CPU_Exception_frame* frame)
|
||||
if (rtems_debugger->target->memory_access) {
|
||||
target_printk("[} server access fault\n");
|
||||
rtems_debugger->target->memory_access = true;
|
||||
rtems_debugger_unlock();
|
||||
longjmp(rtems_debugger->target->access_return, -1);
|
||||
}
|
||||
target_printk("[} server exception\n");
|
||||
rtems_debugger_unlock();
|
||||
return rtems_debugger_target_exc_cascade;
|
||||
}
|
||||
|
||||
@@ -327,6 +333,7 @@ rtems_debugger_target_exception(CPU_Exception_frame* frame)
|
||||
* swbreak's contents.
|
||||
*/
|
||||
target_printk("[} tid:%08lx: excluded\n", tid);
|
||||
rtems_debugger_unlock();
|
||||
return rtems_debugger_target_exc_cascade;
|
||||
}
|
||||
}
|
||||
@@ -340,18 +347,39 @@ rtems_debugger_target_exception(CPU_Exception_frame* frame)
|
||||
stepper->thread->frame = frame;
|
||||
rtems_debugger_target_thread_stepping(stepper->thread);
|
||||
target_printk("[} tid:%08lx: stepping\n", tid);
|
||||
rtems_debugger_unlock();
|
||||
return rtems_debugger_target_exc_step;
|
||||
}
|
||||
|
||||
target_printk("[} tid:%08lx: suspending\n", tid);
|
||||
|
||||
/*
|
||||
* Tag the thread as being debugged, wake the debug server's event thread,
|
||||
* then suspend this thread.
|
||||
* Initialise the target exception data and queue ready for the debugger
|
||||
* server's event processor to handle.
|
||||
*/
|
||||
_Thread_Set_state(thread, STATES_DEBUGGER);
|
||||
rtems_debugger_server_events_wake();
|
||||
rtems_task_suspend(tid);
|
||||
rtems_chain_initialize_node(&target_exception.node);
|
||||
target_exception.frame = frame;
|
||||
target_exception.id = tid;
|
||||
_Condition_Initialize(&target_exception.cond);
|
||||
|
||||
rtems_chain_append_unprotected(&rtems_debugger->exception_threads,
|
||||
&target_exception.node);
|
||||
|
||||
/*
|
||||
* Signal the debug server's thread.
|
||||
*/
|
||||
rtems_debugger_server_events_signal();
|
||||
|
||||
/*
|
||||
* Block on the exception thread's condition variable unlocking the
|
||||
* debugger's mutex and letting the server's thread run.
|
||||
*/
|
||||
_Condition_Wait_recursive(&target_exception.cond, &rtems_debugger->lock);
|
||||
|
||||
/*
|
||||
* Unlock the debugger's lock now the exception is resuming.
|
||||
*/
|
||||
rtems_debugger_unlock();
|
||||
|
||||
target_printk("[} tid:%08lx: resuming\n", tid);
|
||||
|
||||
@@ -363,31 +391,39 @@ rtems_debugger_target_exception(CPU_Exception_frame* frame)
|
||||
return rtems_debugger_target_exc_cascade;
|
||||
}
|
||||
|
||||
int
|
||||
rtems_debugger_target_set_exception_frame(rtems_debugger_thread* thread)
|
||||
void
|
||||
rtems_debugger_target_exception_thread(rtems_debugger_thread* thread)
|
||||
{
|
||||
int r = 0;
|
||||
rtems_chain_node* node;
|
||||
thread->frame = NULL;
|
||||
thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING;
|
||||
if ((thread->tcb->current_state & STATES_DEBUGGER) != 0) {
|
||||
CPU_Exception_frame* frame = NULL;
|
||||
DB_UINT* sp;
|
||||
int i;
|
||||
sp = (DB_UINT*) rtems_debugger_target_tcb_sp(thread);
|
||||
for (i = 0; i < 128; ++i) {
|
||||
if (sp[i] == frame_magic[0] && sp[i + 1] == frame_magic[1]) {
|
||||
frame = (CPU_Exception_frame*) sp[i + 2];
|
||||
break;
|
||||
}
|
||||
thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
|
||||
for (node = rtems_chain_first(&rtems_debugger->exception_threads);
|
||||
!rtems_chain_is_tail(&rtems_debugger->exception_threads, node);
|
||||
node = rtems_chain_next(node)) {
|
||||
rtems_debugger_exception* target_exception = (rtems_debugger_exception*) node;
|
||||
if (target_exception->id == thread->id) {
|
||||
thread->frame = target_exception->frame;
|
||||
thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rtems_debugger_target_exception_thread_resume(rtems_debugger_thread* thread)
|
||||
{
|
||||
rtems_chain_node* node;
|
||||
for (node = rtems_chain_first(&rtems_debugger->exception_threads);
|
||||
!rtems_chain_is_tail(&rtems_debugger->exception_threads, node);
|
||||
node = rtems_chain_next(node)) {
|
||||
rtems_debugger_exception* target_exception = (rtems_debugger_exception*) node;
|
||||
if (target_exception->id == thread->id) {
|
||||
rtems_chain_extract(node);
|
||||
thread->frame = NULL;
|
||||
thread->flags &= ~RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION;
|
||||
_Condition_Signal(&target_exception->cond);
|
||||
break;
|
||||
}
|
||||
_Thread_Clear_state(thread->tcb, STATES_DEBUGGER);
|
||||
thread->frame = frame;
|
||||
if (frame != NULL)
|
||||
thread->flags |= RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING;
|
||||
else
|
||||
r = -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -193,6 +194,16 @@ extern int rtems_debugger_target_swbreak_insert(void);
|
||||
*/
|
||||
extern int rtems_debugger_target_swbreak_remove(void);
|
||||
|
||||
/**
|
||||
* Insert hardware breakpoints into the hardware.
|
||||
*/
|
||||
extern int rtems_debugger_target_hwbreak_insert(void);
|
||||
|
||||
/**
|
||||
* Remove hardware breakpoints from the hardware.
|
||||
*/
|
||||
extern int rtems_debugger_target_hwbreak_remove(void);
|
||||
|
||||
/**
|
||||
* Hardware breakpoints.
|
||||
*/
|
||||
@@ -208,9 +219,14 @@ extern rtems_debugger_target_exc_action
|
||||
rtems_debugger_target_exception(CPU_Exception_frame* frame);
|
||||
|
||||
/**
|
||||
* Set the thread's exception stack frame pointer.
|
||||
* See if the thread is an exception thread.
|
||||
*/
|
||||
extern int rtems_debugger_target_set_exception_frame(rtems_debugger_thread* thread);
|
||||
extern void rtems_debugger_target_exception_thread(rtems_debugger_thread* thread);
|
||||
|
||||
/**
|
||||
* If the thread is an exception thread, resume it.
|
||||
*/
|
||||
extern void rtems_debugger_target_exception_thread_resume(rtems_debugger_thread* thread);
|
||||
|
||||
/**
|
||||
* Target instruction cache sync. This depends on the target but it normally
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -43,6 +44,7 @@ static const char * const excludes_defaults[] =
|
||||
"IRQS",
|
||||
"DBSs",
|
||||
"DBSe",
|
||||
"IDLE",
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -240,23 +242,13 @@ snapshot_thread(rtems_tcb* tcb, void* arg)
|
||||
* See if there is a valid exception stack frame and if the thread is being
|
||||
* debugged.
|
||||
*/
|
||||
r = rtems_debugger_target_set_exception_frame(thread);
|
||||
if (r < 0) {
|
||||
rtems_debugger_printf("error: rtems-db: thread: snap: %08lx: not valid frame\n",
|
||||
id);
|
||||
}
|
||||
rtems_debugger_target_exception_thread(thread);
|
||||
|
||||
/*
|
||||
* Read the target registers into the thread register array.
|
||||
*/
|
||||
rtems_debugger_target_read_regs(thread);
|
||||
|
||||
/*
|
||||
* Debugger threads are stopped for breakpoint, segv or other errors have
|
||||
* the RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING set.
|
||||
* Exception threads have stopped for breakpoint, segv or other errors.
|
||||
*/
|
||||
if (rtems_debugger_thread_flag(thread,
|
||||
RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING)) {
|
||||
RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION)) {
|
||||
rtems_id* stopped;
|
||||
r = rtems_debugger_block_resize(&threads->stopped);
|
||||
if (r < 0) {
|
||||
@@ -276,6 +268,11 @@ snapshot_thread(rtems_tcb* tcb, void* arg)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the target registers into the thread register array.
|
||||
*/
|
||||
rtems_debugger_target_read_regs(thread);
|
||||
|
||||
if (rtems_debugger_server_flag(RTEMS_DEBUGGER_FLAG_VERBOSE))
|
||||
rtems_debugger_printf("rtems-db: sys: thd: %08lx: signal: %d\n",
|
||||
id, thread->signal);
|
||||
@@ -300,6 +297,8 @@ rtems_debugger_thread_system_suspend(void)
|
||||
if (rtems_debugger_verbose())
|
||||
rtems_debugger_printf("rtems-db: sys: : suspending\n");
|
||||
r = rtems_debugger_target_swbreak_remove();
|
||||
if (r == 0)
|
||||
r = rtems_debugger_target_hwbreak_remove();
|
||||
if (r == 0) {
|
||||
rtems_debugger_thread* current;
|
||||
threads->current.level = 0;
|
||||
@@ -352,15 +351,19 @@ rtems_debugger_thread_system_resume(bool detaching)
|
||||
size_t i;
|
||||
if (rtems_debugger_verbose())
|
||||
rtems_debugger_printf("rtems-db: sys: : resuming\n");
|
||||
if (!detaching)
|
||||
if (!detaching) {
|
||||
r = rtems_debugger_target_swbreak_insert();
|
||||
if (r == 0)
|
||||
r = rtems_debugger_target_hwbreak_insert();
|
||||
}
|
||||
if (r == 0) {
|
||||
for (i = 0; i < threads->current.level; ++i) {
|
||||
rtems_debugger_thread* thread = ¤t[i];
|
||||
rtems_status_code sc;
|
||||
int rr;
|
||||
/*
|
||||
* Check if resuming, which is continuing, a step, or stepping a range.
|
||||
* Check if resuming, which can be continuing, a step, or stepping a
|
||||
* range.
|
||||
*/
|
||||
if (detaching ||
|
||||
rtems_debugger_thread_flag(thread,
|
||||
@@ -376,10 +379,18 @@ rtems_debugger_thread_system_resume(bool detaching)
|
||||
r = rr;
|
||||
}
|
||||
}
|
||||
sc = rtems_task_resume(thread->id);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
rtems_debugger_printf("error: rtems-db: thread: resume: %08lx: %s\n",
|
||||
thread->id, rtems_status_text(sc));
|
||||
if (rtems_debugger_verbose())
|
||||
rtems_debugger_printf("rtems-db: sys: : resume: 0x%08lx\n",
|
||||
thread->id);
|
||||
if (rtems_debugger_thread_flag(thread,
|
||||
RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION)) {
|
||||
rtems_debugger_target_exception_thread_resume(thread);
|
||||
} else {
|
||||
sc = rtems_task_resume(thread->id);
|
||||
if (sc != RTEMS_SUCCESSFUL) {
|
||||
rtems_debugger_printf("error: rtems-db: thread: resume: %08lx: %s\n",
|
||||
thread->id, rtems_status_text(sc));
|
||||
}
|
||||
}
|
||||
thread->flags &= ~(RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE |
|
||||
RTEMS_DEBUGGER_THREAD_FLAG_STEP);
|
||||
@@ -420,10 +431,12 @@ rtems_debugger_thread_continue_all(void)
|
||||
size_t i;
|
||||
for (i = 0; i < threads->current.level; ++i) {
|
||||
rtems_debugger_thread* thread = ¤t[i];
|
||||
int r;
|
||||
r = rtems_debugger_thread_continue(thread);
|
||||
if (r < 0)
|
||||
break;
|
||||
if (!rtems_debugger_thread_flag(thread,
|
||||
RTEMS_DEBUGGER_THREAD_FLAG_STEP_INSTR)) {
|
||||
r = rtems_debugger_thread_continue(thread);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Chris Johns <chrisj@rtems.org>. All rights reserved.
|
||||
* Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -51,7 +52,7 @@ extern "C" {
|
||||
/**
|
||||
* Debugger thread flags.
|
||||
*/
|
||||
#define RTEMS_DEBUGGER_THREAD_FLAG_DEBUGGING (1 << 0)
|
||||
#define RTEMS_DEBUGGER_THREAD_FLAG_EXCEPTION (1 << 0)
|
||||
#define RTEMS_DEBUGGER_THREAD_FLAG_REG_VALID (1 << 1)
|
||||
#define RTEMS_DEBUGGER_THREAD_FLAG_REG_DIRTY (1 << 2)
|
||||
#define RTEMS_DEBUGGER_THREAD_FLAG_CONTINUE (1 << 3)
|
||||
@@ -162,8 +163,8 @@ extern int rtems_debugger_thread_step(rtems_debugger_thread* thread);
|
||||
* Thread is stepping so record the details.
|
||||
*/
|
||||
extern int rtems_debugger_thread_stepping(rtems_debugger_thread* thread,
|
||||
DB_UINT start,
|
||||
DB_UINT end);
|
||||
DB_UINT start,
|
||||
DB_UINT end);
|
||||
|
||||
/**
|
||||
* Thread's PC in the stepping range? Returns the stepper is in range else
|
||||
@@ -196,8 +197,8 @@ extern int rtems_debugger_thread_state(rtems_debugger_thread* thread);
|
||||
* Return a string of the thread's state.
|
||||
*/
|
||||
extern int rtems_debugger_thread_state_str(rtems_debugger_thread* thread,
|
||||
char* buffer,
|
||||
size_t size);
|
||||
char* buffer,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* Return the thread's stack size.
|
||||
@@ -214,8 +215,7 @@ extern void* rtems_debugger_thread_stack_area(rtems_debugger_thread* thread);
|
||||
* set.
|
||||
*/
|
||||
static inline bool
|
||||
rtems_debugger_thread_flag(rtems_debugger_thread* thread,
|
||||
uint32_t mask)
|
||||
rtems_debugger_thread_flag(rtems_debugger_thread* thread, uint32_t mask)
|
||||
{
|
||||
return (thread->flags & mask) != 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user